Getting the address of the error that caused the UNIX signal

I'm interested in a signal handler that can identify the address of the instruction that caused the problem.

I know about siginfo_t and __builtin_return_address and it doesn't seem to work:

 #include <iostream> #include <signal.h> void handler (int, siginfo_t *, void *); int main () { begin: std :: cerr << &&begin << " ~ " << &&before << " ~ " << &&after << "\n"; 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.\n"; } void handler (int, siginfo_t *si, void *) { std :: cerr << "si:" << si -> si_addr << "\n"; std :: cerr << "At: " << __builtin_return_address (0) << "\n"; std :: cerr << "At: " << __builtin_return_address (1) << "\n"; std :: cerr << "At: " << __builtin_return_address (2) << "\n"; std :: cerr << "At: " << __builtin_return_address (3) << "\n"; std :: cerr << "At: " << __builtin_return_address (4) << "\n"; std :: cerr << "At: " << __builtin_return_address (5) << "\n"; } 

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 gives values โ€‹โ€‹somewhere between the specified labels.

I expected both to return &&before . Am I using these features correctly?

Tested on Linux 2.6.9-89.0.9.Elsmp and SunOS.

+6
c unix posix signals
source share
2 answers

The third argument to the handler set with SA_SIGINFO (declared as void * ) is a pointer to the ucontext_t structure. The content of this structure refers to the architecture and OS, and not to any standard, but includes the information you need. Here is the version of your program adapted for its use (specific Linux / x86-64, you will need #ifdef for each 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 << '\n'; cout << "ip:" << std::hex << uc->uc_mcontext.gregs[REG_RIP] << '\n'; } int main() { begin: cout.setf(std::ios::unitbuf); cout << &&begin << " ~ " << &&before << " ~ " << &&after << '\n'; 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.\n"; } 

By the way, GCC has this unpleasant habit of moving labels whose address is taken, but not used in the transfer of control operation (as much as possible). For comparison:

 $ 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 labels have the same address in the optimized version? This is going to disable any attempt to, say, restore due to a malfunction by setting up a PC. IIRC is a way to make GCC not do this, but I donโ€™t know what it is and could not find it in the manual.

+7
source share

siginfo_t will not work, because it contains the address of the memory to which it was accessed , and not the address of the instruction that it is.

Now __builtin_return_address interesting. On my machine, it returns some nonsense:

 0x40089f ~ 0x400935 ~ 0x40093f si:0 At: 0x7fe22916fc20 At: 0x7fe22915ad8e 

I have no idea why. But then I examined the core dump:

 (gdb) bt #0 0x00000000004009ff in handler(int, siginfo*, void*) () #1 <signal handler called> #2 0x0000000000400939 in main () 

As you can see, as in your case, the violation address is somewhere between the labels. This is easy to explain. Just look at the disassembly of main ():

 (gdb) disas Dump of assembler code for function main: ... ; the label is here: 0x0000000000400935 <+161>: mov -0x8(%rbp),%rax => 0x0000000000400939 <+165>: movl $0x0,(%rax) 0x000000000040093f <+171>: mov $0x400c32,%esi 

The designated operator consists of several instructions. The first loads the address into the RAX register. It succeeds because there is nothing wrong with that. This is the second one that accesses the address and is broken. This explains why the address in your trace is slightly different from the label address, although the code is likely to be different from my example. This all does not explain why __builtin_return_address gives stupidity in my case.

+1
source share

All Articles