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   if (AndroidGetApiLevel() >= ANDROID_LOLLIPOP_MR1) return;
     52 
     53   // Pre-lollipop Android can not unwind through signal handler frames with
     54   // libgcc unwinder, but it has a libcorkscrew.so library with the necessary
     55   // workarounds.
     56   void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
     57   if (!p) {
     58     VReport(1,
     59             "Failed to open libcorkscrew.so. You may see broken stack traces "
     60             "in SEGV reports.");
     61     return;
     62   }
     63   acquire_my_map_info_list =
     64       (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");
     65   release_my_map_info_list =
     66       (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");
     67   unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(
     68       p, "unwind_backtrace_signal_arch");
     69   if (!acquire_my_map_info_list || !release_my_map_info_list ||
     70       !unwind_backtrace_signal_arch) {
     71     VReport(1,
     72             "Failed to find one of the required symbols in libcorkscrew.so. "
     73             "You may see broken stack traces in SEGV reports.");
     74     acquire_my_map_info_list = 0;
     75     unwind_backtrace_signal_arch = 0;
     76     release_my_map_info_list = 0;
     77   }
     78 }
     79 #endif
     80 
     81 #ifdef __arm__
     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 
     89 uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
     90 #if defined(__arm__) && !SANITIZER_MAC
     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 _Unwind_GetIP(ctx);
     99 #endif
    100 }
    101 
    102 struct 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 
    121 void 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   trace_buffer[0] = pc;
    138 }
    139 
    140 void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
    141                                                     u32 max_depth) {
    142   CHECK_GE(max_depth, 2);
    143   if (!unwind_backtrace_signal_arch) {
    144     SlowUnwindStack(pc, max_depth);
    145     return;
    146   }
    147 
    148   void *map = acquire_my_map_info_list();
    149   CHECK(map);
    150   InternalScopedBuffer<backtrace_frame_t> frames(kStackTraceMax);
    151   // siginfo argument appears to be unused.
    152   sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map,
    153                                           frames.data(),
    154                                           /* ignore_depth */ 0, max_depth);
    155   release_my_map_info_list(map);
    156   if (res < 0) return;
    157   CHECK_LE((uptr)res, kStackTraceMax);
    158 
    159   size = 0;
    160   // +2 compensate for libcorkscrew unwinder returning addresses of call
    161   // instructions instead of raw return addresses.
    162   for (sptr i = 0; i < res; ++i)
    163     trace_buffer[size++] = frames[i].absolute_pc + 2;
    164 }
    165 
    166 }  // namespace __sanitizer
    167 
    168 #endif  // SANITIZER_FREEBSD || SANITIZER_LINUX
    169