sanitizer_unwind_linux_libcdep.cpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  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-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This file contains the unwind.h-based (aka "slow") stack unwinding routines
  10. // available to the tools on Linux, Android, NetBSD, FreeBSD, and Solaris.
  11. //===----------------------------------------------------------------------===//
  12. #include "sanitizer_platform.h"
  13. #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \
  14. SANITIZER_SOLARIS
  15. #include "sanitizer_common.h"
  16. #include "sanitizer_stacktrace.h"
  17. #if SANITIZER_ANDROID
  18. #include <dlfcn.h> // for dlopen()
  19. #endif
  20. #if SANITIZER_FREEBSD
  21. #define _GNU_SOURCE // to declare _Unwind_Backtrace() from <unwind.h>
  22. #endif
  23. #include <unwind.h>
  24. namespace __sanitizer {
  25. namespace {
  26. //---------------------------- UnwindSlow --------------------------------------
  27. typedef struct {
  28. uptr absolute_pc;
  29. uptr stack_top;
  30. uptr stack_size;
  31. } backtrace_frame_t;
  32. extern "C" {
  33. typedef void *(*acquire_my_map_info_list_func)();
  34. typedef void (*release_my_map_info_list_func)(void *map);
  35. typedef sptr (*unwind_backtrace_signal_arch_func)(
  36. void *siginfo, void *sigcontext, void *map_info_list,
  37. backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth);
  38. acquire_my_map_info_list_func acquire_my_map_info_list;
  39. release_my_map_info_list_func release_my_map_info_list;
  40. unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;
  41. } // extern "C"
  42. #if defined(__arm__) && !SANITIZER_NETBSD
  43. // NetBSD uses dwarf EH
  44. #define UNWIND_STOP _URC_END_OF_STACK
  45. #define UNWIND_CONTINUE _URC_NO_REASON
  46. #else
  47. #define UNWIND_STOP _URC_NORMAL_STOP
  48. #define UNWIND_CONTINUE _URC_NO_REASON
  49. #endif
  50. uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
  51. #if defined(__arm__) && !SANITIZER_MAC
  52. uptr val;
  53. _Unwind_VRS_Result res = _Unwind_VRS_Get(ctx, _UVRSC_CORE,
  54. 15 /* r15 = PC */, _UVRSD_UINT32, &val);
  55. CHECK(res == _UVRSR_OK && "_Unwind_VRS_Get failed");
  56. // Clear the Thumb bit.
  57. return val & ~(uptr)1;
  58. #else
  59. return (uptr)_Unwind_GetIP(ctx);
  60. #endif
  61. }
  62. struct UnwindTraceArg {
  63. BufferedStackTrace *stack;
  64. u32 max_depth;
  65. };
  66. _Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
  67. UnwindTraceArg *arg = (UnwindTraceArg*)param;
  68. CHECK_LT(arg->stack->size, arg->max_depth);
  69. uptr pc = Unwind_GetIP(ctx);
  70. const uptr kPageSize = GetPageSizeCached();
  71. // Let's assume that any pointer in the 0th page (i.e. <0x1000 on i386 and
  72. // x86_64) is invalid and stop unwinding here. If we're adding support for
  73. // a platform where this isn't true, we need to reconsider this check.
  74. if (pc < kPageSize) return UNWIND_STOP;
  75. arg->stack->trace_buffer[arg->stack->size++] = pc;
  76. if (arg->stack->size == arg->max_depth) return UNWIND_STOP;
  77. return UNWIND_CONTINUE;
  78. }
  79. } // namespace
  80. #if SANITIZER_ANDROID
  81. void SanitizerInitializeUnwinder() {
  82. if (AndroidGetApiLevel() >= ANDROID_LOLLIPOP_MR1) return;
  83. // Pre-lollipop Android can not unwind through signal handler frames with
  84. // libgcc unwinder, but it has a libcorkscrew.so library with the necessary
  85. // workarounds.
  86. void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
  87. if (!p) {
  88. VReport(1,
  89. "Failed to open libcorkscrew.so. You may see broken stack traces "
  90. "in SEGV reports.");
  91. return;
  92. }
  93. acquire_my_map_info_list =
  94. (acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");
  95. release_my_map_info_list =
  96. (release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");
  97. unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(
  98. p, "unwind_backtrace_signal_arch");
  99. if (!acquire_my_map_info_list || !release_my_map_info_list ||
  100. !unwind_backtrace_signal_arch) {
  101. VReport(1,
  102. "Failed to find one of the required symbols in libcorkscrew.so. "
  103. "You may see broken stack traces in SEGV reports.");
  104. acquire_my_map_info_list = 0;
  105. unwind_backtrace_signal_arch = 0;
  106. release_my_map_info_list = 0;
  107. }
  108. }
  109. #endif
  110. void BufferedStackTrace::UnwindSlow(uptr pc, u32 max_depth) {
  111. CHECK_GE(max_depth, 2);
  112. size = 0;
  113. UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
  114. _Unwind_Backtrace(Unwind_Trace, &arg);
  115. // We need to pop a few frames so that pc is on top.
  116. uptr to_pop = LocatePcInTrace(pc);
  117. // trace_buffer[0] belongs to the current function so we always pop it,
  118. // unless there is only 1 frame in the stack trace (1 frame is always better
  119. // than 0!).
  120. // 1-frame stacks don't normally happen, but this depends on the actual
  121. // unwinder implementation (libgcc, libunwind, etc) which is outside of our
  122. // control.
  123. if (to_pop == 0 && size > 1)
  124. to_pop = 1;
  125. PopStackFrames(to_pop);
  126. #if defined(__GNUC__) && defined(__sparc__)
  127. // __builtin_return_address returns the address of the call instruction
  128. // on the SPARC and not the return address, so we need to compensate.
  129. trace_buffer[0] = GetNextInstructionPc(pc);
  130. #else
  131. trace_buffer[0] = pc;
  132. #endif
  133. }
  134. void BufferedStackTrace::UnwindSlow(uptr pc, void *context, u32 max_depth) {
  135. CHECK(context);
  136. CHECK_GE(max_depth, 2);
  137. if (!unwind_backtrace_signal_arch) {
  138. UnwindSlow(pc, max_depth);
  139. return;
  140. }
  141. void *map = acquire_my_map_info_list();
  142. CHECK(map);
  143. InternalMmapVector<backtrace_frame_t> frames(kStackTraceMax);
  144. // siginfo argument appears to be unused.
  145. sptr res = unwind_backtrace_signal_arch(/* siginfo */ 0, context, map,
  146. frames.data(),
  147. /* ignore_depth */ 0, max_depth);
  148. release_my_map_info_list(map);
  149. if (res < 0) return;
  150. CHECK_LE((uptr)res, kStackTraceMax);
  151. size = 0;
  152. // +2 compensate for libcorkscrew unwinder returning addresses of call
  153. // instructions instead of raw return addresses.
  154. for (sptr i = 0; i < res; ++i)
  155. trace_buffer[size++] = frames[i].absolute_pc + 2;
  156. }
  157. } // namespace __sanitizer
  158. #endif // SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD ||
  159. // SANITIZER_SOLARIS