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 constexpr ptrdiff_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 ptrdiff_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 ? static_cast<ptrdiff_t>(high_char_ptr - low_char_ptr) 48 : kUnknownFrameSize; 49} 50 51// Given a pointer to a stack frame, locate and return the calling stackframe, 52// or return null if no stackframe can be found. Perform sanity checks (the 53// strictness of which is controlled by the boolean parameter 54// "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. 55template <bool STRICT_UNWINDING, bool WITH_CONTEXT> 56ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. 57ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. 58static void ** NextStackFrame(void **old_frame_pointer, const void *uc, 59 const std::pair<size_t, size_t> range) { 60 // . 61 // . 62 // . 63 // +-> +----------------+ 64 // | | return address | 65 // | | previous fp | 66 // | | ... | 67 // | +----------------+ <-+ 68 // | | return address | | 69 // +---|- previous fp | | 70 // | ... | | 71 // $fp ->|----------------+ | 72 // | return address | | 73 // | previous fp -|---+ 74 // $sp ->| ... | 75 // +----------------+ 76 void **new_frame_pointer = reinterpret_cast<void **>(old_frame_pointer[-2]); 77 uintptr_t frame_pointer = reinterpret_cast<uintptr_t>(new_frame_pointer); 78 79 // The RISCV ELF psABI mandates that the stack pointer is always 16-byte 80 // aligned. 81 // TODO(#1236) this doesn't hold for ILP32E which only mandates a 4-byte 82 // alignment. 83 if (frame_pointer & 15) 84 return nullptr; 85 86 // If the new frame pointer matches the signal context, avoid terminating 87 // early to deal with alternate signal stacks. 88 if (WITH_CONTEXT) 89 if (const ucontext_t *ucv = static_cast<const ucontext_t *>(uc)) 90 // RISCV ELF psABI has the frame pointer at x8/fp/s0. 91 // -- RISCV psABI Table 18.2 92 if (ucv->uc_mcontext.__gregs[8] == frame_pointer) 93 return new_frame_pointer; 94 95 // Check frame size. In strict mode, we assume frames to be under 100,000 96 // bytes. In non-strict mode, we relax the limit to 1MB. 97 const ptrdiff_t max_size = STRICT_UNWINDING ? 100000 : 1000000; 98 const ptrdiff_t frame_size = 99 ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); 100 if (frame_size == kUnknownFrameSize) { 101 if (STRICT_UNWINDING) 102 return nullptr; 103 104 // In non-strict mode permit non-contiguous stacks (e.g. alternate signal 105 // frame handling). 106 if (reinterpret_cast<uintptr_t>(new_frame_pointer) < range.first || 107 reinterpret_cast<uintptr_t>(new_frame_pointer) > range.second) 108 return nullptr; 109 } 110 111 if (frame_size > max_size) 112 return nullptr; 113 114 return new_frame_pointer; 115} 116 117template <bool IS_STACK_FRAMES, bool IS_WITH_CONTEXT> 118ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. 119ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. 120static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, 121 const void *ucp, int *min_dropped_frames) { 122 // The `frame_pointer` that is computed here points to the top of the frame. 123 // The two words preceding the address are the return address and the previous 124 // frame pointer. 125#if defined(__GNUC__) 126 void **frame_pointer = reinterpret_cast<void **>(__builtin_frame_address(0)); 127#else 128#error reading stack pointer not yet supported on this platform 129#endif 130 131 std::pair<size_t, size_t> stack = { 132 // assume that the first page is not the stack. 133 static_cast<size_t>(sysconf(_SC_PAGESIZE)), 134 std::numeric_limits<size_t>::max() - sizeof(void *) 135 }; 136 137 int n = 0; 138 void *return_address = nullptr; 139 while (frame_pointer && n < max_depth) { 140 return_address = frame_pointer[-1]; 141 142 // The absl::GetStackFrames routine is called when we are in some 143 // informational context (the failure signal handler for example). Use the 144 // non-strict unwinding rules to produce a stack trace that is as complete 145 // as possible (even if it contains a few bogus entries in some rare cases). 146 void **next_frame_pointer = 147 NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp, 148 stack); 149 150 if (skip_count > 0) { 151 skip_count--; 152 } else { 153 result[n] = return_address; 154 if (IS_STACK_FRAMES) { 155 // NextStackFrame() has already checked that frame size fits to int 156 sizes[n] = static_cast<int>(ComputeStackFrameSize(frame_pointer, 157 next_frame_pointer)); 158 } 159 n++; 160 } 161 162 frame_pointer = next_frame_pointer; 163 } 164 165 if (min_dropped_frames != nullptr) { 166 // Implementation detail: we clamp the max of frames we are willing to 167 // count, so as not to spend too much time in the loop below. 168 const int kMaxUnwind = 200; 169 int num_dropped_frames = 0; 170 for (int j = 0; frame_pointer != nullptr && j < kMaxUnwind; j++) { 171 if (skip_count > 0) { 172 skip_count--; 173 } else { 174 num_dropped_frames++; 175 } 176 frame_pointer = 177 NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(frame_pointer, ucp, 178 stack); 179 } 180 *min_dropped_frames = num_dropped_frames; 181 } 182 183 return n; 184} 185 186namespace absl { 187ABSL_NAMESPACE_BEGIN 188namespace debugging_internal { 189bool StackTraceWorksForTest() { return true; } 190} // namespace debugging_internal 191ABSL_NAMESPACE_END 192} // namespace absl 193 194#endif 195