This looks good to me, I cannot approve it though.

Adding the port maintainer to the list!

Thanks,

Manjunath S Matti.


On 16/06/26 11:15 am, 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))
+             && 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)
            {
              /* 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));
            }
        }
      }

Reply via email to