]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - bfd/elfnn-aarch64.c
[BFD, LD, AArch64, 2/3] Add --force-bti to enable BTI and to select BTI enabled PLTs
[thirdparty/binutils-gdb.git] / bfd / elfnn-aarch64.c
index d6a215e8e3643e0a63c6f9cc525d8d00e968fd4e..69b8f83f3c8102ad84bd963733ed066ca11469e1 100644 (file)
@@ -1,5 +1,5 @@
 /* AArch64-specific support for NN-bit ELF.
-   Copyright (C) 2009-2018 Free Software Foundation, Inc.
+   Copyright (C) 2009-2019 Free Software Foundation, Inc.
    Contributed by ARM Ltd.
 
    This file is part of BFD, the Binary File Descriptor library.
 #include "bfd.h"
 #include "libiberty.h"
 #include "libbfd.h"
-#include "bfd_stdint.h"
 #include "elf-bfd.h"
 #include "bfdlink.h"
 #include "objalloc.h"
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12       \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12       \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12_NC    \
+   || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_LDST16_TPREL_LO12    \
+   || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_LDST16_TPREL_LO12_NC \
+   || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_LDST32_TPREL_LO12    \
+   || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_LDST32_TPREL_LO12_NC \
+   || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_LDST64_TPREL_LO12    \
+   || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_LDST64_TPREL_LO12_NC \
+   || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_LDST8_TPREL_LO12     \
+   || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_LDST8_TPREL_LO12_NC  \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0                \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC     \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1                \
 #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
@@ -290,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  */
@@ -306,6 +335,21 @@ static const bfd_byte elfNN_aarch64_small_plt_entry[PLT_SMALL_ENTRY_SIZE] =
   0x20, 0x02, 0x1f, 0xd6,      /* br x17.  */
 };
 
+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] =
 {
@@ -324,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
 
@@ -2428,6 +2491,16 @@ struct elf_aarch64_obj_tdata
 
   /* Zero to warn when linking objects with incompatible wchar_t sizes.  */
   int no_wchar_size_warning;
+
+  /* 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_bti_warn;
+
+  /* PLT type based on security.  */
+  aarch64_plt_type plt_type;
 };
 
 #define elf_aarch64_tdata(bfd)                         \
@@ -2533,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;
 
@@ -2578,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.  */
@@ -2821,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;
 
@@ -3737,7 +3822,8 @@ _bfd_aarch64_erratum_835769_stub_name (unsigned num_fixes)
 {
   char *stub_name = (char *) bfd_malloc
     (strlen ("__erratum_835769_veneer_") + 16);
-  sprintf (stub_name,"__erratum_835769_veneer_%d", num_fixes);
+  if (stub_name != NULL)
+    sprintf (stub_name,"__erratum_835769_veneer_%d", num_fixes);
   return stub_name;
 }
 
