2009-01-19 Andrew Stubbs <ams@codesourcery.com>
[deliverable/binutils-gdb.git] / bfd / elf32-arm.c
index 2f10f0284b575837f90fb3e8c66f7c7da8021141..a047c5f18b3bddfe9e38805698652074b5a99d6e 100644 (file)
@@ -67,7 +67,7 @@ static struct elf_backend_data elf32_arm_vxworks_bed;
 
 static reloc_howto_type elf32_arm_howto_table_1[] =
 {
-  /* No relocation */
+  /* No relocation */
   HOWTO (R_ARM_NONE,           /* type */
         0,                     /* rightshift */
         0,                     /* size (0 = byte, 1 = short, 2 = long) */
@@ -1347,7 +1347,7 @@ static reloc_howto_type elf32_arm_howto_table_1[] =
         0x040f70ff,            /* dst_mask */
         FALSE),                /* pcrel_offset */
 
-  EMPTY_HOWTO (90),   /* unallocated */
+  EMPTY_HOWTO (90),   /* Unallocated.  */
   EMPTY_HOWTO (91),
   EMPTY_HOWTO (92),
   EMPTY_HOWTO (93),
@@ -1778,6 +1778,7 @@ elf32_arm_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
                             bfd_reloc_code_real_type code)
 {
   unsigned int i;
+
   for (i = 0; i < ARRAY_SIZE (elf32_arm_reloc_map); i ++)
     if (elf32_arm_reloc_map[i].bfd_reloc_val == code)
       return elf32_arm_howto_from_type (elf32_arm_reloc_map[i].elf_reloc_val);
@@ -1817,7 +1818,7 @@ elf32_arm_nabi_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
       default:
        return FALSE;
 
-      case 148:                /* Linux/ARM 32-bit*/
+      case 148:                /* Linux/ARM 32-bit.  */
        /* pr_cursig */
        elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12);
 
@@ -1844,7 +1845,7 @@ elf32_arm_nabi_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
       default:
        return FALSE;
 
-      case 124:                /* Linux/ARM elf_prpsinfo */
+      case 124:                /* Linux/ARM elf_prpsinfo */
        elf_tdata (abfd)->core_program
         = _bfd_elfcore_strndup (abfd, note->descdata + 28, 16);
        elf_tdata (abfd)->core_command
@@ -1898,6 +1899,8 @@ typedef unsigned short int insn16;
 #define ARM_BX_GLUE_SECTION_NAME ".v4_bx"
 #define ARM_BX_GLUE_ENTRY_NAME   "__bx_r%d"
 
+#define STUB_ENTRY_NAME   "__%s_veneer"
+
 /* The name of the dynamic interpreter.  This is put in the .interp
    section.  */
 #define ELF_DYNAMIC_INTERPRETER     "/usr/lib/ld.so.1"
@@ -2024,8 +2027,8 @@ static const bfd_vma arm_thumb_thumb_long_branch_stub[] =
   {
     0x4e02b540,         /* push {r6, lr} */
                         /* ldr  r6, [pc, #8] */
-    0xe7fe46fe,         /* mov  lr, pc */
-                        /* b.n  r6 */
+    0x473046fe,         /* mov  lr, pc */
+                        /* b  r6 */
     0xbf00bd40,         /* pop  {r6, pc} */
                         /* nop */
     0x00000000,         /* dcd  R_ARM_ABS32(X) */
@@ -2042,11 +2045,18 @@ static const bfd_vma arm_thumb_arm_v4t_long_branch_stub[] =
     0x00000000,         /* dcd  R_ARM_ABS32(X) */
   };
 
+static const bfd_vma arm_thumb_arm_v4t_short_branch_stub[] =
+  {
+    0x46c04778,         /* bx   pc */
+                        /* nop   */
+    0xea000000,         /* b    (X) */
+  };
+
 static const bfd_vma arm_pic_long_branch_stub[] =
   {
     0xe59fc000,         /* ldr   r12, [pc] */
     0xe08ff00c,         /* add   pc, pc, ip */
-    0x00000000,         /* dcd   R_ARM_ABS32(X) */
+    0x00000000,         /* dcd   R_ARM_REL32(X) */
   };
 
 /* Section name for stubs is the associated section name plus this
@@ -2060,6 +2070,7 @@ enum elf32_arm_stub_type
   arm_thumb_v4t_stub_long_branch,
   arm_thumb_thumb_stub_long_branch,
   arm_thumb_arm_v4t_stub_long_branch,
+  arm_thumb_arm_v4t_stub_short_branch,
   arm_stub_pic_long_branch,
 };
 
@@ -2090,6 +2101,11 @@ struct elf32_arm_stub_hash_entry
   /* Where this stub is being called from, or, in the case of combined
      stub sections, the first input section in the group.  */
   asection *id_sec;
+
+  /* The name for the local symbol at the start of this stub.  The
+     stub name in the hash table has to be unique; this does not, so
+     it can be friendlier.  */
+  char *output_name;
 };
 
 /* Used to build a map of a section.  This is required for mixed-endian
@@ -2160,6 +2176,9 @@ struct elf_arm_obj_tdata
 
   /* Zero to warn when linking objects with incompatible enum sizes.  */
   int no_enum_size_warning;
+
+  /* Zero to warn when linking objects with incompatible wchar_t sizes.  */
+  int no_wchar_size_warning;
 };
 
 #define elf_arm_tdata(bfd) \
@@ -2233,9 +2252,9 @@ struct elf32_arm_link_hash_entry
        symbols with Arm stubs.  */
     struct elf_link_hash_entry *export_glue;
 
-  /* A pointer to the most recently used stub hash entry against this
-     symbol. */
-  struct elf32_arm_stub_hash_entry *stub_cache;
+   /* A pointer to the most recently used stub hash entry against this
+     symbol.  */
+    struct elf32_arm_stub_hash_entry *stub_cache;
   };
 
 /* Traverse an arm ELF linker hash table.  */
@@ -2447,19 +2466,6 @@ stub_hash_newfunc (struct bfd_hash_entry *entry,
   return entry;
 }
 
-/* Return true if NAME is the name of the relocation section associated
-   with S.  */
-
-static bfd_boolean
-reloc_section_p (struct elf32_arm_link_hash_table *htab,
-                const char *name, asection *s)
-{
-  if (htab->use_rel)
-    return CONST_STRNEQ (name, ".rel") && strcmp (s->name, name + 4) == 0;
-  else
-    return CONST_STRNEQ (name, ".rela") && strcmp (s->name, name + 5) == 0;
-}
-
 /* Create .got, .gotplt, and .rel(a).got sections in DYNOBJ, and set up
    shortcuts to them in our hash table.  */
 
@@ -2662,6 +2668,13 @@ elf32_arm_link_hash_table_create (bfd *abfd)
   ret->sym_sec.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->bfd_count = 0;
+  ret->top_index = 0;
+  ret->input_list = NULL;
 
   if (!bfd_hash_table_init (&ret->stub_hash_table, stub_hash_newfunc,
                            sizeof (struct elf32_arm_stub_hash_entry)))
@@ -2713,6 +2726,24 @@ using_thumb2 (struct elf32_arm_link_hash_table *globals)
   return arch == TAG_CPU_ARCH_V6T2 || arch >= TAG_CPU_ARCH_V7;
 }
 
+static bfd_boolean
+arm_stub_is_thumb (enum elf32_arm_stub_type stub_type)
+{
+  switch (stub_type)
+    {
+    case arm_thumb_thumb_stub_long_branch:
+    case arm_thumb_arm_v4t_stub_long_branch:
+    case arm_thumb_arm_v4t_stub_short_branch:
+      return TRUE;
+    case arm_stub_none:
+      BFD_FAIL ();
+      return FALSE;
+      break;
+    default:
+      return FALSE;
+    }
+}
+
 /* Determine the type of stub needed, if any, for a call.  */
 
 static enum elf32_arm_stub_type
