elf32-arm.c (elf32_arm_tls_relax): Note fixme pointed out by AlanM
[platform/upstream/binutils.git] / bfd / elf32-arm.c
index 2168fc0..716b177 100644 (file)
@@ -1,6 +1,6 @@
 /* 32-bit ELF support for ARM
    Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
-   2008, 2009  Free Software Foundation, Inc.
+   2008, 2009, 2010  Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -222,7 +222,7 @@ static reloc_howto_type elf32_arm_howto_table_1[] =
   HOWTO (R_ARM_THM_CALL,       /* type */
         1,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        25,                    /* bitsize */
+        24,                    /* bitsize */
         TRUE,                  /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_signed,/* complain_on_overflow */
@@ -261,18 +261,18 @@ static reloc_howto_type elf32_arm_howto_table_1[] =
         0xffffffff,            /* dst_mask */
         FALSE),                /* pcrel_offset */
 
-  HOWTO (R_ARM_SWI24,          /* type */
+  HOWTO (R_ARM_TLS_DESC,       /* type */
         0,                     /* rightshift */
-        0,                     /* size (0 = byte, 1 = short, 2 = long) */
-        0,                     /* bitsize */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        32,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_signed,/* complain_on_overflow */
+        complain_overflow_bitfield,/* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
-        "R_ARM_SWI24",         /* name */
+        "R_ARM_TLS_DESC",      /* name */
         FALSE,                 /* partial_inplace */
-        0x00000000,            /* src_mask */
-        0x00000000,            /* dst_mask */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
         FALSE),                /* pcrel_offset */
 
   HOWTO (R_ARM_THM_SWI8,       /* type */
@@ -1352,10 +1352,61 @@ static reloc_howto_type elf32_arm_howto_table_1[] =
         0x040f70ff,            /* dst_mask */
         FALSE),                /* pcrel_offset */
 
-  EMPTY_HOWTO (90),   /* Unallocated.  */
-  EMPTY_HOWTO (91),
-  EMPTY_HOWTO (92),
-  EMPTY_HOWTO (93),
+  HOWTO (R_ARM_TLS_GOTDESC,    /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        32,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield,/* complain_on_overflow */
+        NULL,                  /* special_function */
+        "R_ARM_TLS_GOTDESC",   /* name */
+        TRUE,                  /* partial_inplace */
+        0xffffffff,            /* src_mask */
+        0xffffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  HOWTO (R_ARM_TLS_CALL,       /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        24,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont,/* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_ARM_TLS_CALL",      /* name */
+        FALSE,                 /* partial_inplace */
+        0x00ffffff,            /* src_mask */
+        0x00ffffff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  HOWTO (R_ARM_TLS_DESCSEQ,    /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        0,                     /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield,/* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_ARM_TLS_DESCSEQ",   /* name */
+        FALSE,                 /* partial_inplace */
+        0x00000000,            /* src_mask */
+        0x00000000,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  HOWTO (R_ARM_THM_TLS_CALL,   /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        24,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont,/* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_ARM_THM_TLS_CALL",  /* name */
+        FALSE,                 /* partial_inplace */
+        0x07ff07ff,            /* src_mask */
+        0x07ff07ff,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
 
   HOWTO (R_ARM_PLT32_ABS,      /* type */
         0,                     /* rightshift */
@@ -1599,6 +1650,38 @@ static reloc_howto_type elf32_arm_howto_table_1[] =
         0x00000fff,            /* src_mask */
         0x00000fff,            /* dst_mask */
         FALSE),                /* pcrel_offset */
+
+  EMPTY_HOWTO (112),
+  EMPTY_HOWTO (113),
+  EMPTY_HOWTO (114),
+  EMPTY_HOWTO (115),
+  EMPTY_HOWTO (116),
+  EMPTY_HOWTO (117),
+  EMPTY_HOWTO (118),
+  EMPTY_HOWTO (119),
+  EMPTY_HOWTO (120),
+  EMPTY_HOWTO (121),
+  EMPTY_HOWTO (122),
+  EMPTY_HOWTO (123),
+  EMPTY_HOWTO (124),
+  EMPTY_HOWTO (125),
+  EMPTY_HOWTO (126),
+  EMPTY_HOWTO (127),
+  EMPTY_HOWTO (128),
+
+  HOWTO (R_ARM_THM_TLS_DESCSEQ,        /* type */
+        0,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        0,                     /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_bitfield,/* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_ARM_THM_TLS_DESCSEQ",/* name */
+        FALSE,                 /* partial_inplace */
+        0x00000000,            /* src_mask */
+        0x00000000,            /* dst_mask */
+        FALSE),                /* pcrel_offset */
 };
 
 /* 112-127 private relocations
@@ -1721,6 +1804,7 @@ static const struct elf32_arm_reloc_map elf32_arm_reloc_map[] =
     {BFD_RELOC_ARM_RELATIVE,         R_ARM_RELATIVE},
     {BFD_RELOC_ARM_GOTOFF,           R_ARM_GOTOFF32},
     {BFD_RELOC_ARM_GOTPC,            R_ARM_GOTPC},
+    {BFD_RELOC_ARM_GOT_PREL,         R_ARM_GOT_PREL},
     {BFD_RELOC_ARM_GOT32,            R_ARM_GOT32},
     {BFD_RELOC_ARM_PLT32,            R_ARM_PLT32},
     {BFD_RELOC_ARM_TARGET1,         R_ARM_TARGET1},
@@ -1729,6 +1813,12 @@ static const struct elf32_arm_reloc_map elf32_arm_reloc_map[] =
     {BFD_RELOC_ARM_PREL31,          R_ARM_PREL31},
     {BFD_RELOC_ARM_TARGET2,         R_ARM_TARGET2},
     {BFD_RELOC_ARM_PLT32,            R_ARM_PLT32},
+    {BFD_RELOC_ARM_TLS_GOTDESC,      R_ARM_TLS_GOTDESC},
+    {BFD_RELOC_ARM_TLS_CALL,         R_ARM_TLS_CALL},
+    {BFD_RELOC_ARM_THM_TLS_CALL,     R_ARM_THM_TLS_CALL},
+    {BFD_RELOC_ARM_TLS_DESCSEQ,      R_ARM_TLS_DESCSEQ},
+    {BFD_RELOC_ARM_THM_TLS_DESCSEQ,  R_ARM_THM_TLS_DESCSEQ},
+    {BFD_RELOC_ARM_TLS_DESC,         R_ARM_TLS_DESC},
     {BFD_RELOC_ARM_TLS_GD32,        R_ARM_TLS_GD32},
     {BFD_RELOC_ARM_TLS_LDO32,       R_ARM_TLS_LDO32},
     {BFD_RELOC_ARM_TLS_LDM32,       R_ARM_TLS_LDM32},
@@ -1828,7 +1918,7 @@ elf32_arm_nabi_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
        elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12);
 
        /* pr_pid */
-       elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 24);
+       elf_tdata (abfd)->core_lwpid = bfd_get_32 (abfd, note->descdata + 24);
 
        /* pr_reg */
        offset = 72;
@@ -1911,6 +2001,26 @@ typedef unsigned short int insn16;
    section.  */
 #define ELF_DYNAMIC_INTERPRETER     "/usr/lib/ld.so.1"
 
+static const unsigned long tls_trampoline [] =
+  {
+    0xe08e0000,                /* add r0, lr, r0 */
+    0xe5901004,                /* ldr r1, [r0,#4] */
+    0xe12fff11,                /* bx  r1 */
+  };
+
+static const unsigned long dl_tlsdesc_lazy_trampoline [] =
+  {
+    0xe52d2004, /*     push    {r2}                    */
+    0xe59f200c, /*      ldr     r2, [pc, #3f - . - 8]  */
+    0xe59f100c, /*      ldr     r1, [pc, #4f - . - 8]  */
+    0xe79f2002, /* 1:   ldr     r2, [pc, r2]           */
+    0xe081100f, /* 2:   add     r1, pc                 */
+    0xe12fff12, /*      bx      r2                     */
+    0x00000014, /* 3:   .word  _GLOBAL_OFFSET_TABLE_ - 1b - 8
+                               + dl_tlsdesc_lazy_resolver(GOT)   */
+    0x00000018, /* 4:   .word  _GLOBAL_OFFSET_TABLE_ - 2b - 8 */ 
+  };
+
 #ifdef FOUR_WORD_PLT
 
 /* The first entry in a procedure linkage table looks like
@@ -2105,7 +2215,7 @@ static const insn_sequence elf32_arm_stub_short_branch_v4t_thumb_arm[] =
    blx to reach the stub if necessary.  */
 static const insn_sequence elf32_arm_stub_long_branch_any_arm_pic[] =
   {
-    ARM_INSN(0xe59fc000),             /* ldr   r12, [pc] */
+    ARM_INSN(0xe59fc000),             /* ldr   ip, [pc] */
     ARM_INSN(0xe08ff00c),             /* add   pc, pc, ip */
     DATA_WORD(0, R_ARM_REL32, -4),    /* dcd   R_ARM_REL32(X-4) */
   };
@@ -2116,7 +2226,7 @@ static const insn_sequence elf32_arm_stub_long_branch_any_arm_pic[] =
    ARMv7).  */
 static const insn_sequence elf32_arm_stub_long_branch_any_thumb_pic[] =
   {
-    ARM_INSN(0xe59fc004),             /* ldr   r12, [pc, #4] */
+    ARM_INSN(0xe59fc004),             /* ldr   ip, [pc, #4] */
     ARM_INSN(0xe08fc00c),             /* add   ip, pc, ip */
     ARM_INSN(0xe12fff1c),             /* bx    ip */
     DATA_WORD(0, R_ARM_REL32, 0),     /* dcd   R_ARM_REL32(X) */
@@ -2166,6 +2276,26 @@ static const insn_sequence elf32_arm_stub_long_branch_v4t_thumb_thumb_pic[] =
     DATA_WORD(0, R_ARM_REL32, 0),     /* dcd  R_ARM_REL32(X) */
   };
 
+/* Thumb2/ARM -> TLS trampoline.  Lowest common denominator, which is a
+   long PIC stub.  We can use r1 as a scratch -- and cannot use ip.  */
+static const insn_sequence elf32_arm_stub_long_branch_any_tls_pic[] =
+{
+    ARM_INSN(0xe59f1000),             /* ldr   r1, [pc] */
+    ARM_INSN(0xe08ff001),             /* add   pc, pc, r1 */
+    DATA_WORD(0, R_ARM_REL32, -4),    /* dcd   R_ARM_REL32(X-4) */
+};
+
+/* V4T Thumb -> TLS trampoline.  lowest common denominator, which is a
+   long PIC stub.  We can use r1 as a scratch -- and cannot use ip.  */
+static const insn_sequence elf32_arm_stub_long_branch_v4t_thumb_tls_pic[] =
+{
+    THUMB16_INSN(0x4778),             /* bx   pc */
+    THUMB16_INSN(0x46c0),             /* nop */
+    ARM_INSN(0xe59f1000),             /* ldr  r1, [pc, #0] */
+    ARM_INSN(0xe081f00f),             /* add  pc, r1, pc */
+    DATA_WORD(0, R_ARM_REL32, -4),    /* dcd  R_ARM_REL32(X) */
+};
+
 /* Cortex-A8 erratum-workaround stubs.  */
 
 /* Stub used for conditional branches (which may be beyond +/-1MB away, so we
@@ -2217,6 +2347,8 @@ static const insn_sequence elf32_arm_stub_a8_veneer_blx[] =
   DEF_STUB(long_branch_v4t_arm_thumb_pic) \
   DEF_STUB(long_branch_v4t_thumb_arm_pic) \
   DEF_STUB(long_branch_thumb_only_pic) \
+  DEF_STUB(long_branch_any_tls_pic) \
+  DEF_STUB(long_branch_v4t_thumb_tls_pic) \
   DEF_STUB(a8_veneer_b_cond) \
   DEF_STUB(a8_veneer_b) \
   DEF_STUB(a8_veneer_bl) \
@@ -2399,6 +2531,7 @@ struct a8_erratum_fix {
   unsigned long orig_insn;
   char *stub_name;
   enum elf32_arm_stub_type stub_type;
+  int st_type;
 };
 
 /* A table of relocs applied to branches which might trigger Cortex-A8
@@ -2407,9 +2540,10 @@ struct a8_erratum_fix {
 struct a8_erratum_reloc {
   bfd_vma from;
   bfd_vma destination;
+  struct elf32_arm_link_hash_entry *hash;
+  const char *sym_name;
   unsigned int r_type;
   unsigned char st_type;
-  const char *sym_name;
   bfd_boolean non_a8_stub;
 };
 
@@ -2423,6 +2557,9 @@ struct elf_arm_obj_tdata
   /* tls_type for each local got entry.  */
   char *local_got_tls_type;
 
+  /* GOTPLT entries for TLS descriptors.  */
+  bfd_vma *local_tlsdesc_gotent;
+
   /* Zero to warn when linking objects with incompatible enum sizes.  */
   int no_enum_size_warning;
 
@@ -2436,38 +2573,21 @@ struct elf_arm_obj_tdata
 #define elf32_arm_local_got_tls_type(bfd) \
   (elf_arm_tdata (bfd)->local_got_tls_type)
 
+#define elf32_arm_local_tlsdesc_gotent(bfd) \
+  (elf_arm_tdata (bfd)->local_tlsdesc_gotent)
+
 #define is_arm_elf(bfd) \
   (bfd_get_flavour (bfd) == bfd_target_elf_flavour \
    && elf_tdata (bfd) != NULL \
-   && elf_object_id (bfd) == ARM_ELF_TDATA)
+   && elf_object_id (bfd) == ARM_ELF_DATA)
 
 static bfd_boolean
 elf32_arm_mkobject (bfd *abfd)
 {
   return bfd_elf_allocate_object (abfd, sizeof (struct elf_arm_obj_tdata),
-                                 ARM_ELF_TDATA);
+                                 ARM_ELF_DATA);
 }
 
-/* The ARM linker needs to keep track of the number of relocs that it
-   decides to copy in check_relocs for each symbol.  This is so that
-   it can discard PC relative relocs if it doesn't need them when
-   linking with -Bsymbolic.  We store the information in a field
-   extending the regular ELF linker hash table.  */
-
-/* This structure keeps track of the number of relocs we have copied
-   for a given symbol.  */
-struct elf32_arm_relocs_copied
-  {
-    /* Next section.  */
-    struct elf32_arm_relocs_copied * next;
-    /* A section in dynobj.  */
-    asection * section;
-    /* Number of relocs copied in this section.  */
-    bfd_size_type count;
-    /* Number of PC-relative relocs copied in this section.  */
-    bfd_size_type pc_count;
-  };
-
 #define elf32_arm_hash_entry(ent) ((struct elf32_arm_link_hash_entry *)(ent))
 
 /* Arm ELF linker hash entry.  */
@@ -2475,8 +2595,8 @@ struct elf32_arm_link_hash_entry
   {
     struct elf_link_hash_entry root;
 
-    /* Number of PC relative relocs copied for this symbol.  */
-    struct elf32_arm_relocs_copied * relocs_copied;
+    /* Track dynamic relocs copied for this symbol.  */
+    struct elf_dyn_relocs *dyn_relocs;
 
     /* We reference count Thumb references to a PLT entry separately,
        so that we can emit the Thumb trampoline only if needed.  */
@@ -2495,8 +2615,14 @@ struct elf32_arm_link_hash_entry
 #define GOT_NORMAL     1
 #define GOT_TLS_GD     2
 #define GOT_TLS_IE     4
+#define GOT_TLS_GDESC  8
+#define GOT_TLS_GD_ANY_P(type) ((type & GOT_TLS_GD) || (type & GOT_TLS_GDESC))
     unsigned char tls_type;
 
+    /* Offset of the GOTPLT entry reserved for the TLS descriptor,
+       starting at the end of the jump table.  */
+    bfd_vma tlsdesc_got;
+
     /* The symbol marking the real symbol location for exported thumb
        symbols with Arm stubs.  */
     struct elf_link_hash_entry *export_glue;
@@ -2515,7 +2641,8 @@ struct elf32_arm_link_hash_entry
 
 /* Get the ARM elf linker hash table from a link_info structure.  */
 #define elf32_arm_hash_table(info) \
-  ((struct elf32_arm_link_hash_table *) ((info)->hash))
+  (elf_hash_table_id ((struct elf_link_hash_table *) ((info)->hash)) \
+  == ARM_ELF_DATA ? ((struct elf32_arm_link_hash_table *) ((info)->hash)) : NULL)
 
 #define arm_stub_hash_lookup(table, string, create, copy) \
   ((struct elf32_arm_stub_hash_entry *) \
@@ -2532,6 +2659,9 @@ struct map_stub
   asection *stub_sec;
 };
 
+#define elf32_arm_compute_jump_table_size(htab) \
+  ((htab)->next_tls_desc_index * 4)
+
 /* ARM ELF linker hash table.  */
 struct elf32_arm_link_hash_table
 {
@@ -2610,18 +2740,32 @@ struct elf32_arm_link_hash_table
   /* True if the target uses REL relocations.  */
   int use_rel;
 
+  /* The index of the next unused R_ARM_TLS_DESC slot in .rel.plt.  */
+  bfd_vma next_tls_desc_index;
+
+  /* How many R_ARM_TLS_DESC relocations were generated so far.  */
+  bfd_vma num_tls_desc;
+
   /* Short-cuts to get to dynamic linker sections.  */
-  asection *sgot;
-  asection *sgotplt;
-  asection *srelgot;
-  asection *splt;
-  asection *srelplt;
   asection *sdynbss;
   asection *srelbss;
 
   /* The (unloaded but important) VxWorks .rela.plt.unloaded section.  */
   asection *srelplt2;
 
+  /* 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 dt_tlsdesc_plt;
+
+  /* The offset into sgot of the GOT entry used by the PLT entry
+     above.  */
+  bfd_vma dt_tlsdesc_got;    
+
+  /* Offset in .plt section of tls_arm_trampoline.  */
+  bfd_vma tls_trampoline;
+
   /* Data for R_ARM_TLS_LDM32 relocations.  */
   union
   {
@@ -2635,6 +2779,10 @@ struct elf32_arm_link_hash_table
   /* For convenience in allocate_dynrelocs.  */
   bfd * obfd;
 
+  /* The amount of space used by the reserved portion of the sgotplt
+     section, plus whatever space is used by the jump slots.  */
+  bfd_vma sgotplt_jump_table_size;
+
   /* The stub hash table.  */
   struct bfd_hash_table stub_hash_table;
 
@@ -2649,6 +2797,9 @@ struct elf32_arm_link_hash_table
      information on stub grouping.  */
   struct map_stub *stub_group;
 
+  /* Number of elements in stub_group.  */
+  int top_id;
+
   /* Assorted information used by elf32_arm_size_stubs.  */
   unsigned int bfd_count;
   int top_index;
@@ -2679,8 +2830,9 @@ elf32_arm_link_hash_newfunc (struct bfd_hash_entry * entry,
                                     table, string));
   if (ret != NULL)
     {
-      ret->relocs_copied = NULL;
+      ret->dyn_relocs = NULL;
       ret->tls_type = GOT_UNKNOWN;
+      ret->tlsdesc_got = (bfd_vma) -1;
       ret->plt_thumb_refcount = 0;
       ret->plt_maybe_thumb_refcount = 0;
       ret->plt_got_offset = -1;
@@ -2744,6 +2896,9 @@ create_got_section (bfd *dynobj, struct bfd_link_info *info)
   struct elf32_arm_link_hash_table *htab;
 
   htab = elf32_arm_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
+
   /* BPABI objects never have a GOT, or associated sections.  */
   if (htab->symbian_p)
     return TRUE;
@@ -2751,15 +2906,6 @@ create_got_section (bfd *dynobj, struct bfd_link_info *info)
   if (! _bfd_elf_create_got_section (dynobj, info))
     return FALSE;
 
-  htab->sgot = bfd_get_section_by_name (dynobj, ".got");
-  htab->sgotplt = bfd_get_section_by_name (dynobj, ".got.plt");
-  if (!htab->sgot || !htab->sgotplt)
-    abort ();
-
-  htab->srelgot = bfd_get_section_by_name (dynobj,
-                                          RELOC_SECTION (htab, ".got"));
-  if (htab->srelgot == NULL)
-    return FALSE;
   return TRUE;
 }
 
@@ -2773,15 +2919,15 @@ elf32_arm_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
   struct elf32_arm_link_hash_table *htab;
 
   htab = elf32_arm_hash_table (info);
-  if (!htab->sgot && !create_got_section (dynobj, info))
+  if (htab == NULL)
+    return FALSE;
+
+  if (!htab->root.sgot && !create_got_section (dynobj, info))
     return FALSE;
 
   if (!_bfd_elf_create_dynamic_sections (dynobj, info))
     return FALSE;
 
-  htab->splt = bfd_get_section_by_name (dynobj, ".plt");
-  htab->srelplt = bfd_get_section_by_name (dynobj,
-                                          RELOC_SECTION (htab, ".plt"));
   htab->sdynbss = bfd_get_section_by_name (dynobj, ".dynbss");
   if (!info->shared)
     htab->srelbss = bfd_get_section_by_name (dynobj,
@@ -2807,8 +2953,8 @@ elf32_arm_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
        }
     }
 
-  if (!htab->splt
-      || !htab->srelplt
+  if (!htab->root.splt
+      || !htab->root.srelplt
       || !htab->sdynbss
       || (!info->shared && !htab->srelbss))
     abort ();
@@ -2828,21 +2974,21 @@ elf32_arm_copy_indirect_symbol (struct bfd_link_info *info,
   edir = (struct elf32_arm_link_hash_entry *) dir;
   eind = (struct elf32_arm_link_hash_entry *) ind;
 
-  if (eind->relocs_copied != NULL)
+  if (eind->dyn_relocs != NULL)
     {
-      if (edir->relocs_copied != NULL)
+      if (edir->dyn_relocs != NULL)
        {
-         struct elf32_arm_relocs_copied **pp;
-         struct elf32_arm_relocs_copied *p;
+         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->relocs_copied; (p = *pp) != NULL; )
+         for (pp = &eind->dyn_relocs; (p = *pp) != NULL; )
            {
-             struct elf32_arm_relocs_copied *q;
+             struct elf_dyn_relocs *q;
 
-             for (q = edir->relocs_copied; q != NULL; q = q->next)
-               if (q->section == p->section)
+             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;
@@ -2852,11 +2998,11 @@ elf32_arm_copy_indirect_symbol (struct bfd_link_info *info,
              if (q == NULL)
                pp = &p->next;
            }
-         *pp = edir->relocs_copied;
+         *pp = edir->dyn_relocs;
        }
 
-      edir->relocs_copied = eind->relocs_copied;
-      eind->relocs_copied = NULL;
+      edir->dyn_relocs = eind->dyn_relocs;
+      eind->dyn_relocs = NULL;
     }
 
   if (ind->root.type == bfd_link_hash_indirect)
@@ -2891,20 +3037,21 @@ elf32_arm_link_hash_table_create (bfd *abfd)
 
   if (!_bfd_elf_link_hash_table_init (& ret->root, abfd,
                                      elf32_arm_link_hash_newfunc,
-                                     sizeof (struct elf32_arm_link_hash_entry)))
+                                     sizeof (struct elf32_arm_link_hash_entry),
+                                     ARM_ELF_DATA))
     {
       free (ret);
       return NULL;
     }
 
-  ret->sgot = NULL;
-  ret->sgotplt = NULL;
-  ret->srelgot = NULL;
-  ret->splt = NULL;
-  ret->srelplt = NULL;
   ret->sdynbss = NULL;
   ret->srelbss = NULL;
   ret->srelplt2 = NULL;
+  ret->dt_tlsdesc_plt = 0;
+  ret->dt_tlsdesc_got = 0;
+  ret->tls_trampoline = 0;
+  ret->next_tls_desc_index = 0;
+  ret->num_tls_desc = 0;
   ret->thumb_glue_size = 0;
   ret->arm_glue_size = 0;
   ret->bx_glue_size = 0;
@@ -2936,6 +3083,7 @@ elf32_arm_link_hash_table_create (bfd *abfd)
   ret->add_stub_section = NULL;
   ret->layout_sections_again = NULL;
   ret->stub_group = NULL;
+  ret->top_id = 0;
   ret->bfd_count = 0;
   ret->top_index = 0;
   ret->input_list = NULL;
@@ -2971,6 +3119,9 @@ using_thumb_only (struct elf32_arm_link_hash_table *globals)
                                       Tag_CPU_arch);
   int profile;
 