@@ -3836,7 +3922,7 @@ _bfd_aarch64_erratum_835769_scan (bfd *input_bfd,
 static bfd_boolean
 _bfd_aarch64_adrp_p (uint32_t insn)
 {
-  return ((insn & 0x9f000000) == 0x90000000);
+  return ((insn & AARCH64_ADRP_OP_MASK) == AARCH64_ADRP_OP);
 }
 
 
@@ -3984,6 +4070,8 @@ _bfd_aarch64_erratum_843419_fixup (uint32_t insn,
   struct elf_aarch64_stub_hash_entry *stub_entry;
 
   stub_name = _bfd_aarch64_erratum_843419_stub_name (section, ldst_offset);
+  if (stub_name == NULL)
+    return FALSE;
   stub_entry = aarch64_stub_hash_lookup (&htab->stub_hash_table, stub_name,
                                         FALSE, FALSE);
   if (stub_entry)
@@ -4001,8 +4089,7 @@ _bfd_aarch64_erratum_843419_fixup (uint32_t insn,
      If we placed workaround veneers in any other stub section then we
      could not assume that all relocations have been processed on the
      corresponding input section at the point we output the stub
-     section.
-   */
+     section.  */
 
   stub_entry = _bfd_aarch64_add_stub_entry_after (stub_name, section, htab);
   if (stub_entry == NULL)
@@ -4404,6 +4491,9 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
                    {
                      /* The proper stub has already been created.  */
                      free (stub_name);
+                     /* Always update this stub's target since it may have
+                        changed after layout.  */
+                     stub_entry->target_value = sym_value + irela->r_addend;
                      continue;
                    }
 
@@ -4584,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,
@@ -4592,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;
 
@@ -4606,6 +4719,20 @@ 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_WARN:
+      elf_aarch64_tdata (output_bfd)->no_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
@@ -5064,7 +5191,7 @@ _bfd_aarch64_erratum_843419_branch_to_stub (struct bfd_hash_entry *gen_entry,
           + stub_entry->adrp_offset);
   insn = bfd_getl32 (contents + stub_entry->adrp_offset);
 
-  if ((insn & AARCH64_ADRP_OP_MASK) !=  AARCH64_ADRP_OP)
+  if (!_bfd_aarch64_adrp_p (insn))
     abort ();
 
   bfd_signed_vma imm =
@@ -5214,8 +5341,7 @@ elfNN_aarch64_final_link_relocate (reloc_howto_type *howto,
 
   weak_undef_p = (h ? h->root.type == bfd_link_hash_undefweak
                  : bfd_is_und_section (sym_sec));
-  abs_symbol_p = (h !=NULL && h->root.type == bfd_link_hash_defined
-                 && bfd_is_abs_section (h->root.u.def.section));
+  abs_symbol_p = h != NULL && bfd_is_abs_symbol (&h->root);
 
 
   /* Since STT_GNU_IFUNC symbol must go through PLT, we handle
@@ -5830,6 +5956,14 @@ bad_ifunc_reloc:
     case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12:
     case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12:
     case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+    case BFD_RELOC_AARCH64_TLSLE_LDST16_TPREL_LO12:
+    case BFD_RELOC_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
+    case BFD_RELOC_AARCH64_TLSLE_LDST32_TPREL_LO12:
+    case BFD_RELOC_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
+    case BFD_RELOC_AARCH64_TLSLE_LDST64_TPREL_LO12:
+    case BFD_RELOC_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
+    case BFD_RELOC_AARCH64_TLSLE_LDST8_TPREL_LO12:
+    case BFD_RELOC_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
     case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0:
     case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
     case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1:
@@ -5921,6 +6055,64 @@ bad_ifunc_reloc:
 # define movz_hw_R0    (0x52c00000)
 #endif
 
+/* Structure to hold payload for _bfd_aarch64_erratum_843419_clear_stub,
+   it is used to identify the stub information to reset.  */
+
+struct erratum_843419_branch_to_stub_clear_data
+{
+  bfd_vma adrp_offset;
+  asection *output_section;
+};
+
+/* Clear the erratum information for GEN_ENTRY if the ADRP_OFFSET and
+   section inside IN_ARG matches.  The clearing is done by setting the
+   stub_type to none.  */
+
+static bfd_boolean
+_bfd_aarch64_erratum_843419_clear_stub (struct bfd_hash_entry *gen_entry,
+                                       void *in_arg)
+{
+  struct elf_aarch64_stub_hash_entry *stub_entry
+    = (struct elf_aarch64_stub_hash_entry *) gen_entry;
+  struct erratum_843419_branch_to_stub_clear_data *data
+    = (struct erratum_843419_branch_to_stub_clear_data *) in_arg;
+
+  if (stub_entry->target_section != data->output_section
+      || stub_entry->stub_type != aarch64_stub_erratum_843419_veneer
+      || stub_entry->adrp_offset != data->adrp_offset)
+    return TRUE;
+
+  /* Change the stub type instead of removing the entry, removing from the hash
+     table would be slower and we have already reserved the memory for the entry
+     so there wouldn't be much gain.  Changing the stub also keeps around a
+     record of what was there before.  */
+  stub_entry->stub_type = aarch64_stub_none;
+
+  /* We're done and there could have been only one matching stub at that
+     particular offset, so abort further traversal.  */
+  return FALSE;
+}
+
+/* TLS Relaxations may relax an adrp sequence that matches the erratum 843419
+   sequence.  In this case the erratum no longer applies and we need to remove
+   the entry from the pending stub generation.  This clears matching adrp insn
+   at ADRP_OFFSET in INPUT_SECTION in the stub table defined in GLOBALS.  */
+
+static void
+clear_erratum_843419_entry (struct elf_aarch64_link_hash_table *globals,
+                           bfd_vma adrp_offset, asection *input_section)
+{
+  if (globals->fix_erratum_843419)
+    {
+      struct erratum_843419_branch_to_stub_clear_data data;
+      data.adrp_offset = adrp_offset;
+      data.output_section = input_section;
+
+      bfd_hash_traverse (&globals->stub_hash_table,
+                        _bfd_aarch64_erratum_843419_clear_stub, &data);
+    }
+}
+
 /* Handle TLS relaxations.  Relaxing is possible for symbols that use
    R_AARCH64_TLSDESC_ADR_{PAGE, LD64_LO12_NC, ADD_LO12_NC} during a static
    link.
@@ -5931,8 +6123,9 @@ bad_ifunc_reloc:
 
 static bfd_reloc_status_type
 elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
-                        bfd *input_bfd, bfd_byte *contents,
-                        Elf_Internal_Rela *rel, struct elf_link_hash_entry *h)
+                        bfd *input_bfd, asection *input_section,
+                        bfd_byte *contents, Elf_Internal_Rela *rel,
+                        struct elf_link_hash_entry *h)
 {
   bfd_boolean is_local = h == NULL;
   unsigned int r_type = ELFNN_R_TYPE (rel->r_info);
@@ -5953,6 +6146,9 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
 
             Where R is x for LP64, and w for ILP32.  */
          bfd_putl32 (movz_R0, contents + rel->r_offset);
+         /* We have relaxed the adrp into a mov, we may have to clear any
+            pending erratum fixes.  */
+         clear_erratum_843419_entry (globals, rel->r_offset, input_section);
          return bfd_reloc_continue;
        }
       else
@@ -6243,6 +6439,9 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
        {
          insn = bfd_getl32 (contents + rel->r_offset);
          bfd_putl32 (movz_R0 | (insn & 0x1f), contents + rel->r_offset);
+         /* We have relaxed the adrp into a mov, we may have to clear any
+            pending erratum fixes.  */
+         clear_erratum_843419_entry (globals, rel->r_offset, input_section);
        }
       return bfd_reloc_continue;
 
@@ -6467,7 +6666,8 @@ elfNN_aarch64_relocate_section (bfd *output_bfd,
          howto = elfNN_aarch64_howto_from_bfd_reloc (bfd_r_type);
          BFD_ASSERT (howto != NULL);
          r_type = howto->type;
-         r = elfNN_aarch64_tls_relax (globals, input_bfd, contents, rel, h);
+         r = elfNN_aarch64_tls_relax (globals, input_bfd, input_section,
+                                      contents, rel, h);
          unresolved_reloc = 0;
        }
       else
@@ -7345,8 +7545,7 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
              if (h != NULL
                  /* This is an absolute symbol.  It represents a value instead
                     of an address.  */
-                 && ((h->root.type == bfd_link_hash_defined
-                      && bfd_is_abs_section (h->root.u.def.section))
+                 && (bfd_is_abs_symbol (&h->root)
                      /* This is an undefined symbol.  */
                      || h->root.type == bfd_link_hash_undefined))
                break;
@@ -7546,9 +7745,6 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case BFD_RELOC_AARCH64_TLSLD_ADD_LO12_NC:
        case BFD_RELOC_AARCH64_TLSLD_ADR_PAGE21:
        case BFD_RELOC_AARCH64_TLSLD_ADR_PREL21:
-       case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1:
-       case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
-       case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2:
          {
            unsigned got_type;
            unsigned old_got_type;
@@ -8058,6 +8254,8 @@ aarch64_map_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
       if (!elfNN_aarch64_output_map_sym (osi, AARCH64_MAP_INSN, addr))
        return FALSE;
       break;
+    case aarch64_stub_none:
+      break;
 
     default:
       abort ();
@@ -8263,7 +8461,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.  */
@@ -8763,10 +8961,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.  */
@@ -8878,6 +9076,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)
@@ -8974,7 +9176,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 */
@@ -9279,10 +9487,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
@@ -9291,18 +9499,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));
 }
 
@@ -9386,9 +9600,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 =
@@ -9410,6 +9633,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,
@@ -9488,6 +9720,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.  */
 
@@ -9495,7 +9774,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.
@@ -9532,6 +9820,52 @@ elfNN_aarch64_backend_symbol_processing (bfd *abfd, asymbol *sym)
     sym->flags |= BSF_KEEP;
 }
 
