* elf32-arm.c (find_stub_size_and_template): Avoid uninitialized
[binutils-gdb.git] / bfd / elf32-arm.c
index 7c82966722eb409dacb0dae3afe1f02e3b3112df..739a79d7f6934ec3a6df0a51fb37dbdfdcf2c59f 100644 (file)
@@ -1,6 +1,6 @@
 /* 32-bit ELF support for ARM
    Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
-   2008 Free Software Foundation, Inc.
+   2008, 2009, 2010  Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -61,8 +61,6 @@
 #define ARM_ELF_ABI_VERSION            0
 #define ARM_ELF_OS_ABI_VERSION         ELFOSABI_ARM
 
-static struct elf_backend_data elf32_arm_vxworks_bed;
-
 static bfd_boolean elf32_arm_write_section (bfd *output_bfd,
                                            struct bfd_link_info *link_info,
                                            asection *sec,
@@ -224,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 */
@@ -1723,6 +1721,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},
@@ -1830,7 +1829,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;
@@ -2026,11 +2025,15 @@ enum stub_insn_type
     DATA_TYPE
   };
 
-#define THUMB16_INSN(X)    {(X), THUMB16_TYPE, R_ARM_NONE, 0}
-#define THUMB32_INSN(X)    {(X), THUMB32_TYPE, R_ARM_NONE, 0}
-#define ARM_INSN(X)        {(X), ARM_TYPE, R_ARM_NONE, 0}
-#define ARM_REL_INSN(X, Z) {(X), ARM_TYPE, R_ARM_JUMP24, (Z)}
-#define DATA_WORD(X,Y,Z)   {(X), DATA_TYPE, (Y), (Z)}
+#define THUMB16_INSN(X)                {(X), THUMB16_TYPE, R_ARM_NONE, 0}
+/* A bit of a hack.  A Thumb conditional branch, in which the proper condition
+   is inserted in arm_build_one_stub().  */
+#define THUMB16_BCOND_INSN(X)  {(X), THUMB16_TYPE, R_ARM_NONE, 1}
+#define THUMB32_INSN(X)                {(X), THUMB32_TYPE, R_ARM_NONE, 0}
+#define THUMB32_B_INSN(X, Z)   {(X), THUMB32_TYPE, R_ARM_THM_JUMP24, (Z)}
+#define ARM_INSN(X)            {(X), ARM_TYPE, R_ARM_NONE, 0}
+#define ARM_REL_INSN(X, Z)     {(X), ARM_TYPE, R_ARM_JUMP24, (Z)}
+#define DATA_WORD(X,Y,Z)       {(X), DATA_TYPE, (Y), (Z)}
 
 typedef struct
 {
@@ -2103,7 +2106,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) */
   };
@@ -2114,7 +2117,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) */
@@ -2164,6 +2167,39 @@ 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) */
   };
 
+/* Cortex-A8 erratum-workaround stubs.  */
+
+/* Stub used for conditional branches (which may be beyond +/-1MB away, so we
+   can't use a conditional branch to reach this stub).  */
+
+static const insn_sequence elf32_arm_stub_a8_veneer_b_cond[] =
+  {
+    THUMB16_BCOND_INSN(0xd001),         /* b<cond>.n true.  */
+    THUMB32_B_INSN(0xf000b800, -4),     /* b.w insn_after_original_branch.  */
+    THUMB32_B_INSN(0xf000b800, -4)      /* true: b.w original_branch_dest.  */
+  };
+
+/* Stub used for b.w and bl.w instructions.  */
+
+static const insn_sequence elf32_arm_stub_a8_veneer_b[] =
+  {
+    THUMB32_B_INSN(0xf000b800, -4)     /* b.w original_branch_dest.  */
+  };
+
+static const insn_sequence elf32_arm_stub_a8_veneer_bl[] =
+  {
+    THUMB32_B_INSN(0xf000b800, -4)     /* b.w original_branch_dest.  */
+  };
+
+/* Stub used for Thumb-2 blx.w instructions.  We modified the original blx.w
+   instruction (which switches to ARM mode) to point to this stub.  Jump to the
+   real destination using an ARM-mode branch.  */
+
+static const insn_sequence elf32_arm_stub_a8_veneer_blx[] =
+  {
+    ARM_REL_INSN(0xea000000, -8)       /* b original_branch_dest.  */
+  };
+
 /* Section name for stubs is the associated section name plus this
    string.  */
 #define STUB_SUFFIX ".stub"
@@ -2181,18 +2217,24 @@ static const insn_sequence elf32_arm_stub_long_branch_v4t_thumb_thumb_pic[] =
   DEF_STUB(long_branch_v4t_thumb_thumb_pic) \
   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_thumb_only_pic) \
+  DEF_STUB(a8_veneer_b_cond) \
+  DEF_STUB(a8_veneer_b) \
+  DEF_STUB(a8_veneer_bl) \
+  DEF_STUB(a8_veneer_blx)
 
 #define DEF_STUB(x) arm_stub_##x,
 enum elf32_arm_stub_type {
   arm_stub_none,
   DEF_STUBS
+  /* Note the first a8_veneer type */
+  arm_stub_a8_veneer_lwm = arm_stub_a8_veneer_b_cond
 };
 #undef DEF_STUB
 
 typedef struct
 {
-  const insn_sequence* template;
+  const insn_sequence* template_sequence;
   int template_size;
 } stub_def;
 
@@ -2218,6 +2260,13 @@ struct elf32_arm_stub_hash_entry
   bfd_vma target_value;
   asection *target_section;
 
+  /* Offset to apply to relocation referencing target_value.  */
+  bfd_vma target_addend;
+
+  /* The instruction which caused this stub to be generated (only valid for
+     Cortex-A8 erratum workaround stubs at present).  */
+  unsigned long orig_insn;
+
   /* The stub type.  */
   enum elf32_arm_stub_type stub_type;
   /* Its encoding size in bytes.  */
@@ -2337,6 +2386,36 @@ _arm_elf_section_data;
 #define elf32_arm_section_data(sec) \
   ((_arm_elf_section_data *) elf_section_data (sec))
 
+/* A fix which might be required for Cortex-A8 Thumb-2 branch/TLB erratum.
+   These fixes are subject to a relaxation procedure (in elf32_arm_size_stubs),
+   so may be created multiple times: we use an array of these entries whilst
+   relaxing which we can refresh easily, then create stubs for each potentially
+   erratum-triggering instruction once we've settled on a solution.  */
+
+struct a8_erratum_fix {
+  bfd *input_bfd;
+  asection *section;
+  bfd_vma offset;
+  bfd_vma addend;
+  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
+   erratum.  */
+
+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;
+  bfd_boolean non_a8_stub;
+};
+
 /* The size of the thread control block.  */
 #define TCB_SIZE       8
 
@@ -2363,13 +2442,13 @@ struct elf_arm_obj_tdata
 #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
@@ -2439,12 +2518,24 @@ 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 *) \
    bfd_hash_lookup ((table), (string), (create), (copy)))
 
+/* Array to keep track of which stub sections have been created, and
+   information on stub grouping.  */
+struct map_stub
+{
+  /* This is the section to which stubs in the group will be
+     attached.  */
+  asection *link_sec;
+  /* The stub section.  */
+  asection *stub_sec;
+};
+
 /* ARM ELF linker hash table.  */
 struct elf32_arm_link_hash_table
 {
@@ -2468,6 +2559,12 @@ struct elf32_arm_link_hash_table
      veneers.  */
   bfd_size_type vfp11_erratum_glue_size;
 
+  /* A table of fix locations for Cortex-A8 Thumb-2 branch/TLB erratum.  This
+     holds Cortex-A8 erratum fix locations between elf32_arm_size_stubs() and
+     elf32_arm_write_section().  */
+  struct a8_erratum_fix *a8_erratum_fixes;
+  unsigned int num_a8_erratum_fixes;
+
   /* An arbitrary input BFD chosen to hold the glue sections.  */
   bfd * bfd_of_glue_owner;
 
@@ -2486,6 +2583,9 @@ struct elf32_arm_link_hash_table
      2 = Generate v4 interworing stubs.  */
   int fix_v4bx;
 
+  /* Whether we should fix the Cortex-A8 Thumb-2 branch/TLB erratum.  */
+  int fix_cortex_a8;
+
   /* Nonzero if the ARM/Thumb BLX instructions are available for use.  */
   int use_blx;
 
@@ -2533,8 +2633,8 @@ struct elf32_arm_link_hash_table
     bfd_vma offset;
   } tls_ldm_got;
 
-  /* Small local sym to section mapping cache.  */
-  struct sym_sec_cache sym_sec;
+  /* Small local sym cache.  */
+  struct sym_cache sym_cache;
 
   /* For convenience in allocate_dynrelocs.  */
   bfd * obfd;
@@ -2551,14 +2651,10 @@ struct elf32_arm_link_hash_table
 
   /* Array to keep track of which stub sections have been created, and
      information on stub grouping.  */
-  struct map_stub
-  {
-    /* This is the section to which stubs in the group will be
-       attached.  */
-    asection *link_sec;
-    /* The stub section.  */
-    asection *stub_sec;
-  } *stub_group;
+  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;
@@ -2579,7 +2675,8 @@ elf32_arm_link_hash_newfunc (struct bfd_hash_entry * entry,
   /* Allocate the structure if it has not already been allocated by a
      subclass.  */
   if (ret == NULL)
-    ret = bfd_hash_allocate (table, sizeof (struct elf32_arm_link_hash_entry));
+    ret = (struct elf32_arm_link_hash_entry *)
+        bfd_hash_allocate (table, sizeof (struct elf32_arm_link_hash_entry));
   if (ret == NULL)
     return (struct bfd_hash_entry *) ret;
 
@@ -2613,8 +2710,8 @@ stub_hash_newfunc (struct bfd_hash_entry *entry,
      subclass.  */
   if (entry == NULL)
     {
-      entry = bfd_hash_allocate (table,
-                                sizeof (struct elf32_arm_stub_hash_entry));
+      entry = (struct bfd_hash_entry *)
+          bfd_hash_allocate (table, sizeof (struct elf32_arm_stub_hash_entry));
       if (entry == NULL)
        return entry;
     }
@@ -2631,12 +2728,15 @@ stub_hash_newfunc (struct bfd_hash_entry *entry,
       eh->stub_offset = 0;
       eh->target_value = 0;
       eh->target_section = NULL;
+      eh->target_addend = 0;
+      eh->orig_insn = 0;
       eh->stub_type = arm_stub_none;
       eh->stub_size = 0;
       eh->stub_template = NULL;
       eh->stub_template_size = 0;
       eh->h = NULL;
       eh->id_sec = NULL;
+      eh->output_name = NULL;
     }
 
   return entry;
@@ -2651,6 +2751,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;
@@ -2663,15 +2766,9 @@ create_got_section (bfd *dynobj, struct bfd_link_info *info)
   if (!htab->sgot || !htab->sgotplt)
     abort ();
 
-  htab->srelgot = bfd_make_section_with_flags (dynobj,
-                                              RELOC_SECTION (htab, ".got"),
-                                              (SEC_ALLOC | SEC_LOAD
-                                               | SEC_HAS_CONTENTS
-                                               | SEC_IN_MEMORY
-                                               | SEC_LINKER_CREATED
-                                               | SEC_READONLY));
-  if (htab->srelgot == NULL
-      || ! bfd_set_section_alignment (dynobj, htab->srelgot, 2))
+  htab->srelgot = bfd_get_section_by_name (dynobj,
+                                          RELOC_SECTION (htab, ".got"));
+  if (htab->srelgot == NULL)
     return FALSE;
   return TRUE;
 }
@@ -2686,6 +2783,9 @@ 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 == NULL)
+    return FALSE;
+
   if (!htab->sgot && !create_got_section (dynobj, info))
     return FALSE;
 
@@ -2798,13 +2898,14 @@ elf32_arm_link_hash_table_create (bfd *abfd)
   struct elf32_arm_link_hash_table *ret;
   bfd_size_type amt = sizeof (struct elf32_arm_link_hash_table);
 
-  ret = bfd_malloc (amt);
+  ret = (struct elf32_arm_link_hash_table *) bfd_malloc (amt);
   if (ret == NULL)
     return NULL;
 
   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;
@@ -2825,6 +2926,7 @@ elf32_arm_link_hash_table_create (bfd *abfd)
   ret->vfp11_fix = BFD_ARM_VFP11_FIX_NONE;
   ret->vfp11_erratum_glue_size = 0;
   ret->num_vfp11_fixes = 0;
+  ret->fix_cortex_a8 = 0;
   ret->bfd_of_glue_owner = NULL;
   ret->byteswap_code = 0;
   ret->target1_is_rel = 0;
@@ -2841,13 +2943,14 @@ elf32_arm_link_hash_table_create (bfd *abfd)
   ret->vxworks_p = 0;
   ret->symbian_p = 0;
   ret->use_rel = 1;
-  ret->sym_sec.abfd = NULL;
+  ret->sym_cache.abfd = NULL;
   ret->obfd = abfd;
   ret->tls_ldm_got.refcount = 0;
   ret->stub_bfd = NULL;
   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;
@@ -2883,7 +2986,10 @@ using_thumb_only (struct elf32_arm_link_hash_table *globals)
                                       Tag_CPU_arch);
   int profile;
 
-  if (arch != TAG_CPU_ARCH_V7)
+  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;
 
   profile = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
@@ -2902,6 +3008,28 @@ using_thumb2 (struct elf32_arm_link_hash_table *globals)
   return arch == TAG_CPU_ARCH_V6T2 || arch >= TAG_CPU_ARCH_V7;
 }
 
+/* Determine what kind of NOPs are available.  */
+
+static bfd_boolean
+arch_has_arm_nop (struct elf32_arm_link_hash_table *globals)
+{
+  const int arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
+                                            Tag_CPU_arch);
+  return arch == TAG_CPU_ARCH_V6T2
+        || arch == TAG_CPU_ARCH_V6K
+        || arch == TAG_CPU_ARCH_V7
+        || arch == TAG_CPU_ARCH_V7E_M;
+}
+
+static bfd_boolean
+arch_has_thumb2_nop (struct elf32_arm_link_hash_table *globals)
+{
+  const int arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC,
+                                            Tag_CPU_arch);
+  return (arch == TAG_CPU_ARCH_V6T2 || arch == TAG_CPU_ARCH_V7
+         || arch == TAG_CPU_ARCH_V7E_M);
+}
+
 static bfd_boolean
 arm_stub_is_thumb (enum elf32_arm_stub_type stub_type)
 {
@@ -2928,7 +3056,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,
@@ -2943,6 +3071,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.  */
@@ -2950,6 +3079,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);
 