@@ -2721,7 +2752,10 @@ arm_type_of_stub (struct bfd_link_info *info,
                  const Elf_Internal_Rela *rel,
                  unsigned char st_type,
                  struct elf32_arm_link_hash_entry *hash,
-                 bfd_vma destination)
+                 bfd_vma destination,
+                 asection *sym_sec,
+                 bfd *input_bfd,
+                 const char *name)
 {
   bfd_vma location;
   bfd_signed_vma branch_offset;
@@ -2731,6 +2765,11 @@ arm_type_of_stub (struct bfd_link_info *info,
   int thumb_only;
   enum elf32_arm_stub_type stub_type = arm_stub_none;
 
+  /* We don't know the actual type of destination in case it is of
+     type STT_SECTION: give up.  */
+  if (st_type == STT_SECTION)
+    return stub_type;
+
   globals = elf32_arm_hash_table (info);
 
   thumb_only = using_thumb_only (globals);
@@ -2748,7 +2787,7 @@ arm_type_of_stub (struct bfd_link_info *info,
 
   /* If the call will go through a PLT entry then we do not need
      glue.  */
-  if (globals->splt != NULL && hash->root.plt.offset != (bfd_vma) -1)
+  if (globals->splt != NULL && hash != NULL && hash->root.plt.offset != (bfd_vma) -1)
     return stub_type;
 
   if (r_type == R_ARM_THM_CALL)
@@ -2786,6 +2825,16 @@ arm_type_of_stub (struct bfd_link_info *info,
          else
            {
              /* Thumb to arm.  */
+             if (sym_sec != NULL
+                 && sym_sec->owner != NULL
+                 && !INTERWORK_FLAG (sym_sec->owner))
+               {
+                 (*_bfd_error_handler)
+                   (_("%B(%s): warning: interworking not enabled.\n"
+                      "  first occurrence: %B: Thumb call to ARM"),
+                    sym_sec->owner, input_bfd, name);
+               }
+
              stub_type = (info->shared | globals->pic_veneer)
                ? ((globals->use_blx)
                   ? arm_stub_pic_long_branch
@@ -2793,6 +2842,12 @@ arm_type_of_stub (struct bfd_link_info *info,
                : (globals->use_blx)
                ? arm_stub_long_branch
                : arm_thumb_arm_v4t_stub_long_branch;
+
+             /* Handle v4t short branches.  */
+             if ((stub_type == arm_thumb_arm_v4t_stub_long_branch)
+                 && (branch_offset <= THM_MAX_FWD_BRANCH_OFFSET)
+                 && (branch_offset >= THM_MAX_BWD_BRANCH_OFFSET))
+               stub_type = arm_thumb_arm_v4t_stub_short_branch;
            }
        }
     }
@@ -2801,8 +2856,19 @@ arm_type_of_stub (struct bfd_link_info *info,
       if (st_type == STT_ARM_TFUNC)
        {
          /* Arm to thumb.  */
-         /* We have an extra 2-bytes reach because of the mode change
-            (bit 24 (H) of BLX encoding).  */
+
+         if (sym_sec != NULL
+             && sym_sec->owner != NULL
+             && !INTERWORK_FLAG (sym_sec->owner))
+           {
+             (*_bfd_error_handler)
+               (_("%B(%s): warning: interworking not enabled.\n"
+                  "  first occurrence: %B: Thumb call to ARM"),
+                sym_sec->owner, input_bfd, name);
+           }
+
+         /* We have an extra 2-bytes reach because of
+            the mode change (bit 24 (H) of BLX encoding).  */
          if (branch_offset > (ARM_MAX_FWD_BRANCH_OFFSET + 2)
              || (branch_offset < ARM_MAX_BWD_BRANCH_OFFSET)
              || !globals->use_blx)
@@ -2915,33 +2981,13 @@ elf32_arm_get_stub_entry (const asection *input_section,
   return stub_entry;
 }
 
-static void elf32_arm_stub_add_mapping_symbol (struct bfd_link_info * link_info,
-                                              asection *stub_sec,
-                                              char* name,
-                                              bfd_vma val)
-{
-  struct bfd_link_hash_entry * bh = NULL;
-  struct elf_link_hash_entry * myh;
-
-  _bfd_generic_link_add_one_symbol (link_info,
-                                   stub_sec->owner, name,
-                                   BSF_LOCAL, stub_sec, stub_sec->size + val,
-                                   NULL, TRUE, FALSE, &bh);
-
-  myh = (struct elf_link_hash_entry *) bh;
-  myh->type = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE);
-  myh->forced_local = 1;
-}
-
 /* 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,
-                   struct bfd_link_info * link_info,
-                   enum elf32_arm_stub_type stub_type)
+                   struct elf32_arm_link_hash_table *htab)
 {
   asection *link_sec;
   asection *stub_sec;
@@ -2989,33 +3035,6 @@ elf32_arm_add_stub (const char *stub_name,
   stub_entry->stub_offset = 0;
   stub_entry->id_sec = link_sec;
 
-  switch (stub_type)
-    {
-    case arm_stub_long_branch:
-      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$a", 0);
-      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$d", 4);
-      break;
-    case arm_thumb_v4t_stub_long_branch:
-      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$a", 0);
-      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$d", 8);
-      break;
-    case arm_thumb_thumb_stub_long_branch:
-      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$t", 0);
-      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$d", 12);
-      break;
-    case arm_thumb_arm_v4t_stub_long_branch:
-      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$t", 0);
-      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$a", 8);
-      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$d", 16);
-      break;
-    case arm_stub_pic_long_branch:
-      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$a", 0);
-      elf32_arm_stub_add_mapping_symbol (link_info, stub_sec, "$d", 8);
-      break;
-    default:
-      BFD_FAIL ();
-  }
-
   return stub_entry;
 }
 
@@ -3023,8 +3042,8 @@ elf32_arm_add_stub (const char *stub_name,
    elf32_arm_write_section.  */
 
 static void
-put_arm_insn (struct elf32_arm_link_hash_table *htab,
-            bfd * output_bfd, bfd_vma val, void * ptr)
+put_arm_insn (struct elf32_arm_link_hash_table * htab,
+             bfd * output_bfd, bfd_vma val, void * ptr)
 {
   if (htab->byteswap_code != bfd_little_endian (output_bfd))
     bfd_putl32 (val, ptr);
@@ -3036,8 +3055,8 @@ put_arm_insn (struct elf32_arm_link_hash_table *htab,
    elf32_arm_write_section.  */
 
 static void
-put_thumb_insn (struct elf32_arm_link_hash_table *htab,
-              bfd * output_bfd, bfd_vma val, void * ptr)
+put_thumb_insn (struct elf32_arm_link_hash_table * htab,
+               bfd * output_bfd, bfd_vma val, void * ptr)
 {
   if (htab->byteswap_code != bfd_little_endian (output_bfd))
     bfd_putl16 (val, ptr);
@@ -3105,6 +3124,10 @@ arm_build_one_stub (struct bfd_hash_entry *gen_entry,
       template =  arm_thumb_arm_v4t_long_branch_stub;
       template_size = (sizeof (arm_thumb_arm_v4t_long_branch_stub) / sizeof (bfd_vma)) * 4;
       break;
+    case arm_thumb_arm_v4t_stub_short_branch:
+      template =  arm_thumb_arm_v4t_short_branch_stub;
+      template_size = (sizeof(arm_thumb_arm_v4t_short_branch_stub) / sizeof (bfd_vma)) * 4;
+      break;
     case arm_stub_pic_long_branch:
       template = arm_pic_long_branch_stub;
       template_size = (sizeof (arm_pic_long_branch_stub) / sizeof (bfd_vma)) * 4;
@@ -3136,32 +3159,43 @@ arm_build_one_stub (struct bfd_hash_entry *gen_entry,
     {
     case arm_stub_long_branch:
       _bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_ABS32),
-                               stub_bfd, stub_sec, stub_sec->contents + 4,
-                               stub_entry->stub_offset, sym_value, 0);
+                               stub_bfd, stub_sec, stub_sec->contents,
+                               stub_entry->stub_offset + 4, sym_value, 0);
       break;
     case arm_thumb_v4t_stub_long_branch:
       _bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_ABS32),
-                               stub_bfd, stub_sec, stub_sec->contents + 8,
-                               stub_entry->stub_offset, sym_value, 0);
+                               stub_bfd, stub_sec, stub_sec->contents,
+                               stub_entry->stub_offset + 8, sym_value, 0);
       break;
     case arm_thumb_thumb_stub_long_branch:
       _bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_ABS32),
-                               stub_bfd, stub_sec, stub_sec->contents + 12,
-                               stub_entry->stub_offset, sym_value, 0);
+                               stub_bfd, stub_sec, stub_sec->contents,
+                               stub_entry->stub_offset + 12, sym_value, 0);
       break;
     case arm_thumb_arm_v4t_stub_long_branch:
       _bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_ABS32),
-                               stub_bfd, stub_sec, stub_sec->contents + 20,
-                               stub_entry->stub_offset, sym_value, 0);
+                               stub_bfd, stub_sec, stub_sec->contents,
+                               stub_entry->stub_offset + 16, sym_value, 0);
       break;
+    case arm_thumb_arm_v4t_stub_short_branch:
+      {
+       long int rel_offset;
+       static const insn32 t2a3_b_insn = 0xea000000;
+
+       rel_offset = sym_value - (stub_addr + 8 + 4);
+
+       put_arm_insn (globals, stub_bfd,
+                     (bfd_vma) t2a3_b_insn | ((rel_offset >> 2) & 0x00FFFFFF),
+                     loc + 4);
+      }
+      break;
+
     case arm_stub_pic_long_branch:
       /* We want the value relative to the address 8 bytes from the
         start of the stub.  */
-      sym_value -= stub_addr + 8;
-
-      _bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_ABS32),
-                               stub_bfd, stub_sec, stub_sec->contents + 8,
-                               stub_entry->stub_offset, sym_value, 0);
+      _bfd_final_link_relocate (elf32_arm_howto_from_type (R_ARM_REL32),
+                               stub_bfd, stub_sec, stub_sec->contents,
+                               stub_entry->stub_offset + 8, sym_value, 0);
       break;
     default:
       break;
