diff mbox

[8/9,AArch64,libgcc] Runtime support for AArch64 DWARF operations

Message ID 1a156de9-b632-92a9-0e6a-24b67e915f81@foss.arm.com
State New
Headers show

Commit Message

Jiong Wang Nov. 11, 2016, 6:36 p.m. UTC
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.wang@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.
diff mbox

Patch

diff --git a/libgcc/config/aarch64/unwind-aarch64.c b/libgcc/config/aarch64/unwind-aarch64.c
index 1fb6026d123f8e7fc676f5e95e8e66caccf3d6ff..f6441a56960dbd4b754f8fc17d581402389a4812 100644
--- a/libgcc/config/aarch64/unwind-aarch64.c
+++ b/libgcc/config/aarch64/unwind-aarch64.c
@@ -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;