Hi all, 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. Built on aarch64-linux-gnu and aarch64-none-elf, and tested on aarch64-none-elf with no regressions. This patch has been tested with the corresponding patch that enables B-key support in GCC. OK for trunk? bfd/ 2018-11-01 Sam Tebbs<sam.te...@arm.com> * elf-eh-frame.c (_bfd_elf_parse_eh_frame): Add check for 'B'. gas/ 2018-11-01 Sam Tebbs<sam.te...@arm.com> * dw2gencfi.c (struct cie_entry): Add pauth_key field. (alloc_fde_entry): Set pauth_key to AARCH64_PAUTH_KEY_A by default. (output_cie): Output 'B' if pauth_key == AARCH64_PAUTH_KEY_B. (select_cie_for_fde): Add check for pauth_key. Set cie's pauth_key to fde's pauth_key. (frch_cfi_data, cfa_save_data): Move to dwgencfi.h. * config/tc-aarch64.c (dot_cfi_b_key_frame): Declare. (md_pseudo_table): Add "cfi_b_key_frame". * dw2gencfi.h (pointer_auth_key): Define. (struct fde_entry): Add pauth_key field. (frch_cfi_data, cfa_save_data): Move from dwgencfi.c. gas/doc/ 2018-11-01 Sam Tebbs<sam.te...@arm.com> * c-aarch64.texi (.cfi_b_key_frame): Add documentation. gas/testsuite 2018-11-01 Sam Tebbs<sam.te...@arm.com> * gas/aarch64/(pac_ab_key.d, pac_ab_key.s): New file.
diff --git a/bfd/elf-eh-frame.c b/bfd/elf-eh-frame.c index 12f06ef9baa150cf460b451dad9cacd37116b19c..b76a657709f94b64a21bd458aed7f83304bca0dd 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/config/tc-aarch64.c b/gas/config/tc-aarch64.c index 085ffa26f0e4594fe97b50d19d79e27f742f34e6..e0861512fe5204f1cefcd1e7583f3c55c1be4b10 100644 --- a/gas/config/tc-aarch64.c +++ b/gas/config/tc-aarch64.c @@ -1992,6 +1992,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. */ @@ -2066,6 +2074,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 b659b8ba5971b964fbdf3357c97093471227db67..26c68efc8f522d9d4379a0ab61073d6839bc9b28 100644 --- a/gas/doc/c-aarch64.texi +++ b/gas/doc/c-aarch64.texi @@ -424,6 +424,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.h b/gas/dw2gencfi.h index cf534b477696eb5f718b47fdf2bb55d89beafec2..2b1362a2bb02161e8b9e6c8b781e38f39fd319d8 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/dw2gencfi.c b/gas/dw2gencfi.c index 3b610708022645d2544175cf53d19f4ced4fa8df..389d7e766a9bbf87c87876c0331cea462def3633 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; } @@ -1581,7 +1567,7 @@ dot_cfi_personality_id (int ignored ATTRIBUTE_UNUSED) ignore_rest_of_line (); } #endif - + static void output_cfi_insn (struct cfi_insn_data *insn) { @@ -1864,6 +1850,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'); @@ -2044,6 +2032,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) @@ -2150,6 +2139,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/testsuite/gas/aarch64/pac_ab_key.d b/gas/testsuite/gas/aarch64/pac_ab_key.d new file mode 100644 index 0000000000000000000000000000000000000000..a428633448900123d76edff641f03234a418f348 --- /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 0000000000000000000000000000000000000000..4b328e72ae456ca139b16de4e38528cde9ccca27 --- /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