1 //===-- sanitizer_unwind_posix.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, FreeBSD and OS X.
12 //===----------------------------------------------------------------------===//
13 
14 #include "sanitizer_platform.h"
15 #if SANITIZER_POSIX
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 //------------------------- SlowUnwindStack -----------------------------------
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
SanitizerInitializeUnwinder()50 void 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 
Unwind_GetIP(struct _Unwind_Context * ctx)84 uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
85 #ifdef __arm__
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 
97 struct UnwindTraceArg {
98   BufferedStackTrace *stack;
99   u32 max_depth;
100 };
101 
Unwind_Trace(struct _Unwind_Context * ctx,void * param)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 
SlowUnwindStack(uptr pc,u32 max_depth)111 void 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 
SlowUnwindStackWithContext(uptr pc,void * context,u32 max_depth)130 void 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_POSIX
159