www.digitalmars.com         C & C++   DMDScript  

c++ - setjmp/longjmp code crashing

reply Daniel Keep <daniel.keep.lists gmail.com> writes:
Hi.

I'm having problems getting the attached code to work under DMC.  Under
MSVC 15.00.30729.01 (from 2008 express) and GCC 4.3.3, it works
perfectly.  I'm using DMC 8.42n.

Basically, it looks like either stack corruption or invalid codegen.

Around line 25, I assign a pointer to global thread local __eh_exc.  If
I do so, the program apparently attempts to assign to a local variable
in a different function (__eh_rethrow_0 in func) by dereferencing __eh_exc.

If I comment the assignment to __eh_exc, its value *changes* despite
nothing assigning to it.  What's worrying is that it changes to the
contents of the other global thread local __eh_ctx.

Alternately, instead of assigning 0xbeeffeed to __eh_exc, I can assign
the address of a valid memory address (see line 43).  If I do this, the
contents of __eh_ctx_prev_0 in func CHANGES to the address of the
allocated message.  Later, the value of __eh_exc is apparently changed to 0.

There's also a loop at 102 to clear __eh_ctx that I've commented out.
Uncomment it and the way the program fails changes again.

As I said, this all works perfectly in MSVC and GCC.  I'm absolutely at
my wit's end attempting to debug this.  I'd really like to get this
working with setjmp/longjmp without having to resort to __try and co.

  -- Daniel

ehtest3.c:

#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>

#define __eh_ctx_t jmp_buf
#define __THREAD_LOCAL __declspec(thread)

static __THREAD_LOCAL __eh_ctx_t __eh_ctx;
static __THREAD_LOCAL void* __eh_exc;

static void __eh_throw(void *obj)
{
    if( obj == 0 )
    {
        printf("Error: tried to throw null exception object.\n");
        abort();
    }
    printf("Setting __eh_exc to 0x%08x\n", obj);

    // FIXME
    // If you uncomment this line, the executable produced by DMC
crashes when
    // you attempt to assign to __eh_rethrow_0 below around line 71.
    // Comment it, and the value of __eh_exc changes *anyway*.  Aside from
    // initialisation in main, this is the ONLY place where it is
assigned to.
    __eh_exc = obj;

    printf("Jumping\n");
    longjmp(__eh_ctx, 1);
}

static void __eh_ctx_cpy(__eh_ctx_t dst, __eh_ctx_t src)
{
    printf("Copying 0x%08x --> 0x%08x\n", (void*)src, (void*)dst);
    memcpy(dst, src, sizeof(__eh_ctx_t));
}

void __cdecl foo(void)
{
    printf("Inside foo\n");

    __eh_throw((void*)0xbeeffeed);

    // Uncomment the following to see the code restore the EH state... from
    // the allocated message around line 76.
    /*
    int *msg = (int*) malloc(4);
    printf("msg = 0x%08x\n", msg);
    *msg = 0xf00dabcd;
    printf("*msg = 0x%08x\n", *msg);
    __eh_throw(msg);
    // */
}

void __cdecl func(void)
{
    __eh_ctx_t __eh_ctx_prev_0;
    int __eh_rethrow_0;
    {
        printf("Before\n");
        __eh_ctx_cpy(__eh_ctx_prev_0, __eh_ctx);
        __eh_rethrow_0 = 0;
        if( setjmp(__eh_ctx) == 0 )
        {
            printf("Before foo\n");
            foo();
            printf("After foo\n");
        }
        else
        {
            printf("Setting rethrow flag\n");
            __eh_rethrow_0 = 1;
            printf("Done\n");
        }
        {
            printf("Restoring EH state\n");
            __eh_ctx_cpy(__eh_ctx, __eh_ctx_prev_0);
            {
                printf("Finally\n");
            }
            if( __eh_rethrow_0 == 1 )
            {
                printf("Rethrowing\n");
                __eh_throw(__eh_exc);
            }
        }
    }
    printf("After\n");
}

int main(int argc, char** argv)
{
    int i;
    int exitCode;

    __eh_exc = (void*)0xdeadbeef;

    printf(" - __eh_ctx = 0x%08x\n", (void*)__eh_ctx);
    printf(" - __eh_exc = 0x%08x\n", (void*)__eh_exc);

    printf("\n");

    // Uncomment this to change the behaviour again.
    /*for( i=0; i<sizeof(__eh_ctx_t); ++i )
        ((unsigned char*)__eh_ctx)[i] = 0;*/

    exitCode = 0;
    if( setjmp(__eh_ctx) == 0 )
    {
        func();
    }
    else
    {
        printf("Error: uncaught exception.\n");
        exitCode = -1;
    }
    printf("After main catch\n");

    printf(" - __eh_ctx = 0x%08x\n", (void*)__eh_ctx);
    printf(" - __eh_exc = 0x%08x\n", (void*)__eh_exc);

    return exitCode;
}
Oct 30 2009
parent Walter Bright <newshound1 digitalmars.com> writes:
The following setjmp/longjmp does work successfully with dmc:

 /*_ testsj.c   Mon Apr  4 1988   Modified by: Walter Bright */
 /* Test setjmp/longjmp	*/
 
 #include	<stdio.h>
 #include	<stdlib.h>
 #include	<assert.h>
 #include	<string.h>
 #include	<setjmp.h>
 
 static jmp_buf sj[2];
 
 /* This function simply returns the value that was passed to it after */
 /* doing some simple jumping around. */
 int func(field)
 int field;
 	{

 	if(setjmp(sj[0])==0)
 		goto JUMP1;
 
 	/* Return the value that was passed to us. */
 	return(field);
 
 JUMP1:

 	if(setjmp(sj[1])==0)
 		goto JUMP2;
 

 	unjmp(0);
 
 JUMP2:

 	unjmp(1);
 	}
 
 /* Perform a longjmp() at a level 'above' the setjmp() call. */
 int unjmp(level)
 int level;
 	{
 	longjmp(sj[level],1);
 	}
 
 /****************************************/
 
 void testsj2()
 {
 	struct {
 		jmp_buf jump;
 		int stomp;
 	} foo;
 
 	foo.stomp = 666;
 	setjmp (foo.jump);
 	assert(foo.stomp == 666);
 }
 
 /****************************************/
 
 int _cdecl main()
 	{
 	int r;
 	r=func(3003);
 
 	/* The value printed out below should be 3003, and it never is! */
 	printf("return value=%d   (should be 3003)\n",r);
 	assert(r == 3003);
 
 	testsj2();
 	printf("Success\n");
 	return EXIT_SUCCESS;
 	}
Nov 15 2009