Hi Piotr, Thank you for working on this. I had a just a suggestion below:
On Tue, 2026-06-16 at 07:45 +0200, Piotr Kubaj wrote: > From: Piotr Kubaj <[email protected]> > > On powerpc64, when the unwind info for a frame does not explicitly > describe how r2 (the TOC pointer) was saved -- which is the normal > case > for the linker-generated PLT call stubs -- frob_update_context > inspects > the code stream to locate the saved TOC and arranges for r2 to be > restored from it. > > The FreeBSD version of this hook hard-coded the ELFv1 TOC save slot > offset of 40 bytes, matching "std r2,40(r1)" (0xF8410028) and > "ld r2,40(r1)" (0xE8410028). FreeBSD/powerpc64 (both big-endian and > powerpc64le) uses ELFv2, where the TOC is saved at offset 24 > ("std r2,24(r1)" / "ld r2,24(r1)"). As a result the checks never > matched, r2 was left unrestored, and code reached by unwinding -- > e.g. a > C++ catch handler in a different module than libgcc_s -- ran with the > wrong TOC. Any global or PLT access from such a handler then > computed a > bogus address, typically crashing. This made C++ exceptions unusable > on > FreeBSD/powerpc64le whenever gcc's shared libgcc_s provided the > unwinder > (for instance any clang-built C++ program that pulls in libgfortran). > > Define TOC_SAVE_SLOT based on _CALL_ELF (24 for ELFv2, 40 otherwise) > and > use it throughout, mirroring linux-unwind.h. > > libgcc/ChangeLog: > > PR target/125803 > * config/rs6000/freebsd-unwind.h (TOC_SAVE_SLOT): New macro, > defined according to _CALL_ELF. > (frob_update_context): Use TOC_SAVE_SLOT instead of the hard-coded > ELFv1 offset 40 when checking for and locating the saved TOC, so > that r2 is restored correctly under ELFv2. > > Signed-off-by: Piotr Kubaj <[email protected]> > --- > Moved the macro to file scope as suggested. > > Changes since v1: > * Move the TOC_SAVE_SLOT definition out of frob_update_context to > file > scope, alongside the other #defines, as suggested by Manjunath > Matti. > > Tested on powerpc64le-unknown-freebsd15.1: a minimal throw/catch > reproducer and a real "octave-cli pkg install" both succeed where > they > previously SIGSEGV'd. freebsd-unwind.h is not exercised by the usual > bootstrap/regtest on Linux, so no on-target regtest line is offered. > Inert on ELFv1. > > libgcc/config/rs6000/freebsd-unwind.h | 20 ++++++++++++++------ > 1 file changed, 14 insertions(+), 6 deletions(-) > > diff --git a/libgcc/config/rs6000/freebsd-unwind.h > b/libgcc/config/rs6000/freebsd-unwind.h > index d97ed0c..6f9efd8 100644 > --- a/libgcc/config/rs6000/freebsd-unwind.h > +++ b/libgcc/config/rs6000/freebsd-unwind.h > @@ -24,6 +24,14 @@ > > #define R_LR 65 > > +#ifdef __powerpc64__ > +#if _CALL_ELF == 2 > +#define TOC_SAVE_SLOT 24 > +#else > +#define TOC_SAVE_SLOT 40 > +#endif > +#endif > + > #define MD_FROB_UPDATE_CONTEXT frob_update_context > > static void > @@ -40,9 +48,9 @@ frob_update_context (struct _Unwind_Context > *context, > figure out if it was saved. The big problem here is that the > code that does the save/restore is generated by the linker, so > we have no good way to determine at compile time what to do. */ > - if (pc[0] == 0xF8410028 > + if (pc[0] == 0xF8410000 + TOC_SAVE_SLOT > || ((pc[0] & 0xFFFF0000) == 0x3D820000 > - && pc[1] == 0xF8410028)) Im not sure if this condition would ever succeed in elf v2. The rhs of the or condition has been guarded linux-unwind.h stating that the sequence never gets generated in v2. > + && pc[1] == 0xF8410000 + TOC_SAVE_SLOT)) > { > /* We are in a plt call stub or r2 adjusting long branch stub, > before r2 has been saved. Keep REG_UNSAVED. */ > @@ -51,17 +59,17 @@ frob_update_context (struct _Unwind_Context > *context, > { > unsigned int *insn > = (unsigned int *) _Unwind_GetGR (context, R_LR); > - if (insn && *insn == 0xE8410028) > - _Unwind_SetGRPtr (context, 2, context->cfa + 40); > + if (insn && *insn == 0xE8410000 + TOC_SAVE_SLOT) > + _Unwind_SetGRPtr (context, 2, context->cfa + TOC_SAVE_SLOT); > else if (pc[0] == 0x4E800421 > - && pc[1] == 0xE8410028) > + && pc[1] == 0xE8410000 + TOC_SAVE_SLOT) Likewise there is a guard here as well in linux unwind. Kindly check if this is actually needed or not. As i understand, correctness wise it is ok not to guard, but it might have been guarded as an optimization since that check would always fail. Other than that it looks good. I would request Michael and Surya to please have a look at this patch, and approve if ok. Thanks, Avinash Jayakar > { > /* We are at the bctrl instruction in a call via function > pointer. gcc always emits the load of the new R2 just > before the bctrl so this is the first and only place > we need to use the stored R2. */ > _Unwind_Word sp = _Unwind_GetGR (context, 1); > - _Unwind_SetGRPtr (context, 2, (void *)(sp + 40)); > + _Unwind_SetGRPtr (context, 2, (void *)(sp + TOC_SAVE_SLOT)); > } > } > }
