Szabolcs Nagy via Gcc-patches <[email protected]> writes:
> The RA_SIGN_STATE dwarf pseudo-register is normally only set using the
> DW_CFA_AARCH64_negate_ra_state (== DW_CFA_window_save) operation which
> toggles the return address signedness state (the default state is 0).
> (It may be set by remember/restore_state CFI too, those save/restore
> the state of all registers.)
>
> However RA_SIGN_STATE can be set directly via DW_CFA_val_expression too.
> GCC does not generate such CFI but some other compilers reportedly do.
>
> Note: the toggle operation must not be mixed with other dwarf register
> rule CFI within the same CIE and FDE.
>
> In libgcc we assume REG_UNSAVED means the RA_STATE is set using toggle
> operations, otherwise we assume its value is set by other CFI.
AFAIK, this is the first time I've looked at the RA_SIGN_STATE code,
so this is probably a naive point/question, but: it seems a bit
underhand for the existing code to be using REG_UNSAVED and
loc.offset to hold the toggle state. Would it make sense to add
a new enum value for known, pre-evaluated constants? _Unwind_GetGR
would then DTRT for both cases.
That's a comment about the pre-existing code though. I agree this
patch looks like the right fix if we keep to the current approach.
Thanks,
Richard
>
> libgcc/ChangeLog:
>
> PR target/104689
> * config/aarch64/aarch64-unwind.h (aarch64_frob_update_context):
> Handle the !REG_UNSAVED case.
> * unwind-dw2.c (execute_cfa_program): Fail toggle if !REG_UNSAVED.
>
> gcc/testsuite/ChangeLog:
>
> PR target/104689
> * gcc.target/aarch64/pr104689.c: New test.
> ---
> gcc/testsuite/gcc.target/aarch64/pr104689.c | 149 ++++++++++++++++++++
> libgcc/config/aarch64/aarch64-unwind.h | 8 +-
> libgcc/unwind-dw2.c | 4 +-
> 3 files changed, 159 insertions(+), 2 deletions(-)
> create mode 100644 gcc/testsuite/gcc.target/aarch64/pr104689.c
>
> diff --git a/gcc/testsuite/gcc.target/aarch64/pr104689.c
> b/gcc/testsuite/gcc.target/aarch64/pr104689.c
> new file mode 100644
> index 00000000000..3b7adbdfe7d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/aarch64/pr104689.c
> @@ -0,0 +1,149 @@
> +/* PR target/104689. Unwind across pac-ret frames with unusual dwarf. */
> +/* { dg-do run } */
> +/* { dg-require-effective-target lp64 } */
> +/* { dg-options "-fexceptions -O2" } */
> +
> +#include <unwind.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +
> +#define die() \
> + do { \
> + printf ("%s:%d: reached unexpectedly.\n", __FILE__, __LINE__); \
> + fflush (stdout); \
> + abort (); \
> + } while (0)
> +
> +
> +/* Code to invoke unwinding with a logging callback. */
> +
> +static struct _Unwind_Exception exc;
> +
> +static _Unwind_Reason_Code
> +force_unwind_stop (int version, _Unwind_Action actions,
> + _Unwind_Exception_Class exc_class,
> + struct _Unwind_Exception *exc_obj,
> + struct _Unwind_Context *context,
> + void *stop_parameter)
> +{
> + printf ("%s: CFA: %p PC: %p actions: %d\n",
> + __func__,
> + (void *)_Unwind_GetCFA (context),
> + (void *)_Unwind_GetIP (context),
> + (int)actions);
> + if (actions & _UA_END_OF_STACK)
> + die ();
> + return _URC_NO_REASON;
> +}
> +
> +static void force_unwind (void)
> +{
> +#ifndef __USING_SJLJ_EXCEPTIONS__
> + _Unwind_ForcedUnwind (&exc, force_unwind_stop, 0);
> +#else
> + _Unwind_SjLj_ForcedUnwind (&exc, force_unwind_stop, 0);
> +#endif
> +}
> +
> +
> +/* Define functions with unusual pac-ret dwarf via top level asm. */
> +
> +#define STR(x) #x
> +#define DW_CFA_val_expression 0x16
> +#define RA_SIGN_STATE 34
> +#define DW_OP_lit0 0x30
> +#define DW_OP_lit1 0x31
> +
> +#define cfi_escape(a1, a2, a3, a4) \
> + ".cfi_escape " STR(a1) ", " STR(a2) ", " STR(a3) ", " STR(a4)
> +
> +/* Bytes: 0x16 0x22 0x01 0x30 */
> +#define SET_RA_STATE_0 \
> + cfi_escape (DW_CFA_val_expression, RA_SIGN_STATE, 1, DW_OP_lit0)
> +
> +/* Bytes: 0x16 0x22 0x01 0x31 */
> +#define SET_RA_STATE_1 \
> + cfi_escape (DW_CFA_val_expression, RA_SIGN_STATE, 1, DW_OP_lit1)
> +
> +/* These function call their argument. */
> +void unusual_pac_ret (void *);
> +void unusual_no_pac_ret (void *);
> +
> +asm(""
> +".global unusual_pac_ret\n"
> +".type unusual_pac_ret, %function\n"
> +"unusual_pac_ret:\n"
> +" .cfi_startproc\n"
> +" " SET_RA_STATE_0 "\n"
> +" hint 25 // paciasp\n"
> +" " SET_RA_STATE_1 "\n"
> +" stp x29, x30, [sp, -16]!\n"
> +" .cfi_def_cfa_offset 16\n"
> +" .cfi_offset 29, -16\n"
> +" .cfi_offset 30, -8\n"
> +" mov x29, sp\n"
> +" blr x0\n"
> +" ldp x29, x30, [sp], 16\n"
> +" .cfi_restore 30\n"
> +" .cfi_restore 29\n"
> +" .cfi_def_cfa_offset 0\n"
> +" hint 29 // autiasp\n"
> +" " SET_RA_STATE_0 "\n"
> +" ret\n"
> +" .cfi_endproc\n");
> +
> +asm(""
> +".global unusual_no_pac_ret\n"
> +".type unusual_no_pac_ret, %function\n"
> +"unusual_no_pac_ret:\n"
> +" .cfi_startproc\n"
> +" " SET_RA_STATE_0 "\n"
> +" stp x29, x30, [sp, -16]!\n"
> +" .cfi_def_cfa_offset 16\n"
> +" .cfi_offset 29, -16\n"
> +" .cfi_offset 30, -8\n"
> +" mov x29, sp\n"
> +" blr x0\n"
> +" ldp x29, x30, [sp], 16\n"
> +" .cfi_restore 30\n"
> +" .cfi_restore 29\n"
> +" .cfi_def_cfa_offset 0\n"
> +" ret\n"
> +" .cfi_endproc\n");
> +
> +
> +/* Functions to create a call chain with mixed pac-ret dwarf. */
> +
> +__attribute__((target("branch-protection=pac-ret")))
> +static void f2_pac_ret (void)
> +{
> + force_unwind ();
> + die ();
> +}
> +
> +__attribute__((target("branch-protection=none")))
> +static void f1_no_pac_ret (void)
> +{
> + unusual_pac_ret (f2_pac_ret);
> + die ();
> +}
> +
> +__attribute__((noinline, target("branch-protection=pac-ret")))
> +static void f0_pac_ret (void)
> +{
> + unusual_no_pac_ret (f1_no_pac_ret);
> + die ();
> +}
> +
> +static void cleanup_handler (void *p)
> +{
> + printf ("%s: Success.\n", __func__);
> + exit (0);
> +}
> +
> +int main ()
> +{
> + char dummy __attribute__((cleanup (cleanup_handler)));
> + f0_pac_ret ();
> + die ();
> +}
> diff --git a/libgcc/config/aarch64/aarch64-unwind.h
> b/libgcc/config/aarch64/aarch64-unwind.h
> index 40b22d3c288..e082e957821 100644
> --- a/libgcc/config/aarch64/aarch64-unwind.h
> +++ b/libgcc/config/aarch64/aarch64-unwind.h
> @@ -78,7 +78,13 @@ static inline void
> aarch64_frob_update_context (struct _Unwind_Context *context,
> _Unwind_FrameState *fs)
> {
> - if (fs->regs.reg[DWARF_REGNUM_AARCH64_RA_STATE].loc.offset & 0x1)
> + const int reg = DWARF_REGNUM_AARCH64_RA_STATE;
> + int ra_signed;
> + if (fs->regs.reg[reg].how == REG_UNSAVED)
> + ra_signed = fs->regs.reg[reg].loc.offset & 0x1;
> + else
> + ra_signed = _Unwind_GetGR (context, reg) & 0x1;
> + if (ra_signed)
> /* The flag is used for re-authenticating EH handler's address. */
> context->flags |= RA_SIGNED_BIT;
> else
> diff --git a/libgcc/unwind-dw2.c b/libgcc/unwind-dw2.c
> index 6ccd8853076..a2eb66dc0de 100644
> --- a/libgcc/unwind-dw2.c
> +++ b/libgcc/unwind-dw2.c
> @@ -1204,7 +1204,9 @@ execute_cfa_program (const unsigned char *insn_ptr,
> #if defined (__aarch64__) && !defined (__ILP32__)
> /* This CFA is multiplexed with Sparc. On AArch64 it's used to toggle
> return address signing status. */
> - fs->regs.reg[DWARF_REGNUM_AARCH64_RA_STATE].loc.offset ^= 1;
> + reg = DWARF_REGNUM_AARCH64_RA_STATE;
> + gcc_assert (fs->regs.reg[reg].how == REG_UNSAVED);
> + fs->regs.reg[reg].loc.offset ^= 1;
> #else
> /* ??? Hardcoded for SPARC register window configuration. */
> if (__LIBGCC_DWARF_FRAME_REGISTERS__ >= 32)