@@ -2960,14 +3091,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->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
@@ -2976,8 +3108,15 @@ 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->splt->output_section->vma
+                    + globals->splt->output_offset
+                    + hash->root.plt.offset);
+      st_type = STT_FUNC;
     }
 
+  branch_offset = (bfd_signed_vma)(destination - location);
+
   if (r_type == R_ARM_THM_CALL || r_type == R_ARM_THM_JUMP24)
     {
       /* Handle cases where:
@@ -3071,7 +3210,9 @@ 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)
     {
       if (st_type == STT_ARM_TFUNC)
        {
@@ -3126,6 +3267,10 @@ arm_type_of_stub (struct bfd_link_info *info,
        }
     }
 
+  /* If a stub is needed, record the actual destination type.  */
+  if (stub_type != arm_stub_none)
+    *actual_st_type = st_type;
+
   return stub_type;
 }
 
@@ -3135,31 +3280,34 @@ 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;
-      stub_name = bfd_malloc (len);
+      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;
-      stub_name = bfd_malloc (len);
+      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);
+                (int) rel->r_addend & 0xffffffff,
+                (int) stub_type);
     }
 
   return stub_name;
@@ -3173,7 +3321,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;
@@ -3191,7 +3340,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;
     }
@@ -3199,7 +3349,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;
 
@@ -3214,17 +3364,16 @@ elf32_arm_get_stub_entry (const asection *input_section,
   return stub_entry;
 }
 
-/* Add a new stub entry to the stub hash.  Not all fields of the new
-   stub entry are initialised.  */
+/* Find or create a stub section.  Returns a pointer to the stub section, and
+   the section to which the stub section will be attached (in *LINK_SEC_P). 
+   LINK_SEC_P may be NULL.  */
 
