Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 1 | //===-- sanitizer_unwind_posix.cc ----------------------------------------===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
| 10 | // This file contains the unwind.h-based (aka "slow") stack unwinding routines |
| 11 | // available to the tools on Linux, Android, FreeBSD and OS X. |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #include "sanitizer_platform.h" |
| 15 | #if SANITIZER_POSIX |
| 16 | #include "sanitizer_common.h" |
| 17 | #include "sanitizer_stacktrace.h" |
| 18 | |
| 19 | #if SANITIZER_ANDROID |
| 20 | #include <dlfcn.h> // for dlopen() |
| 21 | #endif |
| 22 | |
| 23 | #if SANITIZER_FREEBSD |
| 24 | #define _GNU_SOURCE // to declare _Unwind_Backtrace() from <unwind.h> |
| 25 | #endif |
| 26 | #include <unwind.h> |
| 27 | |
| 28 | namespace __sanitizer { |
| 29 | |
| 30 | //------------------------- SlowUnwindStack ----------------------------------- |
| 31 | |
| 32 | typedef struct { |
| 33 | uptr absolute_pc; |
| 34 | uptr stack_top; |
| 35 | uptr stack_size; |
| 36 | } backtrace_frame_t; |
| 37 | |
| 38 | extern "C" { |
| 39 | typedef void *(*acquire_my_map_info_list_func)(); |
| 40 | typedef void (*release_my_map_info_list_func)(void *map); |
| 41 | typedef sptr (*unwind_backtrace_signal_arch_func)( |
| 42 | void *siginfo, void *sigcontext, void *map_info_list, |
| 43 | backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth); |
| 44 | acquire_my_map_info_list_func acquire_my_map_info_list; |
| 45 | release_my_map_info_list_func release_my_map_info_list; |
| 46 | unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch; |
| 47 | } // extern "C" |
| 48 | |
| 49 | #if SANITIZER_ANDROID |
| 50 | void SanitizerInitializeUnwinder() { |
| 51 | void *p = dlopen("libcorkscrew.so", RTLD_LAZY); |
| 52 | if (!p) { |
| 53 | VReport(1, |
| 54 | "Failed to open libcorkscrew.so. You may see broken stack traces " |
| 55 | "in SEGV reports."); |
| 56 | return; |
| 57 | } |
| 58 | acquire_my_map_info_list = |
| 59 | (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list"); |
| 60 | release_my_map_info_list = |
| 61 | (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list"); |
| 62 | unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym( |
| 63 | p, "unwind_backtrace_signal_arch"); |
| 64 | if (!acquire_my_map_info_list || !release_my_map_info_list || |
| 65 | !unwind_backtrace_signal_arch) { |
| 66 | VReport(1, |
| 67 | "Failed to find one of the required symbols in libcorkscrew.so. " |
| 68 | "You may see broken stack traces in SEGV reports."); |
| 69 | acquire_my_map_info_list = 0; |
| 70 | unwind_backtrace_signal_arch = 0; |
| 71 | release_my_map_info_list = 0; |
| 72 | } |
| 73 | } |
| 74 | #endif |
| 75 | |
| 76 | #ifdef __arm__ |
| 77 | #define UNWIND_STOP _URC_END_OF_STACK |
| 78 | #define UNWIND_CONTINUE _URC_NO_REASON |
| 79 | #else |
| 80 | #define UNWIND_STOP _URC_NORMAL_STOP |
| 81 | #define UNWIND_CONTINUE _URC_NO_REASON |
| 82 | #endif |
| 83 | |
| 84 | uptr Unwind_GetIP(struct _Unwind_Context *ctx) { |
| 85 | #ifdef __arm__ |
| 86 | uptr val; |
| 87 | _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE, |
| 88 | 15 /* r15 = PC */, _UVRSD_UINT32, &val); |
| 89 | CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed"); |
| 90 | // Clear the Thumb bit. |
| 91 | return val & ~(uptr)1; |
| 92 | #else |
| 93 | return _Unwind_GetIP(ctx); |
| 94 | #endif |
| 95 | } |
| 96 | |
| 97 | struct UnwindTraceArg { |
| 98 | BufferedStackTrace *stack; |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 99 | u32 max_depth; |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 100 | }; |
| 101 | |
| 102 | _Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { |
| 103 | UnwindTraceArg *arg = (UnwindTraceArg*)param; |
| 104 | CHECK_LT(arg->stack->size, arg->max_depth); |
| 105 | uptr pc = Unwind_GetIP(ctx); |
| 106 | arg->stack->trace_buffer[arg->stack->size++] = pc; |
| 107 | if (arg->stack->size == arg->max_depth) return UNWIND_STOP; |
| 108 | return UNWIND_CONTINUE; |
| 109 | } |
| 110 | |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 111 | void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) { |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 112 | CHECK_GE(max_depth, 2); |
| 113 | size = 0; |
| 114 | UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)}; |
| 115 | _Unwind_Backtrace(Unwind_Trace, &arg); |
| 116 | // We need to pop a few frames so that pc is on top. |
| 117 | uptr to_pop = LocatePcInTrace(pc); |
| 118 | // trace_buffer[0] belongs to the current function so we always pop it, |
| 119 | // unless there is only 1 frame in the stack trace (1 frame is always better |
| 120 | // than 0!). |
| 121 | // 1-frame stacks don't normally happen, but this depends on the actual |
| 122 | // unwinder implementation (libgcc, libunwind, etc) which is outside of our |
| 123 | // control. |
| 124 | if (to_pop == 0 && size > 1) |
| 125 | to_pop = 1; |
| 126 | PopStackFrames(to_pop); |
| 127 | trace_buffer[0] = pc; |
| 128 | } |
| 129 | |
| 130 | void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, |
Stephen Hines | 86277eb | 2015-03-23 12:06:32 -0700 | [diff] [blame] | 131 | u32 max_depth) { |
Stephen Hines | 6d18623 | 2014-11-26 17:56:19 -0800 | [diff] [blame] | 132 | CHECK_GE(max_depth, 2); |
| 133 | if (!unwind_backtrace_signal_arch) { |
| 134 | SlowUnwindStack(pc, max_depth); |
| 135 | return; |
| 136 | } |
| 137 | |
| 138 | void *map = acquire_my_map_info_list(); |
| 139 | CHECK(map); |
| 140 | InternalScopedBuffer<backtrace_frame_t> frames(kStackTraceMax); |
| 141 | // siginfo argument appears to be unused. |
| 142 | sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map, |
| 143 | frames.data(), |
| 144 | /* ignore_depth */ 0, max_depth); |
| 145 | release_my_map_info_list(map); |
| 146 | if (res < 0) return; |
| 147 | CHECK_LE((uptr)res, kStackTraceMax); |
| 148 | |
| 149 | size = 0; |
| 150 | // +2 compensate for libcorkscrew unwinder returning addresses of call |
| 151 | // instructions instead of raw return addresses. |
| 152 | for (sptr i = 0; i < res; ++i) |
| 153 | trace_buffer[size++] = frames[i].absolute_pc + 2; |
| 154 | } |
| 155 | |
| 156 | } // namespace __sanitizer |
| 157 | |
| 158 | #endif // SANITIZER_POSIX |