1 //===-- sanitizer_unwind_linux_libcdep.cc ---------------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file contains the unwind.h-based (aka "slow") stack unwinding routines
11 // available to the tools on Linux, Android, NetBSD, FreeBSD, and Solaris.
12 //===----------------------------------------------------------------------===//
14 #include "sanitizer_platform.h"
15 #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
17 #include "sanitizer_common.h"
18 #include "sanitizer_stacktrace.h"
21 #include <dlfcn.h> // for dlopen()
25 #define _GNU_SOURCE // to declare _Unwind_Backtrace() from <unwind.h>
29 namespace __sanitizer {
31 //------------------------- SlowUnwindStack -----------------------------------
40 typedef void *(*acquire_my_map_info_list_func)();
41 typedef void (*release_my_map_info_list_func)(void *map);
42 typedef sptr (*unwind_backtrace_signal_arch_func)(
43 void *siginfo, void *sigcontext, void *map_info_list,
44 backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth);
45 acquire_my_map_info_list_func acquire_my_map_info_list;
46 release_my_map_info_list_func release_my_map_info_list;
47 unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;
51 void SanitizerInitializeUnwinder() {
52 if (AndroidGetApiLevel() >= ANDROID_LOLLIPOP_MR1) return;
54 // Pre-lollipop Android can not unwind through signal handler frames with
55 // libgcc unwinder, but it has a libcorkscrew.so library with the necessary
57 void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
60 "Failed to open libcorkscrew.so. You may see broken stack traces "
64 acquire_my_map_info_list =
65 (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");
66 release_my_map_info_list =
67 (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");
68 unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(
69 p, "unwind_backtrace_signal_arch");
70 if (!acquire_my_map_info_list || !release_my_map_info_list ||
71 !unwind_backtrace_signal_arch) {
73 "Failed to find one of the required symbols in libcorkscrew.so. "
74 "You may see broken stack traces in SEGV reports.");
75 acquire_my_map_info_list = 0;
76 unwind_backtrace_signal_arch = 0;
77 release_my_map_info_list = 0;
82 #if defined(__arm__) && !SANITIZER_NETBSD
83 // NetBSD uses dwarf EH
84 #define UNWIND_STOP _URC_END_OF_STACK
85 #define UNWIND_CONTINUE _URC_NO_REASON
87 #define UNWIND_STOP _URC_NORMAL_STOP
88 #define UNWIND_CONTINUE _URC_NO_REASON
91 uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
92 #if defined(__arm__) && !SANITIZER_MAC
94 _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
95 15 /* r15 = PC */, _UVRSD_UINT32, &val);
96 CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
97 // Clear the Thumb bit.
98 return val & ~(uptr)1;
100 return _Unwind_GetIP(ctx);
104 struct UnwindTraceArg {
105 BufferedStackTrace *stack;
109 _Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
110 UnwindTraceArg *arg = (UnwindTraceArg*)param;
111 CHECK_LT(arg->stack->size, arg->max_depth);
112 uptr pc = Unwind_GetIP(ctx);
113 const uptr kPageSize = GetPageSizeCached();
114 // Let's assume that any pointer in the 0th page (i.e. <0x1000 on i386 and
115 // x86_64) is invalid and stop unwinding here. If we're adding support for
116 // a platform where this isn't true, we need to reconsider this check.
117 if (pc < kPageSize) return UNWIND_STOP;
118 arg->stack->trace_buffer[arg->stack->size++] = pc;
119 if (arg->stack->size == arg->max_depth) return UNWIND_STOP;
120 return UNWIND_CONTINUE;
123 void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) {
124 CHECK_GE(max_depth, 2);
126 UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
127 _Unwind_Backtrace(Unwind_Trace, &arg);
128 // We need to pop a few frames so that pc is on top.
129 uptr to_pop = LocatePcInTrace(pc);
130 // trace_buffer[0] belongs to the current function so we always pop it,
131 // unless there is only 1 frame in the stack trace (1 frame is always better
133 // 1-frame stacks don't normally happen, but this depends on the actual
134 // unwinder implementation (libgcc, libunwind, etc) which is outside of our
136 if (to_pop == 0 && size > 1)
138 PopStackFrames(to_pop);
139 trace_buffer[0] = pc;
142 void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
144 CHECK_GE(max_depth, 2);
145 if (!unwind_backtrace_signal_arch) {
146 SlowUnwindStack(pc, max_depth);
150 void *map = acquire_my_map_info_list();
152 InternalMmapVector<backtrace_frame_t> frames(kStackTraceMax);
153 // siginfo argument appears to be unused.
154 sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map,
156 /* ignore_depth */ 0, max_depth);
157 release_my_map_info_list(map);
159 CHECK_LE((uptr)res, kStackTraceMax);
162 // +2 compensate for libcorkscrew unwinder returning addresses of call
163 // instructions instead of raw return addresses.
164 for (sptr i = 0; i < res; ++i)
165 trace_buffer[size++] = frames[i].absolute_pc + 2;
168 } // namespace __sanitizer
170 #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||