@@ -3206,6 +3240,10 @@ arm_size_one_stub (struct bfd_hash_entry *gen_entry,
       template =  arm_thumb_arm_v4t_long_branch_stub;
       template_size = (sizeof (arm_thumb_arm_v4t_long_branch_stub) / sizeof (bfd_vma)) * 4;
       break;
+    case arm_thumb_arm_v4t_stub_short_branch:
+      template =  arm_thumb_arm_v4t_short_branch_stub;
+      template_size = (sizeof(arm_thumb_arm_v4t_short_branch_stub) / sizeof (bfd_vma)) * 4;
+      break;
     case arm_stub_pic_long_branch:
       template = arm_pic_long_branch_stub;
       template_size = (sizeof (arm_pic_long_branch_stub) / sizeof (bfd_vma)) * 4;
@@ -3509,6 +3547,7 @@ elf32_arm_size_stubs (bfd *output_bfd,
                  bfd_vma sym_value;
                  bfd_vma destination;
                  struct elf32_arm_link_hash_entry *hash;
+                 const char *sym_name;
                  char *stub_name;
                  const asection *id_sec;
                  unsigned char st_type;
@@ -3536,6 +3575,7 @@ elf32_arm_size_stubs (bfd *output_bfd,
                  sym_value = 0;
                  destination = 0;
                  hash = NULL;
+                 sym_name = NULL;
                  if (r_indx < symtab_hdr->sh_info)
                    {
                      /* It's a local symbol.  */
@@ -3564,6 +3604,10 @@ elf32_arm_size_stubs (bfd *output_bfd,
                                     + sym_sec->output_offset
                                     + sym_sec->output_section->vma);
                      st_type = ELF_ST_TYPE (sym->st_info);
+                     sym_name
+                       = bfd_elf_string_from_elf_section (input_bfd,
+                                                          symtab_hdr->sh_link,
+                                                          sym->st_name);
                    }
                  else
                    {
@@ -3601,11 +3645,13 @@ elf32_arm_size_stubs (bfd *output_bfd,
                          goto error_ret_free_internal;
                        }
                      st_type = ELF_ST_TYPE (hash->root.type);
+                     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);
+                                               hash, destination, sym_sec,
+                                               input_bfd, sym_name);
                  if (stub_type == arm_stub_none)
                    continue;
 
@@ -3627,7 +3673,7 @@ elf32_arm_size_stubs (bfd *output_bfd,
                      continue;
                    }
 
-                 stub_entry = elf32_arm_add_stub (stub_name, section, htab, info, stub_type);
+                 stub_entry = elf32_arm_add_stub (stub_name, section, htab);
                  if (stub_entry == NULL)
                    {
                      free (stub_name);
@@ -3639,6 +3685,33 @@ elf32_arm_size_stubs (bfd *output_bfd,
                  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)
+                   {
+                     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
+                     && 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
+                          && 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;
                }
 
@@ -3692,7 +3765,7 @@ elf32_arm_build_stubs (struct bfd_link_info *info)
     {
       bfd_size_type size;
 
-      /* Ignore non-stub sections */
+      /* Ignore non-stub sections */
       if (!strstr (stub_sec->name, STUB_SUFFIX))
        continue;
 
@@ -3802,8 +3875,7 @@ find_arm_glue (struct bfd_link_info *link_info,
    add r12, r12, pc
    bx  r12
    __func_offset:
-   .word func - .
-   */
+   .word func - .   */
 
 #define ARM2THUMB_STATIC_GLUE_SIZE 12
 static const insn32 a2t1_ldr_insn = 0xe59fc000;
@@ -3821,19 +3893,19 @@ static const insn32 a2t3p_bx_r12_insn = 0xe12fff1c;
 
 /* Thumb->ARM:                          Thumb->(non-interworking aware) ARM
 
-   .thumb                               .thumb
-   .align 2                             .align 2
  __func_from_thumb:              __func_from_thumb:
-   bx pc                                push {r6, lr}
-   nop                                  ldr  r6, __func_addr
-   .arm                                         mov  lr, pc
-   __func_change_to_arm:                        bx   r6
-   b func                       .arm
-   __func_back_to_thumb:
-   ldmia r13! {r6, lr}
-   bx    lr
-   __func_addr:
-   .word        func  */
+     .thumb                             .thumb
+     .align 2                           .align 2
__func_from_thumb:                 __func_from_thumb:
+     bx pc                              push {r6, lr}
+     nop                                ldr  r6, __func_addr
+     .arm                               mov  lr, pc
+     b func                             bx   r6
+                                        .arm
+                                   ;; back_to_thumb       
+                                       ldmia r13! {r6, lr}
+                                       bx    lr           
+                                    __func_addr:
+                                        .word        func  */
 
 #define THUMB2ARM_GLUE_SIZE 8
 static const insn16 t2a1_bx_pc_insn = 0x4778;
@@ -3848,78 +3920,50 @@ static const insn32 armbx2_moveq_insn = 0x01a0f000;
 static const insn32 armbx3_bx_insn = 0xe12fff10;
 
 #ifndef ELFARM_NABI_C_INCLUDED
-bfd_boolean
-bfd_elf32_arm_allocate_interworking_sections (struct bfd_link_info * info)
+static void
+arm_allocate_glue_section_space (bfd * abfd, bfd_size_type size, const char * name)
 {
   asection * s;
-  bfd_byte * foo;
-  struct elf32_arm_link_hash_table * globals;
-
-  globals = elf32_arm_hash_table (info);
+  bfd_byte * contents;
 
-  BFD_ASSERT (globals != NULL);
-
-  if (globals->arm_glue_size != 0)
-    {
-      BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
-
-      s = bfd_get_section_by_name (globals->bfd_of_glue_owner,
-                                  ARM2THUMB_GLUE_SECTION_NAME);
-
-      BFD_ASSERT (s != NULL);
-
-      foo = bfd_alloc (globals->bfd_of_glue_owner, globals->arm_glue_size);
-
-      BFD_ASSERT (s->size == globals->arm_glue_size);
-      s->contents = foo;
-    }
-
-  if (globals->thumb_glue_size != 0)
-    {
-      BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
+  if (size == 0)
+    return;
 
-      s = bfd_get_section_by_name
-       (globals->bfd_of_glue_owner, THUMB2ARM_GLUE_SECTION_NAME);
+  BFD_ASSERT (abfd != NULL);
 
-      BFD_ASSERT (s != NULL);
+  s = bfd_get_section_by_name (abfd, name);
+  BFD_ASSERT (s != NULL);
 
-      foo = bfd_alloc (globals->bfd_of_glue_owner, globals->thumb_glue_size);
+  contents = bfd_alloc (abfd, size);
 
-      BFD_ASSERT (s->size == globals->thumb_glue_size);
-      s->contents = foo;
-    }
-
-  if (globals->vfp11_erratum_glue_size != 0)
-    {
-      BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
+  BFD_ASSERT (s->size == size);
+  s->contents = contents;
+}
 
-      s = bfd_get_section_by_name
-        (globals->bfd_of_glue_owner, VFP11_ERRATUM_VENEER_SECTION_NAME);
+bfd_boolean
+bfd_elf32_arm_allocate_interworking_sections (struct bfd_link_info * info)
+{
+  struct elf32_arm_link_hash_table * globals;
 
-      BFD_ASSERT (s != NULL);
+  globals = elf32_arm_hash_table (info);
+  BFD_ASSERT (globals != NULL);
 
-      foo = bfd_alloc (globals->bfd_of_glue_owner,
-                      globals->vfp11_erratum_glue_size);
+  arm_allocate_glue_section_space (globals->bfd_of_glue_owner,
+                                  globals->arm_glue_size,
+                                  ARM2THUMB_GLUE_SECTION_NAME);
 
-      BFD_ASSERT (s->size == globals->vfp11_erratum_glue_size);
-      s->contents = foo;
-    }
+  arm_allocate_glue_section_space (globals->bfd_of_glue_owner,
+                                  globals->thumb_glue_size,
+                                  THUMB2ARM_GLUE_SECTION_NAME);
 
-  if (globals->bx_glue_size != 0)
-    {
-      BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
+  arm_allocate_glue_section_space (globals->bfd_of_glue_owner,
+                                  globals->vfp11_erratum_glue_size,
+                                  VFP11_ERRATUM_VENEER_SECTION_NAME);
 
-      s = bfd_get_section_by_name (globals->bfd_of_glue_owner,
+  arm_allocate_glue_section_space (globals->bfd_of_glue_owner,
+                                  globals->bx_glue_size,
                                   ARM_BX_GLUE_SECTION_NAME);
 
-      BFD_ASSERT (s != NULL);
-
-      foo = bfd_alloc (globals->bfd_of_glue_owner, globals->bx_glue_size);
-
-      BFD_ASSERT (s->size == globals->bx_glue_size);
-      s->contents = foo;
-    }
-
   return TRUE;
 }
 
@@ -3967,7 +4011,8 @@ record_arm_to_thumb_glue (struct bfd_link_info * link_info,
 
   /* The only trick here is using hash_table->arm_glue_size as the value.
      Even though the section isn't allocated yet, this is where we will be
-     putting it.  */
+     putting it.  The +1 on the value marks that the stub has not been
+     output yet - not that it is a Thumb function.  */
   bh = NULL;
   val = globals->arm_glue_size + 1;
   _bfd_generic_link_add_one_symbol (link_info, globals->bfd_of_glue_owner,
@@ -4033,6 +4078,10 @@ record_thumb_to_arm_glue (struct bfd_link_info *link_info,
       return;
     }
 
+  /* The only trick here is using hash_table->thumb_glue_size as the value.
+     Even though the section isn't allocated yet, this is where we will be
+     putting it.  The +1 on the value marks that the stub has not been
+     output yet - not that it is a Thumb function.  */
   bh = NULL;
   val = hash_table->thumb_glue_size + 1;
   _bfd_generic_link_add_one_symbol (link_info, hash_table->bfd_of_glue_owner,
@@ -4067,8 +4116,6 @@ record_thumb_to_arm_glue (struct bfd_link_info *link_info,
 
   s->size += THUMB2ARM_GLUE_SIZE;
   hash_table->thumb_glue_size += THUMB2ARM_GLUE_SIZE;
-
-  return;
 }
 
 
@@ -4282,6 +4329,37 @@ record_vfp11_erratum_veneer (struct bfd_link_info *link_info,
   return val;
 }
 
+/* Note: we do not include the flag SEC_LINKER_CREATED, as that
+   would prevent elf_link_input_bfd() from processing the contents
+   of the section.  */
+#define ARM_GLUE_SECTION_FLAGS \
+  (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_CODE | SEC_READONLY)
+
+/* Create a fake section for use by the ARM backend of the linker.  */
+
+static bfd_boolean
+arm_make_glue_section (bfd * abfd, const char * name)
+{
+  asection * sec;
+
+  sec = bfd_get_section_by_name (abfd, name);
+  if (sec != NULL)
+    /* Already made.  */
+    return TRUE;
+
+  sec = bfd_make_section_with_flags (abfd, name, ARM_GLUE_SECTION_FLAGS);
+
+  if (sec == NULL
+      || !bfd_set_section_alignment (abfd, sec, 2))
+    return FALSE;
+
+  /* Set the gc mark to prevent the section from being removed by garbage
+     collection, despite the fact that no relocs refer to this section.  */
+  sec->gc_mark = 1;
+
+  return TRUE;
+}
+
 /* Add the glue sections to ABFD.  This function is called from the
    linker scripts in ld/emultempl/{armelf}.em.  */
 
@@ -4289,101 +4367,24 @@ bfd_boolean
 bfd_elf32_arm_add_glue_sections_to_bfd (bfd *abfd,
                                        struct bfd_link_info *info)
 {
-  flagword flags;
-  asection *sec;
-
   /* If we are only performing a partial
      link do not bother adding the glue.  */
   if (info->relocatable)
     return TRUE;
 
-  /* linker stubs don't need glue */
+  /* Linker stubs don't need glue.  */
   if (!strcmp (abfd->filename, "linker stubs"))
     return TRUE;
 
-  sec = bfd_get_section_by_name (abfd, ARM2THUMB_GLUE_SECTION_NAME);
-
-  if (sec == NULL)
-    {
-      /* Note: we do not include the flag SEC_LINKER_CREATED, as this
-        will prevent elf_link_input_bfd() from processing the contents
-        of this section.  */
-      flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
-              | SEC_CODE | SEC_READONLY);
-
-      sec = bfd_make_section_with_flags (abfd,
-                                        ARM2THUMB_GLUE_SECTION_NAME,
-                                        flags);
-
-      if (sec == NULL
-         || !bfd_set_section_alignment (abfd, sec, 2))
-       return FALSE;
-
-      /* Set the gc mark to prevent the section from being removed by garbage
-        collection, despite the fact that no relocs refer to this section.  */
-      sec->gc_mark = 1;
-    }
-
-  sec = bfd_get_section_by_name (abfd, THUMB2ARM_GLUE_SECTION_NAME);
-
-  if (sec == NULL)
-    {
-      flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
-              | SEC_CODE | SEC_READONLY);
-
-      sec = bfd_make_section_with_flags (abfd,
-                                        THUMB2ARM_GLUE_SECTION_NAME,
-                                        flags);
-
-      if (sec == NULL
-         || !bfd_set_section_alignment (abfd, sec, 2))
-       return FALSE;
-
-      sec->gc_mark = 1;
-    }
-
-  sec = bfd_get_section_by_name (abfd, VFP11_ERRATUM_VENEER_SECTION_NAME);
-
-  if (sec == NULL)
-    {
-      flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
-              | SEC_CODE | SEC_READONLY);
-
-      sec = bfd_make_section_with_flags (abfd,
-                                        VFP11_ERRATUM_VENEER_SECTION_NAME,
-                                         flags);
-
-      if (sec == NULL
-         || !bfd_set_section_alignment (abfd, sec, 2))
-       return FALSE;
-
-      sec->gc_mark = 1;
-    }
-
-  sec = bfd_get_section_by_name (abfd, ARM_BX_GLUE_SECTION_NAME);
-
-  if (sec == NULL)
-    {
-      flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
-              | SEC_CODE | SEC_READONLY);
-
-      sec = bfd_make_section_with_flags (abfd,
-                                        ARM_BX_GLUE_SECTION_NAME,
-                                         flags);
-
-      if (sec == NULL
-         || !bfd_set_section_alignment (abfd, sec, 2))
-       return FALSE;
-
-      sec->gc_mark = 1;
-    }
-
-  return TRUE;
+  return arm_make_glue_section (abfd, ARM2THUMB_GLUE_SECTION_NAME)
+    && arm_make_glue_section (abfd, THUMB2ARM_GLUE_SECTION_NAME)
+    && arm_make_glue_section (abfd, VFP11_ERRATUM_VENEER_SECTION_NAME)
+    && arm_make_glue_section (abfd, ARM_BX_GLUE_SECTION_NAME);
 }
 
 /* Select a BFD to be used to hold the sections used by the glue code.
    This function is called from the linker scripts in ld/emultempl/
-   {armelf/pe}.em  */
+   {armelf/pe}.em.  */
 
 bfd_boolean
 bfd_elf32_arm_get_bfd_for_interworking (bfd *abfd, struct bfd_link_info *info)
@@ -4607,6 +4608,10 @@ bfd_elf32_arm_init_maps (bfd *abfd)
   Elf_Internal_Shdr *hdr;
   unsigned int i, localsyms;
 
+  /* PR 7093: Make sure that we are dealing with an arm elf binary.  */
+  if (! is_arm_elf (abfd))
+    return;
+
   if ((abfd->flags & DYNAMIC) != 0)
     return;
 
@@ -5245,7 +5250,8 @@ bfd_elf32_arm_set_target_relocs (struct bfd *output_bfd,
                                  int fix_v4bx,
                                 int use_blx,
                                  bfd_arm_vfp11_fix vfp11_fix,
-                                int no_enum_warn, int pic_veneer)
+                                int no_enum_warn, int no_wchar_warn,
+                                int pic_veneer)
 {
   struct elf32_arm_link_hash_table *globals;
 
@@ -5270,6 +5276,7 @@ bfd_elf32_arm_set_target_relocs (struct bfd *output_bfd,
 
   BFD_ASSERT (is_arm_elf (output_bfd));
   elf_arm_tdata (output_bfd)->no_enum_size_warning = no_enum_warn;
+  elf_arm_tdata (output_bfd)->no_wchar_size_warning = no_wchar_warn;
 }
 
 /* Replace the target offset of a Thumb bl or b.w instruction.  */
@@ -5408,8 +5415,8 @@ elf32_arm_create_thumb_stub (struct bfd_link_info * info,
                             bfd *                  output_bfd,
                             asection *             sym_sec,
                             bfd_vma                val,
-                            asection               *s,
-                            char **error_message)
+                            asection *             s,
+                            char **                error_message)
 {
   bfd_vma my_offset;
   long int ret_offset;
@@ -5484,6 +5491,8 @@ elf32_arm_create_thumb_stub (struct bfd_link_info * info,
          /* It's a thumb address.  Add the low order bit.  */
          bfd_put_32 (output_bfd, val | a2t3_func_addr_insn,
                      s->contents + my_offset + 8);
+
+         my_offset += 12;
        }
     }
 
@@ -5586,6 +5595,7 @@ elf32_arm_to_thumb_export_stub (struct elf_link_hash_entry *h, void * inf)
 
   val = eh->export_glue->root.u.def.value + sec->output_offset
        + sec->output_section->vma;
+
   myh = elf32_arm_create_thumb_stub (info, h->root.root.string,
                                     h->root.u.def.section->owner,
                                     globals->obfd, sec, val, s,
@@ -5638,7 +5648,8 @@ elf32_arm_begin_write_processing (bfd *abfd ATTRIBUTE_UNUSED,
 {
   struct elf32_arm_link_hash_table * globals;
 
-  if (!link_info)
+  if (link_info == NULL)
+    /* Ignore this if we are not called by the ELF backend linker.  */
     return;
 
   globals = elf32_arm_hash_table (link_info);
@@ -5653,6 +5664,7 @@ elf32_arm_begin_write_processing (bfd *abfd ATTRIBUTE_UNUSED,
 
 /* Some relocations map to different relocations depending on the
    target.  Return the real relocation.  */
+
 static int
 arm_real_reloc_type (struct elf32_arm_link_hash_table * globals,
                     int r_type)
@@ -5801,7 +5813,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                               int                          sym_flags,
                               struct elf_link_hash_entry * h,
                               bfd_boolean *                unresolved_reloc_p,
-                              char **error_message)
+                              char **                      error_message)
 {
   unsigned long                 r_type = howto->type;
   unsigned long                 r_symndx;
@@ -5939,19 +5951,11 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
 
          if (sreloc == NULL)
            {
-             const char * name;
+             sreloc = _bfd_elf_get_dynamic_reloc_section (input_bfd, input_section,
+                                                          ! globals->use_rel);
 
-             name = (bfd_elf_string_from_elf_section
-                     (input_bfd,
-                      elf_elfheader (input_bfd)->e_shstrndx,
-                      elf_section_data (input_section)->rel_hdr.sh_name));
-             if (name == NULL)
+             if (sreloc == NULL)
                return bfd_reloc_notsupported;
-
-             BFD_ASSERT (reloc_section_p (globals, name, input_section));
-
-             sreloc = bfd_get_section_by_name (dynobj, name);
-             BFD_ASSERT (sreloc != NULL);
            }
 
          skip = FALSE;
@@ -6051,7 +6055,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
        case R_ARM_XPC25:         /* Arm BLX instruction.  */
        case R_ARM_CALL:
        case R_ARM_JUMP24:
-       case R_ARM_PC24:          /* Arm B/BL instruction */
+       case R_ARM_PC24:          /* Arm B/BL instruction */
        case R_ARM_PLT32:
          {
          bfd_vma from;
@@ -6091,7 +6095,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
            }
 
          /* Check if a stub has to be inserted because the
-            destination is too far or we are changing mode */
+            destination is too far or we are changing mode */
          if (r_type == R_ARM_CALL)
            {
              if (branch_offset > ARM_MAX_FWD_BRANCH_OFFSET
@@ -6173,7 +6177,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                {
                  /* Select the correct instruction (BL or BLX).  */
                  /* Only if we are not handling a BL to a stub. In this
-                    case, mode switching is performed by the stub. */
+                    case, mode switching is performed by the stub.  */
                  if (sym_flags == STT_ARM_TFUNC && !stub_entry)
                    value |= (1 << 28);
                  else
@@ -6216,7 +6220,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
          value += signed_addend;
          if (! h || h->root.type != bfd_link_hash_undefweak)
            {
-             /* Check for overflow */
+             /* Check for overflow */
              if ((value ^ (value >> 1)) & (1 << 30))
                return bfd_reloc_overflow;
            }
@@ -6360,8 +6364,9 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
        int thumb2 = using_thumb2 (globals);
 
        /* A branch to an undefined weak symbol is turned into a jump to
-          the next instruction.  */
-       if (h && h->root.type == bfd_link_hash_undefweak)
+          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))
          {
            bfd_put_16 (input_bfd, 0xe000, hit_data);
            bfd_put_16 (input_bfd, 0xbf00, hit_data + 2);
@@ -6413,13 +6418,16 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                    /* Convert BL to BLX.  */
                    lower_insn = (lower_insn & ~0x1000) | 0x0800;
                  }
-               else if (elf32_thumb_to_arm_stub
-                   (info, sym_name, input_bfd, output_bfd, input_section,
-                    hit_data, sym_sec, rel->r_offset, signed_addend, value,
-                    error_message))
-                 return bfd_reloc_ok;
-               else
-                 return bfd_reloc_dangerous;
+               else if (r_type != R_ARM_THM_CALL)
+                 {
+                   if (elf32_thumb_to_arm_stub
+                       (info, sym_name, input_bfd, output_bfd, input_section,
+                        hit_data, sym_sec, rel->r_offset, signed_addend, value,
+                        error_message))
+                     return bfd_reloc_ok;
+                   else
+                     return bfd_reloc_dangerous;
+                 }
              }
            else if (sym_flags == STT_ARM_TFUNC && globals->use_blx
                     && r_type == R_ARM_THM_CALL)
@@ -6450,7 +6458,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
        if (r_type == R_ARM_THM_CALL)
          {
            /* Check if a stub has to be inserted because the destination
-              is too far. */
+              is too far.  */
            bfd_vma from;
            bfd_signed_vma branch_offset;
            struct elf32_arm_stub_hash_entry *stub_entry = NULL;
@@ -6466,7 +6474,8 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                ||
                (thumb2
                 && (branch_offset > THM2_MAX_FWD_BRANCH_OFFSET
-                    || (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET))))
+                    || (branch_offset < THM2_MAX_BWD_BRANCH_OFFSET)))
+               || ((sym_flags != STT_ARM_TFUNC) && !globals->use_blx))
              {
                /* The target is out of reach or we are changing modes, so
                   redirect the branch to the local stub for this
@@ -6479,8 +6488,14 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                           + stub_entry->stub_sec->output_offset
                           + stub_entry->stub_sec->output_section->vma);
 
-               /* This call becomes a call to Arm for sure. Force BLX.  */
-               lower_insn = (lower_insn & ~0x1000) | 0x0800;
+               /* If this call becomes a call to Arm, force BLX.  */
+               if (globals->use_blx)
+                 {
+                   if ((stub_entry
+                        && !arm_stub_is_thumb (stub_entry->stub_type))
+                       || (sym_flags != STT_ARM_TFUNC))
+                     lower_insn = (lower_insn & ~0x1000) | 0x0800;
+                 }
              }
          }
 
@@ -7967,7 +7982,7 @@ elf32_arm_relocate_section (bfd *                  output_bfd,
 
            default:
              error_message = _("unknown error");
-             /* fall through */
+             /* Fall through.  */
 
            common_error:
              BFD_ASSERT (error_message != NULL);
@@ -8124,6 +8139,8 @@ elf32_arm_obj_attrs_arg_type (int tag)
 {
   if (tag == Tag_compatibility)
     return 3;
+  else if (tag == Tag_nodefaults)
+    return 5;
   else if (tag == 4 || tag == 5)
     return 2;
   else if (tag < 32)
@@ -8132,6 +8149,209 @@ elf32_arm_obj_attrs_arg_type (int tag)
     return (tag & 1) != 0 ? 2 : 1;
 }
 
+/* Read the architecture from the Tag_also_compatible_with attribute, if any.
+   Returns -1 if no architecture could be read.  */
+
+static int
+get_secondary_compatible_arch (bfd *abfd)
+{
+  obj_attribute *attr =
+    &elf_known_obj_attributes_proc (abfd)[Tag_also_compatible_with];
+
+  /* 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[0] == Tag_CPU_arch
+      && (attr->s[1] & 128) != 128
+      && attr->s[2] == 0)
+   return attr->s[1];
+
+  /* This tag is "safely ignorable", so don't complain if it looks funny.  */
+  return -1;
+}
+
+/* Set, or unset, the architecture of the Tag_also_compatible_with attribute.
+   The tag is removed if ARCH is -1.  */
+
+static void
+set_secondary_compatible_arch (bfd *abfd, int arch)
+{
+  obj_attribute *attr =
+    &elf_known_obj_attributes_proc (abfd)[Tag_also_compatible_with];
+
+  if (arch == -1)
+    {
+      attr->s = NULL;
+      return;
+    }
+
+  /* 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[0] = Tag_CPU_arch;
+  attr->s[1] = arch;
+  attr->s[2] = '\0';
+}
+
+/* Combine two values for Tag_CPU_arch, taking secondary compatibility tags
+   into account.  */
+
+static int
+tag_cpu_arch_combine (bfd *ibfd, int oldtag, int *secondary_compat_out,
+                     int newtag, int secondary_compat)
+{
+#define T(X) TAG_CPU_ARCH_##X
+  int tagl, tagh, result;
+  const int v6t2[] =
+    {
+      T(V6T2),   /* PRE_V4.  */
+      T(V6T2),   /* V4.  */
+      T(V6T2),   /* V4T.  */
+      T(V6T2),   /* V5T.  */
+      T(V6T2),   /* V5TE.  */
+      T(V6T2),   /* V5TEJ.  */
+      T(V6T2),   /* V6.  */
+      T(V7),     /* V6KZ.  */
+      T(V6T2)    /* V6T2.  */
+    };
+  const int v6k[] =
+    {
+      T(V6K),    /* PRE_V4.  */
+      T(V6K),    /* V4.  */
+      T(V6K),    /* V4T.  */
+      T(V6K),    /* V5T.  */
+      T(V6K),    /* V5TE.  */
+      T(V6K),    /* V5TEJ.  */
+      T(V6K),    /* V6.  */
+      T(V6KZ),   /* V6KZ.  */
+      T(V7),     /* V6T2.  */
+      T(V6K)     /* V6K.  */
+    };
+  const int v7[] =
+    {
+      T(V7),     /* PRE_V4.  */
+      T(V7),     /* V4.  */
+      T(V7),     /* V4T.  */
+      T(V7),     /* V5T.  */
+      T(V7),     /* V5TE.  */
+      T(V7),     /* V5TEJ.  */
+      T(V7),     /* V6.  */
+      T(V7),     /* V6KZ.  */
+      T(V7),     /* V6T2.  */
+      T(V7),     /* V6K.  */
+      T(V7)      /* V7.  */
+    };
+  const int v6_m[] =
+    {
+      -1,        /* PRE_V4.  */
+      -1,        /* V4.  */
+      T(V6K),    /* V4T.  */
+      T(V6K),    /* V5T.  */
+      T(V6K),    /* V5TE.  */
+      T(V6K),    /* V5TEJ.  */
+      T(V6K),    /* V6.  */
+      T(V6KZ),   /* V6KZ.  */
+      T(V7),     /* V6T2.  */
+      T(V6K),    /* V6K.  */
+      T(V7),     /* V7.  */
+      T(V6_M)    /* V6_M.  */
+    };
+  const int v6s_m[] =
+    {
+      -1,        /* PRE_V4.  */
+      -1,        /* V4.  */
+      T(V6K),    /* V4T.  */
+      T(V6K),    /* V5T.  */
+      T(V6K),    /* V5TE.  */
+      T(V6K),    /* V5TEJ.  */
+      T(V6K),    /* V6.  */
+      T(V6KZ),   /* V6KZ.  */
+      T(V7),     /* V6T2.  */
+      T(V6K),    /* V6K.  */
+      T(V7),     /* V7.  */
+      T(V6S_M),  /* V6_M.  */
+      T(V6S_M)   /* V6S_M.  */
+    };
+  const int v4t_plus_v6_m[] =
+    {
+      -1,              /* PRE_V4.  */
+      -1,              /* V4.  */
+      T(V4T),          /* V4T.  */
+      T(V5T),          /* V5T.  */
+      T(V5TE),         /* V5TE.  */
+      T(V5TEJ),                /* V5TEJ.  */
+      T(V6),           /* V6.  */
+      T(V6KZ),         /* V6KZ.  */
+      T(V6T2),         /* V6T2.  */
+      T(V6K),          /* V6K.  */
+      T(V7),           /* V7.  */
+      T(V6_M),         /* V6_M.  */
+      T(V6S_M),                /* V6S_M.  */
+      T(V4T_PLUS_V6_M) /* V4T plus V6_M.  */
+    };
+  const int *comb[] =
+    {
+      v6t2,
+      v6k,
+      v7,
+      v6_m,
+      v6s_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)
+    {
+      _bfd_error_handler (_("ERROR: %B: Unknown CPU architecture"), ibfd);
+      return -1;
+    }
+
+  /* Override old tag if we have a Tag_also_compatible_with on the output.  */
+
+  if ((oldtag == T(V6_M) && *secondary_compat_out == T(V4T))
+      || (oldtag == T(V4T) && *secondary_compat_out == T(V6_M)))
+    oldtag = T(V4T_PLUS_V6_M);
+
+  /* And override the new tag if we have a Tag_also_compatible_with on the
+     input.  */
+
+  if ((newtag == T(V6_M) && secondary_compat == T(V4T))
+      || (newtag == T(V4T) && secondary_compat == T(V6_M)))
+    newtag = T(V4T_PLUS_V6_M);
+
+  tagl = (oldtag < newtag) ? oldtag : newtag;
+  result = tagh = (oldtag > newtag) ? oldtag : newtag;
+
+  /* Architectures before V6KZ add features monotonically.  */
+  if (tagh <= TAG_CPU_ARCH_V6KZ)
+    return result;
+
+  result = comb[tagh - T(V6T2)][tagl];
+
+  /* Use Tag_CPU_arch == V4T and Tag_also_compatible_with (Tag_CPU_arch V6_M)
+     as the canonical version.  */
+  if (result == T(V4T_PLUS_V6_M))
+    {
+      result = T(V4T);
+      *secondary_compat_out = T(V6_M);
+    }
+  else
+    *secondary_compat_out = -1;
+
+  if (result == -1)
+    {
+      _bfd_error_handler (_("ERROR: %B: Conflicting CPU architectures %d/%d"),
+                         ibfd, oldtag, newtag);
+      return -1;
+    }
+
+  return result;
+#undef T
+}
+
 /* Merge EABI object attributes from IBFD into OBFD.  Raise an error if there
    are conflicting attributes.  */
 
@@ -8141,12 +8361,15 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
   obj_attribute *in_attr;
   obj_attribute *out_attr;
   obj_attribute_list *in_list;
+  obj_attribute_list *out_list;
+  obj_attribute_list **out_listp;
   /* Some tags have 0 = don't care, 1 = strong requirement,
      2 = weak requirement.  */
-  static const int order_312[3] = {3, 1, 2};
+  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;
 
   if (!elf_known_obj_attributes_proc (obfd)[0].i)
     {
@@ -8165,7 +8388,7 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
   /* This needs to happen before Tag_ABI_FP_number_model is merged.  */
   if (in_attr[Tag_ABI_VFP_args].i != out_attr[Tag_ABI_VFP_args].i)
     {
-      /* Ignore mismatches if teh object doesn't use floating point.  */
+      /* Ignore mismatches if the object doesn't use floating point.  */
       if (out_attr[Tag_ABI_FP_number_model].i == 0)
        out_attr[Tag_ABI_VFP_args].i = in_attr[Tag_ABI_VFP_args].i;
       else if (in_attr[Tag_ABI_FP_number_model].i != 0)
@@ -8173,7 +8396,7 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
          _bfd_error_handler
            (_("ERROR: %B uses VFP register arguments, %B does not"),
             ibfd, obfd);
-         return FALSE;
+         result = FALSE;
        }
     }
 
@@ -8184,12 +8407,7 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
        {
        case Tag_CPU_raw_name:
        case Tag_CPU_name:
-         /* Use whichever has the greatest architecture requirements.  We
-            won't necessarily have both the above tags, so make sure input
-            name is non-NULL.  */
-         if (in_attr[Tag_CPU_arch].i > out_attr[Tag_CPU_arch].i
-             && in_attr[i].s)
-           out_attr[i].s = _bfd_elf_attr_strdup (obfd, in_attr[i].s);
+         /* These are merged after Tag_CPU_arch. */
          break;
 
        case Tag_ABI_optimization_goals:
@@ -8198,38 +8416,148 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
          break;
 
        case Tag_CPU_arch:
+         {
+           int secondary_compat = -1, secondary_compat_out = -1;
+           unsigned int saved_out_attr = out_attr[i].i;
+           static const char *name_table[] = {
+               /* These aren't real CPU names, but we can't guess
+                  that from the architecture version alone.  */
+               "Pre v4",
+               "ARM v4",
+               "ARM v4T",
+               "ARM v5T",
+               "ARM v5TE",
+               "ARM v5TEJ",
+               "ARM v6",
+               "ARM v6KZ",
+               "ARM v6T2",
+               "ARM v6K",
+               "ARM v7",
+               "ARM v6-M",
+               "ARM v6S-M"
+           };
+
+           /* Merge Tag_CPU_arch and Tag_also_compatible_with.  */
+           secondary_compat = get_secondary_compatible_arch (ibfd);
+           secondary_compat_out = get_secondary_compatible_arch (obfd);
+           out_attr[i].i = tag_cpu_arch_combine (ibfd, out_attr[i].i,
+                                                 &secondary_compat_out,
+                                                 in_attr[i].i,
+                                                 secondary_compat);
+           set_secondary_compatible_arch (obfd, secondary_compat_out);
+
+           /* Merge Tag_CPU_name and Tag_CPU_raw_name.  */
+           if (out_attr[i].i == saved_out_attr)
+             ; /* Leave the names alone.  */
+           else if (out_attr[i].i == in_attr[i].i)
+             {
+               /* The output architecture has been changed to match the
+                  input architecture.  Use the input names.  */
+               out_attr[Tag_CPU_name].s = in_attr[Tag_CPU_name].s
+                 ? _bfd_elf_attr_strdup (obfd, in_attr[Tag_CPU_name].s)
+                 : NULL;
+               out_attr[Tag_CPU_raw_name].s = in_attr[Tag_CPU_raw_name].s
+                 ? _bfd_elf_attr_strdup (obfd, in_attr[Tag_CPU_raw_name].s)
+                 : NULL;
+             }
+           else
+             {
+               out_attr[Tag_CPU_name].s = NULL;
+               out_attr[Tag_CPU_raw_name].s = NULL;
+             }
+
+           /* If we still don't have a value for Tag_CPU_name,
+              make one up now.  Tag_CPU_raw_name remains blank.  */
+           if (out_attr[Tag_CPU_name].s == NULL
+               && out_attr[i].i < ARRAY_SIZE (name_table))
+             out_attr[Tag_CPU_name].s =
+               _bfd_elf_attr_strdup (obfd, name_table[out_attr[i].i]);
+         }
+         break;
+
        case Tag_ARM_ISA_use:
        case Tag_THUMB_ISA_use:
        case Tag_WMMX_arch:
-       case Tag_NEON_arch:
-         /* ??? Do NEON and WMMX conflict?  */
+       case Tag_Advanced_SIMD_arch:
+         /* ??? Do Advanced_SIMD (NEON) and WMMX conflict?  */
        case Tag_ABI_FP_rounding:
-       case Tag_ABI_FP_denormal:
        case Tag_ABI_FP_exceptions:
        case Tag_ABI_FP_user_exceptions:
        case Tag_ABI_FP_number_model:
-       case Tag_ABI_align8_preserved:
-       case Tag_ABI_HardFP_use:
+       case Tag_VFP_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_CPU_arch_profile:
-         /* Warn if conflicting architecture profiles used.  */
-         if (out_attr[i].i && in_attr[i].i && in_attr[i].i != out_attr[i].i)
+       case Tag_ABI_align8_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:
+         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))
            {
+             /* This error message should be enabled once all non-conformant
+                binaries in the toolchain have had the attributes set
+                properly.
              _bfd_error_handler
-               (_("ERROR: %B: Conflicting architecture profiles %c/%c"),
-                ibfd, in_attr[i].i, out_attr[i].i);
-             return FALSE;
+               (_("ERROR: %B: 8-byte data alignment conflicts with %B"),
+                obfd, ibfd);
+             result = FALSE; */
            }
-         if (in_attr[i].i)
+         /* Fall through.  */
+       case Tag_ABI_FP_denormal:
+       case Tag_ABI_PCS_GOT_use:
+         /* Use the "greatest" from the sequence 0, 2, 1, or the largest
+            value if greater than 2 (for future-proofing).  */
+         if ((in_attr[i].i > 2 && in_attr[i].i > out_attr[i].i)
+             || (in_attr[i].i <= 2 && out_attr[i].i <= 2
+                 && order_021[in_attr[i].i] > order_021[out_attr[i].i]))
            out_attr[i].i = in_attr[i].i;
          break;
+
+
+       case Tag_CPU_arch_profile:
+         if (out_attr[i].i != in_attr[i].i)
+           {
+             /* 0 will merge with anything.
+                'A' and 'S' merge to 'A'.
+                'R' and 'S' merge to 'R'.
+                'M' and 'A|R|S' is an error.  */
+             if (out_attr[i].i == 0
+                 || (out_attr[i].i == 'S'
+                     && (in_attr[i].i == 'A' || in_attr[i].i == 'R')))
+               out_attr[i].i = in_attr[i].i;
+             else if (in_attr[i].i == 0
+                      || (in_attr[i].i == 'S'
+                          && (out_attr[i].i == 'A' || out_attr[i].i == 'R')))
+               ; /* Do nothing. */
+             else
+               {
+                 _bfd_error_handler
+                   (_("ERROR: %B: Conflicting architecture profiles %c/%c"),
+                    ibfd,
+                    in_attr[i].i ? in_attr[i].i : '0',
+                    out_attr[i].i ? out_attr[i].i : '0');
+                 result = FALSE;
+               }
+           }
+         break;
        case Tag_VFP_arch:
-         if (in_attr[i].i > 4 || out_attr[i].i > 4
-             || order_01243[in_attr[i].i] > order_01243[out_attr[i].i])
+         /* 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;
          break;
        case Tag_PCS_config:
@@ -8250,7 +8578,7 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
            {
              _bfd_error_handler
                (_("ERROR: %B: Conflicting use of R9"), ibfd);
-             return FALSE;
+             result = FALSE;
            }
          if (out_attr[i].i == AEABI_R9_unused)
            out_attr[i].i = in_attr[i].i;
@@ -8263,36 +8591,21 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
              _bfd_error_handler
                (_("ERROR: %B: SB relative addressing conflicts with use of R9"),
                 ibfd);
-             return FALSE;
+             result = FALSE;
            }
          /* 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_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_PCS_GOT_use:
-         if (in_attr[i].i > 2 || out_attr[i].i > 2
-             || order_312[in_attr[i].i] < order_312[out_attr[i].i])
-           out_attr[i].i = in_attr[i].i;
-         break;
        case Tag_ABI_PCS_wchar_t:
-         if (out_attr[i].i && in_attr[i].i && out_attr[i].i != in_attr[i].i)
+         if (out_attr[i].i && in_attr[i].i && out_attr[i].i != in_attr[i].i
+             && !elf_arm_tdata (obfd)->no_wchar_size_warning)
            {
              _bfd_error_handler
-               (_("ERROR: %B: Conflicting definitions of wchar_t"), ibfd);
-             return FALSE;
+               (_("warning: %B uses %u-byte wchar_t yet the output is to use %u-byte wchar_t; use of wchar_t values across objects may fail"),
+                ibfd, in_attr[i].i, out_attr[i].i);
            }
-         if (in_attr[i].i)
-           out_attr[i].i = in_attr[i].i;
-         break;
-       case Tag_ABI_align8_needed:
-         /* ??? Check against Tag_ABI_align8_preserved.  */
-         if (in_attr[i].i > 2 || out_attr[i].i > 2
-             || order_312[in_attr[i].i] < order_312[out_attr[i].i])
+         else if (in_attr[i].i && !out_attr[i].i)
            out_attr[i].i = in_attr[i].i;
          break;
        case Tag_ABI_enum_size:
@@ -8309,12 +8622,19 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
                       && out_attr[i].i != in_attr[i].i
                       && !elf_arm_tdata (obfd)->no_enum_size_warning)
                {
-                 const char *aeabi_enum_names[] =
+                 static const char *aeabi_enum_names[] =
                    { "", "variable-size", "32-bit", "" };
+                 const char *in_name =
+                   in_attr[i].i < ARRAY_SIZE(aeabi_enum_names)
+                   ? aeabi_enum_names[in_attr[i].i]
+                   : "<unknown>";
+                 const char *out_name =
+                   out_attr[i].i < ARRAY_SIZE(aeabi_enum_names)
+                   ? aeabi_enum_names[out_attr[i].i]
+                   : "<unknown>";
                  _bfd_error_handler
                    (_("warning: %B uses %s enums yet the output is to use %s enums; use of enum values across objects may fail"),
-                    ibfd, aeabi_enum_names[in_attr[i].i],
-                    aeabi_enum_names[out_attr[i].i]);
+                    ibfd, in_name, out_name);
                }
            }
          break;
@@ -8327,29 +8647,97 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
              _bfd_error_handler
                (_("ERROR: %B uses iWMMXt register arguments, %B does not"),
                 ibfd, obfd);
-             return FALSE;
+             result = FALSE;
            }
          break;