+/* Implement elf_backend_setup_gnu_properties for AArch64.  It serves as a
+   wrapper function for _bfd_aarch64_elf_link_setup_gnu_properties to account
+   for the effect of GNU properties of the output_bfd.  */
+static bfd *
+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;
+}
+
+/* Implement elf_backend_merge_gnu_properties for AArch64.  It serves as a
+   wrapper function for _bfd_aarch64_elf_merge_gnu_properties to account
+   for the effect of GNU properties of the output_bfd.  */
+static bfd_boolean
+elfNN_aarch64_merge_gnu_properties (struct bfd_link_info *info,
+                                      bfd *abfd,
+                                      elf_property *aprop,
+                                      elf_property *bprop)
+{
+  uint32_t prop
+    = elf_aarch64_tdata (info->output_bfd)->gnu_and_prop;
+
+  /* If output has been marked with BTI using command line argument, give out
+     warning if necessary.  */
+  if ((prop & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
+      && (!elf_aarch64_tdata (info->output_bfd)->no_bti_warn))
+    {
+      if ((aprop && !(aprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_BTI))
+         || (bprop && !(bprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_BTI))
+         /* If either property is NULL, it means its bfd did not have any
+            property.  */
+         || !aprop || !bprop)
+       {
+         _bfd_error_handler (_("warning: BTI turned on by --force-bti when "
+                               "all inputs do not have BTI in NOTE section."));
+       }
+    }
+
+  return  _bfd_aarch64_elf_merge_gnu_properties (info, abfd, aprop,
+                                                bprop, prop);
+}
 
 /* We use this so we can override certain functions
    (though currently we don't).  */
@@ -9606,6 +9940,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
 
@@ -9671,6 +10008,12 @@ const struct elf_size_info elfNN_aarch64_size_info =
 #define elf_backend_symbol_processing          \
   elfNN_aarch64_backend_symbol_processing
 
+#define elf_backend_setup_gnu_properties       \
+  elfNN_aarch64_link_setup_gnu_properties
+
+#define elf_backend_merge_gnu_properties       \
+  elfNN_aarch64_merge_gnu_properties
+
 #define elf_backend_can_refcount       1
 #define elf_backend_can_gc_sections    1
 #define elf_backend_plt_readonly       1