]>
Commit | Line | Data |
---|---|---|
b667dd70 | 1 | //===-- sanitizer_unwind_linux_libcdep.cpp --------------------------------===// |
866e32ad | 2 | // |
b667dd70 ML |
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 | |
866e32ad KS |
6 | // |
7 | //===----------------------------------------------------------------------===// | |
8 | // | |
9 | // This file contains the unwind.h-based (aka "slow") stack unwinding routines | |
eac97531 | 10 | // available to the tools on Linux, Android, NetBSD, FreeBSD, and Solaris. |
866e32ad KS |
11 | //===----------------------------------------------------------------------===// |
12 | ||
13 | #include "sanitizer_platform.h" | |
eac97531 ML |
14 | #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ |
15 | SANITIZER_SOLARIS | |
866e32ad KS |
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 | ||
3ca75cd5 ML |
30 | namespace { |
31 | ||
b667dd70 | 32 | //---------------------------- UnwindSlow -------------------------------------- |
866e32ad KS |
33 | |
34 | typedef struct { | |
35 | uptr absolute_pc; | |
36 | uptr stack_top; | |
37 | uptr stack_size; | |
38 | } backtrace_frame_t; | |
39 | ||
40 | extern "C" { | |
41 | typedef void *(*acquire_my_map_info_list_func)(); | |
42 | typedef void (*release_my_map_info_list_func)(void *map); | |
43 | typedef 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); | |
46 | acquire_my_map_info_list_func acquire_my_map_info_list; | |
47 | release_my_map_info_list_func release_my_map_info_list; | |
48 | unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch; | |
49 | } // extern "C" | |
50 | ||
5d3805fc JJ |
51 | #if defined(__arm__) && !SANITIZER_NETBSD |
52 | // NetBSD uses dwarf EH | |
866e32ad KS |
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 | ||
60 | uptr Unwind_GetIP(struct _Unwind_Context *ctx) { | |
696d846a | 61 | #if defined(__arm__) && !SANITIZER_MAC |
866e32ad KS |
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 | |
b667dd70 | 69 | return (uptr)_Unwind_GetIP(ctx); |
866e32ad KS |
70 | #endif |
71 | } | |
72 | ||
73 | struct UnwindTraceArg { | |
c5be964a | 74 | BufferedStackTrace *stack; |
696d846a | 75 | u32 max_depth; |
866e32ad KS |
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); | |
10189819 MO |
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; | |
c5be964a | 87 | arg->stack->trace_buffer[arg->stack->size++] = pc; |
866e32ad KS |
88 | if (arg->stack->size == arg->max_depth) return UNWIND_STOP; |
89 | return UNWIND_CONTINUE; | |
90 | } | |
91 | ||
3ca75cd5 ML |
92 | } // namespace |
93 | ||
94 | #if SANITIZER_ANDROID | |
95 | void 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 | ||
b667dd70 | 126 | void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) { |
866e32ad KS |
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); | |
696d846a MO |
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. | |
c5be964a | 139 | if (to_pop == 0 && size > 1) |
866e32ad KS |
140 | to_pop = 1; |
141 | PopStackFrames(to_pop); | |
017abbe3 EB |
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 | |
c5be964a | 147 | trace_buffer[0] = pc; |
017abbe3 | 148 | #endif |
866e32ad KS |
149 | } |
150 | ||
b667dd70 ML |
151 | void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) { |
152 | CHECK(context); | |
866e32ad KS |
153 | CHECK_GE(max_depth, 2); |
154 | if (!unwind_backtrace_signal_arch) { | |
b667dd70 | 155 | UnwindSlow(pc, max_depth); |
866e32ad KS |
156 | return; |
157 | } | |
158 | ||
159 | void *map = acquire_my_map_info_list(); | |
160 | CHECK(map); | |
eac97531 | 161 | InternalMmapVector<backtrace_frame_t> frames(kStackTraceMax); |
866e32ad KS |
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) | |
c5be964a | 174 | trace_buffer[size++] = frames[i].absolute_pc + 2; |
866e32ad KS |
175 | } |
176 | ||
177 | } // namespace __sanitizer | |
178 | ||
eac97531 ML |
179 | #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || |
180 | // SANITIZER_SOLARIS |