This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH, BFD, LD, AArch64, 2/4] Add --bti-nowarn to enable BTI without warning and to select BTI enabled PLTs
- From: Sudakshina Das <Sudi dot Das at arm dot com>
- To: "binutils at sourceware dot org" <binutils at sourceware dot org>
- Cc: nd <nd at arm dot com>, "nickc at redhat dot com" <nickc at redhat dot com>, Ramana Radhakrishnan <Ramana dot Radhakrishnan at arm dot com>
- Date: Wed, 6 Mar 2019 10:34:26 +0000
- Subject: [PATCH, BFD, LD, AArch64, 2/4] Add --bti-nowarn to enable BTI without warning and to select BTI enabled PLTs
- References: <08762e41-1e80-a504-d840-f8715ea59a50@arm.com> <935ee242-d6e0-63f2-f369-4a264fe15d99@arm.com>
Hi
On 06/03/2019 10:30, Sudakshina Das wrote:
> Hi
>
> On 06/03/2019 10:26, Sudakshina Das wrote:
>> Hi
>>
>> This patch series is aimed at giving support for the new Armv8.3-A
>> Pointer Authentication and Armv8.5-A Branch Target Identification
>> feature in the linker.
>>
>> In order to support these, we propose to make the following changes:
>> 1) We have defined .note.gnu.property for AArch64.
>> 2) We have defined a new Program Property type
>> GNU_PROPERTY_AARCH64_FEATURE_1_AND and used 2 bits to represent
>> BTI and PAC respectively.
>> - GNU_PROPERTY_AARCH64_FEATURE_1_BTI
>> - GNU_PROPERTY_AARCH64_FEATURE_1_PAC (We have only reserved this bit
>> for now.)
>> 3) We also need custom PLTs when these features are turned on and thus
>> we have defined the following processor-specific dynamic array tags:
>> - DT_AARCH64_BTI_PLT
>> - DT_AARCH64_PAC_PLT
>> Details of these can be found in the new AArch64 ELF documentation:
>> https://developer.arm.com/docs/ihi0056/latest/elf-for-the-arm-64-bit-architecture-aarch64-abi-2018q4
>>
>>
>> Command line options:
>> We introduce a new set of command line options for the linker in order
>> to support the correct PLTs
>> 1) --pac-plt : In the presence of this option, the linker uses a PAC
>> enabled PLT. It also uses the dynamic tag DT_AARCH64_PAC_PLT to
>> reflect the same. Other tools like Objdump can use this to determine
>> the size of the PLTs.
>> 2) --bti: In the presence of this option, the linker enables BTI with
>> the GNU_PROPERTY_AARCH64_FEATURE_1_BTI feature and also uses a BTI
>> enabled PLT. It also uses the dynamic tag DT_AARCH64_BTI_PLT to
>> reflect the choice of the PLTs. Other tools like Objdump can use this
>> to determine the size of the PLTs. Using this option can give a
>> warning if not all input objects are marked with
>> GNU_PROPERTY_AARCH64_FEATURE_1_BTI.
>> 3)--bti-nowarn - Same as above but does not emit any warnings.
>>
>> In terms of the PLTs, in the presence of both --pac-plt and
>> --bti/--bti-nowarn, the linker chooses the PLTs protected with both
>> BTI and PAC and uses both DT_AARCH64_PAC_PLT and DT_AARCH64_BTI_PLT.
>>
>> Interaction between Command line arguments and GNU NOTE section
>> 1) For PAC, in the presence of --pac-plt along with BIND_NOW, the
>> linker can choose to ignore the pac-plt directive and use smaller PLTs
>> without compromising on security,
>> 2) For BTI, the linker must also check for the
>> GNU_PROPERTY_AARCH64_FEATURE_1_BTI in its input. If all inputs have
>> GNU_PROPERTY_AARCH64_FEATURE_1_BTI, the final output will also be
>> marked as such. The PLT should also be protected with a BTI PLT in
>> this case. Thus even if there is no linker option to use BTI PLT, the
>> linker
>> should be able to use them depending on the NOTE section. The user can
>> use the linker option --bti, to make sure that their intention of
>> having all input objects (and hence the output) marked with BTI is not
>> disrupted by any stray objects as this option will warn about it.
>>
>>
>> The following patches implement these changes as follows:
>> [1/4] Add support for GNU PROPERTIES in AArch64 for BTI and PAC:
>> [2/4] Add --bti-nowarn to enable BTI without warning and to select BTI
>> enabled PLTs
>> [3/4] Add --bti to enable BTI and select BTI enabled PLTs but also
>> warn for missing NOTE sections.
>> [4/4] Add --pac-plt to enable PLTs protected with PAC.
>>
>> This is my first time making such intrusive changes to the linker.
>> Please be kind :P
>>
>> Thanks
>> Sudi
>
> This is part of the patch series to add support for BTI and
> PAC in AArch64 linker.
>
> This patch implements the following:
> 1) This extends in the gnu property support in the linker for
> AArch64 by defining backend hooks for elf_backend_setup_gnu_properties,
> elf_backend_merge_gnu_properties and elf_backend_parse_gnu_properties.
> 2) It defines AArch64 specific GNU property
> GNU_PROPERTY_AARCH64_FEATURE_1_AND and 2 bit for BTI and PAC in it.
> 3) It also adds support in readelf.c to read and print these new
> GNU properties in AArch64.
> All these are made according to the new AArch64 ELF ABI
> https://developer.arm.com/docs/ihi0056/latest/elf-for-the-arm-64-bit-architecture-aarch64-abi-2018q4
>
>
> Build and regression tests all pass on aarch64-none-linux-gnu and
> new tests are added.
>
> Is this ok for trunk?
>
> Thanks
> Sudi
>
> *** bfd/ChangeLog ***
>
> 2019-xx-xx Sudakshina Das <sudi.das@arm.com>
>
> * elf-properties.c (_bfd_elf_link_setup_gnu_properties): Exclude
> linker created inputs from merge.
> * elfnn-aarch64.c (struct elf_aarch64_obj_tdata): Add field for
> GNU_PROPERTY_AARCH64_FEATURE_1_AND properties.
> (elfNN_aarch64_link_setup_gnu_properties): New.
> (elfNN_aarch64_merge_gnu_properties): New.
> (elf_backend_setup_gnu_properties): Define for AArch64.
> (elf_backend_merge_gnu_properties): Likewise.
> * elfxx-aarch64.c (_bfd_aarch64_elf_link_setup_gnu_properties):
> Define.
> (_bfd_aarch64_elf_parse_gnu_properties): Define.
> (_bfd_aarch64_elf_merge_gnu_properties): Define.
> * elfxx-aarch64.h (_bfd_aarch64_elf_link_setup_gnu_properties):
> Declare.
> (_bfd_aarch64_elf_parse_gnu_properties): Declare.
> (_bfd_aarch64_elf_merge_gnu_properties): Declare.
> (elf_backend_parse_gnu_properties): Define for AArch64.
>
> *** binutils/ChangeLog ***
>
> 2019-xx-xx Sudakshina Das <sudi.das@arm.com>
>
> * readelf.c (decode_aarch64_feature_1_and): New.
> (print_gnu_property_note): Add case for AArch64 gnu notes.
>
> *** include/ChangeLog ***
>
> 2019-xx-xx Sudakshina Das <sudi.das@arm.com>
>
> * elf/common.h (GNU_PROPERTY_AARCH64_FEATURE_1_AND): New.
> (GNU_PROPERTY_AARCH64_FEATURE_1_BTI): New.
> (GNU_PROPERTY_AARCH64_FEATURE_1_PAC): New.
>
> *** ld/ChangeLog ***
>
> 2019-xx-xx Sudakshina Das <sudi.das@arm.com>
>
> * NEWS: Document GNU_PROPERTY_AARCH64_FEATURE_1_BTI and
> GNU_PROPERTY_AARCH64_FEATURE_1_PAC.
> * testsuite/ld-aarch64/aarch64-elf.exp: Add run commands for new
> tests.
> * testsuite/ld-aarch64/property-bti-pac1.d: New test.
> * testsuite/ld-aarch64/property-bti-pac1.s: New test.
> * testsuite/ld-aarch64/property-bti-pac2.d: New test.
> * testsuite/ld-aarch64/property-bti-pac2.s: New test.
> * testsuite/ld-aarch64/property-bti-pac3.d: New test.
This is part of the patch series to add support for BTI and
PAC in AArch64 linker.
1) This patch adds a new ld command line option: --bti-nowarn.
In the presence of this option, the linker enables BTI with the
GNU_PROPERTY_AARCH64_FEATURE_1_BTI feature. This gives no warning
in case of missing gnu notes for BTI in inputs.
2) It also defines a new set of BTI enabled PLTs. These are used either
when all the inputs are marked with GNU_PROPERTY_AARCH64_FEATURE_1_BTI
or when the new --bti-nowarn option is used. This required adding new
fields in elf_aarch64_link_hash_table so that we could make the PLT
related information more generic.
3) It also defines a dynamic tag DT_AARCH64_BTI_PLT. The linker uses
this whenever it picks BTI enabled PLTs.
All these are made according to the new AArch64 ELF ABI
https://developer.arm.com/docs/ihi0056/latest/elf-for-the-arm-64-bit-architecture-aarch64-abi-2018q4
Build and regression tests all pass on aarch64-none-linux-gnu and
new tests are added.
Is this ok for trunk?
Thanks
Sudi
*** bfd/ChangeLog ***
2019-xx-xx Sudakshina Das <sudi.das@arm.com>
Szabolcs Nagy <szabolcs.nagy@arm.com>
* bfd-in.h (aarch64_plt_type, aarch64_enable_bti_type): New.
(aarch64_bti_pac_info): New.
(bfd_elf64_aarch64_set_options): Add aarch64_bti_pac_info argument.
(bfd_elf32_aarch64_set_options): Likewise.
* bfd-in2.h: Regenerate
* elfnn-aarch64.c (PLT_BTI_ENTRY_SIZE): New.
(PLT_BTI_SMALL_ENTRY_SIZE, PLT_BTI_TLSDESC_ENTRY_SIZE): New.
(elfNN_aarch64_small_plt0_bti_entry): New.
(elfNN_aarch64_small_plt_bti_entry): New.
(elfNN_aarch64_tlsdesc_small_plt_bti_entry): New.
(elf_aarch64_obj_tdata): Add no_enable_bti_warn and plt_type fields.
(elf_aarch64_link_hash_table): Add plt0_entry, plt_entry and
tlsdesc_plt_entry_size fields.
(elfNN_aarch64_link_hash_table_create): Initialise the new fields.
(setup_plt_values): New helper function.
(bfd_elfNN_aarch64_set_options): Use new bp_info to set plt sizes and
bti enable type.
(elfNN_aarch64_allocate_dynrelocs): Use new size members instead of
fixed macros.
(elfNN_aarch64_size_dynamic_sections): Likewise and add checks.
(elfNN_aarch64_create_small_pltn_entry): Use new generic pointers
to plt stubs instead of fixed ones and update filling them according
to the need for bti.
(elfNN_aarch64_init_small_plt0_entry): Likewise.
(elfNN_aarch64_finish_dynamic_sections): Likewise.
(get_plt_type, elfNN_aarch64_get_synthetic_symtab): New.
(elfNN_aarch64_plt_sym_val): Update size accordingly.
(elfNN_aarch64_link_setup_gnu_properties): Set up plts if BTI GNU NOTE
is set.
(bfd_elfNN_get_synthetic_symtab): Define.
*** binutils/ChangeLog ***
2019-xx-xx Sudakshina Das <sudi.das@arm.com>
Szabolcs Nagy <szabolcs.nagy@arm.com>
* readelf.c (get_aarch64_dynamic_type): New.
(get_dynamic_type): Use above for EM_AARCH64.
(dynamic_section_aarch64_val): New.
(process_dynamic_section): Use above for EM_AARCH64.
*** include/ChangeLog ***
2019-xx-xx Sudakshina Das <sudi.das@arm.com>
Szabolcs Nagy <szabolcs.nagy@arm.com>
* elf/aarch64.h (DT_AARCH64_BTI_PLT): New.
*** ld/ChangeLog ***
2019-xx-xx Sudakshina Das <sudi.das@arm.com>
Szabolcs Nagy <szabolcs.nagy@arm.com>
* NEWS: Document --bti-nowarn.
* emultempl/aarch64elf.em (plt_type, bti_type, OPTION_BTI_NOWARN): New.
(PARSE_AND_LIST_SHORTOPTS, PARSE_AND_LIST_OPTIONS): Add bti-nowarn.
(PARSE_AND_LIST_ARGS_CASES): Handle OPTION_BTI_NOWARN.
* testsuite/ld-aarch64/aarch64-elf.exp: Add all the tests below.
* testsuite/ld-aarch64/bti-plt-1.d: New test.
* testsuite/ld-aarch64/bti-plt-1.s: New test.
* testsuite/ld-aarch64/bti-plt-2.d: New test.
* testsuite/ld-aarch64/bti-plt-3.d: New test.
* testsuite/ld-aarch64/bti-plt-4.d: New test.
* testsuite/ld-aarch64/bti-plt-5.d: New test.
* testsuite/ld-aarch64/bti-plt-so.s: New test.
* testsuite/ld-aarch64/bti-plt.ld: New test.
diff --git a/bfd/bfd-in.h b/bfd/bfd-in.h
index e7c2eaa93591ca2fc2cfd6f2c5a161ba0c2928a1..61affcbbb991e4c5b6c5ca28df0c60eafec3da05 100644
--- a/bfd/bfd-in.h
+++ b/bfd/bfd-in.h
@@ -995,11 +995,46 @@ extern void bfd_elf64_aarch64_init_maps
extern void bfd_elf32_aarch64_init_maps
(bfd *);
+/* Types of PLTs based on the level of security. This would be a
+ bit-mask to denote which of the combinations of security features
+ are enabled:
+ - No security feature PLTs
+ - PLTs with BTI instruction
+ - PLTs with PAC instruction
+*/
+typedef enum
+{
+ PLT_NORMAL = 0x0, /* Normal plts. */
+ PLT_BTI = 0x1, /* plts with bti. */
+ PLT_PAC = 0x2, /* plts with pointer authentication. */
+ PLT_BTI_PAC = PLT_BTI | PLT_PAC
+} aarch64_plt_type;
+
+/* To indicate if BTI is enabled with/without warning. */
+typedef enum
+{
+ BTI_NONE = 0, /* BTI is not enabled. */
+ BTI_NOWARN = 1, /* BTI is enabled with --bti-nowarn. */
+ BTI_WARN = 2, /* BTI is enabled with --bti. */
+} aarch64_enable_bti_type;
+
+/* A structure to encompass all information coming from BTI or PAC
+ related command line options. This involves the "PLT_TYPE" to determine
+ which version of PLTs to pick and "BTI_TYPE" to determine if
+ BTI should be turned on with/without any warnings. */
+typedef struct
+{
+ aarch64_plt_type plt_type;
+ aarch64_enable_bti_type bti_type;
+} aarch64_bti_pac_info;
+
extern void bfd_elf64_aarch64_set_options
- (bfd *, struct bfd_link_info *, int, int, int, int, int, int);
+ (bfd *, struct bfd_link_info *, int, int, int, int, int, int,
+ aarch64_bti_pac_info);
extern void bfd_elf32_aarch64_set_options
- (bfd *, struct bfd_link_info *, int, int, int, int, int, int);
+ (bfd *, struct bfd_link_info *, int, int, int, int, int, int,
+ aarch64_bti_pac_info);
/* ELF AArch64 mapping symbol support. */
#define BFD_AARCH64_SPECIAL_SYM_TYPE_MAP (1 << 0)
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index e25da50aafbf4fe14b7220bb2d0791ac7899b4f6..5bc09f50c4d68dba7615007dccdc9b541e49aa04 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -1002,11 +1002,46 @@ extern void bfd_elf64_aarch64_init_maps
extern void bfd_elf32_aarch64_init_maps
(bfd *);
+/* Types of PLTs based on the level of security. This would be a
+ bit-mask to denote which of the combinations of security features
+ are enabled:
+ - No security feature PLTs
+ - PLTs with BTI instruction
+ - PLTs with PAC instruction
+*/
+typedef enum
+{
+ PLT_NORMAL = 0x0, /* Normal plts. */
+ PLT_BTI = 0x1, /* plts with bti. */
+ PLT_PAC = 0x2, /* plts with pointer authentication. */
+ PLT_BTI_PAC = PLT_BTI | PLT_PAC
+} aarch64_plt_type;
+
+/* To indicate if BTI is enabled with/without warning. */
+typedef enum
+{
+ BTI_NONE = 0, /* BTI is not enabled. */
+ BTI_NOWARN = 1, /* BTI is enabled with --bti-nowarn. */
+ BTI_WARN = 2, /* BTI is enabled with --bti. */
+} aarch64_enable_bti_type;
+
+/* A structure to encompass all information coming from BTI or PAC
+ related command line options. This involves the "PLT_TYPE" to determine
+ which version of PLTs to pick and "BTI_TYPE" to determine if
+ BTI should be turned on with/without any warnings. */
+typedef struct
+{
+ aarch64_plt_type plt_type;
+ aarch64_enable_bti_type bti_type;
+} aarch64_bti_pac_info;
+
extern void bfd_elf64_aarch64_set_options
- (bfd *, struct bfd_link_info *, int, int, int, int, int, int);
+ (bfd *, struct bfd_link_info *, int, int, int, int, int, int,
+ aarch64_bti_pac_info);
extern void bfd_elf32_aarch64_set_options
- (bfd *, struct bfd_link_info *, int, int, int, int, int, int);
+ (bfd *, struct bfd_link_info *, int, int, int, int, int, int,
+ aarch64_bti_pac_info);
/* ELF AArch64 mapping symbol support. */
#define BFD_AARCH64_SPECIAL_SYM_TYPE_MAP (1 << 0)
diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
index 5b8cc4c9701feacd0deb5c998c89d17d97864646..66fe86bbac45f2de1bce43c68036ff18ffb989a8 100644
--- a/bfd/elfnn-aarch64.c
+++ b/bfd/elfnn-aarch64.c
@@ -267,6 +267,10 @@
#define PLT_ENTRY_SIZE (32)
#define PLT_SMALL_ENTRY_SIZE (16)
#define PLT_TLSDESC_ENTRY_SIZE (32)
+/* PLT sizes with BTI insn. */
+#define PLT_BTI_ENTRY_SIZE (36)
+#define PLT_BTI_SMALL_ENTRY_SIZE (20)
+#define PLT_BTI_TLSDESC_ENTRY_SIZE (36)
/* Encoding of the nop instruction. */
#define INSN_NOP 0xd503201f
@@ -297,9 +301,27 @@ static const bfd_byte elfNN_aarch64_small_plt0_entry[PLT_ENTRY_SIZE] =
0x1f, 0x20, 0x03, 0xd5, /* nop */
};
+static const bfd_byte elfNN_aarch64_small_plt0_bti_entry[PLT_BTI_ENTRY_SIZE] =
+{
+ 0x5f, 0x24, 0x03, 0xd5, /* bti c. */
+ 0xf0, 0x7b, 0xbf, 0xa9, /* stp x16, x30, [sp, #-16]! */
+ 0x10, 0x00, 0x00, 0x90, /* adrp x16, (GOT+16) */
+#if ARCH_SIZE == 64
+ 0x11, 0x0A, 0x40, 0xf9, /* ldr x17, [x16, #PLT_GOT+0x10] */
+ 0x10, 0x42, 0x00, 0x91, /* add x16, x16,#PLT_GOT+0x10 */
+#else
+ 0x11, 0x0A, 0x40, 0xb9, /* ldr w17, [x16, #PLT_GOT+0x8] */
+ 0x10, 0x22, 0x00, 0x11, /* add w16, w16,#PLT_GOT+0x8 */
+#endif
+ 0x20, 0x02, 0x1f, 0xd6, /* br x17 */
+ 0x1f, 0x20, 0x03, 0xd5, /* nop */
+ 0x1f, 0x20, 0x03, 0xd5, /* nop */
+ 0x1f, 0x20, 0x03, 0xd5, /* nop */
+};
+
/* Per function entry in a procedure linkage table looks like this
if the distance between the PLTGOT and the PLT is < 4GB use
- these PLT entries. */
+ these PLT entries. Use BTI versions of the PLTs when enabled. */
static const bfd_byte elfNN_aarch64_small_plt_entry[PLT_SMALL_ENTRY_SIZE] =
{
0x10, 0x00, 0x00, 0x90, /* adrp x16, PLTGOT + n * 8 */
@@ -314,6 +336,21 @@ static const bfd_byte elfNN_aarch64_small_plt_entry[PLT_SMALL_ENTRY_SIZE] =
};
static const bfd_byte
+elfNN_aarch64_small_plt_bti_entry[PLT_BTI_SMALL_ENTRY_SIZE] =
+{
+ 0x5f, 0x24, 0x03, 0xd5, /* bti c. */
+ 0x10, 0x00, 0x00, 0x90, /* adrp x16, PLTGOT + n * 8 */
+#if ARCH_SIZE == 64
+ 0x11, 0x02, 0x40, 0xf9, /* ldr x17, [x16, PLTGOT + n * 8] */
+ 0x10, 0x02, 0x00, 0x91, /* add x16, x16, :lo12:PLTGOT + n * 8 */
+#else
+ 0x11, 0x02, 0x40, 0xb9, /* ldr w17, [x16, PLTGOT + n * 4] */
+ 0x10, 0x02, 0x00, 0x11, /* add w16, w16, :lo12:PLTGOT + n * 4 */
+#endif
+ 0x20, 0x02, 0x1f, 0xd6, /* br x17. */
+};
+
+static const bfd_byte
elfNN_aarch64_tlsdesc_small_plt_entry[PLT_TLSDESC_ENTRY_SIZE] =
{
0xe2, 0x0f, 0xbf, 0xa9, /* stp x2, x3, [sp, #-16]! */
@@ -331,6 +368,25 @@ elfNN_aarch64_tlsdesc_small_plt_entry[PLT_TLSDESC_ENTRY_SIZE] =
0x1f, 0x20, 0x03, 0xd5, /* nop */
};
+static const bfd_byte
+elfNN_aarch64_tlsdesc_small_plt_bti_entry[PLT_BTI_TLSDESC_ENTRY_SIZE] =
+{
+ 0x5f, 0x24, 0x03, 0xd5, /* bti c. */
+ 0xe2, 0x0f, 0xbf, 0xa9, /* stp x2, x3, [sp, #-16]! */
+ 0x02, 0x00, 0x00, 0x90, /* adrp x2, 0 */
+ 0x03, 0x00, 0x00, 0x90, /* adrp x3, 0 */
+#if ARCH_SIZE == 64
+ 0x42, 0x00, 0x40, 0xf9, /* ldr x2, [x2, #0] */
+ 0x63, 0x00, 0x00, 0x91, /* add x3, x3, 0 */
+#else
+ 0x42, 0x00, 0x40, 0xb9, /* ldr w2, [x2, #0] */
+ 0x63, 0x00, 0x00, 0x11, /* add w3, w3, 0 */
+#endif
+ 0x40, 0x00, 0x1f, 0xd6, /* br x2 */
+ 0x1f, 0x20, 0x03, 0xd5, /* nop */
+ 0x1f, 0x20, 0x03, 0xd5, /* nop */
+};
+
#define elf_info_to_howto elfNN_aarch64_info_to_howto
#define elf_info_to_howto_rel elfNN_aarch64_info_to_howto
@@ -2438,6 +2494,13 @@ struct elf_aarch64_obj_tdata
/* All GNU_PROPERTY_AARCH64_FEATURE_1_AND properties. */
uint32_t gnu_and_prop;
+
+ /* Zero to warn when linking objects with incompatible
+ GNU_PROPERTY_AARCH64_FEATURE_1_BTI. */
+ int no_enable_bti_warn;
+
+ /* PLT type based on security. */
+ aarch64_plt_type plt_type;
};
#define elf_aarch64_tdata(bfd) \
@@ -2543,9 +2606,15 @@ struct elf_aarch64_link_hash_table
/* The number of bytes in the initial entry in the PLT. */
bfd_size_type plt_header_size;
- /* The number of bytes in the subsequent PLT etries. */
+ /* The bytes of the initial PLT entry. */
+ const bfd_byte *plt0_entry;
+
+ /* The number of bytes in the subsequent PLT entries. */
bfd_size_type plt_entry_size;
+ /* The bytes of the subsequent PLT entry. */
+ const bfd_byte *plt_entry;
+
/* Small local sym cache. */
struct sym_cache sym_cache;
@@ -2588,6 +2657,9 @@ struct elf_aarch64_link_hash_table
yet. */
bfd_vma tlsdesc_plt;
+ /* The number of bytes in the PLT enty for the TLS descriptor. */
+ bfd_size_type tlsdesc_plt_entry_size;
+
/* The GOT offset for the lazy trampoline. Communicated to the
loader via DT_TLSDESC_GOT. The magic value (bfd_vma) -1
indicates an offset is not allocated. */
@@ -2831,7 +2903,10 @@ elfNN_aarch64_link_hash_table_create (bfd *abfd)
}
ret->plt_header_size = PLT_ENTRY_SIZE;
+ ret->plt0_entry = elfNN_aarch64_small_plt0_entry;
ret->plt_entry_size = PLT_SMALL_ENTRY_SIZE;
+ ret->plt_entry = elfNN_aarch64_small_plt_entry;
+ ret->tlsdesc_plt_entry_size = PLT_TLSDESC_ENTRY_SIZE;
ret->obfd = abfd;
ret->dt_tlsdesc_got = (bfd_vma) - 1;
@@ -4599,6 +4674,28 @@ bfd_elfNN_aarch64_init_maps (bfd *abfd)
}
}
+static void
+setup_plt_values (struct bfd_link_info *link_info,
+ aarch64_plt_type plt_type)
+{
+ struct elf_aarch64_link_hash_table *globals;
+ globals = elf_aarch64_hash_table (link_info);
+
+ if (plt_type == PLT_BTI)
+ {
+ globals->plt_header_size = PLT_BTI_ENTRY_SIZE;
+ globals->plt0_entry = elfNN_aarch64_small_plt0_bti_entry;
+ globals->tlsdesc_plt_entry_size = PLT_BTI_TLSDESC_ENTRY_SIZE;
+
+ /* Only in ET_EXEC we need PLTn with BTI. */
+ if (bfd_link_pde (link_info))
+ {
+ globals->plt_entry_size = PLT_BTI_SMALL_ENTRY_SIZE;
+ globals->plt_entry = elfNN_aarch64_small_plt_bti_entry;
+ }
+ }
+}
+
/* Set option values needed during linking. */
void
bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
@@ -4607,7 +4704,8 @@ bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
int no_wchar_warn, int pic_veneer,
int fix_erratum_835769,
int fix_erratum_843419,
- int no_apply_dynamic_relocs)
+ int no_apply_dynamic_relocs,
+ aarch64_bti_pac_info bp_info)
{
struct elf_aarch64_link_hash_table *globals;
@@ -4621,6 +4719,26 @@ bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
BFD_ASSERT (is_aarch64_elf (output_bfd));
elf_aarch64_tdata (output_bfd)->no_enum_size_warning = no_enum_warn;
elf_aarch64_tdata (output_bfd)->no_wchar_size_warning = no_wchar_warn;
+
+ switch (bp_info.bti_type)
+ {
+ case BTI_NOWARN:
+ elf_aarch64_tdata (output_bfd)->no_enable_bti_warn = 1;
+ elf_aarch64_tdata (output_bfd)->gnu_and_prop
+ |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+ break;
+
+ case BTI_WARN:
+ elf_aarch64_tdata (output_bfd)->no_enable_bti_warn = 0;
+ elf_aarch64_tdata (output_bfd)->gnu_and_prop
+ |= GNU_PROPERTY_AARCH64_FEATURE_1_BTI;
+ break;
+
+ default:
+ break;
+ }
+ elf_aarch64_tdata (output_bfd)->plt_type = bp_info.plt_type;
+ setup_plt_values (link_info, bp_info.plt_type);
}
static bfd_vma
@@ -8349,7 +8467,7 @@ elfNN_aarch64_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
/* Make room for this entry. For now we only create the
small model PLT entries. We later need to find a way
of relaxing into these from the large model PLT entries. */
- s->size += PLT_SMALL_ENTRY_SIZE;
+ s->size += htab->plt_entry_size;
/* We also need to make an entry in the .got.plt section, which
will be placed in the .got section by the linker script. */
@@ -8849,10 +8967,10 @@ elfNN_aarch64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
if (htab->tlsdesc_plt)
{
if (htab->root.splt->size == 0)
- htab->root.splt->size += PLT_ENTRY_SIZE;
+ htab->root.splt->size += htab->plt_header_size;
htab->tlsdesc_plt = htab->root.splt->size;
- htab->root.splt->size += PLT_TLSDESC_ENTRY_SIZE;
+ htab->root.splt->size += htab->tlsdesc_plt_entry_size;
/* If we're not using lazy TLS relocations, don't generate the
GOT entry required. */
@@ -8964,6 +9082,10 @@ elfNN_aarch64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
&& (!add_dynamic_entry (DT_TLSDESC_PLT, 0)
|| !add_dynamic_entry (DT_TLSDESC_GOT, 0)))
return FALSE;
+
+ if ((elf_aarch64_tdata (output_bfd)->plt_type == PLT_BTI)
+ && !add_dynamic_entry (DT_AARCH64_BTI_PLT, 0))
+ return FALSE;
}
if (relocs)
@@ -9060,7 +9182,13 @@ elfNN_aarch64_create_small_pltn_entry (struct elf_link_hash_entry *h,
gotplt->output_offset + got_offset;
/* Copy in the boiler-plate for the PLTn entry. */
- memcpy (plt_entry, elfNN_aarch64_small_plt_entry, PLT_SMALL_ENTRY_SIZE);
+ memcpy (plt_entry, htab->plt_entry, htab->plt_entry_size);
+
+ /* First instruction in BTI enabled PLT stub is a BTI
+ instruction so skip it. */
+ if (elf_aarch64_tdata (output_bfd)->plt_type & PLT_BTI
+ && elf_elfheader (output_bfd)->e_type == ET_EXEC)
+ plt_entry = plt_entry + 4;
/* Fill in the top 21 bits for this: ADRP x16, PLT_GOT + n * 8.
ADRP: ((PG(S+A)-PG(P)) >> 12) & 0x1fffff */
@@ -9365,10 +9493,10 @@ elfNN_aarch64_init_small_plt0_entry (bfd *output_bfd ATTRIBUTE_UNUSED,
bfd_vma plt_base;
- memcpy (htab->root.splt->contents, elfNN_aarch64_small_plt0_entry,
- PLT_ENTRY_SIZE);
+ memcpy (htab->root.splt->contents, htab->plt0_entry,
+ htab->plt_header_size);
elf_section_data (htab->root.splt->output_section)->this_hdr.sh_entsize =
- PLT_ENTRY_SIZE;
+ htab->plt_header_size;
plt_got_2nd_ent = (htab->root.sgotplt->output_section->vma
+ htab->root.sgotplt->output_offset
@@ -9377,18 +9505,24 @@ elfNN_aarch64_init_small_plt0_entry (bfd *output_bfd ATTRIBUTE_UNUSED,
plt_base = htab->root.splt->output_section->vma +
htab->root.splt->output_offset;
+ /* First instruction in BTI enabled PLT stub is a BTI
+ instruction so skip it. */
+ bfd_byte *plt0_entry = htab->root.splt->contents;
+ if (elf_aarch64_tdata (output_bfd)->plt_type & PLT_BTI)
+ plt0_entry = plt0_entry + 4;
+
/* Fill in the top 21 bits for this: ADRP x16, PLT_GOT + n * 8.
ADRP: ((PG(S+A)-PG(P)) >> 12) & 0x1fffff */
elf_aarch64_update_plt_entry (output_bfd, BFD_RELOC_AARCH64_ADR_HI21_PCREL,
- htab->root.splt->contents + 4,
+ plt0_entry + 4,
PG (plt_got_2nd_ent) - PG (plt_base + 4));
elf_aarch64_update_plt_entry (output_bfd, BFD_RELOC_AARCH64_LDSTNN_LO12,
- htab->root.splt->contents + 8,
+ plt0_entry + 8,
PG_OFFSET (plt_got_2nd_ent));
elf_aarch64_update_plt_entry (output_bfd, BFD_RELOC_AARCH64_ADD_LO12,
- htab->root.splt->contents + 12,
+ plt0_entry + 12,
PG_OFFSET (plt_got_2nd_ent));
}
@@ -9472,9 +9606,18 @@ elfNN_aarch64_finish_dynamic_sections (bfd *output_bfd,
bfd_put_NN (output_bfd, (bfd_vma) 0,
htab->root.sgot->contents + htab->dt_tlsdesc_got);
+ const bfd_byte *entry = elfNN_aarch64_tlsdesc_small_plt_entry;
+ htab->tlsdesc_plt_entry_size = PLT_TLSDESC_ENTRY_SIZE;
+
+ aarch64_plt_type type = elf_aarch64_tdata (output_bfd)->plt_type;
+ if (type == PLT_BTI)
+ {
+ entry = elfNN_aarch64_tlsdesc_small_plt_bti_entry;
+ htab->tlsdesc_plt_entry_size = PLT_BTI_TLSDESC_ENTRY_SIZE;
+ }
+
memcpy (htab->root.splt->contents + htab->tlsdesc_plt,
- elfNN_aarch64_tlsdesc_small_plt_entry,
- sizeof (elfNN_aarch64_tlsdesc_small_plt_entry));
+ entry, htab->tlsdesc_plt_entry_size);
{
bfd_vma adrp1_addr =
@@ -9496,6 +9639,15 @@ elfNN_aarch64_finish_dynamic_sections (bfd *output_bfd,
bfd_byte *plt_entry =
htab->root.splt->contents + htab->tlsdesc_plt;
+ /* First instruction in BTI enabled PLT stub is a BTI
+ instruction so skip it. */
+ if (type & PLT_BTI)
+ {
+ plt_entry = plt_entry + 4;
+ adrp1_addr = adrp1_addr + 4;
+ adrp2_addr = adrp2_addr + 4;
+ }
+
/* adrp x2, DT_TLSDESC_GOT */
elf_aarch64_update_plt_entry (output_bfd,
BFD_RELOC_AARCH64_ADR_HI21_PCREL,
@@ -9574,6 +9726,53 @@ elfNN_aarch64_finish_dynamic_sections (bfd *output_bfd,
return TRUE;
}
+/* Check if BTI enabled PLTs are needed. Returns the type needed. */
+static aarch64_plt_type
+get_plt_type (bfd *abfd)
+{
+ aarch64_plt_type ret = PLT_NORMAL;
+ bfd_byte *contents, *extdyn, *extdynend;
+ asection *sec = bfd_get_section_by_name (abfd, ".dynamic");
+ if (!sec || !bfd_malloc_and_get_section (abfd, sec, &contents))
+ return ret;
+ extdyn = contents;
+ extdynend = contents + sec->size;
+ for (; extdyn < extdynend; extdyn += sizeof (ElfNN_External_Dyn))
+ {
+ Elf_Internal_Dyn dyn;
+ bfd_elfNN_swap_dyn_in (abfd, extdyn, &dyn);
+
+ /* Let's check the processor specific dynamic array tags. */
+ bfd_vma tag = dyn.d_tag;
+ if (tag < DT_LOPROC || tag > DT_HIPROC)
+ continue;
+
+ switch (tag)
+ {
+ case DT_AARCH64_BTI_PLT:
+ ret = PLT_BTI;
+ break;
+
+ default: break;
+ }
+ }
+ free (contents);
+ return ret;
+}
+
+static long
+elfNN_aarch64_get_synthetic_symtab (bfd *abfd,
+ long symcount,
+ asymbol **syms,
+ long dynsymcount,
+ asymbol **dynsyms,
+ asymbol **ret)
+{
+ elf_aarch64_tdata (abfd)->plt_type = get_plt_type (abfd);
+ return _bfd_elf_get_synthetic_symtab (abfd, symcount, syms,
+ dynsymcount, dynsyms, ret);
+}
+
/* Return address for Ith PLT stub in section PLT, for relocation REL
or (bfd_vma) -1 if it should not be included. */
@@ -9581,7 +9780,16 @@ static bfd_vma
elfNN_aarch64_plt_sym_val (bfd_vma i, const asection *plt,
const arelent *rel ATTRIBUTE_UNUSED)
{
- return plt->vma + PLT_ENTRY_SIZE + i * PLT_SMALL_ENTRY_SIZE;
+ size_t plt0_size = PLT_ENTRY_SIZE;
+ size_t pltn_size = PLT_SMALL_ENTRY_SIZE;
+
+ if (elf_aarch64_tdata (plt->owner)->plt_type == PLT_BTI)
+ {
+ plt0_size = PLT_BTI_ENTRY_SIZE;
+ if (elf_elfheader (plt->owner)->e_type == ET_EXEC)
+ pltn_size = PLT_BTI_SMALL_ENTRY_SIZE;
+ }
+ return plt->vma + plt0_size + i * pltn_size;
}
/* Returns TRUE if NAME is an AArch64 mapping symbol.
@@ -9627,6 +9835,9 @@ elfNN_aarch64_link_setup_gnu_properties (struct bfd_link_info *info)
uint32_t prop = elf_aarch64_tdata (info->output_bfd)->gnu_and_prop;
bfd *pbfd = _bfd_aarch64_elf_link_setup_gnu_properties (info, &prop);
elf_aarch64_tdata (info->output_bfd)->gnu_and_prop = prop;
+ elf_aarch64_tdata (info->output_bfd)->plt_type
+ |= (prop & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) ? PLT_BTI : 0;
+ setup_plt_values (info, elf_aarch64_tdata (info->output_bfd)->plt_type);
return pbfd;
}
@@ -9718,6 +9929,9 @@ const struct elf_size_info elfNN_aarch64_size_info =
#define bfd_elfNN_find_nearest_line \
elfNN_aarch64_find_nearest_line
+#define bfd_elfNN_get_synthetic_symtab \
+ elfNN_aarch64_get_synthetic_symtab
+
#define bfd_elfNN_mkobject \
elfNN_aarch64_mkobject
diff --git a/binutils/readelf.c b/binutils/readelf.c
index 7446ffeee21961bf56b12ff16afbf52e5cd35387..f4775b439fc6a14a14699c88af3fe5f07648352e 100644
--- a/binutils/readelf.c
+++ b/binutils/readelf.c
@@ -1798,6 +1798,17 @@ dump_relocations (Filedata * filedata,
}
static const char *
+get_aarch64_dynamic_type (unsigned long type)
+{
+ switch (type)
+ {
+ case DT_AARCH64_BTI_PLT: return "AARCH64_BTI_PLT";
+ default:
+ return NULL;
+ }
+}
+
+static const char *
get_mips_dynamic_type (unsigned long type)
{
switch (type)
@@ -2170,6 +2181,9 @@ get_dynamic_type (Filedata * filedata, unsigned long type)
switch (filedata->file_header.e_machine)
{
+ case EM_AARCH64:
+ result = get_aarch64_dynamic_type (type);
+ break;
case EM_MIPS:
case EM_MIPS_RS3_LE:
result = get_mips_dynamic_type (type);
@@ -9345,6 +9359,20 @@ process_unwind (Filedata * filedata)
}
static void
+dynamic_section_aarch64_val (Elf_Internal_Dyn * entry)
+{
+ switch (entry->d_tag)
+ {
+ case DT_AARCH64_BTI_PLT:
+ break;
+ default:
+ print_vma (entry->d_un.d_ptr, PREFIX_HEX);
+ break;
+ }
+ putchar ('\n');
+}
+
+static void
dynamic_section_mips_val (Elf_Internal_Dyn * entry)
{
switch (entry->d_tag)
@@ -10358,6 +10386,9 @@ process_dynamic_section (Filedata * filedata)
{
switch (filedata->file_header.e_machine)
{
+ case EM_AARCH64:
+ dynamic_section_aarch64_val (entry);
+ break;
case EM_MIPS:
case EM_MIPS_RS3_LE:
dynamic_section_mips_val (entry);
diff --git a/include/elf/aarch64.h b/include/elf/aarch64.h
index 3133ea6f6653eefc846da224e14fc6974cb2bf54..b86a1006297891d38f55f0c29d0ac51056b0c6e3 100644
--- a/include/elf/aarch64.h
+++ b/include/elf/aarch64.h
@@ -35,6 +35,8 @@
entry point. */
#define SHF_COMDEF 0x80000000 /* Section may be multiply defined
in the input to a link step. */
+/* Processor specific dynamic array tags. */
+#define DT_AARCH64_BTI_PLT (DT_LOPROC + 1)
/* Relocation types. */
diff --git a/ld/NEWS b/ld/NEWS
index dcf11854f528028820cbe1983bbcdd5753d97eaf..31731219ad546483ae92a7e32862986627bfc4d4 100644
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -10,6 +10,9 @@ Changes in 2.33:
* Add support for GNU_PROPERTY_AARCH64_FEATURE_1_PAC in ELF GNU program
properties in the AArch64 ELF linker.
+* Add --bti-nowarn for AArch64 to enable GNU_PROPERTY_AARCH64_FEATURE_1_BTI
+ on output without any warnings and use PLTs protected with BTI.
+
Changes in 2.32:
* Report property change in linker map file when merging GNU properties.
diff --git a/ld/emultempl/aarch64elf.em b/ld/emultempl/aarch64elf.em
index 45e40b510cf0da21e777f5734dcc9d1be54918a5..146bfad31fd299ef5c170b269e86afd0f3e1f5b9 100644
--- a/ld/emultempl/aarch64elf.em
+++ b/ld/emultempl/aarch64elf.em
@@ -33,6 +33,8 @@ static int pic_veneer = 0;
static int fix_erratum_835769 = 0;
static int fix_erratum_843419 = 0;
static int no_apply_dynamic_relocs = 0;
+static aarch64_plt_type plt_type = PLT_NORMAL;
+static aarch64_enable_bti_type bti_type = BTI_NONE;
static void
gld${EMULATION_NAME}_before_parse (void)
@@ -308,12 +310,17 @@ aarch64_elf_create_output_section_statements (void)
return;
}
+ aarch64_bti_pac_info bp_info;
+ bp_info.plt_type = plt_type;
+ bp_info.bti_type = bti_type;
+
bfd_elf${ELFSIZE}_aarch64_set_options (link_info.output_bfd, &link_info,
no_enum_size_warning,
no_wchar_size_warning,
pic_veneer,
fix_erratum_835769, fix_erratum_843419,
- no_apply_dynamic_relocs);
+ no_apply_dynamic_relocs,
+ bp_info);
stub_file = lang_add_input_file ("linker stubs",
lang_input_file_is_fake_enum,
@@ -365,6 +372,7 @@ PARSE_AND_LIST_PROLOGUE='
#define OPTION_FIX_ERRATUM_835769 313
#define OPTION_FIX_ERRATUM_843419 314
#define OPTION_NO_APPLY_DYNAMIC_RELOCS 315
+#define OPTION_BTI_NOWARN 316
'
PARSE_AND_LIST_SHORTOPTS=p
@@ -378,6 +386,7 @@ PARSE_AND_LIST_LONGOPTS='
{ "fix-cortex-a53-835769", no_argument, NULL, OPTION_FIX_ERRATUM_835769},
{ "fix-cortex-a53-843419", no_argument, NULL, OPTION_FIX_ERRATUM_843419},
{ "no-apply-dynamic-relocs", no_argument, NULL, OPTION_NO_APPLY_DYNAMIC_RELOCS},
+ { "bti-nowarn", no_argument, NULL, OPTION_BTI_NOWARN},
'
PARSE_AND_LIST_OPTIONS='
@@ -398,6 +407,7 @@ PARSE_AND_LIST_OPTIONS='
fprintf (file, _(" --fix-cortex-a53-835769 Fix erratum 835769\n"));
fprintf (file, _(" --fix-cortex-a53-843419 Fix erratum 843419\n"));
fprintf (file, _(" --no-apply-dynamic-relocs Do not apply link-time values for dynamic relocations\n"));
+ fprintf (file, _(" --bti-nowarn Turn on Branch Target Identification mechanism and generate PLTs with BTI. Generate no warnings for missing BTI on inputs\n"));
'
PARSE_AND_LIST_ARGS_CASES='
@@ -429,6 +439,11 @@ PARSE_AND_LIST_ARGS_CASES='
no_apply_dynamic_relocs = 1;
break;
+ case OPTION_BTI_NOWARN:
+ plt_type |= PLT_BTI;
+ bti_type = BTI_NOWARN;
+ break;
+
case OPTION_STUBGROUP_SIZE:
{
const char *end;
diff --git a/ld/testsuite/ld-aarch64/aarch64-elf.exp b/ld/testsuite/ld-aarch64/aarch64-elf.exp
index c6fefbbd5c3f7e5516086c7eca0e056ae243d36d..ed0bca5b081c5c016b3f8e96619b76820aa1525d 100644
--- a/ld/testsuite/ld-aarch64/aarch64-elf.exp
+++ b/ld/testsuite/ld-aarch64/aarch64-elf.exp
@@ -374,6 +374,8 @@ run_dump_test_lp64 "pie-bind-locally"
run_dump_test "property-bti-pac1"
run_dump_test "property-bti-pac2"
run_dump_test "property-bti-pac3"
+run_dump_test "bti-plt-1"
+run_dump_test "bti-plt-2"
set aarch64elflinktests {
{"ld-aarch64/so with global symbol" "-shared" "" "" {copy-reloc-so.s}
@@ -389,6 +391,12 @@ set aarch64elflinktests {
{"ld-aarch64/func sym hash opt for exe"
"-e0 --hash-style=gnu tmpdir/func-in-so.so" "" ""
{func-sym-hash-opt.s} {{readelf --dyn-sym func-sym-hash-opt.d}} "hash-opt"}
+ {"Build bti-plt-so for PLT tests" "-shared" "" "" {bti-plt-so.s}
+ {} "libbti-plt-so.so"}
}
run_ld_link_tests $aarch64elflinktests
+
+run_dump_test "bti-plt-3"
+run_dump_test "bti-plt-4"
+run_dump_test "bti-plt-5"
diff --git a/ld/testsuite/ld-aarch64/bti-plt-1.d b/ld/testsuite/ld-aarch64/bti-plt-1.d
new file mode 100644
index 0000000000000000000000000000000000000000..16c71024989c4ecd6bc2fc2356956ac7a4c0debf
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/bti-plt-1.d
@@ -0,0 +1,32 @@
+#name: Check --bti-nowarn emits BTI PLT (shared)
+#source: bti-plt-1.s
+#as: -mabi=lp64
+#ld: -shared --bti-nowarn -T bti-plt.ld
+#objdump: -dr -j .plt
+
+[^:]*: *file format elf64-.*aarch64
+
+Disassembly of section \.plt:
+
+[0-9]+ <.*>:
+.*: d503245f bti c
+.*: a9bf7bf0 stp x16, x30, \[sp, #-16\]!
+.*: 90000090 adrp x16, 28000 <_GLOBAL_OFFSET_TABLE_>
+.*: f9400e11 ldr x17, \[x16, #24\]
+.*: 91006210 add x16, x16, #0x18
+.*: d61f0220 br x17
+.*: d503201f nop
+.*: d503201f nop
+.*: d503201f nop
+
+[0-9]+ <.*>:
+.*: 90000090 adrp x16, 28000 <_GLOBAL_OFFSET_TABLE_>
+.*: f9401211 ldr x17, \[x16, #32\]
+.*: 91008210 add x16, x16, #0x20
+.*: d61f0220 br x17
+
+[0-9]+ <.*>:
+.*: 90000090 adrp x16, 28000 <_GLOBAL_OFFSET_TABLE_>
+.*: f9401611 ldr x17, \[x16, #40\]
+.*: 9100a210 add x16, x16, #0x28
+.*: d61f0220 br x17
diff --git a/ld/testsuite/ld-aarch64/bti-plt-1.s b/ld/testsuite/ld-aarch64/bti-plt-1.s
new file mode 100644
index 0000000000000000000000000000000000000000..9cd7cc467b32d13b8531a0c183b6e50d78971ef6
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/bti-plt-1.s
@@ -0,0 +1,6 @@
+ .text
+ .globl _start
+ .type _start,@function
+_start:
+ bl foo
+ bl bar
diff --git a/ld/testsuite/ld-aarch64/bti-plt-2.d b/ld/testsuite/ld-aarch64/bti-plt-2.d
new file mode 100644
index 0000000000000000000000000000000000000000..2c7cc0df8d4a165193bb7299b93ce814ed81f4dc
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/bti-plt-2.d
@@ -0,0 +1,11 @@
+#name: Check --bti-nowarn emits BTI feature (shared)
+#source: bti-plt-1.s
+#source: bti-plt-2.s
+#as: -mabi=lp64
+#ld: -shared --bti-nowarn -T bti-plt.ld
+#readelf: -n
+
+Displaying notes found in: .note.gnu.property
+ Owner Data size Description
+ GNU 0x00000010 NT_GNU_PROPERTY_TYPE_0
+ Properties: AArch64 feature: BTI
diff --git a/ld/testsuite/ld-aarch64/bti-plt-2.s b/ld/testsuite/ld-aarch64/bti-plt-2.s
new file mode 100644
index 0000000000000000000000000000000000000000..4c29bb879c80eea377ae666de4f5f3fac01b472f
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/bti-plt-2.s
@@ -0,0 +1,6 @@
+ .text
+ .globl _start
+ .type _start,@function
+func2:
+ bl foo2
+ bl bar2
diff --git a/ld/testsuite/ld-aarch64/bti-plt-3.d b/ld/testsuite/ld-aarch64/bti-plt-3.d
new file mode 100644
index 0000000000000000000000000000000000000000..47116cc21db775ae1470198d6ef7c6f99f09eb43
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/bti-plt-3.d
@@ -0,0 +1,34 @@
+#name: Check --bti-nowarn emits BTI PLT (exec)
+#source: bti-plt-1.s
+#as: -mabi=lp64
+#ld: --bti-nowarn -e _start -T bti-plt.ld -L./tmpdir -lbti-plt-so
+#objdump: -dr -j .plt
+
+[^:]*: *file format elf64-.*aarch64
+
+Disassembly of section \.plt:
+
+[0-9]+ <.*>:
+.*: d503245f bti c
+.*: a9bf7bf0 stp x16, x30, \[sp, #-16\]!
+.*: 90000090 adrp x16, 28000 <_GLOBAL_OFFSET_TABLE_>
+.*: f9400e11 ldr x17, \[x16, #24\]
+.*: 91006210 add x16, x16, #0x18
+.*: d61f0220 br x17
+.*: d503201f nop
+.*: d503201f nop
+.*: d503201f nop
+
+[0-9]+ <.*>:
+.*: d503245f bti c
+.*: 90000090 adrp x16, 28000 <_GLOBAL_OFFSET_TABLE_>
+.*: f9401211 ldr x17, \[x16, #32\]
+.*: 91008210 add x16, x16, #0x20
+.*: d61f0220 br x17
+
+[0-9]+ <.*>:
+.*: d503245f bti c
+.*: 90000090 adrp x16, 28000 <_GLOBAL_OFFSET_TABLE_>
+.*: f9401611 ldr x17, \[x16, #40\]
+.*: 9100a210 add x16, x16, #0x28
+.*: d61f0220 br x17
diff --git a/ld/testsuite/ld-aarch64/bti-plt-4.d b/ld/testsuite/ld-aarch64/bti-plt-4.d
new file mode 100644
index 0000000000000000000000000000000000000000..92949c0c7325121710892193f19b04c8f1e9fe29
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/bti-plt-4.d
@@ -0,0 +1,10 @@
+#name: Check --bti-nowarn emits BTI feature (exec)
+#source: bti-plt-1.s
+#as: -mabi=lp64
+#ld: --bti-nowarn -e _start -T bti-plt.ld -L./tmpdir -lbti-plt-so
+#readelf: -n
+
+Displaying notes found in: .note.gnu.property
+ Owner Data size Description
+ GNU 0x00000010 NT_GNU_PROPERTY_TYPE_0
+ Properties: AArch64 feature: BTI
diff --git a/ld/testsuite/ld-aarch64/bti-plt-5.d b/ld/testsuite/ld-aarch64/bti-plt-5.d
new file mode 100644
index 0000000000000000000000000000000000000000..01231b69d2c4066d0fe3c49e359cef9b670ba34d
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/bti-plt-5.d
@@ -0,0 +1,28 @@
+#name: BTI PLT with only GNU PROP
+#source: property-bti-pac1.s
+#as: -mabi=lp64 -defsym __property_bti__=1
+#ld: -e _start -L./tmpdir -lbti-plt-so
+#objdump: -dr -j .plt
+#target: *linux*
+
+[^:]*: *file format elf64-.*aarch64
+
+Disassembly of section \.plt:
+
+[0-9a-f]+ <.*>:
+.*: d503245f bti c
+.*: a9bf7bf0 stp x16, x30, \[sp, #-16\]!
+.*: 90000090 adrp x16, 410000 <_start\+0xfd28>
+.*: f9421611 ldr x17, \[x16, #1064\]
+.*: 9110a210 add x16, x16, #0x428
+.*: d61f0220 br x17
+.*: d503201f nop
+.*: d503201f nop
+.*: d503201f nop
+
+[0-9a-f]+ <.*>:
+.*: d503245f bti c
+.*: 90000090 adrp x16, 410000 <_start\+0xfd28>
+.*: f9421a11 ldr x17, \[x16, #1072\]
+.*: 9110c210 add x16, x16, #0x430
+.*: d61f0220 br x17
diff --git a/ld/testsuite/ld-aarch64/bti-plt-so.s b/ld/testsuite/ld-aarch64/bti-plt-so.s
new file mode 100644
index 0000000000000000000000000000000000000000..d335af02536eefa77bea9ebbf5f1407817b42fa0
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/bti-plt-so.s
@@ -0,0 +1,26 @@
+ .global foo
+ .type foo, %function
+foo:
+ sub sp, sp, #16
+ mov w0, 9
+ str w0, [sp, 12]
+ ldr w0, [sp, 12]
+ add w0, w0, 4
+ str w0, [sp, 12]
+ nop
+ add sp, sp, 16
+ ret
+ .size foo, .-foo
+ .global bar
+ .type bar, %function
+bar:
+ sub sp, sp, #16
+ mov w0, 9
+ str w0, [sp, 12]
+ ldr w0, [sp, 12]
+ add w0, w0, 4
+ str w0, [sp, 12]
+ nop
+ add sp, sp, 16
+ ret
+ .size bar, .-bar
diff --git a/ld/testsuite/ld-aarch64/bti-plt.ld b/ld/testsuite/ld-aarch64/bti-plt.ld
new file mode 100644
index 0000000000000000000000000000000000000000..8682623d69b97cd735a7459838c0ce547723c337
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/bti-plt.ld
@@ -0,0 +1,14 @@
+OUTPUT_ARCH(aarch64)
+ENTRY(_start)
+SECTIONS
+{
+ . = 0x10000;
+ .rela.plt : { *(.rela.plt) *(.rela.iplt) }
+ . = 0x18000;
+ .plt : { *(.plt) *(.iplt) }
+ . = 0x20000;
+ .text : { *(.text) }
+ . = 0x28000;
+ .got : { *(.got) *(.got.plt) }
+ .ARM.attributes 0 : { *(.ARM.atttributes) }
+}