master
  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 || SANITIZER_HAIKU
 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_APPLE
 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
 94void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) {
 95  CHECK_GE(max_depth, 2);
 96  size = 0;
 97  UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
 98  _Unwind_Backtrace(Unwind_Trace, &arg);
 99  // We need to pop a few frames so that pc is on top.
100  uptr to_pop = LocatePcInTrace(pc);
101  // trace_buffer[0] belongs to the current function so we always pop it,
102  // unless there is only 1 frame in the stack trace (1 frame is always better
103  // than 0!).
104  // 1-frame stacks don't normally happen, but this depends on the actual
105  // unwinder implementation (libgcc, libunwind, etc) which is outside of our
106  // control.
107  if (to_pop == 0 && size > 1)
108    to_pop = 1;
109  PopStackFrames(to_pop);
110  trace_buffer[0] = pc;
111}
112
113void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) {
114  CHECK(context);
115  CHECK_GE(max_depth, 2);
116  if (!unwind_backtrace_signal_arch) {
117    UnwindSlow(pc, max_depth);
118    return;
119  }
120
121  void *map = acquire_my_map_info_list();
122  CHECK(map);
123  InternalMmapVector<backtrace_frame_t> frames(kStackTraceMax);
124  // siginfo argument appears to be unused.
125  sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map,
126                                          frames.data(),
127                                          /* ignore_depth */ 0, max_depth);
128  release_my_map_info_list(map);
129  if (res < 0) return;
130  CHECK_LE((uptr)res, kStackTraceMax);
131
132  size = 0;
133  // +2 compensate for libcorkscrew unwinder returning addresses of call
134  // instructions instead of raw return addresses.
135  for (sptr i = 0; i < res; ++i)
136    trace_buffer[size++] = frames[i].absolute_pc + 2;
137}
138
139}  // namespace __sanitizer
140
141#endif  // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||
142        // SANITIZER_SOLARIS || SANITIZER_HAIKU