libunwind while unwinding a NULL IP
I am trying to use libunwind to crawl stacktraces from within my signal handler. The problem is that the frame with an IP of 0 stops unwinding process.
example:
void (*fp)() = NULL;
fp();
The testcase is both clang and g++ friendly. You can see from the output of the posted testcase that backtrace does this job.
Can we somehow force libunwind (unw_step) to ignore NULL IP and keep unwinding?
Here is the testcase:
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include <iostream>
#include <signal.h>
#include <execinfo.h>
#include <cassert>
void gen_segv_bad_ip(){
printf("\nInside gen_segv_bad_ip --> %p", gen_segv_bad_ip); fflush(stdout);
int (*fp)(int) = (int (*)(int)) NULL;
//Bad IP!!!
int g = fp(100);
}
void bail(){
printf("\nInside bail --> %p", bail); fflush(stdout);
gen_segv_bad_ip();
}
void foo3(){ printf("\nInside foo3 --> %p", foo3); fflush(stdout); bail(); }
void foo2(){ printf("\nInside foo2 --> %p", foo2); fflush(stdout); foo3(); }
void foo1(){ printf("\nInside foo1 --> %p", foo1); fflush(stdout); foo2(); }
void foo(){ printf("\nInside foo --> %p", foo); fflush(stdout); foo1(); }
/* Modified example given by libunwind */
void show_backtrace (unw_cursor_t *cursor) {
printf("\n\nInside show_backtrace --> %p\n", show_backtrace);
fflush(stdout);
unw_word_t ip, sp;
char buf[1000];
size_t len = sizeof(buf);
unw_word_t off;
do {
unw_get_reg(cursor, UNW_REG_IP, &ip);
unw_get_reg(cursor, UNW_REG_SP, &sp);
printf("ip = %llX sp = %llX ",ip, sp);
buf[0] = '\0';
unw_get_proc_name(cursor, buf, len, &off);
std::cout << "Procedure name returned --> " << buf << std::endl;
} while (unw_step(cursor) > 0);
printf("Leaving show_backtrace\n");
}
void segv_helper(void *uc){
printf("\nInside segv_helper --> %p\n", segv_helper);
fflush(stdout);
unw_cursor_t cursor;
unw_word_t ip, sp;
std::cout << "Getting local context " << std::endl;
int a = unw_getcontext((unw_context_t*) uc);
std::cout << "unw_init_local returned " << unw_init_local(&cursor, (unw_context_t*) uc) << std::endl;
unw_get_reg(&cursor, UNW_REG_IP, &ip);
unw_get_reg(&cursor, UNW_REG_SP, &sp);
printf("IP = %llX SP = %llX", ip, sp);
fflush(stdout);
show_backtrace(&cursor);
printf("Leaving segv_helper\n");
fflush(stdout);
}
void segv_handler (int signum, siginfo_t* info, void *old_context){
//Show stacktrace using backtrace....
void *buff[300];
int size = 300, nptrs;
char **pbuff;
nptrs = backtrace((void**) &buff, size);
printf("\n\nbacktrace() returned %d addresses\n", nptrs);
assert(NULL != (pbuff = backtrace_symbols(buff, nptrs)));
for (int i = 0; i < nptrs; i++) {
printf ("%s\n", pbuff[i]);
fflush(stdout);
}
free(pbuff);
printf("\nInside segv_handler --> %p\n", segv_handler);
fflush(stdout);
segv_helper( old_context );
std::cout << "\n\nBAILING...\n";
exit(1);
}
void test_signal_handler(int signum){
std::cout << "\nInside test_signal_handler. I should not be here...\n";
signal(SIGSEGV, SIG_DFL);
}
int main()
{
//Install signal handler...
struct sigaction new_sigaction, old_sigaction;
new_sigaction.sa_sigaction = segv_handler;
new_sigaction.sa_flags = SA_SIGINFO;
sigemptyset (&new_sigaction.sa_mask);
sigaddset(&new_sigaction.sa_mask, SIGSEGV);
sigaction (SIGSEGV, NULL, &new_sigaction);
if (old_sigaction.sa_handler != SIG_IGN){
new_sigaction.sa_handler = test_signal_handler;
new_sigaction.sa_sigaction = segv_handler;
sigaction (SIGSEGV, &new_sigaction, &old_sigaction);
}
printf("\nInside main --> %p", main);
foo();
return 0;
}
OS X Mountain Lion (10.8)