-static struct elf32_arm_stub_hash_entry *
-elf32_arm_add_stub (const char *stub_name,
-                   asection *section,
-                   struct elf32_arm_link_hash_table *htab)
+static asection *
+elf32_arm_create_or_find_stub_sec (asection **link_sec_p, asection *section,
+                                  struct elf32_arm_link_hash_table *htab)
 {
   asection *link_sec;
   asection *stub_sec;
-  struct elf32_arm_stub_hash_entry *stub_entry;
 
   link_sec = htab->stub_group[section->id].link_sec;
   stub_sec = htab->stub_group[section->id].stub_sec;
@@ -3239,7 +3388,7 @@ elf32_arm_add_stub (const char *stub_name,
 
          namelen = strlen (link_sec->name);
          len = namelen + sizeof (STUB_SUFFIX);
-         s_name = bfd_alloc (htab->stub_bfd, len);
+         s_name = (char *) bfd_alloc (htab->stub_bfd, len);
          if (s_name == NULL)
            return NULL;
 
@@ -3252,6 +3401,28 @@ elf32_arm_add_stub (const char *stub_name,
        }
       htab->stub_group[section->id].stub_sec = stub_sec;
     }
+  
+  if (link_sec_p)
+    *link_sec_p = link_sec;
+  
+  return stub_sec;
+}
+
+/* Add a new stub entry to the stub hash.  Not all fields of the new
+   stub entry are initialised.  */
+
+static struct elf32_arm_stub_hash_entry *
+elf32_arm_add_stub (const char *stub_name,
+                   asection *section,
+                   struct elf32_arm_link_hash_table *htab)
+{
+  asection *link_sec;
+  asection *stub_sec;
+  struct elf32_arm_stub_hash_entry *stub_entry;
+
+  stub_sec = elf32_arm_create_or_find_stub_sec (&link_sec, section, htab);
+  if (stub_sec == NULL)
+    return NULL;
 
   /* Enter this entry into the linker stub hash table.  */
   stub_entry = arm_stub_hash_lookup (&htab->stub_hash_table, stub_name,
@@ -3297,79 +3468,142 @@ put_thumb_insn (struct elf32_arm_link_hash_table * htab,
     bfd_putb16 (val, ptr);
 }
 
+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_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;
+  const insn_sequence *template_sequence;
   int i;
-  struct elf32_arm_link_hash_table * globals;
-  int stub_reloc_idx = -1;
-  int stub_reloc_offset = 0;
+  int stub_reloc_idx[MAXRELOCS] = {-1, -1};
+  int stub_reloc_offset[MAXRELOCS] = {0, 0};
+  int nrelocs = 0;
 
   /* Massage our args to the form they really have.  */
   stub_entry = (struct elf32_arm_stub_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 ((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
               + stub_entry->target_section->output_section->vma);
 
-  template = stub_entry->stub_template;
+  template_sequence = stub_entry->stub_template;
   template_size = stub_entry->stub_template_size;
 
   size = 0;
   for (i = 0; i < template_size; i++)
     {
-      switch (template[i].type)
+      switch (template_sequence[i].type)
        {
        case THUMB16_TYPE:
-         put_thumb_insn (globals, stub_bfd, template[i].data, loc + size);
-         size += 2;
+         {
+           bfd_vma data = (bfd_vma) template_sequence[i].data;
+           if (template_sequence[i].reloc_addend != 0)
+             {
+                /* We've borrowed the reloc_addend field to mean we should
+                   insert a condition code into this (Thumb-1 branch)
+                   instruction.  See THUMB16_BCOND_INSN.  */
+                BFD_ASSERT ((data & 0xff00) == 0xd000);
+                data |= ((stub_entry->orig_insn >> 22) & 0xf) << 8;
+             }
+           bfd_put_16 (stub_bfd, data, loc + size);
+           size += 2;
+         }
          break;
 
+       case THUMB32_TYPE:
+         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;
+              stub_reloc_offset[nrelocs++] = size;
+            }
+          size += 4;
+          break;
+
        case ARM_TYPE:
-         put_arm_insn (globals, stub_bfd, template[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[i].r_type == R_ARM_JUMP24)
+         if (template_sequence[i].r_type == R_ARM_JUMP24)
            {
-             stub_reloc_idx = i;
-             stub_reloc_offset = size;
+             stub_reloc_idx[nrelocs] = i;
+             stub_reloc_offset[nrelocs++] = size;
            }
          size += 4;
          break;
 
        case DATA_TYPE:
-         bfd_put_32 (stub_bfd, template[i].data, loc + size);
-         stub_reloc_idx = i;
-         stub_reloc_offset = size;
+         bfd_put_32 (stub_bfd, template_sequence[i].data, loc + size);
+         stub_reloc_idx[nrelocs] = i;
+         stub_reloc_offset[nrelocs++] = size;
          size += 4;
          break;
 
@@ -3389,66 +3623,140 @@ arm_build_one_stub (struct bfd_hash_entry *gen_entry,
   if (stub_entry->st_type == STT_ARM_TFUNC)
     sym_value |= 1;
 
-  /* Assume there is one and only one entry to relocate in each stub.  */
-  BFD_ASSERT (stub_reloc_idx != -1);
+  /* Assume there is at least one and at most MAXRELOCS entries to relocate
+     in each stub.  */
+  BFD_ASSERT (nrelocs != 0 && nrelocs <= MAXRELOCS);
 
-  _bfd_final_link_relocate (elf32_arm_howto_from_type (template[stub_reloc_idx].r_type),
-                           stub_bfd, stub_sec, stub_sec->contents,
-                           stub_entry->stub_offset + stub_reloc_offset,
-                           sym_value, template[stub_reloc_idx].reloc_addend);
+  for (i = 0; i < nrelocs; i++)
+    if (template_sequence[stub_reloc_idx[i]].r_type == R_ARM_THM_JUMP24
+       || template_sequence[stub_reloc_idx[i]].r_type == R_ARM_THM_JUMP19
+       || template_sequence[stub_reloc_idx[i]].r_type == R_ARM_THM_CALL
+       || template_sequence[stub_reloc_idx[i]].r_type == R_ARM_THM_XPC22)
+      {
+       Elf_Internal_Rela rel;
+       bfd_boolean unresolved_reloc;
+       char *error_message;
+       int sym_flags
+         = (template_sequence[stub_reloc_idx[i]].r_type != R_ARM_THM_XPC22)
+           ? STT_ARM_TFUNC : 0;
+       bfd_vma points_to = sym_value + stub_entry->target_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 = template_sequence[stub_reloc_idx[i]].reloc_addend;
+
+       if (stub_entry->stub_type == arm_stub_a8_veneer_b_cond && i == 0)
+         /* The first relocation in the elf32_arm_stub_a8_veneer_b_cond[]
+            template should refer back to the instruction after the original
+            branch.  */
+         points_to = sym_value;
+
+       /* There may be unintended consequences if this is not true.  */
+       BFD_ASSERT (stub_entry->h == NULL);
+
+       /* Note: _bfd_final_link_relocate doesn't handle these relocations
+          properly.  We should probably use this function unconditionally,
+          rather than only for certain relocations listed in the enclosing
+          conditional, for the sake of consistency.  */
+       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, "", sym_flags,
+         (struct elf_link_hash_entry *) stub_entry->h, &unresolved_reloc,
+         &error_message);
+      }
+    else
+      {
+       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;
+#undef MAXRELOCS
 }
 
-/* As above, but don't actually build the stub.  Just bump offset so
-   we know stub section sizes.  */
+/* Calculate the template, template size and instruction size for a stub.
+   Return value is the instruction size.  */
 
-static bfd_boolean
-arm_size_one_stub (struct bfd_hash_entry *gen_entry,
-                  void * in_arg)
+static unsigned int
+find_stub_size_and_template (enum elf32_arm_stub_type stub_type,
+                            const insn_sequence **stub_template,
+                            int *stub_template_size)
 {
-  struct elf32_arm_stub_hash_entry *stub_entry;
-  struct elf32_arm_link_hash_table *htab;
-  const insn_sequence *template;
-  int template_size;
-  int size;
-  int i;
+  const insn_sequence *template_sequence = NULL;
+  int template_size = 0, i;
+  unsigned int 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));
+  template_sequence = stub_definitions[stub_type].template_sequence;
+  if (stub_template)
+    *stub_template = template_sequence;
 
-  template = stub_definitions[stub_entry->stub_type].template;
-  template_size = stub_definitions[stub_entry->stub_type].template_size;
+  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++)
     {
-      switch (template[i].type)
+      switch (template_sequence[i].type)
        {
        case THUMB16_TYPE:
          size += 2;
          break;
 
        case ARM_TYPE:
-         size += 4;
-         break;
-
+       case THUMB32_TYPE:
        case DATA_TYPE:
          size += 4;
          break;
 
        default:
          BFD_FAIL ();
-         return FALSE;
+         return 0;
        }
     }
 
+  return size;
+}
+
+/* As above, but don't actually build the stub.  Just bump offset so
+   we know stub section sizes.  */
+
+static bfd_boolean
+arm_size_one_stub (struct bfd_hash_entry *gen_entry,
+                  void *in_arg ATTRIBUTE_UNUSED)
+{
+  struct elf32_arm_stub_hash_entry *stub_entry;
+  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;
+
+  BFD_ASSERT((stub_entry->stub_type > arm_stub_none)
+            && stub_entry->stub_type < ARRAY_SIZE(stub_definitions));
+
+  size = find_stub_size_and_template (stub_entry->stub_type, &template_sequence,
+                                     &template_size);
+
   stub_entry->stub_size = size;
-  stub_entry->stub_template = template;
+  stub_entry->stub_template = template_sequence;
   stub_entry->stub_template_size = template_size;
 
   size = (size + 7) & ~7;
@@ -3475,6 +3783,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;
 
@@ -3495,9 +3805,10 @@ elf32_arm_setup_section_lists (bfd *output_bfd,
   htab->bfd_count = bfd_count;
 
   amt = sizeof (struct map_stub) * (top_id + 1);
-  htab->stub_group = bfd_zmalloc (amt);
+  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
@@ -3512,7 +3823,7 @@ elf32_arm_setup_section_lists (bfd *output_bfd,
 
   htab->top_index = top_index;
   amt = sizeof (asection *) * (top_index + 1);
-  input_list = bfd_malloc (amt);
+  input_list = (asection **) bfd_malloc (amt);
   htab->input_list = input_list;
   if (input_list == NULL)
     return -1;
@@ -3546,11 +3857,14 @@ 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;
 
-      if (*list != bfd_abs_section_ptr)
+      if (*list != bfd_abs_section_ptr && (isec->flags & SEC_CODE) != 0)
        {
          /* Steal the link_sec pointer for our list.  */
 #define PREV_SEC(sec) (htab->stub_group[(sec)->id].link_sec)
@@ -3663,88 +3977,447 @@ group_sections (struct elf32_arm_link_hash_table *htab,
 #undef NEXT_SEC
 }
 
-/* Determine and set the size of the stub section for a final link.
-
-   The basic idea here is to examine all the relocations looking for
-   PC-relative calls to a target that is unreachable with a "bl"
-   instruction.  */
+/* Comparison function for sorting/searching relocations relating to Cortex-A8
+   erratum fix.  */
 
-bfd_boolean
-elf32_arm_size_stubs (bfd *output_bfd,
-                     bfd *stub_bfd,
-                     struct bfd_link_info *info,
-                     bfd_signed_vma group_size,
-                     asection * (*add_stub_section) (const char *, asection *),
-                     void (*layout_sections_again) (void))
+static int
+a8_reloc_compare (const void *a, const void *b)
 {
-  bfd_size_type stub_group_size;
-  bfd_boolean stubs_always_after_branch;
-  bfd_boolean stub_changed = 0;
-  struct elf32_arm_link_hash_table *htab = elf32_arm_hash_table (info);
-
-  /* Propagate mach to stub bfd, because it may not have been
-     finalized when we created stub_bfd.  */
-  bfd_set_arch_mach (stub_bfd, bfd_get_arch (output_bfd),
-                    bfd_get_mach (output_bfd));
+  const struct a8_erratum_reloc *ra = (const struct a8_erratum_reloc *) a;
+  const struct a8_erratum_reloc *rb = (const struct a8_erratum_reloc *) b;
 
-  /* Stash our params away.  */
-  htab->stub_bfd = stub_bfd;
-  htab->add_stub_section = add_stub_section;
-  htab->layout_sections_again = layout_sections_again;
-  stubs_always_after_branch = group_size < 0;
-  if (group_size < 0)
-    stub_group_size = -group_size;
+  if (ra->from < rb->from)
+    return -1;
+  else if (ra->from > rb->from)
+    return 1;
   else
-    stub_group_size = group_size;
+    return 0;
+}
 
-  if (stub_group_size == 1)
-    {
-      /* Default values.  */
-      /* Thumb branch range is +-4MB has to be used as the default
-        maximum size (a given section can contain both ARM and Thumb
-        code, so the worst case has to be taken into account).
+static struct elf_link_hash_entry *find_thumb_glue (struct bfd_link_info *,
+                                                   const char *, char **);
 
-        This value is 24K less than that, which allows for 2025
-        12-byte stubs.  If we exceed that, then we will fail to link.
-        The user will have to relink with an explicit group size
-        option.  */
-      stub_group_size = 4170000;
-    }
+/* Helper function to scan code for sequences which might trigger the Cortex-A8
+   branch/TLB erratum.  Fill in the table described by A8_FIXES_P,
+   NUM_A8_FIXES_P, A8_FIX_TABLE_SIZE_P.  Returns true if an error occurs, false
+   otherwise.  */
 
-  group_sections (htab, stub_group_size, stubs_always_after_branch);
+static bfd_boolean
+cortex_a8_erratum_scan (bfd *input_bfd,
+                       struct bfd_link_info *info,
+                       struct a8_erratum_fix **a8_fixes_p,
+                       unsigned int *num_a8_fixes_p,
+                       unsigned int *a8_fix_table_size_p,
+                       struct a8_erratum_reloc *a8_relocs,
+                       unsigned int num_a8_relocs,
+                       unsigned prev_num_a8_fixes,
+                       bfd_boolean *stub_changed_p)
+{
+  asection *section;
+  struct elf32_arm_link_hash_table *htab = elf32_arm_hash_table (info);
+  struct a8_erratum_fix *a8_fixes = *a8_fixes_p;
+  unsigned int num_a8_fixes = *num_a8_fixes_p;
+  unsigned int a8_fix_table_size = *a8_fix_table_size_p;
 
-  while (1)
-    {
-      bfd *input_bfd;
-      unsigned int bfd_indx;
-      asection *stub_sec;
+  if (htab == NULL)
+    return FALSE;
 
-      for (input_bfd = info->input_bfds, bfd_indx = 0;
-          input_bfd != NULL;
-          input_bfd = input_bfd->link_next, bfd_indx++)
-       {
-         Elf_Internal_Shdr *symtab_hdr;
-         asection *section;
-         Elf_Internal_Sym *local_syms = NULL;
+  for (section = input_bfd->sections;
+       section != NULL;
+       section = section->next)
+    {
+      bfd_byte *contents = NULL;
+      struct _arm_elf_section_data *sec_data;
+      unsigned int span;
+      bfd_vma base_vma;
+
+      if (elf_section_type (section) != SHT_PROGBITS
+          || (elf_section_flags (section) & SHF_EXECINSTR) == 0
+          || (section->flags & SEC_EXCLUDE) != 0
+          || (section->sec_info_type == ELF_INFO_TYPE_JUST_SYMS)
+          || (section->output_section == bfd_abs_section_ptr))
+        continue;
 
-         /* We'll need the symbol table in a second.  */
-         symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
-         if (symtab_hdr->sh_info == 0)
-           continue;
+      base_vma = section->output_section->vma + section->output_offset;
 
-         /* Walk over each section attached to the input bfd.  */
-         for (section = input_bfd->sections;
-              section != NULL;
-              section = section->next)
-           {
-             Elf_Internal_Rela *internal_relocs, *irelaend, *irela;
+      if (elf_section_data (section)->this_hdr.contents != NULL)
+        contents = elf_section_data (section)->this_hdr.contents;
+      else if (! bfd_malloc_and_get_section (input_bfd, section, &contents))
+        return TRUE;
 
-             /* If there aren't any relocs, then there's nothing more
-                to do.  */
-             if ((section->flags & SEC_RELOC) == 0
-                 || section->reloc_count == 0
-                 || (section->flags & SEC_CODE) == 0)
-               continue;
+      sec_data = elf32_arm_section_data (section);
+
+      for (span = 0; span < sec_data->mapcount; span++)
+        {
+          unsigned int span_start = sec_data->map[span].vma;
+          unsigned int span_end = (span == sec_data->mapcount - 1)
+            ? section->size : sec_data->map[span + 1].vma;
+          unsigned int i;
+          char span_type = sec_data->map[span].type;
+          bfd_boolean last_was_32bit = FALSE, last_was_branch = FALSE;
+
+          if (span_type != 't')
+            continue;
+
+          /* Span is entirely within a single 4KB region: skip scanning.  */
+          if (((base_vma + span_start) & ~0xfff)
+             == ((base_vma + span_end) & ~0xfff))
+            continue;
+
+          /* Scan for 32-bit Thumb-2 branches which span two 4K regions, where:
+
+               * The opcode is BLX.W, BL.W, B.W, Bcc.W
+               * The branch target is in the same 4KB region as the
+                 first half of the branch.
+               * The instruction before the branch is a 32-bit
+                 length non-branch instruction.  */
+          for (i = span_start; i < span_end;)
+            {
+              unsigned int insn = bfd_getl16 (&contents[i]);
+              bfd_boolean insn_32bit = FALSE, is_blx = FALSE, is_b = FALSE;
+             bfd_boolean is_bl = FALSE, is_bcc = FALSE, is_32bit_branch;
+
+              if ((insn & 0xe000) == 0xe000 && (insn & 0x1800) != 0x0000)
+                insn_32bit = TRUE;
+
+             if (insn_32bit)
+               {
+                  /* Load the rest of the insn (in manual-friendly order).  */
+                  insn = (insn << 16) | bfd_getl16 (&contents[i + 2]);
+
+                 /* Encoding T4: B<c>.W.  */
+                 is_b = (insn & 0xf800d000) == 0xf0009000;
+                 /* Encoding T1: BL<c>.W.  */
+                 is_bl = (insn & 0xf800d000) == 0xf000d000;
+                 /* Encoding T2: BLX<c>.W.  */
+                 is_blx = (insn & 0xf800d000) == 0xf000c000;
+                 /* Encoding T3: B<c>.W (not permitted in IT block).  */
+                 is_bcc = (insn & 0xf800d000) == 0xf0008000
+                          && (insn & 0x07f00000) != 0x03800000;
+               }
+
+             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 = 0;
+                  bfd_boolean force_target_arm = FALSE;
+                 bfd_boolean force_target_thumb = FALSE;
+                  bfd_vma target;
+                  enum elf32_arm_stub_type stub_type = arm_stub_none;
+                  struct a8_erratum_reloc key, *found;
+
+                  key.from = base_vma + i;
+                  found = (struct a8_erratum_reloc *)
+                      bsearch (&key, a8_relocs, num_a8_relocs,
+                               sizeof (struct a8_erratum_reloc),
+                               &a8_reloc_compare);
+
+                 if (found)
+                   {
+                     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.  */
+                     entry = find_thumb_glue (info, found->sym_name,
+                                              &error_message);
+
+                     if (entry)
+                       found->non_a8_stub = TRUE;
+
+                     /* Keep a simpler condition, for the sake of clarity.  */
+                     if (htab->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.  */
+
+                 if (found && found->non_a8_stub)
+                   /* We've already made a stub for this instruction, e.g.
+                      it's a long branch or a Thumb->ARM stub.  Assume that
+                      stub will suffice to work around the A8 erratum (see
+                      setting of always_after_branch above).  */
+                   ;
+                  else if (is_bcc)
+                    {
+                      offset = (insn & 0x7ff) << 1;
+                      offset |= (insn & 0x3f0000) >> 4;
+                      offset |= (insn & 0x2000) ? 0x40000 : 0;
+                      offset |= (insn & 0x800) ? 0x80000 : 0;
+                      offset |= (insn & 0x4000000) ? 0x100000 : 0;
+                      if (offset & 0x100000)
+                        offset |= ~ ((bfd_signed_vma) 0xfffff);
+                      stub_type = arm_stub_a8_veneer_b_cond;
+                    }
+                  else if (is_b || is_bl || is_blx)
+                    {
+                      int s = (insn & 0x4000000) != 0;
+                      int j1 = (insn & 0x2000) != 0;
+                      int j2 = (insn & 0x800) != 0;
+                      int i1 = !(j1 ^ s);
+                      int i2 = !(j2 ^ s);
+
+                      offset = (insn & 0x7ff) << 1;
+                      offset |= (insn & 0x3ff0000) >> 4;
+                      offset |= i2 << 22;
+                      offset |= i1 << 23;
+                      offset |= s << 24;
+                      if (offset & 0x1000000)
+                        offset |= ~ ((bfd_signed_vma) 0xffffff);
+
+                      if (is_blx)
+                        offset &= ~ ((bfd_signed_vma) 3);
+
+                      stub_type = is_blx ? arm_stub_a8_veneer_blx :
+                        is_bl ? arm_stub_a8_veneer_bl : arm_stub_a8_veneer_b;
+                    }
+
+                  if (stub_type != arm_stub_none)
+                    {
+                      bfd_vma pc_for_insn = base_vma + i + 4;
+
+                     /* The original instruction is a BL, but the target is
+                        an ARM instruction.  If we were not making a stub,
+                        the BL would have been converted to a BLX.  Use the
+                        BLX stub instead in that case.  */
+                     if (htab->use_blx && force_target_arm
+                         && stub_type == arm_stub_a8_veneer_bl)
+                       {
+                         stub_type = arm_stub_a8_veneer_blx;
+                         is_blx = TRUE;
+                         is_bl = FALSE;
+                       }
+                     /* Conversely, if the original instruction was
+                        BLX but the target is Thumb mode, use the BL
+                        stub.  */
+                     else if (force_target_thumb
+                              && stub_type == arm_stub_a8_veneer_blx)
+                       {
+                         stub_type = arm_stub_a8_veneer_bl;
+                         is_blx = FALSE;
+                         is_bl = TRUE;
+                       }
+
+                      if (is_blx)
+                        pc_for_insn &= ~ ((bfd_vma) 3);
+
+                      /* If we found a relocation, use the proper destination,
+                        not the offset in the (unrelocated) instruction.
+                        Note this is always done if we switched the stub type
+                        above.  */
+                      if (found)
+                        offset =
+                         (bfd_signed_vma) (found->destination - pc_for_insn);
+
+                      target = pc_for_insn + offset;
+
+                      /* The BLX stub is ARM-mode code.  Adjust the offset to
+                        take the different PC value (+8 instead of +4) into
+                        account.  */
+                      if (stub_type == arm_stub_a8_veneer_blx)
+                        offset += 4;
+
+                      if (((base_vma + i) & ~0xfff) == (target & ~0xfff))
+                        {
+                          char *stub_name = NULL;
+
+                          if (num_a8_fixes == a8_fix_table_size)
+                            {
+                              a8_fix_table_size *= 2;
+                              a8_fixes = (struct a8_erratum_fix *)
+                                  bfd_realloc (a8_fixes,
+                                               sizeof (struct a8_erratum_fix)
+                                               * a8_fix_table_size);
+                            }
+
+                         if (num_a8_fixes < prev_num_a8_fixes)
+                           {
+                             /* If we're doing a subsequent scan,
+                                check if we've found the same fix as
+                                before, and try and reuse the stub
+                                name.  */
+                             stub_name = a8_fixes[num_a8_fixes].stub_name;
+                             if ((a8_fixes[num_a8_fixes].section != section)
+                                 || (a8_fixes[num_a8_fixes].offset != i))
+                               {
+                                 free (stub_name);
+                                 stub_name = NULL;
+                                 *stub_changed_p = TRUE;
+                               }
+                           }
+
+                         if (!stub_name)
+                           {
+                             stub_name = (char *) bfd_malloc (8 + 1 + 8 + 1);
+                             if (stub_name != NULL)
+                               sprintf (stub_name, "%x:%x", section->id, i);
+                           }
+
+                          a8_fixes[num_a8_fixes].input_bfd = input_bfd;
+                          a8_fixes[num_a8_fixes].section = section;
+                          a8_fixes[num_a8_fixes].offset = i;
+                          a8_fixes[num_a8_fixes].addend = offset;
+                          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++;
+                        }
+                    }
+                }
+
+              i += insn_32bit ? 4 : 2;
+              last_was_32bit = insn_32bit;
+             last_was_branch = is_32bit_branch;
+            }
+        }
+
+      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;
+}
+
+/* Determine and set the size of the stub section for a final link.
+
+   The basic idea here is to examine all the relocations looking for
+   PC-relative calls to a target that is unreachable with a "bl"
+   instruction.  */
+
+bfd_boolean
+elf32_arm_size_stubs (bfd *output_bfd,
+                     bfd *stub_bfd,
+                     struct bfd_link_info *info,
+                     bfd_signed_vma group_size,
+                     asection * (*add_stub_section) (const char *, asection *),
+                     void (*layout_sections_again) (void))
+{
+  bfd_size_type stub_group_size;
+  bfd_boolean stubs_always_after_branch;
+  struct elf32_arm_link_hash_table *htab = elf32_arm_hash_table (info);
+  struct a8_erratum_fix *a8_fixes = NULL;
+  unsigned int num_a8_fixes = 0, a8_fix_table_size = 10;
+  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 *)
+          bfd_zmalloc (sizeof (struct a8_erratum_fix) * a8_fix_table_size);
+      a8_relocs = (struct a8_erratum_reloc *)
+          bfd_zmalloc (sizeof (struct a8_erratum_reloc) * a8_reloc_table_size);
+    }
+
+  /* Propagate mach to stub bfd, because it may not have been
+     finalized when we created stub_bfd.  */
+  bfd_set_arch_mach (stub_bfd, bfd_get_arch (output_bfd),
+                    bfd_get_mach (output_bfd));
+
+  /* Stash our params away.  */
+  htab->stub_bfd = stub_bfd;
+  htab->add_stub_section = add_stub_section;
+  htab->layout_sections_again = layout_sections_again;
+  stubs_always_after_branch = group_size < 0;
+
+  /* The Cortex-A8 erratum fix depends on stubs not being in the same 4K page
+     as the first half of a 32-bit branch straddling two 4K pages.  This is a
+     crude way of enforcing that.  */
+  if (htab->fix_cortex_a8)
+    stubs_always_after_branch = 1;
+
+  if (group_size < 0)
+    stub_group_size = -group_size;
+  else
+    stub_group_size = group_size;
+
+  if (stub_group_size == 1)
+    {
+      /* Default values.  */
+      /* Thumb branch range is +-4MB has to be used as the default
+        maximum size (a given section can contain both ARM and Thumb
+        code, so the worst case has to be taken into account).
+
+        This value is 24K less than that, which allows for 2025
+        12-byte stubs.  If we exceed that, then we will fail to link.
+        The user will have to relink with an explicit group size
+        option.  */
+      stub_group_size = 4170000;
+    }
+
+  group_sections (htab, stub_group_size, stubs_always_after_branch);
+
+  /* If we're applying the cortex A8 fix, we need to determine the
+     program header size now, because we cannot change it later --
+     that could alter section placements.  Notice the A8 erratum fix
+     ends up requiring the section addresses to remain unchanged
+     modulo the page size.  That's something we cannot represent
+     inside BFD, and we don't want to force the section alignment to
+     be the page size.  */
+  if (htab->fix_cortex_a8)
+    (*htab->layout_sections_again) ();
+
+  while (1)
+    {
+      bfd *input_bfd;
+      unsigned int bfd_indx;
+      asection *stub_sec;
+      bfd_boolean stub_changed = FALSE;
+      unsigned prev_num_a8_fixes = num_a8_fixes;
+
+      num_a8_fixes = 0;
+      for (input_bfd = info->input_bfds, bfd_indx = 0;
+          input_bfd != NULL;
+          input_bfd = input_bfd->link_next, bfd_indx++)
+       {
+         Elf_Internal_Shdr *symtab_hdr;
+         asection *section;
+         Elf_Internal_Sym *local_syms = NULL;
+
+         num_a8_relocs = 0;
+
+         /* We'll need the symbol table in a second.  */
+         symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
+         if (symtab_hdr->sh_info == 0)
+           continue;
+
+         /* Walk over each section attached to the input bfd.  */
+         for (section = input_bfd->sections;
+              section != NULL;
+              section = section->next)
+           {
+             Elf_Internal_Rela *internal_relocs, *irelaend, *irela;
+
+             /* If there aren't any relocs, then there's nothing more
+                to do.  */
+             if ((section->flags & SEC_RELOC) == 0
+                 || section->reloc_count == 0
+                 || (section->flags & SEC_CODE) == 0)
+               continue;
 
              /* If this section is a link-once section that will be
                 discarded, then don't create any stubs.  */
@@ -3774,7 +4447,8 @@ 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);
                  r_indx = ELF32_R_SYM (irela->r_info);
@@ -3792,6 +4466,8 @@ elf32_arm_size_stubs (bfd *output_bfd,
                  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))
                    continue;
@@ -3807,7 +4483,6 @@ elf32_arm_size_stubs (bfd *output_bfd,
                    {
                      /* It's a local symbol.  */
                      Elf_Internal_Sym *sym;
-                     Elf_Internal_Shdr *hdr;
 
                      if (local_syms == NULL)
                        {
@@ -3823,8 +4498,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
@@ -3855,7 +4543,27 @@ elf32_arm_size_stubs (bfd *output_bfd,
                        {
                          sym_sec = hash->root.root.u.def.section;
                          sym_value = hash->root.root.u.def.value;
-                         if (sym_sec->output_section != NULL)
+
+                         struct elf32_arm_link_hash_table *globals =
+                                                 elf32_arm_hash_table (info);
+
+                         /* For a destination in a shared library,
+                            use the PLT stub as target address to
+                            decide whether a branch stub is
+                            needed.  */
+                         if (globals != NULL
+                             && globals->splt != NULL
+                             && hash != NULL
+                             && hash->root.plt.offset != (bfd_vma) -1)
+                           {
+                             sym_sec = globals->splt;
+                             sym_value = hash->root.plt.offset;
+                             if (sym_sec->output_section != NULL)
+                               destination = (sym_value
+                                              + sym_sec->output_offset
+                                              + sym_sec->output_section->vma);
+                           }
+                         else if (sym_sec->output_section != NULL)
                            destination = (sym_value + irela->r_addend
                                           + sym_sec->output_offset
                                           + sym_sec->output_section->vma);
@@ -3870,7 +4578,9 @@ 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->splt != NULL
+                             && hash != NULL
                              && hash->root.plt.offset != (bfd_vma) -1)
                            {
                              sym_sec = globals->splt;
@@ -3892,81 +4602,152 @@ elf32_arm_size_stubs (bfd *output_bfd,
                      sym_name = hash->root.root.root.string;
                    }
 
-                 /* Determine what (if any) linker stub is needed.  */
-                 stub_type = arm_type_of_stub (info, section, irela, st_type,
-                                               hash, destination, sym_sec,
-                                               input_bfd, sym_name);
-                 if (stub_type == arm_stub_none)
-                   continue;
-
-                 /* Support for grouping stub sections.  */
-                 id_sec = htab->stub_group[section->id].link_sec;
-
-                 /* Get the name of this stub.  */
-                 stub_name = elf32_arm_stub_name (id_sec, sym_sec, hash, irela);
-                 if (!stub_name)
-                   goto error_ret_free_internal;
-
-                 stub_entry = arm_stub_hash_lookup (&htab->stub_hash_table,
-                                                   stub_name,
-                                                   FALSE, FALSE);
-                 if (stub_entry != NULL)
-                   {
-                     /* The proper stub has already been created.  */
-                     free (stub_name);
-                     continue;
-                   }
-
-                 stub_entry = elf32_arm_add_stub (stub_name, section, htab);
-                 if (stub_entry == NULL)
-                   {
-                     free (stub_name);
-                     goto error_ret_free_internal;
-                   }
-
-                 stub_entry->target_value = sym_value;
-                 stub_entry->target_section = sym_sec;
-                 stub_entry->stub_type = stub_type;
-                 stub_entry->h = hash;
-                 stub_entry->st_type = st_type;
-
-                 if (sym_name == NULL)
-                   sym_name = "unnamed";
-                 stub_entry->output_name
-                   = bfd_alloc (htab->stub_bfd,
-                                sizeof (THUMB2ARM_GLUE_ENTRY_NAME)
-                                + strlen (sym_name));
-                 if (stub_entry->output_name == NULL)
+                 do
                    {
-                     free (stub_name);
-                     goto error_ret_free_internal;
-                   }
+                     /* Determine what (if any) linker stub is needed.  */
+                     stub_type = arm_type_of_stub (info, section, irela,
+                                                   &st_type, hash,
+                                                   destination, sym_sec,
+                                                   input_bfd, sym_name);
+                     if (stub_type == arm_stub_none)
+                       break;
+
+                     /* Support for grouping stub sections.  */
+                     id_sec = htab->stub_group[section->id].link_sec;
+
+                     /* Get the name of this stub.  */
+                     stub_name = elf32_arm_stub_name (id_sec, sym_sec, hash,
+                                                      irela, stub_type);
+                     if (!stub_name)
+                       goto error_ret_free_internal;
+
+                     /* We've either created a stub for this reloc already,
+                        or we are about to.  */
+                     created_stub = TRUE;
+
+                     stub_entry = arm_stub_hash_lookup
+                                    (&htab->stub_hash_table, stub_name,
+                                     FALSE, FALSE);
+                     if (stub_entry != NULL)
+                       {
+                         /* The proper stub has already been created.  */
+                         free (stub_name);
+                         stub_entry->target_value = sym_value;
+                         break;
+                       }
 
-                 /* For historical reasons, use the existing names for
-                    ARM-to-Thumb and Thumb-to-ARM stubs.  */
-                 if ( ((r_type == (unsigned int) R_ARM_THM_CALL)
-                       || (r_type == (unsigned int) R_ARM_THM_JUMP24))
-                      && st_type != STT_ARM_TFUNC)
-                   sprintf (stub_entry->output_name, THUMB2ARM_GLUE_ENTRY_NAME,
-                            sym_name);
-                 else if ( ((r_type == (unsigned int) R_ARM_CALL)
-                            || (r_type == (unsigned int) R_ARM_JUMP24))
-                          && st_type == STT_ARM_TFUNC)
-                   sprintf (stub_entry->output_name, ARM2THUMB_GLUE_ENTRY_NAME,
-                            sym_name);
-                 else
-                   sprintf (stub_entry->output_name, STUB_ENTRY_NAME,
-                            sym_name);
+                     stub_entry = elf32_arm_add_stub (stub_name, section,
+                                                      htab);
+                     if (stub_entry == NULL)
+                       {
+                         free (stub_name);
+                         goto error_ret_free_internal;
+                       }
 
-                 stub_changed = TRUE;
+                      stub_entry->target_value = sym_value;
+                      stub_entry->target_section = sym_sec;
+                      stub_entry->stub_type = stub_type;
+                      stub_entry->h = hash;
+                      stub_entry->st_type = st_type;
+
+                      if (sym_name == NULL)
+                       sym_name = "unnamed";
+                      stub_entry->output_name = (char *)
+                          bfd_alloc (htab->stub_bfd,
+                                     sizeof (THUMB2ARM_GLUE_ENTRY_NAME)
+                                     + strlen (sym_name));
+                      if (stub_entry->output_name == NULL)
+                       {
+                          free (stub_name);
+                          goto error_ret_free_internal;
+                       }
+
+                      /* For historical reasons, use the existing names for
+                        ARM-to-Thumb and Thumb-to-ARM stubs.  */
+                      if ( ((r_type == (unsigned int) R_ARM_THM_CALL)
+                            || (r_type == (unsigned int) R_ARM_THM_JUMP24))
+                           && st_type != STT_ARM_TFUNC)
+                       sprintf (stub_entry->output_name,
+                                THUMB2ARM_GLUE_ENTRY_NAME, sym_name);
+                      else if ( ((r_type == (unsigned int) R_ARM_CALL)
+                                || (r_type == (unsigned int) R_ARM_JUMP24))
+                               && st_type == STT_ARM_TFUNC)
+                       sprintf (stub_entry->output_name,
+                                ARM2THUMB_GLUE_ENTRY_NAME, sym_name);
+                      else
+                       sprintf (stub_entry->output_name, STUB_ENTRY_NAME,
+                                sym_name);
+
+                      stub_changed = TRUE;
+                    }
+                  while (0);
+
+                  /* Look for relocations which might trigger Cortex-A8
+                     erratum.  */
+                  if (htab->fix_cortex_a8
+                      && (r_type == (unsigned int) R_ARM_THM_JUMP24
+                          || r_type == (unsigned int) R_ARM_THM_JUMP19
+                          || r_type == (unsigned int) R_ARM_THM_CALL
+                          || r_type == (unsigned int) R_ARM_THM_XPC22))
+                    {
+                      bfd_vma from = section->output_section->vma
+                                     + section->output_offset
+                                     + irela->r_offset;
+
+                      if ((from & 0xfff) == 0xffe)
+                        {
+                          /* Found a candidate.  Note we haven't checked the
+                             destination is within 4K here: if we do so (and
+                             don't create an entry in a8_relocs) we can't tell
+                             that a branch should have been relocated when
+                             scanning later.  */
+                          if (num_a8_relocs == a8_reloc_table_size)
+                            {
+                              a8_reloc_table_size *= 2;
+                              a8_relocs = (struct a8_erratum_reloc *)
+                                  bfd_realloc (a8_relocs,
+                                               sizeof (struct a8_erratum_reloc)
+                                               * a8_reloc_table_size);
+                            }
+
+                          a8_relocs[num_a8_relocs].from = from;
+                          a8_relocs[num_a8_relocs].destination = destination;
+                          a8_relocs[num_a8_relocs].r_type = r_type;
+                          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++;
+                        }
+                    }
                }
 
-             /* We're done with the internal relocs, free them.  */
-             if (elf_section_data (section)->relocs == NULL)
-               free (internal_relocs);
+              /* We're done with the internal relocs, free them.  */
+              if (elf_section_data (section)->relocs == NULL)
+                free (internal_relocs);
+            }
+
+          if (htab->fix_cortex_a8)
+           {
+              /* Sort relocs which might apply to Cortex-A8 erratum.  */
+              qsort (a8_relocs, num_a8_relocs,
+                    sizeof (struct a8_erratum_reloc),
+                     &a8_reloc_compare);
+
+              /* Scan for branches which might trigger Cortex-A8 erratum.  */
+              if (cortex_a8_erratum_scan (input_bfd, info, &a8_fixes,
+                                         &num_a8_fixes, &a8_fix_table_size,
+                                         a8_relocs, num_a8_relocs,
+                                         prev_num_a8_fixes, &stub_changed)
+                 != 0)
+               goto error_ret_free_local;
            }
        }
 
+      if (prev_num_a8_fixes != num_a8_fixes)
+        stub_changed = TRUE;
+
       if (!stub_changed)
        break;
 
@@ -3985,11 +4766,79 @@ elf32_arm_size_stubs (bfd *output_bfd,
 
       bfd_hash_traverse (&htab->stub_hash_table, arm_size_one_stub, htab);
 
+      /* Add Cortex-A8 erratum veneers to stub section sizes too.  */
+      if (htab->fix_cortex_a8)
+        for (i = 0; i < num_a8_fixes; i++)
+          {
+           stub_sec = elf32_arm_create_or_find_stub_sec (NULL,
+                        a8_fixes[i].section, htab);
+
+           if (stub_sec == NULL)
+             goto error_ret_free_local;
+
+            stub_sec->size
+              += find_stub_size_and_template (a8_fixes[i].stub_type, NULL,
+                                              NULL);
+          }
+
+
       /* Ask the linker to do its stuff.  */
       (*htab->layout_sections_again) ();
-      stub_changed = FALSE;
     }
 
+  /* Add stubs for Cortex-A8 erratum fixes now.  */
+  if (htab->fix_cortex_a8)
+    {
+      for (i = 0; i < num_a8_fixes; i++)
+        {
+          struct elf32_arm_stub_hash_entry *stub_entry;
+          char *stub_name = a8_fixes[i].stub_name;
+          asection *section = a8_fixes[i].section;
+          unsigned int section_id = a8_fixes[i].section->id;
+          asection *link_sec = htab->stub_group[section_id].link_sec;
+          asection *stub_sec = htab->stub_group[section_id].stub_sec;
+          const insn_sequence *template_sequence;
+          int template_size, size = 0;
+
+          stub_entry = arm_stub_hash_lookup (&htab->stub_hash_table, stub_name,
+                                             TRUE, FALSE);
+          if (stub_entry == NULL)
+            {
+              (*_bfd_error_handler) (_("%s: cannot create stub entry %s"),
+                                     section->owner,
+                                     stub_name);
+              return FALSE;
+            }
+
+          stub_entry->stub_sec = stub_sec;
+          stub_entry->stub_offset = 0;
+          stub_entry->id_sec = link_sec;
+          stub_entry->stub_type = a8_fixes[i].stub_type;
+          stub_entry->target_section = a8_fixes[i].section;
+          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 = a8_fixes[i].st_type;
+
+          size = find_stub_size_and_template (a8_fixes[i].stub_type,
+                                              &template_sequence,
+                                              &template_size);
+
+          stub_entry->stub_size = size;
+          stub_entry->stub_template = template_sequence;
+          stub_entry->stub_template_size = template_size;
+        }
+
+      /* Stash the Cortex-A8 erratum fix array for use later in
+         elf32_arm_write_section().  */
+      htab->a8_erratum_fixes = a8_fixes;
+      htab->num_a8_erratum_fixes = num_a8_fixes;
+    }
+  else
+    {
+      htab->a8_erratum_fixes = NULL;
+      htab->num_a8_erratum_fixes = 0;
+    }
   return TRUE;
 
  error_ret_free_local:
@@ -4010,6 +4859,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;
@@ -4023,7 +4874,7 @@ elf32_arm_build_stubs (struct bfd_link_info *info)
 
       /* Allocate memory to hold the linker stubs.  */
       size = stub_sec->size;
-      stub_sec->contents = bfd_zalloc (htab->stub_bfd, size);
+      stub_sec->contents = (unsigned char *) bfd_zalloc (htab->stub_bfd, size);
       if (stub_sec->contents == NULL && size != 0)
        return FALSE;
       stub_sec->size = 0;
@@ -4032,6 +4883,12 @@ elf32_arm_build_stubs (struct bfd_link_info *info)
   /* Build the stubs as directed by the stub hash table.  */
   table = &htab->stub_hash_table;
   bfd_hash_traverse (table, arm_build_one_stub, info);
+  if (htab->fix_cortex_a8)
+    {
+      /* Place the cortex a8 stubs last.  */
+      htab->fix_cortex_a8 = -1;
+      bfd_hash_traverse (table, arm_build_one_stub, info);
+    }
 
   return TRUE;
 }
@@ -4049,9 +4906,11 @@ 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 = bfd_malloc ((bfd_size_type) strlen (name)
-                        + strlen (THUMB2ARM_GLUE_ENTRY_NAME) + 1);
+  tmp_name = (char *) bfd_malloc ((bfd_size_type) strlen (name)
+                                  + strlen (THUMB2ARM_GLUE_ENTRY_NAME) + 1);
 
   BFD_ASSERT (tmp_name);
 
@@ -4083,9 +4942,11 @@ 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 = bfd_malloc ((bfd_size_type) strlen (name)
-                        + strlen (ARM2THUMB_GLUE_ENTRY_NAME) + 1);
+  tmp_name = (char *) bfd_malloc ((bfd_size_type) strlen (name)
+                                  + strlen (ARM2THUMB_GLUE_ENTRY_NAME) + 1);
 
   BFD_ASSERT (tmp_name);
 
@@ -4195,7 +5056,7 @@ arm_allocate_glue_section_space (bfd * abfd, bfd_size_type size, const char * na
   s = bfd_get_section_by_name (abfd, name);
   BFD_ASSERT (s != NULL);
 
-  contents = bfd_alloc (abfd, size);
+  contents = (bfd_byte *) bfd_alloc (abfd, size);
 
   BFD_ASSERT (s->size == size);
   s->contents = contents;
@@ -4245,7 +5106,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);
 
@@ -4254,7 +5114,8 @@ record_arm_to_thumb_glue (struct bfd_link_info * link_info,
 
   BFD_ASSERT (s != NULL);
 
-  tmp_name = bfd_malloc ((bfd_size_type) strlen (name) + strlen (ARM2THUMB_GLUE_ENTRY_NAME) + 1);
+  tmp_name = (char *) bfd_malloc ((bfd_size_type) strlen (name)
+                                  + strlen (ARM2THUMB_GLUE_ENTRY_NAME) + 1);
 
   BFD_ASSERT (tmp_name);
 
@@ -4317,7 +5178,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);
 
@@ -4331,7 +5191,8 @@ record_arm_bx_glue (struct bfd_link_info * link_info, int reg)
   BFD_ASSERT (s != NULL);
 
   /* Add symbol for veneer.  */
-  tmp_name = bfd_malloc ((bfd_size_type) strlen (ARM_BX_GLUE_ENTRY_NAME) + 1);
+  tmp_name = (char *)
+      bfd_malloc ((bfd_size_type) strlen (ARM_BX_GLUE_ENTRY_NAME) + 1);
 
   BFD_ASSERT (tmp_name);
 
@@ -4368,7 +5229,8 @@ elf32_arm_section_map_add (asection *sec, char type, bfd_vma vma)
 
   if (sec_data->map == NULL)
     {
-      sec_data->map = bfd_malloc (sizeof (elf32_arm_section_map));
+      sec_data->map = (elf32_arm_section_map *)
+          bfd_malloc (sizeof (elf32_arm_section_map));
       sec_data->mapcount = 0;
       sec_data->mapsize = 1;
     }
@@ -4378,8 +5240,9 @@ elf32_arm_section_map_add (asection *sec, char type, bfd_vma vma)
   if (sec_data->mapcount > sec_data->mapsize)
     {
       sec_data->mapsize *= 2;
-      sec_data->map = bfd_realloc_or_free (sec_data->map, sec_data->mapsize
-                                          * sizeof (elf32_arm_section_map));
+      sec_data->map = (elf32_arm_section_map *)
+          bfd_realloc_or_free (sec_data->map, sec_data->mapsize
+                               * sizeof (elf32_arm_section_map));
     }
 
   if (sec_data->map)
@@ -4407,11 +5270,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);
 
@@ -4422,8 +5283,8 @@ record_vfp11_erratum_veneer (struct bfd_link_info *link_info,
 
   BFD_ASSERT (s != NULL);
 
-  tmp_name = bfd_malloc ((bfd_size_type) strlen
-                        (VFP11_ERRATUM_VENEER_ENTRY_NAME) + 10);
+  tmp_name = (char *) bfd_malloc ((bfd_size_type) strlen
+                                  (VFP11_ERRATUM_VENEER_ENTRY_NAME) + 10);
 
   BFD_ASSERT (tmp_name);
 
@@ -4446,8 +5307,9 @@ record_vfp11_erratum_veneer (struct bfd_link_info *link_info,
   myh->forced_local = 1;
 
   /* Link veneer back to calling location.  */
-  errcount = ++(sec_data->erratumcount);
-  newerr = bfd_zmalloc (sizeof (elf32_vfp11_erratum_list));
+  sec_data->erratumcount += 1;
+  newerr = (elf32_vfp11_erratum_list *)
+      bfd_zmalloc (sizeof (elf32_vfp11_erratum_list));
 
   newerr->type = VFP11_ERRATUM_ARM_VENEER;
   newerr->vma = -1;
@@ -4575,7 +5437,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)
@@ -4615,7 +5476,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);
@@ -4807,12 +5667,39 @@ bfd_elf32_arm_init_maps (bfd *abfd)
 }
 
 
+/* Auto-select enabling of Cortex-A8 erratum fix if the user didn't explicitly
+   say what they wanted.  */
+
+void
+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.  */
+      if (out_attr[Tag_CPU_arch].i == TAG_CPU_ARCH_V7
+         && (out_attr[Tag_CPU_arch_profile].i == 'A'
+             || out_attr[Tag_CPU_arch_profile].i == 0))
+       globals->fix_cortex_a8 = 1;
+      else
+       globals->fix_cortex_a8 = 0;
+    }
+}
+
+
 void
 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)
     {
@@ -4917,7 +5804,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.  */
@@ -4936,7 +5823,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.  */
@@ -4948,11 +5835,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.  */
@@ -4982,14 +5869,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}.  */
@@ -5004,7 +5891,7 @@ bfd_arm_vfp11_insn_decode (unsigned int insn, unsigned int *destmask, int *regs,
 
                   *numregs = rnum;
 
-                  pipe = VFP11_FMAC;
+                  vpipe = VFP11_FMAC;
                 }
                 break;
 
@@ -5034,7 +5921,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.  */
     {
@@ -5069,7 +5956,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)
@@ -5091,10 +5978,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;
 }
 
 
@@ -5116,6 +6003,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:
 
@@ -5214,17 +6104,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;
@@ -5235,10 +6125,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;
@@ -5250,10 +6140,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;
@@ -5271,11 +6161,10 @@ bfd_elf32_arm_vfp11_erratum_scan (bfd *abfd, struct bfd_link_info *link_info)
 
               if (state == 3)
                 {
-                  elf32_vfp11_erratum_list *newerr
-                    = bfd_zmalloc (sizeof (elf32_vfp11_erratum_list));
-                  int errcount;
+                  elf32_vfp11_erratum_list *newerr =(elf32_vfp11_erratum_list *)
+                      bfd_zmalloc (sizeof (elf32_vfp11_erratum_list));
 
-                  errcount = ++(elf32_arm_section_data (sec)->erratumcount);
+                  elf32_arm_section_data (sec)->erratumcount += 1;
 
                   newerr->u.b.vfp_insn = veneer_of_insn;
 
@@ -5339,9 +6228,11 @@ bfd_elf32_arm_vfp11_fix_veneer_locations (bfd *abfd,
     return;
 
   globals = elf32_arm_hash_table (link_info);
+  if (globals == NULL)
+    return;
 
-  tmp_name = bfd_malloc ((bfd_size_type) strlen
-                          (VFP11_ERRATUM_VENEER_ENTRY_NAME) + 10);
+  tmp_name = (char *) bfd_malloc ((bfd_size_type) strlen
+                                  (VFP11_ERRATUM_VENEER_ENTRY_NAME) + 10);
 
   for (sec = abfd->sections; sec != NULL; sec = sec->next)
     {
@@ -5416,11 +6307,13 @@ bfd_elf32_arm_set_target_relocs (struct bfd *output_bfd,
                                 int use_blx,
                                  bfd_arm_vfp11_fix vfp11_fix,
                                 int no_enum_warn, int no_wchar_warn,
-                                int pic_veneer)
+                                int pic_veneer, int fix_cortex_a8)
 {
   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)
@@ -5438,6 +6331,7 @@ bfd_elf32_arm_set_target_relocs (struct bfd *output_bfd,
   globals->use_blx |= use_blx;
   globals->vfp11_fix = vfp11_fix;
   globals->pic_veneer = pic_veneer;
+  globals->fix_cortex_a8 = fix_cortex_a8;
 
   BFD_ASSERT (is_arm_elf (output_bfd));
   elf_arm_tdata (output_bfd)->no_enum_size_warning = no_enum_warn;
@@ -5495,7 +6389,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);
 
@@ -5593,7 +6486,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);
 
@@ -5689,7 +6581,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);
 
@@ -5744,7 +6635,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);
 
@@ -5780,7 +6670,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);
 
@@ -5818,6 +6707,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)
@@ -5984,8 +6876,6 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
   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;
   asection *                    sgot = NULL;
   asection *                    splt = NULL;
@@ -5995,6 +6885,8 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
   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));
 
@@ -6022,8 +6914,6 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
       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);
   local_got_offsets = elf_local_got_offsets (input_bfd);
   r_symndx = ELF32_R_SYM (rel->r_info);
 