-       default: /* All known attributes should be explicitly covered.   */
-         abort ();
-       }
+       case Tag_compatibility:
+         /* 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;
+         break;
+       case Tag_ABI_FP_16bit_format:
+         if (in_attr[i].i != 0 && out_attr[i].i != 0)
+           {
+             if (in_attr[i].i != out_attr[i].i)
+               {
+                 _bfd_error_handler
+                   (_("ERROR: fp16 format mismatch between %B and %B"),
+                    ibfd, obfd);
+                 result = FALSE;
+               }
+           }
+         if (in_attr[i].i != 0)
+           out_attr[i].i = in_attr[i].i;
+         break;
 
-      if (in_attr[i].type && !out_attr[i].type)
-       switch (in_attr[i].type)
+       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 -
+            the merge happens automatically when the type flags are merged
+            below.  */
+         break;
+       case Tag_also_compatible_with:
+         /* Already done in Tag_CPU_arch.  */
+         break;
+       case Tag_conformance:
+         /* Keep the attribute if it matches.  Throw it away otherwise.
+            No attribute means no claim to conform.  */
+         if (!in_attr[i].s || !out_attr[i].s
+             || strcmp (in_attr[i].s, out_attr[i].s) != 0)
+           out_attr[i].s = NULL;
+         break;
+
+       default:
          {
-         case 1:
-           if (out_attr[i].i)
-             out_attr[i].type = 1;
-           break;
+           bfd *err_bfd = NULL;
 
-         case 2:
-           if (out_attr[i].s)
-             out_attr[i].type = 2;
-           break;
+           /* The "known_obj_attributes" table does contain some undefined
+              attributes.  Ensure that there are unused.  */
+           if (out_attr[i].i != 0 || out_attr[i].s != NULL)
+             err_bfd = obfd;
+           else if (in_attr[i].i != 0 || in_attr[i].s != NULL)
+             err_bfd = ibfd;
 
-         default:
-           abort ();
+           if (err_bfd != NULL)
+             {
+               /* Attribute numbers >=64 (mod 128) can be safely ignored.  */
+               if ((i & 127) < 64)
+                 {
+                   _bfd_error_handler
+                     (_("%B: Unknown mandatory EABI object attribute %d"),
+                      err_bfd, i);
+                   bfd_set_error (bfd_error_bad_value);
+                   result = FALSE;
+                 }
+               else
+                 {
+                   _bfd_error_handler
+                     (_("Warning: %B: Unknown EABI object attribute %d"),
+                      err_bfd, i);
+                 }
+             }
+
+           /* Only pass on attributes that match in both inputs.  */
+           if (in_attr[i].i != out_attr[i].i
+               || in_attr[i].s != out_attr[i].s
+               || (in_attr[i].s != NULL && out_attr[i].s != NULL
+                   && strcmp (in_attr[i].s, out_attr[i].s) != 0))
+             {
+               out_attr[i].i = 0;
+               out_attr[i].s = NULL;
+             }
          }
