This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
longjmp()/setjmp() difference between gcc 3.3 (Apple) and gcc 3.4.x
- From: "Remy X.O. Martin" <vsxo at hotmail dot com>
- To: gcc at gcc dot gnu dot org
- Date: Sat, 22 Jan 2005 18:41:32 +0100
- Subject: longjmp()/setjmp() difference between gcc 3.3 (Apple) and gcc 3.4.x
Hello,
I'm working on a simple scripting language, in particular on its pointer implementation. In a small nutshell: the only values that are actually passed between callback functions (and that the user sees) are doubles. On a system with 32bit pointers, I can thus create a 'pointer variable' by encoding those 32bits in the 64bit double. I am testing the possibility to catch invalid memory accesses (SEGVs) when determining if a passed value represents indeed a valid pointer by using a setjmp in the parsing routine, and a longjmp in a segv_handler to that position:
segv_handler(int sig)
{
....
if( sig==SIGSEGV && parsing_pointer ){
signal( SIGSEGV, segv_handler );
longjmp(pa_jmp, 1);
}
....
}
void* parse_pointer( value_t value, ... )
{ static short jmpset= False;
parsing_pointer= True;
....
if( /* cast value to a specific struct and check various conditions to be sure it is a pointer */ ){
if( jmpset || setjmp(pa_jmp)== 0 ){
jmpset= True;
/* all OK: return pointer to internal structure */
if( (++count) == 10 ){
/* blunt test if the principle works. */
raise(SIGSEGV);
}
}
else{
/* the checking caused a SEGV since value does not represent a pointerr to an internal structure */
/* return NULL */
}
}
....
}
I use the jmpset state flag since setjmp() turns out to be very expensive: it in fact makes execution on a 1.8Ghz G5 biproc desktop system SLOWER than on a 1.5Ghz G4 laptop (not twice slower, but almost). setjmp() needs to be called only once, I think, and in any case the code sketched above works as intended. However, there is a side effect.
For testing, I raise a SEGV after 10 calls, as shown. The testing, calling code is:
for( n= i= 0; i< LOOPS; i++ ){
if( !(ptr= parse_pointer(encoded_pointer,...)) ){
fprintf( stderr, "parse_pointer() failed at loop i==%d, n==%d\n", i, n );
}
else{
fprintf( stderr, " %d ", i );
n+= 1;
}
}
When compiled with gcc-3.3 (as included with Apple's XCode 1.5), this loop does what is expected: it prints a message at i==n==9, and after termination, i==LOOPS-1, n==i-1 (1 failure).
When I compile this with gcc 3.4.1 or 3.4.3, I still get the message, but the longjmp() resets n to 0, the value it had when the corresponding setjmp() was first called.
I can see the logic for both solutions, and I assume that this is exactly what is discussed in the gcc info pages concerning non-volatile automatic variables (taking n's address indeed 'solves' the issue).
Two questions:
1) Any idea why the setjmp() call is so expensive on a G5, and is there a better, less expensive way I could implement the catch mechanism?
2) Are there really no assumptions that can be made about how this situation is handled on a given system? I mean, isn't it a bit surprising that 2 versions of the same compiler family give different results on the same system? Equally surprising: the value of the loop counter i was correct all along!
Thanks!
Remy.
PS: it turns out that this parser is exactly 2x faster compiled for G5 with gcc-3.4.3 than with Apple's own gcc-3.3!