diff options
-rw-r--r-- | bfd/elfnn-aarch64.c | 175 | ||||
-rw-r--r-- | ld/testsuite/ld-aarch64/aarch64-elf.exp | 3 | ||||
-rw-r--r-- | ld/testsuite/ld-aarch64/bti-far-1.d | 83 | ||||
-rw-r--r-- | ld/testsuite/ld-aarch64/bti-far-2.d | 62 | ||||
-rw-r--r-- | ld/testsuite/ld-aarch64/bti-far.ld | 15 | ||||
-rw-r--r-- | ld/testsuite/ld-aarch64/bti-far.s | 29 |
6 files changed, 352 insertions, 15 deletions
diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c index f858d10c596..d9ebeae922c 100644 --- a/bfd/elfnn-aarch64.c +++ b/bfd/elfnn-aarch64.c @@ -2354,6 +2354,9 @@ elfNN_aarch64_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, name can be changed. The only requirement is the %s be present. */ #define STUB_ENTRY_NAME "__%s_veneer" +/* Stub name for a BTI landing stub. */ +#define BTI_STUB_ENTRY_NAME "__%s_bti_veneer" + /* The name of the dynamic interpreter. This is put in the .interp section. */ #define ELF_DYNAMIC_INTERPRETER "/lib/ld.so.1" @@ -2406,6 +2409,12 @@ static const uint32_t aarch64_long_branch_stub[] = 0x00000000, }; +static const uint32_t aarch64_bti_direct_branch_stub[] = +{ + 0xd503245f, /* bti c */ + 0x14000000, /* b <label> */ +}; + static const uint32_t aarch64_erratum_835769_stub[] = { 0x00000000, /* Placeholder for multiply accumulate. */ @@ -2427,6 +2436,7 @@ enum elf_aarch64_stub_type aarch64_stub_none, aarch64_stub_adrp_branch, aarch64_stub_long_branch, + aarch64_stub_bti_direct_branch, aarch64_stub_erratum_835769_veneer, aarch64_stub_erratum_843419_veneer, }; @@ -2455,6 +2465,9 @@ struct elf_aarch64_stub_hash_entry /* Destination symbol type */ unsigned char st_type; + /* The target is also a stub. */ + bool double_stub; + /* Where this stub is being called from, or, in the case of combined stub sections, the first input section in the group. */ asection *id_sec; @@ -2679,6 +2692,11 @@ struct elf_aarch64_link_hash_table unsigned int top_index; asection **input_list; + /* True when two stubs are added where one targets the other, happens + when BTI stubs are inserted and then the stub layout must not change + during elfNN_aarch64_build_stubs. */ + bool has_double_stub; + /* JUMP_SLOT relocs for variant PCS symbols may be present. */ int variant_pcs; @@ -2974,14 +2992,6 @@ aarch64_relocate (unsigned int r_type, bfd *input_bfd, asection *input_section, howto, value) == bfd_reloc_ok; } -static enum elf_aarch64_stub_type -aarch64_select_branch_stub (bfd_vma value, bfd_vma place) -{ - if (aarch64_valid_for_adrp_p (value, place)) - return aarch64_stub_adrp_branch; - return aarch64_stub_long_branch; -} - /* Determine the type of stub needed, if any, for a call. */ static enum elf_aarch64_stub_type @@ -3251,14 +3261,17 @@ aarch64_build_one_stub (struct bfd_hash_entry *gen_entry, bfd_vma veneer_entry_loc; bfd_signed_vma branch_offset = 0; unsigned int template_size; + unsigned int pad_size = 0; const uint32_t *template; unsigned int i; struct bfd_link_info *info; + struct elf_aarch64_link_hash_table *htab; /* Massage our args to the form they really have. */ stub_entry = (struct elf_aarch64_stub_hash_entry *) gen_entry; info = (struct bfd_link_info *) in_arg; + htab = elf_aarch64_hash_table (info); /* Fail if the target section could not be assigned to an output section. The user should fix his linker script. */ @@ -3271,6 +3284,10 @@ aarch64_build_one_stub (struct bfd_hash_entry *gen_entry, stub_sec = stub_entry->stub_sec; + /* The layout must not change when a stub may be the target of another. */ + if (htab->has_double_stub) + BFD_ASSERT (stub_entry->stub_offset == stub_sec->size); + /* Make a note of the offset within the stubs for this entry. */ stub_entry->stub_offset = stub_sec->size; loc = stub_sec->contents + stub_entry->stub_offset; @@ -3289,7 +3306,14 @@ aarch64_build_one_stub (struct bfd_hash_entry *gen_entry, /* See if we can relax the stub. */ if (aarch64_valid_for_adrp_p (sym_value, place)) - stub_entry->stub_type = aarch64_select_branch_stub (sym_value, place); + { + stub_entry->stub_type = aarch64_stub_adrp_branch; + + /* Avoid the relaxation changing the layout. */ + if (htab->has_double_stub) + pad_size = sizeof (aarch64_long_branch_stub) + - sizeof (aarch64_adrp_branch_stub); + } } switch (stub_entry->stub_type) @@ -3302,6 +3326,10 @@ aarch64_build_one_stub (struct bfd_hash_entry *gen_entry, template = aarch64_long_branch_stub; template_size = sizeof (aarch64_long_branch_stub); break; + case aarch64_stub_bti_direct_branch: + template = aarch64_bti_direct_branch_stub; + template_size = sizeof (aarch64_bti_direct_branch_stub); + break; case aarch64_stub_erratum_835769_veneer: template = aarch64_erratum_835769_stub; template_size = sizeof (aarch64_erratum_835769_stub); @@ -3320,6 +3348,7 @@ aarch64_build_one_stub (struct bfd_hash_entry *gen_entry, loc += 4; } + template_size += pad_size; template_size = (template_size + 7) & ~7; stub_sec->size += template_size; @@ -3345,6 +3374,12 @@ aarch64_build_one_stub (struct bfd_hash_entry *gen_entry, BFD_FAIL (); break; + case aarch64_stub_bti_direct_branch: + if (!aarch64_relocate (AARCH64_R (JUMP26), stub_bfd, stub_sec, + stub_entry->stub_offset + 4, sym_value)) + BFD_FAIL (); + break; + case aarch64_stub_erratum_835769_veneer: veneered_insn_loc = stub_entry->target_section->output_section->vma + stub_entry->target_section->output_offset @@ -3375,7 +3410,8 @@ aarch64_build_one_stub (struct bfd_hash_entry *gen_entry, } /* As above, but don't actually build the stub. Just bump offset so - we know stub section sizes. */ + we know stub section sizes and record the offset for each stub so + a stub can target another stub (needed for BTI direct branch stub). */ static bool aarch64_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) @@ -3396,6 +3432,9 @@ aarch64_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) case aarch64_stub_long_branch: size = sizeof (aarch64_long_branch_stub); break; + case aarch64_stub_bti_direct_branch: + size = sizeof (aarch64_bti_direct_branch_stub); + break; case aarch64_stub_erratum_835769_veneer: size = sizeof (aarch64_erratum_835769_stub); break; @@ -3411,10 +3450,20 @@ aarch64_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) } size = (size + 7) & ~7; + stub_entry->stub_offset = stub_entry->stub_sec->size; stub_entry->stub_sec->size += size; return true; } +/* Output is BTI compatible. */ + +static bool +elf_aarch64_bti_p (bfd *output_bfd) +{ + uint32_t prop = elf_aarch64_tdata (output_bfd)->gnu_and_prop; + return prop & GNU_PROPERTY_AARCH64_FEATURE_1_BTI; +} + /* External entry points for sizing and building linker stubs. */ /* Set up various things so that we can make a list of input sections @@ -3621,6 +3670,16 @@ group_sections (struct elf_aarch64_link_hash_table *htab, #undef PREV_SEC #undef PREV_SEC +/* True if the inserted stub does not break BTI compatibility. */ + +static bool +aarch64_bti_stub_p (struct elf_aarch64_stub_hash_entry *stub_entry) +{ + /* Stubs without indirect branch are BTI compatible. */ + return stub_entry->stub_type != aarch64_stub_adrp_branch + && stub_entry->stub_type != aarch64_stub_long_branch; +} + #define AARCH64_BITS(x, pos, n) (((x) >> (pos)) & ((1 << (n)) - 1)) #define AARCH64_RT(insn) AARCH64_BITS (insn, 0, 5) @@ -4080,7 +4139,10 @@ _bfd_aarch64_resize_stubs (struct elf_aarch64_link_hash_table *htab) /* Ignore non-stub sections. */ if (!strstr (section->name, STUB_SUFFIX)) continue; - section->size = 0; + + /* Add space for a branch. Add 8 bytes to keep section 8 byte aligned, + as long branch stubs contain a 64-bit address. */ + section->size = 8; } bfd_hash_traverse (&htab->stub_hash_table, aarch64_size_one_stub, htab); @@ -4091,10 +4153,9 @@ _bfd_aarch64_resize_stubs (struct elf_aarch64_link_hash_table *htab) if (!strstr (section->name, STUB_SUFFIX)) continue; - /* Add space for a branch. Add 8 bytes to keep section 8 byte aligned, - as long branch stubs contain a 64-bit address. */ - if (section->size) - section->size += 8; + /* Empty stub section. */ + if (section->size == 8) + section->size = 0; /* Ensure all stub sections have a size which is a multiple of 4096. This is important in order to ensure that the insertion @@ -4276,6 +4337,7 @@ _bfd_aarch64_add_call_stub_entries (bool *stub_changed, bfd *output_bfd, struct bfd_link_info *info) { struct elf_aarch64_link_hash_table *htab = elf_aarch64_hash_table (info); + bool need_bti = elf_aarch64_bti_p (output_bfd); bfd *input_bfd; for (input_bfd = info->input_bfds; input_bfd != NULL; @@ -4327,13 +4389,16 @@ _bfd_aarch64_add_call_stub_entries (bool *stub_changed, bfd *output_bfd, unsigned int r_type, r_indx; enum elf_aarch64_stub_type stub_type; struct elf_aarch64_stub_hash_entry *stub_entry; + struct elf_aarch64_stub_hash_entry *stub_entry_bti; asection *sym_sec; bfd_vma sym_value; bfd_vma destination; struct elf_aarch64_link_hash_entry *hash; const char *sym_name; char *stub_name; + char *stub_name_bti; const asection *id_sec; + const asection *id_sec_bti; unsigned char st_type; bfd_size_type len; @@ -4498,6 +4563,25 @@ _bfd_aarch64_add_call_stub_entries (bool *stub_changed, bfd *output_bfd, /* Always update this stub's target since it may have changed after layout. */ stub_entry->target_value = sym_value + irela->r_addend; + + if (stub_entry->double_stub) + { + /* Update the target of both stubs. */ + + id_sec_bti = htab->stub_group[sym_sec->id].link_sec; + stub_name_bti = + elfNN_aarch64_stub_name (id_sec_bti, sym_sec, hash, + irela); + if (!stub_name_bti) + goto error_ret_free_internal; + stub_entry_bti = + aarch64_stub_hash_lookup (&htab->stub_hash_table, + stub_name_bti, false, false); + BFD_ASSERT (stub_entry_bti != NULL); + free (stub_name_bti); + stub_entry_bti->target_value = stub_entry->target_value; + stub_entry->target_value = stub_entry_bti->stub_offset; + } continue; } @@ -4528,6 +4612,60 @@ _bfd_aarch64_add_call_stub_entries (bool *stub_changed, bfd *output_bfd, snprintf (stub_entry->output_name, len, STUB_ENTRY_NAME, sym_name); + /* A stub with indirect jump may break BTI compatibility, so + insert another stub with direct jump near the target then. */ + if (need_bti && !aarch64_bti_stub_p (stub_entry)) + { + stub_entry->double_stub = true; + htab->has_double_stub = true; + id_sec_bti = htab->stub_group[sym_sec->id].link_sec; + stub_name_bti = + elfNN_aarch64_stub_name (id_sec_bti, sym_sec, hash, irela); + if (!stub_name_bti) + { + free (stub_name); + goto error_ret_free_internal; + } + + stub_entry_bti = + aarch64_stub_hash_lookup (&htab->stub_hash_table, + stub_name_bti, false, false); + if (stub_entry_bti == NULL) + stub_entry_bti = + _bfd_aarch64_add_stub_entry_in_group (stub_name_bti, + sym_sec, htab); + if (stub_entry_bti == NULL) + { + free (stub_name); + free (stub_name_bti); + goto error_ret_free_internal; + } + + stub_entry_bti->target_value = sym_value + irela->r_addend; + stub_entry_bti->target_section = sym_sec; + stub_entry_bti->stub_type = aarch64_stub_bti_direct_branch; + stub_entry_bti->h = hash; + stub_entry_bti->st_type = st_type; + + len = sizeof (BTI_STUB_ENTRY_NAME) + strlen (sym_name); + stub_entry_bti->output_name = bfd_alloc (htab->stub_bfd, len); + if (stub_entry_bti->output_name == NULL) + { + free (stub_name); + free (stub_name_bti); + goto error_ret_free_internal; + } + snprintf (stub_entry_bti->output_name, len, + BTI_STUB_ENTRY_NAME, sym_name); + + /* Update the indirect call stub to target the BTI stub. */ + stub_entry->target_value = 0; + stub_entry->target_section = stub_entry_bti->stub_sec; + stub_entry->stub_type = stub_type; + stub_entry->h = NULL; + stub_entry->st_type = STT_FUNC; + } + *stub_changed = true; } @@ -8443,6 +8581,13 @@ aarch64_map_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) if (!elfNN_aarch64_output_map_sym (osi, AARCH64_MAP_DATA, addr + 16)) return false; break; + case aarch64_stub_bti_direct_branch: + if (!elfNN_aarch64_output_stub_sym (osi, stub_name, addr, + sizeof (aarch64_bti_direct_branch_stub))) + return false; + if (!elfNN_aarch64_output_map_sym (osi, AARCH64_MAP_INSN, addr)) + return false; + break; case aarch64_stub_erratum_835769_veneer: if (!elfNN_aarch64_output_stub_sym (osi, stub_name, addr, sizeof (aarch64_erratum_835769_stub))) diff --git a/ld/testsuite/ld-aarch64/aarch64-elf.exp b/ld/testsuite/ld-aarch64/aarch64-elf.exp index 48c8c12ef8f..80662de7e44 100644 --- a/ld/testsuite/ld-aarch64/aarch64-elf.exp +++ b/ld/testsuite/ld-aarch64/aarch64-elf.exp @@ -461,6 +461,9 @@ run_dump_test "bti-warn" run_dump_test "weak-tls" run_dump_test "undef-tls" +run_dump_test "bti-far-1" +run_dump_test "bti-far-2" + if { ![skip_sframe_tests] } { run_dump_test "sframe-simple-1" } diff --git a/ld/testsuite/ld-aarch64/bti-far-1.d b/ld/testsuite/ld-aarch64/bti-far-1.d new file mode 100644 index 00000000000..d2dbc9db110 --- /dev/null +++ b/ld/testsuite/ld-aarch64/bti-far-1.d @@ -0,0 +1,83 @@ +#name: Check linker stubs with indirect calls handle BTI (shared lib). +#source: bti-far.s +#target: [check_shared_lib_support] +#as: -mabi=lp64 +#ld: -shared -T bti-far.ld +#objdump: -dr + +[^:]*: *file format elf64-.*aarch64 + + +Disassembly of section \.plt: + +0000000000018000 <\.plt>: + 18000: d503245f bti c + 18004: a9bf7bf0 stp x16, x30, \[sp, #-16\]! + 18008: 900000d0 adrp x16, 30000 <_GLOBAL_OFFSET_TABLE_> + 1800c: f9400e11 ldr x17, \[x16, #24\] + 18010: 91006210 add x16, x16, #0x18 + 18014: d61f0220 br x17 + 18018: d503201f nop + 1801c: d503201f nop + +0000000000018020 <foo@plt>: + 18020: 900000d0 adrp x16, 30000 <_GLOBAL_OFFSET_TABLE_> + 18024: f9401211 ldr x17, \[x16, #32\] + 18028: 91008210 add x16, x16, #0x20 + 1802c: d61f0220 br x17 + 18030: 14000004 b 18040 <__foo_bti_veneer\+0x8> + 18034: d503201f nop + +0000000000018038 <__foo_bti_veneer>: + 18038: d503245f bti c + 1803c: 17fffff9 b 18020 <foo@plt> + +Disassembly of section \.text: + +0000000000020000 <_start>: + 20000: 97ffe008 bl 18020 <foo@plt> + 20004: 94000007 bl 20020 <___veneer> + 20008: 94000001 bl 2000c <baz> + +000000000002000c <baz>: + 2000c: d65f03c0 ret + 20010: 1400000a b 20038 <___veneer\+0x18> + 20014: d503201f nop + +0000000000020018 <___bti_veneer>: + 20018: d503245f bti c + 2001c: 17fffffc b 2000c <baz> + +0000000000020020 <___veneer>: + 20020: 90091910 adrp x16, 12340000 <foo> + 20024: 91012210 add x16, x16, #0x48 + 20028: d61f0200 br x16 + \.\.\. + +Disassembly of section \.far: + +0000000012340000 <foo>: + 12340000: 9400000c bl 12340030 <___veneer> + +0000000012340004 <bar>: + 12340004: 9400000b bl 12340030 <___veneer> + 12340008: 94000004 bl 12340018 <__foo_veneer> + 1234000c: 00000000 udf #0 + 12340010: 14000010 b 12340050 <___bti_veneer\+0x8> + 12340014: d503201f nop + +0000000012340018 <__foo_veneer>: + 12340018: 90f6e6d0 adrp x16, 18000 <\.plt> + 1234001c: 9100e210 add x16, x16, #0x38 + 12340020: d61f0200 br x16 + \.\.\. + +0000000012340030 <___veneer>: + 12340030: 90f6e710 adrp x16, 20000 <_start> + 12340034: 91006210 add x16, x16, #0x18 + 12340038: d61f0200 br x16 + \.\.\. + +0000000012340048 <___bti_veneer>: + 12340048: d503245f bti c + 1234004c: 17ffffee b 12340004 <bar> diff --git a/ld/testsuite/ld-aarch64/bti-far-2.d b/ld/testsuite/ld-aarch64/bti-far-2.d new file mode 100644 index 00000000000..b859e6ce399 --- /dev/null +++ b/ld/testsuite/ld-aarch64/bti-far-2.d @@ -0,0 +1,62 @@ +#name: Check linker stubs with indirect calls handle BTI (exe). +#source: bti-far.s +#as: -mabi=lp64 +#ld: -T bti-far.ld +#objdump: -dr + +[^:]*: *file format elf64-.*aarch64 + + +Disassembly of section \.text: + +0000000000020000 <_start>: + 20000: 9400000e bl 20038 <__foo_veneer> + 20004: 94000007 bl 20020 <___veneer> + 20008: 94000001 bl 2000c <baz> + +000000000002000c <baz>: + 2000c: d65f03c0 ret + 20010: 14000010 b 20050 <__foo_veneer\+0x18> + 20014: d503201f nop + +0000000000020018 <___bti_veneer>: + 20018: d503245f bti c + 2001c: 17fffffc b 2000c <baz> + +0000000000020020 <___veneer>: + 20020: 90091910 adrp x16, 12340000 <foo> + 20024: 9100e210 add x16, x16, #0x38 + 20028: d61f0200 br x16 + \.\.\. + +0000000000020038 <__foo_veneer>: + 20038: 90091910 adrp x16, 12340000 <foo> + 2003c: 91006210 add x16, x16, #0x18 + 20040: d61f0200 br x16 + \.\.\. + +Disassembly of section \.far: + +0000000012340000 <foo>: + 12340000: 94000008 bl 12340020 <___veneer> + +0000000012340004 <bar>: + 12340004: 94000007 bl 12340020 <___veneer> + 12340008: 97fffffe bl 12340000 <foo> + 1234000c: 00000000 udf #0 + 12340010: 1400000c b 12340040 <___bti_veneer\+0x8> + 12340014: d503201f nop + +0000000012340018 <__foo_bti_veneer>: + 12340018: d503245f bti c + 1234001c: 17fffff9 b 12340000 <foo> + +0000000012340020 <___veneer>: + 12340020: 90f6e710 adrp x16, 20000 <_start> + 12340024: 91006210 add x16, x16, #0x18 + 12340028: d61f0200 br x16 + \.\.\. + +0000000012340038 <___bti_veneer>: + 12340038: d503245f bti c + 1234003c: 17fffff2 b 12340004 <bar> diff --git a/ld/testsuite/ld-aarch64/bti-far.ld b/ld/testsuite/ld-aarch64/bti-far.ld new file mode 100644 index 00000000000..214b8cb919e --- /dev/null +++ b/ld/testsuite/ld-aarch64/bti-far.ld @@ -0,0 +1,15 @@ +OUTPUT_ARCH(aarch64) +ENTRY(_start) +SECTIONS +{ + . = 0x10000; + .rela.plt : { *(.rela.plt) *(.rela.iplt) } + . = 0x18000; + .plt : { *(.plt) *(.iplt) } + . = 0x20000; + .text : { *(.text) } + . = 0x30000; + .got : { *(.got) *(.got.plt) } + . = 0x12340000; + .far : { *(.far) } +} diff --git a/ld/testsuite/ld-aarch64/bti-far.s b/ld/testsuite/ld-aarch64/bti-far.s new file mode 100644 index 00000000000..20cffd225cb --- /dev/null +++ b/ld/testsuite/ld-aarch64/bti-far.s @@ -0,0 +1,29 @@ + .text + .global _start + .type _start, %function +_start: + bl foo + bl bar + bl baz +baz: + ret + + .section .far,"ax",@progbits + .global foo + .type foo, %function +foo: + bl baz +bar: + bl baz + bl foo + + .section .note.gnu.property,"a" + .align 3 + .word 4 + .word 16 + .word 5 + .string "GNU" + .word 0xc0000000 + .word 4 + .word 1 + .align 3 |