+       }
+
+      /* If out_attr was copied from in_attr then it won't have a type yet.  */
+      if (in_attr[i].type && !out_attr[i].type)
+       out_attr[i].type = in_attr[i].type;
     }
 
   /* Merge Tag_compatibility attributes and any common GNU ones.  */
@@ -8357,20 +8745,78 @@ elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
 
   /* Check for any attributes not known on ARM.  */
   in_list = elf_other_obj_attributes_proc (ibfd);
-  while (in_list && in_list->tag == Tag_compatibility)
-    in_list = in_list->next;
+  out_listp = &elf_other_obj_attributes_proc (obfd);
+  out_list = *out_listp;
 
-  for (; in_list; in_list = in_list->next)
+  for (; in_list || out_list; )
     {
-      if ((in_list->tag & 128) < 64)
+      bfd *err_bfd = NULL;
+      int err_tag = 0;
+
+      /* The tags for each list are in numerical order.  */
+      /* If the tags are equal, then merge.  */
+      if (out_list && (!in_list || in_list->tag > out_list->tag))
        {
-         _bfd_error_handler
-           (_("Warning: %B: Unknown EABI object attribute %d"),
-            ibfd, in_list->tag);
-         break;
+         /* This attribute only exists in obfd.  We can't merge, and we don't
+            know what the tag means, so delete it.  */
+         err_bfd = obfd;
+         err_tag = out_list->tag;
+         *out_listp = out_list->next;
+         out_list = *out_listp;
+       }
+      else if (in_list && (!out_list || in_list->tag < out_list->tag))
+       {
+         /* This attribute only exists in ibfd. We can't merge, and we don't
+            know what the tag means, so ignore it.  */
+         err_bfd = ibfd;
+         err_tag = in_list->tag;
+         in_list = in_list->next;
+       }
+      else /* The tags are equal.  */
+       {
+         /* As present, all attributes in the list are unknown, and
+            therefore can't be merged meaningfully.  */
+         err_bfd = obfd;
+         err_tag = out_list->tag;
+
+         /*  Only pass on attributes that match in both inputs.  */
+         if (in_list->attr.i != out_list->attr.i
+             || in_list->attr.s != out_list->attr.s
+             || (in_list->attr.s && out_list->attr.s
+                 && strcmp (in_list->attr.s, out_list->attr.s) != 0))
+           {
+             /* No match.  Delete the attribute.  */
+             *out_listp = out_list->next;
+             out_list = *out_listp;
+           }
+         else
+           {
+             /* Matched.  Keep the attribute and move to the next.  */
+             out_list = out_list->next;
+             in_list = in_list->next;
+           }
+       }
+
+      if (err_bfd)
+       {
+         /* Attribute numbers >=64 (mod 128) can be safely ignored.  */
+         if ((err_tag & 127) < 64)
+           {
+             _bfd_error_handler
+               (_("%B: Unknown mandatory EABI object attribute %d"),
+                err_bfd, err_tag);
+             bfd_set_error (bfd_error_bad_value);
+             result = FALSE;
+           }
+         else
+           {
+             _bfd_error_handler
+               (_("Warning: %B: Unknown EABI object attribute %d"),
+                err_bfd, err_tag);
+           }
        }
     }
