1 //===-- sanitizer_unwind_linux_libcdep.cpp --------------------------------===//
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
7 //===----------------------------------------------------------------------===//
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 //===----------------------------------------------------------------------===//
13 #include "sanitizer_platform.h"
14 #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
16 #include "sanitizer_common.h"
17 #include "sanitizer_stacktrace.h"
20 #include <dlfcn.h> // for dlopen()
24 #define _GNU_SOURCE // to declare _Unwind_Backtrace() from <unwind.h>
28 namespace __sanitizer
{
30 //---------------------------- UnwindSlow --------------------------------------
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
;
50 void SanitizerInitializeUnwinder() {
51 if (AndroidGetApiLevel() >= ANDROID_LOLLIPOP_MR1
) return;
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
56 void *p
= dlopen("libcorkscrew.so", RTLD_LAZY
);
59 "Failed to open libcorkscrew.so. You may see broken stack traces "
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
) {
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;
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
86 #define UNWIND_STOP _URC_NORMAL_STOP
87 #define UNWIND_CONTINUE _URC_NO_REASON
90 uptr
Unwind_GetIP(struct _Unwind_Context
*ctx
) {
91 #if defined(__arm__) && !SANITIZER_MAC
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;
99 return (uptr
)_Unwind_GetIP(ctx
);
103 struct UnwindTraceArg
{
104 BufferedStackTrace
*stack
;
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
;
122 void BufferedStackTrace::UnwindSlow(uptr pc
, u32 max_depth
) {
123 CHECK_GE(max_depth
, 2);
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
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
135 if (to_pop
== 0 && size
> 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
);
143 trace_buffer
[0] = pc
;
147 void BufferedStackTrace::UnwindSlow(uptr pc
, void *context
, u32 max_depth
) {
149 CHECK_GE(max_depth
, 2);
150 if (!unwind_backtrace_signal_arch
) {
151 UnwindSlow(pc
, max_depth
);
155 void *map
= acquire_my_map_info_list();
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
,
161 /* ignore_depth */ 0, max_depth
);
162 release_my_map_info_list(map
);
164 CHECK_LE((uptr
)res
, kStackTraceMax
);
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;
173 } // namespace __sanitizer
175 #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||