[aarch64] Add support for pointer authentication B key
authorSam Tebbs <sam.tebbs@arm.com>
Wed, 5 Dec 2018 18:27:23 +0000 (18:27 +0000)
committerThomas Preud'homme <thomas.preudhomme@linaro.org>
Wed, 5 Dec 2018 18:30:08 +0000 (18:30 +0000)
Armv8.3-A has another key used in pointer authentication called the
B-key (other than the A-key that is already supported). In order for
stack unwinders to work it is necessary to be able to identify frames
that have been signed with the B-key rather than the A-key and it was
felt that keeping this as an augmentation character in the CIE was the
best bet. The DWARF extensions for ARM therefore propose to add a new
augmentation character 'B' to the CIE augmentation string and the
corresponding cfi directive ".cfi_b_key_frame". I've made the relevant
changes to GAS and LD to add support for B-key unwinding, which required
modifying LD to check for 'B' in the augmentation string, adding the
".cfi_b_key_frame" directive to GAS and adding a "pauth_key" field to
GAS's fde_entry and cie_entry structs.

The pointer authentication instructions will behave as NOPs on
architectures that don't support them, and so a check for the
architecture being assembled for is not necessary since there will be no
behavioural difference between augmentation strings with and without the
'B' character on such architectures.

2018-12-05  Sam Tebbs  <sam.tebbs@arm.com>

bfd/
* elf-eh-frame.c (_bfd_elf_parse_eh_frame): Add check for 'B'.

gas/
* dw2gencfi.c (struct cie_entry): Add tc_cie_entry_extras invocation.
(alloc_fde_entry): Add tc_fde_entry_init_extra invocation.
(output_cie): Add tc_output_cie_extra invocation.
(select_cie_for_fde): Add tc_cie_fde_equivalent_extra and
tc_cie_entry_init_extra invocation.
(frch_cfi_data, cfa_save_data): Move to dwgencfi.h.
* config/tc-aarch64.c (s_aarch64_cfi_b_key_frame): Declare.
(md_pseudo_table): Add "cfi_b_key_frame".
* config/tc-aarch64.h (tc_fde_entry_extras, tc_cie_entry_extras,
tc_fde_entry_init_extra, tc_output_cie_extra,
tc_cie_fde_equivalent_extra, tc_cie_entry_init_extra): Define.
* dw2gencfi.h (struct fde_entry): Add tc_fde_entry_extras invocation.
(pointer_auth_key): Define.
(frch_cfi_data, cfa_save_data): Move from dwgencfi.c.
* doc/c-aarch64.texi (.cfi_b_key_frame): Add documentation.
* testsuite/gas/aarch64/(pac_ab_key.d, pac_ab_key.s): New file.

bfd/ChangeLog
bfd/elf-eh-frame.c
gas/ChangeLog
gas/config/tc-aarch64.c
gas/doc/c-aarch64.texi
gas/dw2gencfi.c
gas/dw2gencfi.h
gas/testsuite/gas/aarch64/pac_ab_key.d [new file with mode: 0644]
gas/testsuite/gas/aarch64/pac_ab_key.s [new file with mode: 0644]

index 618126fffbeb4f485f54bba2e1e523ae84ed44a4..de468a49501421ce6c057dc91b0f71232cd5467d 100644 (file)
@@ -1,3 +1,7 @@
+2018-12-05  Sam Tebbs  <sam.tebbs@arm.com>
+
+       * elf-eh-frame.c (_bfd_elf_parse_eh_frame): Add check for 'B'.
+
 2018-12-04  H.J. Lu  <hongjiu.lu@intel.com>
 
        PR ld/23372
