@@ -37,6 +37,9 @@
#include "gthr.h"
#include "unwind-dw2.h"
+/* This is a copy of libgcc/unwind-dw2.c with AArch64 return address signing
+ support. */
+
#ifdef HAVE_SYS_SDT_H
#include <sys/sdt.h>
#endif
@@ -55,6 +58,8 @@
#define PRE_GCC3_DWARF_FRAME_REGISTERS __LIBGCC_DWARF_FRAME_REGISTERS__
#endif
+#define DWARF_REGNUM_AARCH64_RA_STATE 32
+
/* ??? For the public function interfaces, we tend to gcc_assert that the
column numbers are in range. For the dwarf2 unwind info this does happen,
although so far in a case that doesn't actually matter.
@@ -136,6 +141,8 @@ struct _Unwind_Context
#define SIGNAL_FRAME_BIT ((~(_Unwind_Word) 0 >> 1) + 1)
/* Context which has version/args_size/by_value fields. */
#define EXTENDED_CONTEXT_BIT ((~(_Unwind_Word) 0 >> 2) + 1)
+ /* Return address has been signed with A key. */
+#define RA_A_SIGNED_BIT ((~(_Unwind_Word) 0 >> 3) + 1)
_Unwind_Word flags;
/* 0 for now, can be increased when further fields are added to
struct _Unwind_Context. */
@@ -1185,13 +1192,9 @@ execute_cfa_program (const unsigned char *insn_ptr,
break;
case DW_CFA_GNU_window_save:
- /* ??? Hardcoded for SPARC register window configuration. */
- if (__LIBGCC_DWARF_FRAME_REGISTERS__ >= 32)
- for (reg = 16; reg < 32; ++reg)
- {
- fs->regs.reg[reg].how = REG_SAVED_OFFSET;
- fs->regs.reg[reg].loc.offset = (reg - 16) * sizeof (void *);
- }
+ /* 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;
break;
case DW_CFA_GNU_args_size:
@@ -1263,6 +1266,8 @@ uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs)
/* First decode all the insns in the CIE. */
end = (const unsigned char *) next_fde ((const struct dwarf_fde *) cie);
execute_cfa_program (insn, end, context, fs);
+ /* Clear bit 0 of RA_STATE pseudo register. */
+ fs->regs.reg[DWARF_REGNUM_AARCH64_RA_STATE].loc.offset &= ~0x1;
/* Locate augmentation for the fde. */
aug = (const unsigned char *) fde + sizeof (*fde);
@@ -1513,10 +1518,19 @@ uw_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs)
stack frame. */
context->ra = 0;
else
- /* Compute the return address now, since the return address column
- can change from frame to frame. */
- context->ra = __builtin_extract_return_addr
- (_Unwind_GetPtr (context, fs->retaddr_column));
+ {
+ /* Compute the return address now, since the return address column
+ can change from frame to frame. */
+ context->ra
+ = __builtin_extract_return_addr (_Unwind_GetPtr (context,
+ fs->retaddr_column));
+ if (fs->regs.reg[DWARF_REGNUM_AARCH64_RA_STATE].loc.offset & 0x1)
+ {
+ _Unwind_Word salt = (_Unwind_Word) context->cfa;
+ context->ra
+ = (void *) __builtin_aarch64_autia1716 (context->ra, salt);
+ }
+ }
}
static void
@@ -1550,6 +1564,11 @@ uw_init_context_1 (struct _Unwind_Context *context,
void *outer_cfa, void *outer_ra)
{
void *ra = __builtin_extract_return_addr (__builtin_return_address (0));
+ /* Drop authentication signature for inner RA. It's anyway will be
+ authenticated later before return if return addresss signing is enabled for
+ libgcc. Here it's served as the seed address which will be used for table
+ searching, we need the original address. */
+ ra = __builtin_aarch64_xpaclri (ra);
_Unwind_FrameState fs;
_Unwind_SpTmp sp_slot;
_Unwind_Reason_Code code;
@@ -1586,6 +1605,10 @@ uw_init_context_1 (struct _Unwind_Context *context,
initialization context, then we can't see it in the given
call frame data. So have the initialization context tell us. */
context->ra = __builtin_extract_return_addr (outer_ra);
+ context->ra = __builtin_aarch64_xpaclri (context->ra);
+ if (fs.regs.reg[DWARF_REGNUM_AARCH64_RA_STATE].loc.offset & 0x1)
+ /* The flag is used for re-authenticating EH handler's address. */
+ context->flags |= RA_A_SIGNED_BIT;
}
static void _Unwind_DebugHook (void *, void *)
@@ -1610,13 +1633,22 @@ _Unwind_DebugHook (void *cfa __attribute__ ((__unused__)),
/* Install TARGET into CURRENT so that we can return to it. This is a
macro because __builtin_eh_return must be invoked in the context of
- our caller. */
+ our caller.
+
+ For AArch64 pointer authentication, as target EH handler's address is
+ already authenticated, we need to sign it again with the original SP
+ of CURRENT. */
#define uw_install_context(CURRENT, TARGET) \
do \
{ \
long offset = uw_install_context_1 ((CURRENT), (TARGET)); \
void *handler = __builtin_frob_return_addr ((TARGET)->ra); \
+ if ((CURRENT)->flags & RA_A_SIGNED_BIT) \
+ handler \
+ = __builtin_aarch64_pacia1716 (handler, \
+ (_Unwind_Word) \
+ (CURRENT)->cfa); \
_Unwind_DebugHook ((TARGET)->cfa, handler); \
__builtin_eh_return (offset, handler); \
} \