@@ -37,6 +37,10 @@
#include "gthr.h"
#include "unwind-dw2.h"
+/* This AArch64 implementation is exactly the same as libgcc/unwind-dw2.c,
+ except we have a customized uw_init_context_1 to handle pointer
+ authentication. */
+
#ifdef HAVE_SYS_SDT_H
#include <sys/sdt.h>
#endif
@@ -67,7 +71,7 @@
waste. However, some runtime libraries supplied with ICC do contain such
an unorthodox transition, as well as the unwind info to match. This loss
of register restoration doesn't matter in practice, because the exception
- is caught in the native unix abi, where all of the xmm registers are
+ is caught in the native unix abi, where all of the xmm registers are
call clobbered.
Ideally, we'd record some bit to notice when we're failing to restore some
@@ -136,6 +140,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. */
+#define RA_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. */
@@ -908,6 +914,89 @@ execute_stack_op (const unsigned char *op_ptr, const unsigned char *op_end,
case DW_OP_nop:
goto no_push;
+ case DW_OP_AARCH64_paciasp:
+ {
+ _Unwind_Word lr_value = _Unwind_GetGR (context, LR_REGNUM);
+ /* Note: initial is guaranteed to be CFA by DWARF specification. */
+ result
+ = (_Unwind_Word) __builtin_aarch64_autia1716 ((void *) lr_value,
+ initial);
+ context->flags |= RA_SIGNED_BIT;
+ break;
+ }
+
+ case DW_OP_AARCH64_paciasp_deref:
+ {
+ _sleb128_t offset;
+ op_ptr = read_sleb128 (op_ptr, &offset);
+ result = (_Unwind_Word) read_pointer ((void *) initial + offset);
+ result
+ = (_Unwind_Word) __builtin_aarch64_autia1716 ((void *) result,
+ initial);
+ context->flags |= RA_SIGNED_BIT;
+ break;
+ }
+
+ case DW_OP_AARCH64_pauth:
+ {
+ _uleb128_t auth_descriptor;
+ op_ptr = read_uleb128 (op_ptr, &auth_descriptor);
+ enum aarch64_pauth_action_type action_code =
+ (enum aarch64_pauth_action_type) (auth_descriptor & 0xf);
+ context->flags |= RA_SIGNED_BIT;
+
+ /* Different action may take different number of operands.
+ AARCH64_PAUTH_DROP* takes one operand while AARCH64_PAUTH_AUTH
+ takes two and both of them produce one result. */
+ switch (action_code)
+ {
+ case AARCH64_PAUTH_DROP_I:
+ {
+ /* Fetch the value to drop signature. */
+ stack_elt -= 1;
+ result = stack[stack_elt];
+ result
+ = (_Unwind_Word)
+ __builtin_aarch64_xpaclri ((void *) result);
+ break;
+ }
+ case AARCH64_PAUTH_AUTH:
+ {
+ enum aarch64_pauth_key_index key_index =
+ (enum aarch64_pauth_key_index)
+ (auth_descriptor >> 4) & 0xf;
+
+ /* Fetch the value to be authenticated and the key. */
+ stack_elt -= 2;
+ _Unwind_Word key = stack[stack_elt];
+ result = stack[stack_elt + 1];
+
+ switch (key_index)
+ {
+ case AARCH64_PAUTH_IKEY_A:
+ result
+ = (_Unwind_Word)
+ __builtin_aarch64_autia1716 ((void *) result, key);
+ break;
+ case AARCH64_PAUTH_IKEY_B:
+ result
+ = (_Unwind_Word)
+ __builtin_aarch64_autib1716 ((void *) result, key);
+ break;
+ default:
+ /* For C++ exception unwinding, only instruction
+ pointers are expected. */
+ gcc_unreachable ();
+ }
+ }
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ break;
+ }
+
default:
gcc_unreachable ();
}
@@ -1534,8 +1623,8 @@ uw_advance_context (struct _Unwind_Context *context, _Unwind_FrameState *fs)
/* Do any necessary initialization to access arbitrary stack frames. \
On the SPARC, this means flushing the register windows. */ \
__builtin_unwind_init (); \
- uw_init_context_1 (CONTEXT, __builtin_dwarf_cfa (), \
- __builtin_return_address (0)); \
+ aarch64_uw_init_context_1 (CONTEXT, __builtin_dwarf_cfa (), \
+ __builtin_return_address (0)); \
} \
while (0)
@@ -1546,10 +1635,15 @@ init_dwarf_reg_size_table (void)
}
static void __attribute__((noinline))
-uw_init_context_1 (struct _Unwind_Context *context,
- void *outer_cfa, void *outer_ra)
+aarch64_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 +1680,11 @@ 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);
+ void *orig_ra = __builtin_aarch64_xpaclri (context->ra);
+ if (context->ra != orig_ra)
+ context->flags |= RA_SIGNED_BIT;
+ /* Same reason as described at this function start. */
+ context->ra = orig_ra;
}
static void _Unwind_DebugHook (void *, void *)
@@ -1610,13 +1709,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_SIGNED_BIT) \
+ handler \
+ = __builtin_aarch64_paci1716 (handler, \
+ (_Unwind_Word) \
+ (CURRENT)->cfa); \
_Unwind_DebugHook ((TARGET)->cfa, handler); \
__builtin_eh_return (offset, handler); \
} \
@@ -1639,7 +1747,8 @@ uw_install_context_1 (struct _Unwind_Context *current,
void *c = (void *) (_Unwind_Internal_Ptr) current->reg[i];
void *t = (void *) (_Unwind_Internal_Ptr)target->reg[i];
- gcc_assert (current->by_value[i] == 0);
+ gcc_assert (current->by_value[i] == 0
+ || i == (AARCH64_DWARF_R0 + LR_REGNUM));
if (target->by_value[i] && c)
{
_Unwind_Word w;