@@ -6098,11 +6988,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)
@@ -6227,8 +7118,6 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
        case R_ARM_PC24:          /* Arm B/BL instruction.  */
        case R_ARM_PLT32:
          {
-         bfd_vma from;
-         bfd_signed_vma branch_offset;
          struct elf32_arm_stub_hash_entry *stub_entry = NULL;
 
          if (r_type == R_ARM_XPC25)
@@ -6264,40 +7153,46 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
              || r_type == R_ARM_JUMP24
              || r_type == R_ARM_PLT32)
            {
-             /* 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;
-               }
+             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
@@ -6332,13 +7227,21 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
          signed_addend >>= howto->rightshift;
 
          /* A branch to an undefined weak symbol is turned into a jump to
-            the next instruction unless a PLT entry will be created.  */
-         if (h && h->root.type == bfd_link_hash_undefweak
-             && !(splt != NULL && h->plt.offset != (bfd_vma) -1))
+            the next instruction unless a PLT entry will be created.
+            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))
+             : r_symndx != STN_UNDEF && bfd_is_und_section (sym_sec))
            {
-             value = (bfd_get_32 (input_bfd, hit_data) & 0xf0000000)
-                     | 0x0affffff;
-           }
+             value = (bfd_get_32 (input_bfd, hit_data) & 0xf0000000);
+
+             if (arch_has_arm_nop (globals))
+               value |= 0x0320f000;
+             else
+               value |= 0x01a00000; /* Using pre-UAL nop: mov r0, r0.  */
+           }
          else
            {
              /* Perform a signed range check.  */
@@ -6423,7 +7326,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);
@@ -6432,7 +7339,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);
@@ -6497,6 +7405,40 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
         return bfd_reloc_ok;
       }
 
