summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bfd/elfnn-aarch64.c175
-rw-r--r--ld/testsuite/ld-aarch64/aarch64-elf.exp3
-rw-r--r--ld/testsuite/ld-aarch64/bti-far-1.d83
-rw-r--r--ld/testsuite/ld-aarch64/bti-far-2.d62
-rw-r--r--ld/testsuite/ld-aarch64/bti-far.ld15
-rw-r--r--ld/testsuite/ld-aarch64/bti-far.s29
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