From 0d344b557604e966dc7f91739881f03e1f221efd Mon Sep 17 00:00:00 2001 From: Szabolcs Nagy Date: Thu, 10 Feb 2022 17:42:56 +0000 Subject: aarch64: Fix pac-ret with unusual dwarf in libgcc unwinder [PR104689] 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. 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. --- libgcc/config/aarch64/aarch64-unwind.h | 8 +++++++- libgcc/unwind-dw2.c | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'libgcc') diff --git a/libgcc/config/aarch64/aarch64-unwind.h b/libgcc/config/aarch64/aarch64-unwind.h index 40b22d3..e082e95 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 6ccd885..a2eb66d 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) -- cgit v1.1