]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - bfd/elfnn-aarch64.c
PCC bounds now span READONLY and RELRO sections
[thirdparty/binutils-gdb.git] / bfd / elfnn-aarch64.c
index 3ccca779ff79cb83d3f2fc7de50bb258a55f94bb..65be85cb7971d325867f86388519773756c0c1db 100644 (file)
@@ -1,5 +1,5 @@
 /* AArch64-specific support for NN-bit ELF.
-   Copyright (C) 2009-2018 Free Software Foundation, Inc.
+   Copyright (C) 2009-2020 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"
 #include "elf/aarch64.h"
 #include "elfxx-aarch64.h"
+#include "cpu-aarch64.h"
 
 #define ARCH_SIZE      NN
 
 #define BFD_RELOC_AARCH64_TLSDESC_LD64_LO12_NC BFD_RELOC_AARCH64_TLSDESC_LD64_LO12
 #endif
 
+#define MORELLO_R(NAME)                R_MORELLO_ ## NAME
+#define MORELLO_R_STR(NAME)    "R_MORELLO_" #NAME
+
 #if ARCH_SIZE == 32
 #define AARCH64_R(NAME)                R_AARCH64_P32_ ## NAME
 #define AARCH64_R_STR(NAME)    "R_AARCH64_P32_" #NAME
 #define IS_AARCH64_TLS_RELAX_RELOC(R_TYPE)                     \
   ((R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_ADD                   \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_ADD_LO12           \
+   || (R_TYPE) == BFD_RELOC_MORELLO_TLSDESC_ADR_PAGE20         \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21         \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_ADR_PREL21         \
+   || (R_TYPE) == BFD_RELOC_MORELLO_TLSDESC_CALL               \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_CALL               \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_LD_PREL19          \
+   || (R_TYPE) == BFD_RELOC_MORELLO_TLSDESC_LD128_LO12         \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_LDNN_LO12_NC       \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_LDR                        \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_OFF_G0_NC          \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_ADD                        \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_ADD_LO12           \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21         \
+   || (R_TYPE) == BFD_RELOC_MORELLO_TLSDESC_ADR_PAGE20         \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_ADR_PREL21         \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_CALL               \
+   || (R_TYPE) == BFD_RELOC_MORELLO_TLSDESC_CALL               \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_LD32_LO12_NC       \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_LD64_LO12          \
+   || (R_TYPE) == BFD_RELOC_MORELLO_TLSDESC_LD128_LO12         \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_LDR                        \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_LD_PREL19          \
    || (R_TYPE) == BFD_RELOC_AARCH64_TLSDESC_OFF_G0_NC          \
    elf_aarch64_link_hash_entry.  */
 #define RELOC_SIZE(HTAB) (sizeof (ElfNN_External_Rela))
 
-/* GOT Entry size - 8 bytes in ELF64 and 4 bytes in ELF32.  */
-#define GOT_ENTRY_SIZE                 (ARCH_SIZE / 8)
+/* GOT Entry size - 16 bytes in C64, 8 bytes in ELF64 and 4 bytes in ELF32.  */
+#define GOT_ENTRY_SIZE(htab) (ARCH_SIZE >> (3 - htab->c64_rel))
+#define GOT_RESERVED_HEADER_SLOTS      (3)
 #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_SMALL_ENTRY_SIZE       (24)
+/* PLT sizes with PAC insn.  */
+#define PLT_PAC_SMALL_ENTRY_SIZE       (24)
+/* PLT sizes with BTI and PAC insn.  */
+#define PLT_BTI_PAC_SMALL_ENTRY_SIZE   (24)
 
 /* Encoding of the nop instruction.  */
 #define INSN_NOP 0xd503201f
 
 #define aarch64_compute_jump_table_size(htab)          \
   (((htab)->root.srelplt == NULL) ? 0                  \
-   : (htab)->root.srelplt->reloc_count * GOT_ENTRY_SIZE)
+   : (htab)->root.srelplt->reloc_count * GOT_ENTRY_SIZE (htab))
 
 /* The first entry in a procedure linkage table looks like this
    if the distance between the PLTGOT and the PLT is < 4GB use
@@ -298,9 +314,39 @@ 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_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 */
+};
+
+/* The C64 PLT0.  */
+static const bfd_byte elfNN_c64_small_plt0_entry[PLT_ENTRY_SIZE] =
+{
+  0xf0, 0x7b, 0xbf, 0x62,      /* stp c16, c30, [csp, #-32]!  */
+  0x10, 0x00, 0x80, 0x90,      /* adrp c16, (GOT+16)  */
+  0x11, 0x0a, 0x40, 0xc2,      /* ldr c17, [c16, #PLT_GOT+0x10]  */
+  0x10, 0x02, 0x00, 0x02,      /* add c16, c16,#PLT_GOT+0x10   */
+  0x20, 0x12, 0xc2, 0xc2,      /* br c17  */
+  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 +360,63 @@ static const bfd_byte elfNN_aarch64_small_plt_entry[PLT_SMALL_ENTRY_SIZE] =
   0x20, 0x02, 0x1f, 0xd6,      /* br x17.  */
 };
 
