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));
>       }
>   }
>      }

Reply via email to