1// Copyright 2021 The Abseil Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15#ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_ 16#define ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_ 17 18// Generate stack trace for riscv 19 20#include <sys/ucontext.h> 21 22#include "absl/base/config.h" 23#if defined(__linux__) 24#include <sys/mman.h> 25#include <ucontext.h> 26#include <unistd.h> 27#endif 28 29#include <atomic> 30#include <cassert> 31#include <cstdint> 32#include <iostream> 33#include <limits> 34#include <utility> 35 36#include "absl/base/attributes.h" 37#include "absl/debugging/stacktrace.h" 38 39static const uintptr_t kUnknownFrameSize = 0; 40 41// Compute the size of a stack frame in [low..high). We assume that low < high. 42// Return size of kUnknownFrameSize. 43template <typename T> 44static inline uintptr_t ComputeStackFrameSize(const T *low, const T *high) { 45 const char *low_char_ptr = reinterpret_cast<const char *>(low); 46 const char *high_char_ptr = reinterpret_cast<const char *>(high); 47 return low < high ? high_char_ptr - low_char_ptr : kUnknownFrameSize; 48} 49 50// Given a pointer to a stack frame, locate and return the calling stackframe, 51// or return null if no stackframe can be found. Perform sanity checks (the 52// strictness of which is controlled by the boolean parameter 53// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. 54template <bool STRICT_UNWINDING, bool WITH_CONTEXT> 55ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. 56ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. 57static void ** NextStackFrame(void **old_frame_pointer, const void *uc, 58 const std::pair<size_t, size_t> range) { 59 // . 60 // . 61 // . 62 // +-> +----------------+ 63 // | | return address | 64 // | | previous fp | 65 // | | ... | 66 // | +----------------+ <-+ 67 // | | return address | | 68 // +---|- previous fp | | 69 // | ... | | 70 // $fp ->|----------------+ | 71 // | return address | | 72 // | previous fp -|---+ 73 // $sp ->| ... | 74 // +----------------+ 75 void **new_frame_pointer = reinterpret_cast<void **>(old_frame_pointer[-2]); 76 uintptr_t frame_pointer = reinterpret_cast<uintptr_t>(new_frame_pointer); 77 78 // The RISCV ELF psABI mandates that the stack pointer is always 16-byte 79 // aligned. 80 // TODO(#1236) this doesn't hold for ILP32E which only mandates a 4-byte 81 // alignment. 82 if (frame_pointer & 15) 83 return nullptr; 84 85 // If the new frame pointer matches the signal context, avoid terminating 86 // early to deal with alternate signal stacks. 87 if (WITH_CONTEXT) 88 if (const ucontext_t *ucv = static_cast<const ucontext_t *>(uc)) 89 // RISCV ELF psABI has the frame pointer at x8/fp/s0. 90 // -- RISCV psABI Table 18.2 91 if (ucv->uc_mcontext.__gregs[8] == frame_pointer) 92 return new_frame_pointer; 93 94 // Check frame size. In strict mode, we assume frames to be under 100,000 95 // bytes. In non-strict mode, we relax the limit to 1MB. 96 const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000; 97 const uintptr_t frame_size = 98 ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); 99 if (frame_size == kUnknownFrameSize) { 100 if (STRICT_UNWINDING) 101 return nullptr; 102 103 // In non-strict mode permit non-contiguous stacks (e.g. alternate signal 104 // frame handling). 105 if (reinterpret_cast<uintptr_t>(new_frame_pointer) < range.first || 106 reinterpret_cast<uintptr_t>(new_frame_pointer) > range.second) 107 return nullptr; 108 } 109 110 if (frame_size > max_size) 111 return nullptr; 112 113 return new_frame_pointer; 114} 115 116template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT> 117ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. 118ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. 119static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, 120 const void *ucp, int *min_dropped_frames) { 121 // The `frame_pointer` that is computed here points to the top of the frame. 122 // The two words preceding the address are the return address and the previous 123 // frame pointer. 124#if defined(__GNUC__) 125 void **frame_pointer = reinterpret_cast<void **>(__builtin_frame_address(0)); 126#else 127#error reading stack pointer not yet supported on this platform 128#endif 129 130 std::pair<size_t, size_t> stack = { 131 // assume that the first page is not the stack. 132 static_cast<size_t>(sysconf(_SC_PAGESIZE)), 133 std::numeric_limits<size_t>::max() - sizeof(void *) 134 }; 135 136 int n = 0; 137 void *return_address = nullptr; 138 while (frame_pointer && n < max_depth) { 139 return_address = frame_pointer[-1]; 140 141 // The absl::GetStackFrames routine is called when we are in some 142 // informational context (the failure signal handler for example). Use the 143 // non-strict unwinding rules to produce a stack trace that is as complete 144 // as possible (even if it contains a few bogus entries in some rare cases). 145 void **next_frame_pointer = 146 NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp, 147 stack); 148 149 if (skip_count > 0) { 150 skip_count--; 151 } else { 152 result[n] = return_address; 153 if (IS_STACK_FRAMES) { 154 sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer); 155 } 156 n++; 157 } 158 159 frame_pointer = next_frame_pointer; 160 } 161 162 if (min_dropped_frames != nullptr) { 163 // Implementation detail: we clamp the max of frames we are willing to 164 // count, so as not to spend too much time in the loop below. 165 const int kMaxUnwind = 200; 166 int num_dropped_frames = 0; 167 for (int j = 0; frame_pointer != nullptr && j < kMaxUnwind; j++) { 168 if (skip_count > 0) { 169 skip_count--; 170 } else { 171 num_dropped_frames++; 172 } 173 frame_pointer = 174 NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp, 175 stack); 176 } 177 *min_dropped_frames = num_dropped_frames; 178 } 179 180 return n; 181} 182 183namespace absl { 184ABSL_NAMESPACE_BEGIN 185namespace debugging_internal { 186bool StackTraceWorksForTest() { return true; } 187} // namespace debugging_internal 188ABSL_NAMESPACE_END 189} // namespace absl 190 191#endif 192