xref: /freebsd/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cpp (revision 8a4dda33d67586ca2624f2a38417baa03a533a7f)
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 
Unwind_GetIP(struct _Unwind_Context * ctx)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 
Unwind_Trace(struct _Unwind_Context * ctx,void * param)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
SanitizerInitializeUnwinder()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 
UnwindSlow(uptr pc,u32 max_depth)126 void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) {
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);
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(to_pop);
142   trace_buffer[0] = pc;
143 }
144 
UnwindSlow(uptr pc,void * context,u32 max_depth)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