-  return TRUE;
+  return result;
 }
 
 
@@ -8418,6 +8864,18 @@ elf32_arm_merge_private_bfd_data (bfd * ibfd, bfd * obfd)
   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
@@ -9044,12 +9502,12 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
                    elf32_arm_local_got_tls_type (abfd) [r_symndx] = tls_type;
                }
            }
-           /* Fall through */
+           /* Fall through */
 
          case R_ARM_TLS_LDM32:
            if (r_type == R_ARM_TLS_LDM32)
                htab->tls_ldm_got.refcount++;
-           /* Fall through */
+           /* Fall through */
 
          case R_ARM_GOTOFF32:
          case R_ARM_GOTPC:
@@ -9067,7 +9525,7 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
               ldr __GOTT_INDEX__ offsets.  */
            if (!htab->vxworks_p)
              break;
-           /* Fall through */
+           /* Fall through */
 
          case R_ARM_PC24:
          case R_ARM_PLT32:
@@ -9153,40 +9611,23 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
                /* When creating a shared object, we must copy these
                    reloc types into the output file.  We create a reloc
                    section in dynobj and make room for this reloc.  */
-               if (sreloc == NULL)
+               if (sreloc == NULL)
                  {
-                   const char * name;
+                   sreloc = _bfd_elf_make_dynamic_reloc_section
+                     (sec, dynobj, 2, abfd, ! htab->use_rel);
 
-                   name = (bfd_elf_string_from_elf_section
-                           (abfd,
-                            elf_elfheader (abfd)->e_shstrndx,
-                            elf_section_data (sec)->rel_hdr.sh_name));
-                   if (name == NULL)
+                   if (sreloc == NULL)
                      return FALSE;
 
-                   BFD_ASSERT (reloc_section_p (htab, name, sec));
-
-                   sreloc = bfd_get_section_by_name (dynobj, name);
-                   if (sreloc == NULL)
+                   /* BPABI objects never have dynamic relocations mapped.  */
+                   if (! htab->symbian_p)
                      {
-                       flagword flags;
-
-                       flags = (SEC_HAS_CONTENTS | SEC_READONLY
-                                | SEC_IN_MEMORY | SEC_LINKER_CREATED);
-                       if ((sec->flags & SEC_ALLOC) != 0
-                           /* BPABI objects never have dynamic
-                              relocations mapped.  */
-                           && !htab->symbian_p)
-                         flags |= SEC_ALLOC | SEC_LOAD;
-                       sreloc = bfd_make_section_with_flags (dynobj,
-                                                             name,
-                                                             flags);
-                       if (sreloc == NULL
-                           || ! bfd_set_section_alignment (dynobj, sreloc, 2))
-                         return FALSE;
-                     }
+                       flagword flags;
 
-                   elf_section_data (sec)->sreloc = sreloc;
+                       flags = bfd_get_section_flags (dynobj, sreloc);
+                       flags &= ~(SEC_LOAD | SEC_ALLOC);
+                       bfd_set_section_flags (dynobj, sreloc, flags);
+                     }
                  }
 
                /* If this is a global symbol, we count the number of
@@ -9907,10 +10348,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
 /* Find any dynamic relocs that apply to read-only sections.  */
 
 static bfd_boolean
