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