On 11/11/16 18:22, Jiong Wang wrote:
This patch add AArch64 specific runtime EH unwinding support for
DW_OP_AARCH64_pauth, DW_OP_AARCH64_paciasp and DW_OP_AARCH64_paciasp_deref.
The semantics of them are described at the specification in patch [1/9].
The support includes:
* Parsing these DWARF operations. Perform unwinding actions according to
their semantics.
* Handling eh_return multi return paths.
Function calling __builtin_eh_return (_Unwind_RaiseException*) will have
multiple return paths. One is for normal exit, the other is for install
EH handler. If the _Unwind_RaiseException itself is return address signed,
then there will always be return address authentication before return,
however, if the return path in _Unwind_RaiseException if from installing EH
handler the address of which has already been authenticated during
unwinding, then we need to re-sign that address, so when the execution
flow
continues at _Unwind_RaiseException's epilogue, the authentication still
works correctly.
OK for trunk?
libgcc/
2016-11-11 Jiong Wang<jiong.w...@arm.com>
* config/aarch64/unwind-aarch64.c (RA_SIGN_BIT): New flag to indicate
one frame is return address signed.
(execute_stack_op): Handle DW_OP_AARCH64_pauth, DW_OP_AARCH64_paciasp,
DW_OP_AARCH64_paciasp_deref.
(uw_init_context): Call aarch64_uw_init_context_1.
(uw_init_context_1): Rename to aarch64_uw_init_context_1. Strip
signature for seed address.
(uw_install_context): Re-sign handler's address so it works correctly
with caller's context.
(uw_install_context_1): by_value[LR] can be true, after return address
signing LR will come from DWARF value expression rule which is a
by_value true rule.
This is the update on libgcc unwinder support according to new DWARF proposal.
As Joseph commented, duplication of unwind-dw2.c is not encouraged in libgcc,
But from this patch, you can see there are a few places we need to modify for
AArch64 in unwind-aarch64.c, so the file duplication approach is acceptable?
libgcc/
2017-01-06 Jiong Wang <jiong.w...@arm.com>
* config/aarch64/unwind-aarch64.c (DWARF_REGNUM_AARCH64_RA_STATE,
RA_A_SIGNED_BIT): New macros.
(execute_cfa_program): Multiplex DW_CFA_GNU_window_save on AArch64.
(uw_frame_state_for): Clear bit[0] of DWARF_REGNUM_AARCH64_RA_STATE.
(uw_update_context): Authenticate return address according to
DWARF_REGNUM_AARCH64_RA_STATE.
(uw_init_context_1): Strip signature of seed address.
(uw_install_context): Re-authenticate EH handler's address.
diff --git a/libgcc/config/aarch64/unwind-aarch64.c b/libgcc/config/aarch64/unwind-aarch64.c
index 1fb6026d123f8e7fc676f5e95e8e66caccf3d6ff..11e3c9f724c9bc5796103a0d973bfe769d23b6e7 100644
--- a/libgcc/config/aarch64/unwind-aarch64.c
+++ b/libgcc/config/aarch64/unwind-aarch64.c
@@ -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); \
} \