+    case R_ARM_THM_PC8:
+      /* PR 10073:  This reloc is not generated by the GNU toolchain,
+        but it is supported for compatibility with third party libraries
+        generated by other compilers, specifically the ARM/IAR.  */
+      {
+       bfd_vma insn;
+       bfd_signed_vma relocation;
+
+       insn = bfd_get_16 (input_bfd, hit_data);
+
+        if (globals->use_rel)
+         addend = (insn & 0x00ff) << 2;
+
+       relocation = value + addend;
+       relocation -= (input_section->output_section->vma
+                      + input_section->output_offset
+                      + rel->r_offset);
+
+        value = abs (relocation);
+
+       /* We do not check for overflow of this reloc.  Although strictly
+          speaking this is incorrect, it appears to be necessary in order
+          to work with IAR generated relocs.  Since GCC and GAS do not
+          generate R_ARM_THM_PC8 relocs, the lack of a check should not be
+          a problem for them.  */
+       value &= 0x3fc;
+
+       insn = (insn & 0xff00) | (value >> 2);
+
+       bfd_put_16 (input_bfd, insn, hit_data);
+
+        return bfd_reloc_ok;
+      }
+
     case R_ARM_THM_PC12:
       /* Corresponds to: ldr.w reg, [pc, #offset].  */
       {
@@ -6548,15 +7490,25 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
        bfd_vma check;
        bfd_signed_vma signed_check;
        int bitsize;
-       int thumb2 = using_thumb2 (globals);
+       const int thumb2 = using_thumb2 (globals);
 
        /* A branch to an undefined weak symbol is turned into a jump to
-          the next instruction unless a PLT entry will be created.  */
+          the next instruction unless a PLT entry will be created.
+          The jump to the next instruction is optimized as a NOP.W for
+          Thumb-2 enabled architectures.  */
        if (h && h->root.type == bfd_link_hash_undefweak
            && !(splt != NULL && h->plt.offset != (bfd_vma) -1))
          {
-           bfd_put_16 (input_bfd, 0xe000, hit_data);
-           bfd_put_16 (input_bfd, 0xbf00, hit_data + 2);
+           if (arch_has_thumb2_nop (globals))
+             {
+               bfd_put_16 (input_bfd, 0xf3af, hit_data);
+               bfd_put_16 (input_bfd, 0x8000, hit_data + 2);
+             }
+           else
+             {
+               bfd_put_16 (input_bfd, 0xe000, hit_data);
+               bfd_put_16 (input_bfd, 0xbf00, hit_data + 2);
+             }
            return bfd_reloc_ok;
          }
 
@@ -6625,54 +7577,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;
-             }
-           else
-             /* Target the Thumb stub before the ARM PLT entry.  */
-             value -= PLT_THUMB_STUB_SIZE;
-           *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
@@ -6689,6 +7616,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
@@ -6710,7 +7664,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.  */
@@ -7269,7 +8223,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
            (_("%B(%A+0x%lx): R_ARM_TLS_LE32 relocation not permitted in shared object"),
             input_bfd, input_section,
             (long) rel->r_offset, howto->name);