-elf32_arm_readonly_dynrelocs (struct elf_link_hash_entry *h, PTR inf)
+elf32_arm_readonly_dynrelocs (struct elf_link_hash_entry * h, void * inf)
 {
-  struct elf32_arm_link_hash_entry *eh;
-  struct elf32_arm_relocs_copied *p;
+  struct elf32_arm_link_hash_entry * eh;
+  struct elf32_arm_relocs_copied * p;
 
   if (h->root.type == bfd_link_hash_warning)
     h = (struct elf_link_hash_entry *) h->root.u.i.link;
@@ -10195,8 +10636,8 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
       /* If any dynamic relocs apply to a read-only section,
         then we need a DT_TEXTREL entry.  */
       if ((info->flags & DF_TEXTREL) == 0)
-       elf_link_hash_traverse (&htab->root, elf32_arm_readonly_dynrelocs,
-                               (PTR) info);
+       elf_link_hash_traverse (& htab->root, elf32_arm_readonly_dynrelocs,
+                               info);
 
       if ((info->flags & DF_TEXTREL) != 0)
        {
@@ -10625,7 +11066,7 @@ elf32_arm_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info * info
                  bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon);
                  break;
                }
-             /* Fall through */
+             /* Fall through */
 
            case DT_REL:
            case DT_RELA:
