https://github.com/pkubaj updated https://github.com/llvm/llvm-project/pull/212142
>From 94411f7961afa9811c0c2ab779ddda9ac5a93400 Mon Sep 17 00:00:00 2001 From: Piotr Kubaj <[email protected]> Date: Sun, 26 Jul 2026 20:13:20 +0200 Subject: [PATCH] [libunwind][PPC64] Fix _Unwind_Backtrace SIGSEGV from TOC restore at end of stack on LE On powerpc64le (ELFv2 ABI), stepWithDwarf applies a heuristic to restore the TOC pointer (r2) when the instruction at the return address is `ld r2, 24(r1)`: it reads the saved value from sp+24. This is correct during normal exception unwinding, where sp+24 lies within the caller's stack frame. During _Unwind_Backtrace the walk continues to the outermost frame, where sp+24 is outside any mapped stack region. Reading from it faults with a SIGSEGV inside _Unwind_Backtrace itself. Because _Unwind_Backtrace is a read-only stack walk that never executes landing pads, a valid r2 is not needed there. Fix: introduce a thread-local flag _unw_ppc64le_in_backtrace, set to 1 at the start of _Unwind_Backtrace and cleared at each of its exit points. On powerpc64le, stepWithDwarf skips the TOC restore heuristic while the flag is set. The flag is thread-local so a backtrace on one thread cannot suppress the heuristic for exception unwinding on another. Discovered while debugging lang/rust build failures with RUST_BACKTRACE=1 on FreeBSD/powerpc64le (IBM POWER9). With this and the companion unw_getcontext VSX corruption fix applied to FreeBSD's libunwind, the rust 1.95.0 build completes. --- libunwind/src/DwarfInstructions.hpp | 20 +++++++++++++++++++- libunwind/src/UnwindLevel1-gcc-ext.c | 22 ++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/libunwind/src/DwarfInstructions.hpp b/libunwind/src/DwarfInstructions.hpp index 8868932d6821f..fdad63408316f 100644 --- a/libunwind/src/DwarfInstructions.hpp +++ b/libunwind/src/DwarfInstructions.hpp @@ -22,6 +22,15 @@ #include "dwarf2.h" #include "libunwind_ext.h" +#if defined(_LIBUNWIND_TARGET_PPC64) && defined(__BYTE_ORDER__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +// Thread-local flag set by _Unwind_Backtrace to suppress the PPC64 ELFv2 TOC +// restoration heuristic during read-only stack walks (see +// UnwindLevel1-gcc-ext.c). Declared extern "C" so the C definition in +// UnwindLevel1-gcc-ext.c is visible here. +extern "C" _Thread_local int _unw_ppc64le_in_backtrace; +#endif + namespace libunwind { @@ -405,7 +414,16 @@ int DwarfInstructions<A, R>::stepWithDwarf( // then r2 was saved and needs to be restored. // ELFv2 ABI specifies that the TOC Pointer must be saved at SP + 24, // while in ELFv1 ABI it is saved at SP + 40. - if (R::getArch() == REGISTERS_PPC64 && returnAddress != 0) { + // + // On powerpc64le (ELFv2), skip this heuristic during _Unwind_Backtrace. + // A read-only stack walk does not execute landing pads and does not need + // a correct r2; suppressing the heuristic avoids reading from invalid + // stack slots at the bottom of the call stack. + if (R::getArch() == REGISTERS_PPC64 && returnAddress != 0 +#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + && !_unw_ppc64le_in_backtrace +#endif + ) { pint_t sp = newRegisters.getRegister(UNW_REG_SP); pint_t r2 = 0; switch (addressSpace.get32(returnAddress)) { diff --git a/libunwind/src/UnwindLevel1-gcc-ext.c b/libunwind/src/UnwindLevel1-gcc-ext.c index 1764499b304f1..e40e259b053f9 100644 --- a/libunwind/src/UnwindLevel1-gcc-ext.c +++ b/libunwind/src/UnwindLevel1-gcc-ext.c @@ -28,6 +28,15 @@ #if defined(_LIBUNWIND_BUILD_ZERO_COST_APIS) +#if defined(_LIBUNWIND_TARGET_PPC64) && defined(__BYTE_ORDER__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +// Set to 1 during _Unwind_Backtrace to suppress the PPC64 ELFv2 TOC +// restoration heuristic (see DwarfInstructions.hpp). A read-only stack walk +// does not need correct r2, and the heuristic can fault on invalid stack +// data at the bottom of the call stack. +_Thread_local int _unw_ppc64le_in_backtrace = 0; +#endif + #if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) #define PRIVATE_1 private_[0] #elif defined(_LIBUNWIND_ARM_EHABI) @@ -139,6 +148,11 @@ _Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) { _LIBUNWIND_TRACE_API("_Unwind_Backtrace(callback=%p)", (void *)(uintptr_t)callback); +#if defined(_LIBUNWIND_TARGET_PPC64) && defined(__BYTE_ORDER__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + _unw_ppc64le_in_backtrace = 1; +#endif + #if defined(_LIBUNWIND_ARM_EHABI) // Create a mock exception object for force unwinding. _Unwind_Exception ex; @@ -157,6 +171,10 @@ _Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) { _LIBUNWIND_TRACE_UNWINDING(" _backtrace: ended because cursor reached " "bottom of stack, returning %d", _URC_END_OF_STACK); +#if defined(_LIBUNWIND_TARGET_PPC64) && defined(__BYTE_ORDER__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + _unw_ppc64le_in_backtrace = 0; +#endif return _URC_END_OF_STACK; } #else @@ -202,6 +220,10 @@ _Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) { if (result != _URC_NO_REASON) { _LIBUNWIND_TRACE_UNWINDING( " _backtrace: ended because callback returned %d", result); +#if defined(_LIBUNWIND_TARGET_PPC64) && defined(__BYTE_ORDER__) && \ + __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + _unw_ppc64le_in_backtrace = 0; +#endif return result; } } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
