* fi.po: Update.
[official-gcc.git] / libsanitizer / sanitizer_common / sanitizer_unwind_linux_libcdep.cc
blob408c21c913bbbc36a2c6dfb0e85349789bb75c05
1 //===-- sanitizer_unwind_linux_libcdep.cc ---------------------------------===//
2 //
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
5 //
6 //===----------------------------------------------------------------------===//
7 //
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"
17 #if SANITIZER_ANDROID
18 #include <dlfcn.h> // for dlopen()
19 #endif
21 #if SANITIZER_FREEBSD
22 #define _GNU_SOURCE // to declare _Unwind_Backtrace() from <unwind.h>
23 #endif
24 #include <unwind.h>
26 namespace __sanitizer {
28 //------------------------- SlowUnwindStack -----------------------------------
30 typedef struct {
31 uptr absolute_pc;
32 uptr stack_top;
33 uptr stack_size;
34 } backtrace_frame_t;
36 extern "C" {
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;
45 } // extern "C"
47 #if SANITIZER_ANDROID
48 void SanitizerInitializeUnwinder() {
49 void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
50 if (!p) {
51 VReport(1,
52 "Failed to open libcorkscrew.so. You may see broken stack traces "
53 "in SEGV reports.");
54 return;
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) {
64 VReport(1,
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;
72 #endif
74 #ifdef __arm__
75 #define UNWIND_STOP _URC_END_OF_STACK
76 #define UNWIND_CONTINUE _URC_NO_REASON
77 #else
78 #define UNWIND_STOP _URC_NORMAL_STOP
79 #define UNWIND_CONTINUE _URC_NO_REASON
80 #endif
82 uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
83 #if defined(__arm__) && !SANITIZER_MAC
84 uptr val;
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;
90 #else
91 return _Unwind_GetIP(ctx);
92 #endif
95 struct UnwindTraceArg {
96 BufferedStackTrace *stack;
97 u32 max_depth;
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);
111 size = 0;
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
118 // than 0!).
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
121 // control.
122 if (to_pop == 0 && size > 1)
123 to_pop = 1;
124 PopStackFrames(to_pop);
125 trace_buffer[0] = pc;
128 void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
129 u32 max_depth) {
130 CHECK_GE(max_depth, 2);
131 if (!unwind_backtrace_signal_arch) {
132 SlowUnwindStack(pc, max_depth);
133 return;
136 void *map = acquire_my_map_info_list();
137 CHECK(map);
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,
141 frames.data(),
142 /* ignore_depth */ 0, max_depth);
143 release_my_map_info_list(map);
144 if (res < 0) return;
145 CHECK_LE((uptr)res, kStackTraceMax);
147 size = 0;
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