1 //===-- sanitizer_unwind_linux_libcdep.cc ---------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // This file contains the unwind.h-based (aka "slow") stack unwinding routines
9 // available to the tools on Linux, Android, and FreeBSD.
10 //===----------------------------------------------------------------------===//
12 #include "sanitizer_platform.h"
13 #if SANITIZER_FREEBSD || SANITIZER_LINUX
14 #include "sanitizer_common.h"
15 #include "sanitizer_stacktrace.h"
18 #include <dlfcn.h> // for dlopen()
22 #define _GNU_SOURCE // to declare _Unwind_Backtrace() from <unwind.h>
26 namespace __sanitizer
{
28 //------------------------- SlowUnwindStack -----------------------------------
37 typedef void *(*acquire_my_map_info_list_func
)();
38 typedef void (*release_my_map_info_list_func
)(void *map
);
39 typedef sptr (*unwind_backtrace_signal_arch_func
)(
40 void *siginfo
, void *sigcontext
, void *map_info_list
,
41 backtrace_frame_t
*backtrace
, uptr ignore_depth
, uptr max_depth
);
42 acquire_my_map_info_list_func acquire_my_map_info_list
;
43 release_my_map_info_list_func release_my_map_info_list
;
44 unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch
;
48 void SanitizerInitializeUnwinder() {
49 void *p
= dlopen("libcorkscrew.so", RTLD_LAZY
);
52 "Failed to open libcorkscrew.so. You may see broken stack traces "
56 acquire_my_map_info_list
=
57 (acquire_my_map_info_list_func
)(uptr
)dlsym(p
, "acquire_my_map_info_list");
58 release_my_map_info_list
=
59 (release_my_map_info_list_func
)(uptr
)dlsym(p
, "release_my_map_info_list");
60 unwind_backtrace_signal_arch
= (unwind_backtrace_signal_arch_func
)(uptr
)dlsym(
61 p
, "unwind_backtrace_signal_arch");
62 if (!acquire_my_map_info_list
|| !release_my_map_info_list
||
63 !unwind_backtrace_signal_arch
) {
65 "Failed to find one of the required symbols in libcorkscrew.so. "
66 "You may see broken stack traces in SEGV reports.");
67 acquire_my_map_info_list
= 0;
68 unwind_backtrace_signal_arch
= 0;
69 release_my_map_info_list
= 0;
75 #define UNWIND_STOP _URC_END_OF_STACK
76 #define UNWIND_CONTINUE _URC_NO_REASON
78 #define UNWIND_STOP _URC_NORMAL_STOP
79 #define UNWIND_CONTINUE _URC_NO_REASON
82 uptr
Unwind_GetIP(struct _Unwind_Context
*ctx
) {
83 #if defined(__arm__) && !SANITIZER_MAC
85 _Unwind_VRS_Result res
= _Unwind_VRS_Get(ctx
, _UVRSC_CORE
,
86 15 /* r15 = PC */, _UVRSD_UINT32
, &val
);
87 CHECK(res
== _UVRSR_OK
&& "_Unwind_VRS_Get failed");
88 // Clear the Thumb bit.
89 return val
& ~(uptr
)1;
91 return _Unwind_GetIP(ctx
);
95 struct UnwindTraceArg
{
96 BufferedStackTrace
*stack
;
100 _Unwind_Reason_Code
Unwind_Trace(struct _Unwind_Context
*ctx
, void *param
) {
101 UnwindTraceArg
*arg
= (UnwindTraceArg
*)param
;
102 CHECK_LT(arg
->stack
->size
, arg
->max_depth
);
103 uptr pc
= Unwind_GetIP(ctx
);
104 arg
->stack
->trace_buffer
[arg
->stack
->size
++] = pc
;
105 if (arg
->stack
->size
== arg
->max_depth
) return UNWIND_STOP
;
106 return UNWIND_CONTINUE
;
109 void BufferedStackTrace::SlowUnwindStack(uptr pc
, u32 max_depth
) {
110 CHECK_GE(max_depth
, 2);
112 UnwindTraceArg arg
= {this, Min(max_depth
+ 1, kStackTraceMax
)};
113 _Unwind_Backtrace(Unwind_Trace
, &arg
);
114 // We need to pop a few frames so that pc is on top.
115 uptr to_pop
= LocatePcInTrace(pc
);
116 // trace_buffer[0] belongs to the current function so we always pop it,
117 // unless there is only 1 frame in the stack trace (1 frame is always better
119 // 1-frame stacks don't normally happen, but this depends on the actual
120 // unwinder implementation (libgcc, libunwind, etc) which is outside of our
122 if (to_pop
== 0 && size
> 1)
124 PopStackFrames(to_pop
);
125 trace_buffer
[0] = pc
;
128 void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc
, void *context
,
130 CHECK_GE(max_depth
, 2);
131 if (!unwind_backtrace_signal_arch
) {
132 SlowUnwindStack(pc
, max_depth
);
136 void *map
= acquire_my_map_info_list();
138 InternalScopedBuffer
<backtrace_frame_t
> frames(kStackTraceMax
);
139 // siginfo argument appears to be unused.
140 sptr res
= unwind_backtrace_signal_arch(/* siginfo */ 0, context
, map
,
142 /* ignore_depth */ 0, max_depth
);
143 release_my_map_info_list(map
);
145 CHECK_LE((uptr
)res
, kStackTraceMax
);
148 // +2 compensate for libcorkscrew unwinder returning addresses of call
149 // instructions instead of raw return addresses.
150 for (sptr i
= 0; i
< res
; ++i
)
151 trace_buffer
[size
++] = frames
[i
].absolute_pc
+ 2;
154 } // namespace __sanitizer
156 #endif // SANITIZER_FREEBSD || SANITIZER_LINUX