sanitizer_unwind_linux_libcdep.cc revision 799172d60d32feb1acba1a6867f3a9c39a999e5c
1//===-- sanitizer_unwind_linux_libcdep.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, and FreeBSD. 12//===----------------------------------------------------------------------===// 13 14#include "sanitizer_platform.h" 15#if SANITIZER_FREEBSD || SANITIZER_LINUX 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 28namespace __sanitizer { 29 30//------------------------- SlowUnwindStack ----------------------------------- 31 32typedef struct { 33 uptr absolute_pc; 34 uptr stack_top; 35 uptr stack_size; 36} backtrace_frame_t; 37 38extern "C" { 39typedef void *(*acquire_my_map_info_list_func)(); 40typedef void (*release_my_map_info_list_func)(void *map); 41typedef 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); 44acquire_my_map_info_list_func acquire_my_map_info_list; 45release_my_map_info_list_func release_my_map_info_list; 46unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch; 47} // extern "C" 48 49#if SANITIZER_ANDROID 50void 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 84uptr Unwind_GetIP(struct _Unwind_Context *ctx) { 85#if defined(__arm__) && !SANITIZER_MAC 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 97struct UnwindTraceArg { 98 BufferedStackTrace *stack; 99 u32 max_depth; 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 111void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) { 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 130void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, 131 u32 max_depth) { 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_FREEBSD || SANITIZER_LINUX 159