[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