1 | //===-- sanitizer_unwind_linux_libcdep.cpp --------------------------------===// |
2 | // |
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 |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file contains the unwind.h-based (aka "slow") stack unwinding routines |
10 | // available to the tools on Linux, Android, NetBSD, FreeBSD, and Solaris. |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "sanitizer_platform.h" |
14 | #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ |
15 | SANITIZER_SOLARIS |
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 | namespace { |
31 | |
32 | //---------------------------- UnwindSlow -------------------------------------- |
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 | |
51 | #if defined(__arm__) && !SANITIZER_NETBSD |
52 | // NetBSD uses dwarf EH |
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) { |
61 | #if defined(__arm__) && !SANITIZER_APPLE |
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 |
69 | return (uptr)_Unwind_GetIP(ctx); |
70 | #endif |
71 | } |
72 | |
73 | struct UnwindTraceArg { |
74 | BufferedStackTrace *stack; |
75 | u32 max_depth; |
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); |
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; |
87 | arg->stack->trace_buffer[arg->stack->size++] = pc; |
88 | if (arg->stack->size == arg->max_depth) return UNWIND_STOP; |
89 | return UNWIND_CONTINUE; |
90 | } |
91 | |
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 | |
126 | void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) { |
127 | CHECK_GE(max_depth, 2); |
128 | size = 0; |
129 | UnwindTraceArg arg = {.stack: this, .max_depth: Min(a: max_depth + 1, b: 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); |
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. |
139 | if (to_pop == 0 && size > 1) |
140 | to_pop = 1; |
141 | PopStackFrames(count: to_pop); |
142 | trace_buffer[0] = pc; |
143 | } |
144 | |
145 | void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) { |
146 | CHECK(context); |
147 | CHECK_GE(max_depth, 2); |
148 | if (!unwind_backtrace_signal_arch) { |
149 | UnwindSlow(pc, max_depth); |
150 | return; |
151 | } |
152 | |
153 | void *map = acquire_my_map_info_list(); |
154 | CHECK(map); |
155 | InternalMmapVector<backtrace_frame_t> frames(kStackTraceMax); |
156 | // siginfo argument appears to be unused. |
157 | sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map, |
158 | frames.data(), |
159 | /* ignore_depth */ 0, max_depth); |
160 | release_my_map_info_list(map); |
161 | if (res < 0) return; |
162 | CHECK_LE((uptr)res, kStackTraceMax); |
163 | |
164 | size = 0; |
165 | // +2 compensate for libcorkscrew unwinder returning addresses of call |
166 | // instructions instead of raw return addresses. |
167 | for (sptr i = 0; i < res; ++i) |
168 | trace_buffer[size++] = frames[i].absolute_pc + 2; |
169 | } |
170 | |
171 | } // namespace __sanitizer |
172 | |
173 | #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || |
174 | // SANITIZER_SOLARIS |
175 | |