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