-         return FALSE;
+         return (bfd_reloc_status_type) FALSE;
        }
       else
        value = tpoff (info, value);
@@ -7905,6 +8859,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);
@@ -7946,6 +8902,27 @@ elf32_arm_relocate_section (bfd *                  output_bfd,
          sym = local_syms + r_symndx;
          sym_type = ELF32_ST_TYPE (sym->st_info);
          sec = local_sections[r_symndx];
+
+         /* An object file might have a reference to a local
+            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.
+            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)
+           {
+             if (!info->callbacks->undefined_symbol
+                 (info, bfd_elf_string_from_elf_section
+                  (input_bfd, symtab_hdr->sh_link, sym->st_name),
+                  input_bfd, input_section,
+                  rel->r_offset, TRUE))
+               return FALSE;
+           }
+         
          if (globals->use_rel)
            {
              relocation = (sec->output_section->vma
@@ -8092,7 +9069,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
@@ -8188,9 +9165,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
@@ -8198,15 +9175,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 = xmalloc (sizeof (arm_unwind_table_edit));
+  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;
 
@@ -8268,6 +9246,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).
 */
@@ -8275,7 +9255,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;
@@ -8294,7 +9275,7 @@ elf32_arm_fix_exidx_coverage (asection **text_section_order,
          struct bfd_elf_section_data *elf_sec = elf_section_data (sec);
          Elf_Internal_Shdr *hdr = &elf_sec->this_hdr;
          
-         if (hdr->sh_type != SHT_ARM_EXIDX)
+         if (!hdr || hdr->sh_type != SHT_ARM_EXIDX)
            continue;
          
          if (elf_sec->linked_to)
@@ -8316,8 +9297,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++)
     {
@@ -8388,7 +9368,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;
@@ -8456,11 +9437,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)
@@ -8647,9 +9649,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;
@@ -8697,7 +9699,7 @@ set_secondary_compatible_arch (bfd *abfd, int arch)
   /* Note: the tag and its argument below are uleb128 values, though
      currently-defined values fit in one byte for each.  */
   if (!attr->s)
-    attr->s = bfd_alloc (abfd, 3);
+    attr->s = (char *) bfd_alloc (abfd, 3);
   attr->s[0] = Tag_CPU_arch;
   attr->s[1] = arch;
   attr->s[2] = '\0';
@@ -8782,6 +9784,23 @@ tag_cpu_arch_combine (bfd *ibfd, int oldtag, int *secondary_compat_out,
       T(V6S_M),  /* V6_M.  */
       T(V6S_M)   /* V6S_M.  */
     };
+  const int v7e_m[] =
+    {
+      -1,        /* PRE_V4.  */
+      -1,        /* V4.  */
+      T(V7E_M),  /* V4T.  */
+      T(V7E_M),  /* V5T.  */
+      T(V7E_M),  /* V5TE.  */
+      T(V7E_M),  /* V5TEJ.  */
+      T(V7E_M),  /* V6.  */
+      T(V7E_M),  /* V6KZ.  */
+      T(V7E_M),  /* V6T2.  */
+      T(V7E_M),  /* V6K.  */
+      T(V7E_M),  /* V7.  */
+      T(V7E_M),  /* V6_M.  */
+      T(V7E_M),  /* V6S_M.  */
+      T(V7E_M)   /* V7E_M.  */
+    };
   const int v4t_plus_v6_m[] =
     {
       -1,              /* PRE_V4.  */
@@ -8797,6 +9816,7 @@ tag_cpu_arch_combine (bfd *ibfd, int oldtag, int *secondary_compat_out,
       T(V7),           /* V7.  */
       T(V6_M),         /* V6_M.  */
       T(V6S_M),                /* V6S_M.  */
+      T(V7E_M),                /* V7E_M.  */
       T(V4T_PLUS_V6_M) /* V4T plus V6_M.  */
     };
   const int *comb[] =
@@ -8806,13 +9826,14 @@ tag_cpu_arch_combine (bfd *ibfd, int oldtag, int *secondary_compat_out,
       v7,
       v6_m,
       v6s_m,
+      v7e_m,
       /* Pseudo-architecture.  */
       v4t_plus_v6_m
     };
 
   /* Check we've not got a higher architecture than we know about.  */
 
-  if (oldtag >= MAX_TAG_CPU_ARCH || newtag >= MAX_TAG_CPU_ARCH)
+  if (oldtag > MAX_TAG_CPU_ARCH || newtag > MAX_TAG_CPU_ARCH)
     {
       _bfd_error_handler (_("error: %B: Unknown CPU architecture"), ibfd);
       return -1;
@@ -8875,8 +9896,6 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
   /* Some tags have 0 = don't care, 1 = strong requirement,
      2 = weak requirement.  */
   static const int order_021[3] = {0, 2, 1};
-  /* For use with Tag_VFP_arch.  */
-  static const int order_01243[5] = {0, 1, 2, 4, 3};
   int i;
   bfd_boolean result = TRUE;
 
@@ -8891,11 +9910,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);
@@ -8910,12 +9951,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)
@@ -8999,27 +10041,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
@@ -9040,6 +10081,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)
@@ -9067,13 +10129,90 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
                }
            }
          break;
-       case Tag_VFP_arch:
-         /* Use the "greatest" from the sequence 0, 1, 2, 4, 3, or the
-            largest value if greater than 4 (for future-proofing).  */
-         if ((in_attr[i].i > 4 && in_attr[i].i > out_attr[i].i)
-             || (in_attr[i].i <= 4 && out_attr[i].i <= 4
-                 && order_01243[in_attr[i].i] > order_01243[out_attr[i].i]))
-           out_attr[i].i = in_attr[i].i;
+       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;
+                 int regs;
+             } vfp_versions[7] =
+               {
+                 {0, 0},
+                 {1, 16},
+                 {2, 16},
+                 {3, 32},
+                 {3, 16},
+                 {4, 32},
+                 {4, 16}
+               };
+             int ver;
+             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)
+               {
+                 out_attr[i] = in_attr[i];
+                 break;
+               }
+             /* The output uses the superset of input features
+                (ISA version) and registers.  */
+             ver = vfp_versions[in_attr[i].i].ver;
+             if (ver < vfp_versions[out_attr[i].i].ver)
+               ver = vfp_versions[out_attr[i].i].ver;
+             regs = vfp_versions[in_attr[i].i].regs;
+             if (regs < vfp_versions[out_attr[i].i].regs)
+               regs = vfp_versions[out_attr[i].i].regs;
+             /* This assumes all possible supersets are also a valid
+                options.  */
+             for (newval = 6; newval > 0; newval--)
+               {
+                 if (regs == vfp_versions[newval].regs
+                     && ver == vfp_versions[newval].ver)
+                   break;
+               }
+             out_attr[i].i = newval;
+           }
          break;
        case Tag_PCS_config:
          if (out_attr[i].i == 0)
@@ -9169,12 +10308,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)
@@ -9191,6 +10325,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 -
@@ -9256,7 +10436,8 @@ 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);
@@ -9353,281 +10534,63 @@ elf32_arm_versions_compatible (unsigned iver, unsigned over)
    object file when linking.  */
 
 static bfd_boolean
