sanitizer_unwind_linux_libcdep.cc revision 799172d60d32feb1acba1a6867f3a9c39a999e5c
1//===-- sanitizer_unwind_linux_libcdep.cc ---------------------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file contains the unwind.h-based (aka "slow") stack unwinding routines
11// available to the tools on Linux, Android, and FreeBSD.
12//===----------------------------------------------------------------------===//
13
14#include "sanitizer_platform.h"
15#if SANITIZER_FREEBSD || SANITIZER_LINUX
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
28namespace __sanitizer {
29
30//------------------------- SlowUnwindStack -----------------------------------
31
32typedef struct {
33  uptr absolute_pc;
34  uptr stack_top;
35  uptr stack_size;
36} backtrace_frame_t;
37
38extern "C" {
39typedef void *(*acquire_my_map_info_list_func)();
40typedef void (*release_my_map_info_list_func)(void *map);
41typedef 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);
44acquire_my_map_info_list_func acquire_my_map_info_list;
45release_my_map_info_list_func release_my_map_info_list;
46unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;
47} // extern "C"
48
49#if SANITIZER_ANDROID
50void SanitizerInitializeUnwinder() {
51  void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
52  if (!p) {
53    VReport(1,
54            "Failed to open libcorkscrew.so. You may see broken stack traces "
55            "in SEGV reports.");
56    return;
57  }
58  acquire_my_map_info_list =
59      (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");
60  release_my_map_info_list =
61      (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");
62  unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(
63      p, "unwind_backtrace_signal_arch");
64  if (!acquire_my_map_info_list || !release_my_map_info_list ||
65      !unwind_backtrace_signal_arch) {
66    VReport(1,
67            "Failed to find one of the required symbols in libcorkscrew.so. "
68            "You may see broken stack traces in SEGV reports.");
69    acquire_my_map_info_list = 0;
70    unwind_backtrace_signal_arch = 0;
71    release_my_map_info_list = 0;
72  }
73}
74#endif
75
76#ifdef __arm__
77#define UNWIND_STOP _URC_END_OF_STACK
78#define UNWIND_CONTINUE _URC_NO_REASON
79#else
80#define UNWIND_STOP _URC_NORMAL_STOP
81#define UNWIND_CONTINUE _URC_NO_REASON
82#endif
83
84uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
85#if defined(__arm__) && !SANITIZER_MAC
86  uptr val;
87  _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
88      15 /* r15 = PC */, _UVRSD_UINT32, &val);
89  CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
90  // Clear the Thumb bit.
91  return val & ~(uptr)1;
92#else
93  return _Unwind_GetIP(ctx);
94#endif
95}
96
97struct UnwindTraceArg {
98  BufferedStackTrace *stack;
99  u32 max_depth;
100};
101
102_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
103  UnwindTraceArg *arg = (UnwindTraceArg*)param;
104  CHECK_LT(arg->stack->size, arg->max_depth);
105  uptr pc = Unwind_GetIP(ctx);
106  arg->stack->trace_buffer[arg->stack->size++] = pc;
107  if (arg->stack->size == arg->max_depth) return UNWIND_STOP;
108  return UNWIND_CONTINUE;
109}
110
111void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) {
112  CHECK_GE(max_depth, 2);
113  size = 0;
114  UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
115  _Unwind_Backtrace(Unwind_Trace, &arg);
116  // We need to pop a few frames so that pc is on top.
117  uptr to_pop = LocatePcInTrace(pc);
118  // trace_buffer[0] belongs to the current function so we always pop it,
119  // unless there is only 1 frame in the stack trace (1 frame is always better
120  // than 0!).
121  // 1-frame stacks don't normally happen, but this depends on the actual
122  // unwinder implementation (libgcc, libunwind, etc) which is outside of our
123  // control.
124  if (to_pop == 0 && size > 1)
125    to_pop = 1;
126  PopStackFrames(to_pop);
127  trace_buffer[0] = pc;
128}
129
130void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
131                                                    u32 max_depth) {
132  CHECK_GE(max_depth, 2);
133  if (!unwind_backtrace_signal_arch) {
134    SlowUnwindStack(pc, max_depth);
135    return;
136  }
137
138  void *map = acquire_my_map_info_list();
139  CHECK(map);
140  InternalScopedBuffer<backtrace_frame_t> frames(kStackTraceMax);
141  // siginfo argument appears to be unused.
142  sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map,
143                                          frames.data(),
144                                          /* ignore_depth */ 0, max_depth);
145  release_my_map_info_list(map);
146  if (res < 0) return;
147  CHECK_LE((uptr)res, kStackTraceMax);
148
149  size = 0;
150  // +2 compensate for libcorkscrew unwinder returning addresses of call
151  // instructions instead of raw return addresses.
152  for (sptr i = 0; i < res; ++i)
153    trace_buffer[size++] = frames[i].absolute_pc + 2;
154}
155
156}  // namespace __sanitizer
157
158#endif  // SANITIZER_FREEBSD || SANITIZER_LINUX
159