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, NetBSD, FreeBSD, and Solaris.
10//===----------------------------------------------------------------------===//
11
12#include "sanitizer_platform.h"
13#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
14    SANITIZER_SOLARIS
15#include "sanitizer_common.h"
16#include "sanitizer_stacktrace.h"
17
18#if SANITIZER_ANDROID
19#include <dlfcn.h>  // for dlopen()
20#endif
21
22#if SANITIZER_FREEBSD
23#define _GNU_SOURCE  // to declare _Unwind_Backtrace() from <unwind.h>
24#endif
25#include <unwind.h>
26
27namespace __sanitizer {
28
29//------------------------- SlowUnwindStack -----------------------------------
30
31typedef struct {
32  uptr absolute_pc;
33  uptr stack_top;
34  uptr stack_size;
35} backtrace_frame_t;
36
37extern "C" {
38typedef void *(*acquire_my_map_info_list_func)();
39typedef void (*release_my_map_info_list_func)(void *map);
40typedef sptr (*unwind_backtrace_signal_arch_func)(
41    void *siginfo, void *sigcontext, void *map_info_list,
42    backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth);
43acquire_my_map_info_list_func acquire_my_map_info_list;
44release_my_map_info_list_func release_my_map_info_list;
45unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;
46} // extern "C"
47
48#if SANITIZER_ANDROID
49void SanitizerInitializeUnwinder() {
50  if (AndroidGetApiLevel() >= ANDROID_LOLLIPOP_MR1) return;
51
52  // Pre-lollipop Android can not unwind through signal handler frames with
53  // libgcc unwinder, but it has a libcorkscrew.so library with the necessary
54  // workarounds.
55  void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
56  if (!p) {
57    VReport(1,
58            "Failed to open libcorkscrew.so. You may see broken stack traces "
59            "in SEGV reports.");
60    return;
61  }
62  acquire_my_map_info_list =
63      (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");
64  release_my_map_info_list =
65      (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");
66  unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(
67      p, "unwind_backtrace_signal_arch");
68  if (!acquire_my_map_info_list || !release_my_map_info_list ||
69      !unwind_backtrace_signal_arch) {
70    VReport(1,
71            "Failed to find one of the required symbols in libcorkscrew.so. "
72            "You may see broken stack traces in SEGV reports.");
73    acquire_my_map_info_list = 0;
74    unwind_backtrace_signal_arch = 0;
75    release_my_map_info_list = 0;
76  }
77}
78#endif
79
80#if defined(__arm__) && !SANITIZER_NETBSD
81// NetBSD uses dwarf EH
82#define UNWIND_STOP _URC_END_OF_STACK
83#define UNWIND_CONTINUE _URC_NO_REASON
84#else
85#define UNWIND_STOP _URC_NORMAL_STOP
86#define UNWIND_CONTINUE _URC_NO_REASON
87#endif
88
89uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
90#if defined(__arm__) && !SANITIZER_MAC && !SANITIZER_NETBSD
91  uptr val;
92  _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
93      15 /* r15 = PC */, _UVRSD_UINT32, &val);
94  CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
95  // Clear the Thumb bit.
96  return val & ~(uptr)1;
97#else
98  return (uptr)_Unwind_GetIP(ctx);
99#endif
100}
101
102struct UnwindTraceArg {
103  BufferedStackTrace *stack;
104  u32 max_depth;
105};
106
107_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
108  UnwindTraceArg *arg = (UnwindTraceArg*)param;
109  CHECK_LT(arg->stack->size, arg->max_depth);
110  uptr pc = Unwind_GetIP(ctx);
111  const uptr kPageSize = GetPageSizeCached();
112  // Let's assume that any pointer in the 0th page (i.e. <0x1000 on i386 and
113  // x86_64) is invalid and stop unwinding here.  If we're adding support for
114  // a platform where this isn't true, we need to reconsider this check.
115  if (pc < kPageSize) return UNWIND_STOP;
116  arg->stack->trace_buffer[arg->stack->size++] = pc;
117  if (arg->stack->size == arg->max_depth) return UNWIND_STOP;
118  return UNWIND_CONTINUE;
119}
120
121void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) {
122  CHECK_GE(max_depth, 2);
123  size = 0;
124  UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
125  _Unwind_Backtrace(Unwind_Trace, &arg);
126  // We need to pop a few frames so that pc is on top.
127  uptr to_pop = LocatePcInTrace(pc);
128  // trace_buffer[0] belongs to the current function so we always pop it,
129  // unless there is only 1 frame in the stack trace (1 frame is always better
130  // than 0!).
131  // 1-frame stacks don't normally happen, but this depends on the actual
132  // unwinder implementation (libgcc, libunwind, etc) which is outside of our
133  // control.
134  if (to_pop == 0 && size > 1)
135    to_pop = 1;
136  PopStackFrames(to_pop);
137#if defined(__GNUC__) && defined(__sparc__)
138  // __builtin_return_address returns the address of the call instruction
139  // on the SPARC and not the return address, so we need to compensate.
140  trace_buffer[0] = GetNextInstructionPc(pc);
141#else
142  trace_buffer[0] = pc;
143#endif
144}
145
146void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
147                                                    u32 max_depth) {
148  CHECK_GE(max_depth, 2);
149  if (!unwind_backtrace_signal_arch) {
150    SlowUnwindStack(pc, max_depth);
151    return;
152  }
153
154  void *map = acquire_my_map_info_list();
155  CHECK(map);
156  InternalMmapVector<backtrace_frame_t> frames(kStackTraceMax);
157  // siginfo argument appears to be unused.
158  sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map,
159                                          frames.data(),
160                                          /* ignore_depth */ 0, max_depth);
161  release_my_map_info_list(map);
162  if (res < 0) return;
163  CHECK_LE((uptr)res, kStackTraceMax);
164
165  size = 0;
166  // +2 compensate for libcorkscrew unwinder returning addresses of call
167  // instructions instead of raw return addresses.
168  for (sptr i = 0; i < res; ++i)
169    trace_buffer[size++] = frames[i].absolute_pc + 2;
170}
171
172}  // namespace __sanitizer
173
174#endif  // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||
175        // SANITIZER_SOLARIS
176