168d75effSDimitry Andric //===-- sanitizer_unwind_linux_libcdep.cpp --------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
968d75effSDimitry Andric // This file contains the unwind.h-based (aka "slow") stack unwinding routines
1068d75effSDimitry Andric // available to the tools on Linux, Android, NetBSD, FreeBSD, and Solaris.
1168d75effSDimitry Andric //===----------------------------------------------------------------------===//
1268d75effSDimitry Andric
1368d75effSDimitry Andric #include "sanitizer_platform.h"
1468d75effSDimitry Andric #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
1568d75effSDimitry Andric SANITIZER_SOLARIS
1668d75effSDimitry Andric #include "sanitizer_common.h"
1768d75effSDimitry Andric #include "sanitizer_stacktrace.h"
1868d75effSDimitry Andric
1968d75effSDimitry Andric #if SANITIZER_ANDROID
2068d75effSDimitry Andric #include <dlfcn.h> // for dlopen()
2168d75effSDimitry Andric #endif
2268d75effSDimitry Andric
2368d75effSDimitry Andric #if SANITIZER_FREEBSD
2468d75effSDimitry Andric #define _GNU_SOURCE // to declare _Unwind_Backtrace() from <unwind.h>
2568d75effSDimitry Andric #endif
2668d75effSDimitry Andric #include <unwind.h>
2768d75effSDimitry Andric
2868d75effSDimitry Andric namespace __sanitizer {
2968d75effSDimitry Andric
3068d75effSDimitry Andric namespace {
3168d75effSDimitry Andric
3268d75effSDimitry Andric //---------------------------- UnwindSlow --------------------------------------
3368d75effSDimitry Andric
3468d75effSDimitry Andric typedef struct {
3568d75effSDimitry Andric uptr absolute_pc;
3668d75effSDimitry Andric uptr stack_top;
3768d75effSDimitry Andric uptr stack_size;
3868d75effSDimitry Andric } backtrace_frame_t;
3968d75effSDimitry Andric
4068d75effSDimitry Andric extern "C" {
4168d75effSDimitry Andric typedef void *(*acquire_my_map_info_list_func)();
4268d75effSDimitry Andric typedef void (*release_my_map_info_list_func)(void *map);
4368d75effSDimitry Andric typedef sptr (*unwind_backtrace_signal_arch_func)(
4468d75effSDimitry Andric void *siginfo, void *sigcontext, void *map_info_list,
4568d75effSDimitry Andric backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth);
4668d75effSDimitry Andric acquire_my_map_info_list_func acquire_my_map_info_list;
4768d75effSDimitry Andric release_my_map_info_list_func release_my_map_info_list;
4868d75effSDimitry Andric unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;
4968d75effSDimitry Andric } // extern "C"
5068d75effSDimitry Andric
5168d75effSDimitry Andric #if defined(__arm__) && !SANITIZER_NETBSD
5268d75effSDimitry Andric // NetBSD uses dwarf EH
5368d75effSDimitry Andric #define UNWIND_STOP _URC_END_OF_STACK
5468d75effSDimitry Andric #define UNWIND_CONTINUE _URC_NO_REASON
5568d75effSDimitry Andric #else
5668d75effSDimitry Andric #define UNWIND_STOP _URC_NORMAL_STOP
5768d75effSDimitry Andric #define UNWIND_CONTINUE _URC_NO_REASON
5868d75effSDimitry Andric #endif
5968d75effSDimitry Andric
Unwind_GetIP(struct _Unwind_Context * ctx)6068d75effSDimitry Andric uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
61*81ad6265SDimitry Andric #if defined(__arm__) && !SANITIZER_APPLE
6268d75effSDimitry Andric uptr val;
6368d75effSDimitry Andric _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
6468d75effSDimitry Andric 15 /* r15 = PC */, _UVRSD_UINT32, &val);
6568d75effSDimitry Andric CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
6668d75effSDimitry Andric // Clear the Thumb bit.
6768d75effSDimitry Andric return val & ~(uptr)1;
6868d75effSDimitry Andric #else
6968d75effSDimitry Andric return (uptr)_Unwind_GetIP(ctx);
7068d75effSDimitry Andric #endif
7168d75effSDimitry Andric }
7268d75effSDimitry Andric
7368d75effSDimitry Andric struct UnwindTraceArg {
7468d75effSDimitry Andric BufferedStackTrace *stack;
7568d75effSDimitry Andric u32 max_depth;
7668d75effSDimitry Andric };
7768d75effSDimitry Andric
Unwind_Trace(struct _Unwind_Context * ctx,void * param)7868d75effSDimitry Andric _Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
7968d75effSDimitry Andric UnwindTraceArg *arg = (UnwindTraceArg*)param;
8068d75effSDimitry Andric CHECK_LT(arg->stack->size, arg->max_depth);
8168d75effSDimitry Andric uptr pc = Unwind_GetIP(ctx);
8268d75effSDimitry Andric const uptr kPageSize = GetPageSizeCached();
8368d75effSDimitry Andric // Let's assume that any pointer in the 0th page (i.e. <0x1000 on i386 and
8468d75effSDimitry Andric // x86_64) is invalid and stop unwinding here. If we're adding support for
8568d75effSDimitry Andric // a platform where this isn't true, we need to reconsider this check.
8668d75effSDimitry Andric if (pc < kPageSize) return UNWIND_STOP;
8768d75effSDimitry Andric arg->stack->trace_buffer[arg->stack->size++] = pc;
8868d75effSDimitry Andric if (arg->stack->size == arg->max_depth) return UNWIND_STOP;
8968d75effSDimitry Andric return UNWIND_CONTINUE;
9068d75effSDimitry Andric }
9168d75effSDimitry Andric
9268d75effSDimitry Andric } // namespace
9368d75effSDimitry Andric
9468d75effSDimitry Andric #if SANITIZER_ANDROID
SanitizerInitializeUnwinder()9568d75effSDimitry Andric void SanitizerInitializeUnwinder() {
9668d75effSDimitry Andric if (AndroidGetApiLevel() >= ANDROID_LOLLIPOP_MR1) return;
9768d75effSDimitry Andric
9868d75effSDimitry Andric // Pre-lollipop Android can not unwind through signal handler frames with
9968d75effSDimitry Andric // libgcc unwinder, but it has a libcorkscrew.so library with the necessary
10068d75effSDimitry Andric // workarounds.
10168d75effSDimitry Andric void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
10268d75effSDimitry Andric if (!p) {
10368d75effSDimitry Andric VReport(1,
10468d75effSDimitry Andric "Failed to open libcorkscrew.so. You may see broken stack traces "
10568d75effSDimitry Andric "in SEGV reports.");
10668d75effSDimitry Andric return;
10768d75effSDimitry Andric }
10868d75effSDimitry Andric acquire_my_map_info_list =
10968d75effSDimitry Andric (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");
11068d75effSDimitry Andric release_my_map_info_list =
11168d75effSDimitry Andric (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");
11268d75effSDimitry Andric unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(
11368d75effSDimitry Andric p, "unwind_backtrace_signal_arch");
11468d75effSDimitry Andric if (!acquire_my_map_info_list || !release_my_map_info_list ||
11568d75effSDimitry Andric !unwind_backtrace_signal_arch) {
11668d75effSDimitry Andric VReport(1,
11768d75effSDimitry Andric "Failed to find one of the required symbols in libcorkscrew.so. "
11868d75effSDimitry Andric "You may see broken stack traces in SEGV reports.");
11968d75effSDimitry Andric acquire_my_map_info_list = 0;
12068d75effSDimitry Andric unwind_backtrace_signal_arch = 0;
12168d75effSDimitry Andric release_my_map_info_list = 0;
12268d75effSDimitry Andric }
12368d75effSDimitry Andric }
12468d75effSDimitry Andric #endif
12568d75effSDimitry Andric
UnwindSlow(uptr pc,u32 max_depth)12668d75effSDimitry Andric void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) {
12768d75effSDimitry Andric CHECK_GE(max_depth, 2);
12868d75effSDimitry Andric size = 0;
12968d75effSDimitry Andric UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
13068d75effSDimitry Andric _Unwind_Backtrace(Unwind_Trace, &arg);
13168d75effSDimitry Andric // We need to pop a few frames so that pc is on top.
13268d75effSDimitry Andric uptr to_pop = LocatePcInTrace(pc);
13368d75effSDimitry Andric // trace_buffer[0] belongs to the current function so we always pop it,
13468d75effSDimitry Andric // unless there is only 1 frame in the stack trace (1 frame is always better
13568d75effSDimitry Andric // than 0!).
13668d75effSDimitry Andric // 1-frame stacks don't normally happen, but this depends on the actual
13768d75effSDimitry Andric // unwinder implementation (libgcc, libunwind, etc) which is outside of our
13868d75effSDimitry Andric // control.
13968d75effSDimitry Andric if (to_pop == 0 && size > 1)
14068d75effSDimitry Andric to_pop = 1;
14168d75effSDimitry Andric PopStackFrames(to_pop);
14268d75effSDimitry Andric trace_buffer[0] = pc;
14368d75effSDimitry Andric }
14468d75effSDimitry Andric
UnwindSlow(uptr pc,void * context,u32 max_depth)14568d75effSDimitry Andric void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) {
14668d75effSDimitry Andric CHECK(context);
14768d75effSDimitry Andric CHECK_GE(max_depth, 2);
14868d75effSDimitry Andric if (!unwind_backtrace_signal_arch) {
14968d75effSDimitry Andric UnwindSlow(pc, max_depth);
15068d75effSDimitry Andric return;
15168d75effSDimitry Andric }
15268d75effSDimitry Andric
15368d75effSDimitry Andric void *map = acquire_my_map_info_list();
15468d75effSDimitry Andric CHECK(map);
15568d75effSDimitry Andric InternalMmapVector<backtrace_frame_t> frames(kStackTraceMax);
15668d75effSDimitry Andric // siginfo argument appears to be unused.
15768d75effSDimitry Andric sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map,
15868d75effSDimitry Andric frames.data(),
15968d75effSDimitry Andric /* ignore_depth */ 0, max_depth);
16068d75effSDimitry Andric release_my_map_info_list(map);
16168d75effSDimitry Andric if (res < 0) return;
16268d75effSDimitry Andric CHECK_LE((uptr)res, kStackTraceMax);
16368d75effSDimitry Andric
16468d75effSDimitry Andric size = 0;
16568d75effSDimitry Andric // +2 compensate for libcorkscrew unwinder returning addresses of call
16668d75effSDimitry Andric // instructions instead of raw return addresses.
16768d75effSDimitry Andric for (sptr i = 0; i < res; ++i)
16868d75effSDimitry Andric trace_buffer[size++] = frames[i].absolute_pc + 2;
16968d75effSDimitry Andric }
17068d75effSDimitry Andric
17168d75effSDimitry Andric } // namespace __sanitizer
17268d75effSDimitry Andric
17368d75effSDimitry Andric #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||
17468d75effSDimitry Andric // SANITIZER_SOLARIS
175