sanitizer_unwind_linux_libcdep.cc revision 1.5
1//===-- sanitizer_unwind_linux_libcdep.cc ---------------------------------===// 2// 3// This file is distributed under the University of Illinois Open Source 4// License. See LICENSE.TXT for details. 5// 6//===----------------------------------------------------------------------===// 7// 8// This file contains the unwind.h-based (aka "slow") stack unwinding routines 9// available to the tools on Linux, Android, NetBSD, FreeBSD, and Solaris. 10//===----------------------------------------------------------------------===// 11 12#include "sanitizer_platform.h" 13#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ 14 SANITIZER_SOLARIS 15#include "sanitizer_common.h" 16#include "sanitizer_stacktrace.h" 17 18#if SANITIZER_ANDROID 19#include <dlfcn.h> // for dlopen() 20#endif 21 22#if SANITIZER_FREEBSD 23#define _GNU_SOURCE // to declare _Unwind_Backtrace() from <unwind.h> 24#endif 25#include <unwind.h> 26 27namespace __sanitizer { 28 29//------------------------- SlowUnwindStack ----------------------------------- 30 31typedef struct { 32 uptr absolute_pc; 33 uptr stack_top; 34 uptr stack_size; 35} backtrace_frame_t; 36 37extern "C" { 38typedef void *(*acquire_my_map_info_list_func)(); 39typedef void (*release_my_map_info_list_func)(void *map); 40typedef sptr (*unwind_backtrace_signal_arch_func)( 41 void *siginfo, void *sigcontext, void *map_info_list, 42 backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth); 43acquire_my_map_info_list_func acquire_my_map_info_list; 44release_my_map_info_list_func release_my_map_info_list; 45unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch; 46} // extern "C" 47 48#if SANITIZER_ANDROID 49void SanitizerInitializeUnwinder() { 50 if (AndroidGetApiLevel() >= ANDROID_LOLLIPOP_MR1) return; 51 52 // Pre-lollipop Android can not unwind through signal handler frames with 53 // libgcc unwinder, but it has a libcorkscrew.so library with the necessary 54 // workarounds. 55 void *p = dlopen("libcorkscrew.so", RTLD_LAZY); 56 if (!p) { 57 VReport(1, 58 "Failed to open libcorkscrew.so. You may see broken stack traces " 59 "in SEGV reports."); 60 return; 61 } 62 acquire_my_map_info_list = 63 (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list"); 64 release_my_map_info_list = 65 (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list"); 66 unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym( 67 p, "unwind_backtrace_signal_arch"); 68 if (!acquire_my_map_info_list || !release_my_map_info_list || 69 !unwind_backtrace_signal_arch) { 70 VReport(1, 71 "Failed to find one of the required symbols in libcorkscrew.so. " 72 "You may see broken stack traces in SEGV reports."); 73 acquire_my_map_info_list = 0; 74 unwind_backtrace_signal_arch = 0; 75 release_my_map_info_list = 0; 76 } 77} 78#endif 79 80#if defined(__arm__) && !SANITIZER_NETBSD 81// NetBSD uses dwarf EH 82#define UNWIND_STOP _URC_END_OF_STACK 83#define UNWIND_CONTINUE _URC_NO_REASON 84#else 85#define UNWIND_STOP _URC_NORMAL_STOP 86#define UNWIND_CONTINUE _URC_NO_REASON 87#endif 88 89uptr Unwind_GetIP(struct _Unwind_Context *ctx) { 90#if defined(__arm__) && !SANITIZER_MAC && !SANITIZER_NETBSD 91 uptr val; 92 _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE, 93 15 /* r15 = PC */, _UVRSD_UINT32, &val); 94 CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed"); 95 // Clear the Thumb bit. 96 return val & ~(uptr)1; 97#else 98 return (uptr)_Unwind_GetIP(ctx); 99#endif 100} 101 102struct UnwindTraceArg { 103 BufferedStackTrace *stack; 104 u32 max_depth; 105}; 106 107_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { 108 UnwindTraceArg *arg = (UnwindTraceArg*)param; 109 CHECK_LT(arg->stack->size, arg->max_depth); 110 uptr pc = Unwind_GetIP(ctx); 111 const uptr kPageSize = GetPageSizeCached(); 112 // Let's assume that any pointer in the 0th page (i.e. <0x1000 on i386 and 113 // x86_64) is invalid and stop unwinding here. If we're adding support for 114 // a platform where this isn't true, we need to reconsider this check. 115 if (pc < kPageSize) return UNWIND_STOP; 116 arg->stack->trace_buffer[arg->stack->size++] = pc; 117 if (arg->stack->size == arg->max_depth) return UNWIND_STOP; 118 return UNWIND_CONTINUE; 119} 120 121void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) { 122 CHECK_GE(max_depth, 2); 123 size = 0; 124 UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)}; 125 _Unwind_Backtrace(Unwind_Trace, &arg); 126 // We need to pop a few frames so that pc is on top. 127 uptr to_pop = LocatePcInTrace(pc); 128 // trace_buffer[0] belongs to the current function so we always pop it, 129 // unless there is only 1 frame in the stack trace (1 frame is always better 130 // than 0!). 131 // 1-frame stacks don't normally happen, but this depends on the actual 132 // unwinder implementation (libgcc, libunwind, etc) which is outside of our 133 // control. 134 if (to_pop == 0 && size > 1) 135 to_pop = 1; 136 PopStackFrames(to_pop); 137#if defined(__GNUC__) && defined(__sparc__) 138 // __builtin_return_address returns the address of the call instruction 139 // on the SPARC and not the return address, so we need to compensate. 140 trace_buffer[0] = GetNextInstructionPc(pc); 141#else 142 trace_buffer[0] = pc; 143#endif 144} 145 146void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, 147 u32 max_depth) { 148 CHECK_GE(max_depth, 2); 149 if (!unwind_backtrace_signal_arch) { 150 SlowUnwindStack(pc, max_depth); 151 return; 152 } 153 154 void *map = acquire_my_map_info_list(); 155 CHECK(map); 156 InternalMmapVector<backtrace_frame_t> frames(kStackTraceMax); 157 // siginfo argument appears to be unused. 158 sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map, 159 frames.data(), 160 /* ignore_depth */ 0, max_depth); 161 release_my_map_info_list(map); 162 if (res < 0) return; 163 CHECK_LE((uptr)res, kStackTraceMax); 164 165 size = 0; 166 // +2 compensate for libcorkscrew unwinder returning addresses of call 167 // instructions instead of raw return addresses. 168 for (sptr i = 0; i < res; ++i) 169 trace_buffer[size++] = frames[i].absolute_pc + 2; 170} 171 172} // namespace __sanitizer 173 174#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || 175 // SANITIZER_SOLARIS 176