]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cc
Merge clang 7.0.1 and several follow-up changes
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / sanitizer_common / sanitizer_unwind_linux_libcdep.cc
1 //===-- sanitizer_unwind_linux_libcdep.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, NetBSD, FreeBSD, and Solaris.
12 //===----------------------------------------------------------------------===//
13
14 #include "sanitizer_platform.h"
15 #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
16     SANITIZER_SOLARIS
17 #include "sanitizer_common.h"
18 #include "sanitizer_stacktrace.h"
19
20 #if SANITIZER_ANDROID
21 #include <dlfcn.h>  // for dlopen()
22 #endif
23
24 #if SANITIZER_FREEBSD
25 #define _GNU_SOURCE  // to declare _Unwind_Backtrace() from <unwind.h>
26 #endif
27 #include <unwind.h>
28
29 namespace __sanitizer {
30
31 //------------------------- SlowUnwindStack -----------------------------------
32
33 typedef struct {
34   uptr absolute_pc;
35   uptr stack_top;
36   uptr stack_size;
37 } backtrace_frame_t;
38
39 extern "C" {
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;
48 } // extern "C"
49
50 #if SANITIZER_ANDROID
51 void SanitizerInitializeUnwinder() {
52   if (AndroidGetApiLevel() >= ANDROID_LOLLIPOP_MR1) return;
53
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
56   // workarounds.
57   void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
58   if (!p) {
59     VReport(1,
60             "Failed to open libcorkscrew.so. You may see broken stack traces "
61             "in SEGV reports.");
62     return;
63   }
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) {
72     VReport(1,
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;
78   }
79 }
80 #endif
81
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
86 #else
87 #define UNWIND_STOP _URC_NORMAL_STOP
88 #define UNWIND_CONTINUE _URC_NO_REASON
89 #endif
90
91 uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
92 #if defined(__arm__) && !SANITIZER_MAC
93   uptr val;
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;
99 #else
100   return _Unwind_GetIP(ctx);
101 #endif
102 }
103
104 struct UnwindTraceArg {
105   BufferedStackTrace *stack;
106   u32 max_depth;
107 };
108
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;
121 }
122
123 void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) {
124   CHECK_GE(max_depth, 2);
125   size = 0;
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
132   // than 0!).
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
135   // control.
136   if (to_pop == 0 && size > 1)
137     to_pop = 1;
138   PopStackFrames(to_pop);
139   trace_buffer[0] = pc;
140 }
141
142 void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
143                                                     u32 max_depth) {
144   CHECK_GE(max_depth, 2);
145   if (!unwind_backtrace_signal_arch) {
146     SlowUnwindStack(pc, max_depth);
147     return;
148   }
149
150   void *map = acquire_my_map_info_list();
151   CHECK(map);
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,
155                                           frames.data(),
156                                           /* ignore_depth */ 0, max_depth);
157   release_my_map_info_list(map);
158   if (res < 0) return;
159   CHECK_LE((uptr)res, kStackTraceMax);
160
161   size = 0;
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;
166 }
167
168 }  // namespace __sanitizer
169
170 #endif  // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||
171         // SANITIZER_SOLARIS