From d8083e75d12d9d8664709bb661d4c8c65f41d3d3 Mon Sep 17 00:00:00 2001 From: Stian Halseth Date: Thu, 22 Oct 2026 16:39:03 +0200 Subject: [PATCH] gh-160253: Fix test_c_stack_unwind manual unwinding on sparc64 SPARC V9 has no frame record. The caller's %fp (biased by 2047) and return address are in slots 14 and 15 of the register window save area at fp, and are only there after the windows have been flushed to the stack. --- Modules/_testinternalcapi.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index 479297a996c2502..49053e340f7d125 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -110,10 +110,20 @@ static const uintptr_t min_frame_pointer_addr = 0x1000; // address is stored at fp[-1]. # define FRAME_POINTER_NEXT_OFFSET -2 # define FRAME_POINTER_RETURN_OFFSET -1 +#elif defined(__sparc__) && defined(__arch64__) +// SPARC V9 has no frame record: fp points to the register window save area, +// where the caller's %fp is saved at fp[14] and its return address (%i7) at +// fp[15]. The saved %fp is biased by 2047. +# define FRAME_POINTER_NEXT_OFFSET 14 +# define FRAME_POINTER_RETURN_OFFSET 15 +# define FRAME_POINTER_BIAS 2047 #else # define FRAME_POINTER_NEXT_OFFSET 0 # define FRAME_POINTER_RETURN_OFFSET 1 #endif +#ifndef FRAME_POINTER_BIAS +# define FRAME_POINTER_BIAS 0 +#endif static PyObject * @@ -527,7 +537,8 @@ manual_unwind_from_fp(uintptr_t *frame_pointer) stack_min, stack_max)) { break; } - uintptr_t *next_fp = (uintptr_t *)frame_pointer[FRAME_POINTER_NEXT_OFFSET]; + uintptr_t *next_fp = (uintptr_t *)(frame_pointer[FRAME_POINTER_NEXT_OFFSET] + + FRAME_POINTER_BIAS); uintptr_t return_addr = frame_pointer[FRAME_POINTER_RETURN_OFFSET]; PyObject *addr_obj = PyLong_FromUnsignedLongLong(return_addr); @@ -599,6 +610,10 @@ gnu_backtrace_unwind(PyObject *self, PyObject *Py_UNUSED(args)) static PyObject * manual_frame_pointer_unwind(PyObject *self, PyObject *args) { +#if defined(__sparc__) && defined(__arch64__) + // The callers' register windows are only in their save areas once spilled. + __asm__ volatile ("flushw" ::: "memory"); +#endif uintptr_t *frame_pointer = (uintptr_t *)__builtin_frame_address(0); return manual_unwind_from_fp(frame_pointer); }