Home | History | Annotate | Download | only in sanitizer_common
      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 
     28 namespace __sanitizer {
     29 
     30 //------------------------- SlowUnwindStack -----------------------------------
     31 
     32 typedef struct {
     33   uptr absolute_pc;
     34   uptr stack_top;
     35   uptr stack_size;
     36 } backtrace_frame_t;
     37 
     38 extern "C" {
     39 typedef void *(*acquire_my_map_info_list_func)();
     40 typedef void (*release_my_map_info_list_func)(void *map);
     41 typedef 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);
     44 acquire_my_map_info_list_func acquire_my_map_info_list;
     45 release_my_map_info_list_func release_my_map_info_list;
     46 unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;
     47 } // extern "C"
     48 
     49 #if SANITIZER_ANDROID
     50 void 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 
     84 uptr 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 
     97 struct 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 
    111 void 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 
    130 void 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