+/* The C64 PLT.  */
+static const bfd_byte elfNN_c64_small_plt_entry[PLT_SMALL_ENTRY_SIZE] =
+{
+  0x10, 0x00, 0x80, 0x90,      /* adrp c16, PLTGOT + offset  */
+  0x11, 0x02, 0x40, 0xc2,      /* ldr c17, [c16, PLTGOT + offset] */
+  0x10, 0x02, 0x00, 0x02,      /* add c16, c16, :lo12:PLTGOT + offset  */
+  0x20, 0x12, 0xc2, 0xc2,      /* br c17.  */
+};
+
+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.  */
+  0x1f, 0x20, 0x03, 0xd5,      /* nop */
+};
+
+static const bfd_byte
+elfNN_aarch64_small_plt_pac_entry[PLT_PAC_SMALL_ENTRY_SIZE] =
+{
+  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
+  0x9f, 0x21, 0x03, 0xd5,      /* autia1716 */
+  0x20, 0x02, 0x1f, 0xd6,      /* br x17.  */
+  0x1f, 0x20, 0x03, 0xd5,      /* nop */
+};
+
+static const bfd_byte
+elfNN_aarch64_small_plt_bti_pac_entry[PLT_BTI_PAC_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
+  0x9f, 0x21, 0x03, 0xd5,      /* autia1716 */
+  0x20, 0x02, 0x1f, 0xd6,      /* br x17.  */
+};
+
 static const bfd_byte
 elfNN_aarch64_tlsdesc_small_plt_entry[PLT_TLSDESC_ENTRY_SIZE] =
 {
@@ -332,6 +435,37 @@ 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_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 */
+};
+
+static const bfd_byte
+elfNN_aarch64_tlsdesc_small_plt_c64_entry[PLT_TLSDESC_ENTRY_SIZE] =
+{
+  0xe2, 0x8f, 0xbf, 0x62,      /* stp c2, c3, [sp, #-16]! */
+  0x02, 0x00, 0x80, 0x90,      /* adrp c2, 0 */
+  0x03, 0x00, 0x80, 0x90,      /* adrp c3, 0 */
+  0x42, 0x00, 0x40, 0xc2,      /* ldr c2, [c2, #0] */
+  0x63, 0x00, 0x00, 0x02,      /* add c3, c3, 0 */
+  0x40, 0x10, 0xc2, 0xc2,      /* br c2 */
+  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
 
@@ -629,7 +763,7 @@ static reloc_howto_type elfNN_aarch64_howto_table[] =
      PC relative address inline.  */
 
   /* MOV[NZ]:   ((S+A-P) >>  0) & 0xffff */
-  HOWTO64 (AARCH64_R (MOVW_PREL_G0),   /* type */
+  HOWTO (AARCH64_R (MOVW_PREL_G0),     /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         17,                    /* bitsize */
@@ -644,7 +778,7 @@ static reloc_howto_type elfNN_aarch64_howto_table[] =
         TRUE),         /* pcrel_offset */
 
   /* MOVK:   ((S+A-P) >>  0) & 0xffff [no overflow check] */
-  HOWTO64 (AARCH64_R (MOVW_PREL_G0_NC),        /* type */
+  HOWTO (AARCH64_R (MOVW_PREL_G0_NC),  /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         16,                    /* bitsize */
@@ -659,7 +793,7 @@ static reloc_howto_type elfNN_aarch64_howto_table[] =
         TRUE),         /* pcrel_offset */
 
   /* MOV[NZ]:   ((S+A-P) >> 16) & 0xffff */
-  HOWTO64 (AARCH64_R (MOVW_PREL_G1),   /* type */
+  HOWTO (AARCH64_R (MOVW_PREL_G1),     /* type */
         16,                    /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         17,                    /* bitsize */
@@ -736,6 +870,21 @@ static reloc_howto_type elfNN_aarch64_howto_table[] =
 /* Relocations to generate 19, 21 and 33 bit PC-relative load/store
    addresses: PG(x) is (x & ~0xfff).  */
 
+  /* LD-lit: ((S+A-P) >> 4) & 0x1ffff */
+  HOWTO64 (MORELLO_R (LD_PREL_LO17),   /* type */
+        4,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        17,                    /* bitsize */
+        TRUE,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed,      /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (LD_PREL_LO17),  /* name */
+        FALSE,                 /* partial_inplace */
+        0x1ffff,               /* src_mask */
+        0x1ffff,               /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
   /* LD-lit: ((S+A-P) >> 2) & 0x7ffff */
   HOWTO (AARCH64_R (LD_PREL_LO19),     /* type */
         2,                     /* rightshift */
@@ -751,6 +900,36 @@ static reloc_howto_type elfNN_aarch64_howto_table[] =
         0x7ffff,               /* dst_mask */
         TRUE),                 /* pcrel_offset */
 
+  /* C64 ADRP:   ((PG(S+A)-PG(P)) >> 12) & 0xfffff */
+  HOWTO64 (MORELLO_R (ADR_PREL_PG_HI20),       /* type */
+        12,                    /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        20,                    /* bitsize */
+        TRUE,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed,      /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (ADR_PREL_PG_HI20),      /* name */
+        FALSE,                 /* partial_inplace */
+        0xfffff,               /* src_mask */
+        0xfffff,               /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  /* C64 ADRP:   ((PG(S+A)-PG(P)) >> 12) & 0xfffff [no overflow check] */
+  HOWTO64 (MORELLO_R (ADR_PREL_PG_HI20_NC),    /* type */
+        12,                    /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        20,                    /* bitsize */
+        TRUE,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont,        /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (ADR_PREL_PG_HI20_NC),   /* name */
+        FALSE,                 /* partial_inplace */
+        0xfffff,               /* src_mask */
+        0xfffff,               /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
   /* ADR:    (S+A-P) & 0x1fffff */
   HOWTO (AARCH64_R (ADR_PREL_LO21),    /* type */
         0,                     /* rightshift */
@@ -888,6 +1067,66 @@ static reloc_howto_type elfNN_aarch64_howto_table[] =
         0x3ffffff,             /* dst_mask */
         TRUE),                 /* pcrel_offset */
 
+  /* TBZ/NZ: ((S+A-P) >> 2) & 0x3fff */
+  HOWTO64 (MORELLO_R (TSTBR14),        /* type */
+        2,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        14,                    /* bitsize */
+        TRUE,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed,      /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (TSTBR14),       /* name */
+        FALSE,                 /* partial_inplace */
+        0x3fff,                /* src_mask */
+        0x3fff,                /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  /* B.cond: ((S+A-P) >> 2) & 0x7ffff */
+  HOWTO64 (MORELLO_R (CONDBR19),       /* type */
+        2,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        19,                    /* bitsize */
+        TRUE,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed,      /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (CONDBR19),      /* name */
+        FALSE,                 /* partial_inplace */
+        0x7ffff,               /* src_mask */
+        0x7ffff,               /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  /* B:      ((S+A-P) >> 2) & 0x3ffffff */
+  HOWTO64 (MORELLO_R (JUMP26), /* type */
+        2,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        26,                    /* bitsize */
+        TRUE,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed,      /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (JUMP26),        /* name */
+        FALSE,                 /* partial_inplace */
+        0x3ffffff,             /* src_mask */
+        0x3ffffff,             /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  /* BL:     ((S+A-P) >> 2) & 0x3ffffff */
+  HOWTO64 (MORELLO_R (CALL26), /* type */
+        2,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        26,                    /* bitsize */
+        TRUE,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed,      /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (CALL26),        /* name */
+        FALSE,                 /* partial_inplace */
+        0x3ffffff,             /* src_mask */
+        0x3ffffff,             /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
   /* LD/ST16:  (S+A) & 0xffe */
   HOWTO (AARCH64_R (LDST16_ABS_LO12_NC),       /* type */
         1,                     /* rightshift */
@@ -980,6 +1219,22 @@ static reloc_howto_type elfNN_aarch64_howto_table[] =
         0x1fffff,              /* dst_mask */
         TRUE),                 /* pcrel_offset */
 
+  /* Get to the page for the GOT entry for the symbol
+     (G(S) - P) using a C64 ADRP instruction.  */
+  HOWTO64 (MORELLO_R (ADR_GOT_PAGE),   /* type */
+        12,                    /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        20,                    /* bitsize */
+        TRUE,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont,        /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (ADR_GOT_PAGE),  /* name */
+        FALSE,                 /* partial_inplace */
+        0xfffff,               /* src_mask */
+        0xfffff,               /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
   /* LD64: GOT offset G(S) & 0xff8  */
   HOWTO64 (AARCH64_R (LD64_GOT_LO12_NC),       /* type */
         3,                     /* rightshift */
@@ -995,6 +1250,21 @@ static reloc_howto_type elfNN_aarch64_howto_table[] =
         0xff8,                 /* dst_mask */
         FALSE),                /* pcrel_offset */
 
+  /* LD128: GOT offset G(S) & 0xff0  */
+  HOWTO64 (MORELLO_R (LD128_GOT_LO12_NC),      /* type */
+        4,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        12,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont,        /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (LD128_GOT_LO12_NC),     /* name */
+        FALSE,                 /* partial_inplace */
+        0xff0,                 /* src_mask */
+        0xff0,                 /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
   /* LD32: GOT offset G(S) & 0xffc  */
   HOWTO32 (AARCH64_R (LD32_GOT_LO12_NC),       /* type */
         2,                     /* rightshift */
@@ -1922,6 +2192,51 @@ static reloc_howto_type elfNN_aarch64_howto_table[] =
         0x0,                   /* dst_mask */
         FALSE),                /* pcrel_offset */
 
+  /* Get to the page for the GOT entry for the symbol
+     (G(S) - P) using an ADRP instruction.  */
+  HOWTO64 (MORELLO_R (TLSDESC_ADR_PAGE20),     /* type */
+        12,                    /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        20,                    /* bitsize */
+        TRUE,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont,        /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (TLSDESC_ADR_PAGE20),    /* name */
+        FALSE,                 /* partial_inplace */
+        0xfffff,               /* src_mask */
+        0xfffff,               /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  /* LD128: GOT offset G(S) & 0xff0.  */
+  HOWTO64 (MORELLO_R (TLSDESC_LD128_LO12),     /* type */
+        4,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        12,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont,        /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (TLSDESC_LD128_LO12),    /* name */
+        FALSE,                 /* partial_inplace */
+        0xff0,                 /* src_mask */
+        0xff0,                 /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  HOWTO64 (MORELLO_R (TLSDESC_CALL),   /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        0,                     /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont,        /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (TLSDESC_CALL),  /* name */
+        FALSE,                 /* partial_inplace */
+        0x0,                   /* src_mask */
+        0x0,                   /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
   HOWTO (AARCH64_R (COPY),     /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
@@ -2060,31 +2375,115 @@ static reloc_howto_type elfNN_aarch64_howto_table[] =
         ALL_ONES,              /* dst_mask */
         FALSE),                /* pcrel_offset */
 
-  EMPTY_HOWTO (0),
-};
-
-static reloc_howto_type elfNN_aarch64_howto_none =
-  HOWTO (R_AARCH64_NONE,       /* type */
+  HOWTO64 (MORELLO_R (CAPINIT),        /* type */
         0,                     /* rightshift */
-        3,                     /* size (0 = byte, 1 = short, 2 = long) */
+        4,                     /* size (0 = byte, 1 = short, 2 = long) */
         0,                     /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_dont,/* complain_on_overflow */
+        complain_overflow_dont,        /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
-        "R_AARCH64_NONE",      /* name */
+        MORELLO_R_STR (CAPINIT),       /* name */
         FALSE,                 /* partial_inplace */
-        0,                     /* src_mask */
-        0,                     /* dst_mask */
-        FALSE);                /* pcrel_offset */
+        ALL_ONES,              /* src_mask */
+        ALL_ONES,              /* dst_mask */
+        FALSE),                /* pcrel_offset */
 
-/* Given HOWTO, return the bfd internal relocation enumerator.  */
+  HOWTO64 (MORELLO_R (GLOB_DAT),/* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        64,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield,    /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (GLOB_DAT),      /* name */
+        TRUE,                  /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
 
-static bfd_reloc_code_real_type
-elfNN_aarch64_bfd_reloc_from_howto (reloc_howto_type *howto)
-{
-  const int size
-    = (int) ARRAY_SIZE (elfNN_aarch64_howto_table);
+  HOWTO64 (MORELLO_R (JUMP_SLOT),      /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        64,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield,    /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (JUMP_SLOT),     /* name */
+        TRUE,                  /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  HOWTO64 (MORELLO_R (RELATIVE),       /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        64,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield,    /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (RELATIVE),      /* name */
+        TRUE,                  /* partial_inplace */
+        ALL_ONES,              /* src_mask */
+        ALL_ONES,              /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  HOWTO64 (MORELLO_R (IRELATIVE),      /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        64,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield,    /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (IRELATIVE),     /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        ALL_ONES,              /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  HOWTO64 (MORELLO_R (TLSDESC),        /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        64,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont,        /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        MORELLO_R_STR (TLSDESC),       /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        ALL_ONES,              /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  EMPTY_HOWTO (0),
+};
+
+static reloc_howto_type elfNN_aarch64_howto_none =
+  HOWTO (R_AARCH64_NONE,       /* type */
+        0,                     /* rightshift */
+        3,                     /* size (0 = byte, 1 = short, 2 = long) */
+        0,                     /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont,/* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_AARCH64_NONE",      /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0,                     /* dst_mask */
+        FALSE);                /* pcrel_offset */
+
+/* Given HOWTO, return the bfd internal relocation enumerator.  */
+
+static bfd_reloc_code_real_type
+elfNN_aarch64_bfd_reloc_from_howto (reloc_howto_type *howto)
+{
+  const int size
+    = (int) ARRAY_SIZE (elfNN_aarch64_howto_table);
   const ptrdiff_t offset
     = howto - elfNN_aarch64_howto_table;
 
@@ -2263,7 +2662,7 @@ elfNN_aarch64_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
    The entry_names are used to do simple name mangling on the stubs.
    Given a function name, and its type, the stub can be found. The
    name can be changed. The only requirement is the %s be present.  */
-#define STUB_ENTRY_NAME   "__%s_veneer"
+#define STUB_ENTRY_NAME   "__%s%s_veneer"
 
 /* The name of the dynamic interpreter.  This is put in the .interp
    section.  */
@@ -2277,6 +2676,26 @@ elfNN_aarch64_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
 #define AARCH64_MAX_ADRP_IMM ((1 << 20) - 1)
 #define AARCH64_MIN_ADRP_IMM (-(1 << 20))
 
+#define C64_MAX_ADRP_IMM ((1 << 19) - 1)
+#define C64_MIN_ADRP_IMM (-(1 << 19))
+
+static bfd_boolean
+aarch64_branch_reloc_p (unsigned int r_type)
+{
+  switch (r_type)
+    {
+    case MORELLO_R (JUMP26):
+    case MORELLO_R (CALL26):
+    case AARCH64_R (JUMP26):
+    case AARCH64_R (CALL26):
+      return TRUE;
+
+    default: break;
+    }
+
+  return FALSE;
+}
+
 static int
 aarch64_valid_for_adrp_p (bfd_vma value, bfd_vma place)
 {
@@ -2284,6 +2703,13 @@ aarch64_valid_for_adrp_p (bfd_vma value, bfd_vma place)
   return offset <= AARCH64_MAX_ADRP_IMM && offset >= AARCH64_MIN_ADRP_IMM;
 }
 
+static bfd_boolean
+c64_valid_for_adrp_p (bfd_vma value, bfd_vma place)
+{
+  bfd_signed_vma offset = (bfd_signed_vma) (PG (value) - PG (place)) >> 12;
+  return offset <= C64_MAX_ADRP_IMM && offset >= C64_MIN_ADRP_IMM;
+}
+
 static int
 aarch64_valid_branch_p (bfd_vma value, bfd_vma place)
 {
@@ -2329,6 +2755,25 @@ static const uint32_t aarch64_erratum_843419_stub[] =
   0x14000000,    /* b <label> */
 };
 
+static const uint32_t aarch64_c64_branch_stub [] =
+{
+  0xc2c273e0,                  /*      bx      #4 */
+  0x90800010,                  /*      adrp    c16, X */
+                               /*              R_MORELLO_ADR_HI20_PCREL(X) */
+  0x02000210,                  /*      add     c16, c16, :lo12:X */
+                               /*              R_AARCH64_ADD_ABS_LO12_NC(X) */
+  0xc2c21200,                  /*      br      c16 */
+};
+
+static const uint32_t c64_aarch64_branch_stub [] =
+{
+  0x90800010,                  /*      adrp    c16, X */
+                               /*              R_MORELLO_ADR_HI20_PCREL(X) */
+  0x02000210,                  /*      add     c16, c16, :lo12:X */
+                               /*              R_AARCH64_ADD_ABS_LO12_NC(X) */
+  0xc2c21200,                  /*      br      c16 */
+};
+
 /* Section name for stubs is the associated section name plus this
    string.  */
 #define STUB_SUFFIX ".stub"
@@ -2340,6 +2785,9 @@ enum elf_aarch64_stub_type
   aarch64_stub_long_branch,
   aarch64_stub_erratum_835769_veneer,
   aarch64_stub_erratum_843419_veneer,
+  aarch64_stub_branch_c64,
+  c64_stub_branch_aarch64,
+  c64_stub_branch_c64,
 };
 
 struct elf_aarch64_stub_hash_entry
@@ -2400,12 +2848,86 @@ typedef struct _aarch64_elf_section_data
   unsigned int mapcount;
   unsigned int mapsize;
   elf_aarch64_section_map *map;
+  bfd_boolean sorted;
 }
 _aarch64_elf_section_data;
 
 #define elf_aarch64_section_data(sec) \
   ((_aarch64_elf_section_data *) elf_section_data (sec))
 
+/* Used to order a list of mapping symbols by address.  */
+
+static int
+elf_aarch64_compare_mapping (const void *a, const void *b)
+{
+  const elf_aarch64_section_map *amap = (const elf_aarch64_section_map *) a;
+  const elf_aarch64_section_map *bmap = (const elf_aarch64_section_map *) b;
+
+  if (amap->vma > bmap->vma)
+    return 1;
+  else if (amap->vma < bmap->vma)
+    return -1;
+  else if (amap->type > bmap->type)
+    /* Ensure results do not depend on the host qsort for objects with
+       multiple mapping symbols at the same address by sorting on type
+       after vma.  */
+    return 1;
+  else if (amap->type < bmap->type)
+    return -1;
+  else
+    return 0;
+}
+
+static _aarch64_elf_section_data *
+elf_aarch64_section_data_get (asection *sec)
+{
+  _aarch64_elf_section_data *sec_data = elf_aarch64_section_data(sec);
+
+  /* A section that does not have aarch64 section data, so it does not have any
+     map information.  Assume A64.  */
+  if (sec_data == NULL || !sec_data->elf.is_target_section_data)
+    return NULL;
+
+  if (sec_data->sorted)
+    goto done;
+
+  qsort (sec_data->map, sec_data->mapcount, sizeof (elf_aarch64_section_map),
+        elf_aarch64_compare_mapping);
+
+  sec_data->sorted = TRUE;
+
+done:
+  return sec_data;
+}
+
+/* Returns TRUE if the label with st_value as VALUE is within a C64 code
+   section or not.  */
+
+static bfd_boolean
+c64_value_p (asection *section, unsigned int value)
+{
+  struct _aarch64_elf_section_data *sec_data =
+    elf_aarch64_section_data_get (section);
+
+  if (sec_data == NULL)
+    return FALSE;
+
+  unsigned int span;
+
+  for (span = 0; span < sec_data->mapcount; span++)
+    {
+      unsigned int span_start = sec_data->map[span].vma;
+      unsigned int span_end = ((span == sec_data->mapcount - 1)
+                              ? sec_data->map[0].vma + section->size
+                              : sec_data->map[span + 1].vma);
+      char span_type = sec_data->map[span].type;
+
+      if (span_start <= value && value < span_end && span_type == 'c')
+       return TRUE;
+    }
+  return FALSE;
+}
+
 /* The size of the thread control block which is defined to be two pointers.  */
 #define TCB_SIZE       (ARCH_SIZE/8)*2
 
@@ -2436,6 +2958,20 @@ 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;
+
+  /* Flag to check if section maps have been initialised for all sections in
+     this object.  */
+  bfd_boolean secmaps_initialised;
 };
 
 #define elf_aarch64_tdata(bfd)                         \
@@ -2463,6 +2999,7 @@ elfNN_aarch64_mkobject (bfd *abfd)
 #define GOT_TLS_GD     2
 #define GOT_TLS_IE     4
 #define GOT_TLSDESC_GD 8
+#define GOT_CAP        16
 
 #define GOT_TLS_GD_ANY_P(type) ((type & GOT_TLS_GD) || (type & GOT_TLSDESC_GD))
 
@@ -2471,9 +3008,6 @@ struct elf_aarch64_link_hash_entry
 {
   struct elf_link_hash_entry root;
 
-  /* Track dynamic relocs copied for this symbol.  */
-  struct elf_dyn_relocs *dyn_relocs;
-
   /* Since PLT entries have variable size, we need to record the
      index into .got.plt instead of recomputing it from the PLT
      offset.  */
@@ -2530,10 +3064,7 @@ struct elf_aarch64_link_hash_table
   int fix_erratum_835769;
 
   /* Fix erratum 843419.  */
-  int fix_erratum_843419;
-
-  /* Enable ADRP->ADR rewrite for erratum 843419 workaround.  */
-  int fix_erratum_843419_adr;
+  erratum_84319_opts fix_erratum_843419;
 
   /* Don't apply link-time values for dynamic relocations.  */
   int no_apply_dynamic_relocs;
@@ -2541,11 +3072,14 @@ 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;
 
-  /* Small local sym cache.  */
-  struct sym_cache sym_cache;
+  /* The bytes of the subsequent PLT entry.  */
+  const bfd_byte *plt_entry;
 
   /* For convenience in allocate_dynrelocs.  */
   bfd *obfd;
@@ -2580,20 +3114,20 @@ struct elf_aarch64_link_hash_table
   unsigned int top_index;
   asection **input_list;
 
-  /* The offset into splt of the PLT entry for the TLS descriptor
-     resolver.  Special values are 0, if not necessary (or not found
-     to be necessary yet), and -1 if needed but not determined
-     yet.  */
-  bfd_vma tlsdesc_plt;
+  /* JUMP_SLOT relocs for variant PCS symbols may be present.  */
+  int variant_pcs;
 
-  /* 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.  */
-  bfd_vma dt_tlsdesc_got;
+  /* The number of bytes in the PLT enty for the TLS descriptor.  */
+  bfd_size_type tlsdesc_plt_entry_size;
 
   /* Used by local STT_GNU_IFUNC symbols.  */
   htab_t loc_hash_table;
   void * loc_hash_memory;
+
+  /* Used for capability relocations.  */
+  asection *srelcaps;
+  int c64_rel;
+  bfd_boolean c64_output;
 };
 
 /* Create an entry in an AArch64 ELF linker hash table.  */
@@ -2620,7 +3154,6 @@ elfNN_aarch64_link_hash_newfunc (struct bfd_hash_entry *entry,
                                     table, string));
   if (ret != NULL)
     {
-      ret->dyn_relocs = NULL;
       ret->got_type = GOT_UNKNOWN;
       ret->plt_got_offset = (bfd_vma) - 1;
       ret->stub_cache = NULL;
@@ -2747,37 +3280,6 @@ elfNN_aarch64_copy_indirect_symbol (struct bfd_link_info *info,
   edir = (struct elf_aarch64_link_hash_entry *) dir;
   eind = (struct elf_aarch64_link_hash_entry *) ind;
 
-  if (eind->dyn_relocs != NULL)
-    {
-      if (edir->dyn_relocs != NULL)
-       {
-         struct elf_dyn_relocs **pp;
-         struct elf_dyn_relocs *p;
-
-         /* Add reloc counts against the indirect sym to the direct sym
-            list.  Merge any entries against the same section.  */
-         for (pp = &eind->dyn_relocs; (p = *pp) != NULL;)
-           {
-             struct elf_dyn_relocs *q;
-
-             for (q = edir->dyn_relocs; q != NULL; q = q->next)
-               if (q->sec == p->sec)
-                 {
-                   q->pc_count += p->pc_count;
-                   q->count += p->count;
-                   *pp = p->next;
-                   break;
-                 }
-             if (q == NULL)
-               pp = &p->next;
-           }
-         *pp = edir->dyn_relocs;
-       }
-
-      edir->dyn_relocs = eind->dyn_relocs;
-      eind->dyn_relocs = NULL;
-    }
-
   if (ind->root.type == bfd_link_hash_indirect)
     {
       /* Copy over PLT info.  */
@@ -2791,6 +3293,31 @@ elfNN_aarch64_copy_indirect_symbol (struct bfd_link_info *info,
   _bfd_elf_link_hash_copy_indirect (info, dir, ind);
 }
 
+/* Merge non-visibility st_other attributes.  */
+
+static void
+elfNN_aarch64_merge_symbol_attribute (struct elf_link_hash_entry *h,
+                                     const Elf_Internal_Sym *isym,
+                                     bfd_boolean definition ATTRIBUTE_UNUSED,
+                                     bfd_boolean dynamic ATTRIBUTE_UNUSED)
+{
+  unsigned int isym_sto = isym->st_other & ~ELF_ST_VISIBILITY (-1);
+  unsigned int h_sto = h->other & ~ELF_ST_VISIBILITY (-1);
+
+  if (isym_sto == h_sto)
+    return;
+
+  if (isym_sto & ~STO_AARCH64_VARIANT_PCS)
+    /* Not fatal, this callback cannot fail.  */
+    _bfd_error_handler (_("unknown attribute for symbol `%s': 0x%02x"),
+                       h->root.root.string, isym_sto);
+
+  /* Note: Ideally we would warn about any attribute mismatch, but
+     this api does not allow that without substantial changes.  */
+  if (isym_sto & STO_AARCH64_VARIANT_PCS)
+    h->other |= STO_AARCH64_VARIANT_PCS;
+}
+
 /* Destroy an AArch64 elf linker hash table.  */
 
 static void
@@ -2814,7 +3341,7 @@ static struct bfd_link_hash_table *
 elfNN_aarch64_link_hash_table_create (bfd *abfd)
 {
   struct elf_aarch64_link_hash_table *ret;
-  bfd_size_type amt = sizeof (struct elf_aarch64_link_hash_table);
+  size_t amt = sizeof (struct elf_aarch64_link_hash_table);
 
   ret = bfd_zmalloc (amt);
   if (ret == NULL)
@@ -2829,9 +3356,12 @@ 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;
+  ret->root.tlsdesc_got = (bfd_vma) - 1;
 
   if (!bfd_hash_table_init (&ret->stub_hash_table, stub_hash_newfunc,
                            sizeof (struct elf_aarch64_stub_hash_entry)))
@@ -2869,12 +3399,38 @@ aarch64_relocate (unsigned int r_type, bfd *input_bfd, asection *input_section,
           + offset);
 
   r_type = elfNN_aarch64_bfd_reloc_from_type (input_bfd, r_type);
-  value = _bfd_aarch64_elf_resolve_relocation (r_type, place, value, 0, FALSE);
+  value = _bfd_aarch64_elf_resolve_relocation (input_bfd, r_type, place,
+                                              value, 0, FALSE);
   return _bfd_aarch64_elf_put_addend (input_bfd,
                                      input_section->contents + offset, r_type,
                                      howto, value) == bfd_reloc_ok;
 }
 
+/* Return interworking stub for a relocation.  */
+
+static enum elf_aarch64_stub_type
+aarch64_interwork_stub (unsigned int r_type,
+                       bfd_boolean branch_to_c64)
+{
+  switch (r_type)
+    {
+    case MORELLO_R (JUMP26):
+    case MORELLO_R (CALL26):
+      if (!branch_to_c64)
+       return c64_stub_branch_aarch64;
+      break;
+    case AARCH64_R (JUMP26):
+    case AARCH64_R (CALL26):
+      if (branch_to_c64)
+       return aarch64_stub_branch_c64;
+      break;
+    default:
+      break;
+    }
+
+  return aarch64_stub_none;
+}
+
 static enum elf_aarch64_stub_type
 aarch64_select_branch_stub (bfd_vma value, bfd_vma place)
 {
@@ -2894,7 +3450,7 @@ aarch64_type_of_stub (asection *input_sec,
 {
   bfd_vma location;
   bfd_signed_vma branch_offset;
-  unsigned int r_type;
+  unsigned int r_type = ELFNN_R_TYPE (rel->r_info);
   enum elf_aarch64_stub_type stub_type = aarch64_stub_none;
 
   if (st_type != STT_FUNC
@@ -2907,19 +3463,45 @@ aarch64_type_of_stub (asection *input_sec,
 
   branch_offset = (bfd_signed_vma) (destination - location);
 
-  r_type = ELFNN_R_TYPE (rel->r_info);
-
-  /* We don't want to redirect any old unconditional jump in this way,
-     only one which is being used for a sibcall, where it is
-     acceptable for the IP0 and IP1 registers to be clobbered.  */
-  if ((r_type == AARCH64_R (CALL26) || r_type == AARCH64_R (JUMP26))
-      && (branch_offset > AARCH64_MAX_FWD_BRANCH_OFFSET
-         || branch_offset < AARCH64_MAX_BWD_BRANCH_OFFSET))
+  /* For A64 <-> C64 branches we only come here for jumps to PLT.  Treat them
+     as regular branches and leave the interworking to PLT.  */
+  if (branch_offset > AARCH64_MAX_FWD_BRANCH_OFFSET
+      || branch_offset < AARCH64_MAX_BWD_BRANCH_OFFSET)
     {
-      stub_type = aarch64_stub_long_branch;
+      switch (r_type)
+       {
+         /* We don't want to redirect any old unconditional jump in this way,
+            only one which is being used for a sibcall, where it is
+            acceptable for the IP0 and IP1 registers to be clobbered.  */
+       case AARCH64_R (CALL26):
+       case AARCH64_R (JUMP26):
+           return aarch64_stub_long_branch;
+       case MORELLO_R (CALL26):
+       case MORELLO_R (JUMP26):
+           return c64_stub_branch_c64;
+       default:
+         break;
+       }
     }
 
-  return stub_type;
+  return aarch64_stub_none;
+}
+
+/* Return a string to add as suffix to a veneer name.  */
+
+static const char *
+aarch64_lookup_stub_type_suffix (enum elf_aarch64_stub_type stub_type)
+{
+      switch (stub_type)
+       {
+       case aarch64_stub_branch_c64:
+         return "_a64c64";
+       case c64_stub_branch_aarch64:
+         return "_c64a64";
+         break;
+       default:
+         return "";
+       }
 }
 
 /* Build a name for an entry in the stub hash table.  */
@@ -2928,31 +3510,33 @@ static char *
 elfNN_aarch64_stub_name (const asection *input_section,
                         const asection *sym_sec,
                         const struct elf_aarch64_link_hash_entry *hash,
-                        const Elf_Internal_Rela *rel)
+                        const Elf_Internal_Rela *rel,
+                        enum elf_aarch64_stub_type stub_type)
 {
   char *stub_name;
   bfd_size_type len;
+  const char *suffix = aarch64_lookup_stub_type_suffix (stub_type);;
 
   if (hash)
     {
       len = 8 + 1 + strlen (hash->root.root.root.string) + 1 + 16 + 1;
       stub_name = bfd_malloc (len);
       if (stub_name != NULL)
-       snprintf (stub_name, len, "%08x_%s+%" BFD_VMA_FMT "x",
+       snprintf (stub_name, len, "%08x_%s%s+%" BFD_VMA_FMT "x",
                  (unsigned int) input_section->id,
                  hash->root.root.root.string,
-                 rel->r_addend);
+                 suffix, rel->r_addend);
     }
   else
     {
       len = 8 + 1 + 8 + 1 + 8 + 1 + 16 + 1;
       stub_name = bfd_malloc (len);
       if (stub_name != NULL)
-       snprintf (stub_name, len, "%08x_%x:%x+%" BFD_VMA_FMT "x",
+       snprintf (stub_name, len, "%08x_%x:%x%s+%" BFD_VMA_FMT "x",
                  (unsigned int) input_section->id,
                  (unsigned int) sym_sec->id,
                  (unsigned int) ELFNN_R_SYM (rel->r_info),
-                 rel->r_addend);
+                 suffix, rel->r_addend);
     }
 
   return stub_name;
@@ -2973,7 +3557,6 @@ elf_aarch64_hash_symbol (struct elf_link_hash_entry *h)
   return _bfd_elf_hash_symbol (h);
 }
 
-
 /* Look up an entry in the stub hash.  Stub entries are cached because
    creating the stub name takes a bit of time.  */
 
@@ -2982,7 +3565,8 @@ elfNN_aarch64_get_stub_entry (const asection *input_section,
                              const asection *sym_sec,
                              struct elf_link_hash_entry *hash,
                              const Elf_Internal_Rela *rel,
-                             struct elf_aarch64_link_hash_table *htab)
+                             struct elf_aarch64_link_hash_table *htab,
+                             enum elf_aarch64_stub_type stub_type)
 {
   struct elf_aarch64_stub_hash_entry *stub_entry;
   struct elf_aarch64_link_hash_entry *h =
@@ -3008,7 +3592,7 @@ elfNN_aarch64_get_stub_entry (const asection *input_section,
     {
       char *stub_name;
 
-      stub_name = elfNN_aarch64_stub_name (id_sec, sym_sec, h, rel);
+      stub_name = elfNN_aarch64_stub_name (id_sec, sym_sec, h, rel, stub_type);
       if (stub_name == NULL)
        return NULL;
 
@@ -3119,7 +3703,10 @@ _bfd_aarch64_add_stub_entry_after (const char *stub_name,
   asection *stub_sec;
   struct elf_aarch64_stub_hash_entry *stub_entry;
 
-  stub_sec = _bfd_aarch64_get_stub_for_link_section (link_section, htab);
+  stub_sec = NULL;
+  /* Only create the actual stub if we will end up needing it.  */
+  if (htab->fix_erratum_843419 & ERRAT_ADRP)
+    stub_sec = _bfd_aarch64_get_stub_for_link_section (link_section, htab);
   stub_entry = aarch64_stub_hash_lookup (&htab->stub_hash_table, stub_name,
                                         TRUE, FALSE);
   if (stub_entry == NULL)
@@ -3138,7 +3725,7 @@ _bfd_aarch64_add_stub_entry_after (const char *stub_name,
 
 static bfd_boolean
 aarch64_build_one_stub (struct bfd_hash_entry *gen_entry,
-                       void *in_arg ATTRIBUTE_UNUSED)
+                       void *in_arg)
 {
   struct elf_aarch64_stub_hash_entry *stub_entry;
   asection *stub_sec;
@@ -3151,10 +3738,22 @@ aarch64_build_one_stub (struct bfd_hash_entry *gen_entry,
   unsigned int template_size;
   const uint32_t *template;
   unsigned int i;
+  struct bfd_link_info *info;
 
   /* Massage our args to the form they really have.  */
   stub_entry = (struct elf_aarch64_stub_hash_entry *) gen_entry;
 
+  info = (struct bfd_link_info *) in_arg;
+
+  /* Fail if the target section could not be assigned to an output
+     section.  The user should fix his linker script.  */
+  if (stub_entry->target_section->output_section == NULL
+      && info->non_contiguous_regions)
+    info->callbacks->einfo (_("%F%P: Could not assign '%pA' to an output section. "
+                             "Retry without "
+                             "--enable-non-contiguous-regions.\n"),
+                           stub_entry->target_section);
+
   stub_sec = stub_entry->stub_sec;
 
   /* Make a note of the offset within the stubs for this entry.  */
@@ -3168,16 +3767,30 @@ aarch64_build_one_stub (struct bfd_hash_entry *gen_entry,
               + stub_entry->target_section->output_offset
               + stub_entry->target_section->output_section->vma);
 
+  bfd_vma place = (stub_entry->stub_offset + stub_sec->output_section->vma
+                  + stub_sec->output_offset);
+
   if (stub_entry->stub_type == aarch64_stub_long_branch)
     {
-      bfd_vma place = (stub_entry->stub_offset + stub_sec->output_section->vma
-                      + stub_sec->output_offset);
-
       /* See if we can relax the stub.  */
       if (aarch64_valid_for_adrp_p (sym_value, place))
        stub_entry->stub_type = aarch64_select_branch_stub (sym_value, place);
     }
 
+  if ((stub_entry->stub_type == aarch64_stub_branch_c64
+      || stub_entry->stub_type == c64_stub_branch_aarch64
+      || stub_entry->stub_type == c64_stub_branch_c64)
+      && !c64_valid_for_adrp_p (sym_value, place))
+    {
+      _bfd_error_handler
+       (_("%s: stub target out of range for %s branch"),
+        stub_entry->output_name,
+        (stub_entry->stub_type == aarch64_stub_branch_c64
+         ? "A64 to C64" : "C64 to A64"));
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
+    }
+
   switch (stub_entry->stub_type)
     {
     case aarch64_stub_adrp_branch:
@@ -3196,6 +3809,15 @@ aarch64_build_one_stub (struct bfd_hash_entry *gen_entry,
       template = aarch64_erratum_843419_stub;
       template_size = sizeof (aarch64_erratum_843419_stub);
       break;
+    case aarch64_stub_branch_c64:
+      template = aarch64_c64_branch_stub;
+      template_size = sizeof (aarch64_c64_branch_stub);
+      break;
+    case c64_stub_branch_aarch64:
+    case c64_stub_branch_c64:
+      template = c64_aarch64_branch_stub;
+      template_size = sizeof (c64_aarch64_branch_stub);
+      break;
     default:
       abort ();
     }
@@ -3209,6 +3831,8 @@ aarch64_build_one_stub (struct bfd_hash_entry *gen_entry,
   template_size = (template_size + 7) & ~7;
   stub_sec->size += template_size;
 
+  bfd_vma stub_offset = stub_entry->stub_offset;
+
   switch (stub_entry->stub_type)
     {
     case aarch64_stub_adrp_branch:
@@ -3253,6 +3877,21 @@ aarch64_build_one_stub (struct bfd_hash_entry *gen_entry,
        BFD_FAIL ();
       break;
 
+    case aarch64_stub_branch_c64:
+      stub_offset += 4;
+      /* Fall through.  */
+    case c64_stub_branch_aarch64:
+    case c64_stub_branch_c64:
+      if (!aarch64_relocate (R_MORELLO_ADR_PREL_PG_HI20, stub_bfd, stub_sec,
+                            stub_offset, sym_value))
+       /* We fail early if offset is out of range.  */
+       BFD_FAIL ();
+
+      if (!aarch64_relocate (AARCH64_R (ADD_ABS_LO12_NC), stub_bfd, stub_sec,
+                            stub_offset + 4, sym_value))
+       BFD_FAIL ();
+      break;
+
     default:
       abort ();
     }
@@ -3264,14 +3903,15 @@ aarch64_build_one_stub (struct bfd_hash_entry *gen_entry,
    we know stub section sizes.  */
 
 static bfd_boolean
-aarch64_size_one_stub (struct bfd_hash_entry *gen_entry,
-                      void *in_arg ATTRIBUTE_UNUSED)
+aarch64_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
 {
   struct elf_aarch64_stub_hash_entry *stub_entry;
+  struct elf_aarch64_link_hash_table *htab;
   int size;
 
   /* Massage our args to the form they really have.  */
   stub_entry = (struct elf_aarch64_stub_hash_entry *) gen_entry;
+  htab = (struct elf_aarch64_link_hash_table *) in_arg;
 
   switch (stub_entry->stub_type)
     {
@@ -3285,7 +3925,18 @@ aarch64_size_one_stub (struct bfd_hash_entry *gen_entry,
       size = sizeof (aarch64_erratum_835769_stub);
       break;
     case aarch64_stub_erratum_843419_veneer:
-      size = sizeof (aarch64_erratum_843419_stub);
+      {
+       if (htab->fix_erratum_843419 == ERRAT_ADR)
+         return TRUE;
+       size = sizeof (aarch64_erratum_843419_stub);
+      }
+      break;
+    case aarch64_stub_branch_c64:
+      size = sizeof (aarch64_c64_branch_stub);
+      break;
+    case c64_stub_branch_aarch64:
+    case c64_stub_branch_c64:
+      size = sizeof (c64_aarch64_branch_stub);
       break;
     default:
       abort ();
@@ -3311,7 +3962,7 @@ elfNN_aarch64_setup_section_lists (bfd *output_bfd,
   unsigned int top_id, top_index;
   asection *section;
   asection **input_list, **list;
-  bfd_size_type amt;
+  size_t amt;
   struct elf_aarch64_link_hash_table *htab =
     elf_aarch64_hash_table (info);
 
@@ -3389,7 +4040,7 @@ elfNN_aarch64_next_input_section (struct bfd_link_info *info, asection *isec)
     {
       asection **list = htab->input_list + isec->output_section->index;
 
-      if (*list != bfd_abs_section_ptr)
+      if (*list != bfd_abs_section_ptr && (isec->flags & SEC_CODE) != 0)
        {
          /* Steal the link_sec pointer for our list.  */
          /* This happens to make the list in reverse order,
@@ -3410,67 +4061,96 @@ elfNN_aarch64_next_input_section (struct bfd_link_info *info, asection *isec)
 static void
 group_sections (struct elf_aarch64_link_hash_table *htab,
                bfd_size_type stub_group_size,
-               bfd_boolean stubs_always_before_branch)
+               bfd_boolean stubs_always_after_branch)
 {
-  asection **list = htab->input_list + htab->top_index;
+  asection **list = htab->input_list;
 
   do
     {
       asection *tail = *list;
+      asection *head;
 
       if (tail == bfd_abs_section_ptr)
        continue;
 
+      /* Reverse the list: we must avoid placing stubs at the
+        beginning of the section because the beginning of the text
+        section may be required for an interrupt vector in bare metal
+        code.  */
+#define NEXT_SEC PREV_SEC
+      head = NULL;
       while (tail != NULL)
+       {
+         /* Pop from tail.  */
+         asection *item = tail;
+         tail = PREV_SEC (item);
+
+         /* Push on head.  */
+         NEXT_SEC (item) = head;
+         head = item;
+       }
+
+      while (head != NULL)
        {
          asection *curr;
-         asection *prev;
-         bfd_size_type total;
+         asection *next;
+         bfd_vma stub_group_start = head->output_offset;
+         bfd_vma end_of_next;
 
-         curr = tail;
-         total = tail->size;
-         while ((prev = PREV_SEC (curr)) != NULL
-                && ((total += curr->output_offset - prev->output_offset)
-                    < stub_group_size))
-           curr = prev;
+         curr = head;
+         while (NEXT_SEC (curr) != NULL)
+           {
+             next = NEXT_SEC (curr);
+             end_of_next = next->output_offset + next->size;
+             if (end_of_next - stub_group_start >= stub_group_size)
+               /* End of NEXT is too far from start, so stop.  */
+               break;
+             /* Add NEXT to the group.  */
+             curr = next;
+           }
 
-         /* OK, the size from the start of CURR to the end is less
+         /* OK, the size from the start to the start of CURR is less
             than stub_group_size and thus can be handled by one stub
-            section.  (Or the tail section is itself larger than
+            section.  (Or the head section is itself larger than
             stub_group_size, in which case we may be toast.)
             We should really be keeping track of the total size of
             stubs added here, as stubs contribute to the final output
             section size.  */
          do
            {
-             prev = PREV_SEC (tail);
+             next = NEXT_SEC (head);
              /* Set up this stub group.  */
-             htab->stub_group[tail->id].link_sec = curr;
+             htab->stub_group[head->id].link_sec = curr;
            }
-         while (tail != curr && (tail = prev) != NULL);
+         while (head != curr && (head = next) != NULL);
 
          /* But wait, there's more!  Input sections up to stub_group_size
-            bytes before the stub section can be handled by it too.  */
-         if (!stubs_always_before_branch)
+            bytes after the stub section can be handled by it too.  */
+         if (!stubs_always_after_branch)
            {
-             total = 0;
-             while (prev != NULL
-                    && ((total += tail->output_offset - prev->output_offset)
-                        < stub_group_size))
+             stub_group_start = curr->output_offset + curr->size;
+
+             while (next != NULL)
                {
-                 tail = prev;
-                 prev = PREV_SEC (tail);
-                 htab->stub_group[tail->id].link_sec = curr;
+                 end_of_next = next->output_offset + next->size;
+                 if (end_of_next - stub_group_start >= stub_group_size)
+                   /* End of NEXT is too far from stubs, so stop.  */
+                   break;
+                 /* Add NEXT to the stub group.  */
+                 head = next;
+                 next = NEXT_SEC (head);
+                 htab->stub_group[head->id].link_sec = curr;
                }
            }
-         tail = prev;
+         head = next;
        }
     }
-  while (list-- != htab->input_list);
+  while (list++ != htab->input_list + htab->top_index);
 
   free (htab->input_list);
 }
 
+#undef PREV_SEC
 #undef PREV_SEC
 
 #define AARCH64_BITS(x, pos, n) (((x) >> (pos)) & ((1 << (n)) - 1))
@@ -3716,36 +4396,14 @@ aarch64_erratum_sequence (uint32_t insn_1, uint32_t insn_2)
   return FALSE;
 }
 
-/* Used to order a list of mapping symbols by address.  */
-
-static int
-elf_aarch64_compare_mapping (const void *a, const void *b)
-{
-  const elf_aarch64_section_map *amap = (const elf_aarch64_section_map *) a;
-  const elf_aarch64_section_map *bmap = (const elf_aarch64_section_map *) b;
-
-  if (amap->vma > bmap->vma)
-    return 1;
-  else if (amap->vma < bmap->vma)
-    return -1;
-  else if (amap->type > bmap->type)
-    /* Ensure results do not depend on the host qsort for objects with
-       multiple mapping symbols at the same address by sorting on type
-       after vma.  */
-    return 1;
-  else if (amap->type < bmap->type)
-    return -1;
-  else
-    return 0;
-}
-
 
 static char *
 _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;
 }
 
@@ -3787,8 +4445,9 @@ _bfd_aarch64_erratum_835769_scan (bfd *input_bfd,
 
       sec_data = elf_aarch64_section_data (section);
 
-      qsort (sec_data->map, sec_data->mapcount,
-            sizeof (elf_aarch64_section_map), elf_aarch64_compare_mapping);
+      if (sec_data->mapcount)
+       qsort (sec_data->map, sec_data->mapcount,
+              sizeof (elf_aarch64_section_map), elf_aarch64_compare_mapping);
 
       for (span = 0; span < sec_data->mapcount; span++)
        {
@@ -3844,7 +4503,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);
 }
 
 
@@ -3949,8 +4608,10 @@ _bfd_aarch64_resize_stubs (struct elf_aarch64_link_hash_table *htab)
       /* Ensure all stub sections have a size which is a multiple of
         4096.  This is important in order to ensure that the insertion
         of stub sections does not in itself move existing code around
-        in such a way that new errata sequences are created.  */
-      if (htab->fix_erratum_843419)
+        in such a way that new errata sequences are created.  We only do this
+        when the ADRP workaround is enabled.  If only the ADR workaround is
+        enabled then the stubs workaround won't ever be used.  */
+      if (htab->fix_erratum_843419 & ERRAT_ADRP)
        if (section->size)
          section->size = BFD_ALIGN (section->size, 0x1000);
     }
@@ -3992,6 +4653,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)
@@ -4009,8 +4672,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)
@@ -4068,8 +4730,9 @@ _bfd_aarch64_erratum_843419_scan (bfd *input_bfd, asection *section,
 
       sec_data = elf_aarch64_section_data (section);
 
-      qsort (sec_data->map, sec_data->mapcount,
-            sizeof (elf_aarch64_section_map), elf_aarch64_compare_mapping);
+      if (sec_data->mapcount)
+       qsort (sec_data->map, sec_data->mapcount,
+              sizeof (elf_aarch64_section_map), elf_aarch64_compare_mapping);
 
       for (span = 0; span < sec_data->mapcount; span++)
        {
@@ -4080,42 +4743,357 @@ _bfd_aarch64_erratum_843419_scan (bfd *input_bfd, asection *section,
          unsigned int i;
          char span_type = sec_data->map[span].type;
 
-         if (span_type == 'd')
-           continue;
+         if (span_type == 'd')
+           continue;
+
+         for (i = span_start; i + 8 < span_end; i += 4)
+           {
+             bfd_vma vma = (section->output_section->vma
+                            + section->output_offset
+                            + i);
+             bfd_vma veneer_i;
+
+             if (_bfd_aarch64_erratum_843419_p
+                 (contents, vma, i, span_end, &veneer_i))
+               {
+                 uint32_t insn = bfd_getl32 (contents + veneer_i);
+
+                 if (!_bfd_aarch64_erratum_843419_fixup (insn, i, veneer_i,
+                                                         section, info))
+                   return FALSE;
+               }
+           }
+       }
+
+      if (elf_section_data (section)->this_hdr.contents == NULL)
+       free (contents);
+    }
+  while (0);
+
+  return TRUE;
+}
+
+static bfd_boolean
+section_start_symbol (bfd *abfd ATTRIBUTE_UNUSED, asection *section,
+                     void *valp)
+{
+  return section->vma == *(bfd_vma *)valp;
+}
+
+/* Capability format functions.  */
+
+static unsigned
+exponent (uint64_t len)
+{
+#define CAP_MAX_EXPONENT 50
+  /* Size is a 65 bit value, so there's an implicit 0 MSB.  */
+  unsigned zeroes = __builtin_clzl (len) + 1;
+
+  /* All bits up to and including CAP_MW - 2 are zero.  */
+  if (CAP_MAX_EXPONENT < zeroes)
+    return (unsigned) -1;
+  else
+    return CAP_MAX_EXPONENT - zeroes;
+#undef CAP_MAX_EXPONENT
+}
+
+#define ONES(x)         ((1ULL << (x)) - 1)
+#define ALIGN_UP(x, a)  (((x) + ONES (a)) & (~ONES (a)))
+
+static bfd_boolean
+c64_valid_cap_range (bfd_vma *basep, bfd_vma *limitp, unsigned *alignmentp)
+{
+  bfd_vma base = *basep, size = *limitp - *basep;
+
+  unsigned e, old_e;
+  *alignmentp = 1;
+
+  if ((e = exponent (size)) == (unsigned) -1)
+    return TRUE;
+
+  size = ALIGN_UP (size, e + 3);
+  old_e = e;
+  e = exponent (size);
+  if (old_e != e)
+    size = ALIGN_UP (size, e + 3);
+
+  base = ALIGN_UP (base, e + 3);
+
+  *alignmentp = e+3;
+  if (base == *basep && *limitp == base + size)
+    return TRUE;
+
+  *basep = base;
+  *limitp = base + size;
+  return FALSE;
+}
+
+struct sec_change_queue
+{
+  asection *sec;
+  struct sec_change_queue *next;
+};
+
+/* Queue up the change, sorted in order of the output section vma.  */
+
+static void
+queue_section_padding (struct sec_change_queue **queue, asection *sec)
+{
+  struct sec_change_queue *q = *queue, *last_q = NULL, *n;
+
+  while (q != NULL)
+    {
+      if (q->sec->vma > sec->vma)
+       break;
+      last_q = q;
+      q = q->next;
+    }
+
+  n = bfd_zmalloc (sizeof (struct sec_change_queue));
+
+  if (last_q == NULL)
+    *queue = n;
+  else
+    {
+      n->next = q;
+      last_q->next = n;
+    }
+
+  n->sec = sec;
+}
+
+/* Check if the bounds covering all sections between LOW_SEC and HIGH_SEC will
+   get rounded off in the Morello capability format and if it does, queue up a
+   change to fix up the section layout.  */
+static inline void
+record_section_change (asection *sec, struct sec_change_queue **queue)
+{
+  bfd_vma low = sec->vma;
+  bfd_vma high = sec->vma + sec->size;
+  unsigned alignment;
+
+  if (!c64_valid_cap_range (&low, &high, &alignment))
+    queue_section_padding (queue, sec);
+}
+
+/* Make sure that all capabilities that refer to sections have bounds that
+   won't overlap with neighbouring sections.  This is needed in two specific
+   cases.  The first case is that of PCC, which needs to span across all
+   readonly sections as well as the GOT and PLT sections in the output binary.
+   The second case is that of linker and ldscript defined symbols that indicate
+   start and/or end of sections and/or zero-sized symbols.
+
+   In both cases, overlap of capability bounds are avoided by aligning the base
+   of the section and if necessary, adding a pad at the end of the section so
+   that the section following it starts only after the pad.  */
+
+static bfd_vma pcc_low;
+static bfd_vma pcc_high;
+void
+elfNN_c64_resize_sections (bfd *output_bfd, struct bfd_link_info *info,
+                          void (*c64_pad_section) (asection *, bfd_vma),
+                          void (*layout_sections_again) (void))
+{
+  asection *sec, *pcc_low_sec = NULL, *pcc_high_sec = NULL;
+  struct elf_aarch64_link_hash_table *htab = elf_aarch64_hash_table (info);
+  bfd_vma low = (bfd_vma) -1, high = 0;
+  bfd *input_bfd;
+
+  htab->layout_sections_again = layout_sections_again;
+
+  if (!htab->c64_output)
+    return;
+
+  struct sec_change_queue *queue = NULL;
+
+  /* First, walk through all the relocations to find those referring to linker
+     defined and ldscript defined symbols since we set their range to their
+     output sections.  */
+  for (input_bfd = info->input_bfds;
+       htab->c64_rel && input_bfd != NULL; input_bfd = input_bfd->link.next)
+    {
+      Elf_Internal_Shdr *symtab_hdr;
+
+      symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+      if (symtab_hdr->sh_info == 0)
+       continue;
+
+      for (sec = input_bfd->sections; sec != NULL; sec = sec->next)
+       {
+         Elf_Internal_Rela *irelaend, *irela;
+
+         /* If there aren't any relocs, then there's nothing more to do.  */
+         if ((sec->flags & SEC_RELOC) == 0 || sec->reloc_count == 0)
+           continue;
+
+         irela = _bfd_elf_link_read_relocs (input_bfd, sec, NULL, NULL,
+                                            info->keep_memory);
+         if (irela == NULL)
+           continue;
+
+         /* Now examine each relocation.  */
+         irelaend = irela + sec->reloc_count;
+         for (; irela < irelaend; irela++)
+           {
+             unsigned int r_indx;
+             struct elf_link_hash_entry *h;
+             int e_indx;
+             asection *os;
+
+             r_indx = ELFNN_R_SYM (irela->r_info);
+
+             /* Linker defined or linker script defined symbols are always in
+                the symbol hash.  */
+             if (r_indx < symtab_hdr->sh_info)
+               continue;
+
+             e_indx = r_indx - symtab_hdr->sh_info;
+             h = elf_sym_hashes (input_bfd)[e_indx];
+
+             /* XXX Does this ever happen?  */
+             if (h == NULL)
+               continue;
+
+             os = h->root.u.def.section->output_section;
+
+             if (h->root.linker_def)
+               record_section_change (os, &queue);
+             else if (h->root.ldscript_def)
+               {
+                 const char *name = h->root.root.string;
+                 size_t len = strlen (name);
+
+                 if (len > 8 && name[0] == '_' && name[1] == '_'
+                     && (!strncmp (name + 2, "start_", 6)
+                         || !strcmp (name + len - 6, "_start")))
+                   {
+                     bfd_vma value = os->vma + os->size;
+
+                     os = bfd_sections_find_if (info->output_bfd,
+                                                section_start_symbol, &value);
+
+                     if (os != NULL)
+                       record_section_change (os, &queue);
+                   }
+                 /* XXX We're overfitting here because the offset of H within
+                    the output section is not yet resolved and ldscript
+                    defined symbols do not have input section information.  */
+                 else
+                   record_section_change (os, &queue);
+               }
+           }
+       }
+    }
+
+  /* Next, walk through output sections to find the PCC span and add a padding
+     at the end to ensure that PCC bounds don't bleed into neighbouring
+     sections.  For now PCC needs to encompass all code sections, .got, .plt
+     and .got.plt.  */
+  for (sec = output_bfd->sections; sec != NULL; sec = sec->next)
+    {
+      /* XXX This is a good place to figure out if there are any readable or
+        writable sections in the PCC range that are not in the list of
+        sections we want the PCC to span and then warn the user of it.  */
+
+#define NOT_OP_SECTION(s) ((s) == NULL || (s)->output_section != sec)
+
+      if ((sec->flags & SEC_READONLY) == 0
+         && NOT_OP_SECTION (htab->root.sgotplt)
+         && NOT_OP_SECTION (htab->root.igotplt)
+         && NOT_OP_SECTION (htab->root.sgot)
+         && NOT_OP_SECTION (htab->root.splt)
+         && NOT_OP_SECTION (htab->root.iplt)
+         && (sec->vma < info->relro_start
+             || sec->vma >= info->relro_end))
+       continue;
+      if ((sec->flags & SEC_ALLOC) == 0)
+       continue;
+
+      if (sec->vma < low)
+       {
+         low = sec->vma;
+         pcc_low_sec = sec;
+       }
+      if (sec->vma + sec->size > high)
+       {
+         high = sec->vma + sec->size;
+         pcc_high_sec = sec;
+       }
+
+#undef NOT_OP_SECTION
+    }
+
+  /* Sequentially add alignment and padding as required.  We also need to
+     account for the PCC-related alignment and padding here since its
+     requirements could change based on the range of sections it encompasses
+     and whether they need to be padded or aligned.  */
+  while (queue)
+    {
+      unsigned align = 0;
+      bfd_vma padding = 0;
+
+      low = queue->sec->vma;
+      high = queue->sec->vma + queue->sec->size;
+
+      if (!c64_valid_cap_range (&low, &high, &align))
+       {
+         if (queue->sec->alignment_power < align)
+           queue->sec->alignment_power = align;
+
+         padding = high - queue->sec->vma - queue->sec->size;
+
+         if (queue->sec != pcc_high_sec)
+           {
+             c64_pad_section (queue->sec, padding);
+             padding = 0;
+           }
+       }
+
+      /* If we have crossed all sections within the PCC range, set up alignment
+         and padding for the PCC range.  */
+      if (pcc_high_sec != NULL && pcc_low_sec != NULL
+         && (queue->next == NULL
+             || queue->next->sec->vma > pcc_high_sec->vma))
+       {
+         /* Layout sections since it affects the final range of PCC.  */
+         (*htab->layout_sections_again) ();
 
-         for (i = span_start; i + 8 < span_end; i += 4)
-           {
-             bfd_vma vma = (section->output_section->vma
-                            + section->output_offset
-                            + i);
-             bfd_vma veneer_i;
+         pcc_low = pcc_low_sec->vma;
+         pcc_high = pcc_high_sec->vma + pcc_high_sec->size + padding;
 
-             if (_bfd_aarch64_erratum_843419_p
-                 (contents, vma, i, span_end, &veneer_i))
-               {
-                 uint32_t insn = bfd_getl32 (contents + veneer_i);
+         if (!c64_valid_cap_range (&pcc_low, &pcc_high, &align))
+           {
+             if (pcc_low_sec->alignment_power < align)
+               pcc_low_sec->alignment_power = align;
 
-                 if (!_bfd_aarch64_erratum_843419_fixup (insn, i, veneer_i,
-                                                         section, info))
-                   return FALSE;
-               }
+             padding = pcc_high - pcc_high_sec->vma - pcc_high_sec->size;
+             c64_pad_section (pcc_high_sec, padding);
            }
        }
 
-      if (elf_section_data (section)->this_hdr.contents == NULL)
-       free (contents);
+      (*htab->layout_sections_again) ();
+
+      struct sec_change_queue *queue_free = queue;
+
+      queue = queue->next;
+      free (queue_free);
     }
-  while (0);
 
-  return TRUE;
+  if (pcc_low_sec)
+    {
+      if (!pcc_high_sec)
+       abort ();
+      pcc_low = pcc_low_sec->vma;
+      pcc_high = pcc_high_sec->vma + pcc_high_sec->size;
+    }
 }
 
-
 /* Determine and set the size of the stub section for a final link.
 
    The basic idea here is to examine all the relocations looking for
-   PC-relative calls to a target that is unreachable with a "bl"
-   instruction.  */
+   PC-relative calls to a target that either needs a PE state change (A64 to
+   C64 or vice versa) or in case of unconditional branches (B/BL), is
+   unreachable.  */
 
 bfd_boolean
 elfNN_aarch64_size_stubs (bfd *output_bfd,
@@ -4164,15 +5142,21 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
 
       for (input_bfd = info->input_bfds;
           input_bfd != NULL; input_bfd = input_bfd->link.next)
-       if (!_bfd_aarch64_erratum_835769_scan (input_bfd, info,
-                                              &num_erratum_835769_fixes))
-         return FALSE;
+       {
+         if (!is_aarch64_elf (input_bfd)
+             || (input_bfd->flags & BFD_LINKER_CREATED) != 0)
+           continue;
+
+         if (!_bfd_aarch64_erratum_835769_scan (input_bfd, info,
+                                                &num_erratum_835769_fixes))
+           return FALSE;
+       }
 
       _bfd_aarch64_resize_stubs (htab);
       (*htab->layout_sections_again) ();
     }
 
-  if (htab->fix_erratum_843419)
+  if (htab->fix_erratum_843419 != ERRAT_NONE)
     {
       bfd *input_bfd;
 
@@ -4182,6 +5166,10 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
        {
          asection *section;
 
+         if (!is_aarch64_elf (input_bfd)
+             || (input_bfd->flags & BFD_LINKER_CREATED) != 0)
+           continue;
+
          for (section = input_bfd->sections;
               section != NULL;
               section = section->next)
@@ -4202,7 +5190,10 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
        {
          Elf_Internal_Shdr *symtab_hdr;
          asection *section;
-         Elf_Internal_Sym *local_syms = NULL;
+
+         if (!is_aarch64_elf (input_bfd)
+             || (input_bfd->flags & BFD_LINKER_CREATED) != 0)
+           continue;
 
          /* We'll need the symbol table in a second.  */
          symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
@@ -4241,7 +5232,7 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
              for (; irela < irelaend; irela++)
                {
                  unsigned int r_type, r_indx;
-                 enum elf_aarch64_stub_type stub_type;
+                 enum elf_aarch64_stub_type stub_type = aarch64_stub_none;
                  struct elf_aarch64_stub_hash_entry *stub_entry;
                  asection *sym_sec;
                  bfd_vma sym_value;
@@ -4252,6 +5243,8 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
                  const asection *id_sec;
                  unsigned char st_type;
                  bfd_size_type len;
+                 unsigned branch_to_c64 = FALSE;
+                 const char *suffix;
 
                  r_type = ELFNN_R_TYPE (irela->r_info);
                  r_indx = ELFNN_R_SYM (irela->r_info);
@@ -4267,8 +5260,7 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
 
                  /* Only look for stubs on unconditional branch and
                     branch and link instructions.  */
-                 if (r_type != (unsigned int) AARCH64_R (CALL26)
-                     && r_type != (unsigned int) AARCH64_R (JUMP26))
+                 if (!aarch64_branch_reloc_p (r_type))
                    continue;
 
                  /* Now determine the call target, its name, value,
@@ -4281,24 +5273,18 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
                  if (r_indx < symtab_hdr->sh_info)
                    {
                      /* It's a local symbol.  */
-                     Elf_Internal_Sym *sym;
-                     Elf_Internal_Shdr *hdr;
+                     Elf_Internal_Sym *sym =
+                       bfd_sym_from_r_symndx (&htab->root.sym_cache,
+                                              input_bfd, r_indx);
+                     if (sym == NULL)
+                       goto error_ret_free_internal;
 
-                     if (local_syms == NULL)
-                       {
-                         local_syms
-                           = (Elf_Internal_Sym *) symtab_hdr->contents;
-                         if (local_syms == NULL)
-                           local_syms
-                             = bfd_elf_get_elf_syms (input_bfd, symtab_hdr,
-                                                     symtab_hdr->sh_info, 0,
-                                                     NULL, NULL, NULL);
-                         if (local_syms == NULL)
-                           goto error_ret_free_internal;
-                       }
+                     branch_to_c64 |= (sym->st_target_internal
+                                       & ST_BRANCH_TO_C64);
+
+                     Elf_Internal_Shdr *hdr =
+                       elf_elfsections (input_bfd)[sym->st_shndx];
 
-                     sym = local_syms + r_indx;
-                     hdr = elf_elfsections (input_bfd)[sym->st_shndx];
                      sym_sec = hdr->bfd_section;
                      if (!sym_sec)
                        /* This is an undefined symbol.  It can never
@@ -4315,10 +5301,16 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
                        = bfd_elf_string_from_elf_section (input_bfd,
                                                           symtab_hdr->sh_link,
                                                           sym->st_name);
+
+                     /* Get the interworking stub if needed.  */
+                     stub_type = aarch64_interwork_stub (r_type,
+                                                         branch_to_c64);
                    }
                  else
                    {
                      int e_indx;
+                     struct elf_aarch64_link_hash_table *globals =
+                       elf_aarch64_hash_table (info);
 
                      e_indx = r_indx - symtab_hdr->sh_info;
                      hash = ((struct elf_aarch64_link_hash_entry *)
@@ -4329,11 +5321,19 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
                        hash = ((struct elf_aarch64_link_hash_entry *)
                                hash->root.root.u.i.link);
 
+                     /* Static executable.  */
+                     if (globals->root.splt == NULL || hash == NULL
+                         || hash->root.plt.offset == (bfd_vma) - 1)
+                       {
+                         branch_to_c64 |= (hash->root.target_internal
+                                           & ST_BRANCH_TO_C64);
+                         stub_type = aarch64_interwork_stub (r_type,
+                                                             branch_to_c64);
+                       }
+
                      if (hash->root.root.type == bfd_link_hash_defined
                          || hash->root.root.type == bfd_link_hash_defweak)
                        {
-                         struct elf_aarch64_link_hash_table *globals =
-                           elf_aarch64_hash_table (info);
                          sym_sec = hash->root.root.u.def.section;
                          sym_value = hash->root.root.u.def.value;
                          /* For a destination in a shared library,
@@ -4364,8 +5364,6 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
                             target address to decide whether a long
                             branch stub is needed.
                             For absolute code, they cannot be handled.  */
-                         struct elf_aarch64_link_hash_table *globals =
-                           elf_aarch64_hash_table (info);
 
                          if (globals->root.splt != NULL && hash != NULL
                              && hash->root.plt.offset != (bfd_vma) - 1)
@@ -4391,8 +5389,10 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
                    }
 
                  /* Determine what (if any) linker stub is needed.  */
-                 stub_type = aarch64_type_of_stub (section, irela, sym_sec,
-                                                   st_type, destination);
+                 if (stub_type == aarch64_stub_none)
+                   stub_type = aarch64_type_of_stub (section, irela, sym_sec,
+                                                     st_type, destination);
+
                  if (stub_type == aarch64_stub_none)
                    continue;
 
@@ -4401,7 +5401,7 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
 
                  /* Get the name of this stub.  */
                  stub_name = elfNN_aarch64_stub_name (id_sec, sym_sec, hash,
-                                                      irela);
+                                                      irela, stub_type);
                  if (!stub_name)
                    goto error_ret_free_internal;
 
@@ -4412,6 +5412,14 @@ 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;
+
+                     /* Set LSB for A64 to C64 branch.  */
+                     if (branch_to_c64)
+                       stub_entry->target_value |= 1;
+
                      continue;
                    }
 
@@ -4424,14 +5432,21 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
                    }
 
                  stub_entry->target_value = sym_value + irela->r_addend;
+                 /* Set LSB for A64 to C64 branch.  */
+                 if (branch_to_c64)
+                   stub_entry->target_value |= 1;
+
                  stub_entry->target_section = sym_sec;
                  stub_entry->stub_type = stub_type;
                  stub_entry->h = hash;
                  stub_entry->st_type = st_type;
 
+                 suffix = aarch64_lookup_stub_type_suffix (stub_type);
+
                  if (sym_name == NULL)
                    sym_name = "unnamed";
-                 len = sizeof (STUB_ENTRY_NAME) + strlen (sym_name);
+                 len = (sizeof (STUB_ENTRY_NAME) + strlen (sym_name)
+                        + strlen (suffix));
                  stub_entry->output_name = bfd_alloc (htab->stub_bfd, len);
                  if (stub_entry->output_name == NULL)
                    {
@@ -4440,7 +5455,7 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
                    }
 
                  snprintf (stub_entry->output_name, len, STUB_ENTRY_NAME,
-                           sym_name);
+                           sym_name, suffix);
 
                  stub_changed = TRUE;
                }
@@ -4463,7 +5478,7 @@ elfNN_aarch64_size_stubs (bfd *output_bfd,
 
   return TRUE;
 
-error_ret_free_local:
+ error_ret_free_local:
   return FALSE;
 }
 
@@ -4507,8 +5522,16 @@ elfNN_aarch64_build_stubs (struct bfd_link_info *info)
 
   /* Build the stubs as directed by the stub hash table.  */
   table = &htab->stub_hash_table;
+
+  bfd_error_type save_error = bfd_get_error ();
+  bfd_set_error (bfd_error_no_error);
   bfd_hash_traverse (table, aarch64_build_one_stub, info);
 
+  if (bfd_get_error () != bfd_error_no_error)
+    return FALSE;
+
+  bfd_set_error (save_error);
+
   return TRUE;
 }
 
@@ -4516,12 +5539,28 @@ elfNN_aarch64_build_stubs (struct bfd_link_info *info)
 /* Add an entry to the code/data map for section SEC.  */
 
 static void
-elfNN_aarch64_section_map_add (asection *sec, char type, bfd_vma vma)
+elfNN_aarch64_section_map_add (bfd *abfd, asection *sec, char type,
+                              bfd_vma vma)
 {
   struct _aarch64_elf_section_data *sec_data =
     elf_aarch64_section_data (sec);
   unsigned int newidx;
 
+  /* The aarch64 section hook was not called for this section.  */
+  if (!sec_data->elf.is_target_section_data)
+    {
+      struct _aarch64_elf_section_data *newdata =
+       bfd_zalloc (abfd, sizeof (*newdata));
+
+      if (newdata == NULL)
+       return;
+
+      newdata->elf = sec_data->elf;
+      newdata->elf.is_target_section_data = TRUE;
+      free (sec_data);
+      sec->used_by_bfd = sec_data = newdata;
+    }
+
   if (sec_data->map == NULL)
     {
       sec_data->map = bfd_malloc (sizeof (elf_aarch64_section_map));
@@ -4548,7 +5587,7 @@ elfNN_aarch64_section_map_add (asection *sec, char type, bfd_vma vma)
 
 /* Initialise maps of insn/data for input BFDs.  */
 void
-bfd_elfNN_aarch64_init_maps (bfd *abfd)
+bfd_elfNN_aarch64_init_maps (bfd *abfd, struct bfd_link_info *info)
 {
   Elf_Internal_Sym *isymbuf;
   Elf_Internal_Shdr *hdr;
@@ -4558,6 +5597,9 @@ bfd_elfNN_aarch64_init_maps (bfd *abfd)
   if (!is_aarch64_elf (abfd))
     return;
 
+  if (elf_aarch64_tdata (abfd)->secmaps_initialised)
+    return;
+
   if ((abfd->flags & DYNAMIC) != 0)
    return;
 
@@ -4573,6 +5615,8 @@ bfd_elfNN_aarch64_init_maps (bfd *abfd)
   if (isymbuf == NULL)
     return;
 
+  struct elf_aarch64_link_hash_table *htab = elf_aarch64_hash_table ((info));
+
   for (i = 0; i < localsyms; i++)
     {
       Elf_Internal_Sym *isym = &isymbuf[i];
@@ -4587,9 +5631,71 @@ bfd_elfNN_aarch64_init_maps (bfd *abfd)
 
          if (bfd_is_aarch64_special_symbol_name
              (name, BFD_AARCH64_SPECIAL_SYM_TYPE_MAP))
-           elfNN_aarch64_section_map_add (sec, name[1], isym->st_value);
+           {
+             elfNN_aarch64_section_map_add (abfd, sec, name[1],
+                                            isym->st_value);
+             if (!htab->c64_output && name[1] == 'c')
+               htab->c64_output = TRUE;
+           }
+       }
+    }
+  elf_aarch64_tdata (abfd)->secmaps_initialised = TRUE;
+}
+
+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);
+
+  /* Set up plt stubs in case we need C64 PLT.  Override BTI/PAC since they're
+     not compatible.  PLT stub sizes are the same as the default ones.  */
+  if (globals->c64_rel)
+    {
+      if (plt_type != PLT_NORMAL)
+       _bfd_error_handler
+         (_("ignoring C64-incompatible extensions: %s"),
+          (plt_type == PLT_BTI_PAC ? "BTI, PAC"
+           : plt_type == PLT_BTI ? "BTI" : "PAC"));
+
+      globals->plt0_entry = elfNN_c64_small_plt0_entry;
+      globals->plt_entry = elfNN_c64_small_plt_entry;
+      return;
+    }
+
+  if (plt_type == PLT_BTI_PAC)
+    {
+      globals->plt0_entry = elfNN_aarch64_small_plt0_bti_entry;
+
+      /* Only in ET_EXEC we need PLTn with BTI.  */
+      if (bfd_link_pde (link_info))
+       {
+         globals->plt_entry_size = PLT_BTI_PAC_SMALL_ENTRY_SIZE;
+         globals->plt_entry = elfNN_aarch64_small_plt_bti_pac_entry;
+       }
+      else
+       {
+         globals->plt_entry_size = PLT_PAC_SMALL_ENTRY_SIZE;
+         globals->plt_entry = elfNN_aarch64_small_plt_pac_entry;
+       }
+    }
+  else if (plt_type == PLT_BTI)
+    {
+      globals->plt0_entry = elfNN_aarch64_small_plt0_bti_entry;
+
+      /* 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;
        }
     }
+  else if (plt_type == PLT_PAC)
+    {
+      globals->plt_entry_size = PLT_PAC_SMALL_ENTRY_SIZE;
+      globals->plt_entry = elfNN_aarch64_small_plt_pac_entry;
+    }
 }
 
 /* Set option values needed during linking.  */
@@ -4599,21 +5705,39 @@ bfd_elfNN_aarch64_set_options (struct bfd *output_bfd,
                               int no_enum_warn,
                               int no_wchar_warn, int pic_veneer,
                               int fix_erratum_835769,
-                              int fix_erratum_843419,
-                              int no_apply_dynamic_relocs)
+                              erratum_84319_opts fix_erratum_843419,
+                              int no_apply_dynamic_relocs,
+                              aarch64_bti_pac_info bp_info)
 {
   struct elf_aarch64_link_hash_table *globals;
 
   globals = elf_aarch64_hash_table (link_info);
   globals->pic_veneer = pic_veneer;
   globals->fix_erratum_835769 = fix_erratum_835769;
+  /* If the default options are used, then ERRAT_ADR will be set by default
+     which will enable the ADRP->ADR workaround for the erratum 843419
+     workaround.  */
   globals->fix_erratum_843419 = fix_erratum_843419;
-  globals->fix_erratum_843419_adr = TRUE;
   globals->no_apply_dynamic_relocs = no_apply_dynamic_relocs;
+  globals->c64_rel = 0;
 
   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;
+  elf_aarch64_tdata (output_bfd)->secmaps_initialised = FALSE;
 }
 
 static bfd_vma
@@ -4669,12 +5793,19 @@ aarch64_calculate_got_entry_vma (struct elf_link_hash_entry *h,
 
 static bfd_reloc_code_real_type
 aarch64_tls_transition_without_check (bfd_reloc_code_real_type r_type,
-                                     struct elf_link_hash_entry *h)
+                                     struct bfd_link_info *info,
+                                     struct elf_link_hash_entry *h,
+                                     bfd_boolean morello_reloc)
 {
   bfd_boolean is_local = h == NULL;
 
   switch (r_type)
     {
+    case BFD_RELOC_MORELLO_TLSDESC_ADR_PAGE20:
+      return (is_local || !bfd_link_pic (info)
+             ? BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1
+             : r_type);
+
     case BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21:
     case BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21:
       return (is_local
@@ -4706,6 +5837,10 @@ aarch64_tls_transition_without_check (bfd_reloc_code_real_type r_type,
              ? BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2
              : BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G1);
 
+    case BFD_RELOC_MORELLO_TLSDESC_LD128_LO12:
+      return ((is_local || !bfd_link_pie (info)
+              ? BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC : r_type));
+
     case BFD_RELOC_AARCH64_TLSDESC_LDNN_LO12_NC:
     case BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC:
       return (is_local
@@ -4726,10 +5861,19 @@ aarch64_tls_transition_without_check (bfd_reloc_code_real_type r_type,
              ? BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12
              : BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_PREL19);
 
-    case BFD_RELOC_AARCH64_TLSDESC_ADD:
+    case BFD_RELOC_MORELLO_TLSDESC_CALL:
+      return ((is_local || !bfd_link_pie (info))
+             ? BFD_RELOC_AARCH64_NONE : r_type);
+
     case BFD_RELOC_AARCH64_TLSDESC_ADD_LO12:
+      if (morello_reloc && !is_local && bfd_link_pie (info))
+       return r_type;
+      /* Fall through.  */
+    case BFD_RELOC_AARCH64_TLSDESC_ADD:
     case BFD_RELOC_AARCH64_TLSDESC_CALL:
-      /* Instructions with these relocations will become NOPs.  */
+      /* Instructions with these relocations will be fully resolved during the
+         transition into either a NOP in the A64 case or movk and add in
+        C64.  */
       return BFD_RELOC_AARCH64_NONE;
 
     case BFD_RELOC_AARCH64_TLSLD_ADD_LO12_NC:
@@ -4772,6 +5916,10 @@ aarch64_reloc_got_type (bfd_reloc_code_real_type r_type)
     case BFD_RELOC_AARCH64_MOVW_GOTOFF_G1:
       return GOT_NORMAL;
 
+    case BFD_RELOC_MORELLO_ADR_GOT_PAGE:
+    case BFD_RELOC_MORELLO_LD128_GOT_LO12_NC:
+      return GOT_CAP;
+
     case BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC:
     case BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21:
     case BFD_RELOC_AARCH64_TLSGD_ADR_PREL21:
@@ -4782,6 +5930,11 @@ aarch64_reloc_got_type (bfd_reloc_code_real_type r_type)
     case BFD_RELOC_AARCH64_TLSLD_ADR_PREL21:
       return GOT_TLS_GD;
 
+    case BFD_RELOC_MORELLO_TLSDESC_ADR_PAGE20:
+    case BFD_RELOC_MORELLO_TLSDESC_CALL:
+    case BFD_RELOC_MORELLO_TLSDESC_LD128_LO12:
+      return GOT_TLSDESC_GD | GOT_CAP;
+
     case BFD_RELOC_AARCH64_TLSDESC_ADD:
     case BFD_RELOC_AARCH64_TLSDESC_ADD_LO12:
     case BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21:
@@ -4812,18 +5965,22 @@ aarch64_reloc_got_type (bfd_reloc_code_real_type r_type)
 static bfd_boolean
 aarch64_can_relax_tls (bfd *input_bfd,
                       struct bfd_link_info *info,
-                      bfd_reloc_code_real_type r_type,
+                      const Elf_Internal_Rela *rel,
                       struct elf_link_hash_entry *h,
                       unsigned long r_symndx)
 {
   unsigned int symbol_got_type;
   unsigned int reloc_got_type;
 
-  if (! IS_AARCH64_TLS_RELAX_RELOC (r_type))
+  bfd_reloc_code_real_type bfd_r_type
+    = elfNN_aarch64_bfd_reloc_from_type (input_bfd,
+                                        ELFNN_R_TYPE (rel->r_info));
+
+  if (! IS_AARCH64_TLS_RELAX_RELOC (bfd_r_type))
     return FALSE;
 
   symbol_got_type = elfNN_aarch64_symbol_got_type (h, input_bfd, r_symndx);
-  reloc_got_type = aarch64_reloc_got_type (r_type);
+  reloc_got_type = aarch64_reloc_got_type (bfd_r_type);
 
   if (symbol_got_type == GOT_TLS_IE && GOT_TLS_GD_ANY_P (reloc_got_type))
     return TRUE;
@@ -4843,17 +6000,29 @@ aarch64_can_relax_tls (bfd *input_bfd,
 static bfd_reloc_code_real_type
 aarch64_tls_transition (bfd *input_bfd,
                        struct bfd_link_info *info,
-                       unsigned int r_type,
+                       const Elf_Internal_Rela *rel,
                        struct elf_link_hash_entry *h,
                        unsigned long r_symndx)
 {
   bfd_reloc_code_real_type bfd_r_type
-    = elfNN_aarch64_bfd_reloc_from_type (input_bfd, r_type);
+    = elfNN_aarch64_bfd_reloc_from_type (input_bfd,
+                                        ELFNN_R_TYPE (rel->r_info));
+
+  if (! aarch64_can_relax_tls (input_bfd, info, rel, h, r_symndx))
+    return bfd_r_type;
+
+  bfd_boolean morello_reloc = (bfd_r_type == BFD_RELOC_AARCH64_TLSDESC_ADD_LO12
+                              && (ELFNN_R_TYPE (rel[1].r_info)
+                                  == MORELLO_R (TLSDESC_CALL)));
 
-  if (! aarch64_can_relax_tls (input_bfd, info, bfd_r_type, h, r_symndx))
+  /* GD -> IE is not supported for Morello TLSDESC yet.  We do however allow
+     lowering of GD -> LE for static non-pie executables.  XXX It ought to be
+     safe to do this for A64 as well but it is not implemented yet.  */
+  if (h != NULL && morello_reloc && bfd_link_pie (info))
     return bfd_r_type;
 
-  return aarch64_tls_transition_without_check (bfd_r_type, h);
+  return aarch64_tls_transition_without_check (bfd_r_type, info, h,
+                                              morello_reloc);
 }
 
 /* Return the base VMA address which should be subtracted from real addresses
@@ -5064,15 +6233,24 @@ _bfd_aarch64_erratum_843419_branch_to_stub (struct bfd_hash_entry *gen_entry,
       || stub_entry->stub_type != aarch64_stub_erratum_843419_veneer)
     return TRUE;
 
-  insn = bfd_getl32 (contents + stub_entry->target_value);
-  bfd_putl32 (insn,
-             stub_entry->stub_sec->contents + stub_entry->stub_offset);
+  BFD_ASSERT (((htab->fix_erratum_843419 & ERRAT_ADRP) && stub_entry->stub_sec)
+             || (htab->fix_erratum_843419 & ERRAT_ADR));
+
+  /* Only update the stub section if we have one.  We should always have one if
+     we're allowed to use the ADRP errata workaround, otherwise it is not
+     required.  */
+  if (stub_entry->stub_sec)
+    {
+      insn = bfd_getl32 (contents + stub_entry->target_value);
+      bfd_putl32 (insn,
+                 stub_entry->stub_sec->contents + stub_entry->stub_offset);
+    }
 
   place = (section->output_section->vma + section->output_offset
           + 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 =
@@ -5080,14 +6258,16 @@ _bfd_aarch64_erratum_843419_branch_to_stub (struct bfd_hash_entry *gen_entry,
      ((bfd_vma) _bfd_aarch64_decode_adrp_imm (insn) << 12, 33)
      - (place & 0xfff));
 
-  if (htab->fix_erratum_843419_adr
+  if ((htab->fix_erratum_843419 & ERRAT_ADR)
       && (imm >= AARCH64_MIN_ADRP_IMM  && imm <= AARCH64_MAX_ADRP_IMM))
     {
-      insn = (_bfd_aarch64_reencode_adr_imm (AARCH64_ADR_OP, imm)
+      insn = (_bfd_aarch64_reencode_adr_imm (AARCH64_ADR_OP, imm, 0)
              | AARCH64_RT (insn));
       bfd_putl32 (insn, contents + stub_entry->adrp_offset);
+      /* Stub is not needed, don't map it out.  */
+      stub_entry->stub_type = aarch64_stub_none;
     }
-  else
+  else if (htab->fix_erratum_843419 & ERRAT_ADRP)
     {
       bfd_vma veneered_insn_loc;
       bfd_vma veneer_entry_loc;
@@ -5114,6 +6294,21 @@ _bfd_aarch64_erratum_843419_branch_to_stub (struct bfd_hash_entry *gen_entry,
       branch_insn |= branch_offset;
       bfd_putl32 (branch_insn, contents + stub_entry->target_value);
     }
+  else
+    {
+      abfd = stub_entry->target_section->owner;
+      _bfd_error_handler
+       (_("%pB: error: erratum 843419 immediate 0x%" BFD_VMA_FMT "x "
+          "out of range for ADR (input file too large) and "
+          "--fix-cortex-a53-843419=adr used.  Run the linker with "
+          "--fix-cortex-a53-843419=full instead"), abfd, imm);
+      bfd_set_error (bfd_error_bad_value);
+      /* This function is called inside a hashtable traversal and the error
+        handlers called above turn into non-fatal errors.  Which means this
+        case ld returns an exit code 0 and also produces a broken object file.
+        To prevent this, issue a hard abort.  */
+      BFD_FAIL ();
+    }
   return TRUE;
 }
 
@@ -5169,6 +6364,239 @@ aarch64_relocation_aginst_gp_p (bfd_reloc_code_real_type reloc)
          || reloc == BFD_RELOC_AARCH64_MOVW_GOTOFF_G1);
 }
 
+/* Build capability meta data, i.e. size and permissions for a capability.  */
+
+static bfd_vma
+cap_meta (size_t size, const asection *sec, bfd_boolean *guessed)
+{
+
+  if (size >= (1ULL << 56))
+    return (bfd_vma) -1;
+
+  /* N.b. We are only ever using this function for Morello.
+     Morello is little-endian.
+     We are returning a 64bit sized integer.
+     The format this metadata is supposed to fit is
+      | 56 bit length | 8 bit permissions |
+     This means that (in little endian layout) we need to put the 56 bit size
+     in the *lower* bits of the uint64_t.  */
+  uint64_t flags = 0;
+  if (sec->flags & SEC_CODE)
+    flags = 4;
+  else if (sec->flags & SEC_READONLY
+      || sec->flags & SEC_ROM)
+    flags = 1;
+  else if (sec->flags & SEC_ALLOC)
+    flags = 2;
+
+  /* We should usually be able to derive a valid set of permissions
+     from the section flags.  We know that when a relocation is against an
+     SHN_ABS symbol the section has no associated flags and we must guess.
+
+     As it stands we don't know of any other instances where we do not have
+     permission flags on a section.  We choose to allow instances that we do
+     not know of rather than abort on them so that if the guess is correct we
+     don't hamper anyone progressing.  */
+  if (flags == 0)
+    {
+      flags = 2;
+      *guessed = TRUE;
+    }
+
+  return size | (flags << 56);
+}
+
+enum c64_section_perm_type {
+    C64_SYM_UNKNOWN = 0,
+    C64_SYM_STANDARD,
+    C64_SYM_LINKER_DEF,
+    C64_SYM_LDSCRIPT_DEF,
+    C64_SYM_LDSCRIPT_START,
+};
+
+static enum c64_section_perm_type
+c64_symbol_section_adjustment (struct elf_link_hash_entry *h, bfd_vma value,
+                              asection *sym_sec, asection **ret_sec,
+                              struct bfd_link_info *info)
+{
+  if (!sym_sec)
+    return C64_SYM_UNKNOWN;
+
+  *ret_sec = sym_sec;
+  if (!h)
+    return C64_SYM_STANDARD;
+
+  /* Linker defined symbols are always at the start of the section they
+     track.  */
+  if (h->root.linker_def)
+    return C64_SYM_LINKER_DEF;
+  else if (h->root.ldscript_def)
+    {
+      const char *name = h->root.root.string;
+      size_t len = strlen (name);
+
+      bfd_vma size = sym_sec->size - (value - sym_sec->vma);
+      /* The special case: the symbol is at the end of the section.
+        This could either mean that it is an end symbol or it is the
+        start of the output section following the symbol.  We try to
+        guess if it is a start of the next section by reading its
+        name.  This is a compatibility hack, ideally linker scripts
+        should be written such that start symbols are defined within
+        the output section it intends to track.  */
+      if (size == 0
+         && (len > 8 && name[0] == '_' && name[1] == '_'
+             && (!strncmp (name + 2, "start_", 6)
+                 || !strcmp (name + len - 6, "_start"))))
+       {
+         asection *s = bfd_sections_find_if (info->output_bfd,
+                                             section_start_symbol,
+                                             &value);
+         if (s != NULL)
+           {
+             *ret_sec = s;
+             return C64_SYM_LDSCRIPT_START;
+           }
+       }
+      return C64_SYM_LDSCRIPT_DEF;
+    }
+  return C64_SYM_STANDARD;
+}
+
+static bfd_reloc_status_type
+c64_fixup_frag (bfd *input_bfd, struct bfd_link_info *info,
+               bfd_reloc_code_real_type bfd_r_type, Elf_Internal_Sym *sym,
+               struct elf_link_hash_entry *h, asection *sym_sec,
+               asection *reloc_sec, bfd_byte *frag_loc, bfd_vma value,
+               bfd_signed_vma addend, bfd_vma r_offset)
+{
+  BFD_ASSERT (h || sym);
+  bfd_vma size = sym ? sym->st_size : h->size;
+  asection *perm_sec = sym_sec;
+  bfd_boolean bounds_ok = FALSE;
+
+  const int aarch64_reloc_idx = bfd_r_type - BFD_RELOC_AARCH64_RELOC_START;
+  const char *reloc_name = elfNN_aarch64_howto_table[aarch64_reloc_idx].name;
+  const char *sym_name;
+
+  if (sym)
+    {
+      Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (input_bfd);
+      sym_name = (bfd_elf_string_from_elf_section (input_bfd,
+                                                  symtab_hdr->sh_link,
+                                                  sym->st_name));
+    }
+  else
+    sym_name = h->root.root.string;
+
+  if (size == 0 && sym_sec)
+    {
+      bounds_ok = TRUE;
+      enum c64_section_perm_type type
+       = c64_symbol_section_adjustment (h, value, sym_sec, &perm_sec, info);
+
+      switch (type)
+       {
+       case C64_SYM_STANDARD:
+         break;
+       case C64_SYM_LINKER_DEF:
+         size = perm_sec->output_section->size;
+         break;
+       case C64_SYM_LDSCRIPT_DEF:
+         size = perm_sec->size - (value - perm_sec->vma);
+         break;
+       case C64_SYM_LDSCRIPT_START:
+         size = perm_sec->size;
+         break;
+       default:
+         abort ();
+       }
+    }
+
+  /* Negative addends are not allowed for capability symbols.  */
+  if (addend < 0 || (bfd_vma) addend > size)
+    return bfd_reloc_outofrange;
+
+  bfd_vma base = value, limit = value + size;
+  unsigned align = 0;
+
+  if (!bounds_ok && !c64_valid_cap_range (&base, &limit, &align))
+    {
+      /* Just warn about this.  It's not a requirement that bounds on
+        objects should be precise, so there's no reason to error out on
+        such an object.  */
+      /* xgettext:c-format */
+      _bfd_error_handler
+       (_("%pB: capability range for '%s' may exceed object bounds"),
+        input_bfd, sym_name);
+    }
+
+  if (perm_sec && perm_sec->flags & SEC_CODE)
+    {
+      /* Any symbol pointing into an executable section gets bounds according
+        to PCC.  In this case the relocation is set up so that the value is
+        the base of the PCC, the addend is the offset from the PCC base to the
+        VA that we want, and the size is the length of the PCC range.
+        In this function we only use `value` to check the bounds make sense,
+        which is somewhat superfluous when we're using pcc_high and pcc_low
+        since we already enforced that in elfNN_c64_resize_sections.  No harm
+        in instead checking that the bounds on the object that were requested
+        made sense even if they were overridden because this symbol points
+        into an executable section.
+
+        `size` on the other hand is part of the fragment that we output to and
+        we need to change it in order to have functions that can access global
+        data or jump to other functions.  */
+      size = pcc_high - pcc_low;
+    }
+
+  if (perm_sec != NULL)
+    {
+      bfd_boolean permissions_guessed = FALSE;
+      bfd_vma frag = cap_meta (size, perm_sec, &permissions_guessed);
+
+      if (frag == (bfd_vma) -1)
+       return bfd_reloc_outofrange;
+
+      if (permissions_guessed)
+       {
+         _bfd_error_handler (_("%pB(%pA+%#" PRIx64 "): "
+                               "warning: relocation %s against symbol '%s' in "
+                               "section without permission flags '%s'.  "
+                               "Assuming Read-Write."),
+                             input_bfd, reloc_sec, r_offset, reloc_name,
+                             sym_name, perm_sec->name);
+       }
+
+      bfd_put_64 (input_bfd, frag, frag_loc);
+    }
+
+  return bfd_reloc_continue;
+}
+
+/* Given either a local symbol SYM or global symbol H, do we need to adjust
+   capability relocations against the symbol due to the fact that it points to
+   a code section?  */
+static bfd_boolean
+c64_symbol_adjust (struct elf_link_hash_entry *h,
+                  bfd_vma value, asection *sym_sec, struct bfd_link_info *info,
+                  bfd_vma *adjust_addr)
+{
+  asection *tmp_sec;
+  enum c64_section_perm_type type
+    = c64_symbol_section_adjustment (h, value, sym_sec, &tmp_sec, info);
+
+  if (type == C64_SYM_UNKNOWN)
+    return FALSE;
+
+  if (tmp_sec->flags & SEC_CODE)
+    {
+      *adjust_addr = pcc_low;
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
 /* Perform a relocation as part of a final link.  The input relocation type
    should be TLS relaxed.  */
 
@@ -5203,6 +6631,9 @@ elfNN_aarch64_final_link_relocate (reloc_howto_type *howto,
   bfd_vma orig_value = value;
   bfd_boolean resolved_to_zero;
   bfd_boolean abs_symbol_p;
+  Elf_Internal_Sym *isym = NULL;
+  bfd_boolean c64_rtype = FALSE;
+  bfd_boolean to_c64 = FALSE;
 
   globals = elf_aarch64_hash_table (info);
 
@@ -5222,8 +6653,17 @@ 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);
+
+  if (sym)
+    {
+      isym = bfd_sym_from_r_symndx (&globals->root.sym_cache, input_bfd,
+                                   r_symndx);
+      BFD_ASSERT (isym != NULL);
+      to_c64 = (isym->st_target_internal & ST_BRANCH_TO_C64) != 0;
+    }
+  else
+    to_c64 = (h->target_internal & ST_BRANCH_TO_C64) != 0;
 
 
   /* Since STT_GNU_IFUNC symbol must go through PLT, we handle
@@ -5272,7 +6712,7 @@ elfNN_aarch64_final_link_relocate (reloc_howto_type *howto,
       switch (bfd_r_type)
        {
        default:
-bad_ifunc_reloc:
+       bad_ifunc_reloc:
          if (h->root.root.string)
            name = h->root.root.string;
          else
@@ -5328,7 +6768,10 @@ bad_ifunc_reloc:
                  || bfd_link_executable (info))
                {
                  /* This symbol is resolved locally.  */
-                 outrel.r_info = ELFNN_R_INFO (0, AARCH64_R (IRELATIVE));
+                 outrel.r_info = (elf_aarch64_hash_entry (h)->got_type
+                                  == GOT_CAP
+                                  ? ELFNN_R_INFO (0, MORELLO_R (IRELATIVE))
+                                  : ELFNN_R_INFO (0, AARCH64_R (IRELATIVE)));
                  outrel.r_addend = (h->root.u.def.value
                                     + h->root.u.def.section->output_section->vma
                                     + h->root.u.def.section->output_offset);
@@ -5350,14 +6793,18 @@ bad_ifunc_reloc:
              return bfd_reloc_ok;
            }
          /* FALLTHROUGH */
+       case BFD_RELOC_MORELLO_CALL26:
+       case BFD_RELOC_MORELLO_JUMP26:
        case BFD_RELOC_AARCH64_CALL26:
        case BFD_RELOC_AARCH64_JUMP26:
-         value = _bfd_aarch64_elf_resolve_relocation (bfd_r_type, place, value,
+         value = _bfd_aarch64_elf_resolve_relocation (input_bfd, bfd_r_type,
+                                                      place, value,
                                                       signed_addend,
                                                       weak_undef_p);
          return _bfd_aarch64_elf_put_addend (input_bfd, hit_data, bfd_r_type,
                                              howto, value);
        case BFD_RELOC_AARCH64_ADR_GOT_PAGE:
+       case BFD_RELOC_MORELLO_ADR_GOT_PAGE:
        case BFD_RELOC_AARCH64_GOT_LD_PREL19:
        case BFD_RELOC_AARCH64_LD32_GOTPAGE_LO14:
        case BFD_RELOC_AARCH64_LD32_GOT_LO12_NC:
@@ -5366,6 +6813,7 @@ bad_ifunc_reloc:
        case BFD_RELOC_AARCH64_MOVW_GOTOFF_G1:
        case BFD_RELOC_AARCH64_LD64_GOTOFF_LO15:
        case BFD_RELOC_AARCH64_LD64_GOT_LO12_NC:
+       case BFD_RELOC_MORELLO_LD128_GOT_LO12_NC:
          base_got = globals->root.sgot;
          off = h->got.offset;
 
@@ -5384,13 +6832,13 @@ bad_ifunc_reloc:
                {
                  plt_index = ((h->plt.offset - globals->plt_header_size) /
                               globals->plt_entry_size);
-                 off = (plt_index + 3) * GOT_ENTRY_SIZE;
+                 off = (plt_index + 3) * GOT_ENTRY_SIZE (globals);
                  base_got = globals->root.sgotplt;
                }
              else
                {
                  plt_index = h->plt.offset / globals->plt_entry_size;
-                 off = plt_index * GOT_ENTRY_SIZE;
+                 off = plt_index * GOT_ENTRY_SIZE (globals);
                  base_got = globals->root.igotplt;
                }
 
@@ -5429,11 +6877,13 @@ bad_ifunc_reloc:
            addend = (globals->root.sgot->output_section->vma
                      + globals->root.sgot->output_offset);
 
-         value = _bfd_aarch64_elf_resolve_relocation (bfd_r_type, place, value,
+         value = _bfd_aarch64_elf_resolve_relocation (input_bfd, bfd_r_type,
+                                                      place, value,
                                                       addend, weak_undef_p);
          return _bfd_aarch64_elf_put_addend (input_bfd, hit_data, bfd_r_type, howto, value);
        case BFD_RELOC_AARCH64_ADD_LO12:
        case BFD_RELOC_AARCH64_ADR_HI21_PCREL:
+       case BFD_RELOC_MORELLO_ADR_HI20_PCREL:
          break;
        }
     }
@@ -5448,6 +6898,7 @@ bad_ifunc_reloc:
     case BFD_RELOC_AARCH64_TLSDESC_ADD:
     case BFD_RELOC_AARCH64_TLSDESC_CALL:
     case BFD_RELOC_AARCH64_TLSDESC_LDR:
+    case BFD_RELOC_MORELLO_TLSDESC_CALL:
       *unresolved_reloc_p = FALSE;
       return bfd_reloc_ok;
 
@@ -5558,6 +7009,8 @@ bad_ifunc_reloc:
        value += signed_addend;
       break;
 
+    case BFD_RELOC_MORELLO_CALL26:
+    case BFD_RELOC_MORELLO_JUMP26:
     case BFD_RELOC_AARCH64_CALL26:
     case BFD_RELOC_AARCH64_JUMP26:
       {
@@ -5585,17 +7038,30 @@ bad_ifunc_reloc:
           is too far away.  */
        struct elf_aarch64_stub_hash_entry *stub_entry = NULL;
 
+       enum elf_aarch64_stub_type c64_stub = aarch64_stub_none;
+
+       /* Figure out if we need an interworking stub and if yes, what
+          kind.  */
+       if (!via_plt_p)
+         c64_stub = aarch64_interwork_stub (r_type, to_c64);
+
        /* If the branch destination is directed to plt stub, "value" will be
           the final destination, otherwise we should plus signed_addend, it may
           contain non-zero value, for example call to local function symbol
           which are turned into "sec_sym + sec_off", and sec_off is kept in
           signed_addend.  */
-       if (! aarch64_valid_branch_p (via_plt_p ? value : value + signed_addend,
-                                     place))
-         /* The target is out of reach, so redirect the branch to
-            the local stub for this function.  */
-       stub_entry = elfNN_aarch64_get_stub_entry (input_section, sym_sec, h,
-                                                  rel, globals);
+       if (c64_stub != aarch64_stub_none
+           || (aarch64_branch_reloc_p (r_type)
+               && !aarch64_valid_branch_p ((via_plt_p ? value
+                                            : value + signed_addend), place)))
+         {
+           /* The target is out of reach, so redirect the branch to
+              the local stub for this function.  */
+           stub_entry = elfNN_aarch64_get_stub_entry (input_section, sym_sec,
+                                                      h, rel, globals,
+                                                      c64_stub);
+         }
+
        if (stub_entry != NULL)
          {
            value = (stub_entry->stub_offset
@@ -5607,7 +7073,8 @@ bad_ifunc_reloc:
            signed_addend = 0;
          }
       }
-      value = _bfd_aarch64_elf_resolve_relocation (bfd_r_type, place, value,
+      value = _bfd_aarch64_elf_resolve_relocation (input_bfd, bfd_r_type,
+                                                  place, value,
                                                   signed_addend, weak_undef_p);
       *unresolved_reloc_p = FALSE;
       break;
@@ -5617,8 +7084,11 @@ bad_ifunc_reloc:
     case BFD_RELOC_AARCH64_64_PCREL:
     case BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL:
     case BFD_RELOC_AARCH64_ADR_HI21_PCREL:
+    case BFD_RELOC_MORELLO_ADR_HI20_NC_PCREL:
+    case BFD_RELOC_MORELLO_ADR_HI20_PCREL:
     case BFD_RELOC_AARCH64_ADR_LO21_PCREL:
     case BFD_RELOC_AARCH64_LD_LO19_PCREL:
+    case BFD_RELOC_MORELLO_LD_LO17_PCREL:
     case BFD_RELOC_AARCH64_MOVW_PREL_G0:
     case BFD_RELOC_AARCH64_MOVW_PREL_G0_NC:
     case BFD_RELOC_AARCH64_MOVW_PREL_G1:
@@ -5643,6 +7113,43 @@ bad_ifunc_reloc:
          bfd_set_error (bfd_error_bad_value);
          return bfd_reloc_notsupported;
        }
+      value = _bfd_aarch64_elf_resolve_relocation (input_bfd, bfd_r_type,
+                                                  place, value,
+                                                  signed_addend,
+                                                  weak_undef_p);
+
+      if (bfd_r_type == BFD_RELOC_AARCH64_ADR_LO21_PCREL && isym != NULL
+         && isym->st_target_internal & ST_BRANCH_TO_C64)
+       value |= 1;
+      break;
+
+    case BFD_RELOC_MORELLO_BRANCH19:
+    case BFD_RELOC_MORELLO_TSTBR14:
+      c64_rtype = TRUE;
+      /* Fall through.  */
+    case BFD_RELOC_AARCH64_BRANCH19:
+    case BFD_RELOC_AARCH64_TSTBR14:
+      if (h && h->root.type == bfd_link_hash_undefined)
+       {
+         _bfd_error_handler
+           /* xgettext:c-format */
+           (_("%pB: conditional branch to undefined symbol `%s' "
+              "not allowed"), input_bfd, h->root.root.string);
+         bfd_set_error (bfd_error_bad_value);
+         return bfd_reloc_notsupported;
+       }
+       {
+         int howto_index = bfd_r_type - BFD_RELOC_AARCH64_RELOC_START;
+
+         if ((c64_rtype && !to_c64) || (!c64_rtype && to_c64))
+           {
+             _bfd_error_handler
+               /* xgettext:c-format */
+               (_("%pB: interworking not supported on relocation %s"),
+                input_bfd, elfNN_aarch64_howto_table[howto_index].name);
+             return bfd_reloc_notsupported;
+           }
+       }
       /* Fall through.  */
 
     case BFD_RELOC_AARCH64_16:
@@ -5650,7 +7157,6 @@ bad_ifunc_reloc:
     case BFD_RELOC_AARCH64_32:
 #endif
     case BFD_RELOC_AARCH64_ADD_LO12:
-    case BFD_RELOC_AARCH64_BRANCH19:
     case BFD_RELOC_AARCH64_LDST128_LO12:
     case BFD_RELOC_AARCH64_LDST16_LO12:
     case BFD_RELOC_AARCH64_LDST32_LO12:
@@ -5666,42 +7172,80 @@ bad_ifunc_reloc:
     case BFD_RELOC_AARCH64_MOVW_G2_NC:
     case BFD_RELOC_AARCH64_MOVW_G2_S:
     case BFD_RELOC_AARCH64_MOVW_G3:
-    case BFD_RELOC_AARCH64_TSTBR14:
-      value = _bfd_aarch64_elf_resolve_relocation (bfd_r_type, place, value,
+      value = _bfd_aarch64_elf_resolve_relocation (input_bfd, bfd_r_type,
+                                                  place, value,
                                                   signed_addend, weak_undef_p);
+      if (bfd_r_type == BFD_RELOC_AARCH64_ADD_LO12 && isym != NULL
+         && isym->st_target_internal & ST_BRANCH_TO_C64)
+       value |= 1;
+
       break;
 
     case BFD_RELOC_AARCH64_ADR_GOT_PAGE:
+    case BFD_RELOC_MORELLO_ADR_GOT_PAGE:
     case BFD_RELOC_AARCH64_GOT_LD_PREL19:
     case BFD_RELOC_AARCH64_LD32_GOTPAGE_LO14:
     case BFD_RELOC_AARCH64_LD32_GOT_LO12_NC:
     case BFD_RELOC_AARCH64_LD64_GOTPAGE_LO15:
     case BFD_RELOC_AARCH64_LD64_GOT_LO12_NC:
+    case BFD_RELOC_MORELLO_LD128_GOT_LO12_NC:
     case BFD_RELOC_AARCH64_LD64_GOTOFF_LO15:
     case BFD_RELOC_AARCH64_MOVW_GOTOFF_G0_NC:
     case BFD_RELOC_AARCH64_MOVW_GOTOFF_G1:
-      if (globals->root.sgot == NULL)
+      off = symbol_got_offset (input_bfd, h, r_symndx);
+      base_got = globals->root.sgot;
+
+      bfd_boolean c64_reloc =
+       (bfd_r_type == BFD_RELOC_MORELLO_LD128_GOT_LO12_NC
+        || bfd_r_type == BFD_RELOC_MORELLO_ADR_GOT_PAGE);
+
+      if (signed_addend != 0)
+       {
+         int howto_index = bfd_r_type - BFD_RELOC_AARCH64_RELOC_START;
+         _bfd_error_handler
+         /* xgettext:c-format */
+         (_("%pB: symbol plus addend can not be placed into the GOT "
+            "for relocation %s"),
+            input_bfd, elfNN_aarch64_howto_table[howto_index].name);
+         abort ();
+       }
+
+      if (base_got == NULL)
        BFD_ASSERT (h != NULL);
 
       relative_reloc = FALSE;
       if (h != NULL)
        {
          bfd_vma addend = 0;
+         bfd_vma frag_value;
 
          /* If a symbol is not dynamic and is not undefined weak, bind it
             locally and generate a RELATIVE relocation under PIC mode.
 
             NOTE: one symbol may be referenced by several relocations, we
             should only generate one RELATIVE relocation for that symbol.
-            Therefore, check GOT offset mark first.  */
-         if (h->dynindx == -1
-             && !h->forced_local
-             && h->root.type != bfd_link_hash_undefweak
-             && bfd_link_pic (info)
+            Therefore, check GOT offset mark first.
+
+            NOTE2: Symbol references via GOT in C64 static binaries without
+            PIC should always have relative relocations, so we do that here
+            early.  */
+         if (((h->dynindx == -1
+               && !h->forced_local
+               && h->root.type != bfd_link_hash_undefweak
+               && bfd_link_pic (info))
+              || (!bfd_link_pic (info) && bfd_link_executable (info)
+                  && c64_reloc))
              && !symbol_got_offset_mark_p (input_bfd, h, r_symndx))
            relative_reloc = TRUE;
 
-         value = aarch64_calculate_got_entry_vma (h, globals, info, value,
+         if (c64_reloc
+             && c64_symbol_adjust (h, value, sym_sec, info, &frag_value))
+           signed_addend = (value | h->target_internal) - frag_value;
+         else
+           frag_value = value | h->target_internal;
+
+         value = aarch64_calculate_got_entry_vma (h, globals, info,
+                                                  frag_value,
                                                   output_bfd,
                                                   unresolved_reloc_p);
          /* Record the GOT entry address which will be used when generating
@@ -5712,7 +7256,8 @@ bad_ifunc_reloc:
          if (aarch64_relocation_aginst_gp_p (bfd_r_type))
            addend = (globals->root.sgot->output_section->vma
                      + globals->root.sgot->output_offset);
-         value = _bfd_aarch64_elf_resolve_relocation (bfd_r_type, place, value,
+         value = _bfd_aarch64_elf_resolve_relocation (input_bfd, bfd_r_type,
+                                                      place, value,
                                                       addend, weak_undef_p);
        }
       else
@@ -5732,21 +7277,29 @@ bad_ifunc_reloc:
            abort ();
          }
 
-       off = symbol_got_offset (input_bfd, h, r_symndx);
-       base_got = globals->root.sgot;
        got_entry_addr = (base_got->output_section->vma
                          + base_got->output_offset + off);
 
        if (!symbol_got_offset_mark_p (input_bfd, h, r_symndx))
          {
-           bfd_put_64 (output_bfd, value, base_got->contents + off);
+           bfd_vma frag_value;
+
+           if (c64_reloc
+               && c64_symbol_adjust (h, value, sym_sec, info, &frag_value))
+             signed_addend = (value | sym->st_target_internal) - frag_value;
+           else
+             frag_value = value | sym->st_target_internal;
+
+           bfd_put_64 (output_bfd, frag_value, base_got->contents + off);
 
            /* For local symbol, we have done absolute relocation in static
               linking stage.  While for shared library, we need to update the
               content of GOT entry according to the shared object's runtime
               base address.  So, we need to generate a R_AARCH64_RELATIVE reloc
               for dynamic linker.  */
-           if (bfd_link_pic (info))
+           if (bfd_link_pic (info)
+               || (!bfd_link_pic (info) && bfd_link_executable (info)
+                   && c64_reloc))
              relative_reloc = TRUE;
 
            symbol_got_offset_mark (input_bfd, h, r_symndx);
@@ -5759,7 +7312,8 @@ bad_ifunc_reloc:
        if (aarch64_relocation_aginst_gp_p (bfd_r_type))
          addend = base_got->output_section->vma + base_got->output_offset;
 
-       value = _bfd_aarch64_elf_resolve_relocation (bfd_r_type, place, value,
+       value = _bfd_aarch64_elf_resolve_relocation (input_bfd, bfd_r_type,
+                                                    place, value,
                                                     addend, weak_undef_p);
       }
 
@@ -5768,13 +7322,38 @@ bad_ifunc_reloc:
          asection *s;
          Elf_Internal_Rela outrel;
 
+         enum elf_aarch64_reloc_type rtype = AARCH64_R (RELATIVE);
+
          s = globals->root.srelgot;
+
+         /* For a C64 relative relocation, also add size and permissions into
+            the frag.  */
+         if (c64_reloc)
+           {
+             bfd_reloc_status_type ret;
+
+             ret = c64_fixup_frag (input_bfd, info, bfd_r_type, sym, h,
+                                   sym_sec, s, base_got->contents + off + 8,
+                                   orig_value, 0, off);
+
+             if (ret != bfd_reloc_continue)
+               return ret;
+
+             rtype = MORELLO_R (RELATIVE);
+
+             if (bfd_link_executable (info) && !bfd_link_pic (info))
+               s = globals->srelcaps;
+
+             outrel.r_addend = signed_addend;
+           }
+         else
+           outrel.r_addend = orig_value;
+
          if (s == NULL)
            abort ();
 
          outrel.r_offset = got_entry_addr;
-         outrel.r_info = ELFNN_R_INFO (0, AARCH64_R (RELATIVE));
-         outrel.r_addend = orig_value;
+         outrel.r_info = ELFNN_R_INFO (0, rtype);
          elf_append_rela (output_bfd, s, &outrel);
        }
       break;
@@ -5796,7 +7375,8 @@ bad_ifunc_reloc:
               + globals->root.sgot->output_section->vma
               + globals->root.sgot->output_offset);
 
-      value = _bfd_aarch64_elf_resolve_relocation (bfd_r_type, place, value,
+      value = _bfd_aarch64_elf_resolve_relocation (input_bfd, bfd_r_type,
+                                                  place, value,
                                                   0, weak_undef_p);
       *unresolved_reloc_p = FALSE;
       break;
@@ -5809,7 +7389,8 @@ bad_ifunc_reloc:
        return bfd_reloc_notsupported;
 
       value = symbol_got_offset (input_bfd, h, r_symndx);
-      value = _bfd_aarch64_elf_resolve_relocation (bfd_r_type, place, value,
+      value = _bfd_aarch64_elf_resolve_relocation (input_bfd, bfd_r_type,
+                                                  place, value,
                                                   0, weak_undef_p);
       *unresolved_reloc_p = FALSE;
       break;
@@ -5830,10 +7411,26 @@ bad_ifunc_reloc:
     case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1:
     case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1_NC:
     case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G2:
-      value = _bfd_aarch64_elf_resolve_relocation (bfd_r_type, place, value,
-                                                  signed_addend - dtpoff_base (info),
-                                                  weak_undef_p);
-      break;
+      {
+       if (!(weak_undef_p || elf_hash_table (info)->tls_sec))
+         {
+           int howto_index = bfd_r_type - BFD_RELOC_AARCH64_RELOC_START;
+           _bfd_error_handler
+             /* xgettext:c-format */
+             (_("%pB: TLS relocation %s against undefined symbol `%s'"),
+                input_bfd, elfNN_aarch64_howto_table[howto_index].name,
+                h->root.root.string);
+           bfd_set_error (bfd_error_bad_value);
+           return bfd_reloc_notsupported;
+         }
+
+       bfd_vma def_value
+         = weak_undef_p ? 0 : signed_addend - dtpoff_base (info);
+       value = _bfd_aarch64_elf_resolve_relocation (input_bfd, bfd_r_type,
+                                                    place, value,
+                                                    def_value, weak_undef_p);
+       break;
+      }
 
     case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12:
     case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12:
@@ -5851,18 +7448,36 @@ bad_ifunc_reloc:
     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:
-      value = _bfd_aarch64_elf_resolve_relocation (bfd_r_type, place, value,
-                                                  signed_addend - tpoff_base (info),
-                                                  weak_undef_p);
-      *unresolved_reloc_p = FALSE;
-      break;
+      {
+       if (!(weak_undef_p || elf_hash_table (info)->tls_sec))
+         {
+           int howto_index = bfd_r_type - BFD_RELOC_AARCH64_RELOC_START;
+           _bfd_error_handler
+             /* xgettext:c-format */
+             (_("%pB: TLS relocation %s against undefined symbol `%s'"),
+                input_bfd, elfNN_aarch64_howto_table[howto_index].name,
+                h->root.root.string);
+           bfd_set_error (bfd_error_bad_value);
+           return bfd_reloc_notsupported;
+         }
+
+       bfd_vma def_value
+         = weak_undef_p ? 0 : signed_addend - tpoff_base (info);
+       value = _bfd_aarch64_elf_resolve_relocation (input_bfd, bfd_r_type,
+                                                    place, value,
+                                                    def_value, weak_undef_p);
+        *unresolved_reloc_p = FALSE;
+       break;
+      }
 
     case BFD_RELOC_AARCH64_TLSDESC_ADD_LO12:
     case BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21:
+    case BFD_RELOC_MORELLO_TLSDESC_ADR_PAGE20:
     case BFD_RELOC_AARCH64_TLSDESC_ADR_PREL21:
     case BFD_RELOC_AARCH64_TLSDESC_LD32_LO12_NC:
     case BFD_RELOC_AARCH64_TLSDESC_LD64_LO12:
     case BFD_RELOC_AARCH64_TLSDESC_LD_PREL19:
+    case BFD_RELOC_MORELLO_TLSDESC_LD128_LO12:
       if (globals->root.sgot == NULL)
        return bfd_reloc_notsupported;
       value = (symbol_tlsdesc_got_offset (input_bfd, h, r_symndx)
@@ -5870,7 +7485,8 @@ bad_ifunc_reloc:
               + globals->root.sgotplt->output_offset
               + globals->sgotplt_jump_table_size);
 
-      value = _bfd_aarch64_elf_resolve_relocation (bfd_r_type, place, value,
+      value = _bfd_aarch64_elf_resolve_relocation (input_bfd, bfd_r_type,
+                                                  place, value,
                                                   0, weak_undef_p);
       *unresolved_reloc_p = FALSE;
       break;
@@ -5888,11 +7504,76 @@ bad_ifunc_reloc:
       value -= (globals->root.sgot->output_section->vma
                + globals->root.sgot->output_offset);
 
-      value = _bfd_aarch64_elf_resolve_relocation (bfd_r_type, place, value,
+      value = _bfd_aarch64_elf_resolve_relocation (input_bfd, bfd_r_type,
+                                                  place, value,
                                                   0, weak_undef_p);
       *unresolved_reloc_p = FALSE;
       break;
 
+    case BFD_RELOC_MORELLO_CAPINIT:
+       {
+         Elf_Internal_Rela outrel;
+
+         if (input_section->flags & SEC_READONLY)
+           {
+             _bfd_error_handler
+               /* xgettext:c-format */
+               (_("%pB: capability relocation section must be writable"),
+                input_bfd);
+             bfd_set_error (bfd_error_bad_value);
+             return bfd_reloc_notsupported;
+           }
+
+         outrel.r_offset = _bfd_elf_section_offset (output_bfd, info,
+                                                    input_section,
+                                                    rel->r_offset);
+
+         outrel.r_offset += (input_section->output_section->vma
+                             + input_section->output_offset);
+
+         /* Capability-aligned.  */
+         if (outrel.r_offset & 0xf)
+           return bfd_reloc_overflow;
+
+         bfd_reloc_status_type ret;
+
+         ret = c64_fixup_frag (input_bfd, info, bfd_r_type, sym, h, sym_sec,
+                               input_section, hit_data + 8, value,
+                               signed_addend, rel->r_offset);
+
+         if (ret != bfd_reloc_continue)
+           return ret;
+
+         outrel.r_addend = signed_addend;
+         value |= (h != NULL ? h->target_internal : sym->st_target_internal);
+
+         /* Emit a dynamic relocation if we are building PIC.  */
+         if (h != NULL
+             && h->dynindx != -1
+             && bfd_link_pic (info)
+             && !SYMBOL_REFERENCES_LOCAL (info, h))
+           outrel.r_info = ELFNN_R_INFO (h->dynindx, r_type);
+         else
+           outrel.r_info = ELFNN_R_INFO (0, MORELLO_R (RELATIVE));
+
+         /* Symbols without size information get bounds to the
+            whole section: adjust the base of the capability to the
+            start of the section and set the addend to obtain the
+            correct address for the symbol.  */
+         bfd_vma new_value;
+         if (c64_symbol_adjust (h, value, sym_sec, info, &new_value))
+           {
+             outrel.r_addend += (value - new_value);
+             value = new_value;
+           }
+
+         asection *s = globals->srelcaps;
+
+         elf_append_rela (output_bfd, s, &outrel);
+         *unresolved_reloc_p = FALSE;
+       }
+      break;
+
     default:
       return bfd_reloc_notsupported;
     }
@@ -5937,6 +7618,69 @@ 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 & ERRAT_ADRP)
+    {
+      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);
+    }
+}
+
+#define BUILD_MOVZ(_reg, _imm) (movz_R0 \
+                               | ((((_imm) >> 16) & 0xffff) << 5) \
+                               | (_reg))
+#define BUILD_MOVK(_reg, _imm) (movk_R0 | (((_imm) & 0xffff) << 5) | (_reg))
+
 /* 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.
@@ -5946,18 +7690,56 @@ bad_ifunc_reloc:
    case of error.  */
 
 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)
+elfNN_aarch64_tls_relax (bfd *input_bfd, struct bfd_link_info *info,
+                        asection *input_section,
+                        bfd_byte *contents, Elf_Internal_Rela *rel,
+                        struct elf_link_hash_entry *h, unsigned long r_symndx)
 {
   bfd_boolean is_local = h == NULL;
+
   unsigned int r_type = ELFNN_R_TYPE (rel->r_info);
   unsigned long insn;
+  bfd_vma sym_size = 0;
+  struct elf_aarch64_link_hash_table *globals = elf_aarch64_hash_table (info);
 
   BFD_ASSERT (globals && input_bfd && contents && rel);
 
+  if (is_local)
+    {
+      if (h != NULL)
+       sym_size = h->size;
+      else
+       {
+         Elf_Internal_Sym *sym;
+
+         sym = bfd_sym_from_r_symndx (&globals->root.sym_cache, input_bfd,
+                                      r_symndx);
+         BFD_ASSERT (sym != NULL);
+         sym_size = sym->st_size;
+       }
+    }
+
   switch (elfNN_aarch64_bfd_reloc_from_type (input_bfd, r_type))
     {
+    case BFD_RELOC_MORELLO_TLSDESC_ADR_PAGE20:
+      if (is_local || !bfd_link_pic (info))
+       {
+         /* GD->LE relaxation:
+            nop                     =>   movz x1, objsize_hi16
+            adrp x0, :tlsdesc:var   =>   movz x0, :tprel_g1:var  */
+         bfd_putl32 (BUILD_MOVZ(1, sym_size), contents + rel->r_offset - 4);
+         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
+       {
+         /* GD->IE relaxation: Not implemented.  */
+         return bfd_reloc_continue;
+       }
     case BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21:
     case BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21:
       if (is_local)
@@ -5969,6 +7751,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
@@ -6123,6 +7908,19 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
     case BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_PREL19:
       return bfd_reloc_continue;
 
+    case BFD_RELOC_MORELLO_TLSDESC_LD128_LO12:
+      if (is_local || !bfd_link_pic (info))
+       {
+         /* GD->LE relaxation:
+            ldr xd, [x0, #:tlsdesc_lo12:var] => movk x0, :tprel_g0_nc:var  */
+         bfd_putl32 (movk_R0, contents + rel->r_offset);
+         return bfd_reloc_continue;
+       }
+      else
+       {
+         /* GD->IE relaxation: not implemented.  */
+         return bfd_reloc_continue;
+       }
     case BFD_RELOC_AARCH64_TLSDESC_LDNN_LO12_NC:
       if (is_local)
        {
@@ -6187,13 +7985,35 @@ elfNN_aarch64_tls_relax (struct elf_aarch64_link_hash_table *globals,
          return bfd_reloc_continue;
        }
 
-    case BFD_RELOC_AARCH64_TLSDESC_ADD:
+    case BFD_RELOC_MORELLO_TLSDESC_CALL:
+      /* GD->LE relaxation:
+        blr cd                           =>   add c0, c2, x0  */
+      if (is_local || !bfd_link_pic (info))
+       {
+         bfd_putl32 (0xc2a06040, contents + rel->r_offset);
+         return bfd_reloc_ok;
+       }
+      else
+       goto set_nop;
+
     case BFD_RELOC_AARCH64_TLSDESC_ADD_LO12:
+      /* GD->LE relaxation:
+        ldr cd, [c0, #:tlsdesc_lo12:var] => movk x1, objsize_lo16  */
+      if ((is_local || !bfd_link_pic (info))
+         && ELFNN_R_TYPE (rel[1].r_info) == MORELLO_R (TLSDESC_CALL))
+       {
+         bfd_putl32 (BUILD_MOVK(1, sym_size), contents + rel->r_offset);
+         return bfd_reloc_continue;
+       }
+
+      /* Fall through.  */
+    case BFD_RELOC_AARCH64_TLSDESC_ADD:
     case BFD_RELOC_AARCH64_TLSDESC_CALL:
       /* GD->IE/LE relaxation:
         add x0, x0, #:tlsdesc_lo12:var   =>   nop
         blr xd                           =>   nop
        */
+set_nop:
       bfd_putl32 (INSN_NOP, contents + rel->r_offset);
       return bfd_reloc_ok;
 
@@ -6259,6 +8079,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;
 
@@ -6449,7 +8272,7 @@ elfNN_aarch64_relocate_section (bfd *output_bfd,
          name = (bfd_elf_string_from_elf_section
                  (input_bfd, symtab_hdr->sh_link, sym->st_name));
          if (name == NULL || *name == '\0')
-           name = bfd_section_name (input_bfd, sec);
+           name = bfd_section_name (sec);
        }
 
       if (r_symndx != 0
@@ -6470,12 +8293,20 @@ elfNN_aarch64_relocate_section (bfd *output_bfd,
             input_section, (uint64_t) rel->r_offset, howto->name, name);
        }
 
+      if (r_symndx
+         && h
+         && IS_AARCH64_TLS_RELOC (bfd_r_type)
+         && h->root.type == bfd_link_hash_undefweak)
+       /* We have already warned about these in aarch64_check_relocs,
+          so just skip over them.  */
+       continue;
+
       /* We relax only if we can see that there can be a valid transition
         from a reloc type to another.
         We call elfNN_aarch64_final_link_relocate unless we're completely
         done, i.e., the relaxation produced the final output we want.  */
 
-      relaxed_bfd_r_type = aarch64_tls_transition (input_bfd, info, r_type,
+      relaxed_bfd_r_type = aarch64_tls_transition (input_bfd, info, rel,
                                                   h, r_symndx);
       if (relaxed_bfd_r_type != bfd_r_type)
        {
@@ -6483,7 +8314,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 (input_bfd, info, input_section,
+                                      contents, rel, h, r_symndx);
          unresolved_reloc = 0;
        }
       else
@@ -6507,6 +8339,8 @@ elfNN_aarch64_relocate_section (bfd *output_bfd,
                                               h, &unresolved_reloc,
                                               save_addend, &addend, sym);
 
+      bfd_boolean c64_rtype = FALSE;
+
       switch (elfNN_aarch64_bfd_reloc_from_type (input_bfd, r_type))
        {
        case BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC:
@@ -6561,14 +8395,14 @@ elfNN_aarch64_relocate_section (bfd *output_bfd,
                         base address when invoke runtime TLS resolver.  */
                      bfd_put_NN (output_bfd, 0,
                                  globals->root.sgot->contents + off
-                                 + GOT_ENTRY_SIZE);
+                                 + GOT_ENTRY_SIZE (globals));
                    }
                  else if (indx == 0)
                    {
                      bfd_put_NN (output_bfd,
                                  relocation - dtpoff_base (info),
                                  globals->root.sgot->contents + off
-                                 + GOT_ENTRY_SIZE);
+                                 + GOT_ENTRY_SIZE (globals));
                    }
                  else
                    {
@@ -6581,7 +8415,7 @@ elfNN_aarch64_relocate_section (bfd *output_bfd,
                      rela.r_offset =
                        (globals->root.sgot->output_section->vma
                         + globals->root.sgot->output_offset + off
-                        + GOT_ENTRY_SIZE);
+                        + GOT_ENTRY_SIZE (globals));
 
                      loc = globals->root.srelgot->contents;
                      loc += globals->root.srelgot->reloc_count++
@@ -6589,7 +8423,7 @@ elfNN_aarch64_relocate_section (bfd *output_bfd,
                      bfd_elfNN_swap_reloca_out (output_bfd, &rela, loc);
                      bfd_put_NN (output_bfd, (bfd_vma) 0,
                                  globals->root.sgot->contents + off
-                                 + GOT_ENTRY_SIZE);
+                                 + GOT_ENTRY_SIZE (globals));
                    }
                }
              else
@@ -6599,7 +8433,7 @@ elfNN_aarch64_relocate_section (bfd *output_bfd,
                  bfd_put_NN (output_bfd,
                              relocation - dtpoff_base (info),
                              globals->root.sgot->contents + off
-                             + GOT_ENTRY_SIZE);
+                             + GOT_ENTRY_SIZE (globals));
                }
 
              symbol_got_offset_mark (input_bfd, h, r_symndx);
@@ -6660,6 +8494,11 @@ elfNN_aarch64_relocate_section (bfd *output_bfd,
            }
          break;
 
+       case BFD_RELOC_MORELLO_TLSDESC_ADR_PAGE20:
+       case BFD_RELOC_MORELLO_TLSDESC_LD128_LO12:
+         c64_rtype = TRUE;
+         /* Fall through.  */
+
        case BFD_RELOC_AARCH64_TLSDESC_ADD_LO12:
        case BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21:
        case BFD_RELOC_AARCH64_TLSDESC_ADR_PREL21:
@@ -6684,7 +8523,10 @@ elfNN_aarch64_relocate_section (bfd *output_bfd,
                {
                  bfd_byte *loc;
                  Elf_Internal_Rela rela;
-                 rela.r_info = ELFNN_R_INFO (indx, AARCH64_R (TLSDESC));
+
+                 rela.r_info = ELFNN_R_INFO (indx,
+                                             (c64_rtype ? MORELLO_R (TLSDESC)
+                                              : AARCH64_R (TLSDESC)));
 
                  rela.r_addend = 0;
                  rela.r_offset = (globals->root.sgotplt->output_section->vma
@@ -6714,7 +8556,7 @@ elfNN_aarch64_relocate_section (bfd *output_bfd,
                  bfd_put_NN (output_bfd, (bfd_vma) 0,
                              globals->root.sgotplt->contents + off +
                              globals->sgotplt_jump_table_size +
-                             GOT_ENTRY_SIZE);
+                             GOT_ENTRY_SIZE (globals));
                }
 
              symbol_tlsdesc_got_offset_mark (input_bfd, h, r_symndx);
@@ -6776,7 +8618,7 @@ elfNN_aarch64_relocate_section (bfd *output_bfd,
 
                 Try to catch this situation here and provide a more helpful
                 error message to the user.  */
-             if (addend & ((1 << howto->rightshift) - 1)
+             if (addend & (((bfd_vma) 1 << howto->rightshift) - 1)
                  /* FIXME: Are we testing all of the appropriate reloc
                     types here ?  */
                  && (real_r_type == BFD_RELOC_AARCH64_LD_LO19_PCREL
@@ -6791,6 +8633,11 @@ symbol is being referenced in the indicated code as if it had a larger \
 alignment than was declared where it was defined"),
                     name, input_bfd, input_section, rel->r_offset);
                }
+
+             if (real_r_type == BFD_RELOC_MORELLO_CAPINIT)
+               info->callbacks->warning
+                 (info, _("relocation offset must be capability aligned"),
+                  name, input_bfd, input_section, rel->r_offset);
              break;
 
            case bfd_reloc_undefined:
@@ -6868,7 +8715,7 @@ elfNN_aarch64_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
   bfd *obfd = info->output_bfd;
   flagword out_flags;
   flagword in_flags;
-  bfd_boolean flags_compatible = TRUE;
+  bfd_boolean flags_compatible = FALSE;
   asection *sec;
 
   /* Check if we have the same endianess.  */
@@ -6889,6 +8736,8 @@ elfNN_aarch64_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
 
   if (!elf_flags_init (obfd))
     {
+      elf_flags_init (obfd) = TRUE;
+
       /* If the input is the default architecture and had the default
         flags then do not bother setting the flags for the output
         architecture, instead allow future merges to do this.  If no
@@ -6899,7 +8748,6 @@ elfNN_aarch64_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
          && elf_elfheader (ibfd)->e_flags == 0)
        return TRUE;
 
-      elf_flags_init (obfd) = TRUE;
       elf_elfheader (obfd)->e_flags = in_flags;
 
       if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
@@ -6923,15 +8771,21 @@ elfNN_aarch64_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info)
      Also check to see if there are no code sections in the input.
      In this case there is no need to check for code specific flags.
      XXX - do we need to worry about floating-point format compatability
-     in data sections ?  */
+     in data sections ?
+
+     We definitely need to check for data sections if one set of flags is
+     targetting the PURECAP abi and another is not.  Pointers being
+     capabilities in data sections can not be glossed over.  */
   if (!(ibfd->flags & DYNAMIC))
     {
       bfd_boolean null_input_bfd = TRUE;
-      bfd_boolean only_data_sections = TRUE;
+      bfd_boolean only_data_sections
+       = !(in_flags & EF_AARCH64_CHERI_PURECAP
+           || out_flags & EF_AARCH64_CHERI_PURECAP);
 
       for (sec = ibfd->sections; sec != NULL; sec = sec->next)
        {
-         if ((bfd_get_section_flags (ibfd, sec)
+         if ((bfd_section_flags (sec)
               & (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
              == (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
            only_data_sections = FALSE;
@@ -6975,23 +8829,6 @@ elfNN_aarch64_print_private_bfd_data (bfd *abfd, void *ptr)
   return TRUE;
 }
 
-/* Find dynamic relocs for H that apply to read-only sections.  */
-
-static asection *
-readonly_dynrelocs (struct elf_link_hash_entry *h)
-{
-  struct elf_dyn_relocs *p;
-
-  for (p = elf_aarch64_hash_entry (h)->dyn_relocs; p != NULL; p = p->next)
-    {
-      asection *s = p->sec->output_section;
-
-      if (s != NULL && (s->flags & SEC_READONLY) != 0)
-       return p->sec;
-    }
-  return NULL;
-}
-
 /* Return true if we need copy relocation against EH.  */
 
 static bfd_boolean
@@ -7000,7 +8837,7 @@ need_copy_relocation_p (struct elf_aarch64_link_hash_entry *eh)
   struct elf_dyn_relocs *p;
   asection *s;
 
-  for (p = eh->dyn_relocs; p != NULL; p = p->next)
+  for (p = eh->root.dyn_relocs; p != NULL; p = p->next)
     {
       /* If there is any pc-relative reference, we need to keep copy relocation
         to avoid propagating the relocation into runtime that current glibc
@@ -7154,6 +8991,45 @@ elfNN_aarch64_allocate_local_symbols (bfd *abfd, unsigned number)
   return TRUE;
 }
 
+/* Initialise the .got section to hold the global offset table.  */
+
+static void
+aarch64_elf_init_got_section (bfd *abfd, struct bfd_link_info *info)
+{
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  asection *s;
+  struct elf_aarch64_link_hash_table *globals = elf_aarch64_hash_table (info);
+  unsigned int align = bed->s->log_file_align + globals->c64_rel;
+
+  if (globals->root.sgot != NULL)
+    {
+      bfd_set_section_alignment (globals->root.srelgot,
+                                bed->s->log_file_align);
+      bfd_set_section_alignment (globals->root.sgot, align);
+      globals->root.sgot->size += GOT_ENTRY_SIZE (globals);
+    }
+
+  /* Track capability initialisation for static non-PIE binaries.  */
+  if (bfd_link_executable (info) && !bfd_link_pic (info)
+      && globals->srelcaps == NULL)
+    globals->srelcaps = globals->root.srelgot;
+
+  if (globals->root.igotplt != NULL)
+    bfd_set_section_alignment (globals->root.igotplt, align);
+
+  s = globals->root.sgot;
+
+  if (globals->root.sgotplt != NULL)
+    {
+      bfd_set_section_alignment (globals->root.sgotplt, align);
+      s = globals->root.sgotplt;
+    }
+
+  /* The first bit of the global offset table is the header.  */
+  if (s != NULL)
+    s->size += bed->got_header_size (info);
+}
+
 /* Create the .got section to hold the global offset table.  */
 
 static bfd_boolean
@@ -7176,17 +9052,14 @@ aarch64_elf_create_got_section (bfd *abfd, struct bfd_link_info *info)
                                           ? ".rela.got" : ".rel.got"),
                                          (bed->dynamic_sec_flags
                                           | SEC_READONLY));
-  if (s == NULL
-      || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
+  if (s == NULL)
     return FALSE;
   htab->srelgot = s;
 
   s = bfd_make_section_anyway_with_flags (abfd, ".got", flags);
-  if (s == NULL
-      || !bfd_set_section_alignment (abfd, s, bed->s->log_file_align))
+  if (s == NULL)
     return FALSE;
   htab->sgot = s;
-  htab->sgot->size += GOT_ENTRY_SIZE;
 
   if (bed->want_got_sym)
     {
@@ -7204,16 +9077,11 @@ aarch64_elf_create_got_section (bfd *abfd, struct bfd_link_info *info)
   if (bed->want_got_plt)
     {
       s = bfd_make_section_anyway_with_flags (abfd, ".got.plt", flags);
-      if (s == NULL
-         || !bfd_set_section_alignment (abfd, s,
-                                        bed->s->log_file_align))
+      if (s == NULL)
        return FALSE;
       htab->sgotplt = s;
     }
 
-  /* The first bit of the global offset table is the header.  */
-  s->size += bed->got_header_size;
-
   return TRUE;
 }
 
@@ -7242,17 +9110,19 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
   symtab_hdr = &elf_symtab_hdr (abfd);
   sym_hashes = elf_sym_hashes (abfd);
 
+  bfd_elfNN_aarch64_init_maps (abfd, info);
+
   rel_end = relocs + sec->reloc_count;
   for (rel = relocs; rel < rel_end; rel++)
     {
       struct elf_link_hash_entry *h;
-      unsigned int r_symndx;
-      unsigned int r_type;
+      unsigned int r_symndx, r_type;
       bfd_reloc_code_real_type bfd_r_type;
       Elf_Internal_Sym *isym;
 
       r_symndx = ELFNN_R_SYM (rel->r_info);
       r_type = ELFNN_R_TYPE (rel->r_info);
+      bfd_r_type = elfNN_aarch64_bfd_reloc_from_type (abfd, r_type);
 
       if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr))
        {
@@ -7264,7 +9134,7 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
       if (r_symndx < symtab_hdr->sh_info)
        {
          /* A local symbol.  */
-         isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+         isym = bfd_sym_from_r_symndx (&htab->root.sym_cache,
                                        abfd, r_symndx);
          if (isym == NULL)
            return FALSE;
@@ -7295,8 +9165,28 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
            h = (struct elf_link_hash_entry *) h->root.u.i.link;
        }
 
+      /* Ignore TLS relocations against weak undef symbols and warn about them.
+        The behaviour of weak TLS variables is not well defined. Since making
+        these well behaved is not a priority for Morello, we simply ignore
+        TLS relocations against such symbols here to avoid the linker crashing
+        on these and to enable making progress in other areas.  */
+      if (r_symndx
+         && h
+         && IS_AARCH64_TLS_RELOC (bfd_r_type)
+         && h->root.type == bfd_link_hash_undefweak)
+       {
+         int howto_index = bfd_r_type - BFD_RELOC_AARCH64_RELOC_START;
+         _bfd_error_handler (_("%pB(%pA+%#" PRIx64 "): ignoring TLS relocation "
+                               "%s against undef weak symbol %s"),
+                             abfd, sec,
+                             (uint64_t) rel->r_offset,
+                             elfNN_aarch64_howto_table[howto_index].name,
+                             h->root.root.string);
+         continue;
+       }
+
       /* Could be done earlier, if h were already available.  */
-      bfd_r_type = aarch64_tls_transition (abfd, info, r_type, h, r_symndx);
+      bfd_r_type = aarch64_tls_transition (abfd, info, rel, h, r_symndx);
 
       if (h != NULL)
        {
@@ -7325,9 +9215,19 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
            default:
              break;
 
+           case BFD_RELOC_MORELLO_CALL26:
+           case BFD_RELOC_MORELLO_JUMP26:
+             /* For dynamic symbols record caller information so that we can
+                decide what kind of PLT stubs to emit.  */
+             if (h != NULL)
+               elf_aarch64_hash_entry (h)->got_type = GOT_CAP;
+             /* Fall through.  */
+
            case BFD_RELOC_AARCH64_ADD_LO12:
            case BFD_RELOC_AARCH64_ADR_GOT_PAGE:
+           case BFD_RELOC_MORELLO_ADR_GOT_PAGE:
            case BFD_RELOC_AARCH64_ADR_HI21_PCREL:
+           case BFD_RELOC_MORELLO_ADR_HI20_PCREL:
            case BFD_RELOC_AARCH64_CALL26:
            case BFD_RELOC_AARCH64_GOT_LD_PREL19:
            case BFD_RELOC_AARCH64_JUMP26:
@@ -7336,6 +9236,7 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
            case BFD_RELOC_AARCH64_LD64_GOTOFF_LO15:
            case BFD_RELOC_AARCH64_LD64_GOTPAGE_LO15:
            case BFD_RELOC_AARCH64_LD64_GOT_LO12_NC:
+           case BFD_RELOC_MORELLO_LD128_GOT_LO12_NC:
            case BFD_RELOC_AARCH64_MOVW_GOTOFF_G0_NC:
            case BFD_RELOC_AARCH64_MOVW_GOTOFF_G1:
            case BFD_RELOC_AARCH64_NN:
@@ -7361,8 +9262,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;
@@ -7406,6 +9306,8 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case BFD_RELOC_AARCH64_ADD_LO12:
        case BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL:
        case BFD_RELOC_AARCH64_ADR_HI21_PCREL:
+       case BFD_RELOC_MORELLO_ADR_HI20_NC_PCREL:
+       case BFD_RELOC_MORELLO_ADR_HI20_PCREL:
        case BFD_RELOC_AARCH64_ADR_LO21_PCREL:
        case BFD_RELOC_AARCH64_LDST128_LO12:
        case BFD_RELOC_AARCH64_LDST16_LO12:
@@ -7413,6 +9315,7 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
        case BFD_RELOC_AARCH64_LDST64_LO12:
        case BFD_RELOC_AARCH64_LDST8_LO12:
        case BFD_RELOC_AARCH64_LD_LO19_PCREL:
+       case BFD_RELOC_MORELLO_LD_LO17_PCREL:
          if (h == NULL || bfd_link_pic (info))
            break;
          /* Fall through.  */
@@ -7481,9 +9384,7 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
               relocations we need for this symbol.  */
            if (h != NULL)
              {
-               struct elf_aarch64_link_hash_entry *eh;
-               eh = (struct elf_aarch64_link_hash_entry *) h;
-               head = &eh->dyn_relocs;
+               head = &h->dyn_relocs;
              }
            else
              {
@@ -7494,7 +9395,7 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
                asection *s;
                void **vpp;
 
-               isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+               isym = bfd_sym_from_r_symndx (&htab->root.sym_cache,
                                              abfd, r_symndx);
                if (isym == NULL)
                  return FALSE;
@@ -7512,7 +9413,7 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
            p = *head;
            if (p == NULL || p->sec != sec)
              {
-               bfd_size_type amt = sizeof *p;
+               size_t amt = sizeof *p;
                p = ((struct elf_dyn_relocs *)
                     bfd_zalloc (htab->root.dynobj, amt));
                if (p == NULL)
@@ -7531,6 +9432,13 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
          /* RR: We probably want to keep a consistency check that
             there are no dangling GOT_PAGE relocs.  */
+       case BFD_RELOC_MORELLO_ADR_GOT_PAGE:
+       case BFD_RELOC_MORELLO_LD128_GOT_LO12_NC:
+       case BFD_RELOC_MORELLO_TLSDESC_ADR_PAGE20:
+       case BFD_RELOC_MORELLO_TLSDESC_LD128_LO12:
+         htab->c64_rel = 1;
+         /* Fall through.  */
+
        case BFD_RELOC_AARCH64_ADR_GOT_PAGE:
        case BFD_RELOC_AARCH64_GOT_LD_PREL19:
        case BFD_RELOC_AARCH64_LD32_GOTPAGE_LO14:
@@ -7562,9 +9470,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;
@@ -7599,7 +9504,8 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
               is a TLS/non-TLS mismatch, based on the symbol type.
               So just combine any TLS types needed.  */
            if (old_got_type != GOT_UNKNOWN && old_got_type != GOT_NORMAL
-               && got_type != GOT_NORMAL)
+               && got_type != GOT_NORMAL && old_got_type != GOT_CAP
+               && got_type != GOT_CAP)
              got_type |= old_got_type;
 
            /* If the symbol is accessed by both IE and GD methods, we
@@ -7609,6 +9515,13 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
            if ((got_type & GOT_TLS_IE) && GOT_TLS_GD_ANY_P (got_type))
              got_type &= ~ (GOT_TLSDESC_GD | GOT_TLS_GD);
 
+           /* Prefer the capability reference.  */
+           if ((old_got_type & GOT_CAP) && (got_type & GOT_NORMAL))
+             {
+               got_type &= ~GOT_NORMAL;
+               got_type |= GOT_CAP;
+             }
+
            if (old_got_type != got_type)
              {
                if (h != NULL)
@@ -7629,12 +9542,44 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
            break;
          }
 
+       case BFD_RELOC_MORELLO_CALL26:
+       case BFD_RELOC_MORELLO_JUMP26:
+         htab->c64_rel = 1;
+         if (h != NULL)
+           elf_aarch64_hash_entry (h)->got_type = GOT_CAP;
+
+         /* Fall through.  */
        case BFD_RELOC_AARCH64_CALL26:
        case BFD_RELOC_AARCH64_JUMP26:
-         /* If this is a local symbol then we resolve it
-            directly without creating a PLT entry.  */
          if (h == NULL)
-           continue;
+           {
+             isym = bfd_sym_from_r_symndx (&htab->root.sym_cache, abfd,
+                                           r_symndx);
+             if (isym == NULL)
+               return FALSE;
+
+             asection *s = bfd_section_from_elf_index (abfd, isym->st_shndx);
+
+             if (s == NULL)
+               s = sec;
+
+             if (c64_value_p (s, isym->st_value))
+               isym->st_target_internal |= ST_BRANCH_TO_C64;
+
+             /* If this is a local symbol then we resolve it
+                directly without creating a PLT entry.  */
+             continue;
+           }
+
+         if (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak)
+           {
+             asection *sym_sec = h->root.u.def.section;
+             bfd_vma sym_value = h->root.u.def.value;
+
+             if (sym_sec != NULL && c64_value_p (sym_sec, sym_value))
+               h->target_internal |= ST_BRANCH_TO_C64;
+           }
 
          h->needs_plt = 1;
          if (h->plt.refcount <= 0)
@@ -7643,6 +9588,25 @@ elfNN_aarch64_check_relocs (bfd *abfd, struct bfd_link_info *info,
            h->plt.refcount += 1;
          break;
 
+       case BFD_RELOC_MORELLO_CAPINIT:
+         if (htab->srelcaps == NULL)
+           {
+             if (htab->root.dynobj == NULL)
+               htab->root.dynobj = abfd;
+
+             sreloc = _bfd_elf_make_dynamic_reloc_section
+               (sec, htab->root.dynobj, LOG_FILE_ALIGN,
+                abfd, /*rela? */ TRUE);
+
+             if (sreloc == NULL)
+               return FALSE;
+
+             htab->srelcaps = sreloc;
+           }
+         htab->srelcaps->size += RELOC_SIZE (htab);
+
+         break;
+
        default:
          break;
        }
@@ -7661,116 +9625,43 @@ elfNN_aarch64_is_target_special_symbol (bfd *abfd ATTRIBUTE_UNUSED,
                                             BFD_AARCH64_SPECIAL_SYM_TYPE_ANY);
 }
 
-/* This is a copy of elf_find_function () from elf.c except that
-   AArch64 mapping symbols are ignored when looking for function names.  */
+/* If the ELF symbol SYM might be a function in SEC, return the
+   function size and set *CODE_OFF to the function's entry point,
+   otherwise return zero.  */
 
-static bfd_boolean
-aarch64_elf_find_function (bfd *abfd ATTRIBUTE_UNUSED,
-                          asymbol **symbols,
-                          asection *section,
-                          bfd_vma offset,
-                          const char **filename_ptr,
-                          const char **functionname_ptr)
+static bfd_size_type
+elfNN_aarch64_maybe_function_sym (const asymbol *sym, asection *sec,
+                                 bfd_vma *code_off)
 {
-  const char *filename = NULL;
-  asymbol *func = NULL;
-  bfd_vma low_func = 0;
-  asymbol **p;
-
-  for (p = symbols; *p != NULL; p++)
-    {
-      elf_symbol_type *q;
+  bfd_size_type size;
 
-      q = (elf_symbol_type *) * p;
+  if ((sym->flags & (BSF_SECTION_SYM | BSF_FILE | BSF_OBJECT
+                    | BSF_THREAD_LOCAL | BSF_RELC | BSF_SRELC)) != 0
+      || sym->section != sec)
+    return 0;
 
-      switch (ELF_ST_TYPE (q->internal_elf_sym.st_info))
-       {
-       default:
-         break;
-       case STT_FILE:
-         filename = bfd_asymbol_name (&q->symbol);
-         break;
+  if (!(sym->flags & BSF_SYNTHETIC))
+    switch (ELF_ST_TYPE (((elf_symbol_type *) sym)->internal_elf_sym.st_info))
+      {
        case STT_FUNC:
        case STT_NOTYPE:
-         /* Skip mapping symbols.  */
-         if ((q->symbol.flags & BSF_LOCAL)
-             && (bfd_is_aarch64_special_symbol_name
-                 (q->symbol.name, BFD_AARCH64_SPECIAL_SYM_TYPE_ANY)))
-           continue;
-         /* Fall through.  */
-         if (bfd_get_section (&q->symbol) == section
-             && q->symbol.value >= low_func && q->symbol.value <= offset)
-           {
-             func = (asymbol *) q;
-             low_func = q->symbol.value;
-           }
          break;
-       }
-    }
-
-  if (func == NULL)
-    return FALSE;
-
-  if (filename_ptr)
-    *filename_ptr = filename;
-  if (functionname_ptr)
-    *functionname_ptr = bfd_asymbol_name (func);
-
-  return TRUE;
-}
-
-
-/* Find the nearest line to a particular section and offset, for error
-   reporting.   This code is a duplicate of the code in elf.c, except
-   that it uses aarch64_elf_find_function.  */
-
-static bfd_boolean
-elfNN_aarch64_find_nearest_line (bfd *abfd,
-                                asymbol **symbols,
-                                asection *section,
-                                bfd_vma offset,
-                                const char **filename_ptr,
-                                const char **functionname_ptr,
-                                unsigned int *line_ptr,
-                                unsigned int *discriminator_ptr)
-{
-  bfd_boolean found = FALSE;
-
-  if (_bfd_dwarf2_find_nearest_line (abfd, symbols, NULL, section, offset,
-                                    filename_ptr, functionname_ptr,
-                                    line_ptr, discriminator_ptr,
-                                    dwarf_debug_sections, 0,
-                                    &elf_tdata (abfd)->dwarf2_find_line_info))
-    {
-      if (!*functionname_ptr)
-       aarch64_elf_find_function (abfd, symbols, section, offset,
-                                  *filename_ptr ? NULL : filename_ptr,
-                                  functionname_ptr);
-
-      return TRUE;
-    }
-
-  /* Skip _bfd_dwarf1_find_nearest_line since no known AArch64
-     toolchain uses DWARF1.  */
-
-  if (!_bfd_stab_section_find_nearest_line (abfd, symbols, section, offset,
-                                           &found, filename_ptr,
-                                           functionname_ptr, line_ptr,
-                                           &elf_tdata (abfd)->line_info))
-    return FALSE;
-
-  if (found && (*functionname_ptr || *line_ptr))
-    return TRUE;
-
-  if (symbols == NULL)
-    return FALSE;
+       default:
+         return 0;
+      }
 
-  if (!aarch64_elf_find_function (abfd, symbols, section, offset,
-                                 filename_ptr, functionname_ptr))
-    return FALSE;
+  if ((sym->flags & BSF_LOCAL)
+      && bfd_is_aarch64_special_symbol_name (sym->name,
+                                            BFD_AARCH64_SPECIAL_SYM_TYPE_ANY))
+    return 0;
 
-  *line_ptr = 0;
-  return TRUE;
+  *code_off = sym->value;
+  size = 0;
+  if (!(sym->flags & BSF_SYNTHETIC))
+    size = ((elf_symbol_type *) sym)->internal_elf_sym.st_size;
+  if (size == 0)
+    size = 1;
+  return size;
 }
 
 static bfd_boolean
@@ -7787,16 +9678,17 @@ elfNN_aarch64_find_inliner_info (bfd *abfd,
 }
 
 
-static void
-elfNN_aarch64_post_process_headers (bfd *abfd,
-                                   struct bfd_link_info *link_info)
+static bfd_boolean
+elfNN_aarch64_init_file_header (bfd *abfd, struct bfd_link_info *link_info)
 {
   Elf_Internal_Ehdr *i_ehdrp;  /* ELF file header, internal form.  */
 
+  if (!_bfd_elf_init_file_header (abfd, link_info))
+    return FALSE;
+
   i_ehdrp = elf_elfheader (abfd);
   i_ehdrp->e_ident[EI_ABIVERSION] = AARCH64_ELF_ABI_VERSION;
-
-  _bfd_elf_post_process_headers (abfd, link_info);
+  return TRUE;
 }
 
 static enum elf_reloc_type_class
@@ -7836,10 +9728,13 @@ elfNN_aarch64_reloc_type_class (const struct bfd_link_info *info ATTRIBUTE_UNUSE
   switch ((int) ELFNN_R_TYPE (rela->r_info))
     {
     case AARCH64_R (IRELATIVE):
+    case MORELLO_R (IRELATIVE):
       return reloc_class_ifunc;
     case AARCH64_R (RELATIVE):
+    case MORELLO_R (RELATIVE):
       return reloc_class_relative;
     case AARCH64_R (JUMP_SLOT):
+    case MORELLO_R (JUMP_SLOT):
       return reloc_class_plt;
     case AARCH64_R (COPY):
       return reloc_class_copy;
@@ -7877,95 +9772,6 @@ elfNN_aarch64_section_from_shdr (bfd *abfd,
   return TRUE;
 }
 
-/* A structure used to record a list of sections, independently
-   of the next and prev fields in the asection structure.  */
-typedef struct section_list
-{
-  asection *sec;
-  struct section_list *next;
-  struct section_list *prev;
-}
-section_list;
-
-/* Unfortunately we need to keep a list of sections for which
-   an _aarch64_elf_section_data structure has been allocated.  This
-   is because it is possible for functions like elfNN_aarch64_write_section
-   to be called on a section which has had an elf_data_structure
-   allocated for it (and so the used_by_bfd field is valid) but
-   for which the AArch64 extended version of this structure - the
-   _aarch64_elf_section_data structure - has not been allocated.  */
-static section_list *sections_with_aarch64_elf_section_data = NULL;
-
-static void
-record_section_with_aarch64_elf_section_data (asection *sec)
-{
-  struct section_list *entry;
-
-  entry = bfd_malloc (sizeof (*entry));
-  if (entry == NULL)
-    return;
-  entry->sec = sec;
-  entry->next = sections_with_aarch64_elf_section_data;
-  entry->prev = NULL;
-  if (entry->next != NULL)
-    entry->next->prev = entry;
-  sections_with_aarch64_elf_section_data = entry;
-}
-
-static struct section_list *
-find_aarch64_elf_section_entry (asection *sec)
-{
-  struct section_list *entry;
-  static struct section_list *last_entry = NULL;
-
-  /* This is a short cut for the typical case where the sections are added
-     to the sections_with_aarch64_elf_section_data list in forward order and
-     then looked up here in backwards order.  This makes a real difference
-     to the ld-srec/sec64k.exp linker test.  */
-  entry = sections_with_aarch64_elf_section_data;
-  if (last_entry != NULL)
-    {
-      if (last_entry->sec == sec)
-       entry = last_entry;
-      else if (last_entry->next != NULL && last_entry->next->sec == sec)
-       entry = last_entry->next;
-    }
-
-  for (; entry; entry = entry->next)
-    if (entry->sec == sec)
-      break;
-
-  if (entry)
-    /* Record the entry prior to this one - it is the entry we are
-       most likely to want to locate next time.  Also this way if we
-       have been called from
-       unrecord_section_with_aarch64_elf_section_data () we will not
-       be caching a pointer that is about to be freed.  */
-    last_entry = entry->prev;
-
-  return entry;
-}
-
-static void
-unrecord_section_with_aarch64_elf_section_data (asection *sec)
-{
-  struct section_list *entry;
-
-  entry = find_aarch64_elf_section_entry (sec);
-
-  if (entry)
-    {
-      if (entry->prev != NULL)
-       entry->prev->next = entry->next;
-      if (entry->next != NULL)
-       entry->next->prev = entry->prev;
-      if (entry == sections_with_aarch64_elf_section_data)
-       sections_with_aarch64_elf_section_data = entry->next;
-      free (entry);
-    }
-}
-
-
 typedef struct
 {
   void *finfo;
@@ -7979,7 +9785,8 @@ typedef struct
 enum map_symbol_type
 {
   AARCH64_MAP_INSN,
-  AARCH64_MAP_DATA
+  AARCH64_MAP_DATA,
+  AARCH64_MAP_C64,
 };
 
 
@@ -7989,7 +9796,7 @@ static bfd_boolean
 elfNN_aarch64_output_map_sym (output_arch_syminfo *osi,
                              enum map_symbol_type type, bfd_vma offset)
 {
-  static const char *names[2] = { "$x", "$d" };
+  static const char *names[3] = { "$x", "$d", "$c" };
   Elf_Internal_Sym sym;
 
   sym.st_value = (osi->sec->output_section->vma
@@ -7998,6 +9805,7 @@ elfNN_aarch64_output_map_sym (output_arch_syminfo *osi,
   sym.st_other = 0;
   sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE);
   sym.st_shndx = osi->sec_shndx;
+  sym.st_target_internal = 0;
   return osi->func (osi->finfo, names[type], &sym, osi->sec, NULL) == 1;
 }
 
@@ -8015,6 +9823,7 @@ elfNN_aarch64_output_stub_sym (output_arch_syminfo *osi, const char *name,
   sym.st_other = 0;
   sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_FUNC);
   sym.st_shndx = osi->sec_shndx;
+  sym.st_target_internal = 0;
   return osi->func (osi->finfo, name, &sym, osi->sec, NULL) == 1;
 }
 
@@ -8074,6 +9883,23 @@ 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_branch_c64:
+      if (!elfNN_aarch64_output_stub_sym (osi, stub_name, addr,
+                                         sizeof (aarch64_c64_branch_stub)))
+       return FALSE;
+      if (!elfNN_aarch64_output_map_sym (osi, AARCH64_MAP_C64, addr))
+       return FALSE;
+      break;
+    case c64_stub_branch_aarch64:
+    case c64_stub_branch_c64:
+      if (!elfNN_aarch64_output_stub_sym (osi, stub_name, addr,
+                                         sizeof (c64_aarch64_branch_stub)))
+       return FALSE;
+      if (!elfNN_aarch64_output_map_sym (osi, AARCH64_MAP_C64, addr))
+       return FALSE;
+      break;
+    case aarch64_stub_none:
+      break;
 
     default:
       abort ();
@@ -8137,7 +9963,8 @@ elfNN_aarch64_output_arch_local_syms (bfd *output_bfd,
     (output_bfd, htab->root.splt->output_section);
   osi.sec = htab->root.splt;
 
-  elfNN_aarch64_output_map_sym (&osi, AARCH64_MAP_INSN, 0);
+  elfNN_aarch64_output_map_sym (&osi, (htab->c64_rel ? AARCH64_MAP_C64
+                                      : AARCH64_MAP_INSN), 0);
 
   return TRUE;
 
@@ -8151,48 +9978,19 @@ elfNN_aarch64_new_section_hook (bfd *abfd, asection *sec)
   if (!sec->used_by_bfd)
     {
       _aarch64_elf_section_data *sdata;
-      bfd_size_type amt = sizeof (*sdata);
+      size_t amt = sizeof (*sdata);
 
       sdata = bfd_zalloc (abfd, amt);
       if (sdata == NULL)
        return FALSE;
+      sdata->elf.is_target_section_data = TRUE;
       sec->used_by_bfd = sdata;
     }
 
-  record_section_with_aarch64_elf_section_data (sec);
-
   return _bfd_elf_new_section_hook (abfd, sec);
 }
 
 
-static void
-unrecord_section_via_map_over_sections (bfd *abfd ATTRIBUTE_UNUSED,
-                                       asection *sec,
-                                       void *ignore ATTRIBUTE_UNUSED)
-{
-  unrecord_section_with_aarch64_elf_section_data (sec);
-}
-
-static bfd_boolean
-elfNN_aarch64_close_and_cleanup (bfd *abfd)
-{
-  if (abfd->sections)
-    bfd_map_over_sections (abfd,
-                          unrecord_section_via_map_over_sections, NULL);
-
-  return _bfd_elf_close_and_cleanup (abfd);
-}
-
-static bfd_boolean
-elfNN_aarch64_bfd_free_cached_info (bfd *abfd)
-{
-  if (abfd->sections)
-    bfd_map_over_sections (abfd,
-                          unrecord_section_via_map_over_sections, NULL);
-
-  return _bfd_free_cached_info (abfd);
-}
-
 /* Create dynamic sections. This is different from the ARM backend in that
    the got, plt, gotplt and their relocation sections are all created in the
    standard part of the bfd elf backend.  */
@@ -8279,11 +10077,11 @@ 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.  */
-         htab->root.sgotplt->size += GOT_ENTRY_SIZE;
+         htab->root.sgotplt->size += GOT_ENTRY_SIZE (htab);
 
          /* We also need to make an entry in the .rela.plt section.  */
          htab->root.srelplt->size += RELOC_SIZE (htab);
@@ -8302,6 +10100,12 @@ elfNN_aarch64_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
             updated.  */
 
          htab->root.srelplt->reloc_count++;
+
+         /* Mark the DSO in case R_<CLS>_JUMP_SLOT relocs against
+            variant PCS symbols are present.  */
+         if (h->other & STO_AARCH64_VARIANT_PCS)
+           htab->variant_pcs = 1;
+
        }
       else
        {
@@ -8339,10 +10143,11 @@ elfNN_aarch64_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
       if (got_type == GOT_UNKNOWN)
        {
        }
-      else if (got_type == GOT_NORMAL)
+      else if (got_type == GOT_NORMAL
+              || got_type == GOT_CAP)
        {
          h->got.offset = htab->root.sgot->size;
-         htab->root.sgot->size += GOT_ENTRY_SIZE;
+         htab->root.sgot->size += GOT_ENTRY_SIZE (htab);
          if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
               || h->root.type != bfd_link_hash_undefweak)
              && (bfd_link_pic (info)
@@ -8353,6 +10158,8 @@ elfNN_aarch64_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
            {
              htab->root.srelgot->size += RELOC_SIZE (htab);
            }
+         else if (bfd_link_executable (info) && !bfd_link_pic (info))
+           htab->srelcaps->size += RELOC_SIZE (htab);
        }
       else
        {
@@ -8362,20 +10169,20 @@ elfNN_aarch64_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
              eh->tlsdesc_got_jump_table_offset =
                (htab->root.sgotplt->size
                 - aarch64_compute_jump_table_size (htab));
-             htab->root.sgotplt->size += GOT_ENTRY_SIZE * 2;
+             htab->root.sgotplt->size += GOT_ENTRY_SIZE (htab) * 2;
              h->got.offset = (bfd_vma) - 2;
            }
 
          if (got_type & GOT_TLS_GD)
            {
              h->got.offset = htab->root.sgot->size;
-             htab->root.sgot->size += GOT_ENTRY_SIZE * 2;
+             htab->root.sgot->size += GOT_ENTRY_SIZE (htab) * 2;
            }
 
          if (got_type & GOT_TLS_IE)
            {
              h->got.offset = htab->root.sgot->size;
-             htab->root.sgot->size += GOT_ENTRY_SIZE;
+             htab->root.sgot->size += GOT_ENTRY_SIZE (htab);
            }
 
          indx = h && h->dynindx != -1 ? h->dynindx : 0;
@@ -8383,7 +10190,10 @@ elfNN_aarch64_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
               || h->root.type != bfd_link_hash_undefweak)
              && (!bfd_link_executable (info)
                  || indx != 0
-                 || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)))
+                 || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)
+                 /* On Morello support only TLSDESC_GD to TLSLE relaxation;
+                    for everything else we must emit a dynamic relocation.  */
+                 || got_type & GOT_CAP))
            {
              if (got_type & GOT_TLSDESC_GD)
                {
@@ -8393,7 +10203,7 @@ elfNN_aarch64_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                     type.  */
 
                  /* TLSDESC PLT is now needed, but not yet determined.  */
-                 htab->tlsdesc_plt = (bfd_vma) - 1;
+                 htab->root.tlsdesc_plt = (bfd_vma) - 1;
                }
 
              if (got_type & GOT_TLS_GD)
@@ -8409,7 +10219,7 @@ elfNN_aarch64_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
       h->got.offset = (bfd_vma) - 1;
     }
 
-  if (eh->dyn_relocs == NULL)
+  if (h->dyn_relocs == NULL)
     return TRUE;
 
   /* In the shared -Bsymbolic case, discard space allocated for
@@ -8430,7 +10240,7 @@ elfNN_aarch64_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
        {
          struct elf_dyn_relocs **pp;
 
-         for (pp = &eh->dyn_relocs; (p = *pp) != NULL;)
+         for (pp = &h->dyn_relocs; (p = *pp) != NULL;)
            {
              p->count -= p->pc_count;
              p->pc_count = 0;
@@ -8443,11 +10253,11 @@ elfNN_aarch64_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
 
       /* Also discard relocs on undefined weak syms with non-default
         visibility.  */
-      if (eh->dyn_relocs != NULL && h->root.type == bfd_link_hash_undefweak)
+      if (h->dyn_relocs != NULL && h->root.type == bfd_link_hash_undefweak)
        {
          if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
              || UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
-           eh->dyn_relocs = NULL;
+           h->dyn_relocs = NULL;
 
          /* Make sure undefined weak symbols are output as a dynamic
             symbol in PIEs.  */
@@ -8486,13 +10296,13 @@ elfNN_aarch64_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
            goto keep;
        }
 
-      eh->dyn_relocs = NULL;
+      h->dyn_relocs = NULL;
 
     keep:;
     }
 
   /* Finally, allocate space.  */
-  for (p = eh->dyn_relocs; p != NULL; p = p->next)
+  for (p = h->dyn_relocs; p != NULL; p = p->next)
     {
       asection *sreloc;
 
@@ -8515,7 +10325,6 @@ elfNN_aarch64_allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h,
 {
   struct bfd_link_info *info;
   struct elf_aarch64_link_hash_table *htab;
-  struct elf_aarch64_link_hash_entry *eh;
 
   /* An example of a bfd_link_hash_indirect symbol is versioned
      symbol. For example: __gxx_personality_v0(bfd_link_hash_indirect)
@@ -8535,41 +10344,19 @@ elfNN_aarch64_allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h,
   info = (struct bfd_link_info *) inf;
   htab = elf_aarch64_hash_table (info);
 
-  eh = (struct elf_aarch64_link_hash_entry *) h;
-
   /* Since STT_GNU_IFUNC symbol must go through PLT, we handle it
      here if it is defined and referenced in a non-shared object.  */
   if (h->type == STT_GNU_IFUNC
       && h->def_regular)
     return _bfd_elf_allocate_ifunc_dyn_relocs (info, h,
-                                              &eh->dyn_relocs,
-                                              NULL,
+                                              &h->dyn_relocs,
                                               htab->plt_entry_size,
                                               htab->plt_header_size,
-                                              GOT_ENTRY_SIZE,
+                                              GOT_ENTRY_SIZE (htab),
                                               FALSE);
   return TRUE;
 }
 
-/* Allocate space in .plt, .got and associated reloc sections for
-   local dynamic relocs.  */
-
-static bfd_boolean
-elfNN_aarch64_allocate_local_dynrelocs (void **slot, void *inf)
-{
-  struct elf_link_hash_entry *h
-    = (struct elf_link_hash_entry *) *slot;
-
-  if (h->type != STT_GNU_IFUNC
-      || !h->def_regular
-      || !h->ref_regular
-      || !h->forced_local
-      || h->root.type != bfd_link_hash_defined)
-    abort ();
-
-  return elfNN_aarch64_allocate_dynrelocs (h, inf);
-}
-
 /* Allocate space in .plt, .got and associated reloc sections for
    local ifunc dynamic relocs.  */
 
@@ -8589,38 +10376,11 @@ elfNN_aarch64_allocate_local_ifunc_dynrelocs (void **slot, void *inf)
   return elfNN_aarch64_allocate_ifunc_dynrelocs (h, inf);
 }
 
-/* Set DF_TEXTREL if we find any dynamic relocs that apply to
-   read-only sections.  */
-
-static bfd_boolean
-maybe_set_textrel (struct elf_link_hash_entry *h, void *info_p)
-{
-  asection *sec;
-
-  if (h->root.type == bfd_link_hash_indirect)
-    return TRUE;
-
-  sec = readonly_dynrelocs (h);
-  if (sec != NULL)
-    {
-      struct bfd_link_info *info = (struct bfd_link_info *) info_p;
-
-      info->flags |= DF_TEXTREL;
-      info->callbacks->minfo
-       (_("%pB: dynamic relocation against `%pT' in read-only section `%pA'\n"),
-        sec->owner, h->root.root.string, sec);
-
-      /* Not an error, just cut short the traversal.  */
-      return FALSE;
-    }
-  return TRUE;
-}
-
 /* This is the most important function of all . Innocuosly named
    though !  */
 
 static bfd_boolean
-elfNN_aarch64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
+elfNN_aarch64_size_dynamic_sections (bfd *output_bfd,
                                     struct bfd_link_info *info)
 {
   struct elf_aarch64_link_hash_table *htab;
@@ -8646,6 +10406,10 @@ elfNN_aarch64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
        }
     }
 
+  aarch64_elf_init_got_section (output_bfd, info);
+
+  setup_plt_values (info, elf_aarch64_tdata (output_bfd)->plt_type);
+
   /* Set up .got offsets for local syms, and space for local dynamic
      relocs.  */
   for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
@@ -8701,21 +10465,22 @@ elfNN_aarch64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
                  locals[i].tlsdesc_got_jump_table_offset =
                    (htab->root.sgotplt->size
                     - aarch64_compute_jump_table_size (htab));
-                 htab->root.sgotplt->size += GOT_ENTRY_SIZE * 2;
+                 htab->root.sgotplt->size += GOT_ENTRY_SIZE (htab) * 2;
                  locals[i].got_offset = (bfd_vma) - 2;
                }
 
              if (got_type & GOT_TLS_GD)
                {
                  locals[i].got_offset = htab->root.sgot->size;
-                 htab->root.sgot->size += GOT_ENTRY_SIZE * 2;
+                 htab->root.sgot->size += GOT_ENTRY_SIZE (htab) * 2;
                }
 
              if (got_type & GOT_TLS_IE
-                 || got_type & GOT_NORMAL)
+                 || got_type & GOT_NORMAL
+                 || got_type & GOT_CAP)
                {
                  locals[i].got_offset = htab->root.sgot->size;
-                 htab->root.sgot->size += GOT_ENTRY_SIZE;
+                 htab->root.sgot->size += GOT_ENTRY_SIZE (htab);
                }
 
              if (got_type == GOT_UNKNOWN)
@@ -8728,16 +10493,20 @@ elfNN_aarch64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
                    {
                      htab->root.srelplt->size += RELOC_SIZE (htab);
                      /* Note RELOC_COUNT not incremented here! */
-                     htab->tlsdesc_plt = (bfd_vma) - 1;
+                     htab->root.tlsdesc_plt = (bfd_vma) - 1;
                    }
 
                  if (got_type & GOT_TLS_GD)
                    htab->root.srelgot->size += RELOC_SIZE (htab) * 2;
 
                  if (got_type & GOT_TLS_IE
-                     || got_type & GOT_NORMAL)
+                     || got_type & GOT_NORMAL
+                     || got_type & GOT_CAP)
                    htab->root.srelgot->size += RELOC_SIZE (htab);
                }
+             /* Static binary; put relocs into srelcaps.  */
+             else if (bfd_link_executable (info) && (got_type & GOT_CAP))
+               htab->srelcaps->size += RELOC_SIZE (htab);
            }
          else
            {
@@ -8757,16 +10526,28 @@ elfNN_aarch64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
   elf_link_hash_traverse (&htab->root, elfNN_aarch64_allocate_ifunc_dynrelocs,
                          info);
 
-  /* Allocate .plt and .got entries, and space for local symbols.  */
-  htab_traverse (htab->loc_hash_table,
-                elfNN_aarch64_allocate_local_dynrelocs,
-                info);
-
   /* Allocate .plt and .got entries, and space for local ifunc symbols.  */
   htab_traverse (htab->loc_hash_table,
                 elfNN_aarch64_allocate_local_ifunc_dynrelocs,
                 info);
 
+  if (bfd_link_executable (info)
+      && !bfd_link_pic (info)
+      && htab->srelcaps
+      && htab->srelcaps->size > 0)
+    {
+      struct elf_link_hash_entry *h;
+
+      h = _bfd_elf_define_linkage_sym (output_bfd, info,
+                                      htab->srelcaps,
+                                      "__rela_dyn_start");
+      h = _bfd_elf_define_linkage_sym (output_bfd, info,
+                                      htab->srelcaps,
+                                      "__rela_dyn_end");
+
+      h->root.u.def.value = htab->srelcaps->vma + htab->srelcaps->size;
+    }
+
   /* For every jump slot reserved in the sgotplt, reloc_count is
      incremented.  However, when we reserve space for TLS descriptors,
      it's not incremented, so in order to compute the space reserved
@@ -8776,20 +10557,22 @@ elfNN_aarch64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
   if (htab->root.srelplt)
     htab->sgotplt_jump_table_size = aarch64_compute_jump_table_size (htab);
 
-  if (htab->tlsdesc_plt)
+  if (htab->root.tlsdesc_plt)
     {
       if (htab->root.splt->size == 0)
-       htab->root.splt->size += PLT_ENTRY_SIZE;
-
-      htab->tlsdesc_plt = htab->root.splt->size;
-      htab->root.splt->size += PLT_TLSDESC_ENTRY_SIZE;
+       htab->root.splt->size += htab->plt_header_size;
 
       /* If we're not using lazy TLS relocations, don't generate the
-        GOT entry required.  */
-      if (!(info->flags & DF_BIND_NOW))
+        GOT and PLT entry required.  */
+      if ((info->flags & DF_BIND_NOW))
+       htab->root.tlsdesc_plt = 0;
+      else
        {
-         htab->dt_tlsdesc_got = htab->root.sgot->size;
-         htab->root.sgot->size += GOT_ENTRY_SIZE;
+         htab->root.tlsdesc_plt = htab->root.splt->size;
+         htab->root.splt->size += htab->tlsdesc_plt_entry_size;
+
+         htab->root.tlsdesc_got = htab->root.sgot->size;
+         htab->root.sgot->size += GOT_ENTRY_SIZE (htab);
        }
     }
 
@@ -8800,7 +10583,7 @@ elfNN_aarch64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       {
        if (!is_aarch64_elf (ibfd))
          continue;
-       bfd_elfNN_aarch64_init_maps (ibfd);
+       bfd_elfNN_aarch64_init_maps (ibfd, info);
       }
 
   /* We now have determined the sizes of the various dynamic sections.
@@ -8822,7 +10605,7 @@ elfNN_aarch64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
          /* Strip this section if we don't need it; see the
             comment below.  */
        }
-      else if (CONST_STRNEQ (bfd_get_section_name (dynobj, s), ".rela"))
+      else if (CONST_STRNEQ (bfd_section_name (s), ".rela"))
        {
          if (s->size != 0 && s != htab->root.srelplt)
            relocs = TRUE;
@@ -8876,43 +10659,27 @@ elfNN_aarch64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 #define add_dynamic_entry(TAG, VAL)                    \
       _bfd_elf_add_dynamic_entry (info, TAG, VAL)
 
-      if (bfd_link_executable (info))
-       {
-         if (!add_dynamic_entry (DT_DEBUG, 0))
-           return FALSE;
-       }
+      if (!_bfd_elf_add_dynamic_tags (output_bfd, info, relocs))
+       return FALSE;
 
       if (htab->root.splt->size != 0)
        {
-         if (!add_dynamic_entry (DT_PLTGOT, 0)
-             || !add_dynamic_entry (DT_PLTRELSZ, 0)
-             || !add_dynamic_entry (DT_PLTREL, DT_RELA)
-             || !add_dynamic_entry (DT_JMPREL, 0))
+         if (htab->variant_pcs
+             && !add_dynamic_entry (DT_AARCH64_VARIANT_PCS, 0))
            return FALSE;
 
-         if (htab->tlsdesc_plt
-             && (!add_dynamic_entry (DT_TLSDESC_PLT, 0)
-                 || !add_dynamic_entry (DT_TLSDESC_GOT, 0)))
+         if ((elf_aarch64_tdata (output_bfd)->plt_type == PLT_BTI_PAC)
+             && (!add_dynamic_entry (DT_AARCH64_BTI_PLT, 0)
+                 || !add_dynamic_entry (DT_AARCH64_PAC_PLT, 0)))
            return FALSE;
-       }
 
-      if (relocs)
-       {
-         if (!add_dynamic_entry (DT_RELA, 0)
-             || !add_dynamic_entry (DT_RELASZ, 0)
-             || !add_dynamic_entry (DT_RELAENT, RELOC_SIZE (htab)))
+         else if ((elf_aarch64_tdata (output_bfd)->plt_type == PLT_BTI)
+                  && !add_dynamic_entry (DT_AARCH64_BTI_PLT, 0))
            return FALSE;
 
-         /* If any dynamic relocs apply to a read-only section,
-            then we need a DT_TEXTREL entry.  */
-         if ((info->flags & DF_TEXTREL) == 0)
-           elf_link_hash_traverse (&htab->root, maybe_set_textrel, info);
-
-         if ((info->flags & DF_TEXTREL) != 0)
-           {
-             if (!add_dynamic_entry (DT_TEXTREL, 0))
-               return FALSE;
-           }
+         else if ((elf_aarch64_tdata (output_bfd)->plt_type == PLT_PAC)
+                  && !add_dynamic_entry (DT_AARCH64_PAC_PLT, 0))
+           return FALSE;
        }
     }
 #undef add_dynamic_entry
@@ -8931,6 +10698,26 @@ elf_aarch64_update_plt_entry (bfd *output_bfd,
   (void) _bfd_aarch64_elf_put_addend (output_bfd, plt_entry, r_type, howto, value);
 }
 
+static void
+aarch64_update_c64_plt_entry (bfd *output_bfd, bfd_byte *plt_entry,
+                             bfd_vma plt_base, bfd_vma plt_got_ent)
+{
+  /* Fill in the top 20 bits for this: ADRP c16, PLT_GOT + n * 16.
+     ADRP:   ((PG(S+A)-PG(P)) >> 12) & 0xfffff */
+  elf_aarch64_update_plt_entry (output_bfd, BFD_RELOC_MORELLO_ADR_HI20_PCREL,
+                               plt_entry,
+                               PG (plt_got_ent) - PG (plt_base));
+
+  elf_aarch64_update_plt_entry (output_bfd,
+                               BFD_RELOC_AARCH64_LDST128_LO12,
+                               plt_entry + 4,
+                               PG_OFFSET (plt_got_ent));
+
+  elf_aarch64_update_plt_entry (output_bfd, BFD_RELOC_AARCH64_ADD_LO12,
+                               plt_entry + 8,
+                               PG_OFFSET (plt_got_ent));
+}
+
 static void
 elfNN_aarch64_create_small_pltn_entry (struct elf_link_hash_entry *h,
                                       struct elf_aarch64_link_hash_table
@@ -8975,12 +10762,12 @@ elfNN_aarch64_create_small_pltn_entry (struct elf_link_hash_entry *h,
   if (plt == htab->root.splt)
     {
       plt_index = (h->plt.offset - htab->plt_header_size) / htab->plt_entry_size;
-      got_offset = (plt_index + 3) * GOT_ENTRY_SIZE;
+      got_offset = (plt_index + 3) * GOT_ENTRY_SIZE (htab);
     }
   else
     {
       plt_index = h->plt.offset / htab->plt_entry_size;
-      got_offset = plt_index * GOT_ENTRY_SIZE;
+      got_offset = plt_index * GOT_ENTRY_SIZE (htab);
     }
 
   plt_entry = plt->contents + h->plt.offset;
@@ -8990,29 +10777,44 @@ 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);
-
-  /* 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,
-                               plt_entry,
-                               PG (gotplt_entry_address) -
-                               PG (plt_entry_address));
+  memcpy (plt_entry, htab->plt_entry, htab->plt_entry_size);
 
-  /* Fill in the lo12 bits for the load from the pltgot.  */
-  elf_aarch64_update_plt_entry (output_bfd, BFD_RELOC_AARCH64_LDSTNN_LO12,
-                               plt_entry + 4,
-                               PG_OFFSET (gotplt_entry_address));
+  if (htab->c64_rel)
+    aarch64_update_c64_plt_entry (output_bfd, plt_entry, plt_entry_address,
+                                 gotplt_entry_address);
+  else
+    {
 
-  /* Fill in the lo12 bits for the add from the pltgot entry.  */
-  elf_aarch64_update_plt_entry (output_bfd, BFD_RELOC_AARCH64_ADD_LO12,
-                               plt_entry + 8,
-                               PG_OFFSET (gotplt_entry_address));
+      /* 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 */
+      elf_aarch64_update_plt_entry (output_bfd,
+                                   BFD_RELOC_AARCH64_ADR_HI21_PCREL,
+                                   plt_entry,
+                                   PG (gotplt_entry_address) -
+                                   PG (plt_entry_address));
+
+      /* Fill in the lo12 bits for the load from the pltgot.  */
+      elf_aarch64_update_plt_entry (output_bfd, BFD_RELOC_AARCH64_LDSTNN_LO12,
+                                   plt_entry + 4,
+                                   PG_OFFSET (gotplt_entry_address));
+
+      /* Fill in the lo12 bits for the add from the pltgot entry.  */
+      elf_aarch64_update_plt_entry (output_bfd, BFD_RELOC_AARCH64_ADD_LO12,
+                                   plt_entry + 8,
+                                   PG_OFFSET (gotplt_entry_address));
+    }
 
-  /* All the GOTPLT Entries are essentially initialized to PLT0.  */
-  bfd_put_NN (output_bfd,
-             plt->output_section->vma + plt->output_offset,
-             gotplt->contents + got_offset);
+  /* All the GOTPLT Entries are essentially initialized to PLT0.  Set LSB if
+     the PLT is C64.  */
+  bfd_vma plt0 = ((plt->output_section->vma + plt->output_offset)
+                 | htab->c64_rel);
+  bfd_put_NN (output_bfd, plt0, gotplt->contents + got_offset);
 
   rela.r_offset = gotplt_entry_address;
 
@@ -9024,7 +10826,9 @@ elfNN_aarch64_create_small_pltn_entry (struct elf_link_hash_entry *h,
     {
       /* If an STT_GNU_IFUNC symbol is locally defined, generate
         R_AARCH64_IRELATIVE instead of R_AARCH64_JUMP_SLOT.  */
-      rela.r_info = ELFNN_R_INFO (0, AARCH64_R (IRELATIVE));
+      rela.r_info = (elf_aarch64_hash_entry (h)->got_type == GOT_CAP
+                    ? ELFNN_R_INFO (0, MORELLO_R (IRELATIVE))
+                    : ELFNN_R_INFO (0, AARCH64_R (IRELATIVE)));
       rela.r_addend = (h->root.u.def.value
                       + h->root.u.def.section->output_section->vma
                       + h->root.u.def.section->output_offset);
@@ -9032,7 +10836,9 @@ elfNN_aarch64_create_small_pltn_entry (struct elf_link_hash_entry *h,
   else
     {
       /* Fill in the entry in the .rela.plt section.  */
-      rela.r_info = ELFNN_R_INFO (h->dynindx, AARCH64_R (JUMP_SLOT));
+      rela.r_info = (elf_aarch64_hash_entry (h)->got_type == GOT_CAP
+                    ? ELFNN_R_INFO (h->dynindx, MORELLO_R (JUMP_SLOT))
+                    : ELFNN_R_INFO (h->dynindx, AARCH64_R (JUMP_SLOT)));
       rela.r_addend = 0;
     }
 
@@ -9150,8 +10956,11 @@ elfNN_aarch64_finish_dynamic_symbol (bfd *output_bfd,
        }
     }
 
+  bfd_boolean is_c64 = elf_aarch64_hash_entry (h)->got_type == GOT_CAP;
+
   if (h->got.offset != (bfd_vma) - 1
-      && elf_aarch64_hash_entry (h)->got_type == GOT_NORMAL
+      && (elf_aarch64_hash_entry (h)->got_type == GOT_NORMAL
+         || elf_aarch64_hash_entry (h)->got_type == GOT_CAP)
       /* Undefined weak symbol in static PIE resolves to 0 without
         any dynamic relocations.  */
       && !UNDEFWEAK_NO_DYNAMIC_RELOC (info, h))
@@ -9201,18 +11010,28 @@ elfNN_aarch64_finish_dynamic_symbol (bfd *output_bfd,
            return FALSE;
 
          BFD_ASSERT ((h->got.offset & 1) != 0);
-         rela.r_info = ELFNN_R_INFO (0, AARCH64_R (RELATIVE));
-         rela.r_addend = (h->root.u.def.value
-                          + h->root.u.def.section->output_section->vma
-                          + h->root.u.def.section->output_offset);
+         if (is_c64)
+           {
+             rela.r_info = ELFNN_R_INFO (0, MORELLO_R (RELATIVE));
+             rela.r_addend = 0;
+           }
+         else
+           {
+             rela.r_info = ELFNN_R_INFO (0, AARCH64_R (RELATIVE));
+             rela.r_addend = (h->root.u.def.value
+                              + h->root.u.def.section->output_section->vma
+                              + h->root.u.def.section->output_offset);
+           }
        }
       else
        {
-do_glob_dat:
+       do_glob_dat:
          BFD_ASSERT ((h->got.offset & 1) == 0);
          bfd_put_NN (output_bfd, (bfd_vma) 0,
                      htab->root.sgot->contents + h->got.offset);
-         rela.r_info = ELFNN_R_INFO (h->dynindx, AARCH64_R (GLOB_DAT));
+         rela.r_info = ELFNN_R_INFO (h->dynindx,
+                                     (is_c64 ? MORELLO_R (GLOB_DAT)
+                                      : AARCH64_R (GLOB_DAT)));
          rela.r_addend = 0;
        }
 
@@ -9295,30 +11114,47 @@ 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);
-  elf_section_data (htab->root.splt->output_section)->this_hdr.sh_entsize =
-    PLT_ENTRY_SIZE;
+  memcpy (htab->root.splt->contents, htab->plt0_entry,
+         htab->plt_header_size);
+
+  /* PR 26312: Explicitly set the sh_entsize to 0 so that
+     consumers do not think that the section contains fixed
+     sized objects.  */
+  elf_section_data (htab->root.splt->output_section)->this_hdr.sh_entsize = 0;
 
   plt_got_2nd_ent = (htab->root.sgotplt->output_section->vma
                  + htab->root.sgotplt->output_offset
-                 + GOT_ENTRY_SIZE * 2);
+                 + GOT_ENTRY_SIZE (htab) * 2);
 
   plt_base = htab->root.splt->output_section->vma +
     htab->root.splt->output_offset;
 
+  bfd_byte *plt0_entry = htab->root.splt->contents;
+
+  if (htab->c64_rel)
+    {
+      aarch64_update_c64_plt_entry (output_bfd, plt0_entry + 4,
+                                   plt_base + 4, plt_got_2nd_ent);
+      return;
+    }
+
+  /* First instruction in BTI enabled PLT stub is a BTI
+     instruction so skip it.  */
+  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));
 }
 
@@ -9373,13 +11209,14 @@ elfNN_aarch64_finish_dynamic_sections (bfd *output_bfd,
            case DT_TLSDESC_PLT:
              s = htab->root.splt;
              dyn.d_un.d_ptr = s->output_section->vma + s->output_offset
-               + htab->tlsdesc_plt;
+               + htab->root.tlsdesc_plt;
              break;
 
            case DT_TLSDESC_GOT:
              s = htab->root.sgot;
+             BFD_ASSERT (htab->root.tlsdesc_got != (bfd_vma)-1);
              dyn.d_un.d_ptr = s->output_section->vma + s->output_offset
-               + htab->dt_tlsdesc_got;
+               + htab->root.tlsdesc_got;
              break;
            }
 
@@ -9393,23 +11230,38 @@ elfNN_aarch64_finish_dynamic_sections (bfd *output_bfd,
     {
       elfNN_aarch64_init_small_plt0_entry (output_bfd, htab);
 
-      elf_section_data (htab->root.splt->output_section)->
-       this_hdr.sh_entsize = htab->plt_entry_size;
-
-
-      if (htab->tlsdesc_plt)
+      if (htab->root.tlsdesc_plt && !(info->flags & DF_BIND_NOW))
        {
+         BFD_ASSERT (htab->root.tlsdesc_got != (bfd_vma)-1);
          bfd_put_NN (output_bfd, (bfd_vma) 0,
-                     htab->root.sgot->contents + htab->dt_tlsdesc_got);
+                     htab->root.sgot->contents + htab->root.tlsdesc_got);
+
+         const bfd_byte *entry = elfNN_aarch64_tlsdesc_small_plt_entry;
+         htab->tlsdesc_plt_entry_size = PLT_TLSDESC_ENTRY_SIZE;
+
+         unsigned adrp_rtype = BFD_RELOC_AARCH64_ADR_HI21_PCREL;
+         unsigned ldr_rtype = BFD_RELOC_AARCH64_LDSTNN_LO12;
+
+         aarch64_plt_type type = elf_aarch64_tdata (output_bfd)->plt_type;
+         if (htab->c64_rel)
+           {
+             entry = elfNN_aarch64_tlsdesc_small_plt_c64_entry;
+             adrp_rtype = BFD_RELOC_MORELLO_ADR_HI20_PCREL;
+             ldr_rtype = BFD_RELOC_AARCH64_LDST128_LO12;
+           }
+         else if (type == PLT_BTI || type == PLT_BTI_PAC)
+           {
+             entry = elfNN_aarch64_tlsdesc_small_plt_bti_entry;
+           }
 
-         memcpy (htab->root.splt->contents + htab->tlsdesc_plt,
-                 elfNN_aarch64_tlsdesc_small_plt_entry,
-                 sizeof (elfNN_aarch64_tlsdesc_small_plt_entry));
+         memcpy (htab->root.splt->contents + htab->root.tlsdesc_plt,
+                 entry, htab->tlsdesc_plt_entry_size);
 
          {
            bfd_vma adrp1_addr =
              htab->root.splt->output_section->vma
-             + htab->root.splt->output_offset + htab->tlsdesc_plt + 4;
+             + htab->root.splt->output_offset
+             + htab->root.tlsdesc_plt + 4;
 
            bfd_vma adrp2_addr = adrp1_addr + 4;
 
@@ -9421,28 +11273,37 @@ elfNN_aarch64_finish_dynamic_sections (bfd *output_bfd,
              htab->root.sgotplt->output_section->vma
              + htab->root.sgotplt->output_offset;
 
-           bfd_vma dt_tlsdesc_got = got_addr + htab->dt_tlsdesc_got;
+           bfd_vma dt_tlsdesc_got = got_addr + htab->root.tlsdesc_got;
 
            bfd_byte *plt_entry =
-             htab->root.splt->contents + htab->tlsdesc_plt;
+             htab->root.splt->contents + htab->root.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,
+                                         adrp_rtype,
                                          plt_entry + 4,
                                          (PG (dt_tlsdesc_got)
                                           - PG (adrp1_addr)));
 
            /* adrp x3, 0 */
            elf_aarch64_update_plt_entry (output_bfd,
-                                         BFD_RELOC_AARCH64_ADR_HI21_PCREL,
+                                         adrp_rtype,
                                          plt_entry + 8,
                                          (PG (pltgot_addr)
                                           - PG (adrp2_addr)));
 
            /* ldr x2, [x2, #0] */
            elf_aarch64_update_plt_entry (output_bfd,
-                                         BFD_RELOC_AARCH64_LDSTNN_LO12,
+                                         ldr_rtype,
                                          plt_entry + 12,
                                          PG_OFFSET (dt_tlsdesc_got));
 
@@ -9472,10 +11333,11 @@ elfNN_aarch64_finish_dynamic_sections (bfd *output_bfd,
          /* Write GOT[1] and GOT[2], needed for the dynamic linker.  */
          bfd_put_NN (output_bfd,
                      (bfd_vma) 0,
-                     htab->root.sgotplt->contents + GOT_ENTRY_SIZE);
+                     htab->root.sgotplt->contents + GOT_ENTRY_SIZE (htab));
          bfd_put_NN (output_bfd,
                      (bfd_vma) 0,
-                     htab->root.sgotplt->contents + GOT_ENTRY_SIZE * 2);
+                     (htab->root.sgotplt->contents
+                      + GOT_ENTRY_SIZE (htab) * 2));
        }
 
       if (htab->root.sgot)
@@ -9489,12 +11351,12 @@ elfNN_aarch64_finish_dynamic_sections (bfd *output_bfd,
        }
 
       elf_section_data (htab->root.sgotplt->output_section)->
-       this_hdr.sh_entsize = GOT_ENTRY_SIZE;
+       this_hdr.sh_entsize = GOT_ENTRY_SIZE (htab);
     }
 
   if (htab->root.sgot && htab->root.sgot->size > 0)
     elf_section_data (htab->root.sgot->output_section)->this_hdr.sh_entsize
-      = GOT_ENTRY_SIZE;
+      = GOT_ENTRY_SIZE (htab);
 
   /* Fill PLT and GOT entries for local STT_GNU_IFUNC symbols.  */
   htab_traverse (htab->loc_hash_table,
@@ -9504,6 +11366,57 @@ 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;
+
+       case DT_AARCH64_PAC_PLT:
+         ret |= PLT_PAC;
+         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.  */
 
@@ -9511,7 +11424,27 @@ 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_PAC)
+    {
+      if (elf_elfheader (plt->owner)->e_type == ET_EXEC)
+       pltn_size = PLT_BTI_PAC_SMALL_ENTRY_SIZE;
+      else
+       pltn_size = PLT_PAC_SMALL_ENTRY_SIZE;
+    }
+  else if (elf_aarch64_tdata (plt->owner)->plt_type == PLT_BTI)
+    {
+      if (elf_elfheader (plt->owner)->e_type == ET_EXEC)
+       pltn_size = PLT_BTI_SMALL_ENTRY_SIZE;
+    }
+  else if (elf_aarch64_tdata (plt->owner)->plt_type == PLT_PAC)
+    {
+      pltn_size = PLT_PAC_SMALL_ENTRY_SIZE;
+    }
+
+  return plt->vma + plt0_size + i * pltn_size;
 }
 
 /* Returns TRUE if NAME is an AArch64 mapping symbol.
@@ -9527,7 +11460,7 @@ is_aarch64_mapping_symbol (const char * name)
                         the mapping symbols could have acquired a prefix.
                         We do not support this here, since such symbols no
                         longer conform to the ARM ELF ABI.  */
-    && (name[1] == 'd' || name[1] == 'x')
+    && (name[1] == 'd' || name[1] == 'x' || name[1] == 'c')
     && (name[2] == 0 || name[2] == '.');
   /* FIXME: Strictly speaking the symbol is only a valid mapping symbol if
      any characters that follow the period are legal characters for the body
@@ -9548,6 +11481,135 @@ 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;
+  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, bfd *bbfd,
+                                      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.  */
+  /* Properties are merged per type, hence only check for warnings when merging
+     GNU_PROPERTY_AARCH64_FEATURE_1_AND.  */
+  if (((aprop && aprop->pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND)
+       || (bprop && bprop->pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND))
+      && (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))
+          || !aprop)
+       {
+         _bfd_error_handler (_("%pB: warning: BTI turned on by -z force-bti when "
+                               "all inputs do not have BTI in NOTE section."),
+                             abfd);
+       }
+      if ((bprop && !(bprop->u.number & GNU_PROPERTY_AARCH64_FEATURE_1_BTI))
+          || !bprop)
+       {
+         _bfd_error_handler (_("%pB: warning: BTI turned on by -z force-bti when "
+                               "all inputs do not have BTI in NOTE section."),
+                             bbfd);
+       }
+    }
+
+  return  _bfd_aarch64_elf_merge_gnu_properties (info, abfd, aprop,
+                                                bprop, prop);
+}
+
+/* Demangle c64 function symbols as we read them in.  */
+
+static bfd_boolean
+aarch64_elfNN_swap_symbol_in (bfd * abfd,
+                             const void *psrc,
+                             const void *pshn,
+                             Elf_Internal_Sym *dst)
+{
+  if (!bfd_elfNN_swap_symbol_in (abfd, psrc, pshn, dst))
+    return FALSE;
+
+  dst->st_target_internal = 0;
+
+  if (ELF_ST_TYPE (dst->st_info) == STT_FUNC
+      || ELF_ST_TYPE (dst->st_info) == STT_GNU_IFUNC)
+    {
+      dst->st_target_internal = dst->st_value & ST_BRANCH_TO_C64;
+      dst->st_value &= ~(bfd_vma) ST_BRANCH_TO_C64;
+    }
+
+  return TRUE;
+}
+
+
+/* Mangle c64 function symbols as we write them out.  */
+
+static void
+aarch64_elfNN_swap_symbol_out (bfd *abfd,
+                              const Elf_Internal_Sym *src,
+                              void *cdst,
+                              void *shndx)
+{
+  Elf_Internal_Sym newsym = *src;
+
+  if ((ELF_ST_TYPE (newsym.st_info) == STT_FUNC
+       || ELF_ST_TYPE (newsym.st_info) == STT_GNU_IFUNC)
+      && newsym.st_shndx != SHN_UNDEF)
+    newsym.st_value |= newsym.st_target_internal;
+
+  bfd_elfNN_swap_symbol_out (abfd, &newsym, cdst, shndx);
+}
+
+/* Define the size of a GOT element for the generic mid-end.  */
+
+static bfd_vma
+elfNN_aarch64_got_elt_size (bfd *abfd ATTRIBUTE_UNUSED,
+                           struct bfd_link_info *info,
+                           struct elf_link_hash_entry *h ATTRIBUTE_UNUSED,
+                           bfd *ibfd ATTRIBUTE_UNUSED,
+                           unsigned long symndx ATTRIBUTE_UNUSED)
+{
+  struct elf_aarch64_link_hash_table *htab = elf_aarch64_hash_table (info);
+
+  return GOT_ENTRY_SIZE (htab);
+}
+
+/* Define the size of a GOT header, which is the minimum size of the GOT section
+   when one is needed.  */
+
+static bfd_vma
+elfNN_aarch64_got_header_size (struct bfd_link_info *info)
+{
+  struct elf_aarch64_link_hash_table *htab = elf_aarch64_hash_table (info);
+
+  return GOT_ENTRY_SIZE (htab) * GOT_RESERVED_HEADER_SLOTS;
+}
+
+/* Identify the 'C' in the CIE augmentation string.  */
+
+static bfd_boolean
+elf64_aarch64_eh_frame_augmentation_char (const char aug)
+{
+  return aug == 'C';
+}
 
 /* We use this so we can override certain functions
    (though currently we don't).  */
@@ -9571,8 +11633,8 @@ const struct elf_size_info elfNN_aarch64_size_info =
   bfd_elfNN_write_shdrs_and_ehdr,
   bfd_elfNN_checksum_contents,
   bfd_elfNN_write_relocs,
-  bfd_elfNN_swap_symbol_in,
-  bfd_elfNN_swap_symbol_out,
+  aarch64_elfNN_swap_symbol_in,
+  aarch64_elfNN_swap_symbol_out,
   bfd_elfNN_slurp_reloc_table,
   bfd_elfNN_slurp_symbol_table,
   bfd_elfNN_swap_dyn_in,
@@ -9589,12 +11651,6 @@ const struct elf_size_info elfNN_aarch64_size_info =
 #define ELF_MINPAGESIZE                        0x1000
 #define ELF_COMMONPAGESIZE             0x1000
 
-#define bfd_elfNN_close_and_cleanup            \
-  elfNN_aarch64_close_and_cleanup
-
-#define bfd_elfNN_bfd_free_cached_info         \
-  elfNN_aarch64_bfd_free_cached_info
-
 #define bfd_elfNN_bfd_is_target_special_symbol \
   elfNN_aarch64_is_target_special_symbol
 
@@ -9619,8 +11675,8 @@ const struct elf_size_info elfNN_aarch64_size_info =
 #define bfd_elfNN_find_inliner_info            \
   elfNN_aarch64_find_inliner_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
@@ -9640,6 +11696,9 @@ const struct elf_size_info elfNN_aarch64_size_info =
 #define elf_backend_copy_indirect_symbol       \
   elfNN_aarch64_copy_indirect_symbol
 
+#define elf_backend_merge_symbol_attribute     \
+  elfNN_aarch64_merge_symbol_attribute
+
 /* Create .dynbss, and .rela.bss sections in DYNOBJ, and set up shortcuts
    to them in our hash.  */
 #define elf_backend_create_dynamic_sections    \
@@ -9660,11 +11719,14 @@ const struct elf_size_info elfNN_aarch64_size_info =
 #define elf_backend_output_arch_local_syms     \
   elfNN_aarch64_output_arch_local_syms
 
+#define elf_backend_maybe_function_sym         \
+  elfNN_aarch64_maybe_function_sym
+
 #define elf_backend_plt_sym_val                        \
   elfNN_aarch64_plt_sym_val
 
-#define elf_backend_post_process_headers       \
-  elfNN_aarch64_post_process_headers
+#define elf_backend_init_file_header           \
+  elfNN_aarch64_init_file_header
 
 #define elf_backend_relocate_section           \
   elfNN_aarch64_relocate_section
@@ -9687,6 +11749,21 @@ 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_got_header_size            \
+  elfNN_aarch64_got_header_size
+
+#define elf_backend_got_elt_size               \
+  elfNN_aarch64_got_elt_size
+
+#define elf_backend_eh_frame_augmentation_char \
+  elf64_aarch64_eh_frame_augmentation_char
+
 #define elf_backend_can_refcount       1
 #define elf_backend_can_gc_sections    1
 #define elf_backend_plt_readonly       1
@@ -9698,7 +11775,6 @@ const struct elf_size_info elfNN_aarch64_size_info =
 #define elf_backend_default_use_rela_p 1
 #define elf_backend_rela_normal               1
 #define elf_backend_dtrel_excludes_plt 1
-#define elf_backend_got_header_size (GOT_ENTRY_SIZE * 3)
 #define elf_backend_default_execstack  0
 #define elf_backend_extern_protected_data 1
 #define elf_backend_hash_symbol elf_aarch64_hash_symbol