[v0,13/13] aarch64: add DWARF CFI directive for PAuth_LR

Message ID 20240708123452.1883314-14-matthieu.longo@arm.com
State New
Headers
Series aarch64: add instructions for Armv9.5-A PAC enhancement |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 fail Build failed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Matthieu Longo July 8, 2024, 12:34 p.m. UTC
  This patch adds a new CFI directive (cfi_negate_ra_state_with_pc) which
set an additional bit in the RA state to inform that RA was signed with
SP but also PC as an additional diversifier.

RA state | Description
0b00     | Return address not signed (default if no cfi_negate_ra_state*)
0b01     | Return address signed with SP (cfi_negate_ra_state)
0b10     | Invalid state
0b11     | Return address signed with SP+PC (cfi_negate_ra_state_with_pc)

It also adds the relevant tests for the new cfi directive.
---
 bfd/elf-eh-frame.c                            |  1 +
 binutils/dwarf.c                              |  5 ++
 gas/dw2gencfi.c                               | 10 +++
 gas/gen-sframe.c                              | 25 +++++++-
 gas/gen-sframe.h                              |  5 +-
 gas/scfidw2gen.c                              |  1 +
 .../gas/aarch64/pac_negate_ra_state_with_pc.d | 64 +++++++++++++++++++
 .../gas/aarch64/pac_negate_ra_state_with_pc.s | 53 +++++++++++++++
 include/dwarf2.def                            |  4 +-
 include/sframe.h                              |  2 +-
 10 files changed, 165 insertions(+), 5 deletions(-)
 create mode 100644 gas/testsuite/gas/aarch64/pac_negate_ra_state_with_pc.d
 create mode 100644 gas/testsuite/gas/aarch64/pac_negate_ra_state_with_pc.s
  

Patch

diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c
index 902d7c16334..acee87f2a5b 100644
--- a/bfd/elf-eh-frame.c
+++ b/bfd/elf-eh-frame.c
@@ -359,6 +359,7 @@  skip_cfa_op (bfd_byte **iter, bfd_byte *end, unsigned int encoded_ptr_width)
     case DW_CFA_remember_state:
     case DW_CFA_restore_state:
     case DW_CFA_GNU_window_save:
+    case DW_CFA_AARCH64_negate_ra_state_with_pc:
       /* No arguments.  */
       return true;
 
