[gcc r17-2785] libgcc: rs6000: fix TOC restore when unwinding on FreeBSD powerpc64 ELFv2 [PR target/125803]
Gerald Pfeifer via Gcc-cvs <[email protected]> Wed, 29 Jul 2026 10:49:14 +0000 (GMT)
| Newsgroups | gmane.comp.gcc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://gcc.gnu.org/g:e8385ea3b57862ce6c6ace5e6973b96cf238f4b6 commit r17-2785-ge8385ea3b57862ce6c6ace5e6973b96cf238f4b6 Author: Piotr Kubaj <[email protected]> Date: Wed Jul 22 15:45:04 2026 +0200 libgcc: rs6000: fix TOC restore when unwinding on FreeBSD powerpc64 ELFv2 [PR target/125803] 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, and guard the ELFv1-only code-reading cases (the old PLT stub form and the function pointer call sequence) with _CALL_ELF != 2, 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. Guard the ELFv1-only PLT stub and function pointer call sequences with _CALL_ELF != 2, mirroring linux-unwind.h. Signed-off-by: Piotr Kubaj <[email protected]> Diff: --- libgcc/config/rs6000/freebsd-unwind.h | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/libgcc/config/rs6000/freebsd-unwind.h b/libgcc/config/rs6000/freebsd-unwind.h index d97ed0cd9188..f5e7f3f42b3d 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,13 @@ 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 +#if _CALL_ELF != 2 + /* The ELFv2 linker never generates the old PLT stub form. */ || ((pc[0] & 0xFFFF0000) == 0x3D820000 - && pc[1] == 0xF8410028)) + && pc[1] == 0xF8410000 + TOC_SAVE_SLOT) +#endif + ) { /* We are in a plt call stub or r2 adjusting long branch stub, before r2 has been saved. Keep REG_UNSAVED. */ @@ -51,18 +63,21 @@ 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); +#if _CALL_ELF != 2 + /* ELFv2 does not use this function pointer call sequence. */ else if (pc[0] == 0x4E800421 - && pc[1] == 0xE8410028) + && pc[1] == 0xE8410000 + TOC_SAVE_SLOT) { /* 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)); } +#endif } } #endif