diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2019-10-23 17:52:22 +0000 | 
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2019-10-23 17:52:22 +0000 | 
| commit | 3a1720af1d7f43edc5b214cde0be11bfb94d077e (patch) | |
| tree | 029e0ff2d5e3c0eaf2405fd8e669555fdf5e1297 /lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cpp | |
| parent | 8f3cadc28cb2bb9e8f9d69eeaaea1f57f2f7b2ab (diff) | |
Notes
Diffstat (limited to 'lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cpp')
| -rw-r--r-- | lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cpp | 180 | 
1 files changed, 180 insertions, 0 deletions
diff --git a/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cpp b/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cpp new file mode 100644 index 0000000000000..b2628dcc4dc1f --- /dev/null +++ b/lib/sanitizer_common/sanitizer_unwind_linux_libcdep.cpp @@ -0,0 +1,180 @@ +//===-- sanitizer_unwind_linux_libcdep.cpp --------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file contains the unwind.h-based (aka "slow") stack unwinding routines +// available to the tools on Linux, Android, NetBSD, FreeBSD, and Solaris. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ +    SANITIZER_SOLARIS +#include "sanitizer_common.h" +#include "sanitizer_stacktrace.h" + +#if SANITIZER_ANDROID +#include <dlfcn.h>  // for dlopen() +#endif + +#if SANITIZER_FREEBSD +#define _GNU_SOURCE  // to declare _Unwind_Backtrace() from <unwind.h> +#endif +#include <unwind.h> + +namespace __sanitizer { + +namespace { + +//---------------------------- UnwindSlow -------------------------------------- + +typedef struct { +  uptr absolute_pc; +  uptr stack_top; +  uptr stack_size; +} backtrace_frame_t; + +extern "C" { +typedef void *(*acquire_my_map_info_list_func)(); +typedef void (*release_my_map_info_list_func)(void *map); +typedef sptr (*unwind_backtrace_signal_arch_func)( +    void *siginfo, void *sigcontext, void *map_info_list, +    backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth); +acquire_my_map_info_list_func acquire_my_map_info_list; +release_my_map_info_list_func release_my_map_info_list; +unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch; +} // extern "C" + +#if defined(__arm__) && !SANITIZER_NETBSD +// NetBSD uses dwarf EH +#define UNWIND_STOP _URC_END_OF_STACK +#define UNWIND_CONTINUE _URC_NO_REASON +#else +#define UNWIND_STOP _URC_NORMAL_STOP +#define UNWIND_CONTINUE _URC_NO_REASON +#endif + +uptr Unwind_GetIP(struct _Unwind_Context *ctx) { +#if defined(__arm__) && !SANITIZER_MAC +  uptr val; +  _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE, +      15 /* r15 = PC */, _UVRSD_UINT32, &val); +  CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed"); +  // Clear the Thumb bit. +  return val & ~(uptr)1; +#else +  return (uptr)_Unwind_GetIP(ctx); +#endif +} + +struct UnwindTraceArg { +  BufferedStackTrace *stack; +  u32 max_depth; +}; + +_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { +  UnwindTraceArg *arg = (UnwindTraceArg*)param; +  CHECK_LT(arg->stack->size, arg->max_depth); +  uptr pc = Unwind_GetIP(ctx); +  const uptr kPageSize = GetPageSizeCached(); +  // Let's assume that any pointer in the 0th page (i.e. <0x1000 on i386 and +  // x86_64) is invalid and stop unwinding here.  If we're adding support for +  // a platform where this isn't true, we need to reconsider this check. +  if (pc < kPageSize) return UNWIND_STOP; +  arg->stack->trace_buffer[arg->stack->size++] = pc; +  if (arg->stack->size == arg->max_depth) return UNWIND_STOP; +  return UNWIND_CONTINUE; +} + +}  // namespace + +#if SANITIZER_ANDROID +void SanitizerInitializeUnwinder() { +  if (AndroidGetApiLevel() >= ANDROID_LOLLIPOP_MR1) return; + +  // Pre-lollipop Android can not unwind through signal handler frames with +  // libgcc unwinder, but it has a libcorkscrew.so library with the necessary +  // workarounds. +  void *p = dlopen("libcorkscrew.so", RTLD_LAZY); +  if (!p) { +    VReport(1, +            "Failed to open libcorkscrew.so. You may see broken stack traces " +            "in SEGV reports."); +    return; +  } +  acquire_my_map_info_list = +      (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list"); +  release_my_map_info_list = +      (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list"); +  unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym( +      p, "unwind_backtrace_signal_arch"); +  if (!acquire_my_map_info_list || !release_my_map_info_list || +      !unwind_backtrace_signal_arch) { +    VReport(1, +            "Failed to find one of the required symbols in libcorkscrew.so. " +            "You may see broken stack traces in SEGV reports."); +    acquire_my_map_info_list = 0; +    unwind_backtrace_signal_arch = 0; +    release_my_map_info_list = 0; +  } +} +#endif + +void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) { +  CHECK_GE(max_depth, 2); +  size = 0; +  UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)}; +  _Unwind_Backtrace(Unwind_Trace, &arg); +  // We need to pop a few frames so that pc is on top. +  uptr to_pop = LocatePcInTrace(pc); +  // trace_buffer[0] belongs to the current function so we always pop it, +  // unless there is only 1 frame in the stack trace (1 frame is always better +  // than 0!). +  // 1-frame stacks don't normally happen, but this depends on the actual +  // unwinder implementation (libgcc, libunwind, etc) which is outside of our +  // control. +  if (to_pop == 0 && size > 1) +    to_pop = 1; +  PopStackFrames(to_pop); +#if defined(__GNUC__) && defined(__sparc__) +  // __builtin_return_address returns the address of the call instruction +  // on the SPARC and not the return address, so we need to compensate. +  trace_buffer[0] = GetNextInstructionPc(pc); +#else +  trace_buffer[0] = pc; +#endif +} + +void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) { +  CHECK(context); +  CHECK_GE(max_depth, 2); +  if (!unwind_backtrace_signal_arch) { +    UnwindSlow(pc, max_depth); +    return; +  } + +  void *map = acquire_my_map_info_list(); +  CHECK(map); +  InternalMmapVector<backtrace_frame_t> frames(kStackTraceMax); +  // siginfo argument appears to be unused. +  sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map, +                                          frames.data(), +                                          /* ignore_depth */ 0, max_depth); +  release_my_map_info_list(map); +  if (res < 0) return; +  CHECK_LE((uptr)res, kStackTraceMax); + +  size = 0; +  // +2 compensate for libcorkscrew unwinder returning addresses of call +  // instructions instead of raw return addresses. +  for (sptr i = 0; i < res; ++i) +    trace_buffer[size++] = frames[i].absolute_pc + 2; +} + +}  // namespace __sanitizer + +#endif  // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || +        // SANITIZER_SOLARIS  | 
