When a segmentation violation signal is handled, the function where the SEGV happens is not available from the backtrace() call; this of-course makes it less usefull, since the correct spot of the crash cannot be found. The program below shows the problem; the last frame available before sigaction() is f5(). f5() calls the function crash(), which results in a SIGSEGV. When the call-stack is extracted, the frame for crash() is not available. When mapping addressess to function-name:line, one only knows which function the crash happens in (by looking at the last line before sigaction(), which will then point to a line with a function call. Program: #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <unistd.h> #include <execinfo.h> static void SignalHandler(int nSignal); static void f1(); static void f2(); static void f3(); static void f4(); static void f5(); static void crash(); int main() { signal(SIGSEGV, SignalHandler); f1(); return 0; } static void SignalHandler(int nSignal) { if ( SIGSEGV == nSignal ) { static bool bHere = false; if ( true == bHere ) { printf("Caught SIGSEGV while handling it -- aborting...\n"); abort(); } bHere = true; void* apFrames[256]; const int nNumFramesFound = backtrace(apFrames, sizeof(apFrames)/sizeof(apFrames[0])); char** apzFrameNames = backtrace_symbols(apFrames, nNumFramesFound); printf("Frame address \t Symbol\n" "--------------------------------------------------------------\n"); for ( int iFrame = 0; iFrame < nNumFramesFound; ++iFrame ) { const unsigned int nAddress = (unsigned int)apFrames[iFrame]; printf("0x%08x \t +%s;\n", nAddress, apzFrameNames[iFrame]); } free(apzFrameNames); bHere = false; abort(); } } static void f1() { f2(); } static void f2() { f3(); } static void f3() { f4(); } static void f4() { f5(); } static void f5() { crash(); } static void crash() { char* oops = 0; *oops = 0; } This is the output caused by the program: Frame address Symbol -------------------------------------------------------------- 0x0804871b +[0x804871b]; 0x4009ec48 +/lib/libc.so.6(sigaction+0x268) [0x4009ec48]; 0x08048853 +[0x8048853]; 0x0804883f +[0x804883f]; 0x08048827 +[0x8048827]; 0x08048813 +[0x8048813]; 0x080487ff +[0x80487ff]; 0x080486bd +[0x80486bd]; 0x400989cb +/lib/libc.so.6(__libc_start_main+0xff) [0x400989cb]; 0x080485e1 +[0x80485e1]; Aborted (core dumped) (This has also been tested on Red Hat Linux v7.0 with the latest glibc package, and the problem is present there as well.)
backtrace reflects the actual layout on the stack, segfault does not create a new stack frame at the instruction which caused the crash. You can inspect either struct sigcontext (for non-SA_SIGINFO signal handlers) or siginfo_t (plus sigcontext) for SA_SIGINFO signals. From there you can get register values at the point of the crash, the address which it tried to dereference etc. libSegFault.so library does exactly this, if you're interested you can check its source in glibc.
I'm not very into how this works, but would it be possible to create a new stack frame when a SIGSEGV occurs? (Does sigaction() reuse the current frame as it is now?) The code I have that uses backtrace() etc. is used both in some custom asserts, when crashes occur (i.e. SIGSEGV handlers) and to just get the current call-stack for later retrieval. Thus it would be nice to have the same interface for this; I'll have a look at the before-mentioned code though.
Ouch, SA_SIGINFO signal handling in Linux doesn't work correctly, the si_addr field doesn't contain anything useful. However, it does work somewhat by using old-style signal-handlers with the extra sigcontext argument, and then map that to the new-style SA_SIGINFO system (by extracting copying sigcontext::eip into siginfo_t::si_addr)... Ugly...