diff mbox

[5/9,AArch64] Generate dwarf information for -msign-return-address

Message ID 304d5b3e-59a3-5a31-653a-6f8f99896264@foss.arm.com
State New
Headers show

Commit Message

Jiong Wang Nov. 11, 2016, 6:35 p.m. UTC
This patch generate DWARF description for pointer authentication.  DWARF value
expression is used to describe the authentication action.

Please see the cover letter and AArch64 DWARF specification for the semantics
of AArch64 DWARF operations.

When authentication key index is A key, we use compact DWARF description which
can largely save DWARF frame size, otherwise we fallback to general operator.



Example
===

     int
     cal (int a, int b, int c)
     {
       return a + dec (b) + c;
     }

Compact DWARF description
(-march=armv8.3-a -msign-return-address)
===

   DW_CFA_advance_loc: 4 to 0000000000000004
   DW_CFA_val_expression: r30 (x30) (DW_OP_AARCH64_paciasp)
   DW_CFA_advance_loc: 4 to 0000000000000008
   DW_CFA_val_expression: r30 (x30) (DW_OP_AARCH64_paciasp_deref: -24)

General DWARF description
===
(-march=armv8.3-a -msign-return-address -mpauth-key=b_key)

   DW_CFA_advance_loc: 4 to 0000000000000004
   DW_CFA_val_expression: r30 (x30) (DW_OP_breg30 (x30): 0; DW_OP_AARCH64_pauth: 18)
   DW_CFA_advance_loc: 4 to 0000000000000008
   DW_CFA_val_expression: r30 (x30) (DW_OP_dup; DW_OP_const1s: -24; DW_OP_plus; DW_OP_deref; DW_OP_AARCH64_pauth: 18)

 From Linux kernel testing, -msign-return-address will introduce +24%
.debug_frame size increase when signing all functions and using compact
description, and about +45% .debug_frame size increase if using general
description.


gcc/
2016-11-11  Jiong Wang<jiong.wang@arm.com>

         * config/aarch64/aarch64.h (aarch64_pauth_action_type): New enum.
         * config/aarch64/aarch64.c (aarch64_attach_ra_auth_dwarf_note): New function.
         (aarch64_attach_ra_auth_dwarf_general): New function.
         (aarch64_attach_ra_auth_dwarf_shortcut): New function.
         (aarch64_save_callee_saves): Generate dwarf information if LR is signed.
         (aarch64_expand_prologue): Likewise.
         (aarch64_expand_epilogue): Likewise.
diff mbox

Patch

diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h
index 4bfadb512915d5dc606f7fc06f027868d6be7613..907e8bdf5b4961b3107dcd5a481de28335e4be89 100644
--- a/gcc/config/aarch64/aarch64.h
+++ b/gcc/config/aarch64/aarch64.h
@@ -970,4 +970,16 @@  extern tree aarch64_fp16_ptr_type_node;
 	     || (aarch64_ra_sign_scope == AARCH64_FUNCTION_NON_LEAF \
 		 && cfun->machine->frame.reg_offset[LR_REGNUM] >= 0))
 
+/* AArch64 pointer authentication action types.  See AArch64 DWARF ABI for
+   details.  */
+enum aarch64_pauth_action_type
+{
+  /* Drop the authentication signature for instruction pointer.  */
+  AARCH64_PAUTH_DROP_I,
+  /* Likewise for data pointer.  */
+  AARCH64_PAUTH_DROP_D,
+  /* Do authentication.  */
+  AARCH64_PAUTH_AUTH
+};
+
 #endif /* GCC_AARCH64_H */
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index b3d9a2a3f51ee240d00beb4cc65f99b089a3215e..cae177dca511fdb909ef82c972d3bbdebab215e2 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -2717,6 +2717,104 @@  aarch64_output_probe_stack_range (rtx reg1, rtx reg2)
   return "";
 }
 