-elf32_arm_merge_private_bfd_data (bfd * ibfd, bfd * obfd)
-{
-  flagword out_flags;
-  flagword in_flags;
-  bfd_boolean flags_compatible = TRUE;
-  asection *sec;
+elf32_arm_merge_private_bfd_data (bfd * ibfd, bfd * obfd);
 
-  /* Check if we have the same endianess.  */
-  if (! _bfd_generic_verify_endian_match (ibfd, obfd))
-    return FALSE;
+/* Display the flags field.  */
 
-  if (! is_arm_elf (ibfd) || ! is_arm_elf (obfd))
-    return TRUE;
+static bfd_boolean
+elf32_arm_print_private_bfd_data (bfd *abfd, void * ptr)
+{
+  FILE * file = (FILE *) ptr;
+  unsigned long flags;
 
-  if (!elf32_arm_merge_eabi_attributes (ibfd, obfd))
-    return FALSE;
+  BFD_ASSERT (abfd != NULL && ptr != NULL);
 
-  /* The input BFD must have had its flags initialised.  */
-  /* The following seems bogus to me -- The flags are initialized in
-     the assembler but I don't think an elf_flags_init field is
-     written into the object.  */
-  /* BFD_ASSERT (elf_flags_init (ibfd)); */
+  /* Print normal ELF private data.  */
+  _bfd_elf_print_private_bfd_data (abfd, ptr);
 
-  in_flags  = elf_elfheader (ibfd)->e_flags;
-  out_flags = elf_elfheader (obfd)->e_flags;
+  flags = elf_elfheader (abfd)->e_flags;
+  /* Ignore init flag - it may not be set, despite the flags field
+     containing valid data.  */
 
-  /* In theory there is no reason why we couldn't handle this.  However
-     in practice it isn't even close to working and there is no real
-     reason to want it.  */
-  if (EF_ARM_EABI_VERSION (in_flags) >= EF_ARM_EABI_VER4
-      && !(ibfd->flags & DYNAMIC)
-      && (in_flags & EF_ARM_BE8))
-    {
-      _bfd_error_handler (_("error: %B is already in final BE8 format"),
-                         ibfd);
-      return FALSE;
-    }
+  /* xgettext:c-format */
+  fprintf (file, _("private flags = %lx:"), elf_elfheader (abfd)->e_flags);
 
-  if (!elf_flags_init (obfd))
+  switch (EF_ARM_EABI_VERSION (flags))
     {
-      /* If the input is the default architecture and had the default
-        flags then do not bother setting the flags for the output
-        architecture, instead allow future merges to do this.  If no
-        future merges ever set these flags then they will retain their
-         uninitialised values, which surprise surprise, correspond
-         to the default values.  */
-      if (bfd_get_arch_info (ibfd)->the_default
-         && elf_elfheader (ibfd)->e_flags == 0)
-       return TRUE;
+    case EF_ARM_EABI_UNKNOWN:
+      /* The following flag bits are GNU extensions and not part of the
+        official ARM ELF extended ABI.  Hence they are only decoded if
+        the EABI version is not set.  */
+      if (flags & EF_ARM_INTERWORK)
+       fprintf (file, _(" [interworking enabled]"));
 
-      elf_flags_init (obfd) = TRUE;
-      elf_elfheader (obfd)->e_flags = in_flags;
+      if (flags & EF_ARM_APCS_26)
+       fprintf (file, " [APCS-26]");
+      else
+       fprintf (file, " [APCS-32]");
 
-      if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
-         && bfd_get_arch_info (obfd)->the_default)
-       return bfd_set_arch_mach (obfd, bfd_get_arch (ibfd), bfd_get_mach (ibfd));
+      if (flags & EF_ARM_VFP_FLOAT)
+       fprintf (file, _(" [VFP float format]"));
+      else if (flags & EF_ARM_MAVERICK_FLOAT)
+       fprintf (file, _(" [Maverick float format]"));
+      else
+       fprintf (file, _(" [FPA float format]"));
 
-      return TRUE;
-    }
+      if (flags & EF_ARM_APCS_FLOAT)
+       fprintf (file, _(" [floats passed in float registers]"));
 
-  /* Determine what should happen if the input ARM architecture
-     does not match the output ARM architecture.  */
-  if (! bfd_arm_merge_machines (ibfd, obfd))
-    return FALSE;
+      if (flags & EF_ARM_PIC)
+       fprintf (file, _(" [position independent]"));
 
-  /* Identical flags must be compatible.  */
-  if (in_flags == out_flags)
-    return TRUE;
+      if (flags & EF_ARM_NEW_ABI)
+       fprintf (file, _(" [new ABI]"));
 
-  /* Check to see if the input BFD actually contains any sections.  If
-     not, its flags may not have been initialised either, but it
-     cannot actually cause any incompatiblity.  Do not short-circuit
-     dynamic objects; their section list may be emptied by
-    elf_link_add_object_symbols.
+      if (flags & EF_ARM_OLD_ABI)
+       fprintf (file, _(" [old ABI]"));
 
-    Also check to see if there are no code sections in the input.
-    In this case there is no need to check for code specific flags.
-    XXX - do we need to worry about floating-point format compatability
-    in data sections ?  */
-  if (!(ibfd->flags & DYNAMIC))
-    {
-      bfd_boolean null_input_bfd = TRUE;
-      bfd_boolean only_data_sections = TRUE;
-
-      for (sec = ibfd->sections; sec != NULL; sec = sec->next)
-       {
-         /* Ignore synthetic glue sections.  */
-         if (strcmp (sec->name, ".glue_7")
-             && strcmp (sec->name, ".glue_7t"))
-           {
-             if ((bfd_get_section_flags (ibfd, sec)
-                  & (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
-                 == (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
-               only_data_sections = FALSE;
-
-             null_input_bfd = FALSE;
-             break;
-           }
-       }
-
-      if (null_input_bfd || only_data_sections)
-       return TRUE;
-    }
-
-  /* Complain about various flag mismatches.  */
-  if (!elf32_arm_versions_compatible (EF_ARM_EABI_VERSION (in_flags),
-                                     EF_ARM_EABI_VERSION (out_flags)))
-    {
-      _bfd_error_handler
-       (_("error: Source object %B has EABI version %d, but target %B has EABI version %d"),
-        ibfd, obfd,
-        (in_flags & EF_ARM_EABIMASK) >> 24,
-        (out_flags & EF_ARM_EABIMASK) >> 24);
-      return FALSE;
-    }
-
-  /* Not sure what needs to be checked for EABI versions >= 1.  */
-  /* VxWorks libraries do not use these flags.  */
-  if (get_elf_backend_data (obfd) != &elf32_arm_vxworks_bed
-      && get_elf_backend_data (ibfd) != &elf32_arm_vxworks_bed
-      && EF_ARM_EABI_VERSION (in_flags) == EF_ARM_EABI_UNKNOWN)
-    {
-      if ((in_flags & EF_ARM_APCS_26) != (out_flags & EF_ARM_APCS_26))
-       {
-         _bfd_error_handler
-           (_("error: %B is compiled for APCS-%d, whereas target %B uses APCS-%d"),
-            ibfd, obfd,
-            in_flags & EF_ARM_APCS_26 ? 26 : 32,
-            out_flags & EF_ARM_APCS_26 ? 26 : 32);
-         flags_compatible = FALSE;
-       }
-
-      if ((in_flags & EF_ARM_APCS_FLOAT) != (out_flags & EF_ARM_APCS_FLOAT))
-       {
-         if (in_flags & EF_ARM_APCS_FLOAT)
-           _bfd_error_handler
-             (_("error: %B passes floats in float registers, whereas %B passes them in integer registers"),
-              ibfd, obfd);
-         else
-           _bfd_error_handler
-             (_("error: %B passes floats in integer registers, whereas %B passes them in float registers"),
-              ibfd, obfd);
-
-         flags_compatible = FALSE;
-       }
-
-      if ((in_flags & EF_ARM_VFP_FLOAT) != (out_flags & EF_ARM_VFP_FLOAT))
-       {
-         if (in_flags & EF_ARM_VFP_FLOAT)
-           _bfd_error_handler
-             (_("error: %B uses VFP instructions, whereas %B does not"),
-              ibfd, obfd);
-         else
-           _bfd_error_handler
-             (_("error: %B uses FPA instructions, whereas %B does not"),
-              ibfd, obfd);
-
-         flags_compatible = FALSE;
-       }
-
-      if ((in_flags & EF_ARM_MAVERICK_FLOAT) != (out_flags & EF_ARM_MAVERICK_FLOAT))
-       {
-         if (in_flags & EF_ARM_MAVERICK_FLOAT)
-           _bfd_error_handler
-             (_("error: %B uses Maverick instructions, whereas %B does not"),
-              ibfd, obfd);
-         else
-           _bfd_error_handler
-             (_("error: %B does not use Maverick instructions, whereas %B does"),
-              ibfd, obfd);
-
-         flags_compatible = FALSE;
-       }
-
-#ifdef EF_ARM_SOFT_FLOAT
-      if ((in_flags & EF_ARM_SOFT_FLOAT) != (out_flags & EF_ARM_SOFT_FLOAT))
-       {
-         /* We can allow interworking between code that is VFP format
-            layout, and uses either soft float or integer regs for
-            passing floating point arguments and results.  We already
-            know that the APCS_FLOAT flags match; similarly for VFP
-            flags.  */
-         if ((in_flags & EF_ARM_APCS_FLOAT) != 0
-             || (in_flags & EF_ARM_VFP_FLOAT) == 0)
-           {
-             if (in_flags & EF_ARM_SOFT_FLOAT)
-               _bfd_error_handler
-                 (_("error: %B uses software FP, whereas %B uses hardware FP"),
-                  ibfd, obfd);
-             else
-               _bfd_error_handler
-                 (_("error: %B uses hardware FP, whereas %B uses software FP"),
-                  ibfd, obfd);
-
-             flags_compatible = FALSE;
-           }
-       }
-#endif
-
-      /* Interworking mismatch is only a warning.  */
-      if ((in_flags & EF_ARM_INTERWORK) != (out_flags & EF_ARM_INTERWORK))
-       {
-         if (in_flags & EF_ARM_INTERWORK)
-           {
-             _bfd_error_handler
-               (_("Warning: %B supports interworking, whereas %B does not"),
-                ibfd, obfd);
-           }
-         else
-           {
-             _bfd_error_handler
-               (_("Warning: %B does not support interworking, whereas %B does"),
-                ibfd, obfd);
-           }
-       }
-    }
-
-  return flags_compatible;
-}
-
-/* Display the flags field.  */
-
-static bfd_boolean
-elf32_arm_print_private_bfd_data (bfd *abfd, void * ptr)
-{
-  FILE * file = (FILE *) ptr;
-  unsigned long flags;
-
-  BFD_ASSERT (abfd != NULL && ptr != NULL);
-
-  /* Print normal ELF private data.  */
-  _bfd_elf_print_private_bfd_data (abfd, ptr);
-
-  flags = elf_elfheader (abfd)->e_flags;
-  /* Ignore init flag - it may not be set, despite the flags field
-     containing valid data.  */
-
-  /* xgettext:c-format */
-  fprintf (file, _("private flags = %lx:"), elf_elfheader (abfd)->e_flags);
-
-  switch (EF_ARM_EABI_VERSION (flags))
-    {
-    case EF_ARM_EABI_UNKNOWN:
-      /* The following flag bits are GNU extensions and not part of the
-        official ARM ELF extended ABI.  Hence they are only decoded if
-        the EABI version is not set.  */
-      if (flags & EF_ARM_INTERWORK)
-       fprintf (file, _(" [interworking enabled]"));
-
-      if (flags & EF_ARM_APCS_26)
-       fprintf (file, " [APCS-26]");
-      else
-       fprintf (file, " [APCS-32]");
-
-      if (flags & EF_ARM_VFP_FLOAT)
-       fprintf (file, _(" [VFP float format]"));
-      else if (flags & EF_ARM_MAVERICK_FLOAT)
-       fprintf (file, _(" [Maverick float format]"));
-      else
-       fprintf (file, _(" [FPA float format]"));
-
-      if (flags & EF_ARM_APCS_FLOAT)
-       fprintf (file, _(" [floats passed in float registers]"));
-
-      if (flags & EF_ARM_PIC)
-       fprintf (file, _(" [position independent]"));
-
-      if (flags & EF_ARM_NEW_ABI)
-       fprintf (file, _(" [new ABI]"));
-
-      if (flags & EF_ARM_OLD_ABI)
-       fprintf (file, _(" [old ABI]"));
-
-      if (flags & EF_ARM_SOFT_FLOAT)
-       fprintf (file, _(" [software FP]"));
+      if (flags & EF_ARM_SOFT_FLOAT)
+       fprintf (file, _(" [software FP]"));
 
       flags &= ~(EF_ARM_INTERWORK | EF_ARM_APCS_26 | EF_ARM_APCS_FLOAT
                 | EF_ARM_PIC | EF_ARM_NEW_ABI | EF_ARM_OLD_ABI
@@ -9767,6 +10730,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;
 
@@ -9813,7 +10778,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:
@@ -9898,7 +10863,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;
@@ -9909,6 +10873,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
@@ -9921,8 +10888,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);
@@ -9943,7 +10908,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);
@@ -9996,7 +10961,8 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
 
                      size = symtab_hdr->sh_info;
                      size *= (sizeof (bfd_signed_vma) + sizeof (char));
-                     local_got_refcounts = bfd_zalloc (abfd, size);
+                     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;
@@ -10173,15 +11139,19 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
                    /* Track dynamic relocs needed for local syms too.
                       We really need local syms available to do this
                       easily.  Oh well.  */
-
                    asection *s;
                    void *vpp;
+                   Elf_Internal_Sym *isym;
 
-                   s = bfd_section_from_r_symndx (abfd, &htab->sym_sec,
-                                                  sec, r_symndx);
-                   if (s == NULL)
+                   isym = bfd_sym_from_r_symndx (&htab->sym_cache,
+                                                 abfd, r_symndx);
+                   if (isym == NULL)
                      return FALSE;
 
+                   s = bfd_section_from_elf_index (abfd, isym->st_shndx);
+                   if (s == NULL)
+                     s = sec;
+
                    vpp = &elf_section_data (s)->local_dynrel;
                    head = (struct elf32_arm_relocs_copied **) vpp;
                  }
@@ -10191,7 +11161,8 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
                  {
                    bfd_size_type amt = sizeof *p;
 
-                   p = bfd_alloc (htab->root.dynobj, amt);
+                   p = (struct elf32_arm_relocs_copied *)
+                        bfd_alloc (htab->root.dynobj, amt);
                    if (p == NULL)
                      return FALSE;
                    p->next = *head;
@@ -10425,6 +11396,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.  */
@@ -10563,6 +11537,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)
@@ -10792,7 +11768,7 @@ 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;
 
@@ -10913,6 +11889,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;
 }
 
@@ -10930,6 +11909,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);
@@ -10956,7 +11938,7 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
       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;
@@ -10965,7 +11947,8 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
        {
          struct elf32_arm_relocs_copied *p;
 
-         for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next)
+         for (p = (struct elf32_arm_relocs_copied *)
+                   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))
@@ -11119,7 +12102,7 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
        continue;
 
       /* Allocate memory for the section contents.  */
-      s->contents = bfd_zalloc (dynobj, s->size);
+      s->contents = (unsigned char *) bfd_zalloc (dynobj, s->size);
       if (s->contents == NULL)
        return FALSE;
     }
@@ -11203,6 +12186,9 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd,
 
   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)
@@ -11493,20 +12479,23 @@ 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);
+  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");
       BFD_ASSERT (splt != NULL && sdyn != NULL);
 
@@ -11665,7 +12654,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;
@@ -11792,7 +12781,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;
     }
 }
@@ -11890,108 +12879,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 = 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;
@@ -12018,10 +12914,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;
@@ -12029,6 +12923,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;
 }
 
@@ -12043,8 +12938,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;
 
@@ -12057,6 +12950,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)
@@ -12116,10 +13013,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;
@@ -12135,13 +13030,11 @@ 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;
   output_arch_syminfo *osi;
-  const insn_sequence *template;
+  const insn_sequence *template_sequence;
   enum stub_insn_type prev_type;
   int size;
   int i;
@@ -12151,9 +13044,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
@@ -12164,34 +13054,36 @@ arm_map_one_stub (struct bfd_hash_entry * gen_entry,
   addr = (bfd_vma) stub_entry->stub_offset;
   stub_name = stub_entry->output_name;
 
-  template = stub_entry->stub_template;
-  switch (template[0].type)
+  template_sequence = stub_entry->stub_template;
+  switch (template_sequence[0].type)
     {
     case ARM_TYPE:
       if (!elf32_arm_output_stub_sym (osi, stub_name, addr, stub_entry->stub_size))
        return FALSE;
       break;
     case THUMB16_TYPE:
+    case THUMB32_TYPE:
       if (!elf32_arm_output_stub_sym (osi, stub_name, addr | 1,
                                      stub_entry->stub_size))
        return FALSE;
       break;
     default:
       BFD_FAIL ();
-      return FALSE;
+      return 0;
     }
 
   prev_type = DATA_TYPE;
   size = 0;
   for (i = 0; i < stub_entry->stub_template_size; i++)
     {
-      switch (template[i].type)
+      switch (template_sequence[i].type)
        {
        case ARM_TYPE:
          sym_type = ARM_MAP_ARM;
          break;
 
        case THUMB16_TYPE:
+       case THUMB32_TYPE:
          sym_type = ARM_MAP_THUMB;
          break;
 
@@ -12204,16 +13096,17 @@ arm_map_one_stub (struct bfd_hash_entry * gen_entry,
          return FALSE;
        }
 
-      if (template[i].type != prev_type)
+      if (template_sequence[i].type != prev_type)
        {
-         prev_type = template[i].type;
+         prev_type = template_sequence[i].type;
          if (!elf32_arm_output_map_sym (osi, sym_type, addr + size))
            return FALSE;
        }
 
-      switch (template[i].type)
+      switch (template_sequence[i].type)
        {
        case ARM_TYPE:
+       case THUMB32_TYPE:
          size += 4;
          break;
 
@@ -12234,7 +13127,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,
@@ -12249,14 +13144,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)
     {
@@ -12375,14 +13303,12 @@ elf32_arm_new_section_hook (bfd *abfd, asection *sec)
       _arm_elf_section_data *sdata;
       bfd_size_type amt = sizeof (*sdata);
 
-      sdata = bfd_zalloc (abfd, amt);
+      sdata = (_arm_elf_section_data *) bfd_zalloc (abfd, amt);
       if (sdata == NULL)
        return FALSE;
       sec->used_by_bfd = sdata;
     }
 
-  record_section_with_arm_elf_section_data (sec);
-
   return _bfd_elf_new_section_hook (abfd, sec);
 }
 
@@ -12440,6 +13366,120 @@ copy_exidx_entry (bfd *output_bfd, bfd_byte *to, bfd_byte *from, bfd_vma offset)
   bfd_put_32 (output_bfd, second_word, to + 4);
 }
 
+/* Data for make_branch_to_a8_stub().  */
+
+struct a8_branch_to_stub_data {
+  asection *writing_section;
+  bfd_byte *contents;
+};
+
+
+/* Helper to insert branches to Cortex-A8 erratum stubs in the right
+   places for a particular section.  */
+
+static bfd_boolean
+make_branch_to_a8_stub (struct bfd_hash_entry *gen_entry,
+                       void *in_arg)
+{
+  struct elf32_arm_stub_hash_entry *stub_entry;
+  struct a8_branch_to_stub_data *data;
+  bfd_byte *contents;
+  unsigned long branch_insn;
+  bfd_vma veneered_insn_loc, veneer_entry_loc;
+  bfd_signed_vma branch_offset;
+  bfd *abfd;
+  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_lwm)
+    return TRUE;
+
+  contents = data->contents;
+
+  veneered_insn_loc = stub_entry->target_section->output_section->vma
+                     + stub_entry->target_section->output_offset
+                     + stub_entry->target_value;
+
+  veneer_entry_loc = stub_entry->stub_sec->output_section->vma
+                    + stub_entry->stub_sec->output_offset
+                    + stub_entry->stub_offset;
+
+  if (stub_entry->stub_type == arm_stub_a8_veneer_blx)
+    veneered_insn_loc &= ~3u;
+
+  branch_offset = veneer_entry_loc - veneered_insn_loc - 4;
+
+  abfd = stub_entry->target_section->owner;
+  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.
+     This check is just to be on the safe side...  */
+  if ((veneered_insn_loc & ~0xfff) == (veneer_entry_loc & ~0xfff))
+    {
+      (*_bfd_error_handler) (_("%B: error: Cortex-A8 erratum stub is "
+                              "allocated in unsafe location"), abfd);
+      return FALSE;
+    }
+
+  switch (stub_entry->stub_type)
+    {
+    case arm_stub_a8_veneer_b:
+    case arm_stub_a8_veneer_b_cond:
+      branch_insn = 0xf0009000;
+      goto jump24;
+
+    case arm_stub_a8_veneer_blx:
+      branch_insn = 0xf000e800;
+      goto jump24;
+
+    case arm_stub_a8_veneer_bl:
+      {
+       unsigned int i1, j1, i2, j2, s;
+
+       branch_insn = 0xf000d000;
+
+      jump24:
+       if (branch_offset < -16777216 || branch_offset > 16777214)
+         {
+           /* There's not much we can do apart from complain if this
+              happens.  */
+           (*_bfd_error_handler) (_("%B: error: Cortex-A8 erratum stub out "
+                                    "of range (input file too large)"), abfd);
+           return FALSE;
+         }
+
+       /* i1 = not(j1 eor s), so:
+          not i1 = j1 eor s
+          j1 = (not i1) eor s.  */
+
+       branch_insn |= (branch_offset >> 1) & 0x7ff;
+       branch_insn |= ((branch_offset >> 12) & 0x3ff) << 16;
+       i2 = (branch_offset >> 22) & 1;
+       i1 = (branch_offset >> 23) & 1;
+       s = (branch_offset >> 24) & 1;
+       j1 = (!i1) ^ s;
+       j2 = (!i2) ^ s;
+       branch_insn |= j2 << 11;
+       branch_insn |= j1 << 13;
+       branch_insn |= s << 26;
+      }
+      break;
+
+    default:
+      BFD_FAIL ();
+      return FALSE;
+    }
+
+  bfd_put_16 (abfd, (branch_insn >> 16) & 0xffff, &contents[target]);
+  bfd_put_16 (abfd, branch_insn & 0xffff, &contents[target + 2]);
+
+  return TRUE;
+}
+
 /* Do code byteswapping.  Return FALSE afterwards so that the section is
    written out as normal.  */
 
