How to get fullstacktrace using _Unwind_Backtrace on SIGSEGV
You want to backtrace from the signal triggering function, but you backtrace from the signal handler function. That's two different stacks. (Note, the SA_ONSTACK flag in sigaction is irrelevant to your question.)
To find the stack pointer of the of the triggering function, use the third parameter of the handler, i.e. void *rserved. You can reference to the answer in this question: Getting the saved instruction pointer address from a signal handler
If you want to use particularly _Unwind_Context()
, you can do it like this (the code is 32-bit ARM specific):
struct BacktraceState { const ucontext_t* signal_ucontext; size_t address_count = 0; static const size_t address_count_max = 30; uintptr_t addresses[address_count_max] = {}; BacktraceState(const ucontext_t* ucontext) : signal_ucontext(ucontext) {} bool AddAddress(uintptr_t ip) { // No more space in the storage. Fail. if (address_count >= address_count_max) return false; // Reset the Thumb bit, if it is set. const uintptr_t thumb_bit = 1; ip &= ~thumb_bit; // Ignore null addresses. // They sometimes happen when using _Unwind_Backtrace() // with the compiler optimizations, // when the Link Register is overwritten by the inner // stack frames. if (ip == 0) return true; // Ignore duplicate addresses. // They sometimes happen when using _Unwind_Backtrace() // with the compiler optimizations, // because we both add the second address from the Link Register // in ProcessRegisters() and receive the same address // in UnwindBacktraceCallback(). if (address_count > 0 && ip == addresses[address_count - 1]) return true; // Finally add the address to the storage. addresses[address_count++] = ip; return true; }};void ProcessRegisters( _Unwind_Context* unwind_context, BacktraceState* state) { assert(state); assert(unwind_context); const ucontext_t* signal_ucontext = state->signal_ucontext; assert(signal_ucontext); const sigcontext* signal_mcontext = &(signal_ucontext->uc_mcontext); assert(signal_mcontext); _Unwind_SetGR(unwind_context, REG_R0, signal_mcontext->arm_r0); _Unwind_SetGR(unwind_context, REG_R1, signal_mcontext->arm_r1); _Unwind_SetGR(unwind_context, REG_R2, signal_mcontext->arm_r2); _Unwind_SetGR(unwind_context, REG_R3, signal_mcontext->arm_r3); _Unwind_SetGR(unwind_context, REG_R4, signal_mcontext->arm_r4); _Unwind_SetGR(unwind_context, REG_R5, signal_mcontext->arm_r5); _Unwind_SetGR(unwind_context, REG_R6, signal_mcontext->arm_r6); _Unwind_SetGR(unwind_context, REG_R7, signal_mcontext->arm_r7); _Unwind_SetGR(unwind_context, REG_R8, signal_mcontext->arm_r8); _Unwind_SetGR(unwind_context, REG_R9, signal_mcontext->arm_r9); _Unwind_SetGR(unwind_context, REG_R10, signal_mcontext->arm_r10); _Unwind_SetGR(unwind_context, REG_R11, signal_mcontext->arm_fp); _Unwind_SetGR(unwind_context, REG_R12, signal_mcontext->arm_ip); _Unwind_SetGR(unwind_context, REG_R13, signal_mcontext->arm_sp); _Unwind_SetGR(unwind_context, REG_R14, signal_mcontext->arm_lr); _Unwind_SetGR(unwind_context, REG_R15, signal_mcontext->arm_pc); // Program Counter register aka Instruction Pointer will contain // the address of the instruction where the crash happened. // UnwindBacktraceCallback() will not supply us with it. state->AddAddress(signal_mcontext->arm_pc); // UnwindBacktraceCallback() does not always supply us with // the return address of the frame where the crash happened. // Sometimes Link Register will contain this address // (noticed when compiling with Clang without optimization), // but LR may also contain address of some previously visitied frame // (noticed when compiling with GCC without optimization), // or LR may contain null address // (noticed when compiling with Clang with optimization). // These heuristics are unreliable.#if __clang__ state->AddAddress(signal_mcontext->arm_lr);#endif}_Unwind_Reason_Code UnwindBacktraceCallback( struct _Unwind_Context* unwind_context, void* state_voidp) { assert(unwind_context); assert(state_voidp); BacktraceState* state = (BacktraceState*)state_voidp; assert(state); // On the first UnwindBacktraceCallback() call, // set registers to _Unwind_Context and BacktraceState. if (state->address_count == 0) { ProcessRegisters(unwind_context, state); return _URC_NO_REASON; } uintptr_t ip = _Unwind_GetIP(unwind_context); bool ok = state->AddAddress(ip); if (!ok) return _URC_END_OF_STACK; return _URC_NO_REASON;}void CaptureBacktrace(BacktraceState* state) { assert(state); _Unwind_Backtrace(UnwindBacktraceCallback, state);}void SigActionHandler(int sig, siginfo_t* info, void* ucontext) { const ucontext_t* signal_ucontext = (const ucontext_t*)ucontext; assert(signal_ucontext); BacktraceState backtrace_state(signal_ucontext); CaptureBacktrace(&backtrace_state); // Do something with the backtrace - print, save to file, etc.}
But I am advising you to not use _Unwind_Context()
, but instead use precompiled libunwind
for 32-bit ARM, bundled with modern Android NDKs (at sources/cxx-stl/llvm-libc++/libs/armeabi-v7a/libunwind.a
). You will have to use use libc++ (LLVM STL). How to do it, is demonstrated in this my answer:
https://stackoverflow.com/a/50027799/1016580
If you use libstdc++ (GNU STL), use the Dar Hoo's solution:
better you use backtrace and backtrace_symbols_fd to get a stacktrace from a signal handler.