Bug 28465 - backtrace() looses last frame on SIGSEGV
Summary: backtrace() looses last frame on SIGSEGV
Keywords:
Status: CLOSED NOTABUG
Alias: None
Product: Red Hat Linux
Classification: Retired
Component: glibc
Version: 6.2
Hardware: i386
OS: Linux
medium
medium
Target Milestone: ---
Assignee: Jakub Jelinek
QA Contact: Aaron Brown
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2001-02-20 15:58 UTC by André Johansen
Modified: 2016-11-24 14:56 UTC (History)
2 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed: 2001-02-20 15:58:41 UTC
Embargoed:


Attachments (Terms of Use)

Description André Johansen 2001-02-20 15:58:37 UTC
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.)

Comment 1 Jakub Jelinek 2001-02-21 17:28:46 UTC
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.

Comment 2 André Johansen 2001-02-22 09:55:39 UTC
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.


Comment 3 André Johansen 2001-02-26 18:39:58 UTC
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...



Note You need to log in before you can comment on or make changes to this bug.