@@ -12449,7 +13489,7 @@ elf32_arm_write_section (bfd *output_bfd,
                         asection *sec,
                         bfd_byte *contents)
 {
-  int mapcount, errcount;
+  unsigned int mapcount, errcount;
   _arm_elf_section_data *arm_data;
   struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info);
   elf32_arm_section_map *map;
@@ -12458,7 +13498,10 @@ elf32_arm_write_section (bfd *output_bfd,
   bfd_vma end;
   bfd_vma offset = sec->output_section->vma + sec->output_offset;
   bfd_byte tmp;
-  int i;
+  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.  */
@@ -12477,7 +13520,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)
             {
@@ -12490,7 +13533,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
@@ -12502,10 +13545,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;
 
@@ -12525,17 +13568,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;
 
@@ -12553,7 +13596,7 @@ elf32_arm_write_section (bfd *output_bfd,
          size (before we merged duplicate entries and inserted EXIDX_CANTUNWIND
         markers) was sec->rawsize.  (This isn't the case if we perform no
         edits, then rawsize will be zero and we should use size).  */
-      bfd_byte *edited_contents = bfd_malloc (sec->size);
+      bfd_byte *edited_contents = (bfd_byte *) bfd_malloc (sec->size);
       unsigned int input_size = sec->rawsize ? sec->rawsize : sec->size;
       unsigned int in_index, out_index;
       bfd_vma add_to_offsets = 0;
@@ -12633,6 +13676,18 @@ elf32_arm_write_section (bfd *output_bfd,
       return TRUE;
     }
 
+  /* Fix code to point to Cortex-A8 erratum stubs.  */
+  if (globals->fix_cortex_a8)
+    {
+      struct a8_branch_to_stub_data data;
+
+      data.writing_section = sec;
+      data.contents = contents;
+
+      bfd_hash_traverse (&globals->stub_hash_table, make_branch_to_a8_stub,
+                        &data);
+    }
+
   if (mapcount == 0)
     return FALSE;
 
@@ -12684,44 +13739,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
@@ -12814,7 +13838,8 @@ elf32_arm_modify_segment_map (bfd *abfd,
        m = m->next;
       if (!m)
        {
-         m = bfd_zalloc (abfd, sizeof (struct elf_segment_map));
+         m = (struct elf_segment_map *)
+              bfd_zalloc (abfd, sizeof (struct elf_segment_map));
          if (m == NULL)
            return FALSE;
          m->p_type = PT_ARM_EXIDX;
@@ -12884,6 +13909,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
@@ -12907,8 +13933,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
@@ -13026,6 +14050,231 @@ elf32_arm_vxworks_final_write_processing (bfd *abfd, bfd_boolean linker)
 #include "elf32-target.h"
 
 
+/* Merge backend specific data from an object file to the output
+   object file when linking.  */
+
+static bfd_boolean
+elf32_arm_merge_private_bfd_data (bfd * ibfd, bfd * obfd)
+{
+  flagword out_flags;
+  flagword in_flags;
+  bfd_boolean flags_compatible = TRUE;
+  asection *sec;
+
+  /* Check if we have the same endianess.  */
+  if (! _bfd_generic_verify_endian_match (ibfd, obfd))
+    return FALSE;
+
+  if (! is_arm_elf (ibfd) || ! is_arm_elf (obfd))
+    return TRUE;
+
+  if (!elf32_arm_merge_eabi_attributes (ibfd, obfd))
+    return FALSE;
+
+  /* The input BFD must have had its flags initialised.  */
+  /* The following seems bogus to me -- The flags are initialized in
+     the assembler but I don't think an elf_flags_init field is
+     written into the object.  */
+  /* BFD_ASSERT (elf_flags_init (ibfd)); */
+
+  in_flags  = elf_elfheader (ibfd)->e_flags;
+  out_flags = elf_elfheader (obfd)->e_flags;
+
+  /* In theory there is no reason why we couldn't handle this.  However
+     in practice it isn't even close to working and there is no real
+     reason to want it.  */
+  if (EF_ARM_EABI_VERSION (in_flags) >= EF_ARM_EABI_VER4
+      && !(ibfd->flags & DYNAMIC)
+      && (in_flags & EF_ARM_BE8))
+    {
+      _bfd_error_handler (_("error: %B is already in final BE8 format"),
+                         ibfd);
+      return FALSE;
+    }
+
+  if (!elf_flags_init (obfd))
+    {
+      /* If the input is the default architecture and had the default
+        flags then do not bother setting the flags for the output
+        architecture, instead allow future merges to do this.  If no
+        future merges ever set these flags then they will retain their
+         uninitialised values, which surprise surprise, correspond
+         to the default values.  */
+      if (bfd_get_arch_info (ibfd)->the_default
+         && elf_elfheader (ibfd)->e_flags == 0)
+       return TRUE;
+
+      elf_flags_init (obfd) = TRUE;
+      elf_elfheader (obfd)->e_flags = in_flags;
+
+      if (bfd_get_arch (obfd) == bfd_get_arch (ibfd)
+         && bfd_get_arch_info (obfd)->the_default)
+       return bfd_set_arch_mach (obfd, bfd_get_arch (ibfd), bfd_get_mach (ibfd));
+
+      return TRUE;
+    }
+
+  /* Determine what should happen if the input ARM architecture
+     does not match the output ARM architecture.  */
+  if (! bfd_arm_merge_machines (ibfd, obfd))
+    return FALSE;
+
+  /* Identical flags must be compatible.  */
+  if (in_flags == out_flags)
+    return TRUE;
+
+  /* Check to see if the input BFD actually contains any sections.  If
+     not, its flags may not have been initialised either, but it
+     cannot actually cause any incompatiblity.  Do not short-circuit
+     dynamic objects; their section list may be emptied by
+    elf_link_add_object_symbols.
+
+    Also check to see if there are no code sections in the input.
+    In this case there is no need to check for code specific flags.
+    XXX - do we need to worry about floating-point format compatability
+    in data sections ?  */
+  if (!(ibfd->flags & DYNAMIC))
+    {
+      bfd_boolean null_input_bfd = TRUE;
+      bfd_boolean only_data_sections = TRUE;
+
+      for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+       {
+         /* Ignore synthetic glue sections.  */
+         if (strcmp (sec->name, ".glue_7")
+             && strcmp (sec->name, ".glue_7t"))
+           {
+             if ((bfd_get_section_flags (ibfd, sec)
+                  & (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
+                 == (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS))
+               only_data_sections = FALSE;
+
+             null_input_bfd = FALSE;
+             break;
+           }
+       }
+
+      if (null_input_bfd || only_data_sections)
+       return TRUE;
+    }
+
+  /* Complain about various flag mismatches.  */
+  if (!elf32_arm_versions_compatible (EF_ARM_EABI_VERSION (in_flags),
+                                     EF_ARM_EABI_VERSION (out_flags)))
+    {
+      _bfd_error_handler
+       (_("error: Source object %B has EABI version %d, but target %B has EABI version %d"),
+        ibfd, obfd,
+        (in_flags & EF_ARM_EABIMASK) >> 24,
+        (out_flags & EF_ARM_EABIMASK) >> 24);
+      return FALSE;
+    }
+
+  /* Not sure what needs to be checked for EABI versions >= 1.  */
+  /* VxWorks libraries do not use these flags.  */
+  if (get_elf_backend_data (obfd) != &elf32_arm_vxworks_bed
+      && get_elf_backend_data (ibfd) != &elf32_arm_vxworks_bed
+      && EF_ARM_EABI_VERSION (in_flags) == EF_ARM_EABI_UNKNOWN)
+    {
+      if ((in_flags & EF_ARM_APCS_26) != (out_flags & EF_ARM_APCS_26))
+       {
+         _bfd_error_handler
+           (_("error: %B is compiled for APCS-%d, whereas target %B uses APCS-%d"),
+            ibfd, obfd,
+            in_flags & EF_ARM_APCS_26 ? 26 : 32,
+            out_flags & EF_ARM_APCS_26 ? 26 : 32);
+         flags_compatible = FALSE;
+       }
+
+      if ((in_flags & EF_ARM_APCS_FLOAT) != (out_flags & EF_ARM_APCS_FLOAT))
+       {
+         if (in_flags & EF_ARM_APCS_FLOAT)
+           _bfd_error_handler
+             (_("error: %B passes floats in float registers, whereas %B passes them in integer registers"),
+              ibfd, obfd);
+         else
+           _bfd_error_handler
+             (_("error: %B passes floats in integer registers, whereas %B passes them in float registers"),
+              ibfd, obfd);
+
+         flags_compatible = FALSE;
+       }
+
+      if ((in_flags & EF_ARM_VFP_FLOAT) != (out_flags & EF_ARM_VFP_FLOAT))
+       {
+         if (in_flags & EF_ARM_VFP_FLOAT)
+           _bfd_error_handler
+             (_("error: %B uses VFP instructions, whereas %B does not"),
+              ibfd, obfd);
+         else
+           _bfd_error_handler
+             (_("error: %B uses FPA instructions, whereas %B does not"),
+              ibfd, obfd);
+
+         flags_compatible = FALSE;
+       }
+
+      if ((in_flags & EF_ARM_MAVERICK_FLOAT) != (out_flags & EF_ARM_MAVERICK_FLOAT))
+       {
+         if (in_flags & EF_ARM_MAVERICK_FLOAT)
+           _bfd_error_handler
+             (_("error: %B uses Maverick instructions, whereas %B does not"),
+              ibfd, obfd);
+         else
+           _bfd_error_handler
+             (_("error: %B does not use Maverick instructions, whereas %B does"),
+              ibfd, obfd);
+
+         flags_compatible = FALSE;
+       }
+
+#ifdef EF_ARM_SOFT_FLOAT
+      if ((in_flags & EF_ARM_SOFT_FLOAT) != (out_flags & EF_ARM_SOFT_FLOAT))
+       {
+         /* We can allow interworking between code that is VFP format
+            layout, and uses either soft float or integer regs for
+            passing floating point arguments and results.  We already
+            know that the APCS_FLOAT flags match; similarly for VFP
+            flags.  */
+         if ((in_flags & EF_ARM_APCS_FLOAT) != 0
+             || (in_flags & EF_ARM_VFP_FLOAT) == 0)
+           {
+             if (in_flags & EF_ARM_SOFT_FLOAT)
+               _bfd_error_handler
+                 (_("error: %B uses software FP, whereas %B uses hardware FP"),
+                  ibfd, obfd);
+             else
+               _bfd_error_handler
+                 (_("error: %B uses hardware FP, whereas %B uses software FP"),
+                  ibfd, obfd);
+
+             flags_compatible = FALSE;
+           }
+       }
+#endif
+
+      /* Interworking mismatch is only a warning.  */
+      if ((in_flags & EF_ARM_INTERWORK) != (out_flags & EF_ARM_INTERWORK))
+       {
+         if (in_flags & EF_ARM_INTERWORK)
+           {
+             _bfd_error_handler
+               (_("Warning: %B supports interworking, whereas %B does not"),
+                ibfd, obfd);
+           }
+         else
+           {
+             _bfd_error_handler
+               (_("Warning: %B does not support interworking, whereas %B does"),
+                ibfd, obfd);
+           }
+       }
+    }
+
+  return flags_compatible;
+}
+
+
 /* Symbian OS Targets.  */
 
 #undef  TARGET_LITTLE_SYM