+  if (arch == TAG_CPU_ARCH_V6_M || arch == TAG_CPU_ARCH_V6S_M)
+    return TRUE;
+
   if (arch != TAG_CPU_ARCH_V7 && arch != TAG_CPU_ARCH_V7E_M)
     return FALSE;
 
@@ -3038,7 +3189,7 @@ static enum elf32_arm_stub_type
 arm_type_of_stub (struct bfd_link_info *info,
                  asection *input_sec,
                  const Elf_Internal_Rela *rel,
-                 unsigned char st_type,
+                 int *actual_st_type,
                  struct elf32_arm_link_hash_entry *hash,
                  bfd_vma destination,
                  asection *sym_sec,
@@ -3053,6 +3204,7 @@ arm_type_of_stub (struct bfd_link_info *info,
   int thumb_only;
   enum elf32_arm_stub_type stub_type = arm_stub_none;
   int use_plt = 0;
+  int st_type = *actual_st_type;
 
   /* We don't know the actual type of destination in case it is of
      type STT_SECTION: give up.  */
@@ -3060,6 +3212,8 @@ arm_type_of_stub (struct bfd_link_info *info,
     return stub_type;
 
   globals = elf32_arm_hash_table (info);
+  if (globals == NULL)
+    return stub_type;
 
   thumb_only = using_thumb_only (globals);
 
@@ -3070,14 +3224,15 @@ arm_type_of_stub (struct bfd_link_info *info,
              + input_sec->output_section->vma
              + rel->r_offset);
 
-  branch_offset = (bfd_signed_vma)(destination - location);
-
   r_type = ELF32_R_TYPE (rel->r_info);
 
   /* Keep a simpler condition, for the sake of clarity.  */
-  if (globals->splt != NULL && hash != NULL && hash->root.plt.offset != (bfd_vma) -1)
+  if (globals->root.splt != NULL
+      && hash != NULL
+      && hash->root.plt.offset != (bfd_vma) -1)
     {
       use_plt = 1;
+
       /* Note when dealing with PLT entries: the main PLT stub is in
         ARM mode, so if the branch is in Thumb mode, another
         Thumb->ARM stub will be inserted later just before the ARM
@@ -3086,9 +3241,17 @@ arm_type_of_stub (struct bfd_link_info *info,
         Thumb->Arm one and branch directly to the ARM PLT entry
         because it avoids spreading offset corrections in several
         places.  */
+
+      destination = (globals->root.splt->output_section->vma
+                    + globals->root.splt->output_offset
+                    + hash->root.plt.offset);
+      st_type = STT_FUNC;
     }
 
-  if (r_type == R_ARM_THM_CALL || r_type == R_ARM_THM_JUMP24)
+  branch_offset = (bfd_signed_vma)(destination - location);
+
+  if (r_type == R_ARM_THM_CALL || r_type == R_ARM_THM_JUMP24
+      || r_type == R_ARM_THM_TLS_CALL)
     {
       /* Handle cases where:
         - this call goes too far (different Thumb/Thumb2 max
@@ -3105,7 +3268,8 @@ arm_type_of_stub (struct bfd_link_info *info,
              && (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
                  || (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET)))
          || ((st_type != STT_ARM_TFUNC)
-             && (((r_type == R_ARM_THM_CALL) && !globals->use_blx)
+             && (((r_type == R_ARM_THM_CALL
+                   || r_type == R_ARM_THM_TLS_CALL) && !globals->use_blx)
                  || (r_type == R_ARM_THM_JUMP24))
              && !use_plt))
        {
@@ -3156,18 +3320,21 @@ arm_type_of_stub (struct bfd_link_info *info,
                     sym_sec->owner, input_bfd, name);
                }
 
-             stub_type = (info->shared | globals->pic_veneer)
+             stub_type =
+               (info->shared | globals->pic_veneer)
                /* PIC stubs.  */
-               ? ((globals->use_blx
-                   && (r_type ==R_ARM_THM_CALL))
-                  /* V5T and above.  */
-                  ? arm_stub_long_branch_any_arm_pic
-                  /* V4T PIC stub.  */
-                  : arm_stub_long_branch_v4t_thumb_arm_pic)
+               ? (r_type == R_ARM_THM_TLS_CALL
+                  /* TLS PIC stubs */
+                  ? (globals->use_blx ? arm_stub_long_branch_any_tls_pic
+                     : arm_stub_long_branch_v4t_thumb_tls_pic)
+                  : ((globals->use_blx && r_type == R_ARM_THM_CALL)
+                     /* V5T PIC and above.  */
+                     ? arm_stub_long_branch_any_arm_pic
+                     /* V4T PIC stub.  */
+                     : arm_stub_long_branch_v4t_thumb_arm_pic))
 
                /* non-PIC stubs.  */
-               : ((globals->use_blx
-                   && (r_type ==R_ARM_THM_CALL))
+               : ((globals->use_blx && r_type == R_ARM_THM_CALL)
                   /* V5T and above.  */
                   ? arm_stub_long_branch_any_any
                   /* V4T.  */
@@ -3181,7 +3348,10 @@ arm_type_of_stub (struct bfd_link_info *info,
            }
        }
     }
-  else if (r_type == R_ARM_CALL || r_type == R_ARM_JUMP24 || r_type == R_ARM_PLT32)
+  else if (r_type == R_ARM_CALL
+          || r_type == R_ARM_JUMP24
+          || r_type == R_ARM_PLT32
+          || r_type == R_ARM_TLS_CALL)
     {
       if (st_type == STT_ARM_TFUNC)
        {
@@ -3201,7 +3371,7 @@ arm_type_of_stub (struct bfd_link_info *info,
             the mode change (bit 24 (H) of BLX encoding).  */
          if (branch_offset > (ARM_MAX_FWD_BRANCH_OFFSET + 2)
              || (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET)
-             || ((r_type == R_ARM_CALL) && !globals->use_blx)
+             || (r_type == R_ARM_CALL && !globals->use_blx)
              || (r_type == R_ARM_JUMP24)
              || (r_type == R_ARM_PLT32))
            {
@@ -3227,15 +3397,23 @@ arm_type_of_stub (struct bfd_link_info *info,
          if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
              || (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET))
            {
-             stub_type = (info->shared | globals->pic_veneer)
+             stub_type =
+               (info->shared | globals->pic_veneer)
                /* PIC stubs.  */
-               ? arm_stub_long_branch_any_arm_pic
+               ? (r_type == R_ARM_TLS_CALL
+                  /* TLS PIC Stub */
+                  ? arm_stub_long_branch_any_tls_pic
+                  : arm_stub_long_branch_any_arm_pic)
                /* non-PIC stubs.  */
                : arm_stub_long_branch_any_any;
            }
        }
     }
 
+  /* If a stub is needed, record the actual destination type.  */
+  if (stub_type != arm_stub_none)
+    *actual_st_type = st_type;
+
   return stub_type;
 }
 
@@ -3245,31 +3423,36 @@ static char *
 elf32_arm_stub_name (const asection *input_section,
                     const asection *sym_sec,
                     const struct elf32_arm_link_hash_entry *hash,
-                    const Elf_Internal_Rela *rel)
+                    const Elf_Internal_Rela *rel,
+                    enum elf32_arm_stub_type stub_type)
 {
   char *stub_name;
   bfd_size_type len;
 
   if (hash)
     {
-      len = 8 + 1 + strlen (hash->root.root.root.string) + 1 + 8 + 1;
+      len = 8 + 1 + strlen (hash->root.root.root.string) + 1 + 8 + 1 + 2 + 1;
       stub_name = (char *) bfd_malloc (len);
       if (stub_name != NULL)
-       sprintf (stub_name, "%08x_%s+%x",
+       sprintf (stub_name, "%08x_%s+%x_%d",
                 input_section->id & 0xffffffff,
                 hash->root.root.root.string,
-                (int) rel->r_addend & 0xffffffff);
+                (int) rel->r_addend & 0xffffffff,
+                (int) stub_type);
     }
   else
     {
-      len = 8 + 1 + 8 + 1 + 8 + 1 + 8 + 1;
+      len = 8 + 1 + 8 + 1 + 8 + 1 + 8 + 1 + 2 + 1;
       stub_name = (char *) bfd_malloc (len);
       if (stub_name != NULL)
-       sprintf (stub_name, "%08x_%x:%x+%x",
+       sprintf (stub_name, "%08x_%x:%x+%x_%d",
                 input_section->id & 0xffffffff,
                 sym_sec->id & 0xffffffff,
-                (int) ELF32_R_SYM (rel->r_info) & 0xffffffff,
-                (int) rel->r_addend & 0xffffffff);
+                ELF32_R_TYPE (rel->r_info) == R_ARM_TLS_CALL
+                || ELF32_R_TYPE (rel->r_info) == R_ARM_THM_TLS_CALL
+                ? 0 : (int) ELF32_R_SYM (rel->r_info) & 0xffffffff,
+                (int) rel->r_addend & 0xffffffff,
+                (int) stub_type);
     }
 
   return stub_name;
@@ -3283,7 +3466,8 @@ elf32_arm_get_stub_entry (const asection *input_section,
                          const asection *sym_sec,
                          struct elf_link_hash_entry *hash,
                          const Elf_Internal_Rela *rel,
-                         struct elf32_arm_link_hash_table *htab)
+                         struct elf32_arm_link_hash_table *htab,
+                         enum elf32_arm_stub_type stub_type)
 {
   struct elf32_arm_stub_hash_entry *stub_entry;
   struct elf32_arm_link_hash_entry *h = (struct elf32_arm_link_hash_entry *) hash;
@@ -3301,7 +3485,8 @@ elf32_arm_get_stub_entry (const asection *input_section,
 
   if (h != NULL && h->stub_cache != NULL
       && h->stub_cache->h == h
-      && h->stub_cache->id_sec == id_sec)
+      && h->stub_cache->id_sec == id_sec
+      && h->stub_cache->stub_type == stub_type)
     {
       stub_entry = h->stub_cache;
     }
@@ -3309,7 +3494,7 @@ elf32_arm_get_stub_entry (const asection *input_section,
     {
       char *stub_name;
 
-      stub_name = elf32_arm_stub_name (id_sec, sym_sec, h, rel);
+      stub_name = elf32_arm_stub_name (id_sec, sym_sec, h, rel, stub_type);
       if (stub_name == NULL)
        return NULL;
 
@@ -3428,29 +3613,85 @@ put_thumb_insn (struct elf32_arm_link_hash_table * htab,
     bfd_putb16 (val, ptr);
 }
 
+/* If it's possible to change R_TYPE to a more efficient access
+   model, return the new reloc type.  */
+
+static unsigned
+elf32_arm_tls_transition (struct bfd_link_info *info, int r_type, 
+                         struct elf_link_hash_entry *h)
+{
+  int is_local = (h == NULL);
+
+  if (info->shared || (h && h->root.type == bfd_link_hash_undefweak))
+    return r_type;
+
+  /* We do not support relaxations for Old TLS models.  */ 
+  switch (r_type)
+    {
+    case R_ARM_TLS_GOTDESC:
+    case R_ARM_TLS_CALL:
+    case R_ARM_THM_TLS_CALL:
+    case R_ARM_TLS_DESCSEQ:
+    case R_ARM_THM_TLS_DESCSEQ:
+      return is_local ? R_ARM_TLS_LE32 : R_ARM_TLS_IE32;
+    }
+
+  return r_type;
+}
+
 static bfd_reloc_status_type elf32_arm_final_link_relocate
   (reloc_howto_type *, bfd *, bfd *, asection *, bfd_byte *,
    Elf_Internal_Rela *, bfd_vma, struct bfd_link_info *, asection *,
    const char *, int, struct elf_link_hash_entry *, bfd_boolean *, char **);
 
+static unsigned int
+arm_stub_required_alignment (enum elf32_arm_stub_type stub_type)
+{
+  switch (stub_type)
+    {
+    case arm_stub_a8_veneer_b_cond:
+    case arm_stub_a8_veneer_b:
+    case arm_stub_a8_veneer_bl:
+      return 2;
+
+    case arm_stub_long_branch_any_any:
+    case arm_stub_long_branch_v4t_arm_thumb:
+    case arm_stub_long_branch_thumb_only:
+    case arm_stub_long_branch_v4t_thumb_thumb:
+    case arm_stub_long_branch_v4t_thumb_arm:
+    case arm_stub_short_branch_v4t_thumb_arm:
+    case arm_stub_long_branch_any_arm_pic:
+    case arm_stub_long_branch_any_thumb_pic:
+    case arm_stub_long_branch_v4t_thumb_thumb_pic:
+    case arm_stub_long_branch_v4t_arm_thumb_pic:
+    case arm_stub_long_branch_v4t_thumb_arm_pic:
+    case arm_stub_long_branch_thumb_only_pic:
+    case arm_stub_long_branch_any_tls_pic:
+    case arm_stub_long_branch_v4t_thumb_tls_pic:
+    case arm_stub_a8_veneer_blx:
+      return 4;
+    
+    default:
+      abort ();  /* Should be unreachable.  */
+    }
+}
+
 static bfd_boolean
 arm_build_one_stub (struct bfd_hash_entry *gen_entry,
                    void * in_arg)
 {
 #define MAXRELOCS 2
   struct elf32_arm_stub_hash_entry *stub_entry;
+  struct elf32_arm_link_hash_table *globals;
   struct bfd_link_info *info;
-  struct elf32_arm_link_hash_table *htab;
   asection *stub_sec;
   bfd *stub_bfd;
-  bfd_vma stub_addr;
   bfd_byte *loc;
   bfd_vma sym_value;
   int template_size;
   int size;
   const insn_sequence *template_sequence;
   int i;
-  struct elf32_arm_link_hash_table * globals;
   int stub_reloc_idx[MAXRELOCS] = {-1, -1};
   int stub_reloc_offset[MAXRELOCS] = {0, 0};
   int nrelocs = 0;
@@ -3460,26 +3701,22 @@ arm_build_one_stub (struct bfd_hash_entry *gen_entry,
   info = (struct bfd_link_info *) in_arg;
 
   globals = elf32_arm_hash_table (info);
+  if (globals == NULL)
+    return FALSE;
 
-  htab = elf32_arm_hash_table (info);
   stub_sec = stub_entry->stub_sec;
 
-  if ((htab->fix_cortex_a8 < 0)
-      != (stub_entry->stub_type >= arm_stub_a8_veneer_lwm))
-    /* We have to do the a8 fixes last, as they are less aligned than
-       the other veneers.  */
+  if ((globals->fix_cortex_a8 < 0)
+      != (arm_stub_required_alignment (stub_entry->stub_type) == 2))
+    /* We have to do less-strictly-aligned fixes last.  */
     return TRUE;
-  
+
   /* Make a note of the offset within the stubs for this entry.  */
   stub_entry->stub_offset = stub_sec->size;
   loc = stub_sec->contents + stub_entry->stub_offset;
 
   stub_bfd = stub_sec->owner;
 
-  /* This is the address of the start of the stub.  */
-  stub_addr = stub_sec->output_section->vma + stub_sec->output_offset
-    + stub_entry->stub_offset;
-
   /* This is the address of the stub destination.  */
   sym_value = (stub_entry->target_value
               + stub_entry->target_section->output_offset
@@ -3504,17 +3741,17 @@ arm_build_one_stub (struct bfd_hash_entry *gen_entry,
                 BFD_ASSERT ((data & 0xff00) == 0xd000);
                 data |= ((stub_entry->orig_insn >> 22) & 0xf) << 8;
              }
-           put_thumb_insn (globals, stub_bfd, data, loc + size);
+           bfd_put_16 (stub_bfd, data, loc + size);
            size += 2;
          }
          break;
 
        case THUMB32_TYPE:
-          put_thumb_insn (globals, stub_bfd,
-                          (template_sequence[i].data >> 16) & 0xffff,
-                          loc + size);
-          put_thumb_insn (globals, stub_bfd, template_sequence[i].data & 0xffff,
-                          loc + size + 2);
+         bfd_put_16 (stub_bfd,
+                     (template_sequence[i].data >> 16) & 0xffff,
+                     loc + size);
+         bfd_put_16 (stub_bfd, template_sequence[i].data & 0xffff,
+                     loc + size + 2);
           if (template_sequence[i].r_type != R_ARM_NONE)
             {
               stub_reloc_idx[nrelocs] = i;
@@ -3524,8 +3761,8 @@ arm_build_one_stub (struct bfd_hash_entry *gen_entry,
           break;
 
        case ARM_TYPE:
-         put_arm_insn (globals, stub_bfd, template_sequence[i].data,
-                        loc + size);
+         bfd_put_32 (stub_bfd, template_sequence[i].data,
+                     loc + size);
          /* Handle cases where the target is encoded within the
             instruction.  */
          if (template_sequence[i].r_type == R_ARM_JUMP24)
@@ -3604,11 +3841,23 @@ arm_build_one_stub (struct bfd_hash_entry *gen_entry,
       }
     else
       {
-       _bfd_final_link_relocate (elf32_arm_howto_from_type
-           (template_sequence[stub_reloc_idx[i]].r_type), stub_bfd, stub_sec,
-         stub_sec->contents, stub_entry->stub_offset + stub_reloc_offset[i],
-         sym_value + stub_entry->target_addend,
-         template_sequence[stub_reloc_idx[i]].reloc_addend);
+       Elf_Internal_Rela rel;
+       bfd_boolean unresolved_reloc;
+       char *error_message;
+       bfd_vma points_to = sym_value + stub_entry->target_addend
+         + template_sequence[stub_reloc_idx[i]].reloc_addend;
+
+       rel.r_offset = stub_entry->stub_offset + stub_reloc_offset[i];
+       rel.r_info = ELF32_R_INFO (0,
+                                   template_sequence[stub_reloc_idx[i]].r_type);
+       rel.r_addend = 0;
+
+       elf32_arm_final_link_relocate (elf32_arm_howto_from_type
+           (template_sequence[stub_reloc_idx[i]].r_type),
+         stub_bfd, info->output_bfd, stub_sec, stub_sec->contents, &rel,
+         points_to, info, stub_entry->target_section, "", stub_entry->st_type,
+         (struct elf_link_hash_entry *) stub_entry->h, &unresolved_reloc,
+         &error_message);
       }
 
   return TRUE;
@@ -3628,7 +3877,12 @@ find_stub_size_and_template (enum elf32_arm_stub_type stub_type,
   unsigned int size;
 
   template_sequence = stub_definitions[stub_type].template_sequence;
+  if (stub_template)
+    *stub_template = template_sequence;
+
   template_size = stub_definitions[stub_type].template_size;
+  if (stub_template_size)
+    *stub_template_size = template_size;
 
   size = 0;
   for (i = 0; i < template_size; i++)
@@ -3647,16 +3901,10 @@ find_stub_size_and_template (enum elf32_arm_stub_type stub_type,
 
        default:
          BFD_FAIL ();
-         return FALSE;
+         return 0;
        }
     }
 
-  if (stub_template)
-    *stub_template = template_sequence;
-
-  if (stub_template_size)
-    *stub_template_size = template_size;
-
   return size;
 }
 
@@ -3665,16 +3913,14 @@ find_stub_size_and_template (enum elf32_arm_stub_type stub_type,
 
 static bfd_boolean
 arm_size_one_stub (struct bfd_hash_entry *gen_entry,
-                  void * in_arg)
+                  void *in_arg ATTRIBUTE_UNUSED)
 {
   struct elf32_arm_stub_hash_entry *stub_entry;
-  struct elf32_arm_link_hash_table *htab;
   const insn_sequence *template_sequence;
   int template_size, size;
 
   /* Massage our args to the form they really have.  */
   stub_entry = (struct elf32_arm_stub_hash_entry *) gen_entry;
-  htab = (struct elf32_arm_link_hash_table *) in_arg;
 
   BFD_ASSERT((stub_entry->stub_type > arm_stub_none)
             && stub_entry->stub_type < ARRAY_SIZE(stub_definitions));
@@ -3710,6 +3956,8 @@ elf32_arm_setup_section_lists (bfd *output_bfd,
   bfd_size_type amt;
   struct elf32_arm_link_hash_table *htab = elf32_arm_hash_table (info);
 
+  if (htab == NULL)
+    return 0;
   if (! is_elf_hash_table (htab))
     return 0;
 
@@ -3733,6 +3981,7 @@ elf32_arm_setup_section_lists (bfd *output_bfd,
   htab->stub_group = (struct map_stub *) bfd_zmalloc (amt);
   if (htab->stub_group == NULL)
     return -1;
+  htab->top_id = top_id;
 
   /* We can't use output_bfd->section_count here to find the top output
      section index as some sections may have been removed, and
@@ -3781,6 +4030,9 @@ elf32_arm_next_input_section (struct bfd_link_info *info,
 {
   struct elf32_arm_link_hash_table *htab = elf32_arm_hash_table (info);
 
+  if (htab == NULL)
+    return;
+
   if (isec->output_section->index <= htab->top_index)
     {
       asection **list = htab->input_list + isec->output_section->index;
@@ -3940,6 +4192,9 @@ cortex_a8_erratum_scan (bfd *input_bfd,
   unsigned int num_a8_fixes = *num_a8_fixes_p;
   unsigned int a8_fix_table_size = *a8_fix_table_size_p;
 
+  if (htab == NULL)
+    return FALSE;
+
   for (section = input_bfd->sections;
        section != NULL;
        section = section->next)
@@ -4015,14 +4270,14 @@ cortex_a8_erratum_scan (bfd *input_bfd,
                }
 
              is_32bit_branch = is_b || is_bl || is_blx || is_bcc;
-                          
+
               if (((base_vma + i) & 0xfff) == 0xffe
                  && insn_32bit
                  && is_32bit_branch
                  && last_was_32bit
                  && ! last_was_branch)
                 {
-                  bfd_signed_vma offset;
+                  bfd_signed_vma offset = 0;
                   bfd_boolean force_target_arm = FALSE;
                  bfd_boolean force_target_thumb = FALSE;
                   bfd_vma target;
@@ -4039,6 +4294,7 @@ cortex_a8_erratum_scan (bfd *input_bfd,
                    {
                      char *error_message = NULL;
                      struct elf_link_hash_entry *entry;
+                     bfd_boolean use_plt = FALSE;
 
                      /* We don't care about the error returned from this
                         function, only if there is glue or not.  */
@@ -4048,12 +4304,18 @@ cortex_a8_erratum_scan (bfd *input_bfd,
                      if (entry)
                        found->non_a8_stub = TRUE;
 
-                     if (found->r_type == R_ARM_THM_CALL
-                         && found->st_type != STT_ARM_TFUNC)
-                       force_target_arm = TRUE;
-                     else if (found->r_type == R_ARM_THM_CALL
-                              && found->st_type == STT_ARM_TFUNC)
-                       force_target_thumb = TRUE;
+                     /* Keep a simpler condition, for the sake of clarity.  */
+                     if (htab->root.splt != NULL && found->hash != NULL
+                         && found->hash->root.plt.offset != (bfd_vma) -1)
+                       use_plt = TRUE;
+
+                     if (found->r_type == R_ARM_THM_CALL)
+                       {
+                         if (found->st_type != STT_ARM_TFUNC || use_plt)
+                           force_target_arm = TRUE;
+                         else
+                           force_target_thumb = TRUE;
+                       }
                    }
 
                   /* Check if we have an offending branch instruction.  */
@@ -4186,6 +4448,8 @@ cortex_a8_erratum_scan (bfd *input_bfd,
                           a8_fixes[num_a8_fixes].orig_insn = insn;
                           a8_fixes[num_a8_fixes].stub_name = stub_name;
                           a8_fixes[num_a8_fixes].stub_type = stub_type;
+                          a8_fixes[num_a8_fixes].st_type =
+                           is_blx ? STT_FUNC : STT_ARM_TFUNC;
 
                           num_a8_fixes++;
                         }
@@ -4201,11 +4465,11 @@ cortex_a8_erratum_scan (bfd *input_bfd,
       if (elf_section_data (section)->this_hdr.contents == NULL)
         free (contents);
     }
-  
+
   *a8_fixes_p = a8_fixes;
   *num_a8_fixes_p = num_a8_fixes;
   *a8_fix_table_size_p = a8_fix_table_size;
-  
+
   return FALSE;
 }
 
@@ -4231,6 +4495,9 @@ elf32_arm_size_stubs (bfd *output_bfd,
   struct a8_erratum_reloc *a8_relocs = NULL;
   unsigned int num_a8_relocs = 0, a8_reloc_table_size = 10, i;
 
+  if (htab == NULL)
+    return FALSE;
+
   if (htab->fix_cortex_a8)
     {
       a8_fixes = (struct a8_erratum_fix *)
@@ -4353,7 +4620,7 @@ elf32_arm_size_stubs (bfd *output_bfd,
                  const char *sym_name;
                  char *stub_name;
                  const asection *id_sec;
-                 unsigned char st_type;
+                 int st_type;
                  bfd_boolean created_stub = FALSE;
 
                  r_type = ELF32_R_TYPE (irela->r_info);
@@ -4367,15 +4634,30 @@ elf32_arm_size_stubs (bfd *output_bfd,
                        free (internal_relocs);
                      goto error_ret_free_local;
                    }
-
-                 /* Only look for stubs on branch instructions.  */
+                 
+                 hash = NULL;
+                 if (r_indx >= symtab_hdr->sh_info)
+                   hash = elf32_arm_hash_entry
+                     (elf_sym_hashes (input_bfd)
+                      [r_indx - symtab_hdr->sh_info]);
+                 
+                 /* Only look for stubs on branch instructions, or
+                    non-relaxed TLSCALL  */
                  if ((r_type != (unsigned int) R_ARM_CALL)
                      && (r_type != (unsigned int) R_ARM_THM_CALL)
                      && (r_type != (unsigned int) R_ARM_JUMP24)
                      && (r_type != (unsigned int) R_ARM_THM_JUMP19)
                      && (r_type != (unsigned int) R_ARM_THM_XPC22)
                      && (r_type != (unsigned int) R_ARM_THM_JUMP24)
-                     && (r_type != (unsigned int) R_ARM_PLT32))
+                     && (r_type != (unsigned int) R_ARM_PLT32)
+                     && !((r_type == (unsigned int) R_ARM_TLS_CALL
+                           || r_type == (unsigned int) R_ARM_THM_TLS_CALL)
+                          && r_type == elf32_arm_tls_transition
+                              (info, r_type, &hash->root)
+                          && ((hash ? hash->tls_type
+                               : (elf32_arm_local_got_tls_type
+                                  (input_bfd)[r_indx]))
+                              & GOT_TLS_GDESC) != 0))
                    continue;
 
                  /* Now determine the call target, its name, value,
@@ -4383,13 +4665,24 @@ elf32_arm_size_stubs (bfd *output_bfd,
                  sym_sec = NULL;
                  sym_value = 0;
                  destination = 0;
-                 hash = NULL;
                  sym_name = NULL;
-                 if (r_indx < symtab_hdr->sh_info)
+                 
+                 if (r_type == (unsigned int) R_ARM_TLS_CALL
+                     || r_type == (unsigned int) R_ARM_THM_TLS_CALL)
+                   {
+                     /* A non-relaxed TLS call.  The target is the
+                        plt-resident trampoline and nothing to do
+                        with the symbol.  */
+                     BFD_ASSERT (htab->tls_trampoline > 0);
+                     sym_sec = htab->root.splt;
+                     sym_value = htab->tls_trampoline;
+                     hash = 0;
+                     st_type = STT_FUNC;
+                   }
+                 else if (!hash)
                    {
                      /* It's a local symbol.  */
                      Elf_Internal_Sym *sym;
-                     Elf_Internal_Shdr *hdr;
 
                      if (local_syms == NULL)
                        {
@@ -4405,13 +4698,21 @@ elf32_arm_size_stubs (bfd *output_bfd,
                        }
 
                      sym = local_syms + r_indx;
-                     hdr = elf_elfsections (input_bfd)[sym->st_shndx];
-                     sym_sec = hdr->bfd_section;
+                     if (sym->st_shndx == SHN_UNDEF)
+                       sym_sec = bfd_und_section_ptr;
+                     else if (sym->st_shndx == SHN_ABS)
+                       sym_sec = bfd_abs_section_ptr;
+                     else if (sym->st_shndx == SHN_COMMON)
+                       sym_sec = bfd_com_section_ptr;
+                     else
+                       sym_sec =
+                         bfd_section_from_elf_index (input_bfd, sym->st_shndx);
+
                      if (!sym_sec)
                        /* This is an undefined symbol.  It can never
                           be resolved. */
                        continue;
-                 
+
                      if (ELF_ST_TYPE (sym->st_info) != STT_SECTION)
                        sym_value = sym->st_value;
                      destination = (sym_value + irela->r_addend
@@ -4426,12 +4727,6 @@ elf32_arm_size_stubs (bfd *output_bfd,
                  else
                    {
                      /* It's an external symbol.  */
-                     int e_indx;
-
-                     e_indx = r_indx - symtab_hdr->sh_info;
-                     hash = ((struct elf32_arm_link_hash_entry *)
-                             elf_sym_hashes (input_bfd)[e_indx]);
-
                      while (hash->root.root.type == bfd_link_hash_indirect
                             || hash->root.root.type == bfd_link_hash_warning)
                        hash = ((struct elf32_arm_link_hash_entry *)
@@ -4450,10 +4745,12 @@ elf32_arm_size_stubs (bfd *output_bfd,
                             use the PLT stub as target address to
                             decide whether a branch stub is
                             needed.  */
-                         if (globals->splt != NULL && hash != NULL
+                         if (globals != NULL
+                             && globals->root.splt != NULL
+                             && hash != NULL
                              && hash->root.plt.offset != (bfd_vma) -1)
                            {
-                             sym_sec = globals->splt;
+                             sym_sec = globals->root.splt;
                              sym_value = hash->root.plt.offset;
                              if (sym_sec->output_section != NULL)
                                destination = (sym_value
@@ -4475,10 +4772,12 @@ elf32_arm_size_stubs (bfd *output_bfd,
                          struct elf32_arm_link_hash_table *globals =
                            elf32_arm_hash_table (info);
 
-                         if (globals->splt != NULL && hash != NULL
+                         if (globals != NULL
+                             && globals->root.splt != NULL
+                             && hash != NULL
                              && hash->root.plt.offset != (bfd_vma) -1)
                            {
-                             sym_sec = globals->splt;
+                             sym_sec = globals->root.splt;
                              sym_value = hash->root.plt.offset;
                              if (sym_sec->output_section != NULL)
                                destination = (sym_value
@@ -4501,7 +4800,7 @@ elf32_arm_size_stubs (bfd *output_bfd,
                    {
                      /* Determine what (if any) linker stub is needed.  */
                      stub_type = arm_type_of_stub (info, section, irela,
-                                                   st_type, hash,
+                                                   &st_type, hash,
                                                    destination, sym_sec,
                                                    input_bfd, sym_name);
                      if (stub_type == arm_stub_none)
@@ -4512,7 +4811,7 @@ elf32_arm_size_stubs (bfd *output_bfd,
 
                      /* Get the name of this stub.  */
                      stub_name = elf32_arm_stub_name (id_sec, sym_sec, hash,
-                                                      irela);
+                                                      irela, stub_type);
                      if (!stub_name)
                        goto error_ret_free_internal;
 
@@ -4611,6 +4910,7 @@ elf32_arm_size_stubs (bfd *output_bfd,
                           a8_relocs[num_a8_relocs].st_type = st_type;
                           a8_relocs[num_a8_relocs].sym_name = sym_name;
                           a8_relocs[num_a8_relocs].non_a8_stub = created_stub;
+                          a8_relocs[num_a8_relocs].hash = hash;
 
                           num_a8_relocs++;
                         }
@@ -4712,7 +5012,7 @@ elf32_arm_size_stubs (bfd *output_bfd,
           stub_entry->target_value = a8_fixes[i].offset;
           stub_entry->target_addend = a8_fixes[i].addend;
           stub_entry->orig_insn = a8_fixes[i].orig_insn;
-          stub_entry->st_type = STT_ARM_TFUNC;
+         stub_entry->st_type = a8_fixes[i].st_type;
 
           size = find_stub_size_and_template (a8_fixes[i].stub_type,
                                               &template_sequence,
@@ -4753,6 +5053,8 @@ elf32_arm_build_stubs (struct bfd_link_info *info)
   struct elf32_arm_link_hash_table *htab;
 
   htab = elf32_arm_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
 
   for (stub_sec = htab->stub_bfd->sections;
        stub_sec != NULL;
@@ -4798,6 +5100,8 @@ find_thumb_glue (struct bfd_link_info *link_info,
 
   /* We need a pointer to the armelf specific hash table.  */
   hash_table = elf32_arm_hash_table (link_info);
+  if (hash_table == NULL)
+    return NULL;
 
   tmp_name = (char *) bfd_malloc ((bfd_size_type) strlen (name)
                                   + strlen (THUMB2ARM_GLUE_ENTRY_NAME) + 1);
@@ -4832,6 +5136,8 @@ find_arm_glue (struct bfd_link_info *link_info,
 
   /* We need a pointer to the elfarm specific hash table.  */
   hash_table = elf32_arm_hash_table (link_info);
+  if (hash_table == NULL)
+    return NULL;
 
   tmp_name = (char *) bfd_malloc ((bfd_size_type) strlen (name)
                                   + strlen (ARM2THUMB_GLUE_ENTRY_NAME) + 1);
@@ -4994,7 +5300,6 @@ record_arm_to_thumb_glue (struct bfd_link_info * link_info,
   bfd_size_type size;
 
   globals = elf32_arm_hash_table (link_info);
-
   BFD_ASSERT (globals != NULL);
   BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
 
@@ -5067,7 +5372,6 @@ record_arm_bx_glue (struct bfd_link_info * link_info, int reg)
     return;
 
   globals = elf32_arm_hash_table (link_info);
-
   BFD_ASSERT (globals != NULL);
   BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
 
@@ -5160,11 +5464,9 @@ record_vfp11_erratum_veneer (struct bfd_link_info *link_info,
   struct bfd_link_hash_entry *bh;
   bfd_vma val;
   struct _arm_elf_section_data *sec_data;
-  int errcount;
   elf32_vfp11_erratum_list *newerr;
 
   hash_table = elf32_arm_hash_table (link_info);
-
   BFD_ASSERT (hash_table != NULL);
   BFD_ASSERT (hash_table->bfd_of_glue_owner != NULL);
 
@@ -5199,7 +5501,7 @@ record_vfp11_erratum_veneer (struct bfd_link_info *link_info,
   myh->forced_local = 1;
 
   /* Link veneer back to calling location.  */
-  errcount = ++(sec_data->erratumcount);
+  sec_data->erratumcount += 1;
   newerr = (elf32_vfp11_erratum_list *)
       bfd_zmalloc (sizeof (elf32_vfp11_erratum_list));
 
@@ -5329,7 +5631,6 @@ bfd_elf32_arm_get_bfd_for_interworking (bfd *abfd, struct bfd_link_info *info)
   BFD_ASSERT (!(abfd->flags & DYNAMIC));
 
   globals = elf32_arm_hash_table (info);
-
   BFD_ASSERT (globals != NULL);
 
   if (globals->bfd_of_glue_owner != NULL)
@@ -5369,7 +5670,6 @@ bfd_elf32_arm_process_before_allocation (bfd *abfd,
   /* Here we have a bfd that is to be included on the link.  We have a
      hook to do reloc rummaging, before section sizes are nailed down.  */
   globals = elf32_arm_hash_table (link_info);
-
   BFD_ASSERT (globals != NULL);
 
   check_use_blx (globals);
@@ -5468,7 +5768,7 @@ bfd_elf32_arm_process_before_allocation (bfd *abfd,
 
          /* If the call will go through a PLT entry then we do not need
             glue.  */
-         if (globals->splt != NULL && h->plt.offset != (bfd_vma) -1)
+         if (globals->root.splt != NULL && h->plt.offset != (bfd_vma) -1)
            continue;
 
          switch (r_type)
@@ -5570,6 +5870,9 @@ bfd_elf32_arm_set_cortex_a8_fix (bfd *obfd, struct bfd_link_info *link_info)
   struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info);
   obj_attribute *out_attr = elf_known_obj_attributes_proc (obfd);
 
+  if (globals == NULL)
+    return;
+
   if (globals->fix_cortex_a8 == -1)
     {
       /* Turn on Cortex-A8 erratum workaround for ARMv7-A.  */
@@ -5589,6 +5892,8 @@ bfd_elf32_arm_set_vfp11_fix (bfd *obfd, struct bfd_link_info *link_info)
   struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info);
   obj_attribute *out_attr = elf_known_obj_attributes_proc (obfd);
 
+  if (globals == NULL)
+    return;
   /* We assume that ARMv7+ does not need the VFP11 denorm erratum fix.  */
   if (out_attr[Tag_CPU_arch].i >= TAG_CPU_ARCH_V7)
     {
@@ -5693,7 +5998,7 @@ static enum bfd_arm_vfp11_pipe
 bfd_arm_vfp11_insn_decode (unsigned int insn, unsigned int *destmask, int *regs,
                            int *numregs)
 {
-  enum bfd_arm_vfp11_pipe pipe = VFP11_BAD;
+  enum bfd_arm_vfp11_pipe vpipe = VFP11_BAD;
   bfd_boolean is_double = ((insn & 0xf00) == 0xb00) ? 1 : 0;
 
   if ((insn & 0x0f000e10) == 0x0e000a00)  /* A data-processing insn.  */
@@ -5712,7 +6017,7 @@ bfd_arm_vfp11_insn_decode (unsigned int insn, unsigned int *destmask, int *regs,
         case 1: /* fnmac[sd].  */
         case 2: /* fmsc[sd].  */
         case 3: /* fnmsc[sd].  */
-          pipe = VFP11_FMAC;
+          vpipe = VFP11_FMAC;
           bfd_arm_vfp11_write_mask (destmask, fd);
           regs[0] = fd;
           regs[1] = bfd_arm_vfp11_regno (insn, is_double, 16, 7);  /* Fn.  */
@@ -5724,11 +6029,11 @@ bfd_arm_vfp11_insn_decode (unsigned int insn, unsigned int *destmask, int *regs,
         case 5: /* fnmul[sd].  */
         case 6: /* fadd[sd].  */
         case 7: /* fsub[sd].  */
-          pipe = VFP11_FMAC;
+          vpipe = VFP11_FMAC;
           goto vfp_binop;
 
         case 8: /* fdiv[sd].  */
-          pipe = VFP11_DS;
+          vpipe = VFP11_DS;
           vfp_binop:
           bfd_arm_vfp11_write_mask (destmask, fd);
           regs[0] = bfd_arm_vfp11_regno (insn, is_double, 16, 7);   /* Fn.  */
@@ -5758,14 +6063,14 @@ bfd_arm_vfp11_insn_decode (unsigned int insn, unsigned int *destmask, int *regs,
               case 27: /* ftosiz[sd].  */
                 /* These instructions will not bounce due to underflow.  */
                 *numregs = 0;
-                pipe = VFP11_FMAC;
+                vpipe = VFP11_FMAC;
                 break;
 
               case 3: /* fsqrt[sd].  */
                 /* fsqrt cannot underflow, but it can (perhaps) overwrite
                    registers to cause the erratum in previous instructions.  */
                 bfd_arm_vfp11_write_mask (destmask, fd);
-                pipe = VFP11_DS;
+                vpipe = VFP11_DS;
                 break;
 
               case 15: /* fcvt{ds,sd}.  */
@@ -5780,7 +6085,7 @@ bfd_arm_vfp11_insn_decode (unsigned int insn, unsigned int *destmask, int *regs,
 
                   *numregs = rnum;
 
-                  pipe = VFP11_FMAC;
+                  vpipe = VFP11_FMAC;
                 }
                 break;
 
@@ -5810,7 +6115,7 @@ bfd_arm_vfp11_insn_decode (unsigned int insn, unsigned int *destmask, int *regs,
             }
        }
 
-      pipe = VFP11_LS;
+      vpipe = VFP11_LS;
     }
   else if ((insn & 0x0e100e00) == 0x0c100a00)  /* A load insn.  */
     {
@@ -5845,7 +6150,7 @@ bfd_arm_vfp11_insn_decode (unsigned int insn, unsigned int *destmask, int *regs,
           return VFP11_BAD;
         }
 
-      pipe = VFP11_LS;
+      vpipe = VFP11_LS;
     }
   /* Single-register transfer. Note L==0.  */
   else if ((insn & 0x0f100e10) == 0x0e000a10)
@@ -5867,10 +6172,10 @@ bfd_arm_vfp11_insn_decode (unsigned int insn, unsigned int *destmask, int *regs,
           break;
         }
 
-      pipe = VFP11_LS;
+      vpipe = VFP11_LS;
     }
 
-  return pipe;
+  return vpipe;
 }
 
 
@@ -5892,6 +6197,9 @@ bfd_elf32_arm_vfp11_erratum_scan (bfd *abfd, struct bfd_link_info *link_info)
   struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info);
   int use_vector = (globals->vfp11_fix == BFD_ARM_VFP11_FIX_VECTOR);
 
+  if (globals == NULL)
+    return FALSE;
+
   /* We use a simple FSM to match troublesome VFP11 instruction sequences.
      The states transition as follows:
 
@@ -5990,17 +6298,17 @@ bfd_elf32_arm_vfp11_erratum_scan (bfd *abfd, struct bfd_link_info *link_info)
                   | (contents[i + 1] << 8)
                   | contents[i];
               unsigned int writemask = 0;
-              enum bfd_arm_vfp11_pipe pipe;
+              enum bfd_arm_vfp11_pipe vpipe;
 
               switch (state)
                 {
                 case 0:
-                  pipe = bfd_arm_vfp11_insn_decode (insn, &writemask, regs,
+                  vpipe = bfd_arm_vfp11_insn_decode (insn, &writemask, regs,
                                                     &numregs);
                   /* I'm assuming the VFP11 erratum can trigger with denorm
                      operands on either the FMAC or the DS pipeline. This might
                      lead to slightly overenthusiastic veneer insertion.  */
-                  if (pipe == VFP11_FMAC || pipe == VFP11_DS)
+                  if (vpipe == VFP11_FMAC || vpipe == VFP11_DS)
                     {
                       state = use_vector ? 1 : 2;
                       first_fmac = i;
@@ -6011,10 +6319,10 @@ bfd_elf32_arm_vfp11_erratum_scan (bfd *abfd, struct bfd_link_info *link_info)
                 case 1:
                   {
                     int other_regs[3], other_numregs;
-                    pipe = bfd_arm_vfp11_insn_decode (insn, &writemask,
+                    vpipe = bfd_arm_vfp11_insn_decode (insn, &writemask,
                                                      other_regs,
                                                       &other_numregs);
-                    if (pipe != VFP11_BAD
+                    if (vpipe != VFP11_BAD
                         && bfd_arm_vfp11_antidependency (writemask, regs,
                                                         numregs))
                       state = 3;
@@ -6026,10 +6334,10 @@ bfd_elf32_arm_vfp11_erratum_scan (bfd *abfd, struct bfd_link_info *link_info)
                 case 2:
                   {
                     int other_regs[3], other_numregs;
-                    pipe = bfd_arm_vfp11_insn_decode (insn, &writemask,
+                    vpipe = bfd_arm_vfp11_insn_decode (insn, &writemask,
                                                      other_regs,
                                                       &other_numregs);
-                    if (pipe != VFP11_BAD
+                    if (vpipe != VFP11_BAD
                         && bfd_arm_vfp11_antidependency (writemask, regs,
                                                         numregs))
                       state = 3;
@@ -6049,9 +6357,8 @@ bfd_elf32_arm_vfp11_erratum_scan (bfd *abfd, struct bfd_link_info *link_info)
                 {
                   elf32_vfp11_erratum_list *newerr =(elf32_vfp11_erratum_list *)
                       bfd_zmalloc (sizeof (elf32_vfp11_erratum_list));
-                  int errcount;
 
-                  errcount = ++(elf32_arm_section_data (sec)->erratumcount);
+                  elf32_arm_section_data (sec)->erratumcount += 1;
 
                   newerr->u.b.vfp_insn = veneer_of_insn;
 
@@ -6115,6 +6422,8 @@ bfd_elf32_arm_vfp11_fix_veneer_locations (bfd *abfd,
     return;
 
   globals = elf32_arm_hash_table (link_info);
+  if (globals == NULL)
+    return;
 
   tmp_name = (char *) bfd_malloc ((bfd_size_type) strlen
                                   (VFP11_ERRATUM_VENEER_ENTRY_NAME) + 10);
@@ -6197,6 +6506,8 @@ bfd_elf32_arm_set_target_relocs (struct bfd *output_bfd,
   struct elf32_arm_link_hash_table *globals;
 
   globals = elf32_arm_hash_table (link_info);
+  if (globals == NULL)
+    return;
 
   globals->target1_is_rel = target1_is_rel;
   if (strcmp (target2_type, "rel") == 0)
@@ -6272,7 +6583,6 @@ elf32_thumb_to_arm_stub (struct bfd_link_info * info,
     return FALSE;
 
   globals = elf32_arm_hash_table (info);
-
   BFD_ASSERT (globals != NULL);
   BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
 
@@ -6370,7 +6680,6 @@ elf32_arm_create_thumb_stub (struct bfd_link_info * info,
     return NULL;
 
   globals = elf32_arm_hash_table (info);
-
   BFD_ASSERT (globals != NULL);
   BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
 
@@ -6466,7 +6775,6 @@ elf32_arm_to_thumb_stub (struct bfd_link_info * info,
   struct elf32_arm_link_hash_table * globals;
 
   globals = elf32_arm_hash_table (info);
-
   BFD_ASSERT (globals != NULL);
   BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
 
@@ -6521,7 +6829,6 @@ elf32_arm_to_thumb_export_stub (struct elf_link_hash_entry *h, void * inf)
     return TRUE;
 
   globals = elf32_arm_hash_table (info);
-
   BFD_ASSERT (globals != NULL);
   BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
 
@@ -6557,7 +6864,6 @@ elf32_arm_bx_glue (struct bfd_link_info * info, int reg)
   struct elf32_arm_link_hash_table *globals;
 
   globals = elf32_arm_hash_table (info);
-
   BFD_ASSERT (globals != NULL);
   BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
 
@@ -6595,6 +6901,9 @@ elf32_arm_begin_write_processing (bfd *abfd ATTRIBUTE_UNUSED,
     return;
 
   globals = elf32_arm_hash_table (link_info);
+  if (globals == NULL)
+    return;
+
   /* If blx is available then exported Thumb symbols are OK and there is
      nothing to do.  */
   if (globals->use_blx)
@@ -6670,29 +6979,174 @@ elf32_arm_abs12_reloc (bfd *abfd, void *data, bfd_vma value)
   return bfd_reloc_ok;
 }
 
-/* For a given value of n, calculate the value of G_n as required to
-   deal with group relocations.  We return it in the form of an
-   encoded constant-and-rotation, together with the final residual.  If n is
-   specified as less than zero, then final_residual is filled with the
-   input value and no further action is performed.  */
+/* Handle TLS relaxations.  Relaxing is possible for symbols that use
+   R_ARM_GOTDESC, R_ARM_{,THM_}TLS_CALL or
+   R_ARM_{,THM_}TLS_DESCSEQ relocations, during a static link.
 
-static bfd_vma
-calculate_group_reloc_mask (bfd_vma value, int n, bfd_vma *final_residual)
-{
-  int current_n;
-  bfd_vma g_n;
-  bfd_vma encoded_g_n = 0;
-  bfd_vma residual = value; /* Also known as Y_n.  */
+   Return bfd_reloc_ok if we're done, bfd_reloc_continue if the caller
+   is to then call final_link_relocate.  Return other values in the
+   case of error.
 
-  for (current_n = 0; current_n <= n; current_n++)
-    {
-      int shift;
+   FIXME:When --emit-relocs is in effect, we'll emit relocs describing
+   the pre-relaxed code.  It would be nice if the relocs were updated
+   to match the optimization.   */
 
-      /* Calculate which part of the value to mask.  */
-      if (residual == 0)
-        shift = 0;
-      else
-        {
+static bfd_reloc_status_type 
+elf32_arm_tls_relax (struct elf32_arm_link_hash_table *globals,
+                    bfd *input_bfd, asection *input_sec, bfd_byte *contents, 
+                    Elf_Internal_Rela *rel, unsigned long is_local)
+{
+  unsigned long insn;
+  
+  switch (ELF32_R_TYPE (rel->r_info))
+    {
+    default:
+      return bfd_reloc_notsupported;
+      
+    case R_ARM_TLS_GOTDESC:
+      if (is_local)
+       insn = 0;
+      else
+       {
+         insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+         if (insn & 1)
+           insn -= 5; /* THUMB */
+         else
+           insn -= 8; /* ARM */
+       }
+      bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+      return bfd_reloc_continue;
+
+    case R_ARM_THM_TLS_DESCSEQ:
+      /* Thumb insn.  */
+      insn = bfd_get_16 (input_bfd, contents + rel->r_offset);
+      if ((insn & 0xff78) == 0x4478)     /* add rx, pc */
+       {
+         if (is_local)
+           /* nop */
+           bfd_put_16 (input_bfd, 0x46c0, contents + rel->r_offset);
+       }
+      else if ((insn & 0xffc0) == 0x6840)  /* ldr rx,[ry,#4] */
+       {
+         if (is_local)
+           /* nop */
+           bfd_put_16 (input_bfd, 0x46c0, contents + rel->r_offset);
+         else
+           /* ldr rx,[ry] */
+           bfd_put_16 (input_bfd, insn & 0xf83f, contents + rel->r_offset);
+       }
+      else if ((insn & 0xff87) == 0x4780)  /* blx rx */
+       {
+         if (is_local)
+           /* nop */
+           bfd_put_16 (input_bfd, 0x46c0, contents + rel->r_offset);
+         else
+           /* mov r0, rx */
+           bfd_put_16 (input_bfd, 0x4600 | (insn & 0x78),
+                       contents + rel->r_offset);
+       }
+      else
+       {
+         if ((insn & 0xf000) == 0xf000 || (insn & 0xf800) == 0xe800)
+           /* It's a 32 bit instruction, fetch the rest of it for
+              error generation.  */
+           insn = (insn << 16)
+             | bfd_get_16 (input_bfd, contents + rel->r_offset + 2);
+         (*_bfd_error_handler)
+           (_("%B(%A+0x%lx):unexpected Thumb instruction '0x%x' in TLS trampoline"),
+            input_bfd, input_sec, (unsigned long)rel->r_offset, insn);
+         return bfd_reloc_notsupported;
+       }
+      break;
+      
+    case R_ARM_TLS_DESCSEQ:
+      /* arm insn.  */
+      insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+      if ((insn & 0xffff0ff0) == 0xe08f0000) /* add rx,pc,ry */
+       {
+         if (is_local)
+           /* mov rx, ry */
+           bfd_put_32 (input_bfd, 0xe1a00000 | (insn & 0xffff),
+                       contents + rel->r_offset);
+       }
+      else if ((insn & 0xfff00fff) == 0xe5900004) /* ldr rx,[ry,#4]*/
+       {
+         if (is_local)
+           /* nop */
+           bfd_put_32 (input_bfd, 0xe1a00000, contents + rel->r_offset);
+         else
+           /* ldr rx,[ry] */
+           bfd_put_32 (input_bfd, insn & 0xfffff000,
+                       contents + rel->r_offset);
+       }
+      else if ((insn & 0xfffffff0) == 0xe12fff30) /* blx rx */
+       {
+         if (is_local)
+           /* nop */
+           bfd_put_32 (input_bfd, 0xe1a00000, contents + rel->r_offset);
+         else
+           /* mov r0, rx */
+           bfd_put_32 (input_bfd, 0xe1a00000 | (insn & 0xf),
+                       contents + rel->r_offset);
+       }
+      else
+       {
+         (*_bfd_error_handler)
+           (_("%B(%A+0x%lx):unexpected ARM instruction '0x%x' in TLS trampoline"),
+            input_bfd, input_sec, (unsigned long)rel->r_offset, insn);
+         return bfd_reloc_notsupported;
+       }
+      break;
+
+    case R_ARM_TLS_CALL:
+      /* GD->IE relaxation, turn the instruction into 'nop' or
+        'ldr r0, [pc,r0]'  */
+      insn = is_local ? 0xe1a00000 : 0xe79f0000;
+      bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+      break;
+      
+    case R_ARM_THM_TLS_CALL:
+      /* GD->IE relaxation */
+      if (!is_local)
+       /* add r0,pc; ldr r0, [r0]  */
+       insn = 0x44786800;
+      else if (arch_has_thumb2_nop (globals))
+       /* nop.w */
+       insn = 0xf3af8000;
+      else
+       /* nop; nop */
+       insn = 0xbf00bf00;
+       
+      bfd_put_16 (input_bfd, insn >> 16, contents + rel->r_offset);
+      bfd_put_16 (input_bfd, insn & 0xffff, contents + rel->r_offset + 2);
+      break;
+    }
+  return bfd_reloc_ok;
+}
+
+/* For a given value of n, calculate the value of G_n as required to
+   deal with group relocations.  We return it in the form of an
+   encoded constant-and-rotation, together with the final residual.  If n is
+   specified as less than zero, then final_residual is filled with the
+   input value and no further action is performed.  */
+
+static bfd_vma
+calculate_group_reloc_mask (bfd_vma value, int n, bfd_vma *final_residual)
+{
+  int current_n;
+  bfd_vma g_n;
+  bfd_vma encoded_g_n = 0;
+  bfd_vma residual = value; /* Also known as Y_n.  */
+
+  for (current_n = 0; current_n <= n; current_n++)
+    {
+      int shift;
+
+      /* Calculate which part of the value to mask.  */
+      if (residual == 0)
+        shift = 0;
+      else
+        {
           int msb;
 
           /* Determine the most significant bit in the residual and
@@ -6760,24 +7214,30 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
   unsigned long                 r_type = howto->type;
   unsigned long                 r_symndx;
   bfd_byte *                    hit_data = contents + rel->r_offset;
-  bfd *                         dynobj = NULL;
-  Elf_Internal_Shdr *           symtab_hdr;
-  struct elf_link_hash_entry ** sym_hashes;
   bfd_vma *                     local_got_offsets;
+  bfd_vma *                     local_tlsdesc_gotents;
   asection *                    sgot = NULL;
   asection *                    splt = NULL;
   asection *                    sreloc = NULL;
+  asection *                    srelgot;
   bfd_vma                       addend;
   bfd_signed_vma                signed_addend;
   struct elf32_arm_link_hash_table * globals;
 
   globals = elf32_arm_hash_table (info);
+  if (globals == NULL)
+    return bfd_reloc_notsupported;
 
   BFD_ASSERT (is_arm_elf (input_bfd));
 
   /* Some relocation types map to different relocations depending on the
      target.  We pick the right one here.  */
   r_type = arm_real_reloc_type (globals, r_type);
+
+  /* It is possible to have linker relaxations on some TLS access
+     models.  Update our information here.  */
+  r_type = elf32_arm_tls_transition (info, r_type, h);
+
   if (r_type != howto->type)
     howto = elf32_arm_howto_from_type (r_type);
 
@@ -6793,15 +7253,12 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
   if (bfd_get_start_address (output_bfd) != 0)
     elf_elfheader (output_bfd)->e_flags |= EF_ARM_HASENTRY;
 
-  dynobj = elf_hash_table (info)->dynobj;
-  if (dynobj)
-    {
-      sgot = bfd_get_section_by_name (dynobj, ".got");
-      splt = bfd_get_section_by_name (dynobj, ".plt");
-    }
-  symtab_hdr = & elf_symtab_hdr (input_bfd);
-  sym_hashes = elf_sym_hashes (input_bfd);
+  sgot = globals->root.sgot;
+  splt = globals->root.splt;
+  srelgot = globals->root.srelgot;
   local_got_offsets = elf_local_got_offsets (input_bfd);
+  local_tlsdesc_gotents = elf32_arm_local_tlsdesc_gotent (input_bfd);
+
   r_symndx = ELF32_R_SYM (rel->r_info);
 
   if (globals->use_rel)
@@ -6875,11 +7332,12 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
         run time.  */
       if ((info->shared || globals->root.is_relocatable_executable)
          && (input_section->flags & SEC_ALLOC)
-         && !(elf32_arm_hash_table (info)->vxworks_p
+         && !(globals->vxworks_p
               && strcmp (input_section->output_section->name,
                          ".tls_vars") == 0)
          && ((r_type != R_ARM_REL32 && r_type != R_ARM_REL32_NOI)
              || !SYMBOL_CALLS_LOCAL (info, h))
+         && (!strstr (input_section->name, STUB_SUFFIX))
          && (h == NULL
              || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
              || h->root.type != bfd_link_hash_undefweak)
@@ -7004,7 +7462,6 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
        case R_ARM_PC24:          /* Arm B/BL instruction.  */
        case R_ARM_PLT32:
          {
-         bfd_signed_vma branch_offset;
          struct elf32_arm_stub_hash_entry *stub_entry = NULL;
 
          if (r_type == R_ARM_XPC25)
@@ -7040,45 +7497,46 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
              || r_type == R_ARM_JUMP24
              || r_type == R_ARM_PLT32)
            {
-             bfd_vma from;
-             
-             /* If the call goes through a PLT entry, make sure to
-                check distance to the right destination address.  */
-             if (h != NULL && splt != NULL && h->plt.offset != (bfd_vma) -1)
-               {
-                 value = (splt->output_section->vma
-                          + splt->output_offset
-                          + h->plt.offset);
-                 *unresolved_reloc_p = FALSE;
-                 /* The PLT entry is in ARM mode, regardless of the
-                    target function.  */
-                 sym_flags = STT_FUNC;
-               }
+             enum elf32_arm_stub_type stub_type = arm_stub_none;
+             struct elf32_arm_link_hash_entry *hash;
+
+             hash = (struct elf32_arm_link_hash_entry *) h;
+             stub_type = arm_type_of_stub (info, input_section, rel,
+                                           &sym_flags, hash,
+                                           value, sym_sec,
+                                           input_bfd, sym_name);
 
-             from = (input_section->output_section->vma
-                     + input_section->output_offset
-                     + rel->r_offset);
-             branch_offset = (bfd_signed_vma)(value - from);
-
-             if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
-                 || branch_offset < ARM_MAX_BWD_BRANCH_OFFSET
-                 || ((sym_flags == STT_ARM_TFUNC)
-                     && (((r_type == R_ARM_CALL) && !globals->use_blx)
-                         || (r_type == R_ARM_JUMP24)
-                         || (r_type == R_ARM_PLT32) ))
-                 )
+             if (stub_type != arm_stub_none)
                {
                  /* The target is out of reach, so redirect the
                     branch to the local stub for this function.  */
 
                  stub_entry = elf32_arm_get_stub_entry (input_section,
                                                         sym_sec, h,
-                                                        rel, globals);
+                                                        rel, globals,
+                                                        stub_type);
                  if (stub_entry != NULL)
                    value = (stub_entry->stub_offset
                             + stub_entry->stub_sec->output_offset
                             + stub_entry->stub_sec->output_section->vma);
                }
+             else
+               {
+                 /* If the call goes through a PLT entry, make sure to
+                    check distance to the right destination address.  */
+                 if (h != NULL
+                     && splt != NULL
+                     && h->plt.offset != (bfd_vma) -1)
+                   {
+                     value = (splt->output_section->vma
+                              + splt->output_offset
+                              + h->plt.offset);
+                     *unresolved_reloc_p = FALSE;
+                     /* The PLT entry is in ARM mode, regardless of the
+                        target function.  */
+                     sym_flags = STT_FUNC;
+                   }
+               }
            }
 
          /* The ARM ELF ABI says that this reloc is computed as: S - P + A
@@ -7114,12 +7572,12 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
 
          /* A branch to an undefined weak symbol is turned into a jump to
             the next instruction unless a PLT entry will be created.
-            Do the same for local undefined symbols.
+            Do the same for local undefined symbols (but not for STN_UNDEF).
             The jump to the next instruction is optimized as a NOP depending
             on the architecture.  */
          if (h ? (h->root.type == bfd_link_hash_undefweak
                   && !(splt != NULL && h->plt.offset != (bfd_vma) -1))
-             : bfd_is_und_section (sym_sec))
+             : r_symndx != STN_UNDEF && bfd_is_und_section (sym_sec))
            {
              value = (bfd_get_32 (input_bfd, hit_data) & 0xf0000000);
 
@@ -7212,7 +7670,11 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
 
     case R_ARM_ABS8:
       value += addend;
-      if ((long) value > 0x7f || (long) value < -0x80)
+
+      /* There is no way to tell whether the user intended to use a signed or
+        unsigned addend.  When checking for overflow we accept either,
+        as specified by the AAELF.  */
+      if ((long) value > 0xff || (long) value < -0x80)
        return bfd_reloc_overflow;
 
       bfd_put_8 (input_bfd, value, hit_data);
@@ -7221,7 +7683,8 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
     case R_ARM_ABS16:
       value += addend;
 
-      if ((long) value > 0x7fff || (long) value < -0x8000)
+      /* See comment for R_ARM_ABS8.  */
+      if ((long) value > 0xffff || (long) value < -0x8000)
        return bfd_reloc_overflow;
 
       bfd_put_16 (input_bfd, value, hit_data);
@@ -7458,58 +7921,29 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
              }
          }
 
-       /* Handle calls via the PLT.  */
-       if (h != NULL && splt != NULL && h->plt.offset != (bfd_vma) -1)
-         {
-           value = (splt->output_section->vma
-                    + splt->output_offset
-                    + h->plt.offset);
-           if (globals->use_blx && r_type == R_ARM_THM_CALL)
-             {
-               /* If the Thumb BLX instruction is available, convert the
-                  BL to a BLX instruction to call the ARM-mode PLT entry.  */
-               lower_insn = (lower_insn & ~0x1000) | 0x0800;
-               sym_flags = STT_FUNC;
-             }
-           else
-             {
-               /* Target the Thumb stub before the ARM PLT entry.  */
-               value -= PLT_THUMB_STUB_SIZE;
-               sym_flags = STT_ARM_TFUNC;
-             }
-           *unresolved_reloc_p = FALSE;
-         }
-
+       enum elf32_arm_stub_type stub_type = arm_stub_none;
        if (r_type == R_ARM_THM_CALL || r_type == R_ARM_THM_JUMP24)
          {
            /* Check if a stub has to be inserted because the destination
               is too far.  */
-           bfd_vma from;
-           bfd_signed_vma branch_offset;
-           struct elf32_arm_stub_hash_entry *stub_entry = NULL;
-
-           from = (input_section->output_section->vma
-                   + input_section->output_offset
-                   + rel->r_offset);
-           branch_offset = (bfd_signed_vma)(value - from);
-
-           if ((!thumb2
-                && (branch_offset > THM_MAX_FWD_BRANCH_OFFSET
-                    || (branch_offset < THM_MAX_BWD_BRANCH_OFFSET)))
-               ||
-               (thumb2
-                && (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
-                    || (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET)))
-               || ((sym_flags != STT_ARM_TFUNC)
-                   && (((r_type == R_ARM_THM_CALL) && !globals->use_blx)
-                       || r_type == R_ARM_THM_JUMP24)))
+           struct elf32_arm_stub_hash_entry *stub_entry;
+           struct elf32_arm_link_hash_entry *hash;
+
+           hash = (struct elf32_arm_link_hash_entry *) h;
+
+           stub_type = arm_type_of_stub (info, input_section, rel,
+                                         &sym_flags, hash, value, sym_sec,
+                                         input_bfd, sym_name);
+
+           if (stub_type != arm_stub_none)
              {
                /* The target is out of reach or we are changing modes, so
                   redirect the branch to the local stub for this
                   function.  */
                stub_entry = elf32_arm_get_stub_entry (input_section,
                                                       sym_sec, h,
-                                                      rel, globals);
+                                                      rel, globals,
+                                                      stub_type);
                if (stub_entry != NULL)
                  value = (stub_entry->stub_offset
                           + stub_entry->stub_sec->output_offset
@@ -7526,6 +7960,33 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
              }
          }
 
+       /* Handle calls via the PLT.  */
+       if (stub_type == arm_stub_none
+           && h != NULL
+           && splt != NULL
+           && h->plt.offset != (bfd_vma) -1)
+         {
+           value = (splt->output_section->vma
+                    + splt->output_offset
+                    + h->plt.offset);
+
+           if (globals->use_blx && r_type == R_ARM_THM_CALL)
+             {
+               /* If the Thumb BLX instruction is available, convert
+                  the BL to a BLX instruction to call the ARM-mode
+                  PLT entry.  */
+               lower_insn = (lower_insn & ~0x1000) | 0x0800;
+               sym_flags = STT_FUNC;
+             }
+           else
+             {
+               /* Target the Thumb stub before the ARM PLT entry.  */
+               value -= PLT_THUMB_STUB_SIZE;
+               sym_flags = STT_ARM_TFUNC;
+             }
+           *unresolved_reloc_p = FALSE;
+         }
+
        relocation = value + signed_addend;
 
        relocation -= (input_section->output_section->vma
@@ -7547,7 +8008,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
        bitsize = howto->bitsize;
        if (!thumb2)
          bitsize -= 2;
-       reloc_signed_max = ((1 << (bitsize - 1)) - 1) >> howto->rightshift;
+       reloc_signed_max = (1 << (bitsize - 1)) - 1;
        reloc_signed_min = ~reloc_signed_max;
 
        /* Assumes two's complement.  */
@@ -7854,12 +8315,9 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
 
              if (info->shared)
                {
-                 asection * srelgot;
                  Elf_Internal_Rela outrel;
                  bfd_byte *loc;
 
-                 srelgot = (bfd_get_section_by_name
-                            (dynobj, RELOC_SECTION (globals, ".got")));
                  BFD_ASSERT (srelgot != NULL);
 
                  outrel.r_addend = addend + value;
@@ -7895,7 +8353,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
       {
        bfd_vma off;
 
-       if (globals->sgot == NULL)
+       if (sgot == NULL)
          abort ();
 
        off = globals->tls_ldm_got.offset;
@@ -7911,29 +8369,29 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                Elf_Internal_Rela outrel;
                bfd_byte *loc;
 
-               if (globals->srelgot == NULL)
+               if (srelgot == NULL)
                  abort ();
 
                outrel.r_addend = 0;
-               outrel.r_offset = (globals->sgot->output_section->vma
-                                  + globals->sgot->output_offset + off);
+               outrel.r_offset = (sgot->output_section->vma
+                                  + sgot->output_offset + off);
                outrel.r_info = ELF32_R_INFO (0, R_ARM_TLS_DTPMOD32);
 
                if (globals->use_rel)
                  bfd_put_32 (output_bfd, outrel.r_addend,
-                             globals->sgot->contents + off);
+                             sgot->contents + off);
 
-               loc = globals->srelgot->contents;
-               loc += globals->srelgot->reloc_count++ * RELOC_SIZE (globals);
+               loc = srelgot->contents;
+               loc += srelgot->reloc_count++ * RELOC_SIZE (globals);
                SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc);
              }
            else
-             bfd_put_32 (output_bfd, 1, globals->sgot->contents + off);
+             bfd_put_32 (output_bfd, 1, sgot->contents + off);
 
            globals->tls_ldm_got.offset |= 1;
          }
 
-       value = globals->sgot->output_section->vma + globals->sgot->output_offset + off
+       value = sgot->output_section->vma + sgot->output_offset + off
          - (input_section->output_section->vma + input_section->output_offset + rel->r_offset);
 
        return _bfd_final_link_relocate (howto, input_bfd, input_section,
@@ -7941,17 +8399,20 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                                         rel->r_addend);
       }
 
+    case R_ARM_TLS_CALL:
+    case R_ARM_THM_TLS_CALL:
     case R_ARM_TLS_GD32:
     case R_ARM_TLS_IE32:
+    case R_ARM_TLS_GOTDESC:
+    case R_ARM_TLS_DESCSEQ:
+    case R_ARM_THM_TLS_DESCSEQ:
       {
-       bfd_vma off;
-       int indx;
+       bfd_vma off, offplt;
+       int indx = 0;
        char tls_type;
 
-       if (globals->sgot == NULL)
-         abort ();
+       BFD_ASSERT (sgot != NULL);
 
-       indx = 0;
        if (h != NULL)
          {
            bfd_boolean dyn;
@@ -7964,18 +8425,23 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                indx = h->dynindx;
              }
            off = h->got.offset;
+           offplt = elf32_arm_hash_entry (h)->tlsdesc_got;
            tls_type = ((struct elf32_arm_link_hash_entry *) h)->tls_type;
          }
        else
          {
-           if (local_got_offsets == NULL)
-             abort ();
+           BFD_ASSERT (local_got_offsets != NULL);
            off = local_got_offsets[r_symndx];
+           offplt = local_tlsdesc_gotents[r_symndx];
            tls_type = elf32_arm_local_got_tls_type (input_bfd)[r_symndx];
          }
 
-       if (tls_type == GOT_UNKNOWN)
-         abort ();
+       /* Linker relaxations happens from one of the
+          R_ARM_{GOTDESC,CALL,DESCSEQ} relocations to IE or LE.  */ 
+       if (ELF32_R_TYPE(rel->r_info) != r_type)
+         tls_type = GOT_TLS_IE; 
+
+       BFD_ASSERT (tls_type != GOT_UNKNOWN);
 
        if ((off & 1) != 0)
          off &= ~1;
@@ -7996,33 +8462,70 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                    || h->root.type != bfd_link_hash_undefweak))
              {
                need_relocs = TRUE;
-               if (globals->srelgot == NULL)
-                 abort ();
-               loc = globals->srelgot->contents;
-               loc += globals->srelgot->reloc_count * RELOC_SIZE (globals);
+               BFD_ASSERT (srelgot != NULL);
              }
 
+           if (tls_type & GOT_TLS_GDESC)
+             {
+               /* We should have relaxed, unless this is an undefined
+                  weak symbol.  */
+               BFD_ASSERT ((h && (h->root.type == bfd_link_hash_undefweak))
+                           || info->shared);
+               BFD_ASSERT (globals->sgotplt_jump_table_size + offplt + 8
+                            <= globals->root.sgotplt->size);
+
+               outrel.r_addend = 0;
+               outrel.r_offset = (globals->root.sgotplt->output_section->vma
+                                  + globals->root.sgotplt->output_offset
+                                  + offplt
+                                  + globals->sgotplt_jump_table_size);
+               
+               outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_DESC);
+               sreloc = globals->root.srelplt;
+               loc = sreloc->contents;
+               loc += globals->next_tls_desc_index++ * RELOC_SIZE (globals);
+               BFD_ASSERT (loc + RELOC_SIZE (globals)
+                          <= sreloc->contents + sreloc->size);
+
+               SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc);
+
+               /* For globals, the first word in the relocation gets
+                  the relocation index and the top bit set, or zero,
+                  if we're binding now.  For locals, it gets the
+                  symbol's offset in the tls section.  */
+               bfd_put_32 (output_bfd,
+                           !h ? value - elf_hash_table (info)->tls_sec->vma
+                           : info->flags & DF_BIND_NOW ? 0
+                           : 0x80000000 | ELF32_R_SYM (outrel.r_info),
+                           globals->root.sgotplt->contents + offplt +
+                           globals->sgotplt_jump_table_size);
+               
+               /* Second word in the relocation is always zero.  */
+               bfd_put_32 (output_bfd, 0,
+                           globals->root.sgotplt->contents + offplt +
+                           globals->sgotplt_jump_table_size + 4);
+             }
            if (tls_type & GOT_TLS_GD)
              {
                if (need_relocs)
                  {
                    outrel.r_addend = 0;
-                   outrel.r_offset = (globals->sgot->output_section->vma
-                                      + globals->sgot->output_offset
+                   outrel.r_offset = (sgot->output_section->vma
+                                      + sgot->output_offset
                                       + cur_off);
                    outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_DTPMOD32);
 
                    if (globals->use_rel)
                      bfd_put_32 (output_bfd, outrel.r_addend,
-                                 globals->sgot->contents + cur_off);
+                                 sgot->contents + cur_off);
+                   loc = srelgot->contents;
+                   loc += srelgot->reloc_count++ * RELOC_SIZE (globals);
 
                    SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc);
-                   globals->srelgot->reloc_count++;
-                   loc += RELOC_SIZE (globals);
 
                    if (indx == 0)
                      bfd_put_32 (output_bfd, value - dtpoff_base (info),
-                                 globals->sgot->contents + cur_off + 4);
+                                 sgot->contents + cur_off + 4);
                    else
                      {
                        outrel.r_addend = 0;
@@ -8032,12 +8535,12 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
 
                        if (globals->use_rel)
                          bfd_put_32 (output_bfd, outrel.r_addend,
-                                     globals->sgot->contents + cur_off + 4);
+                                     sgot->contents + cur_off + 4);
 
+                       loc = srelgot->contents;
+                       loc += srelgot->reloc_count++ * RELOC_SIZE (globals);
 
                        SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc);
-                       globals->srelgot->reloc_count++;
-                       loc += RELOC_SIZE (globals);
                      }
                  }
                else
@@ -8048,9 +8551,9 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                       symbol binding locally.  Mark it as belonging
                       to module 1, the executable.  */
                    bfd_put_32 (output_bfd, 1,
-                               globals->sgot->contents + cur_off);
+                               sgot->contents + cur_off);
                    bfd_put_32 (output_bfd, value - dtpoff_base (info),
-                               globals->sgot->contents + cur_off + 4);
+                               sgot->contents + cur_off + 4);
                  }
 
                cur_off += 8;
@@ -8064,22 +8567,23 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                      outrel.r_addend = value - dtpoff_base (info);
                    else
                      outrel.r_addend = 0;
-                   outrel.r_offset = (globals->sgot->output_section->vma
-                                      + globals->sgot->output_offset
+                   outrel.r_offset = (sgot->output_section->vma
+                                      + sgot->output_offset
                                       + cur_off);
                    outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_TPOFF32);
 
                    if (globals->use_rel)
                      bfd_put_32 (output_bfd, outrel.r_addend,
-                                 globals->sgot->contents + cur_off);
+                                 sgot->contents + cur_off);
+
+                   loc = srelgot->contents;
+                   loc += srelgot->reloc_count++ * RELOC_SIZE (globals);
 
                    SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc);
-                   globals->srelgot->reloc_count++;
-                   loc += RELOC_SIZE (globals);
                  }
                else
                  bfd_put_32 (output_bfd, tpoff (info, value),
-                             globals->sgot->contents + cur_off);
+                             sgot->contents + cur_off);
                cur_off += 4;
              }
 
@@ -8091,8 +8595,142 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
 
        if ((tls_type & GOT_TLS_GD) && r_type != R_ARM_TLS_GD32)
          off += 8;
-       value = globals->sgot->output_section->vma + globals->sgot->output_offset + off
-         - (input_section->output_section->vma + input_section->output_offset + rel->r_offset);
+       else if (tls_type & GOT_TLS_GDESC)
+         off = offplt;
+
+       if (ELF32_R_TYPE(rel->r_info) == R_ARM_TLS_CALL
+           || ELF32_R_TYPE(rel->r_info) == R_ARM_THM_TLS_CALL)
+         {
+           bfd_signed_vma offset;
+           enum elf32_arm_stub_type stub_type
+             = arm_type_of_stub (info, input_section, rel, &sym_flags,
+                                 (struct elf32_arm_link_hash_entry *)h,
+                                 globals->tls_trampoline, globals->root.splt,
+                                 input_bfd, sym_name);
+
+           if (stub_type != arm_stub_none)
+             {
+               struct elf32_arm_stub_hash_entry *stub_entry
+                 = elf32_arm_get_stub_entry
+                 (input_section, globals->root.splt, 0, rel,
+                  globals, stub_type);
+               offset = (stub_entry->stub_offset
+                         + stub_entry->stub_sec->output_offset
+                         + stub_entry->stub_sec->output_section->vma);
+             }
+           else
+             offset = (globals->root.splt->output_section->vma
+                       + globals->root.splt->output_offset
+                       + globals->tls_trampoline);
+
+           if (ELF32_R_TYPE(rel->r_info) == R_ARM_TLS_CALL)
+             {
+               unsigned long inst;
+               
+               offset -= (input_section->output_section->vma +
+                          input_section->output_offset + rel->r_offset + 8);
+
+               inst = offset >> 2;
+               inst &= 0x00ffffff;
+               value = inst | (globals->use_blx ? 0xfa000000 : 0xeb000000);
+             }
+           else
+             {
+               /* Thumb blx encodes the offset in a complicated
+                  fashion.  */
+               unsigned upper_insn, lower_insn;
+               unsigned neg;
+
+               offset -= (input_section->output_section->vma + 
+                          input_section->output_offset
+                          + rel->r_offset + 4);
+           
+               /* Round up the offset to a word boundary */
+               offset = (offset + 2) & ~2;
+               neg = offset < 0;
+               upper_insn = (0xf000
+                             | ((offset >> 12) & 0x3ff)
+                             | (neg << 10));
+               lower_insn = (0xc000
+                             | (((!((offset >> 23) & 1)) ^ neg) << 13)
+                             | (((!((offset >> 22) & 1)) ^ neg) << 11)
+                             | ((offset >> 1) & 0x7ff));
+               bfd_put_16 (input_bfd, upper_insn, hit_data);
+               bfd_put_16 (input_bfd, lower_insn, hit_data + 2);
+               return bfd_reloc_ok;
+             }
+         }
+       /* These relocations needs special care, as besides the fact
+          they point somewhere in .gotplt, the addend must be
+          adjusted accordingly depending on the type of instruction
+          we refer to */
+       else if ((r_type == R_ARM_TLS_GOTDESC) && (tls_type & GOT_TLS_GDESC))
+         {
+           unsigned long data, insn;
+           unsigned thumb;
+           
+           data = bfd_get_32 (input_bfd, hit_data);
+           thumb = data & 1;
+           data &= ~1u;
+           
+           if (thumb)
+             {
+               insn = bfd_get_16 (input_bfd, contents + rel->r_offset - data);
+               if ((insn & 0xf000) == 0xf000 || (insn & 0xf800) == 0xe800)
+                 insn = (insn << 16)
+                   | bfd_get_16 (input_bfd,
+                                 contents + rel->r_offset - data + 2);
+               if ((insn & 0xf800c000) == 0xf000c000)
+                 /* bl/blx */
+                 value = -6;
+               else if ((insn & 0xffffff00) == 0x4400)
+                 /* add */
+                 value = -5;
+               else
+                 {
+                   (*_bfd_error_handler)
+                     (_("%B(%A+0x%lx):unexpected Thumb instruction '0x%x' referenced by TLS_GOTDESC"),
+                      input_bfd, input_section,
+                      (unsigned long)rel->r_offset, insn);
+                   return bfd_reloc_notsupported;
+                 }
+             }
+           else
+             {
+               insn = bfd_get_32 (input_bfd, contents + rel->r_offset - data);
+
+               switch (insn >> 24)
+                 {
+                 case 0xeb:  /* bl */
+                 case 0xfa:  /* blx */
+                   value = -4;
+                   break;
+
+                 case 0xe0:    /* add */
+                   value = -8;
+                   break;
+                   
+                 default:
+                   (*_bfd_error_handler)
+                     (_("%B(%A+0x%lx):unexpected ARM instruction '0x%x' referenced by TLS_GOTDESC"),
+                      input_bfd, input_section,
+                      (unsigned long)rel->r_offset, insn);
+                   return bfd_reloc_notsupported;
+                 }
+             }
+           value += ((globals->root.sgotplt->output_section->vma
+                      + globals->root.sgotplt->output_offset + off)
+                     - (input_section->output_section->vma
+                        + input_section->output_offset
+                        + rel->r_offset)
+                     + globals->sgotplt_jump_table_size);
+         }
+       else
+         value = ((globals->root.sgot->output_section->vma
+                   + globals->root.sgot->output_offset + off)
+                  - (input_section->output_section->vma
+                     + input_section->output_offset + rel->r_offset));
 
        return _bfd_final_link_relocate (howto, input_bfd, input_section,
                                         contents, rel->r_offset, value,
@@ -8720,7 +9358,16 @@ arm_add_to_rel (bfd *              abfd,
    || (R_TYPE) == R_ARM_TLS_DTPMOD32   \
    || (R_TYPE) == R_ARM_TLS_TPOFF32    \
    || (R_TYPE) == R_ARM_TLS_LE32       \
-   || (R_TYPE) == R_ARM_TLS_IE32)
+   || (R_TYPE) == R_ARM_TLS_IE32       \
+   || IS_ARM_TLS_GNU_RELOC (R_TYPE))
+
+/* Specific set of relocations for the gnu tls dialect.  */
+#define IS_ARM_TLS_GNU_RELOC(R_TYPE)   \
+  ((R_TYPE) == R_ARM_TLS_GOTDESC       \
+   || (R_TYPE) == R_ARM_TLS_CALL       \
+   || (R_TYPE) == R_ARM_THM_TLS_CALL   \
+   || (R_TYPE) == R_ARM_TLS_DESCSEQ    \
+   || (R_TYPE) == R_ARM_THM_TLS_DESCSEQ)
 
 /* Relocate an ARM ELF section.  */
 
@@ -8742,6 +9389,8 @@ elf32_arm_relocate_section (bfd *                  output_bfd,
   struct elf32_arm_link_hash_table * globals;
 
   globals = elf32_arm_hash_table (info);
+  if (globals == NULL)
+    return FALSE;
 
   symtab_hdr = & elf_symtab_hdr (input_bfd);
   sym_hashes = elf_sym_hashes (input_bfd);
@@ -8788,9 +9437,11 @@ elf32_arm_relocate_section (bfd *                  output_bfd,
             undefined symbol.  This is a daft object file, but we
             should at least do something about it.  V4BX & NONE
             relocations do not use the symbol and are explicitly
-            allowed to use the undefined symbol, so allow those.  */
+            allowed to use the undefined symbol, so allow those.
+            Likewise for relocations against STN_UNDEF.  */
          if (r_type != R_ARM_V4BX
              && r_type != R_ARM_NONE
+             && r_symndx != STN_UNDEF
              && bfd_is_und_section (sec)
              && ELF_ST_BIND (sym->st_info) != STB_WEAK)
            {
@@ -8911,15 +9562,8 @@ elf32_arm_relocate_section (bfd *                  output_bfd,
        }
 
       if (sec != NULL && elf_discarded_section (sec))
-       {
-         /* For relocs against symbols from removed linkonce sections,
-            or sections discarded by a linker script, we just want the
-            section contents zeroed.  Avoid any special processing.  */
-         _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset);
-         rel->r_info = 0;
-         rel->r_addend = 0;
-         continue;
-       }
+       RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
+                                        rel, relend, howto, contents);
 
       if (info->relocatable)
        {
@@ -8948,7 +9592,7 @@ elf32_arm_relocate_section (bfd *                  output_bfd,
            name = bfd_section_name (input_bfd, sec);
        }
 
-      if (r_symndx != 0
+      if (r_symndx != STN_UNDEF
          && r_type != R_ARM_NONE
          && (h == NULL
              || h->root.type == bfd_link_hash_defined
@@ -8966,12 +9610,33 @@ elf32_arm_relocate_section (bfd *                  output_bfd,
             name);
        }
 
-      r = elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
-                                        input_section, contents, rel,
-                                        relocation, info, sec, name,
-                                        (h ? ELF_ST_TYPE (h->type) :
-                                         ELF_ST_TYPE (sym->st_info)), h,
-                                        &unresolved_reloc, &error_message);
+      /* We call elf32_arm_final_link_relocate unless we're completely
+         done, i.e., the relaxation produced the final output we want,
+         and we won't let anybody mess with it. Also, we have to do
+         addend adjustments in case of a R_ARM_TLS_GOTDESC relocation
+         both in relaxed and non-relaxed cases */
+     if ((elf32_arm_tls_transition (info, r_type, h) != (unsigned)r_type)
+        || (IS_ARM_TLS_GNU_RELOC (r_type)
+            && !((h ? elf32_arm_hash_entry (h)->tls_type : 
+                  elf32_arm_local_got_tls_type (input_bfd)[r_symndx])
+                 & GOT_TLS_GDESC)))
+       {
+        r = elf32_arm_tls_relax (globals, input_bfd, input_section,
+                                 contents, rel, h == NULL);
+        /* This may have been marked unresolved because it came from
+           a shared library.  But we've just dealt with that.  */
+        unresolved_reloc = 0;
+       }
+     else
+       r = bfd_reloc_continue;
+     
+     if (r == bfd_reloc_continue)
+       r = elf32_arm_final_link_relocate (howto, input_bfd, output_bfd,
+                                         input_section, contents, rel,
+                                         relocation, info, sec, name,
+                                         (h ? ELF_ST_TYPE (h->type) :
+                                          ELF_ST_TYPE (sym->st_info)), h,
+                                         &unresolved_reloc, &error_message);
 
       /* Dynamic relocs are not propagated for SEC_DEBUGGING sections
         because such sections are not SEC_ALLOC and thus ld.so will
@@ -9044,9 +9709,9 @@ elf32_arm_relocate_section (bfd *                  output_bfd,
   return TRUE;
 }
 
-/* Add a new unwind edit to the list described by HEAD, TAIL.  If INDEX is zero,
+/* Add a new unwind edit to the list described by HEAD, TAIL.  If TINDEX is zero,
    adds the edit to the start of the list.  (The list must be built in order of
-   ascending INDEX: the function's callers are primarily responsible for
+   ascending TINDEX: the function's callers are primarily responsible for
    maintaining that condition).  */
 
 static void
@@ -9054,16 +9719,16 @@ add_unwind_table_edit (arm_unwind_table_edit **head,
                       arm_unwind_table_edit **tail,
                       arm_unwind_edit_type type,
                       asection *linked_section,
-                      unsigned int index)
+                      unsigned int tindex)
 {
   arm_unwind_table_edit *new_edit = (arm_unwind_table_edit *)
       xmalloc (sizeof (arm_unwind_table_edit));
   
   new_edit->type = type;
   new_edit->linked_section = linked_section;
-  new_edit->index = index;
+  new_edit->index = tindex;
   
-  if (index > 0)
+  if (tindex > 0)
     {
       new_edit->next = NULL;
 
@@ -9125,6 +9790,8 @@ insert_cantunwind_after(asection *text_sec, asection *exidx_sec)
      2. Duplicate entries are merged together (EXIDX_CANTUNWIND, or unwind
         codes which have been inlined into the index).
 
+   If MERGE_EXIDX_ENTRIES is false, duplicate entries are not merged.
+
    The edits are applied when the tables are written
    (in elf32_arm_write_section).
 */
@@ -9132,7 +9799,8 @@ insert_cantunwind_after(asection *text_sec, asection *exidx_sec)
 bfd_boolean
 elf32_arm_fix_exidx_coverage (asection **text_section_order,
                              unsigned int num_text_sections,
-                             struct bfd_link_info *info)
+                             struct bfd_link_info *info,
+                             bfd_boolean merge_exidx_entries)
 {
   bfd *inp;
   unsigned int last_second_word = 0, i;
@@ -9173,8 +9841,7 @@ elf32_arm_fix_exidx_coverage (asection **text_section_order,
 
   /* Walk all text sections in order of increasing VMA.  Eilminate duplicate
      index table entries (EXIDX_CANTUNWIND and inlined unwind opcodes),
-     and add EXIDX_CANTUNWIND entries for sections with no unwind table data.
-   */
+     and add EXIDX_CANTUNWIND entries for sections with no unwind table data.  */
 
   for (i = 0; i < num_text_sections; i++)
     {
@@ -9245,7 +9912,8 @@ elf32_arm_fix_exidx_coverage (asection **text_section_order,
          /* Inlined unwinding data.  Merge if equal to previous.  */
          else if ((second_word & 0x80000000) != 0)
            {
-             if (last_second_word == second_word && last_unwind_type == 1)
+             if (merge_exidx_entries
+                  && last_second_word == second_word && last_unwind_type == 1)
                elide = 1;
              unwind_type = 1;
              last_second_word = second_word;
@@ -9313,11 +9981,32 @@ static bfd_boolean
 elf32_arm_final_link (bfd *abfd, struct bfd_link_info *info)
 {
   struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (info);
+  asection *sec, *osec;
+
+  if (globals == NULL)
+    return FALSE;
 
   /* Invoke the regular ELF backend linker to do all the work.  */
   if (!bfd_elf_final_link (abfd, info))
     return FALSE;
 
+  /* Process stub sections (eg BE8 encoding, ...).  */
+  struct elf32_arm_link_hash_table *htab = elf32_arm_hash_table (info);
+  int i;
+  for (i=0; i<htab->top_id; i++)
+    {
+      sec = htab->stub_group[i].stub_sec;
+      /* Only process it once, in its link_sec slot.  */
+      if (sec && i == htab->stub_group[i].link_sec->id)
+       {
+         osec = sec->output_section;
+         elf32_arm_write_section (abfd, info, sec, sec->contents);
+         if (! bfd_set_section_contents (abfd, osec, sec->contents,
+                                         sec->output_offset, sec->size))
+           return FALSE;
+       }
+    }
+
   /* Write out any glue sections now that we have created all the
      stubs.  */
   if (globals->bfd_of_glue_owner != NULL)
@@ -9504,9 +10193,9 @@ elf32_arm_obj_attrs_arg_type (int tag)
 static int
 elf32_arm_obj_attrs_order (int num)
 {
-  if (num == 4)
+  if (num == LEAST_KNOWN_OBJ_ATTRIBUTE)
     return Tag_conformance;
-  if (num == 5)
+  if (num == LEAST_KNOWN_OBJ_ATTRIBUTE + 1)
     return Tag_nodefaults;
   if ((num - 2) < Tag_nodefaults)
     return num - 2;
@@ -9515,6 +10204,27 @@ elf32_arm_obj_attrs_order (int num)
   return num;
 }
 
+/* Attribute numbers >=64 (mod 128) can be safely ignored.  */
+static bfd_boolean
+elf32_arm_obj_attrs_handle_unknown (bfd *abfd, int tag)
+{
+  if ((tag & 127) < 64)
+    {
+      _bfd_error_handler
+       (_("%B: Unknown mandatory EABI object attribute %d"),
+        abfd, tag);
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
+    }
+  else
+    {
+      _bfd_error_handler
+       (_("Warning: %B: Unknown EABI object attribute %d"),
+        abfd, tag);
+      return TRUE;
+    }
+}
+
 /* Read the architecture from the Tag_also_compatible_with attribute, if any.
    Returns -1 if no architecture could be read.  */
 
@@ -9745,9 +10455,6 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
 {
   obj_attribute *in_attr;
   obj_attribute *out_attr;
-  obj_attribute_list *in_list;
-  obj_attribute_list *out_list;
-  obj_attribute_list **out_listp;
   /* Some tags have 0 = don't care, 1 = strong requirement,
      2 = weak requirement.  */
   static const int order_021[3] = {0, 2, 1};
@@ -9765,11 +10472,33 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
       /* This is the first object.  Copy the attributes.  */
       _bfd_elf_copy_obj_attributes (ibfd, obfd);
 
+      out_attr = elf_known_obj_attributes_proc (obfd);
+
       /* Use the Tag_null value to indicate the attributes have been
         initialized.  */
-      elf_known_obj_attributes_proc (obfd)[0].i = 1;
+      out_attr[0].i = 1;
 
-      return TRUE;
+      /* We do not output objects with Tag_MPextension_use_legacy - we move
+        the attribute's value to Tag_MPextension_use.  */
+      if (out_attr[Tag_MPextension_use_legacy].i != 0)
+       {
+         if (out_attr[Tag_MPextension_use].i != 0
+             && out_attr[Tag_MPextension_use_legacy].i
+               != out_attr[Tag_MPextension_use].i)
+           {
+             _bfd_error_handler
+               (_("Error: %B has both the current and legacy "
+                  "Tag_MPextension_use attributes"), ibfd);
+             result = FALSE;
+           }
+
+         out_attr[Tag_MPextension_use] =
+           out_attr[Tag_MPextension_use_legacy];
+         out_attr[Tag_MPextension_use_legacy].type = 0;
+         out_attr[Tag_MPextension_use_legacy].i = 0;
+       }
+
+      return result;
     }
 
   in_attr = elf_known_obj_attributes_proc (ibfd);
@@ -9784,12 +10513,13 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
        {
          _bfd_error_handler
            (_("error: %B uses VFP register arguments, %B does not"),
-            ibfd, obfd);
+            in_attr[Tag_ABI_VFP_args].i ? ibfd : obfd,
+            in_attr[Tag_ABI_VFP_args].i ? obfd : ibfd);
          result = FALSE;
        }
     }
 
-  for (i = 4; i < NUM_KNOWN_OBJ_ATTRIBUTES; i++)
+  for (i = LEAST_KNOWN_OBJ_ATTRIBUTE; i < NUM_KNOWN_OBJ_ATTRIBUTES; i++)
     {
       /* Merge this attribute with existing attributes.  */
       switch (i)
@@ -9873,27 +10603,26 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
        case Tag_ABI_FP_exceptions:
        case Tag_ABI_FP_user_exceptions:
        case Tag_ABI_FP_number_model:
-       case Tag_VFP_HP_extension:
+       case Tag_FP_HP_extension:
        case Tag_CPU_unaligned_access:
        case Tag_T2EE_use:
-       case Tag_Virtualization_use:
        case Tag_MPextension_use:
          /* Use the largest value specified.  */
          if (in_attr[i].i > out_attr[i].i)
            out_attr[i].i = in_attr[i].i;
          break;
 
-       case Tag_ABI_align8_preserved:
+       case Tag_ABI_align_preserved:
        case Tag_ABI_PCS_RO_data:
          /* Use the smallest value specified.  */
          if (in_attr[i].i < out_attr[i].i)
            out_attr[i].i = in_attr[i].i;
          break;
 
-       case Tag_ABI_align8_needed:
+       case Tag_ABI_align_needed:
          if ((in_attr[i].i > 0 || out_attr[i].i > 0)
-             && (in_attr[Tag_ABI_align8_preserved].i == 0
-                 || out_attr[Tag_ABI_align8_preserved].i == 0))
+             && (in_attr[Tag_ABI_align_preserved].i == 0
+                 || out_attr[Tag_ABI_align_preserved].i == 0))
            {
              /* This error message should be enabled once all non-conformant
                 binaries in the toolchain have had the attributes set
@@ -9914,6 +10643,27 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
            out_attr[i].i = in_attr[i].i;
          break;
 
+       case Tag_Virtualization_use:
+         /* The virtualization tag effectively stores two bits of
+            information: the intended use of TrustZone (in bit 0), and the
+            intended use of Virtualization (in bit 1).  */
+         if (out_attr[i].i == 0)
+           out_attr[i].i = in_attr[i].i;
+         else if (in_attr[i].i != 0
+                  && in_attr[i].i != out_attr[i].i)
+           {
+             if (in_attr[i].i <= 3 && out_attr[i].i <= 3)
+               out_attr[i].i = 3;
+             else
+               {
+                 _bfd_error_handler
+                   (_("error: %B: unable to merge virtualization attributes "
+                      "with %B"),
+                    obfd, ibfd);
+                 result = FALSE;
+               }
+           }
+         break;
 
        case Tag_CPU_arch_profile:
          if (out_attr[i].i != in_attr[i].i)
@@ -9941,8 +10691,13 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
                }
            }
          break;
-       case Tag_VFP_arch:
+       case Tag_FP_arch:
            {
+             /* Tag_ABI_HardFP_use is handled along with Tag_FP_arch since
+                the meaning of Tag_ABI_HardFP_use depends on Tag_FP_arch
+                when it's 0.  It might mean absence of FP hardware if
+                Tag_FP_arch is zero, otherwise it is effectively SP + DP.  */
+
              static const struct
              {
                  int ver;
@@ -9961,6 +10716,40 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
              int regs;
              int newval;
 
+             /* If the output has no requirement about FP hardware,
+                follow the requirement of the input.  */
+             if (out_attr[i].i == 0)
+               {
+                 BFD_ASSERT (out_attr[Tag_ABI_HardFP_use].i == 0);
+                 out_attr[i].i = in_attr[i].i;
+                 out_attr[Tag_ABI_HardFP_use].i
+                   = in_attr[Tag_ABI_HardFP_use].i;
+                 break;
+               }
+             /* If the input has no requirement about FP hardware, do
+                nothing.  */
+             else if (in_attr[i].i == 0)
+               {
+                 BFD_ASSERT (in_attr[Tag_ABI_HardFP_use].i == 0);
+                 break;
+               }
+
+             /* Both the input and the output have nonzero Tag_FP_arch.
+                So Tag_ABI_HardFP_use is (SP & DP) when it's zero.  */
+
+             /* If both the input and the output have zero Tag_ABI_HardFP_use,
+                do nothing.  */
+             if (in_attr[Tag_ABI_HardFP_use].i == 0
+                 && out_attr[Tag_ABI_HardFP_use].i == 0)
+               ;
+             /* If the input and the output have different Tag_ABI_HardFP_use,
+                the combination of them is 3 (SP & DP).  */
+             else if (in_attr[Tag_ABI_HardFP_use].i
+                      != out_attr[Tag_ABI_HardFP_use].i)
+               out_attr[Tag_ABI_HardFP_use].i = 3;
+
+             /* Now we can handle Tag_FP_arch.  */
+
              /* Values greater than 6 aren't defined, so just pick the
                 biggest */
              if (in_attr[i].i > 6 && in_attr[i].i > out_attr[i].i)
@@ -10081,12 +10870,7 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
          /* Merged in target-independent code.  */
          break;
        case Tag_ABI_HardFP_use:
-         /* 1 (SP) and 2 (DP) conflict, so combine to 3 (SP & DP).  */
-         if ((in_attr[i].i == 1 && out_attr[i].i == 2)
-             || (in_attr[i].i == 2 && out_attr[i].i == 1))
-           out_attr[i].i = 3;
-         else if (in_attr[i].i > out_attr[i].i)
-           out_attr[i].i = in_attr[i].i;
+         /* This is handled along with Tag_FP_arch.  */
          break;
        case Tag_ABI_FP_16bit_format:
          if (in_attr[i].i != 0 && out_attr[i].i != 0)
@@ -10103,6 +10887,52 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
            out_attr[i].i = in_attr[i].i;
          break;
 
+       case Tag_DIV_use:
+         /* This tag is set to zero if we can use UDIV and SDIV in Thumb
+            mode on a v7-M or v7-R CPU; to one if we can not use UDIV or
+            SDIV at all; and to two if we can use UDIV or SDIV on a v7-A
+            CPU.  We will merge as follows: If the input attribute's value
+            is one then the output attribute's value remains unchanged.  If
+            the input attribute's value is zero or two then if the output
+            attribute's value is one the output value is set to the input
+            value, otherwise the output value must be the same as the
+            inputs.  */ 
+         if (in_attr[i].i != 1 && out_attr[i].i != 1) 
+           { 
+             if (in_attr[i].i != out_attr[i].i)
+               {
+                 _bfd_error_handler
+                   (_("DIV usage mismatch between %B and %B"),
+                    ibfd, obfd); 
+                 result = FALSE;
+               }
+           } 
+
+         if (in_attr[i].i != 1)
+           out_attr[i].i = in_attr[i].i; 
+         
+         break;
+
+       case Tag_MPextension_use_legacy:
+         /* We don't output objects with Tag_MPextension_use_legacy - we
+            move the value to Tag_MPextension_use.  */
+         if (in_attr[i].i != 0 && in_attr[Tag_MPextension_use].i != 0)
+           {
+             if (in_attr[Tag_MPextension_use].i != in_attr[i].i)
+               {
+                 _bfd_error_handler
+                   (_("%B has has both the current and legacy "
+                      "Tag_MPextension_use attributes"), 
+                    ibfd);
+                 result = FALSE;
+               }
+           }
+
+         if (in_attr[i].i > out_attr[Tag_MPextension_use].i)
+           out_attr[Tag_MPextension_use] = in_attr[i];
+
+         break;
+
        case Tag_nodefaults:
          /* This tag is set if it exists, but the value is unused (and is
             typically zero).  We don't actually need to do anything here -
@@ -10121,45 +10951,8 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
          break;
 
        default:
-         {
-           bfd *err_bfd = NULL;
-
-           /* The "known_obj_attributes" table does contain some undefined
-              attributes.  Ensure that there are unused.  */
-           if (out_attr[i].i != 0 || out_attr[i].s != NULL)
-             err_bfd = obfd;
-           else if (in_attr[i].i != 0 || in_attr[i].s != NULL)
-             err_bfd = ibfd;
-
-           if (err_bfd != NULL)
-             {
-               /* Attribute numbers >=64 (mod 128) can be safely ignored.  */
-               if ((i & 127) < 64)
-                 {
-                   _bfd_error_handler
-                     (_("%B: Unknown mandatory EABI object attribute %d"),
-                      err_bfd, i);
-                   bfd_set_error (bfd_error_bad_value);
-                   result = FALSE;
-                 }
-               else
-                 {
-                   _bfd_error_handler
-                     (_("Warning: %B: Unknown EABI object attribute %d"),
-                      err_bfd, i);
-                 }
-             }
-
-           /* Only pass on attributes that match in both inputs.  */
-           if (in_attr[i].i != out_attr[i].i
-               || in_attr[i].s != out_attr[i].s
-               || (in_attr[i].s != NULL && out_attr[i].s != NULL
-                   && strcmp (in_attr[i].s, out_attr[i].s) != 0))
-             {
-               out_attr[i].i = 0;
-               out_attr[i].s = NULL;
-             }
-         }
+         result
+           = result && _bfd_elf_merge_unknown_attribute_low (ibfd, obfd, i);
        }
 
       /* If out_attr was copied from in_attr then it won't have a type yet.  */
@@ -10168,81 +10961,12 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
     }
 
   /* Merge Tag_compatibility attributes and any common GNU ones.  */
-  _bfd_elf_merge_object_attributes (ibfd, obfd);
+  if (!_bfd_elf_merge_object_attributes (ibfd, obfd))
+    return FALSE;
 
   /* Check for any attributes not known on ARM.  */
-  in_list = elf_other_obj_attributes_proc (ibfd);
-  out_listp = &elf_other_obj_attributes_proc (obfd);
-  out_list = *out_listp;
+  result &= _bfd_elf_merge_unknown_attribute_list (ibfd, obfd);
 
-  for (; in_list || out_list; )
-    {
-      bfd *err_bfd = NULL;
-      int err_tag = 0;
-
-      /* The tags for each list are in numerical order.  */
-      /* If the tags are equal, then merge.  */
-      if (out_list && (!in_list || in_list->tag > out_list->tag))
-       {
-         /* This attribute only exists in obfd.  We can't merge, and we don't
-            know what the tag means, so delete it.  */
-         err_bfd = obfd;
-         err_tag = out_list->tag;
-         *out_listp = out_list->next;
-         out_list = *out_listp;
-       }
-      else if (in_list && (!out_list || in_list->tag < out_list->tag))
-       {
-         /* This attribute only exists in ibfd. We can't merge, and we don't
-            know what the tag means, so ignore it.  */
-         err_bfd = ibfd;
-         err_tag = in_list->tag;
-         in_list = in_list->next;
-       }
-      else /* The tags are equal.  */
-       {
-         /* As present, all attributes in the list are unknown, and
-            therefore can't be merged meaningfully.  */
-         err_bfd = obfd;
-         err_tag = out_list->tag;
-
-         /*  Only pass on attributes that match in both inputs.  */
-         if (in_list->attr.i != out_list->attr.i
-             || in_list->attr.s != out_list->attr.s
-             || (in_list->attr.s && out_list->attr.s
-                 && strcmp (in_list->attr.s, out_list->attr.s) != 0))
-           {
-             /* No match.  Delete the attribute.  */
-             *out_listp = out_list->next;
-             out_list = *out_listp;
-           }
-         else
-           {
-             /* Matched.  Keep the attribute and move to the next.  */
-             out_list = out_list->next;
-             in_list = in_list->next;
-           }
-       }
-
-      if (err_bfd)
-       {
-         /* Attribute numbers >=64 (mod 128) can be safely ignored.  */
-         if ((err_tag & 127) < 64)
-           {
-             _bfd_error_handler
-               (_("%B: Unknown mandatory EABI object attribute %d"),
-                err_bfd, err_tag);
-             bfd_set_error (bfd_error_bad_value);
-             result = FALSE;
-           }
-         else
-           {
-             _bfd_error_handler
-               (_("Warning: %B: Unknown EABI object attribute %d"),
-                err_bfd, err_tag);
-           }
-       }
-    }
   return result;
 }
 
@@ -10461,6 +11185,8 @@ elf32_arm_gc_sweep_hook (bfd *                     abfd,
     return TRUE;
 
   globals = elf32_arm_hash_table (info);
+  if (globals == NULL)
+    return FALSE;
 
   elf_section_data (sec)->local_dynrel = NULL;
 
@@ -10507,7 +11233,7 @@ elf32_arm_gc_sweep_hook (bfd *                     abfd,
          break;
 
        case R_ARM_TLS_LDM32:
-         elf32_arm_hash_table (info)->tls_ldm_got.refcount -= 1;
+         globals->tls_ldm_got.refcount -= 1;
          break;
 
        case R_ARM_ABS32:
@@ -10535,8 +11261,8 @@ elf32_arm_gc_sweep_hook (bfd *                     abfd,
          if (h != NULL)
            {
              struct elf32_arm_link_hash_entry *eh;
-             struct elf32_arm_relocs_copied **pp;
-             struct elf32_arm_relocs_copied *p;
+             struct elf_dyn_relocs **pp;
+             struct elf_dyn_relocs *p;
 
              eh = (struct elf32_arm_link_hash_entry *) h;
 
@@ -10555,20 +11281,17 @@ elf32_arm_gc_sweep_hook (bfd *                     abfd,
                  || r_type == R_ARM_REL32
                   || r_type == R_ARM_ABS32_NOI
                   || r_type == R_ARM_REL32_NOI)
-               {
-                 for (pp = &eh->relocs_copied; (p = *pp) != NULL;
-                      pp = &p->next)
-                 if (p->section == sec)
+               for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
+                 if (p->sec == sec)
                    {
                      p->count -= 1;
                      if (ELF32_R_TYPE (rel->r_info) == R_ARM_REL32
-                          || ELF32_R_TYPE (rel->r_info) == R_ARM_REL32_NOI)
+                         || ELF32_R_TYPE (rel->r_info) == R_ARM_REL32_NOI)
                        p->pc_count -= 1;
                      if (p->count == 0)
                        *pp = p->next;
                      break;
                    }
-               }
            }
          break;
 
@@ -10592,7 +11315,6 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
   const Elf_Internal_Rela *rel_end;
   bfd *dynobj;
   asection *sreloc;
-  bfd_vma *local_got_offsets;
   struct elf32_arm_link_hash_table *htab;
   bfd_boolean needs_plt;
   unsigned long nsyms;
@@ -10603,6 +11325,9 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
   BFD_ASSERT (is_arm_elf (abfd));
 
   htab = elf32_arm_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
+
   sreloc = NULL;
 
   /* Create dynamic sections for relocatable executables so that we can
@@ -10615,8 +11340,6 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
     }
 
   dynobj = elf_hash_table (info)->dynobj;
-  local_got_offsets = elf_local_got_offsets (abfd);
-
   symtab_hdr = & elf_symtab_hdr (abfd);
   sym_hashes = elf_sym_hashes (abfd);
   nsyms = NUM_SHDR_ENTRIES (symtab_hdr);
@@ -10637,7 +11360,7 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
          /* PR 9934: It is possible to have relocations that do not
             refer to symbols, thus it is also possible to have an
             object file containing relocations but no symbol table.  */
-         && (r_symndx > 0 || nsyms > 0))
+         && (r_symndx > STN_UNDEF || nsyms > 0))
        {
          (*_bfd_error_handler) (_("%B: bad symbol index: %d"), abfd,
                                   r_symndx);
@@ -10656,12 +11379,19 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
       eh = (struct elf32_arm_link_hash_entry *) h;
 
+      /* Could be done earlier, if h were already available.  */
+      r_type = elf32_arm_tls_transition (info, r_type, h);
       switch (r_type)
         {
          case R_ARM_GOT32:
          case R_ARM_GOT_PREL:
          case R_ARM_TLS_GD32:
          case R_ARM_TLS_IE32:
+         case R_ARM_TLS_GOTDESC:
+         case R_ARM_TLS_DESCSEQ:
+         case R_ARM_THM_TLS_DESCSEQ:
+         case R_ARM_TLS_CALL:
+         case R_ARM_THM_TLS_CALL:
            /* This symbol requires a global offset table entry.  */
            {
              int tls_type, old_tls_type;
@@ -10669,7 +11399,14 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
              switch (r_type)
                {
                case R_ARM_TLS_GD32: tls_type = GOT_TLS_GD; break;
+                 
                case R_ARM_TLS_IE32: tls_type = GOT_TLS_IE; break;
+                 
+               case R_ARM_TLS_GOTDESC:
+               case R_ARM_TLS_CALL: case R_ARM_THM_TLS_CALL:
+               case R_ARM_TLS_DESCSEQ: case R_ARM_THM_TLS_DESCSEQ:
+                 tls_type = GOT_TLS_GDESC; break;
+                 
                default: tls_type = GOT_NORMAL; break;
                }
 
@@ -10689,27 +11426,44 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
                      bfd_size_type size;
 
                      size = symtab_hdr->sh_info;
-                     size *= (sizeof (bfd_signed_vma) + sizeof (char));
+                     size *= (sizeof (bfd_signed_vma)
+                              + sizeof (bfd_vma) + sizeof (char));
                      local_got_refcounts = (bfd_signed_vma *)
                           bfd_zalloc (abfd, size);
                      if (local_got_refcounts == NULL)
                        return FALSE;
                      elf_local_got_refcounts (abfd) = local_got_refcounts;
+                     elf32_arm_local_tlsdesc_gotent (abfd)
+                       = (bfd_vma *) (local_got_refcounts
+                                      + symtab_hdr->sh_info);
                      elf32_arm_local_got_tls_type (abfd)
-                       = (char *) (local_got_refcounts + symtab_hdr->sh_info);
+                       = (char *) (elf32_arm_local_tlsdesc_gotent (abfd)
+                                   + symtab_hdr->sh_info);
                    }
                  local_got_refcounts[r_symndx] += 1;
                  old_tls_type = elf32_arm_local_got_tls_type (abfd) [r_symndx];
                }
 
-             /* We will already have issued an error message if there is a
-                TLS / non-TLS mismatch, based on the symbol type.  We don't
-                support any linker relaxations.  So just combine any TLS
-                types needed.  */
+             /* If a variable is accessed with both tls methods, two
+                slots may be created.  */
+             if (GOT_TLS_GD_ANY_P (old_tls_type)
+                 && GOT_TLS_GD_ANY_P (tls_type))
+               tls_type |= old_tls_type;
+
+             /* We will already have issued an error message if there
+                is a TLS/non-TLS mismatch, based on the symbol
+                type.  So just combine any TLS types needed.  */
              if (old_tls_type != GOT_UNKNOWN && old_tls_type != GOT_NORMAL
                  && tls_type != GOT_NORMAL)
                tls_type |= old_tls_type;
 
+             /* If the symbol is accessed in both IE and GDESC
+                method, we're able to relax. Turn off the GDESC flag,
+                without messing up with any other kind of tls types
+                that may be involved */
+             if ((tls_type & GOT_TLS_IE) && (tls_type & GOT_TLS_GDESC))
+               tls_type &= ~GOT_TLS_GDESC;
+
              if (old_tls_type != tls_type)
                {
                  if (h != NULL)
@@ -10727,7 +11481,7 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
          case R_ARM_GOTOFF32:
          case R_ARM_GOTPC:
-           if (htab->sgot == NULL)
+           if (htab->root.sgot == NULL)
              {
                if (htab->root.dynobj == NULL)
                  htab->root.dynobj = abfd;
@@ -10826,14 +11580,14 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
                possible that DEF_REGULAR is not set now but will be set
                later (it is never cleared).  We account for that
                possibility below by storing information in the
-               relocs_copied field of the hash table entry.  */
+               dyn_relocs field of the hash table entry.  */
            if ((info->shared || htab->root.is_relocatable_executable)
                && (sec->flags & SEC_ALLOC) != 0
                && ((r_type == R_ARM_ABS32 || r_type == R_ARM_ABS32_NOI)
                    || (h != NULL && ! h->needs_plt
                        && (! info->symbolic || ! h->def_regular))))
              {
-               struct elf32_arm_relocs_copied *p, **head;
+               struct elf_dyn_relocs *p, **head;
 
                /* When creating a shared object, we must copy these
                    reloc types into the output file.  We create a reloc
@@ -10861,7 +11615,7 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
                   relocations we need for this symbol.  */
                if (h != NULL)
                  {
-                   head = &((struct elf32_arm_link_hash_entry *) h)->relocs_copied;
+                   head = &((struct elf32_arm_link_hash_entry *) h)->dyn_relocs;
                  }
                else
                  {
@@ -10882,21 +11636,21 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
                      s = sec;
 
                    vpp = &elf_section_data (s)->local_dynrel;
-                   head = (struct elf32_arm_relocs_copied **) vpp;
+                   head = (struct elf_dyn_relocs **) vpp;
                  }
 
                p = *head;
-               if (p == NULL || p->section != sec)
+               if (p == NULL || p->sec != sec)
                  {
                    bfd_size_type amt = sizeof *p;
 
-                   p = (struct elf32_arm_relocs_copied *)
+                   p = (struct elf_dyn_relocs *)
                         bfd_alloc (htab->root.dynobj, amt);
                    if (p == NULL)
                      return FALSE;
                    p->next = *head;
                    *head = p;
-                   p->section = sec;
+                   p->sec = sec;
                    p->count = 0;
                    p->pc_count = 0;
                  }
@@ -11125,6 +11879,9 @@ elf32_arm_adjust_dynamic_symbol (struct bfd_link_info * info,
   struct elf32_arm_link_hash_table *globals;
 
   globals = elf32_arm_hash_table (info);
+  if (globals == NULL)
+    return FALSE;
+
   dynobj = elf_hash_table (info)->dynobj;
 
   /* Make sure we know what is going on here.  */
@@ -11247,7 +12004,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
   struct bfd_link_info *info;
   struct elf32_arm_link_hash_table *htab;
   struct elf32_arm_link_hash_entry *eh;
-  struct elf32_arm_relocs_copied *p;
+  struct elf_dyn_relocs *p;
   bfd_signed_vma thumb_refs;
 
   eh = (struct elf32_arm_link_hash_entry *) h;
@@ -11263,6 +12020,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
 
   info = (struct bfd_link_info *) inf;
   htab = elf32_arm_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
 
   if (htab->root.dynamic_sections_created
       && h->plt.refcount > 0)
@@ -11279,7 +12038,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
       if (info->shared
          || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
        {
-         asection *s = htab->splt;
+         asection *s = htab->root.splt;
 
          /* If this is the first .plt entry, make room for the special
             first entry.  */
@@ -11325,12 +12084,14 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
            {
              /* We also need to make an entry in the .got.plt section, which
                 will be placed in the .got section by the linker script.  */
-             eh->plt_got_offset = htab->sgotplt->size;
-             htab->sgotplt->size += 4;
+             eh->plt_got_offset = (htab->root.sgotplt->size
+                                   - 8 * htab->num_tls_desc);
+             htab->root.sgotplt->size += 4;
            }
 
          /* We also need to make an entry in the .rel(a).plt section.  */
-         htab->srelplt->size += RELOC_SIZE (htab);
+         htab->root.srelplt->size += RELOC_SIZE (htab);
+         htab->next_tls_desc_index++;
 
          /* VxWorks executables have a second set of relocations for
             each PLT entry.  They go in a separate relocation section,
@@ -11360,6 +12121,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
       h->needs_plt = 0;
     }
 
+  eh = (struct elf32_arm_link_hash_entry *) h;
+  eh->tlsdesc_got = (bfd_vma) -1;
+
   if (h->got.refcount > 0)
     {
       asection *s;
@@ -11378,7 +12142,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
 
       if (!htab->symbian_p)
        {
-         s = htab->sgot;
+         s = htab->root.sgot;
          h->got.offset = s->size;
 
          if (tls_type == GOT_UNKNOWN)
@@ -11389,9 +12153,28 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
            s->size += 4;
          else
            {
+              if (tls_type & GOT_TLS_GDESC)
+               {
+                 /* R_ARM_TLS_DESC needs 2 GOT slots.  */
+                 eh->tlsdesc_got
+                   = (htab->root.sgotplt->size
+                      - elf32_arm_compute_jump_table_size (htab));
+                 htab->root.sgotplt->size += 8;
+                 h->got.offset = (bfd_vma) -2;
+                 /* plt_got_offset needs to know there's a TLS_DESC
+                    reloc in the middle of .got.plt.  */
+                  htab->num_tls_desc++;
+               }
+
              if (tls_type & GOT_TLS_GD)
-               /* R_ARM_TLS_GD32 needs 2 consecutive GOT slots.  */
-               s->size += 8;
+               {
+                 /* R_ARM_TLS_GD32 needs 2 consecutive GOT slots.  If
+                    the symbol is both GD and GDESC, got.offset may
+                    have been overwritten.  */
+                 h->got.offset = s->size;
+                 s->size += 8;
+               }
+
              if (tls_type & GOT_TLS_IE)
                /* R_ARM_TLS_IE32 needs one GOT slot.  */
                s->size += 4;
@@ -11411,19 +12194,28 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
                  || h->root.type != bfd_link_hash_undefweak))
            {
              if (tls_type & GOT_TLS_IE)
-               htab->srelgot->size += RELOC_SIZE (htab);
+               htab->root.srelgot->size += RELOC_SIZE (htab);
 
              if (tls_type & GOT_TLS_GD)
-               htab->srelgot->size += RELOC_SIZE (htab);
+               htab->root.srelgot->size += RELOC_SIZE (htab);
+
+             if (tls_type & GOT_TLS_GDESC) 
+               {
+                 htab->root.srelplt->size += RELOC_SIZE (htab); 
+                 /* GDESC needs a trampoline to jump to.  */
+                 htab->tls_trampoline = -1;
+               }
 
-             if ((tls_type & GOT_TLS_GD) && indx != 0)
-               htab->srelgot->size += RELOC_SIZE (htab);
+             /* Only GD needs it.  GDESC just emits one relocation per
+                2 entries.  */
+             if ((tls_type & GOT_TLS_GD) && indx != 0)  
+               htab->root.srelgot->size += RELOC_SIZE (htab); 
            }
          else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
                    || h->root.type != bfd_link_hash_undefweak)
                   && (info->shared
                   || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)))
-           htab->srelgot->size += RELOC_SIZE (htab);
+           htab->root.srelgot->size += RELOC_SIZE (htab);
        }
     }
   else
@@ -11460,7 +12252,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
       h->root.u.def.value = th->root.u.def.value & ~1;
     }
 
-  if (eh->relocs_copied == NULL)
+  if (eh->dyn_relocs == NULL)
     return TRUE;
 
   /* In the shared -Bsymbolic case, discard space allocated for
@@ -11479,9 +12271,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
          should avoid writing assembly like ".long foo - .".  */
       if (SYMBOL_CALLS_LOCAL (info, h))
        {
-         struct elf32_arm_relocs_copied **pp;
+         struct elf_dyn_relocs **pp;
 
-         for (pp = &eh->relocs_copied; (p = *pp) != NULL; )
+         for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
            {
              p->count -= p->pc_count;
              p->pc_count = 0;
@@ -11492,13 +12284,13 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
            }
        }
 
-      if (elf32_arm_hash_table (info)->vxworks_p)
+      if (htab->vxworks_p)
        {
-         struct elf32_arm_relocs_copied **pp;
+         struct elf_dyn_relocs **pp;
 
-         for (pp = &eh->relocs_copied; (p = *pp) != NULL; )
+         for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
            {
-             if (strcmp (p->section->output_section->name, ".tls_vars") == 0)
+             if (strcmp (p->sec->output_section->name, ".tls_vars") == 0)
                *pp = p->next;
              else
                pp = &p->next;
@@ -11507,11 +12299,11 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
 
       /* Also discard relocs on undefined weak syms with non-default
          visibility.  */
-      if (eh->relocs_copied != NULL
+      if (eh->dyn_relocs != NULL
          && h->root.type == bfd_link_hash_undefweak)
        {
          if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
-           eh->relocs_copied = NULL;
+           eh->dyn_relocs = NULL;
 
          /* Make sure undefined weak symbols are output as a dynamic
             symbol in PIEs.  */
@@ -11562,15 +12354,15 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
            goto keep;
        }
 
-      eh->relocs_copied = NULL;
+      eh->dyn_relocs = NULL;
 
     keep: ;
     }
 
   /* Finally, allocate space.  */
-  for (p = eh->relocs_copied; p != NULL; p = p->next)
+  for (p = eh->dyn_relocs; p != NULL; p = p->next)
     {
-      asection *sreloc = elf_section_data (p->section)->sreloc;
+      asection *sreloc = elf_section_data (p->sec)->sreloc;
       sreloc->size += p->count * RELOC_SIZE (htab);
     }
 
@@ -11583,15 +12375,15 @@ static bfd_boolean
 elf32_arm_readonly_dynrelocs (struct elf_link_hash_entry * h, void * inf)
 {
   struct elf32_arm_link_hash_entry * eh;
-  struct elf32_arm_relocs_copied * p;
+  struct elf_dyn_relocs * p;
 
   if (h->root.type == bfd_link_hash_warning)
     h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
   eh = (struct elf32_arm_link_hash_entry *) h;
-  for (p = eh->relocs_copied; p != NULL; p = p->next)
+  for (p = eh->dyn_relocs; p != NULL; p = p->next)
     {
-      asection *s = p->section;
+      asection *s = p->sec;
 
       if (s != NULL && (s->flags & SEC_READONLY) != 0)
        {
@@ -11613,6 +12405,9 @@ bfd_elf32_arm_set_byteswap_code (struct bfd_link_info *info,
   struct elf32_arm_link_hash_table *globals;
 
   globals = elf32_arm_hash_table (info);
+  if (globals == NULL)
+    return;
+
   globals->byteswap_code = byteswap_code;
 }
 
@@ -11630,6 +12425,9 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
   struct elf32_arm_link_hash_table *htab;
 
   htab = elf32_arm_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
+
   dynobj = elf_hash_table (info)->dynobj;
   BFD_ASSERT (dynobj != NULL);
   check_use_blx (htab);
@@ -11653,23 +12451,24 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
       bfd_signed_vma *local_got;
       bfd_signed_vma *end_local_got;
       char *local_tls_type;
+      bfd_vma *local_tlsdesc_gotent;
       bfd_size_type locsymcount;
       Elf_Internal_Shdr *symtab_hdr;
       asection *srel;
-      bfd_boolean is_vxworks = elf32_arm_hash_table (info)->vxworks_p;
+      bfd_boolean is_vxworks = htab->vxworks_p;
 
       if (! is_arm_elf (ibfd))
        continue;
 
       for (s = ibfd->sections; s != NULL; s = s->next)
        {
-         struct elf32_arm_relocs_copied *p;
+         struct elf_dyn_relocs *p;
 
-         for (p = (struct elf32_arm_relocs_copied *)
+         for (p = (struct elf_dyn_relocs *)
                    elf_section_data (s)->local_dynrel; p != NULL; p = p->next)
            {
-             if (!bfd_is_abs_section (p->section)
-                 && bfd_is_abs_section (p->section->output_section))
+             if (!bfd_is_abs_section (p->sec)
+                 && bfd_is_abs_section (p->sec->output_section))
                {
                  /* Input section has been discarded, either because
                     it is a copy of a linkonce section or due to
@@ -11677,7 +12476,7 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
                     the relocs too.  */
                }
              else if (is_vxworks
-                      && strcmp (p->section->output_section->name,
+                      && strcmp (p->sec->output_section->name,
                                  ".tls_vars") == 0)
                {
                  /* Relocations in vxworks .tls_vars sections are
@@ -11685,9 +12484,9 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
                }
              else if (p->count != 0)
                {
-                 srel = elf_section_data (p->section)->sreloc;
+                 srel = elf_section_data (p->sec)->sreloc;
                  srel->size += p->count * RELOC_SIZE (htab);
-                 if ((p->section->output_section->flags & SEC_READONLY) != 0)
+                 if ((p->sec->output_section->flags & SEC_READONLY) != 0)
                    info->flags |= DF_TEXTREL;
                }
            }
@@ -11701,23 +12500,49 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
       locsymcount = symtab_hdr->sh_info;
       end_local_got = local_got + locsymcount;
       local_tls_type = elf32_arm_local_got_tls_type (ibfd);
-      s = htab->sgot;
-      srel = htab->srelgot;
-      for (; local_got < end_local_got; ++local_got, ++local_tls_type)
+      local_tlsdesc_gotent = elf32_arm_local_tlsdesc_gotent (ibfd);
+      s = htab->root.sgot;
+      srel = htab->root.srelgot;
+      for (; local_got < end_local_got;
+          ++local_got, ++local_tls_type, ++local_tlsdesc_gotent)
        {
+         *local_tlsdesc_gotent = (bfd_vma) -1;
          if (*local_got > 0)
            {
              *local_got = s->size;
              if (*local_tls_type & GOT_TLS_GD)
                /* TLS_GD relocs need an 8-byte structure in the GOT.  */
                s->size += 8;
+             if (*local_tls_type & GOT_TLS_GDESC)
+               {
+                 *local_tlsdesc_gotent = htab->root.sgotplt->size
+                   - elf32_arm_compute_jump_table_size (htab);
+                 htab->root.sgotplt->size += 8;
+                 *local_got = (bfd_vma) -2;
+                 /* plt_got_offset needs to know there's a TLS_DESC
+                    reloc in the middle of .got.plt.  */
+                  htab->num_tls_desc++;
+               }
              if (*local_tls_type & GOT_TLS_IE)
                s->size += 4;
-             if (*local_tls_type == GOT_NORMAL)
-               s->size += 4;
 
-             if (info->shared || *local_tls_type == GOT_TLS_GD)
+             if (*local_tls_type & GOT_NORMAL)
+               {
+                 /* If the symbol is both GD and GDESC, *local_got
+                    may have been overwritten.  */
+                 *local_got = s->size;
+                 s->size += 4;
+               }
+
+             if ((info->shared && !(*local_tls_type & GOT_TLS_GDESC))
+                 || *local_tls_type & GOT_TLS_GD)
                srel->size += RELOC_SIZE (htab);
+
+             if (info->shared && *local_tls_type & GOT_TLS_GDESC)
+               {
+                 htab->root.srelplt->size += RELOC_SIZE (htab);
+                 htab->tls_trampoline = -1;
+               }
            }
          else
            *local_got = (bfd_vma) -1;
@@ -11728,10 +12553,10 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
     {
       /* Allocate two GOT entries and one dynamic relocation (if necessary)
         for R_ARM_TLS_LDM32 relocations.  */
-      htab->tls_ldm_got.offset = htab->sgot->size;
-      htab->sgot->size += 8;
+      htab->tls_ldm_got.offset = htab->root.sgot->size;
+      htab->root.sgot->size += 8;
       if (info->shared)
-       htab->srelgot->size += RELOC_SIZE (htab);
+       htab->root.srelgot->size += RELOC_SIZE (htab);
     }
   else
     htab->tls_ldm_got.offset = -1;
@@ -11759,6 +12584,34 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
   /* Allocate space for the glue sections now that we've sized them.  */
   bfd_elf32_arm_allocate_interworking_sections (info);
 
+  /* 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
+     for them, it suffices to multiply the reloc count by the jump
+     slot size.  */
+  if (htab->root.srelplt)
+    htab->sgotplt_jump_table_size = elf32_arm_compute_jump_table_size(htab);
+
+  if (htab->tls_trampoline)
+    {
+      if (htab->root.splt->size == 0)
+       htab->root.splt->size += htab->plt_header_size;
+      
+      htab->tls_trampoline = htab->root.splt->size;
+      htab->root.splt->size += htab->plt_entry_size;
+      
+      /* If we're not using lazy TLS relocations, don't generate the
+         PLT and GOT entries they require.  */
+      if (!(info->flags & DF_BIND_NOW))
+       {
+         htab->dt_tlsdesc_got = htab->root.sgot->size;
+         htab->root.sgot->size += 4;
+
+         htab->dt_tlsdesc_plt = htab->root.splt->size;
+         htab->root.splt->size += 4 * ARRAY_SIZE (dl_tlsdesc_lazy_trampoline);
+       }
+    }
+
   /* The check_relocs and adjust_dynamic_symbol entry points have
      determined the sizes of the various dynamic sections.  Allocate
      memory for them.  */
@@ -11786,7 +12639,7 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
            {
              /* Remember whether there are any reloc sections other
                 than .rel(a).plt and .rela.plt.unloaded.  */
-             if (s != htab->srelplt && s != htab->srelplt2)
+             if (s != htab->root.srelplt && s != htab->srelplt2)
                relocs = TRUE;
 
              /* We use the reloc_count field as a counter if we need
@@ -11849,6 +12702,11 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
                                     htab->use_rel ? DT_REL : DT_RELA)
              || !add_dynamic_entry (DT_JMPREL, 0))
            return FALSE;
+
+         if (htab->dt_tlsdesc_plt &&
+               (!add_dynamic_entry (DT_TLSDESC_PLT,0) 
+                || !add_dynamic_entry (DT_TLSDESC_GOT,0)))
+           return FALSE; 
        }
 
       if (relocs)
@@ -11889,6 +12747,49 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
   return TRUE;
 }
 
+/* Size sections even though they're not dynamic.  We use it to setup
+   _TLS_MODULE_BASE_, if needed.  */
+
+static bfd_boolean
+elf32_arm_always_size_sections (bfd *output_bfd,
+                               struct bfd_link_info *info)
+{
+  asection *tls_sec;
+
+  if (info->relocatable)
+    return TRUE;
+
+  tls_sec = elf_hash_table (info)->tls_sec;
+
+  if (tls_sec)
+    {
+      struct elf_link_hash_entry *tlsbase;
+
+      tlsbase = elf_link_hash_lookup
+       (elf_hash_table (info), "_TLS_MODULE_BASE_", TRUE, TRUE, FALSE);
+
+      if (tlsbase)
+        {
+          struct bfd_link_hash_entry *bh = NULL;
+         const struct elf_backend_data *bed
+            = get_elf_backend_data (output_bfd);
+
+          if (!(_bfd_generic_link_add_one_symbol
+               (info, output_bfd, "_TLS_MODULE_BASE_", BSF_LOCAL,
+                tls_sec, 0, NULL, FALSE,
+                bed->collect, &bh)))
+           return FALSE;
+         
+         tlsbase->type = STT_TLS;
+          tlsbase = (struct elf_link_hash_entry *)bh;
+          tlsbase->def_regular = 1;
+          tlsbase->other = STV_HIDDEN;
+          (*bed->elf_backend_hide_symbol) (info, tlsbase, TRUE);
+       }
+    }
+  return TRUE;
+}
+
 /* Finish up dynamic symbol handling.  We set the contents of various
    dynamic sections here.  */
 
@@ -11898,12 +12799,13 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd,
                                 struct elf_link_hash_entry * h,
                                 Elf_Internal_Sym * sym)
 {
-  bfd * dynobj;
   struct elf32_arm_link_hash_table *htab;
   struct elf32_arm_link_hash_entry *eh;
 
-  dynobj = elf_hash_table (info)->dynobj;
   htab = elf32_arm_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
+
   eh = (struct elf32_arm_link_hash_entry *) h;
 
   if (h->plt.offset != (bfd_vma) -1)
@@ -11919,8 +12821,8 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd,
 
       BFD_ASSERT (h->dynindx != -1);
 
-      splt = bfd_get_section_by_name (dynobj, ".plt");
-      srel = bfd_get_section_by_name (dynobj, RELOC_SECTION (htab, ".plt"));
+      splt = htab->root.splt;
+      srel = htab->root.srelplt;
       BFD_ASSERT (splt != NULL && srel != NULL);
 
       /* Fill in the entry in the procedure linkage table.  */
@@ -11953,7 +12855,7 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd,
          asection * sgot;
          bfd_byte * ptr;
 
-         sgot = bfd_get_section_by_name (dynobj, ".got.plt");
+         sgot = htab->root.sgotplt;
          BFD_ASSERT (sgot != NULL);
 
          /* Get the offset into the .got.plt table of the entry that
@@ -11977,7 +12879,7 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd,
                         + splt->output_offset
                         + h->plt.offset);
 
-         ptr = htab->splt->contents + h->plt.offset;
+         ptr = splt->contents + h->plt.offset;
          if (htab->vxworks_p && info->shared)
            {
              unsigned int i;
@@ -12104,7 +13006,7 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd,
     }
 
   if (h->got.offset != (bfd_vma) -1
-      && (elf32_arm_hash_entry (h)->tls_type & GOT_TLS_GD) == 0
+      && (! GOT_TLS_GD_ANY_P (elf32_arm_hash_entry (h)->tls_type)) 
       && (elf32_arm_hash_entry (h)->tls_type & GOT_TLS_IE) == 0)
     {
       asection * sgot;
@@ -12115,8 +13017,8 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd,
 
       /* This symbol has an entry in the global offset table.  Set it
         up.  */
-      sgot = bfd_get_section_by_name (dynobj, ".got");
-      srel = bfd_get_section_by_name (dynobj, RELOC_SECTION (htab, ".got"));
+      sgot = htab->root.sgot;
+      srel = htab->root.srelgot;
       BFD_ASSERT (sgot != NULL && srel != NULL);
 
       offset = (h->got.offset & ~(bfd_vma) 1);
@@ -12163,8 +13065,7 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd,
                  && (h->root.type == bfd_link_hash_defined
                      || h->root.type == bfd_link_hash_defweak));
 
-      s = bfd_get_section_by_name (h->root.u.def.section->owner,
-                                  RELOC_SECTION (htab, ".bss"));
+      s = htab->srelbss;
       BFD_ASSERT (s != NULL);
 
       rel.r_addend = 0;
@@ -12186,6 +13087,24 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd,
   return TRUE;
 }
 
+static void
+arm_put_trampoline (struct elf32_arm_link_hash_table *htab, bfd *output_bfd,
+                   void *contents,
+                   const unsigned long *template, unsigned count)
+{
+  unsigned ix;
+  
+  for (ix = 0; ix != count; ix++)
+    {
+      unsigned long insn = template[ix];
+
+      /* Emit mov pc,rx if bx is not permitted.  */
+      if (htab->fix_v4bx == 1 && (insn & 0x0ffffff0) == 0x012fff10)
+       insn = (insn & 0xf000000f) | 0x01a0f000;
+      put_arm_insn (htab, output_bfd, insn, (char *)contents + ix*4);
+    }
+}
+
 /* Finish up the dynamic sections.  */
 
 static bfd_boolean
@@ -12194,21 +13113,24 @@ elf32_arm_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info * info
   bfd * dynobj;
   asection * sgot;
   asection * sdyn;
+  struct elf32_arm_link_hash_table *htab;
+
+  htab = elf32_arm_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
 
   dynobj = elf_hash_table (info)->dynobj;
 
-  sgot = bfd_get_section_by_name (dynobj, ".got.plt");
-  BFD_ASSERT (elf32_arm_hash_table (info)->symbian_p || sgot != NULL);
+  sgot = htab->root.sgotplt;
+  BFD_ASSERT (htab->symbian_p || sgot != NULL);
   sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
 
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       asection *splt;
       Elf32_External_Dyn *dyncon, *dynconend;
-      struct elf32_arm_link_hash_table *htab;
 
-      htab = elf32_arm_hash_table (info);
-      splt = bfd_get_section_by_name (dynobj, ".plt");
+      splt = htab->root.splt;
       BFD_ASSERT (splt != NULL && sdyn != NULL);
 
       dyncon = (Elf32_External_Dyn *) sdyn->contents;
@@ -12275,8 +13197,7 @@ elf32_arm_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info * info
              break;
 
            case DT_PLTRELSZ:
-             s = bfd_get_section_by_name (output_bfd,
-                                          RELOC_SECTION (htab, ".plt"));
+             s = htab->root.srelplt;
              BFD_ASSERT (s != NULL);
              dyn.d_un.d_val = s->size;
              bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
@@ -12295,8 +13216,7 @@ elf32_arm_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info * info
                     the linker script arranges for .rel(a).plt to follow all
                     other relocation sections, we don't have to worry
                     about changing the DT_REL entry.  */
-                 s = bfd_get_section_by_name (output_bfd,
-                                              RELOC_SECTION (htab, ".plt"));
+                 s = htab->root.srelplt;
                  if (s != NULL)
                    dyn.d_un.d_val -= s->size;
                  bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
@@ -12338,6 +13258,20 @@ elf32_arm_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info * info
                }
              break;
 
+           case DT_TLSDESC_PLT:
+              s = htab->root.splt;
+             dyn.d_un.d_ptr = (s->output_section->vma + s->output_offset
+                               + htab->dt_tlsdesc_plt);
+             bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
+             break;
+
+           case DT_TLSDESC_GOT:
+              s = htab->root.sgot;
+             dyn.d_un.d_ptr = (s->output_section->vma + s->output_offset
+                               + htab->dt_tlsdesc_got);
+             bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
+             break;
+
              /* Set the bottom bit of DT_INIT/FINI if the
                 corresponding function is Thumb.  */
            case DT_INIT:
@@ -12366,7 +13300,7 @@ elf32_arm_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info * info
        }
 
       /* Fill in the first entry in the procedure linkage table.  */
-      if (splt->size > 0 && elf32_arm_hash_table (info)->plt_header_size)
+      if (splt->size > 0 && htab->plt_header_size)
        {
          const bfd_vma *plt0_entry;
          bfd_vma got_address, plt_address, got_displacement;
@@ -12427,14 +13361,49 @@ elf32_arm_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info * info
       if (splt->output_section->owner == output_bfd)
        elf_section_data (splt->output_section)->this_hdr.sh_entsize = 4;
 
-      if (htab->vxworks_p && !info->shared && htab->splt->size > 0)
+      if (htab->dt_tlsdesc_plt)
+       {
+         bfd_vma got_address
+           = sgot->output_section->vma + sgot->output_offset;
+         bfd_vma gotplt_address = (htab->root.sgot->output_section->vma
+                                   + htab->root.sgot->output_offset);
+         bfd_vma plt_address
+           = splt->output_section->vma + splt->output_offset;
+
+         arm_put_trampoline (htab, output_bfd, 
+                             splt->contents + htab->dt_tlsdesc_plt,
+                             dl_tlsdesc_lazy_trampoline, 6);
+
+         bfd_put_32 (output_bfd,
+                     gotplt_address + htab->dt_tlsdesc_got
+                     - (plt_address + htab->dt_tlsdesc_plt)
+                     - dl_tlsdesc_lazy_trampoline[6],
+                     splt->contents + htab->dt_tlsdesc_plt + 24);
+         bfd_put_32 (output_bfd,
+                     got_address - (plt_address + htab->dt_tlsdesc_plt)
+                     - dl_tlsdesc_lazy_trampoline[7],
+                     splt->contents + htab->dt_tlsdesc_plt + 24 + 4);
+       }
+
+      if (htab->tls_trampoline)
+       {
+         arm_put_trampoline (htab, output_bfd, 
+                             splt->contents + htab->tls_trampoline,
+                             tls_trampoline, 3);
+#ifdef FOUR_WORD_PLT
+         bfd_put_32 (output_bfd, 0x00000000,
+                     splt->contents + htab->tls_trampoline + 12);
+#endif 
+       }
+
+      if (htab->vxworks_p && !info->shared && htab->root.splt->size > 0)
        {
          /* Correct the .rel(a).plt.unloaded relocations.  They will have
             incorrect symbol indexes.  */
          int num_plts;
          unsigned char *p;
 
-         num_plts = ((htab->splt->size - htab->plt_header_size)
+         num_plts = ((htab->root.splt->size - htab->plt_header_size)
                      / htab->plt_entry_size);
          p = htab->srelplt2->contents + RELOC_SIZE (htab);
 
@@ -12493,7 +13462,7 @@ elf32_arm_post_process_headers (bfd * abfd, struct bfd_link_info * link_info ATT
   if (link_info)
     {
       globals = elf32_arm_hash_table (link_info);
-      if (globals->byteswap_code)
+      if (globals != NULL && globals->byteswap_code)
        i_ehdrp->e_flags |= EF_ARM_BE8;
     }
 }
@@ -12591,108 +13560,15 @@ elf32_arm_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 _arm_elf_section_data structure has been allocated.  This
-   is because it is possible for functions like elf32_arm_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 ARM extended version of this structure - the
-   _arm_elf_section_data structure - has not been allocated.  */
-static section_list * sections_with_arm_elf_section_data = NULL;
-
-static void
-record_section_with_arm_elf_section_data (asection * sec)
-{
-  struct section_list * entry;
-
-  entry = (struct section_list *) bfd_malloc (sizeof (* entry));
-  if (entry == NULL)
-    return;
-  entry->sec = sec;
-  entry->next = sections_with_arm_elf_section_data;
-  entry->prev = NULL;
-  if (entry->next != NULL)
-    entry->next->prev = entry;
-  sections_with_arm_elf_section_data = entry;
-}
-
-static struct section_list *
-find_arm_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_arm_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_arm_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_arm_elf_section_data() we will not
-       be caching a pointer that is about to be freed.  */
-    last_entry = entry->prev;
-
-  return entry;
-}
-
 static _arm_elf_section_data *
 get_arm_elf_section_data (asection * sec)
 {
-  struct section_list * entry;
-
-  entry = find_arm_elf_section_entry (sec);
-
-  if (entry)
-    return elf32_arm_section_data (entry->sec);
+  if (sec && sec->owner && is_arm_elf (sec->owner))
+    return elf32_arm_section_data (sec);
   else
     return NULL;
 }
 
-static void
-unrecord_section_with_arm_elf_section_data (asection * sec)
-{
-  struct section_list * entry;
-
-  entry = find_arm_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_arm_elf_section_data)
-       sections_with_arm_elf_section_data = entry->next;
-      free (entry);
-    }
-}
-
-
 typedef struct
 {
   void *finfo;
@@ -12719,10 +13595,8 @@ elf32_arm_output_map_sym (output_arch_syminfo *osi,
                          bfd_vma offset)
 {
   static const char *names[3] = {"$a", "$t", "$d"};
-  struct elf32_arm_link_hash_table *htab;
   Elf_Internal_Sym sym;
 
-  htab = elf32_arm_hash_table (osi->info);
   sym.st_value = osi->sec->output_section->vma
                 + osi->sec->output_offset
                 + offset;
@@ -12730,6 +13604,7 @@ elf32_arm_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;
+  elf32_arm_section_map_add (osi->sec, names[type][1], offset);
   return osi->func (osi->finfo, names[type], &sym, osi->sec, NULL) == 1;
 }
 
@@ -12744,8 +13619,6 @@ elf32_arm_output_plt_map (struct elf_link_hash_entry *h, void *inf)
   struct elf32_arm_link_hash_entry *eh;
   bfd_vma addr;
 
-  htab = elf32_arm_hash_table (osi->info);
-
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
 
@@ -12758,6 +13631,10 @@ elf32_arm_output_plt_map (struct elf_link_hash_entry *h, void *inf)
   if (h->plt.offset == (bfd_vma) -1)
     return TRUE;
 
+  htab = elf32_arm_hash_table (osi->info);
+  if (htab == NULL)
+    return FALSE;
+
   eh = (struct elf32_arm_link_hash_entry *) h;
   addr = h->plt.offset;
   if (htab->symbian_p)
@@ -12817,10 +13694,8 @@ static bfd_boolean
 elf32_arm_output_stub_sym (output_arch_syminfo *osi, const char *name,
                           bfd_vma offset, bfd_vma size)
 {
-  struct elf32_arm_link_hash_table *htab;
   Elf_Internal_Sym sym;
 
-  htab = elf32_arm_hash_table (osi->info);
   sym.st_value = osi->sec->output_section->vma
                 + osi->sec->output_offset
                 + offset;
@@ -12836,8 +13711,6 @@ arm_map_one_stub (struct bfd_hash_entry * gen_entry,
                  void * in_arg)
 {
   struct elf32_arm_stub_hash_entry *stub_entry;
-  struct bfd_link_info *info;
-  struct elf32_arm_link_hash_table *htab;
   asection *stub_sec;
   bfd_vma addr;
   char *stub_name;
@@ -12852,9 +13725,6 @@ arm_map_one_stub (struct bfd_hash_entry * gen_entry,
   stub_entry = (struct elf32_arm_stub_hash_entry *) gen_entry;
   osi = (output_arch_syminfo *) in_arg;
 
-  info = osi->info;
-
-  htab = elf32_arm_hash_table (info);
   stub_sec = stub_entry->stub_sec;
 
   /* Ensure this stub is attached to the current section being
@@ -12938,7 +13808,9 @@ arm_map_one_stub (struct bfd_hash_entry * gen_entry,
   return TRUE;
 }
 
-/* Output mapping symbols for linker generated sections.  */
+/* Output mapping symbols for linker generated sections,
+   and for those data-only sections that do not have a
+   $d.  */
 
 static bfd_boolean
 elf32_arm_output_arch_local_syms (bfd *output_bfd,
@@ -12953,14 +13825,47 @@ elf32_arm_output_arch_local_syms (bfd *output_bfd,
   struct elf32_arm_link_hash_table *htab;
   bfd_vma offset;
   bfd_size_type size;
+  bfd *input_bfd;
 
   htab = elf32_arm_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
+
   check_use_blx (htab);
 
   osi.finfo = finfo;
   osi.info = info;
   osi.func = func;
 
+  /* Add a $d mapping symbol to data-only sections that
+     don't have any mapping symbol.  This may result in (harmless) redundant
+     mapping symbols.  */
+  for (input_bfd = info->input_bfds;
+       input_bfd != NULL;
+       input_bfd = input_bfd->link_next)
+    {
+      if ((input_bfd->flags & (BFD_LINKER_CREATED | HAS_SYMS)) == HAS_SYMS)
+       for (osi.sec = input_bfd->sections;
+            osi.sec != NULL;
+            osi.sec = osi.sec->next)
+         {
+           if (osi.sec->output_section != NULL
+               && ((osi.sec->output_section->flags & (SEC_ALLOC | SEC_CODE))
+                   != 0)
+               && (osi.sec->flags & (SEC_HAS_CONTENTS | SEC_LINKER_CREATED))
+                  == SEC_HAS_CONTENTS
+               && get_arm_elf_section_data (osi.sec) != NULL
+               && get_arm_elf_section_data (osi.sec)->mapcount == 0
+               && osi.sec->size > 0)
+             {
+               osi.sec_shndx = _bfd_elf_section_from_bfd_section
+                 (output_bfd, osi.sec->output_section);
+               if (osi.sec_shndx != (int)SHN_BAD)
+                 elf32_arm_output_map_sym (&osi, ARM_MAP_DATA, 0);
+             }
+         }
+    }
+
   /* ARM->Thumb glue.  */
   if (htab->arm_glue_size > 0)
     {
@@ -13036,12 +13941,12 @@ elf32_arm_output_arch_local_syms (bfd *output_bfd,
     }
 
   /* Finally, output mapping symbols for the PLT.  */
-  if (!htab->splt || htab->splt->size == 0)
+  if (!htab->root.splt || htab->root.splt->size == 0)
     return TRUE;
 
+  osi.sec = htab->root.splt;
   osi.sec_shndx = _bfd_elf_section_from_bfd_section (output_bfd,
-                                                    htab->splt->output_section);
-  osi.sec = htab->splt;
+                                                    osi.sec->output_section);
   /* Output mapping symbols for the plt header.  SymbianOS does not have a
      plt header.  */
   if (htab->vxworks_p)
@@ -13065,6 +13970,28 @@ elf32_arm_output_arch_local_syms (bfd *output_bfd,
 #endif
     }
 
+  if (htab->dt_tlsdesc_plt != 0)
+    {
+      /* Mapping symbols for the lazy tls trampoline.  */
+      if (!elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, htab->dt_tlsdesc_plt))
+       return FALSE;
+       
+      if (!elf32_arm_output_map_sym (&osi, ARM_MAP_DATA,
+                                    htab->dt_tlsdesc_plt + 24))
+       return FALSE;
+    }
+  if (htab->tls_trampoline != 0)
+    {
+      /* Mapping symbols for the tls trampoline.  */
+      if (!elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, htab->tls_trampoline))
+       return FALSE;
+#ifdef FOUR_WORD_PLT
+      if (!elf32_arm_output_map_sym (&osi, ARM_MAP_DATA,
+                                    htab->tls_trampoline + 12))
+       return FALSE;
+#endif 
+    }
+  
   elf_link_hash_traverse (&htab->root, elf32_arm_output_plt_map, (void *) &osi);
   return TRUE;
 }
@@ -13085,8 +14012,6 @@ elf32_arm_new_section_hook (bfd *abfd, asection *sec)
       sec->used_by_bfd = sdata;
     }
 
-  record_section_with_arm_elf_section_data (sec);
-
   return _bfd_elf_new_section_hook (abfd, sec);
 }
 
@@ -13166,13 +14091,13 @@ make_branch_to_a8_stub (struct bfd_hash_entry *gen_entry,
   bfd_vma veneered_insn_loc, veneer_entry_loc;
   bfd_signed_vma branch_offset;
   bfd *abfd;
-  unsigned int index;
+  unsigned int target;
 
   stub_entry = (struct elf32_arm_stub_hash_entry *) gen_entry;
   data = (struct a8_branch_to_stub_data *) in_arg;
 
   if (stub_entry->target_section != data->writing_section
-      || stub_entry->stub_type < arm_stub_a8_veneer_b_cond)
+      || stub_entry->stub_type < arm_stub_a8_veneer_lwm)
     return TRUE;
 
   contents = data->contents;
@@ -13191,7 +14116,7 @@ make_branch_to_a8_stub (struct bfd_hash_entry *gen_entry,
   branch_offset = veneer_entry_loc - veneered_insn_loc - 4;
 
   abfd = stub_entry->target_section->owner;
-  index = stub_entry->target_value;
+  target = stub_entry->target_value;
 
   /* We attempt to avoid this condition by setting stubs_always_after_branch
      in elf32_arm_size_stubs if we've enabled the Cortex-A8 erratum workaround.
@@ -13252,8 +14177,8 @@ make_branch_to_a8_stub (struct bfd_hash_entry *gen_entry,
       return FALSE;
     }
 
-  bfd_put_16 (abfd, (branch_insn >> 16) & 0xffff, &contents[index]);
-  bfd_put_16 (abfd, branch_insn & 0xffff, &contents[index + 2]);
+  bfd_put_16 (abfd, (branch_insn >> 16) & 0xffff, &contents[target]);
+  bfd_put_16 (abfd, branch_insn & 0xffff, &contents[target + 2]);
 
   return TRUE;
 }
@@ -13278,6 +14203,9 @@ elf32_arm_write_section (bfd *output_bfd,
   bfd_byte tmp;
   unsigned int i;
 
+  if (globals == NULL)
+    return FALSE;
+
   /* If this section has not been allocated an _arm_elf_section_data
      structure then we cannot record anything.  */
   arm_data = get_arm_elf_section_data (sec);
@@ -13295,7 +14223,7 @@ elf32_arm_write_section (bfd *output_bfd,
       for (errnode = arm_data->erratumlist; errnode != 0;
            errnode = errnode->next)
         {
-          bfd_vma index = errnode->vma - offset;
+          bfd_vma target = errnode->vma - offset;
 
           switch (errnode->type)
             {
@@ -13308,7 +14236,7 @@ elf32_arm_write_section (bfd *output_bfd,
                                   | 0x0a000000;
 
                /* The instruction is before the label.  */
-               index -= 4;
+               target -= 4;
 
                /* Above offset included in -4 below.  */
                branch_to_veneer = errnode->u.b.veneer->vma
@@ -13320,10 +14248,10 @@ elf32_arm_write_section (bfd *output_bfd,
                                           "range"), output_bfd);
 
                 insn |= (branch_to_veneer >> 2) & 0xffffff;
-                contents[endianflip ^ index] = insn & 0xff;
-                contents[endianflip ^ (index + 1)] = (insn >> 8) & 0xff;
-                contents[endianflip ^ (index + 2)] = (insn >> 16) & 0xff;
-                contents[endianflip ^ (index + 3)] = (insn >> 24) & 0xff;
+                contents[endianflip ^ target] = insn & 0xff;
+                contents[endianflip ^ (target + 1)] = (insn >> 8) & 0xff;
+                contents[endianflip ^ (target + 2)] = (insn >> 16) & 0xff;
+                contents[endianflip ^ (target + 3)] = (insn >> 24) & 0xff;
               }
               break;
 
@@ -13343,17 +14271,17 @@ elf32_arm_write_section (bfd *output_bfd,
 
                 /* Original instruction.  */
                 insn = errnode->u.v.branch->u.b.vfp_insn;
-                contents[endianflip ^ index] = insn & 0xff;
-                contents[endianflip ^ (index + 1)] = (insn >> 8) & 0xff;
-                contents[endianflip ^ (index + 2)] = (insn >> 16) & 0xff;
-                contents[endianflip ^ (index + 3)] = (insn >> 24) & 0xff;
+                contents[endianflip ^ target] = insn & 0xff;
+                contents[endianflip ^ (target + 1)] = (insn >> 8) & 0xff;
+                contents[endianflip ^ (target + 2)] = (insn >> 16) & 0xff;
+                contents[endianflip ^ (target + 3)] = (insn >> 24) & 0xff;
 
                 /* Branch back to insn after original insn.  */
                 insn = 0xea000000 | ((branch_from_veneer >> 2) & 0xffffff);
-                contents[endianflip ^ (index + 4)] = insn & 0xff;
-                contents[endianflip ^ (index + 5)] = (insn >> 8) & 0xff;
-                contents[endianflip ^ (index + 6)] = (insn >> 16) & 0xff;
-                contents[endianflip ^ (index + 7)] = (insn >> 24) & 0xff;
+                contents[endianflip ^ (target + 4)] = insn & 0xff;
+                contents[endianflip ^ (target + 5)] = (insn >> 8) & 0xff;
+                contents[endianflip ^ (target + 6)] = (insn >> 16) & 0xff;
+                contents[endianflip ^ (target + 7)] = (insn >> 24) & 0xff;
               }
               break;
 
@@ -13514,44 +14442,13 @@ elf32_arm_write_section (bfd *output_bfd,
     }
 
   free (map);
-  arm_data->mapcount = 0;
+  arm_data->mapcount = -1;
   arm_data->mapsize = 0;
   arm_data->map = NULL;
-  unrecord_section_with_arm_elf_section_data (sec);
 
   return FALSE;
 }
 
-static void
-unrecord_section_via_map_over_sections (bfd * abfd ATTRIBUTE_UNUSED,
-                                       asection * sec,
-                                       void * ignore ATTRIBUTE_UNUSED)
-{
-  unrecord_section_with_arm_elf_section_data (sec);
-}
-
-static bfd_boolean
-elf32_arm_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
-elf32_arm_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);
-}
-
 /* Display STT_ARM_TFUNC symbols as functions.  */
 
 static void
@@ -13715,6 +14612,7 @@ const struct elf_size_info elf32_arm_size_info =
 };
 
 #define ELF_ARCH                       bfd_arch_arm
+#define ELF_TARGET_ID                  ARM_ELF_DATA
 #define ELF_MACHINE_CODE               EM_ARM
 #ifdef __QNXTARGET__
 #define ELF_MAXPAGESIZE                        0x1000
@@ -13738,8 +14636,6 @@ const struct elf_size_info elf32_arm_size_info =
 #define bfd_elf32_find_inliner_info            elf32_arm_find_inliner_info
 #define bfd_elf32_new_section_hook             elf32_arm_new_section_hook
 #define bfd_elf32_bfd_is_target_special_symbol elf32_arm_is_target_special_symbol
-#define bfd_elf32_close_and_cleanup             elf32_arm_close_and_cleanup
-#define bfd_elf32_bfd_free_cached_info          elf32_arm_bfd_free_cached_info
 #define bfd_elf32_bfd_final_link               elf32_arm_final_link
 
 #define elf_backend_get_symbol_type             elf32_arm_get_symbol_type
@@ -13754,6 +14650,7 @@ const struct elf_size_info elf32_arm_size_info =
 #define elf_backend_finish_dynamic_symbol      elf32_arm_finish_dynamic_symbol
 #define elf_backend_finish_dynamic_sections    elf32_arm_finish_dynamic_sections
 #define elf_backend_size_dynamic_sections      elf32_arm_size_dynamic_sections
+#define elf_backend_always_size_sections       elf32_arm_always_size_sections
 #define elf_backend_init_index_section         _bfd_elf_init_2_index_sections
 #define elf_backend_post_process_headers       elf32_arm_post_process_headers
 #define elf_backend_reloc_type_class           elf32_arm_reloc_type_class
@@ -13791,6 +14688,7 @@ const struct elf_size_info elf32_arm_size_info =
 #undef  elf_backend_obj_attrs_section_type
 #define elf_backend_obj_attrs_section_type     SHT_ARM_ATTRIBUTES
 #define elf_backend_obj_attrs_order    elf32_arm_obj_attrs_order
+#define elf_backend_obj_attrs_handle_unknown elf32_arm_obj_attrs_handle_unknown
 
 #include "elf32-target.h"