[BFD,LD,AArch64,2/4] Add --bti-nowarn to enable BTI without warning and to select BTI enabled PLTs

Message ID 2277b4b2-6509-1558-bd40-17ffcb27dbb1@arm.com
State New
Headers show
Series
  • Add support for AArch64 BTI and PAC in the linker
Related show

Commit Message

Sudakshina Das March 6, 2019, 10:34 a.m.
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.

Patch

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) }
+}