123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- //===-- sanitizer_unwind_posix.cc ----------------------------------------===//
- //
- // 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, FreeBSD and OS X.
- //===----------------------------------------------------------------------===//
- #include "sanitizer_platform.h"
- #if SANITIZER_POSIX
- #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() {
- 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) {
- #ifdef __arm__
- 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;
- uptr 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);
- 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, uptr 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.
- if (to_pop == 0 && size > 1)
- to_pop = 1;
- PopStackFrames(to_pop);
- trace_buffer[0] = pc;
- }
- void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
- uptr 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_POSIX
|