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 
27 namespace __sanitizer {
28 
29 //------------------------- SlowUnwindStack -----------------------------------
30 
31 typedef struct {
32   uptr absolute_pc;
33   uptr stack_top;
34   uptr stack_size;
35 } backtrace_frame_t;
36 
37 extern "C" {
38 typedef void *(*acquire_my_map_info_list_func)();
39 typedef void (*release_my_map_info_list_func)(void *map);
40 typedef 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);
43 acquire_my_map_info_list_func acquire_my_map_info_list;
44 release_my_map_info_list_func release_my_map_info_list;
45 unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;
46 } // extern "C"
47 
48 #if SANITIZER_ANDROID
SanitizerInitializeUnwinder()49 void 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 
Unwind_GetIP(struct _Unwind_Context * ctx)89 uptr 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 
102 struct UnwindTraceArg {
103   BufferedStackTrace *stack;
104   u32 max_depth;
105 };
106 
Unwind_Trace(struct _Unwind_Context * ctx,void * param)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 
SlowUnwindStack(uptr pc,u32 max_depth)121 void 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 
SlowUnwindStackWithContext(uptr pc,void * context,u32 max_depth)146 void 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