+/* Generate return address signing DWARF annotation using general DWARF
+   operator.  DWARF frame size will be bigger than using shortcut DWARF
+   operator.  See aarch64_attach_ra_auth_dwarf for parameter meanings.  */
+
+static rtx
+aarch64_attach_ra_auth_dwarf_general (rtx notes, HOST_WIDE_INT offset)
+{
+  /* The authentication descriptor.  */
+  HOST_WIDE_INT desc_const = (AARCH64_PAUTH_AUTH | (aarch64_pauth_key << 4));
+
+  /* DW_OP_AARCH64_pauth takes one uleb128 operand which is the authentication
+     descriptor.  The low 4 bits of the descriptor is the authentication action
+     code, all other bits are reserved and initialized into zero except when the
+     action code is AARCH64_PAUTH_AUTH then bits[7:4] is the authentication key
+     index.  */
+  rtx auth_op
+    = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, GEN_INT (desc_const), const0_rtx),
+		      DW_OP_AARCH64_pauth);
+
+  rtx par;
+  if (offset == 0)
+    {
+      /* Step 1: Push LR onto stack.
+		 NOTE: the bottom of DWARF expression stack is always CFA.
+	 Step 2: Issue AArch64 authentication operation.  */
+    par = gen_rtx_PARALLEL (DImode,
+			    gen_rtvec (2, gen_rtx_REG (Pmode, LR_REGNUM),
+				       auth_op));
+    }
+  else
+    {
+      rtx dup_cfa
+	= gen_rtx_UNSPEC (Pmode, gen_rtvec (2, const0_rtx, const0_rtx),
+			  DW_OP_dup);
+
+      rtx deref_op
+	= gen_rtx_UNSPEC (Pmode, gen_rtvec (2, const0_rtx, const0_rtx),
+			  DW_OP_deref);
+
+      rtx raw_plus
+	= gen_rtx_UNSPEC (Pmode, gen_rtvec (2, const0_rtx, const0_rtx),
+			  DW_OP_plus);
+      /* Step 1: Push the authentication key on to dwarf expression stack.
+	 Step 2: Push the stack address of where return address saved, followed
+	 by a memory de-reference operation.
+	 Step 3: Push the authentication descriptor.
+	 Step 4: Issue AArch64 authentication operation.  */
+      par = gen_rtx_PARALLEL (DImode,
+			      gen_rtvec (5, dup_cfa, GEN_INT (offset), raw_plus,
+					 deref_op, auth_op));
+    }
+
+  /* Generate the final dwarf value expression.  */
+  return alloc_reg_note (REG_CFA_VAL_EXPRESSION,
+			 gen_rtx_SET (gen_rtx_REG (DImode, LR_REGNUM), par),
+			 notes);
+}
+
+/* Generate return address signing DWARF annotation using shortcut DWARF
+   operators.  See aarch64_attach_ra_auth_dwarf for parameter meanings.  */
+
+static rtx
+aarch64_attach_ra_auth_dwarf_shortcut (rtx notes, HOST_WIDE_INT offset)
+{
+  rtx auth_op, par;
+
+  /* See GCC/include/dwarf2.def for the semantics of DW_OP_AARCH64_paciasp
+     and DW_OP_AARCH64_paciasp_deref.  */
+  if (offset == 0)
+    auth_op
+      = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, const0_rtx, const0_rtx),
+			DW_OP_AARCH64_paciasp);
+  else
+    auth_op
+      = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, GEN_INT (offset), const0_rtx),
+			DW_OP_AARCH64_paciasp_deref);
+
+  par = gen_rtx_PARALLEL (DImode, gen_rtvec (1, auth_op));
+
+  /* Generate the final dwarf value expression.  */
+  return alloc_reg_note (REG_CFA_VAL_EXPRESSION,
+			 gen_rtx_SET (gen_rtx_REG (DImode, LR_REGNUM), par),
+			 notes);
+}
+
+/* Generate AArch64 specific dwarf expression annotation.  NOTES is any existed
+   annotation that we want this new one to append.  BASE is a valid base
+   register if the value should be fetch from BASE + OFFSET.  */
+
+static rtx
+aarch64_attach_ra_auth_dwarf_note (rtx notes, HOST_WIDE_INT offset)
+{
+  if (aarch64_pauth_key != AARCH64_PAUTH_IKEY_A)
+    return aarch64_attach_ra_auth_dwarf_general (notes, offset);
+  else
+    return aarch64_attach_ra_auth_dwarf_shortcut (notes, offset);
+}
+
 static bool
 aarch64_frame_pointer_required (void)
 {
@@ -3058,6 +3156,7 @@  aarch64_save_callee_saves (machine_mode mode, HOST_WIDE_INT start_offset,
     {
       rtx reg, mem;
       HOST_WIDE_INT offset;
+      unsigned lr_pair_reg = INVALID_REGNUM;
 
       if (skip_wb
 	  && (regno == cfun->machine->frame.wb_candidate1
@@ -3069,6 +3168,9 @@  aarch64_save_callee_saves (machine_mode mode, HOST_WIDE_INT start_offset,
       mem = gen_mem_ref (mode, plus_constant (Pmode, stack_pointer_rtx,
 					      offset));
 
+      if (regno == LR_REGNUM)
+	lr_pair_reg = LR_REGNUM;
+
       regno2 = aarch64_next_callee_save (regno + 1, limit);
 
       if (regno2 <= limit
@@ -3090,12 +3192,39 @@  aarch64_save_callee_saves (machine_mode mode, HOST_WIDE_INT start_offset,
 	     calculations; subsequent parts, are only
 	     frame-related if explicitly marked.  */
 	  RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, 1)) = 1;
+
+	  if (regno2 == LR_REGNUM)
+	    lr_pair_reg = regno;
+
 	  regno = regno2;
 	}
       else
 	insn = emit_move_insn (mem, reg);
 
       RTX_FRAME_RELATED_P (insn) = 1;
+
+      if (AARCH64_ENABLE_RETURN_ADDRESS_SIGN && lr_pair_reg != INVALID_REGNUM)
+	{
+	  rtx cfi_ops = NULL_RTX;
+
+	  if (lr_pair_reg != LR_REGNUM)
+	    {
+	      /* Another register is saved together with LR to make a STP.  We
+		 need to generate .cfi_offset for that register.  */
+	      cfi_ops =
+		alloc_reg_note (REG_CFA_OFFSET,
+				gen_rtx_SET (mem,
+					     gen_rtx_REG (Pmode, lr_pair_reg)),
+				NULL_RTX);
+	    }
+
+	  HOST_WIDE_INT lr_cfa_adj
+	    = (-cfun->machine->frame.hard_fp_offset
+	       + cfun->machine->frame.reg_offset[LR_REGNUM]);
+
+	  REG_NOTES (insn)
+	    = aarch64_attach_ra_auth_dwarf_note (cfi_ops, lr_cfa_adj);
+	}
     }
 }
 
@@ -3204,12 +3333,18 @@  aarch64_expand_prologue (void)
   unsigned reg2 = cfun->machine->frame.wb_candidate2;
   rtx_insn *insn;
 
+  /* Do return address signing for all functions, even those for which LR is not
+     pushed onto stack.  */
   if (AARCH64_ENABLE_RETURN_ADDRESS_SIGN)
-    emit_insn (gen_sign_reg (gen_rtx_REG (Pmode, LR_REGNUM),
-			     gen_rtx_REG (Pmode, LR_REGNUM),
-			     stack_pointer_rtx,
-			     GEN_INT (aarch64_pauth_key),
-			     const0_rtx));
+    {
+      insn = emit_insn (gen_sign_reg (gen_rtx_REG (Pmode, LR_REGNUM),
+				      gen_rtx_REG (Pmode, LR_REGNUM),
+				      stack_pointer_rtx,
+				      GEN_INT (aarch64_pauth_key),
+				      const0_rtx));
+      RTX_FRAME_RELATED_P (insn) = 1;
+      REG_NOTES (insn) = aarch64_attach_ra_auth_dwarf_note (NULL_RTX, 0);
+    }
 
   if (flag_stack_usage_info)
     current_function_static_stack_size = frame_size;
@@ -3229,7 +3364,42 @@  aarch64_expand_prologue (void)
   aarch64_add_constant (Pmode, SP_REGNUM, IP0_REGNUM, -initial_adjust, true);
 
   if (callee_adjust != 0)
-    aarch64_push_regs (reg1, reg2, callee_adjust);
+    {
+      aarch64_push_regs (reg1, reg2, callee_adjust);
+      /* Generate return address signing dwarf annotation when
+	 omit-frame-pointer.  */
+      if (AARCH64_ENABLE_RETURN_ADDRESS_SIGN
+	  && (reg1 == LR_REGNUM || reg2 == LR_REGNUM))
+	{
+	  rtx cfi_ops = NULL_RTX;
+
+	  if (reg2 == LR_REGNUM)
+	    {
+	      rtx mem_loc
+		= gen_rtx_MEM (Pmode,
+			       plus_constant (Pmode, stack_pointer_rtx, 0));
+	      cfi_ops
+		= alloc_reg_note (REG_CFA_OFFSET,
+				  gen_rtx_SET (mem_loc,
+					       gen_rtx_REG (Pmode, reg1)),
+				  cfi_ops);
+	    }
+
+	  rtx sp_adj
+	    = plus_constant (Pmode, stack_pointer_rtx, -callee_adjust);
+	  cfi_ops
+	    = alloc_reg_note (REG_CFA_ADJUST_CFA,
+			      gen_rtx_SET (stack_pointer_rtx, sp_adj),
+			      cfi_ops);
+
+	  HOST_WIDE_INT lr_cfa_adj
+	    = (-cfun->machine->frame.hard_fp_offset
+	       + cfun->machine->frame.reg_offset[LR_REGNUM]);
+	  rtx insn = get_last_insn ();
+	  REG_NOTES (insn) = aarch64_attach_ra_auth_dwarf_note (cfi_ops,
+								lr_cfa_adj);
+	}
+    }
 
   if (frame_pointer_needed)
     {
@@ -3331,7 +3501,12 @@  aarch64_expand_epilogue (bool for_sibcall)
       /* Emit delayed restores and set the CFA to be SP + initial_adjust.  */
       insn = get_last_insn ();
       rtx new_cfa = plus_constant (Pmode, stack_pointer_rtx, initial_adjust);
-      REG_NOTES (insn) = alloc_reg_note (REG_CFA_DEF_CFA, new_cfa, cfi_ops);
+      cfi_ops = alloc_reg_note (REG_CFA_DEF_CFA, new_cfa, cfi_ops);
+
+      if (AARCH64_ENABLE_RETURN_ADDRESS_SIGN)
+	REG_NOTES (insn) = aarch64_attach_ra_auth_dwarf_note (cfi_ops, 0);
+      else
+	REG_NOTES (insn) = cfi_ops;
       RTX_FRAME_RELATED_P (insn) = 1;
       cfi_ops = NULL;
     }
@@ -3355,11 +3530,14 @@  aarch64_expand_epilogue (bool for_sibcall)
      __builtin_eh_return.  */
   if (AARCH64_ENABLE_RETURN_ADDRESS_SIGN
       && (for_sibcall || !TARGET_PAUTH || crtl->calls_eh_return))
-    emit_insn (gen_auth_reg (gen_rtx_REG (Pmode, LR_REGNUM),
-			     gen_rtx_REG (Pmode, LR_REGNUM),
-			     stack_pointer_rtx,
-			     GEN_INT (aarch64_pauth_key),
-			     const0_rtx));
+    {
+      insn = emit_insn (gen_auth_reg (gen_rtx_REG (Pmode, LR_REGNUM),
+				      gen_rtx_REG (Pmode, LR_REGNUM),
+				      stack_pointer_rtx,
+				      GEN_INT (aarch64_pauth_key), const0_rtx));
+      add_reg_note (insn, REG_CFA_RESTORE, gen_rtx_REG (Pmode, LR_REGNUM));
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
 
   /* Stack adjustment for exception handler.  */
   if (crtl->calls_eh_return)