Arm: Fix arm libsanitizer bootstrap failure
[gcc.git] / libsanitizer / sanitizer_common / sanitizer_unwind_linux_libcdep.cpp
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 //---------------------------- UnwindSlow --------------------------------------
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
50 void SanitizerInitializeUnwinder() {
51 if (AndroidGetApiLevel() >= ANDROID_LOLLIPOP_MR1) return;
52
53 // Pre-lollipop Android can not unwind through signal handler frames with
54 // libgcc unwinder, but it has a libcorkscrew.so library with the necessary
55 // workarounds.
56 void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
57 if (!p) {
58 VReport(1,
59 "Failed to open libcorkscrew.so. You may see broken stack traces "
60 "in SEGV reports.");
61 return;
62 }
63 acquire_my_map_info_list =
64 (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");
65 release_my_map_info_list =
66 (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");
67 unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(
68 p, "unwind_backtrace_signal_arch");
69 if (!acquire_my_map_info_list || !release_my_map_info_list ||
70 !unwind_backtrace_signal_arch) {
71 VReport(1,
72 "Failed to find one of the required symbols in libcorkscrew.so. "
73 "You may see broken stack traces in SEGV reports.");
74 acquire_my_map_info_list = 0;
75 unwind_backtrace_signal_arch = 0;
76 release_my_map_info_list = 0;
77 }
78 }
79 #endif
80
81 #if defined(__arm__) && !SANITIZER_NETBSD
82 // NetBSD uses dwarf EH
83 #define UNWIND_STOP _URC_END_OF_STACK
84 #define UNWIND_CONTINUE _URC_NO_REASON
85 #else
86 #define UNWIND_STOP _URC_NORMAL_STOP
87 #define UNWIND_CONTINUE _URC_NO_REASON
88 #endif
89
90 uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
91 #if defined(__arm__) && !SANITIZER_MAC
92 uptr val;
93 _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
94 15 /* r15 = PC */, _UVRSD_UINT32, &val);
95 CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
96 // Clear the Thumb bit.
97 return val & ~(uptr)1;
98 #else
99 return (uptr)_Unwind_GetIP(ctx);
100 #endif
101 }
102
103 struct UnwindTraceArg {
104 BufferedStackTrace *stack;
105 u32 max_depth;
106 };
107
108 _Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
109 UnwindTraceArg *arg = (UnwindTraceArg*)param;
110 CHECK_LT(arg->stack->size, arg->max_depth);
111 uptr pc = Unwind_GetIP(ctx);
112 const uptr kPageSize = GetPageSizeCached();
113 // Let's assume that any pointer in the 0th page (i.e. <0x1000 on i386 and
114 // x86_64) is invalid and stop unwinding here. If we're adding support for
115 // a platform where this isn't true, we need to reconsider this check.
116 if (pc < kPageSize) return UNWIND_STOP;
117 arg->stack->trace_buffer[arg->stack->size++] = pc;
118 if (arg->stack->size == arg->max_depth) return UNWIND_STOP;
119 return UNWIND_CONTINUE;
120 }
121
122 void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) {
123 CHECK_GE(max_depth, 2);
124 size = 0;
125 UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
126 _Unwind_Backtrace(Unwind_Trace, &arg);
127 // We need to pop a few frames so that pc is on top.
128 uptr to_pop = LocatePcInTrace(pc);
129 // trace_buffer[0] belongs to the current function so we always pop it,
130 // unless there is only 1 frame in the stack trace (1 frame is always better
131 // than 0!).
132 // 1-frame stacks don't normally happen, but this depends on the actual
133 // unwinder implementation (libgcc, libunwind, etc) which is outside of our
134 // control.
135 if (to_pop == 0 && size > 1)
136 to_pop = 1;
137 PopStackFrames(to_pop);
138 #if defined(__GNUC__) && defined(__sparc__)
139 // __builtin_return_address returns the address of the call instruction
140 // on the SPARC and not the return address, so we need to compensate.
141 trace_buffer[0] = GetNextInstructionPc(pc);
142 #else
143 trace_buffer[0] = pc;
144 #endif
145 }
146
147 void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) {
148 CHECK(context);
149 CHECK_GE(max_depth, 2);
150 if (!unwind_backtrace_signal_arch) {
151 UnwindSlow(pc, max_depth);
152 return;
153 }
154
155 void *map = acquire_my_map_info_list();
156 CHECK(map);
157 InternalMmapVector<backtrace_frame_t> frames(kStackTraceMax);
158 // siginfo argument appears to be unused.
159 sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map,
160 frames.data(),
161 /* ignore_depth */ 0, max_depth);
162 release_my_map_info_list(map);
163 if (res < 0) return;
164 CHECK_LE((uptr)res, kStackTraceMax);
165
166 size = 0;
167 // +2 compensate for libcorkscrew unwinder returning addresses of call
168 // instructions instead of raw return addresses.
169 for (sptr i = 0; i < res; ++i)
170 trace_buffer[size++] = frames[i].absolute_pc + 2;
171 }
172
173 } // namespace __sanitizer
174
175 #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||
176 // SANITIZER_SOLARIS