// Copyright 2021 The Abseil Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_ #define ABSL_DEBUGGING_INTERNAL_STACKTRACE_RISCV_INL_H_ // Generate stack trace for riscv #include #include "absl/base/config.h" #if defined(__linux__) #include #include #include #endif #include #include #include #include #include #include #include "absl/base/attributes.h" #include "absl/debugging/stacktrace.h" static const uintptr_t kUnknownFrameSize = 0; // Compute the size of a stack frame in [low..high). We assume that low < high. // Return size of kUnknownFrameSize. template static inline uintptr_t ComputeStackFrameSize(const T *low, const T *high) { const char *low_char_ptr = reinterpret_cast(low); const char *high_char_ptr = reinterpret_cast(high); return low < high ? high_char_ptr - low_char_ptr : kUnknownFrameSize; } // Given a pointer to a stack frame, locate and return the calling stackframe, // or return null if no stackframe can be found. Perform sanity checks (the // strictness of which is controlled by the boolean parameter // "STRICT_UNWINDING") to reduce the chance that a bad pointer is returned. template ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. static void ** NextStackFrame(void **old_frame_pointer, const void *uc, const std::pair range) { // . // . // . // +-> +----------------+ // | | return address | // | | previous fp | // | | ... | // | +----------------+ <-+ // | | return address | | // +---|- previous fp | | // | ... | | // $fp ->|----------------+ | // | return address | | // | previous fp -|---+ // $sp ->| ... | // +----------------+ void **new_frame_pointer = reinterpret_cast(old_frame_pointer[-2]); uintptr_t frame_pointer = reinterpret_cast(new_frame_pointer); // The RISCV ELF psABI mandates that the stack pointer is always 16-byte // aligned. // TODO(#1236) this doesn't hold for ILP32E which only mandates a 4-byte // alignment. if (frame_pointer & 15) return nullptr; // If the new frame pointer matches the signal context, avoid terminating // early to deal with alternate signal stacks. if (WITH_CONTEXT) if (const ucontext_t *ucv = static_cast(uc)) // RISCV ELF psABI has the frame pointer at x8/fp/s0. // -- RISCV psABI Table 18.2 if (ucv->uc_mcontext.__gregs[8] == frame_pointer) return new_frame_pointer; // Check frame size. In strict mode, we assume frames to be under 100,000 // bytes. In non-strict mode, we relax the limit to 1MB. const uintptr_t max_size = STRICT_UNWINDING ? 100000 : 1000000; const uintptr_t frame_size = ComputeStackFrameSize(old_frame_pointer, new_frame_pointer); if (frame_size == kUnknownFrameSize) { if (STRICT_UNWINDING) return nullptr; // In non-strict mode permit non-contiguous stacks (e.g. alternate signal // frame handling). if (reinterpret_cast(new_frame_pointer) < range.first || reinterpret_cast(new_frame_pointer) > range.second) return nullptr; } if (frame_size > max_size) return nullptr; return new_frame_pointer; } template ABSL_ATTRIBUTE_NO_SANITIZE_ADDRESS // May read random elements from stack. ABSL_ATTRIBUTE_NO_SANITIZE_MEMORY // May read random elements from stack. static int UnwindImpl(void **result, int *sizes, int max_depth, int skip_count, const void *ucp, int *min_dropped_frames) { // The `frame_pointer` that is computed here points to the top of the frame. // The two words preceding the address are the return address and the previous // frame pointer. #if defined(__GNUC__) void **frame_pointer = reinterpret_cast(__builtin_frame_address(0)); #else #error reading stack pointer not yet supported on this platform #endif std::pair stack = { // assume that the first page is not the stack. static_cast(sysconf(_SC_PAGESIZE)), std::numeric_limits::max() - sizeof(void *) }; int n = 0; void *return_address = nullptr; while (frame_pointer && n < max_depth) { return_address = frame_pointer[-1]; // The absl::GetStackFrames routine is called when we are in some // informational context (the failure signal handler for example). Use the // non-strict unwinding rules to produce a stack trace that is as complete // as possible (even if it contains a few bogus entries in some rare cases). void **next_frame_pointer = NextStackFrame(frame_pointer, ucp, stack); if (skip_count > 0) { skip_count--; } else { result[n] = return_address; if (IS_STACK_FRAMES) { sizes[n] = ComputeStackFrameSize(frame_pointer, next_frame_pointer); } n++; } frame_pointer = next_frame_pointer; } if (min_dropped_frames != nullptr) { // Implementation detail: we clamp the max of frames we are willing to // count, so as not to spend too much time in the loop below. const int kMaxUnwind = 200; int num_dropped_frames = 0; for (int j = 0; frame_pointer != nullptr && j < kMaxUnwind; j++) { if (skip_count > 0) { skip_count--; } else { num_dropped_frames++; } frame_pointer = NextStackFrame(frame_pointer, ucp, stack); } *min_dropped_frames = num_dropped_frames; } return n; } namespace absl { ABSL_NAMESPACE_BEGIN namespace debugging_internal { bool StackTraceWorksForTest() { return true; } } // namespace debugging_internal ABSL_NAMESPACE_END } // namespace absl #endif