|  | #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; | 
        
          |  | } |