169 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			169 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- sanitizer_unwind_linux_libcdep.cc ---------------------------------===//
 | 
						|
//
 | 
						|
//                     The LLVM Compiler Infrastructure
 | 
						|
//
 | 
						|
// This file is distributed under the University of Illinois Open Source
 | 
						|
// License. See LICENSE.TXT for details.
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
//
 | 
						|
// This file contains the unwind.h-based (aka "slow") stack unwinding routines
 | 
						|
// available to the tools on Linux, Android, and FreeBSD.
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "sanitizer_platform.h"
 | 
						|
#if SANITIZER_FREEBSD || SANITIZER_LINUX
 | 
						|
#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 {
 | 
						|
 | 
						|
//------------------------- SlowUnwindStack -----------------------------------
 | 
						|
 | 
						|
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 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
 | 
						|
 | 
						|
#ifdef __arm__
 | 
						|
#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 _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;
 | 
						|
}
 | 
						|
 | 
						|
void BufferedStackTrace::SlowUnwindStack(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);
 | 
						|
  trace_buffer[0] = pc;
 | 
						|
}
 | 
						|
 | 
						|
void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
 | 
						|
                                                    u32 max_depth) {
 | 
						|
  CHECK_GE(max_depth, 2);
 | 
						|
  if (!unwind_backtrace_signal_arch) {
 | 
						|
    SlowUnwindStack(pc, max_depth);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  void *map = acquire_my_map_info_list();
 | 
						|
  CHECK(map);
 | 
						|
  InternalScopedBuffer<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
 |