143 lines · cpp
1//===-- sanitizer_unwind_linux_libcdep.cpp --------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file contains the unwind.h-based (aka "slow") stack unwinding routines10// available to the tools on Linux, Android, NetBSD, FreeBSD, and Solaris.11//===----------------------------------------------------------------------===//12 13#include "sanitizer_platform.h"14#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \15 SANITIZER_SOLARIS || SANITIZER_HAIKU16#include "sanitizer_common.h"17#include "sanitizer_stacktrace.h"18 19#if SANITIZER_ANDROID20#include <dlfcn.h> // for dlopen()21#endif22 23#if SANITIZER_FREEBSD24#define _GNU_SOURCE // to declare _Unwind_Backtrace() from <unwind.h>25#endif26#include <unwind.h>27 28namespace __sanitizer {29 30namespace {31 32//---------------------------- UnwindSlow --------------------------------------33 34typedef struct {35 uptr absolute_pc;36 uptr stack_top;37 uptr stack_size;38} backtrace_frame_t;39 40extern "C" {41typedef void *(*acquire_my_map_info_list_func)();42typedef void (*release_my_map_info_list_func)(void *map);43typedef sptr (*unwind_backtrace_signal_arch_func)(44 void *siginfo, void *sigcontext, void *map_info_list,45 backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth);46acquire_my_map_info_list_func acquire_my_map_info_list;47release_my_map_info_list_func release_my_map_info_list;48unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;49} // extern "C"50 51#if defined(__arm__) && !SANITIZER_NETBSD52// NetBSD uses dwarf EH53#define UNWIND_STOP _URC_END_OF_STACK54#define UNWIND_CONTINUE _URC_NO_REASON55#else56#define UNWIND_STOP _URC_NORMAL_STOP57#define UNWIND_CONTINUE _URC_NO_REASON58#endif59 60uptr Unwind_GetIP(struct _Unwind_Context *ctx) {61#if defined(__arm__) && !SANITIZER_APPLE62 uptr val;63 _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,64 15 /* r15 = PC */, _UVRSD_UINT32, &val);65 CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");66 // Clear the Thumb bit.67 return val & ~(uptr)1;68#else69 return (uptr)_Unwind_GetIP(ctx);70#endif71}72 73struct UnwindTraceArg {74 BufferedStackTrace *stack;75 u32 max_depth;76};77 78_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {79 UnwindTraceArg *arg = (UnwindTraceArg*)param;80 CHECK_LT(arg->stack->size, arg->max_depth);81 uptr pc = Unwind_GetIP(ctx);82 const uptr kPageSize = GetPageSizeCached();83 // Let's assume that any pointer in the 0th page (i.e. <0x1000 on i386 and84 // x86_64) is invalid and stop unwinding here. If we're adding support for85 // a platform where this isn't true, we need to reconsider this check.86 if (pc < kPageSize) return UNWIND_STOP;87 arg->stack->trace_buffer[arg->stack->size++] = pc;88 if (arg->stack->size == arg->max_depth) return UNWIND_STOP;89 return UNWIND_CONTINUE;90}91 92} // namespace93 94void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) {95 CHECK_GE(max_depth, 2);96 size = 0;97 UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};98 _Unwind_Backtrace(Unwind_Trace, &arg);99 // We need to pop a few frames so that pc is on top.100 uptr to_pop = LocatePcInTrace(pc);101 // trace_buffer[0] belongs to the current function so we always pop it,102 // unless there is only 1 frame in the stack trace (1 frame is always better103 // than 0!).104 // 1-frame stacks don't normally happen, but this depends on the actual105 // unwinder implementation (libgcc, libunwind, etc) which is outside of our106 // control.107 if (to_pop == 0 && size > 1)108 to_pop = 1;109 PopStackFrames(to_pop);110 trace_buffer[0] = pc;111}112 113void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) {114 CHECK(context);115 CHECK_GE(max_depth, 2);116 if (!unwind_backtrace_signal_arch) {117 UnwindSlow(pc, max_depth);118 return;119 }120 121 void *map = acquire_my_map_info_list();122 CHECK(map);123 InternalMmapVector<backtrace_frame_t> frames(kStackTraceMax);124 // siginfo argument appears to be unused.125 sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map,126 frames.data(),127 /* ignore_depth */ 0, max_depth);128 release_my_map_info_list(map);129 if (res < 0) return;130 CHECK_LE((uptr)res, kStackTraceMax);131 132 size = 0;133 // +2 compensate for libcorkscrew unwinder returning addresses of call134 // instructions instead of raw return addresses.135 for (sptr i = 0; i < res; ++i)136 trace_buffer[size++] = frames[i].absolute_pc + 2;137}138 139} // namespace __sanitizer140 141#endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||142 // SANITIZER_SOLARIS || SANITIZER_HAIKU143