index 12f06ef9baa150cf460b451dad9cacd37116b19c..b76a657709f94b64a21bd458aed7f83304bca0dd 100644 (file)
@@ -797,6 +797,8 @@ _bfd_elf_parse_eh_frame (bfd *abfd, struct bfd_link_info *info,
              while (*aug != '\0')
                switch (*aug++)
                  {
+                 case 'B':
+                   break;
                  case 'L':
                    REQUIRE (read_byte (&buf, end, &cie->lsda_encoding));
                    ENSURE_NO_RELOCS (buf);
index dbb8a379c0ba306a6b7173c02477265558016220..73cadc7a77511c526dbc65b4701bbdf2dadb8147 100644 (file)
@@ -1,3 +1,22 @@
+2018-12-05  Sam Tebbs  <sam.tebbs@arm.com>
+
+       * dw2gencfi.c (struct cie_entry): Add tc_cie_entry_extras invocation.
+       (alloc_fde_entry): Add tc_fde_entry_init_extra invocation.
+       (output_cie): Add tc_output_cie_extra invocation.
+       (select_cie_for_fde): Add tc_cie_fde_equivalent_extra and
+       tc_cie_entry_init_extra invocation.
+       (frch_cfi_data, cfa_save_data): Move to dwgencfi.h.
+       * config/tc-aarch64.c (s_aarch64_cfi_b_key_frame): Declare.
+       (md_pseudo_table): Add "cfi_b_key_frame".
+       * config/tc-aarch64.h (tc_fde_entry_extras, tc_cie_entry_extras,
+       tc_fde_entry_init_extra, tc_output_cie_extra,
+       tc_cie_fde_equivalent_extra, tc_cie_entry_init_extra): Define.
+       * dw2gencfi.h (struct fde_entry): Add tc_fde_entry_extras invocation.
+       (pointer_auth_key): Define.
+       (frch_cfi_data, cfa_save_data): Move from dwgencfi.c.
+       * doc/c-aarch64.texi (.cfi_b_key_frame): Add documentation.
+       * testsuite/gas/aarch64/(pac_ab_key.d, pac_ab_key.s): New file.
+
 2018-12-04  wu.heng  <wu.heng@zte.com.cn>
 
        PR 23939
index 3fed11cb6c61430f1c4822a1a18b7635c473bebf..4c97703f22ea9825b80e36f78a4864fcd0d5caf7 100644 (file)
@@ -1993,6 +1993,14 @@ s_aarch64_inst (int ignored ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
+static void
+s_aarch64_cfi_b_key_frame (int ignored ATTRIBUTE_UNUSED)
+{
+  demand_empty_rest_of_line ();
+  struct fde_entry *fde = frchain_now->frch_cfi_data->cur_fde_data;
+  fde->pauth_key = AARCH64_PAUTH_KEY_B;
+}
+
 #ifdef OBJ_ELF
 /* Emit BFD_RELOC_AARCH64_TLSDESC_ADD on the next ADD instruction.  */
 
@@ -2067,6 +2075,7 @@ const pseudo_typeS md_pseudo_table[] = {
   {"arch", s_aarch64_arch, 0},
   {"arch_extension", s_aarch64_arch_extension, 0},
   {"inst", s_aarch64_inst, 0},
+  {"cfi_b_key_frame", s_aarch64_cfi_b_key_frame, 0},
 #ifdef OBJ_ELF
   {"tlsdescadd", s_tlsdescadd, 0},
   {"tlsdesccall", s_tlsdesccall, 0},
index f38fdf591a23c9c70f1b8e5fc5f183b5a21840e2..ca84c9d766fc28e67008f3764710dfe44c6b630a 100644 (file)
@@ -435,6 +435,14 @@ as the @code{.dword} directive.
 @c YYYYYYYYYYYYYYYYYYYYYYYYYY
 @c ZZZZZZZZZZZZZZZZZZZZZZZZZZ
 
+@cindex @code{.cfi_b_key_frame} directive, AArch64
+@item  @code{.cfi_b_key_frame}
+The @code{.cfi_b_key_frame} directive inserts a 'B' character into the CIE
+corresponding to the current frame's FDE, meaning that its return address has
+been signed with the B-key.  If two frames are signed with differing keys then
+they will not share the same CIE.  This information is intended to be used by
+the stack unwinder in order to properly authenticate return addresses.
+
 @end table
 
 @node AArch64 Opcodes
index 4add17356322204236229f9af85ac07c9cfb2ace..ff5c0df7f61210c544faaeb46520c4f235826569 100644 (file)
@@ -403,6 +403,7 @@ struct cie_entry
   unsigned char per_encoding;
   unsigned char lsda_encoding;
   expressionS personality;
+  enum pointer_auth_key pauth_key;
   struct cfi_insn_data *first, *last;
 };
 
@@ -414,22 +415,6 @@ static struct fde_entry **last_fde_data = &all_fde_data;
 /* List of CIEs so that they could be reused.  */
 static struct cie_entry *cie_root;
 
-/* Stack of old CFI data, for save/restore.  */
-struct cfa_save_data
-{
-  struct cfa_save_data *next;
-  offsetT cfa_offset;
-};
-
-/* Current open FDE entry.  */
-struct frch_cfi_data
-{
-  struct fde_entry *cur_fde_data;
-  symbolS *last_address;
-  offsetT cur_cfa_offset;
-  struct cfa_save_data *cfa_save_stack;
-};
-\f
 /* Construct a new FDE structure and add it to the end of the fde list.  */
 
 static struct fde_entry *
@@ -448,6 +433,7 @@ alloc_fde_entry (void)
   fde->per_encoding = DW_EH_PE_omit;
   fde->lsda_encoding = DW_EH_PE_omit;
   fde->eh_header_type = EH_COMPACT_UNKNOWN;
+  fde->pauth_key = AARCH64_PAUTH_KEY_A;
 
   return fde;
 }
@@ -1872,6 +1858,8 @@ output_cie (struct cie_entry *cie, bfd_boolean eh_frame, int align)
       if (cie->lsda_encoding != DW_EH_PE_omit)
        out_one ('L');
       out_one ('R');
+      if (cie->pauth_key == AARCH64_PAUTH_KEY_B)
+       out_one ('B');
     }
   if (cie->signal_frame)
     out_one ('S');
@@ -2052,6 +2040,7 @@ select_cie_for_fde (struct fde_entry *fde, bfd_boolean eh_frame,
       if (CUR_SEG (cie) != CUR_SEG (fde))
        continue;
       if (cie->return_column != fde->return_column
+         || cie->pauth_key != fde->pauth_key
          || cie->signal_frame != fde->signal_frame
          || cie->per_encoding != fde->per_encoding
          || cie->lsda_encoding != fde->lsda_encoding)
@@ -2158,6 +2147,7 @@ select_cie_for_fde (struct fde_entry *fde, bfd_boolean eh_frame,
   cie->lsda_encoding = fde->lsda_encoding;
   cie->personality = fde->personality;
   cie->first = fde->data;
+  cie->pauth_key = fde->pauth_key;
 
   for (i = cie->first; i ; i = i->next)
     if (i->insn == DW_CFA_advance_loc
index cf534b477696eb5f718b47fdf2bb55d89beafec2..2b1362a2bb02161e8b9e6c8b781e38f39fd319d8 100644 (file)
@@ -135,6 +135,27 @@ enum {
   EH_COMPACT_HAS_LSDA
 };
 
+enum pointer_auth_key {
+  AARCH64_PAUTH_KEY_A,
+  AARCH64_PAUTH_KEY_B
+};
+
+/* Stack of old CFI data, for save/restore.  */
+struct cfa_save_data
+{
+  struct cfa_save_data *next;
+  offsetT cfa_offset;
+};
+
+/* Current open FDE entry.  */
+struct frch_cfi_data
+{
+  struct fde_entry *cur_fde_data;
+  symbolS *last_address;
+  offsetT cur_cfa_offset;
+  struct cfa_save_data *cfa_save_stack;
+};
+
 struct fde_entry
 {
   struct fde_entry *next;
@@ -162,6 +183,8 @@ struct fde_entry
   /* For out of line tables and FDEs.  */
   symbolS *eh_loc;
   int sections;
+  /* The pointer authentication key used.  Only used for AArch64.  */
+  enum pointer_auth_key pauth_key;
 };
 
 /* The list of all FDEs that have been collected.  */
diff --git a/gas/testsuite/gas/aarch64/pac_ab_key.d b/gas/testsuite/gas/aarch64/pac_ab_key.d
new file mode 100644 (file)
index 0000000..a428633
--- /dev/null
@@ -0,0 +1,54 @@
+#objdump: --dwarf=frames
+# Test assembling a file with functions signed by two different pointer
+# authentication keys. It must interpret .cfi_b_key_frame properly and emit a
+# 'B' character into the correct CIE's augmentation string.
+
+.+:     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 0000000000000018 00000018 FDE cie=00000000 pc=0000000000000000..0000000000000008
+  DW_CFA_advance_loc: 4 to 0000000000000004
+  DW_CFA_GNU_window_save
+  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_nop
+  DW_CFA_nop
+
+00000030 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
+
+00000048 000000000000001c 0000001c FDE cie=00000030 pc=0000000000000008..0000000000000010
+  DW_CFA_advance_loc: 4 to 000000000000000c
+  DW_CFA_GNU_window_save
+  DW_CFA_advance_loc: 4 to 0000000000000010
+  DW_CFA_def_cfa_offset: 16
+  DW_CFA_offset: r29 \(x29\) at cfa-16
+  DW_CFA_offset: r30 \(x30\) at cfa-8
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+  DW_CFA_nop
+
diff --git a/gas/testsuite/gas/aarch64/pac_ab_key.s b/gas/testsuite/gas/aarch64/pac_ab_key.s
new file mode 100644 (file)
index 0000000..4b328e7
--- /dev/null
@@ -0,0 +1,31 @@
+       .arch armv8-a
+       .text
+       .align  2
+       .global _Z5foo_av
+       .type   _Z5foo_av, %function
+_Z5foo_av:
+.LFB0:
+       .cfi_startproc
+       hint    25 // paciasp
+       .cfi_window_save
+       stp     x29, x30, [sp, -16]!
+       .cfi_def_cfa_offset 16
+       .cfi_offset 29, -16
+       .cfi_offset 30, -8
+       .cfi_endproc
+.LFE0:
+       .size   _Z5foo_av, .-_Z5foo_av
+       .align  2
+       .global _Z5foo_bv
+       .type   _Z5foo_bv, %function
+_Z5foo_bv:
+.LFB1:
+       .cfi_startproc
+       .cfi_b_key_frame
+       hint    27 // pacibsp
+       .cfi_window_save
+       stp     x29, x30, [sp, -16]!
+       .cfi_def_cfa_offset 16
+       .cfi_offset 29, -16
+       .cfi_offset 30, -8
+       .cfi_endproc