@@ -10714,7 +11155,7 @@ elf32_arm_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info * info
                            splt->contents + 8);
              bfd_put_32 (output_bfd, got_address, splt->contents + 12);
 
-             /* Generate a relocation for _GLOBAL_OFFSET_TABLE_. */
+             /* Generate a relocation for _GLOBAL_OFFSET_TABLE_.  */
              rel.r_offset = plt_address + 12;
              rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_ARM_ABS32);
              rel.r_addend = 0;
@@ -11034,12 +11475,12 @@ enum map_symbol_type
 };
 
 
-/* Output a single PLT mapping symbol.  */
+/* Output a single mapping symbol.  */
 
 static bfd_boolean
-elf32_arm_ouput_plt_map_sym (output_arch_syminfo *osi,
-                            enum map_symbol_type type,
-                            bfd_vma offset)
+elf32_arm_output_map_sym (output_arch_syminfo *osi,
+                         enum map_symbol_type type,
+                         bfd_vma offset)
 {
   static const char *names[3] = {"$a", "$t", "$d"};
   struct elf32_arm_link_hash_table *htab;
@@ -11087,20 +11528,20 @@ elf32_arm_output_plt_map (struct elf_link_hash_entry *h, void *inf)
   addr = h->plt.offset;
   if (htab->symbian_p)
     {
-      if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr))
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr))
        return FALSE;
-      if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 4))
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 4))
        return FALSE;
     }
   else if (htab->vxworks_p)
     {
-      if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr))
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr))
        return FALSE;
-      if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 8))
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 8))
        return FALSE;
-      if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr + 12))
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr + 12))
        return FALSE;
-      if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 20))
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 20))
        return FALSE;
     }
   else
@@ -11113,13 +11554,13 @@ elf32_arm_output_plt_map (struct elf_link_hash_entry *h, void *inf)
 
       if (thumb_refs > 0)
        {
-         if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_THUMB, addr - 4))
+         if (!elf32_arm_output_map_sym (osi, ARM_MAP_THUMB, addr - 4))
            return FALSE;
        }
 #ifdef FOUR_WORD_PLT
-      if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr))
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr))
        return FALSE;
-      if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 12))
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 12))
        return FALSE;
 #else
       /* A three-word PLT with no Thumb thunk contains only Arm code,
@@ -11127,7 +11568,7 @@ elf32_arm_output_plt_map (struct elf_link_hash_entry *h, void *inf)
         entries with thumb thunks.  */
       if (thumb_refs > 0 || addr == 20)
        {
-         if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr))
+         if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr))
            return FALSE;
        }
 #endif
@@ -11136,6 +11577,113 @@ elf32_arm_output_plt_map (struct elf_link_hash_entry *h, void *inf)
   return TRUE;
 }
 
+/* Output a single local symbol for a generated stub.  */
+
+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;
+  sym.st_size = size;
+  sym.st_other = 0;
+  sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_FUNC);
+  sym.st_shndx = osi->sec_shndx;
+  if (!osi->func (osi->finfo, name, &sym, osi->sec, NULL))
+    return FALSE;
+  return TRUE;
+}
+
+static bfd_boolean
+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;
+
+  /* Massage our args to the form they really have.  */
+  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
+     processed.  */
+  if (stub_sec != osi->sec)
+    return TRUE;
+
+  addr = (bfd_vma) stub_entry->stub_offset;
+  stub_name = stub_entry->output_name;
+
+  switch (stub_entry->stub_type)
+    {
+    case arm_stub_long_branch:
+      if (!elf32_arm_output_stub_sym (osi, stub_name, addr, 8))
+       return FALSE;
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr))
+       return FALSE;
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 4))
+       return FALSE;
+      break;
+    case arm_thumb_v4t_stub_long_branch:
+      if (!elf32_arm_output_stub_sym (osi, stub_name, addr, 12))
+       return FALSE;
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr))
+       return FALSE;
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 8))
+       return FALSE;
+      break;
+    case arm_thumb_thumb_stub_long_branch:
+      if (!elf32_arm_output_stub_sym (osi, stub_name, addr | 1, 16))
+       return FALSE;
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_THUMB, addr))
+       return FALSE;
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 12))
+       return FALSE;
+      break;
+    case arm_thumb_arm_v4t_stub_long_branch:
+      if (!elf32_arm_output_stub_sym (osi, stub_name, addr | 1, 20))
+       return FALSE;
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_THUMB, addr))
+       return FALSE;
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr + 8))
+       return FALSE;
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 16))
+       return FALSE;
+      break;
+    case arm_thumb_arm_v4t_stub_short_branch:
+      if (!elf32_arm_output_stub_sym (osi, stub_name, addr | 1, 8))
+       return FALSE;
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr + 4))
+       return FALSE;
+      break;
+    case arm_stub_pic_long_branch:
+      if (!elf32_arm_output_stub_sym (osi, stub_name, addr, 12))
+       return FALSE;
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_ARM, addr))
+       return FALSE;
+      if (!elf32_arm_output_map_sym (osi, ARM_MAP_DATA, addr + 8))
+       return FALSE;
+      break;
+    default:
+      BFD_FAIL ();
+    }
+
+  return TRUE;
+}
 
 /* Output mapping symbols for linker generated sections.  */
 
@@ -11178,8 +11726,8 @@ elf32_arm_output_arch_local_syms (bfd *output_bfd,
 
       for (offset = 0; offset < htab->arm_glue_size; offset += size)
        {
-         elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, offset);
-         elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_DATA, offset + size - 4);
+         elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, offset);
+         elf32_arm_output_map_sym (&osi, ARM_MAP_DATA, offset + size - 4);
        }
     }
 
@@ -11195,8 +11743,8 @@ elf32_arm_output_arch_local_syms (bfd *output_bfd,
 
       for (offset = 0; offset < htab->thumb_glue_size; offset += size)
        {
-         elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_THUMB, offset);
-         elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, offset + 4);
+         elf32_arm_output_map_sym (&osi, ARM_MAP_THUMB, offset);
+         elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, offset + 4);
        }
     }
 
@@ -11209,7 +11757,29 @@ elf32_arm_output_arch_local_syms (bfd *output_bfd,
       osi.sec_shndx = _bfd_elf_section_from_bfd_section
          (output_bfd, osi.sec->output_section);
 
-      elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, 0);
+      elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, 0);
+    }
+
+  /* Long calls stubs.  */
+  if (htab->stub_bfd && htab->stub_bfd->sections)
+    {
+      asection* stub_sec;
+
+      for (stub_sec = htab->stub_bfd->sections;
+          stub_sec != NULL;
+          stub_sec = stub_sec->next)
+       {
+         /* Ignore non-stub sections.  */
+         if (!strstr (stub_sec->name, STUB_SUFFIX))
+           continue;
+
+         osi.sec = stub_sec;
+
+         osi.sec_shndx = _bfd_elf_section_from_bfd_section
+           (output_bfd, osi.sec->output_section);
+
+         bfd_hash_traverse (&htab->stub_hash_table, arm_map_one_stub, &osi);
+       }
     }
 
   /* Finally, output mapping symbols for the PLT.  */
@@ -11217,7 +11787,7 @@ elf32_arm_output_arch_local_syms (bfd *output_bfd,
     return TRUE;
 
   osi.sec_shndx = _bfd_elf_section_from_bfd_section (output_bfd,
-      htab->splt->output_section);
+                                                    htab->splt->output_section);
   osi.sec = htab->splt;
   /* Output mapping symbols for the plt header.  SymbianOS does not have a
      plt header.  */
@@ -11226,18 +11796,18 @@ elf32_arm_output_arch_local_syms (bfd *output_bfd,
       /* VxWorks shared libraries have no PLT header.  */
       if (!info->shared)
        {
-         if (!elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, 0))
+         if (!elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, 0))
            return FALSE;
-         if (!elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_DATA, 12))
+         if (!elf32_arm_output_map_sym (&osi, ARM_MAP_DATA, 12))
            return FALSE;
        }
     }
   else if (!htab->symbian_p)
     {
-      if (!elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, 0))
+      if (!elf32_arm_output_map_sym (&osi, ARM_MAP_ARM, 0))
        return FALSE;
 #ifndef FOUR_WORD_PLT
-      if (!elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_DATA, 16))
+      if (!elf32_arm_output_map_sym (&osi, ARM_MAP_DATA, 16))
        return FALSE;
 #endif
     }
@@ -11297,7 +11867,8 @@ elf32_arm_compare_mapping (const void * a, const void * b)
 
 static bfd_boolean
 elf32_arm_write_section (bfd *output_bfd,
-                        struct bfd_link_info *link_info, asection *sec,
+                        struct bfd_link_info *link_info,
+                        asection *sec,
                         bfd_byte *contents)
 {
   int mapcount, errcount;
@@ -11511,7 +12082,7 @@ elf32_arm_swap_symbol_in (bfd * abfd,
 
   /* New EABI objects mark thumb function symbols by setting the low bit of
      the address.  Turn these into STT_ARM_TFUNC.  */
-  if (ELF_ST_TYPE (dst->st_info) == STT_FUNC
+  if ((ELF_ST_TYPE (dst->st_info) == STT_FUNC)
       && (dst->st_value & 1))
     {
       dst->st_info = ELF_ST_INFO (ELF_ST_BIND (dst->st_info), STT_ARM_TFUNC);
@@ -11901,7 +12472,7 @@ elf32_arm_symbian_plt_sym_val (bfd_vma i, const asection *plt,
 }
 
 
-#undef elf32_bed
+#undef  elf32_bed
 #define elf32_bed elf32_arm_symbian_bed
 
 /* The dynamic sections are not allocated on SymbianOS; the postlinker
This page took 0.053343 seconds and 4 git commands to generate.