diff --git a/binutils/dwarf.c b/binutils/dwarf.c
index 972bb920161..7e4a4427095 100644
--- a/binutils/dwarf.c
+++ b/binutils/dwarf.c
@@ -10358,6 +10358,11 @@  display_debug_frames (struct dwarf_section *section,
 	      fc->pc_begin += ofs;
 	      break;
 
+	    case DW_CFA_AARCH64_negate_ra_state_with_pc:
+	      if (! do_debug_frames_interp)
+		printf ("  DW_CFA_AARCH64_negate_ra_state_with_pc\n");
+	      break;
+
 	    case DW_CFA_GNU_window_save:
 	      if (! do_debug_frames_interp)
 		printf ("  %s\n", DW_CFA_GNU_window_save_name[is_aarch64]);
diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c
index db0261dd187..936cf7eb4b0 100644
--- a/gas/dw2gencfi.c
+++ b/gas/dw2gencfi.c
@@ -718,6 +718,7 @@  const pseudo_typeS cfi_pseudo_table[] =
     { "cfi_restore_state", dot_cfi, DW_CFA_restore_state },
     { "cfi_window_save", dot_cfi, DW_CFA_GNU_window_save },
     { "cfi_negate_ra_state", dot_cfi, DW_CFA_AARCH64_negate_ra_state },
+    { "cfi_negate_ra_state_with_pc", dot_cfi, DW_CFA_AARCH64_negate_ra_state_with_pc },
     { "cfi_escape", dot_cfi_escape, 0 },
     { "cfi_signal_frame", dot_cfi, CFI_signal_frame },
     { "cfi_personality", dot_cfi_personality, 0 },
@@ -918,6 +919,10 @@  dot_cfi (int arg)
       cfi_add_CFA_insn (DW_CFA_GNU_window_save);
       break;
 
+    case DW_CFA_AARCH64_negate_ra_state_with_pc:
+      cfi_add_CFA_insn (DW_CFA_AARCH64_negate_ra_state_with_pc);
+      break;
+
     case CFI_signal_frame:
       frchain_now->frch_cfi_data->cur_fde_data->signal_frame = 1;
       break;
@@ -1758,6 +1763,10 @@  output_cfi_insn (struct cfi_insn_data *insn)
       out_one (DW_CFA_GNU_window_save);
       break;
 
+    case DW_CFA_AARCH64_negate_ra_state_with_pc:
+      out_one (DW_CFA_AARCH64_negate_ra_state_with_pc);
+      break;
+
     case CFI_escape:
       {
 	struct cfi_escape_data *e;
@@ -2216,6 +2225,7 @@  cfi_change_reg_numbers (struct cfi_insn_data *insn, segT ccseg)
 	case DW_CFA_remember_state:
 	case DW_CFA_restore_state:
 	case DW_CFA_GNU_window_save:
+	case DW_CFA_AARCH64_negate_ra_state_with_pc:
 	case CFI_escape:
 	case CFI_label:
 	  break;
diff --git a/gas/gen-sframe.c b/gas/gen-sframe.c
index 247b65a37d0..77870dde166 100644
--- a/gas/gen-sframe.c
+++ b/gas/gen-sframe.c
@@ -928,7 +928,7 @@  sframe_row_entry_initialize (struct sframe_row_entry *cur_fre,
   cur_fre->ra_loc = prev_fre->ra_loc;
   cur_fre->ra_offset = prev_fre->ra_offset;
   /* Treat RA mangling as a sticky bit.  It retains its value until another
-     .cfi_negate_ra_state is seen.  */
+     .cfi_negate_ra_state or .cfi_negate_ra_state_with_pc is seen.  */
   cur_fre->ra_signing_info = prev_fre->ra_signing_info;
 }
 
@@ -1306,6 +1306,26 @@  sframe_xlate_do_gnu_window_save (struct sframe_xlate_ctx *xlate_ctx,
   return SFRAME_XLATE_ERR_NOTREPRESENTED;  /* Not represented.  */
 }
 
+/* Translate DW_CFA_AARCH64_negate_ra_state_with_pc into SFrame context.
+   Return SFRAME_XLATE_OK if success.  */
+
+static int
+sframe_xlate_do_aarch64_negate_ra_state_with_pc (struct sframe_xlate_ctx *xlate_ctx,
+						 struct cfi_insn_data *cfi_insn ATTRIBUTE_UNUSED)
+{
+  struct sframe_row_entry *cur_fre = xlate_ctx->cur_fre;
+
+  gas_assert (cur_fre);
+  /* Toggle the mangled RA status bit.  */
+  cur_fre->ra_signing_info
+    = ((cur_fre->ra_signing_info == RA_no_signing)
+       ? RA_signing_SP_PC
+       : RA_no_signing);
+  cur_fre->merge_candidate = false;
+
+  return SFRAME_XLATE_OK;
+}
+
 /* Returns the DWARF call frame instruction name or fake CFI name for the
    specified CFI opcode, or NULL if the value is not recognized.  */
 
@@ -1396,6 +1416,9 @@  sframe_do_cfi_insn (struct sframe_xlate_ctx *xlate_ctx,
     case DW_CFA_GNU_window_save:
       err = sframe_xlate_do_gnu_window_save (xlate_ctx, cfi_insn);
       break;
+    case DW_CFA_AARCH64_negate_ra_state_with_pc:
+      err = sframe_xlate_do_aarch64_negate_ra_state_with_pc (xlate_ctx, cfi_insn);
+      break;
     case DW_CFA_register:
       err = sframe_xlate_do_register (xlate_ctx, cfi_insn);
       break;
diff --git a/gas/gen-sframe.h b/gas/gen-sframe.h
index db1e5de968f..81c99bfc263 100644
--- a/gas/gen-sframe.h
+++ b/gas/gen-sframe.h
@@ -43,8 +43,9 @@ 
 
 typedef enum ATTRIBUTE_PACKED
 {
-  RA_no_signing = 0x0,
-  RA_signing_SP = 0x1,
+  RA_no_signing    = 0x0,
+  RA_signing_SP    = 0x1,
+  RA_signing_SP_PC = 0x3,
 } RA_signing_method_t;
 
 struct sframe_row_entry
diff --git a/gas/scfidw2gen.c b/gas/scfidw2gen.c
index 2b018fac8bd..1fd0cd832e5 100644
--- a/gas/scfidw2gen.c
+++ b/gas/scfidw2gen.c
@@ -113,6 +113,7 @@  const pseudo_typeS scfi_pseudo_table[] =
     { "cfi_restore_state", dot_scfi_ignore, 0 },
     { "cfi_window_save", dot_scfi_ignore, 0 },
     { "cfi_negate_ra_state", dot_scfi_ignore, 0 },
+    { "cfi_negate_ra_state_with_pc", dot_scfi_ignore, 0 },
     { "cfi_escape", dot_scfi_ignore, 0 },
     { "cfi_personality", dot_scfi_ignore, 0 },
     { "cfi_personality_id", dot_scfi_ignore, 0 },
diff --git a/gas/testsuite/gas/aarch64/pac_negate_ra_state_with_pc.d b/gas/testsuite/gas/aarch64/pac_negate_ra_state_with_pc.d
new file mode 100644
index 00000000000..e3ffc9eb494
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/pac_negate_ra_state_with_pc.d
@@ -0,0 +1,64 @@ 
+#source: pac_negate_ra_state_with_pc.s
+#as: -march=armv9.4-a+pauth-lr
+#objdump: --dwarf=frames
+# This test is only valid on ELF based ports.
+#notarget: *-*-*coff *-*-pe *-*-wince *-*-*aout* *-*-netbsd
+
+.+:     file .+
+
+Contents of the .eh_frame section:
+
+
+00000000 0000000000000010 00000000 CIE
+  Version:               1
+  Augmentation:          "zR"
+  Code alignment factor: 4
+  Data alignment factor: -8
+  Return address column: 30
+  Augmentation data:     1b
+  DW_CFA_def_cfa: r31 \(sp\) ofs 0
+
+00000014 000000000000001c 00000018 FDE cie=00000000 pc=0000000000000000..0000000000000014
+  DW_CFA_advance_loc: 4 to 0000000000000004
+  DW_CFA_AARCH64_negate_ra_state_with_pc
+  DW_CFA_advance_loc: 4 to 0000000000000008
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r29 \(x29\) at cfa-16
+  DW_CFA_offset: r30 \(x30\) at cfa-8
+  DW_CFA_advance_loc: 8 to 0000000000000010
+  DW_CFA_restore: r30 \(x30\)
+  DW_CFA_restore: r29 \(x29\)
+  DW_CFA_advance_loc: 4 to 0000000000000014
+  DW_CFA_AARCH64_negate_ra_state_with_pc
+  DW_CFA_nop
+
+00000034 0000000000000014 00000000 CIE
+  Version:               1
+  Augmentation:          "zRB"
+  Code alignment factor: 4
+  Data alignment factor: -8
+  Return address column: 30
+  Augmentation data:     1b
+  DW_CFA_def_cfa: r31 \(sp\) ofs 0
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
+0000004c 0000000000000020 0000001c FDE cie=00000034 pc=0000000000000014..0000000000000038
+  DW_CFA_advance_loc: 8 to 000000000000001c
+  DW_CFA_AARCH64_negate_ra_state_with_pc
+  DW_CFA_advance_loc: 4 to 0000000000000020
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r29 \(x29\) at cfa-16
+  DW_CFA_offset: r30 \(x30\) at cfa-8
+  DW_CFA_advance_loc: 8 to 0000000000000028
+  DW_CFA_restore: r30 \(x30\)
+  DW_CFA_restore: r29 \(x29\)
+  DW_CFA_advance_loc: 12 to 0000000000000034
+  DW_CFA_AARCH64_negate_ra_state_with_pc
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
diff --git a/gas/testsuite/gas/aarch64/pac_negate_ra_state_with_pc.s b/gas/testsuite/gas/aarch64/pac_negate_ra_state_with_pc.s
new file mode 100644
index 00000000000..e179b40ab1a
--- /dev/null
+++ b/gas/testsuite/gas/aarch64/pac_negate_ra_state_with_pc.s
@@ -0,0 +1,53 @@ 
+	.text
+	.align	2
+	.global	_Z5foo_pauth_lrv
+	.type	_Z5foo_pauth_lrv, %function
+_Z5foo_pauth_lrv:
+.LFB0:
+	.cfi_startproc
+	paciasppc
+	.cfi_negate_ra_state_with_pc
+	stp		x29, x30, [sp, -16]!
+	.cfi_def_cfa_offset 16
+	.cfi_offset 29, -16
+	.cfi_offset 30, -8
+	mov		x29, sp
+	// ...
+	ldp		x29, x30, [sp], #16
+	.cfi_restore 30
+	.cfi_restore 29
+	retaasppc	.LFB0
+	.cfi_negate_ra_state_with_pc
+	.cfi_endproc
+.LFE0:
+	.size	_Z5foo_pauth_lrv, .-_Z5foo_pauth_lrv
+
+
+	.align	2
+	.global	_Z5foo_pacmv
+	.type	_Z5foo_pacmv, %function
+_Z5foo_pac2_compatible_pauthv:
+.LFB1:
+	.cfi_startproc
+	.cfi_b_key_frame
+	hint		#0x27 // pacm
+.Ltmp1:
+	hint		#0x1b // pacibsp
+	.cfi_negate_ra_state_with_pc
+	stp		x29, x30, [sp, -16]!
+	.cfi_def_cfa_offset 16
+	.cfi_offset 29, -16
+	.cfi_offset 30, -8
+	mov		x29, sp
+	// ...
+	ldp		x29, x30, [sp], #16
+	.cfi_restore 30
+	.cfi_restore 29
+	adr		x16, .Ltmp1
+	hint		#0x27 // pacm
+	hint		#0x1f // autibsp
+	.cfi_negate_ra_state_with_pc
+	ret
+	.cfi_endproc
+.LFE1:
+	.size	_Z5foo_pac2_compatible_pauthv, .-_Z5foo_pac2_compatible_pauthv
diff --git a/include/dwarf2.def b/include/dwarf2.def
index 66c7fa1220f..95601fc7018 100644
--- a/include/dwarf2.def
+++ b/include/dwarf2.def
@@ -788,9 +788,11 @@  DW_CFA (DW_CFA_MIPS_advance_loc8, 0x1d)
 /* GNU extensions.
    NOTE: DW_CFA_GNU_window_save is multiplexed on Sparc and AArch64.  */
 DW_CFA (DW_CFA_GNU_window_save, 0x2d)
-DW_CFA_DUP (DW_CFA_AARCH64_negate_ra_state, 0x2d)
 DW_CFA (DW_CFA_GNU_args_size, 0x2e)
 DW_CFA (DW_CFA_GNU_negative_offset_extended, 0x2f)
+/* AArch64 extensions. */
+DW_CFA (DW_CFA_AARCH64_negate_ra_state_with_pc, 0x2c)
+DW_CFA_DUP (DW_CFA_AARCH64_negate_ra_state, 0x2d)
 
 DW_END_CFA
 
diff --git a/include/sframe.h b/include/sframe.h
index 53388aaaa91..f7a830a7275 100644
--- a/include/sframe.h
+++ b/include/sframe.h
@@ -265,7 +265,7 @@  typedef struct sframe_fre_info
 
 /* Set the mangled_ra_p bit as indicated.  */
 #define SFRAME_V1_FRE_INFO_UPDATE_RA_SIGNING_INFO(ra_signing_info, fre_info) \
-  ((((ra_signing_info) & 0x1) << 7) | ((fre_info) & 0x7f))
+  ((((ra_signing_info) & 0x3) << 7) | ((fre_info) & 0x7f))
 
 #define SFRAME_V1_FRE_CFA_BASE_REG_ID(data)	  ((data) & 0x1)
 #define SFRAME_V1_FRE_OFFSET_COUNT(data)	  (((data) >> 1) & 0xf)