blob: f4859d7c210af087fffcfa41ebc0bb900f88b3ab [file] [log] [blame]
Austin Schuh36244a12019-09-21 17:52:38 -07001#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
17#include "absl/base/attributes.h"
18#include "absl/debugging/internal/address_is_readable.h"
19#include "absl/debugging/internal/vdso_support.h" // a no-op on non-elf or non-glibc systems
20#include "absl/debugging/stacktrace.h"
21
22static const uintptr_t kUnknownFrameSize = 0;
23
24#if defined(__linux__)
25// Returns the address of the VDSO __kernel_rt_sigreturn function, if present.
26static const unsigned char* GetKernelRtSigreturnAddress() {
27 constexpr uintptr_t kImpossibleAddress = 1;
28 ABSL_CONST_INIT static std::atomic<uintptr_t> memoized{kImpossibleAddress};
29 uintptr_t address = memoized.load(std::memory_order_relaxed);
30 if (address != kImpossibleAddress) {
31 return reinterpret_cast<const unsigned char*>(address);
32 }
33
34 address = reinterpret_cast<uintptr_t>(nullptr);
35
36#ifdef ABSL_HAVE_VDSO_SUPPORT
37 absl::debugging_internal::VDSOSupport vdso;
38 if (vdso.IsPresent()) {
39 absl::debugging_internal::VDSOSupport::SymbolInfo symbol_info;
Austin Schuhb4691e92020-12-31 12:37:18 -080040 auto lookup = [&](int type) {
41 return vdso.LookupSymbol("__kernel_rt_sigreturn", "LINUX_2.6.39", type,
42 &symbol_info);
43 };
44 if ((!lookup(STT_FUNC) && !lookup(STT_NOTYPE)) ||
Austin Schuh36244a12019-09-21 17:52:38 -070045 symbol_info.address == nullptr) {
46 // Unexpected: VDSO is present, yet the expected symbol is missing
47 // or null.
48 assert(false && "VDSO is present, but doesn't have expected symbol");
49 } else {
50 if (reinterpret_cast<uintptr_t>(symbol_info.address) !=
51 kImpossibleAddress) {
52 address = reinterpret_cast<uintptr_t>(symbol_info.address);
53 } else {
54 assert(false && "VDSO returned invalid address");
55 }
56 }
57 }
58#endif
59
60 memoized.store(address, std::memory_order_relaxed);
61 return reinterpret_cast<const unsigned char*>(address);
62}
63#endif // __linux__
64
65// Compute the size of a stack frame in [low..high). We assume that
66// low < high. Return size of kUnknownFrameSize.
67template<typename T>
68static inline uintptr_t ComputeStackFrameSize(const T* low,
69 const T* high) {
70 const char* low_char_ptr = reinterpret_cast<const char *>(low);
71 const char* high_char_ptr = reinterpret_cast<const char *>(high);
72 return low < high ? high_char_ptr - low_char_ptr : kUnknownFrameSize;
73}
74
75// Given a pointer to a stack frame, locate and return the calling
76// stackframe, or return null if no stackframe can be found. Perform sanity
77// checks (the strictness of which is controlled by the boolean parameter
78// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned.
79template<bool STRICT_UNWINDING, bool WITH_CONTEXT>
Austin Schuhb4691e92020-12-31 12:37:18 -080080ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
81ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack.
Austin Schuh36244a12019-09-21 17:52:38 -070082static void **NextStackFrame(void **old_frame_pointer, const void *uc) {
83 void **new_frame_pointer = reinterpret_cast<void**>(*old_frame_pointer);
84 bool check_frame_size = true;
85
86#if defined(__linux__)
87 if (WITH_CONTEXT && uc != nullptr) {
88 // Check to see if next frame's return address is __kernel_rt_sigreturn.
89 if (old_frame_pointer[1] == GetKernelRtSigreturnAddress()) {
90 const ucontext_t *ucv = static_cast<const ucontext_t *>(uc);
91 // old_frame_pointer[0] is not suitable for unwinding, look at
92 // ucontext to discover frame pointer before signal.
93 void **const pre_signal_frame_pointer =
94 reinterpret_cast<void **>(ucv->uc_mcontext.regs[29]);
95
96 // Check that alleged frame pointer is actually readable. This is to
97 // prevent "double fault" in case we hit the first fault due to e.g.
98 // stack corruption.
99 if (!absl::debugging_internal::AddressIsReadable(
100 pre_signal_frame_pointer))
101 return nullptr;
102
103 // Alleged frame pointer is readable, use it for further unwinding.
104 new_frame_pointer = pre_signal_frame_pointer;
105
106 // Skip frame size check if we return from a signal. We may be using a
107 // an alternate stack for signals.
108 check_frame_size = false;
109 }
110 }
111#endif
112
113 // aarch64 ABI requires stack pointer to be 16-byte-aligned.
114 if ((reinterpret_cast<uintptr_t>(new_frame_pointer) & 15) != 0)
115 return nullptr;
116
117 // Check frame size. In strict mode, we assume frames to be under
118 // 100,000 bytes. In non-strict mode, we relax the limit to 1MB.
119 if (check_frame_size) {
120 const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000;
121 const uintptr_t frame_size =
122 ComputeStackFrameSize(old_frame_pointer, new_frame_pointer);
123 if (frame_size == kUnknownFrameSize || frame_size > max_size)
124 return nullptr;
125 }
126
127 return new_frame_pointer;
128}
129
130template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT>
Austin Schuhb4691e92020-12-31 12:37:18 -0800131ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack.
132ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack.
Austin Schuh36244a12019-09-21 17:52:38 -0700133static int UnwindImpl(void** result, int* sizes, int max_depth, int skip_count,
134 const void *ucp, int *min_dropped_frames) {
135#ifdef __GNUC__
136 void **frame_pointer = reinterpret_cast<void**>(__builtin_frame_address(0));
137#else
138# error reading stack point not yet supported on this platform.
139#endif
140
141 skip_count++; // Skip the frame for this function.
142 int n = 0;
143
144 // The frame pointer points to low address of a frame. The first 64-bit
145 // word of a frame points to the next frame up the call chain, which normally
146 // is just after the high address of the current frame. The second word of
147 // a frame contains return adress of to the caller. To find a pc value
148 // associated with the current frame, we need to go down a level in the call
149 // chain. So we remember return the address of the last frame seen. This
150 // does not work for the first stack frame, which belongs to UnwindImp() but
151 // we skip the frame for UnwindImp() anyway.
152 void* prev_return_address = nullptr;
153
154 while (frame_pointer && n < max_depth) {
155 // The absl::GetStackFrames routine is called when we are in some
156 // informational context (the failure signal handler for example).
157 // Use the non-strict unwinding rules to produce a stack trace
158 // that is as complete as possible (even if it contains a few bogus
159 // entries in some rare cases).
160 void **next_frame_pointer =
161 NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp);
162
163 if (skip_count > 0) {
164 skip_count--;
165 } else {
166 result[n] = prev_return_address;
167 if (IS_STACK_FRAMES) {
168 sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer);
169 }
170 n++;
171 }
172 prev_return_address = frame_pointer[1];
173 frame_pointer = next_frame_pointer;
174 }
175 if (min_dropped_frames != nullptr) {
176 // Implementation detail: we clamp the max of frames we are willing to
177 // count, so as not to spend too much time in the loop below.
178 const int kMaxUnwind = 200;
179 int j = 0;
180 for (; frame_pointer != nullptr && j < kMaxUnwind; j++) {
181 frame_pointer =
182 NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp);
183 }
184 *min_dropped_frames = j;
185 }
186 return n;
187}
188
189namespace absl {
Austin Schuhb4691e92020-12-31 12:37:18 -0800190ABSL_NAMESPACE_BEGIN
Austin Schuh36244a12019-09-21 17:52:38 -0700191namespace debugging_internal {
192bool StackTraceWorksForTest() {
193 return true;
194}
195} // namespace debugging_internal
Austin Schuhb4691e92020-12-31 12:37:18 -0800196ABSL_NAMESPACE_END
Austin Schuh36244a12019-09-21 17:52:38 -0700197} // namespace absl
198
199#endif // ABSL_DEBUGGING_INTERNAL_STACKTRACE_AARCH64_INL_H_