|
#include <string.h> |
|
#include <signal.h> |
|
|
|
#define UNW_LOCAL_ONLY |
|
#include <libunwind.h> |
|
|
|
#include <cxxabi.h> |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <unistd.h> |
|
#include <sys/mman.h> |
|
|
|
// https://fossies.org/linux/ruby/vm_dump.c |
|
int backtrace(void **trace, int size) |
|
{ |
|
unw_cursor_t cursor; unw_context_t uc; |
|
unw_word_t ip; |
|
int n = 0; |
|
|
|
unw_getcontext(&uc); |
|
unw_init_local(&cursor, &uc); |
|
|
|
while (unw_step(&cursor) > 0) { |
|
unw_get_reg(&cursor, UNW_REG_IP, &ip); |
|
trace[n++] = (void *)ip; |
|
|
|
char buf[256] = {}; |
|
unw_get_proc_name(&cursor, buf, sizeof(buf), &ip); |
|
printf("%2d: %s\n", n, buf); |
|
|
|
#if defined(__APPLE__) |
|
if (strncmp("_sigtramp", buf, sizeof("_sigtramp")) == 0) { |
|
goto darwin_sigtramp; |
|
} |
|
#endif |
|
} |
|
return n; |
|
|
|
#if defined(__APPLE__) |
|
darwin_sigtramp: |
|
|
|
/* darwin's bundled libunwind doesn't support signal trampoline */ |
|
{ |
|
ucontext_t *uctx; |
|
/* get _sigtramp's ucontext_t and set values to cursor |
|
* http://www.opensource.apple.com/source/Libc/Libc-825.25/i386/sys/_sigtramp.s |
|
* http://www.opensource.apple.com/source/libunwind/libunwind-35.1/src/unw_getcontext.s |
|
*/ |
|
unw_get_reg(&cursor, UNW_X86_64_RBX, &ip); |
|
uctx = (ucontext_t *)ip; |
|
|
|
# if __DARWIN_UNIX03 |
|
# define MCTX_SS_REG(reg) __ss.__##reg |
|
# else |
|
# define MCTX_SS_REG(reg) ss.reg |
|
# endif |
|
unw_set_reg(&cursor, UNW_X86_64_RAX, uctx->uc_mcontext->MCTX_SS_REG(rax)); |
|
unw_set_reg(&cursor, UNW_X86_64_RBX, uctx->uc_mcontext->MCTX_SS_REG(rbx)); |
|
unw_set_reg(&cursor, UNW_X86_64_RCX, uctx->uc_mcontext->MCTX_SS_REG(rcx)); |
|
unw_set_reg(&cursor, UNW_X86_64_RDX, uctx->uc_mcontext->MCTX_SS_REG(rdx)); |
|
unw_set_reg(&cursor, UNW_X86_64_RDI, uctx->uc_mcontext->MCTX_SS_REG(rdi)); |
|
unw_set_reg(&cursor, UNW_X86_64_RSI, uctx->uc_mcontext->MCTX_SS_REG(rsi)); |
|
unw_set_reg(&cursor, UNW_X86_64_RBP, uctx->uc_mcontext->MCTX_SS_REG(rbp)); |
|
unw_set_reg(&cursor, UNW_X86_64_RSP, 8+(uctx->uc_mcontext->MCTX_SS_REG(rsp))); |
|
unw_set_reg(&cursor, UNW_X86_64_R8, uctx->uc_mcontext->MCTX_SS_REG(r8)); |
|
unw_set_reg(&cursor, UNW_X86_64_R9, uctx->uc_mcontext->MCTX_SS_REG(r9)); |
|
unw_set_reg(&cursor, UNW_X86_64_R10, uctx->uc_mcontext->MCTX_SS_REG(r10)); |
|
unw_set_reg(&cursor, UNW_X86_64_R11, uctx->uc_mcontext->MCTX_SS_REG(r11)); |
|
unw_set_reg(&cursor, UNW_X86_64_R12, uctx->uc_mcontext->MCTX_SS_REG(r12)); |
|
unw_set_reg(&cursor, UNW_X86_64_R13, uctx->uc_mcontext->MCTX_SS_REG(r13)); |
|
unw_set_reg(&cursor, UNW_X86_64_R14, uctx->uc_mcontext->MCTX_SS_REG(r14)); |
|
unw_set_reg(&cursor, UNW_X86_64_R15, uctx->uc_mcontext->MCTX_SS_REG(r15)); |
|
|
|
ip = uctx->uc_mcontext->MCTX_SS_REG(rip); |
|
|
|
/* There are 4 cases for SEGV: |
|
* (1) called invalid address |
|
* (2) read or write invalid address |
|
* (3) received signal |
|
* |
|
* Detail: |
|
* (1) called invalid address |
|
* In this case, saved ip is invalid address. |
|
* It needs to just save the address for the information, |
|
* skip the frame, and restore the frame calling the |
|
* invalid address from %rsp. |
|
* The problem is how to check whether the ip is valid or not. |
|
* This code uses mincore(2) and assume the address's page is |
|
* incore/referenced or not reflects the problem. |
|
* Note that High Sierra's mincore(2) may return -128. |
|
* (2) read or write invalid address |
|
* saved ip is valid. just restart backtracing. |
|
* (3) received signal in user space |
|
* Same as (2). |
|
* (4) received signal in kernel |
|
* In this case saved ip points just after syscall, but registers are |
|
* already overwritten by kernel. To fix register consistency, |
|
* skip libc's kernel wrapper. |
|
* To detect this case, just previous two bytes of ip is "\x0f\x05", |
|
* syscall instruction of x86_64. |
|
*/ |
|
char vec[1]; |
|
int r = mincore((const void *)ip, 1, vec); |
|
if (r || vec[0] <= 0 || memcmp((const char *)ip-2, "\x0f\x05", 2) == 0) { |
|
// if segv is caused by invalid call or signal received in syscall |
|
// the frame is invalid; skip |
|
trace[n++] = (void *)ip; |
|
ip = *(unw_word_t*)uctx->uc_mcontext->MCTX_SS_REG(rsp); |
|
} |
|
|
|
trace[n++] = (void *)ip; |
|
unw_set_reg(&cursor, UNW_REG_IP, ip); |
|
|
|
} |
|
|
|
while (unw_step(&cursor) > 0) { |
|
|
|
unw_get_reg(&cursor, UNW_REG_IP, &ip); |
|
trace[n++] = (void *)ip; |
|
|
|
char buf[256] = {}; |
|
unw_get_proc_name(&cursor, buf, sizeof(buf), &ip); |
|
printf("%2d: %s\n", n, buf); // // doesn't get here |
|
} |
|
|
|
return n; |
|
#endif // __APPLE__ |
|
} |
|
|
|
|
|
void cause_segfault() |
|
{ |
|
// produces a well behaved callstack from cause_segfault() to main() |
|
// int * p = (int*)0x12345678; |
|
// *p = 0; |
|
|
|
// Doesn't produce a proper callstack. Stops at "_sigtramp" |
|
printf("STR: %s\n", (char*)1); |
|
} |
|
|
|
int fac(int n) |
|
{ |
|
if ( n == 0 ) { |
|
cause_segfault(); |
|
return 1; |
|
} else { |
|
return n*fac(n-1); |
|
} |
|
} |
|
|
|
// This array contains the default behavior for each signal. |
|
static const int SIGNAL_MAX = 64; |
|
static struct sigaction sigdfl[SIGNAL_MAX]; |
|
|
|
static void Handler(const int signum, siginfo_t *const si, void *const sc) |
|
{ |
|
sigaction(signum, &sigdfl[signum], NULL); // to avoid infinite recursion |
|
|
|
void* callstack[64]; |
|
backtrace(callstack, 64); |
|
} |
|
|
|
void InstallOnSignal(int signum) |
|
{ |
|
struct sigaction sa; |
|
memset(&sa, 0, sizeof(sa)); |
|
sigemptyset(&sa.sa_mask); |
|
sa.sa_sigaction = Handler; |
|
sa.sa_flags = SA_SIGINFO; |
|
|
|
// The current (default) behavior is stored in sigdfl. |
|
sigaction(signum, &sa, &sigdfl[signum]); |
|
} |
|
|
|
void InstallHandler() |
|
{ |
|
InstallOnSignal(SIGSEGV); |
|
InstallOnSignal(SIGBUS); |
|
InstallOnSignal(SIGTRAP); |
|
InstallOnSignal(SIGILL); |
|
InstallOnSignal(SIGABRT); |
|
} |
|
|
|
int main() |
|
{ |
|
InstallHandler(); |
|
fac(10); |
|
return 0; |
|
} |