This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Problems with _Unwind_Backtrace() vs signal handlers
- From: Bryce McKinlay <mckinlay at redhat dot com>
- To: gcc at gcc dot gnu dot org
- Date: Sat, 31 Jul 2004 20:05:10 -0400
- Subject: Problems with _Unwind_Backtrace() vs signal handlers
libjava wants to use _Unwind_Backtrace() and friends to print diagnostic
stack traces for exceptions (as well as examine the stack for security
checks, etc). There appears to be a problem when trying to
_Unwind_Backtrace through signal handler frames.
For a normal call, the IP returned by _Unwind_GetIP() is the return
address for the frame above it - that is, the instruction following the
"call" instruction. When unwinding through a signal handler, however,
_Unwind_GetIP() returns a pointer to the faulting instruction. This
inconsistency makes it difficult for libjava to print the correct source
line number for NullPointerExceptions, and I suspect it also has
something to do with the next problem:
The test case below is a simplification of what libjava is doing when it
tries to print a stack trace for a NullPointerException. When compiled
with no optimization, it works ok, but when compiled with -O2, the
_Unwind_Backtrace() either misses/skips over some frames, or terminates
prematurely. I tried this test on i686, x86-64, and ppc-32, and it seems
more or less consistently broken on each of those platforms.
Note that the unwinder seems to work fine through the signal frame to
find an exception handler, it is just _Unwind_Backtrace() that fails.
Bryce
/* Compile with -fnon-call-exceptions */
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unwind.h>
extern void segv_handler(int sig);
extern int foo() __attribute__ ((noinline));
extern int bar(char *z) __attribute__ ((noinline));
int main(int argc, char *argv)
{
/* Set up SEGV handler. */
struct sigaction action;
action.sa_handler = segv_handler;
action.sa_flags = 0;
sigaction (SIGSEGV, &action, NULL);
/* Cause a segfault. */
printf ("%i\n", foo());
}
int bar(char *ptr)
{
return ptr[10]; /* segfault */
}
int foo()
{
return bar(NULL);
}
_Unwind_Reason_Code
unwind_trace_fn (struct _Unwind_Context *context, void *p)
{
_Unwind_Ptr ip = _Unwind_GetIP (context);
void *fn = _Unwind_FindEnclosingFunction((void *) ip);
if (fn == main)
printf ("main()\n");
if (fn == foo)
printf ("foo()\n");
if (fn == bar)
printf ("bar()\n");
return _URC_NO_REASON;
}
void segv_handler(int sig)
{
_Unwind_Backtrace (unwind_trace_fn, NULL);
exit(1);
}