From 3a67e1a6b4430374f3073e51bb19347d4c421cfe Mon Sep 17 00:00:00 2001 From: Sam Tebbs Date: Wed, 5 Dec 2018 18:27:23 +0000 Subject: [PATCH] [aarch64] Add support for pointer authentication B key 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 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 | 4 ++ bfd/elf-eh-frame.c | 2 + gas/ChangeLog | 19 +++++++++ gas/config/tc-aarch64.c | 9 +++++ gas/doc/c-aarch64.texi | 8 ++++ gas/dw2gencfi.c | 22 +++-------- gas/dw2gencfi.h | 23 +++++++++++ gas/testsuite/gas/aarch64/pac_ab_key.d | 54 ++++++++++++++++++++++++++ gas/testsuite/gas/aarch64/pac_ab_key.s | 31 +++++++++++++++ 9 files changed, 156 insertions(+), 16 deletions(-) create mode 100644 gas/testsuite/gas/aarch64/pac_ab_key.d create mode 100644 gas/testsuite/gas/aarch64/pac_ab_key.s diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 618126fffbe..de468a49501 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,7 @@ +2018-12-05 Sam Tebbs + + * elf-eh-frame.c (_bfd_elf_parse_eh_frame): Add check for 'B'. + 2018-12-04 H.J. Lu PR ld/23372 diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c index 12f06ef9baa..b76a657709f 100644 --- a/bfd/elf-eh-frame.c +++ b/bfd/elf-eh-frame.c @@ -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); diff --git a/gas/ChangeLog b/gas/ChangeLog index dbb8a379c0b..73cadc7a775 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,22 @@ +2018-12-05 Sam Tebbs + + * 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 PR 23939 diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c index 3fed11cb6c6..4c97703f22e 100644 --- a/gas/config/tc-aarch64.c +++ b/gas/config/tc-aarch64.c @@ -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}, diff --git a/gas/doc/c-aarch64.texi b/gas/doc/c-aarch64.texi index f38fdf591a2..ca84c9d766f 100644 --- a/gas/doc/c-aarch64.texi +++ b/gas/doc/c-aarch64.texi @@ -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 diff --git a/gas/dw2gencfi.c b/gas/dw2gencfi.c index 4add1735632..ff5c0df7f61 100644 --- a/gas/dw2gencfi.c +++ b/gas/dw2gencfi.c @@ -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; -}; - /* 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 diff --git a/gas/dw2gencfi.h b/gas/dw2gencfi.h index cf534b47769..2b1362a2bb0 100644 --- a/gas/dw2gencfi.h +++ b/gas/dw2gencfi.h @@ -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 index 00000000000..a4286334489 --- /dev/null +++ b/gas/testsuite/gas/aarch64/pac_ab_key.d @@ -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 index 00000000000..4b328e72ae4 --- /dev/null +++ b/gas/testsuite/gas/aarch64/pac_ab_key.s @@ -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 -- 2.30.2