1#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_ 2#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_ 3 4// Generate stack tracer for aarch64 5 6#if defined(__linux__) 7#include <sys/mman.h> 8#include <ucontext.h> 9#include <unistd.h> 10#endif 11 12#include <atomic> 13#include <cassert> 14#include <cstdint> 15#include <iostream> 16#include <limits> 17 18#include "absl/base/attributes.h" 19#include "absl/debugging/internal/address_is_readable.h" 20#include "absl/debugging/internal/vdso_support.h" // a no-op on non-elf or non-glibc systems 21#include "absl/debugging/stacktrace.h" 22 23static const size_t kUnknownFrameSize = 0; 24// Stack end to use when we don't know the actual stack end 25// (effectively just the end of address space). 26constexpr uintptr_t kUnknownStackEnd = 27 std::numeric_limits<size_t>::max() - sizeof(void *); 28 29#if defined(__linux__) 30// Returns the address of the VDSO __kernel_rt_sigreturn function, if present. 31static const unsigned char* GetKernelRtSigreturnAddress() { 32 constexpr uintptr_t kImpossibleAddress = 1; 33 ABSL_CONST_INIT static std::atomic<uintptr_t> memoized{kImpossibleAddress}; 34 uintptr_t address = memoized.load(std::memory_order_relaxed); 35 if (address != kImpossibleAddress) { 36 return reinterpret_cast<const unsigned char*>(address); 37 } 38 39 address = reinterpret_cast<uintptr_t>(nullptr); 40 41#ifdef ABSL_HAVE_VDSO_SUPPORT 42 absl::debugging_internal::VDSOSupport vdso; 43 if (vdso.IsPresent()) { 44 absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info; 45 auto lookup = [&](int type) { 46 return vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_2.6.39", type, 47 &symbol_info); 48 }; 49 if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) || 50 symbol_info.address == nullptr) { 51 // Unexpected: VDSO is present, yet the expected symbol is missing 52 // or null. 53 assert(false && "VDSO is present, but doesn't have expected symbol"); 54 } else { 55 if (reinterpret_cast<uintptr_t>(symbol_info.address) != 56 kImpossibleAddress) { 57 address = reinterpret_cast<uintptr_t>(symbol_info.address); 58 } else { 59 assert(false && "VDSO returned invalid address"); 60 } 61 } 62 } 63#endif 64 65 memoized.store(address, std::memory_order_relaxed); 66 return reinterpret_cast<const unsigned char*>(address); 67} 68#endif // __linux__ 69 70// Compute the size of a stack frame in [low..high). We assume that 71// low < high. Return size of kUnknownFrameSize. 72template<typename T> 73static inline size_t ComputeStackFrameSize(const T* low, 74 const T* high) { 75 const char* low_char_ptr = reinterpret_cast<const char *>(low); 76 const char* high_char_ptr = reinterpret_cast<const char *>(high); 77 return low < high ? static_cast<size_t>(high_char_ptr - low_char_ptr) 78 : kUnknownFrameSize; 79} 80 81// Given a pointer to a stack frame, locate and return the calling 82// stackframe, or return null if no stackframe can be found. Perform sanity 83// checks (the strictness of which is controlled by the boolean parameter 84// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. 85template<bool STRICT_UNWINDING, bool WITH_CONTEXT> 86ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. 87ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. 88static void **NextStackFrame(void **old_frame_pointer, const void *uc, 89 size_t stack_low, size_t stack_high) { 90 void **new_frame_pointer = reinterpret_cast<void**>(*old_frame_pointer); 91 bool check_frame_size = true; 92 93#if defined(__linux__) 94 if (WITH_CONTEXT && uc != nullptr) { 95 // Check to see if next frame's return address is __kernel_rt_sigreturn. 96 if (old_frame_pointer[1] == GetKernelRtSigreturnAddress()) { 97 const ucontext_t *ucv = static_cast<const ucontext_t *>(uc); 98 // old_frame_pointer[0] is not suitable for unwinding, look at 99 // ucontext to discover frame pointer before signal. 100 void **const pre_signal_frame_pointer = 101 reinterpret_cast<void **>(ucv->uc_mcontext.regs[29]); 102 103 // The most recent signal always needs special handling to find the frame 104 // pointer, but a nested signal does not. If pre_signal_frame_pointer is 105 // earlier in the stack than the old_frame_pointer, then use it. If it is 106 // later, then we have already unwound through it and it needs no special 107 // handling. 108 if (pre_signal_frame_pointer >= old_frame_pointer) { 109 new_frame_pointer = pre_signal_frame_pointer; 110 } 111 // Check that alleged frame pointer is actually readable. This is to 112 // prevent "double fault" in case we hit the first fault due to e.g. 113 // stack corruption. 114 if (!absl::debugging_internal::AddressIsReadable( 115 new_frame_pointer)) 116 return nullptr; 117 118 // Skip frame size check if we return from a signal. We may be using a 119 // an alternate stack for signals. 120 check_frame_size = false; 121 } 122 } 123#endif 124 125 // The frame pointer should be 8-byte aligned. 126 if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 7) != 0) 127 return nullptr; 128 129 // Check frame size. In strict mode, we assume frames to be under 130 // 100,000 bytes. In non-strict mode, we relax the limit to 1MB. 131 if (check_frame_size) { 132 const size_t max_size = STRICT_UNWINDING ? 100000 : 1000000; 133 const size_t frame_size = 134 ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); 135 if (frame_size == kUnknownFrameSize) 136 return nullptr; 137 // A very large frame may mean corrupt memory or an erroneous frame 138 // pointer. But also maybe just a plain-old large frame. Assume that if the 139 // frame is within the known stack, then it is valid. 140 if (frame_size > max_size) { 141 if (stack_high < kUnknownStackEnd && 142 static_cast<size_t>(getpagesize()) < stack_low) { 143 const uintptr_t new_fp_u = 144 reinterpret_cast<uintptr_t>(new_frame_pointer); 145 // Stack bounds are known. 146 if (!(stack_low < new_fp_u && new_fp_u <= stack_high)) { 147 // new_frame_pointer is not within the known stack. 148 return nullptr; 149 } 150 } else { 151 // Stack bounds are unknown, prefer truncated stack to possible crash. 152 return nullptr; 153 } 154 } 155 } 156 157 return new_frame_pointer; 158} 159 160template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT> 161ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. 162ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. 163static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count, 164 const void *ucp, int *min_dropped_frames) { 165#ifdef __GNUC__ 166 void **frame_pointer = reinterpret_cast<void**>(__builtin_frame_address(0)); 167#else 168# error reading stack point not yet supported on this platform. 169#endif 170 skip_count++; // Skip the frame for this function. 171 int n = 0; 172 173 // Assume that the first page is not stack. 174 size_t stack_low = static_cast<size_t>(getpagesize()); 175 size_t stack_high = kUnknownStackEnd; 176 177 // The frame pointer points to low address of a frame. The first 64-bit 178 // word of a frame points to the next frame up the call chain, which normally 179 // is just after the high address of the current frame. The second word of 180 // a frame contains return address of to the caller. To find a pc value 181 // associated with the current frame, we need to go down a level in the call 182 // chain. So we remember return the address of the last frame seen. This 183 // does not work for the first stack frame, which belongs to UnwindImp() but 184 // we skip the frame for UnwindImp() anyway. 185 void* prev_return_address = nullptr; 186 // The nth frame size is the difference between the nth frame pointer and the 187 // the frame pointer below it in the call chain. There is no frame below the 188 // leaf frame, but this function is the leaf anyway, and we skip it. 189 void** prev_frame_pointer = nullptr; 190 191 while (frame_pointer && n < max_depth) { 192 if (skip_count > 0) { 193 skip_count--; 194 } else { 195 result[n] = prev_return_address; 196 if (IS_STACK_FRAMES) { 197 sizes[n] = static_cast<int>( 198 ComputeStackFrameSize(prev_frame_pointer, frame_pointer)); 199 } 200 n++; 201 } 202 prev_return_address = frame_pointer[1]; 203 prev_frame_pointer = frame_pointer; 204 // The absl::GetStackFrames routine is called when we are in some 205 // informational context (the failure signal handler for example). 206 // Use the non-strict unwinding rules to produce a stack trace 207 // that is as complete as possible (even if it contains a few bogus 208 // entries in some rare cases). 209 frame_pointer = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>( 210 frame_pointer, ucp, stack_low, stack_high); 211 } 212 213 if (min_dropped_frames != nullptr) { 214 // Implementation detail: we clamp the max of frames we are willing to 215 // count, so as not to spend too much time in the loop below. 216 const int kMaxUnwind = 200; 217 int num_dropped_frames = 0; 218 for (int j = 0; frame_pointer != nullptr && j < kMaxUnwind; j++) { 219 if (skip_count > 0) { 220 skip_count--; 221 } else { 222 num_dropped_frames++; 223 } 224 frame_pointer = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>( 225 frame_pointer, ucp, stack_low, stack_high); 226 } 227 *min_dropped_frames = num_dropped_frames; 228 } 229 return n; 230} 231 232namespace absl { 233ABSL_NAMESPACE_BEGIN 234namespace debugging_internal { 235bool StackTraceWorksForTest() { 236 return true; 237} 238} // namespace debugging_internal 239ABSL_NAMESPACE_END 240} // namespace absl 241 242#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_ 243