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