Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
373 views
in Technique[技术] by (71.8m points)

c++ - Getting fault address that generated a UNIX signal

I am interested in a signal handler which can identify the address of the instruction which caused the problem.

I know about siginfo_t and __builtin_return_address and neither seem to work:

#include <iostream>
#include <signal.h>

void handler (int, siginfo_t *, void *);

int main ()
{
begin:
    std :: cerr << &&begin << " ~ " << &&before << " ~ " << &&after << "
";

    struct sigaction s;
    s .sa_flags = SA_SIGINFO;
    sigemptyset (& s .sa_mask);
    s .sa_sigaction = handler;
    sigaction (SIGSEGV, &s, NULL);

    int * i = NULL;
before:
    *i = 0;
after:
    std :: cout << "End.
";
}

void handler (int, siginfo_t *si, void *)
{
    std :: cerr << "si:" << si -> si_addr << "
";
    std :: cerr << "At: " << __builtin_return_address (0) << "
";
    std :: cerr << "At: " << __builtin_return_address (1) << "
";
    std :: cerr << "At: " << __builtin_return_address (2) << "
";
    std :: cerr << "At: " << __builtin_return_address (3) << "
";
    std :: cerr << "At: " << __builtin_return_address (4) << "
";
    std :: cerr << "At: " << __builtin_return_address (5) << "
";
}

This outputs something like:

0x10978 ~ 0x10a4c ~ 0x10a54
si:0
At: 0xfb945364
At: 0xfb939e64
At: 0x10a40
At: 0x10740
At: 0
At: Segmentation Fault

So siginfo_t is NULL and __builtin_return_address is yielding values somewhere in between the named labels.

I was expecting both of these to return the value of &&before. Am I using these functions correctly?

Tested on Linux 2.6.9-89.0.9.Elsmp and SunOS.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The third argument to a handler installed with SA_SIGINFO (the one declared as void *) is a pointer to a ucontext_t structure. The contents of this structure are architecture- and OS-specific and not part of any standard, but they include the information you need. Here's a version of your program adapted to use it (Linux/x86-64 specific; you will need #ifdefs for every architecture and OS of interest):

#define _GNU_SOURCE 1
#include <iostream>
#include <iomanip>
#include <signal.h>
#include <ucontext.h>

using std::cout;

static volatile int *causecrash;

static void handler(int, siginfo_t *si, void *ptr)
{
   ucontext_t *uc = (ucontext_t *)ptr;

   cout << "si:" << si->si_addr << '
';
   cout << "ip:" << std::hex << uc->uc_mcontext.gregs[REG_RIP] << '
';
}

int main()
{
begin:
    cout.setf(std::ios::unitbuf);
    cout << &&begin << " ~ " << &&before << " ~ " << &&after << '
';

    struct sigaction s;
    s.sa_flags = SA_SIGINFO|SA_RESETHAND;
    s.sa_sigaction = handler;
    sigemptyset(&s.sa_mask);
    sigaction(SIGSEGV, &s, 0);

before:
    *causecrash = 0;
after:
    cout << "End.
";
}

By the way, GCC has this nasty habit of moving labels whose address is taken but not used in a control transfer operation (as far as it can tell). Compare:

$ g++ -O0 -W -Wall test.cc && ./a.out 
0x400a30 ~ 0x400acd ~ 0x400ada
si:0
ip:400ad4
Segmentation fault
$ g++ -O2 -W -Wall test.cc && ./a.out 
0x4009f0 ~ 0x4009f0 ~ 0x4009f0
si:0
ip:400ab4
Segmentation fault

See how all the labels are at the same address in the optimized version? That's going to trip up any attempt to, say, recover from the fault by adjusting the PC. IIRC there is a way to make GCC not do that, but I don't know what it is and wasn't able to find it in the manual.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

1.4m articles

1.4m replys

5 comments

57.0k users

...