* elf-m10300.c (_bfd_mn10300_elf_finish_dynamic_symbol): Use the
[deliverable/binutils-gdb.git] / bfd / elf32-arm.c
index 162b1be4d5d510455692f50f735ac0537b8a617d..ef19e6efb07d63954866d6ceaf3d65fd2cd783eb 100644 (file)
@@ -1,5 +1,5 @@
 /* 32-bit ELF support for ARM
-   Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
+   Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
    Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
 
-#include "elf/arm.h"
 #include "bfd.h"
 #include "sysdep.h"
 #include "libbfd.h"
 #include "elf-bfd.h"
+#include "elf/arm.h"
 
 #ifndef NUM_ELEM
 #define NUM_ELEM(a)  (sizeof (a) / (sizeof (a)[0]))
@@ -435,13 +435,13 @@ static reloc_howto_type elf32_arm_howto_table_1[] =
   HOWTO (R_ARM_PLT32,          /* type */
          2,                     /* rightshift */
          2,                     /* size (0 = byte, 1 = short, 2 = long) */
-         26,                    /* bitsize */
+         24,                    /* bitsize */
          TRUE,                 /* pc_relative */
          0,                     /* bitpos */
          complain_overflow_bitfield,/* complain_on_overflow */
          bfd_elf_generic_reloc, /* special_function */
          "R_ARM_PLT32",                /* name */
-         TRUE,                 /* partial_inplace */
+         FALSE,                        /* partial_inplace */
          0x00ffffff,           /* src_mask */
          0x00ffffff,           /* dst_mask */
          TRUE),                        /* pcrel_offset */
@@ -1297,6 +1297,8 @@ static const struct elf32_arm_reloc_map elf32_arm_reloc_map[] =
   {
     {BFD_RELOC_NONE,                 R_ARM_NONE},
     {BFD_RELOC_ARM_PCREL_BRANCH,     R_ARM_PC24},
+    {BFD_RELOC_ARM_PCREL_CALL,      R_ARM_CALL},
+    {BFD_RELOC_ARM_PCREL_JUMP,      R_ARM_JUMP24},
     {BFD_RELOC_ARM_PCREL_BLX,        R_ARM_XPC25},
     {BFD_RELOC_THUMB_PCREL_BLX,      R_ARM_THM_XPC22},
     {BFD_RELOC_32,                   R_ARM_ABS32},
@@ -1515,25 +1517,45 @@ typedef struct elf32_elf_section_map
 }
 elf32_arm_section_map;
 
-struct _arm_elf_section_data
+typedef struct _arm_elf_section_data
 {
   struct bfd_elf_section_data elf;
-  int mapcount;
+  unsigned int mapcount;
   elf32_arm_section_map *map;
-};
+}
+_arm_elf_section_data;
 
 #define elf32_arm_section_data(sec) \
-  ((struct _arm_elf_section_data *) elf_section_data (sec))
+  ((_arm_elf_section_data *) elf_section_data (sec))
 
 /* The size of the thread control block.  */
 #define TCB_SIZE       8
 
+#define NUM_KNOWN_ATTRIBUTES 32
+
+typedef struct aeabi_attribute
+{
+  int type;
+  unsigned int i;
+  char *s;
+} aeabi_attribute;
+
+typedef struct aeabi_attribute_list
+{
+  struct aeabi_attribute_list *next;
+  int tag;
+  aeabi_attribute attr;
+} aeabi_attribute_list;
+
 struct elf32_arm_obj_tdata
 {
   struct elf_obj_tdata root;
 
   /* tls_type for each local got entry.  */
   char *local_got_tls_type;
+
+  aeabi_attribute known_eabi_attributes[NUM_KNOWN_ATTRIBUTES];
+  aeabi_attribute_list *other_eabi_attributes;
 };
 
 #define elf32_arm_tdata(abfd) \
@@ -1773,7 +1795,7 @@ elf32_arm_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
 /* Copy the extra info we tack onto an elf_link_hash_entry.  */
 
 static void
-elf32_arm_copy_indirect_symbol (const struct elf_backend_data *bed,
+elf32_arm_copy_indirect_symbol (struct bfd_link_info *info,
                                struct elf_link_hash_entry *dir,
                                struct elf_link_hash_entry *ind)
 {
@@ -1789,10 +1811,7 @@ elf32_arm_copy_indirect_symbol (const struct elf_backend_data *bed,
          struct elf32_arm_relocs_copied **pp;
          struct elf32_arm_relocs_copied *p;
 
-         if (ind->root.type == bfd_link_hash_indirect)
-           abort ();
-
-         /* Add reloc counts against the weak sym to the strong sym
+         /* Add reloc counts against the indirect sym to the direct sym
             list.  Merge any entries against the same section.  */
          for (pp = &eind->relocs_copied; (p = *pp) != NULL; )
            {
@@ -1816,16 +1835,9 @@ elf32_arm_copy_indirect_symbol (const struct elf_backend_data *bed,
       eind->relocs_copied = NULL;
     }
 
-  /* If the direct symbol already has an associated PLT entry, the
-     indirect symbol should not.  If it doesn't, swap refcount information
-     from the indirect symbol.  */
-  if (edir->plt_thumb_refcount == 0)
-    {
-      edir->plt_thumb_refcount = eind->plt_thumb_refcount;
-      eind->plt_thumb_refcount = 0;
-    }
-  else
-    BFD_ASSERT (eind->plt_thumb_refcount == 0);
+  /* Copy over PLT info.  */
+  edir->plt_thumb_refcount += eind->plt_thumb_refcount;
+  eind->plt_thumb_refcount = 0;
 
   if (ind->root.type == bfd_link_hash_indirect
       && dir->got.refcount <= 0)
@@ -1834,7 +1846,7 @@ elf32_arm_copy_indirect_symbol (const struct elf_backend_data *bed,
       eind->tls_type = GOT_UNKNOWN;
     }
 
-  _bfd_elf_link_hash_copy_indirect (bed, dir, ind);
+  _bfd_elf_link_hash_copy_indirect (info, dir, ind);
 }
 
 /* Create an ARM elf linker hash table.  */
@@ -2275,6 +2287,12 @@ bfd_elf32_arm_get_bfd_for_interworking (bfd *abfd, struct bfd_link_info *info)
   return TRUE;
 }
 
+static void check_use_blx(struct elf32_arm_link_hash_table *globals)
+{
+  if (elf32_arm_get_eabi_attr_int (globals->obfd, Tag_CPU_arch) > 2)
+    globals->use_blx = 1;
+}
+
 bfd_boolean
 bfd_elf32_arm_process_before_allocation (bfd *abfd,
                                         struct bfd_link_info *link_info,
@@ -2296,6 +2314,7 @@ 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);
+  check_use_blx (globals);
 
   BFD_ASSERT (globals != NULL);
   BFD_ASSERT (globals->bfd_of_glue_owner != NULL);
@@ -2393,7 +2412,8 @@ bfd_elf32_arm_process_before_allocation (bfd *abfd,
              /* This one is a call from arm code.  We need to look up
                 the target of the call.  If it is a thumb target, we
                 insert glue.  */
-             if (ELF_ST_TYPE(h->type) == STT_ARM_TFUNC)
+             if (ELF_ST_TYPE(h->type) == STT_ARM_TFUNC
+                 && !(r_type == R_ARM_CALL && globals->use_blx))
                record_arm_to_thumb_glue (link_info, h);
              break;
 
@@ -2401,7 +2421,7 @@ bfd_elf32_arm_process_before_allocation (bfd *abfd,
              /* This one is a call from thumb code.  We look
                 up the target of the call.  If it is not a thumb
                  target, we insert glue.  */
-             if (ELF_ST_TYPE (h->type) != STT_ARM_TFUNC)
+             if (ELF_ST_TYPE (h->type) != STT_ARM_TFUNC && !globals->use_blx)
                record_thumb_to_arm_glue (link_info, h);
              break;
 
@@ -3035,7 +3055,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                   input_bfd,
                   h ? h->root.root.string : "(local)");
            }
-         else
+         else if (r_type != R_ARM_CALL || !globals->use_blx)
            {
              /* Check for Arm calling Thumb function.  */
              if (sym_flags == STT_ARM_TFUNC)
@@ -3091,14 +3111,30 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                return bfd_reloc_overflow;
            }
 
-         /* If necessary set the H bit in the BLX instruction.  */
-         if (r_type == R_ARM_XPC25 && ((value & 2) == 2))
-           value = (signed_addend & howto->dst_mask)
-             | (bfd_get_32 (input_bfd, hit_data) & (~ howto->dst_mask))
-             | (1 << 24);
-         else
-           value = (signed_addend & howto->dst_mask)
-             | (bfd_get_32 (input_bfd, hit_data) & (~ howto->dst_mask));
+         addend = (value & 2);
+
+         value = (signed_addend & howto->dst_mask)
+           | (bfd_get_32 (input_bfd, hit_data) & (~ howto->dst_mask));
+
+         /* Set the H bit in the BLX instruction.  */
+         if (sym_flags == STT_ARM_TFUNC)
+           {
+             if (addend)
+               value |= (1 << 24);
+             else
+               value &= ~(bfd_vma)(1 << 24);
+           }
+         if (r_type == R_ARM_CALL)
+           {
+             /* Select the correct instruction (BL or BLX).  */
+             if (sym_flags == STT_ARM_TFUNC)
+               value |= (1 << 28);
+             else
+               {
+                 value &= ~(bfd_vma)(1 << 28);
+                 value |= (1 << 24);
+               }
+           }
          break;
 
        case R_ARM_ABS32:
@@ -3194,7 +3230,6 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
        bfd_signed_vma reloc_signed_min = ~ reloc_signed_max;
        bfd_vma check;
        bfd_signed_vma signed_check;
-       bfd_boolean thumb_plt_call = FALSE;
 
        /* Need to refetch the addend and squish the two 11 bit pieces
           together.  */
@@ -3228,13 +3263,23 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
                && (h == NULL || splt == NULL
                    || h->plt.offset == (bfd_vma) -1))
              {
-               if (elf32_thumb_to_arm_stub
+               if (globals->use_blx)
+                 {
+                   /* 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))
                  return bfd_reloc_ok;
                else
                  return bfd_reloc_dangerous;
              }
+           else if (sym_flags == STT_ARM_TFUNC && globals->use_blx)
+             {
+               /* Make sure this is a BL.  */
+               lower_insn |= 0x1800;
+             }
          }
 
        /* Handle calls via the PLT.  */
@@ -3247,11 +3292,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
              {
                /* If the Thumb BLX instruction is available, convert the
                   BL to a BLX instruction to call the ARM-mode PLT entry.  */
-               if ((lower_insn & (0x3 << 11)) == 0x3 << 11)
-                 {
-                   lower_insn = (lower_insn & ~(0x3 << 11)) | 0x1 << 11;
-                   thumb_plt_call = TRUE;
-                 }
+               lower_insn = (lower_insn & ~0x1000) | 0x0800;
              }
            else
              /* Target the Thumb stub before the ARM PLT entry.  */
@@ -3278,9 +3319,7 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
        if (signed_check > reloc_signed_max || signed_check < reloc_signed_min)
          overflow = TRUE;
 
-       if ((r_type == R_ARM_THM_XPC22
-            && ((lower_insn & 0x1800) == 0x0800))
-           || thumb_plt_call)
+       if ((lower_insn & 0x1800) == 0x0800)
          /* For a BLX instruction, make sure that the relocation is rounded up
             to a word boundary.  This follows the semantics of the instruction
             which specifies that bit 1 of the target address will come from bit
@@ -3908,6 +3947,194 @@ elf32_arm_final_link_relocate (reloc_howto_type *           howto,
     }
 }
 
+
+static int
+uleb128_size (unsigned int i)
+{
+  int size;
+  size = 1;
+  while (i >= 0x80)
+    {
+      i >>= 7;
+      size++;
+    }
+  return size;
+}
+
+/* Return TRUE if the attribute has the default value (0/"").  */
+static bfd_boolean
+is_default_attr (aeabi_attribute *attr)
+{
+  if ((attr->type & 1) && attr->i != 0)
+    return FALSE;
+  if ((attr->type & 2) && attr->s && *attr->s)
+    return FALSE;
+
+  return TRUE;
+}
+
+/* Return the size of a single attribute.  */
+static bfd_vma
+eabi_attr_size(int tag, aeabi_attribute *attr)
+{
+  bfd_vma size;
+
+  if (is_default_attr (attr))
+    return 0;
+
+  size = uleb128_size (tag);
+  if (attr->type & 1)
+    size += uleb128_size (attr->i);
+  if (attr->type & 2)
+    size += strlen ((char *)attr->s) + 1;
+  return size;
+}
+  
+/* Returns the size of the eabi object attributess section.  */
+bfd_vma
+elf32_arm_eabi_attr_size (bfd *abfd)
+{
+  bfd_vma size;
+  aeabi_attribute *attr;
+  aeabi_attribute_list *list;
+  int i;
+
+  attr = elf32_arm_tdata (abfd)->known_eabi_attributes;
+  size = 16; /* 'A' <size> "aeabi" 0x1 <size>.  */
+  for (i = 4; i < NUM_KNOWN_ATTRIBUTES; i++)
+    size += eabi_attr_size (i, &attr[i]);
+
+  for (list = elf32_arm_tdata (abfd)->other_eabi_attributes;
+       list;
+       list = list->next)
+    size += eabi_attr_size (list->tag, &list->attr);
+
+  return size;
+}
+
+static bfd_byte *
+write_uleb128 (bfd_byte *p, unsigned int val)
+{
+  bfd_byte c;
+  do
+    {
+      c = val & 0x7f;
+      val >>= 7;
+      if (val)
+       c |= 0x80;
+      *(p++) = c;
+    }
+  while (val);
+  return p;
+}
+
+/* Write attribute ATTR to butter P, and return a pointer to the following
+   byte.  */
+static bfd_byte *
+write_eabi_attribute (bfd_byte *p, int tag, aeabi_attribute *attr)
+{
+  /* Suppress default entries.  */
+  if (is_default_attr(attr))
+    return p;
+
+  p = write_uleb128 (p, tag);
+  if (attr->type & 1)
+    p = write_uleb128 (p, attr->i);
+  if (attr->type & 2)
+    {
+      int len;
+
+      len = strlen (attr->s) + 1;
+      memcpy (p, attr->s, len);
+      p += len;
+    }
+
+  return p;
+}
+
+/* Write the contents of the eabi attributes section to p.  */
+void
+elf32_arm_set_eabi_attr_contents (bfd *abfd, bfd_byte *contents, bfd_vma size)
+{
+  bfd_byte *p;
+  aeabi_attribute *attr;
+  aeabi_attribute_list *list;
+  int i;
+
+  p = contents;
+  *(p++) = 'A';
+  bfd_put_32 (abfd, size - 1, p);
+  p += 4;
+  memcpy (p, "aeabi", 6);
+  p += 6;
+  *(p++) = Tag_File;
+  bfd_put_32 (abfd, size - 11, p);
+  p += 4;
+
+  attr = elf32_arm_tdata (abfd)->known_eabi_attributes;
+  for (i = 4; i < NUM_KNOWN_ATTRIBUTES; i++)
+    p = write_eabi_attribute (p, i, &attr[i]);
+
+  for (list = elf32_arm_tdata (abfd)->other_eabi_attributes;
+       list;
+       list = list->next)
+    p = write_eabi_attribute (p, list->tag, &list->attr);
+}
+
+/* Override final_link to handle EABI object attribute sections.  */
+
+static bfd_boolean
+elf32_arm_bfd_final_link (bfd *abfd, struct bfd_link_info *info)
+{
+  asection *o;
+  struct bfd_link_order *p;
+  asection *attr_section = NULL;
+  bfd_byte *contents;
+  bfd_vma size = 0;
+
+  /* elf32_arm_merge_private_bfd_data will already have merged the
+     object attributes.  Remove the input sections from the link, and set
+     the contents of the output secton.  */
+  for (o = abfd->sections; o != NULL; o = o->next)
+    {
+      if (strcmp (o->name, ".ARM.attributes") == 0)
+       {
+         for (p = o->map_head.link_order; p != NULL; p = p->next)
+           {
+             asection *input_section;
+
+             if (p->type != bfd_indirect_link_order)
+               continue;
+             input_section = p->u.indirect.section;
+             /* Hack: reset the SEC_HAS_CONTENTS flag so that
+                elf_link_input_bfd ignores this section.  */
+             input_section->flags &= ~SEC_HAS_CONTENTS;
+           }
+           
+         size = elf32_arm_eabi_attr_size (abfd);
+         bfd_set_section_size (abfd, o, size);
+         attr_section = o;
+         /* Skip this section later on.  */
+         o->map_head.link_order = NULL;
+       }
+    }
+  /* Invoke the ELF linker to do all the work.  */
+  if (!bfd_elf_final_link (abfd, info))
+    return FALSE;
+
+  if (attr_section)
+    {
+      contents = bfd_malloc(size);
+      if (contents == NULL)
+       return FALSE;
+      elf32_arm_set_eabi_attr_contents (abfd, contents, size);
+      bfd_set_section_contents (abfd, attr_section, contents, 0, size);
+      free (contents);
+    }
+  return TRUE;
+}
+
+
 /* Add INCREMENT to the reloc (of type HOWTO) at ADDRESS.  */
 static void
 arm_add_to_rel (bfd *              abfd,
@@ -4174,9 +4401,12 @@ elf32_arm_relocate_section (bfd *                  output_bfd,
                && h->def_dynamic))
        {
          (*_bfd_error_handler)
-           (_("%B(%A+0x%lx): warning: unresolvable relocation %d against symbol `%s'"),
-            input_bfd, input_section, (long) rel->r_offset,
-            r_type, h->root.root.string);
+           (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
+            input_bfd,
+            input_section,
+            (long) rel->r_offset,
+            howto->name,
+            h->root.root.string);
          return FALSE;
        }
 
@@ -4235,6 +4465,130 @@ elf32_arm_relocate_section (bfd *                  output_bfd,
   return TRUE;
 }
 
+/* Allocate/find an object attribute.  */
+static aeabi_attribute *
+elf32_arm_new_eabi_attr (bfd *abfd, int tag)
+{
+  aeabi_attribute *attr;
+  aeabi_attribute_list *list;
+  aeabi_attribute_list *p;
+  aeabi_attribute_list **lastp;
+
+
+  if (tag < NUM_KNOWN_ATTRIBUTES)
+    {
+      /* Knwon tags are preallocated.  */
+      attr = &elf32_arm_tdata (abfd)->known_eabi_attributes[tag];
+    }
+  else
+    {
+      /* Create a new tag.  */
+      list = (aeabi_attribute_list *)
+       bfd_alloc (abfd, sizeof (aeabi_attribute_list));
+      memset (list, 0, sizeof (aeabi_attribute_list));
+      list->tag = tag;
+      /* Keep the tag list in order.  */
+      lastp = &elf32_arm_tdata (abfd)->other_eabi_attributes;
+      for (p = *lastp; p; p = p->next)
+       {
+         if (tag < p->tag)
+           break;
+         lastp = &p->next;
+       }
+      list->next = *lastp;
+      *lastp = list;
+      attr = &list->attr;
+    }
+
+  return attr;
+}
+
+int
+elf32_arm_get_eabi_attr_int (bfd *abfd, int tag)
+{
+  aeabi_attribute_list *p;
+
+  if (tag < NUM_KNOWN_ATTRIBUTES)
+    {
+      /* Knwon tags are preallocated.  */
+      return elf32_arm_tdata (abfd)->known_eabi_attributes[tag].i;
+    }
+  else
+    {
+      for (p = elf32_arm_tdata (abfd)->other_eabi_attributes;
+          p;
+          p = p->next)
+       {
+         if (tag == p->tag)
+           return p->attr.i;
+         if (tag < p->tag)
+           break;
+       }
+      return 0;
+    }
+}
+
+void
+elf32_arm_add_eabi_attr_int (bfd *abfd, int tag, unsigned int i)
+{
+  aeabi_attribute *attr;
+
+  attr = elf32_arm_new_eabi_attr (abfd, tag);
+  attr->type = 1;
+  attr->i = i;
+}
+
+static char *
+attr_strdup (bfd *abfd, const char * s)
+{
+  char * p;
+  int len;
+  
+  len = strlen (s) + 1;
+  p = (char *)bfd_alloc(abfd, len);
+  return memcpy (p, s, len);
+}
+
+void
+elf32_arm_add_eabi_attr_string (bfd *abfd, int tag, const char *s)
+{
+  aeabi_attribute *attr;
+
+  attr = elf32_arm_new_eabi_attr (abfd, tag);
+  attr->type = 2;
+  attr->s = attr_strdup (abfd, s);
+}
+
+void
+elf32_arm_add_eabi_attr_compat (bfd *abfd, unsigned int i, const char *s)
+{
+  aeabi_attribute_list *list;
+  aeabi_attribute_list *p;
+  aeabi_attribute_list **lastp;
+
+  list = (aeabi_attribute_list *)
+    bfd_alloc (abfd, sizeof (aeabi_attribute_list));
+  memset (list, 0, sizeof (aeabi_attribute_list));
+  list->tag = Tag_compatibility;
+  list->attr.type = 3;
+  list->attr.i = i;
+  list->attr.s = attr_strdup (abfd, s);
+
+  lastp = &elf32_arm_tdata (abfd)->other_eabi_attributes;
+  for (p = *lastp; p; p = p->next)
+    {
+      int cmp;
+      if (p->tag != Tag_compatibility)
+       break;
+      cmp = strcmp(s, p->attr.s);
+      if (cmp < 0 || (cmp == 0 && i < p->attr.i))
+       break;
+      lastp = &p->next;
+    }
+  list->next = *lastp;
+  *lastp = list;
+}
+
 /* Set the right machine number.  */
 
 static bfd_boolean
@@ -4285,6 +4639,49 @@ elf32_arm_set_private_flags (bfd *abfd, flagword flags)
   return TRUE;
 }
 
+/* Copy the eabi object attribute from IBFD to OBFD.  */
+static void
+copy_eabi_attributes (bfd *ibfd, bfd *obfd)
+{
+  aeabi_attribute *in_attr;
+  aeabi_attribute *out_attr;
+  aeabi_attribute_list *list;
+  int i;
+
+  in_attr = elf32_arm_tdata (ibfd)->known_eabi_attributes;
+  out_attr = elf32_arm_tdata (obfd)->known_eabi_attributes;
+  for (i = 4; i < NUM_KNOWN_ATTRIBUTES; i++)
+    {
+      out_attr->i = in_attr->i;
+      if (in_attr->s && *in_attr->s)
+       out_attr->s = attr_strdup (obfd, in_attr->s);
+      in_attr++;
+      out_attr++;
+    }
+
+  for (list = elf32_arm_tdata (ibfd)->other_eabi_attributes;
+       list;
+       list = list->next)
+    {
+      in_attr = &list->attr;
+      switch (in_attr->type)
+       {
+       case 1:
+         elf32_arm_add_eabi_attr_int (obfd, list->tag, in_attr->i);
+         break;
+       case 2:
+         elf32_arm_add_eabi_attr_string (obfd, list->tag, in_attr->s);
+         break;
+       case 3:
+         elf32_arm_add_eabi_attr_compat (obfd, in_attr->i, in_attr->s);
+         break;
+       default:
+         abort();
+       }
+    }
+}
+
+
 /* Copy backend specific data from one object module to another.  */
 
 static bfd_boolean
@@ -4336,6 +4733,289 @@ elf32_arm_copy_private_bfd_data (bfd *ibfd, bfd *obfd)
   elf_elfheader (obfd)->e_ident[EI_OSABI] =
     elf_elfheader (ibfd)->e_ident[EI_OSABI];
 
+  /* Copy EABI object attributes.  */
+  copy_eabi_attributes (ibfd, obfd);
+
+  return TRUE;
+}
+
+/* Values for Tag_ABI_PCS_R9_use.  */
+enum
+{
+  AEABI_R9_V6,
+  AEABI_R9_SB,
+  AEABI_R9_TLS,
+  AEABI_R9_unused
+};
+
+/* Values for Tag_ABI_PCS_RW_data.  */
+enum
+{
+  AEABI_PCS_RW_data_absolute,
+  AEABI_PCS_RW_data_PCrel,
+  AEABI_PCS_RW_data_SBrel,
+  AEABI_PCS_RW_data_unused
+};
+
+/* Values for Tag_ABI_enum_size.  */
+enum
+{
+  AEABI_enum_unused,
+  AEABI_enum_short,
+  AEABI_enum_wide,
+  AEABI_enum_forced_wide
+};
+
+/* Merge EABI object attributes from IBFD into OBFD.  Raise an error if there
+   are conflicting attributes.  */
+static bfd_boolean
+elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd)
+{
+  aeabi_attribute *in_attr;
+  aeabi_attribute *out_attr;
+  aeabi_attribute_list *in_list;
+  aeabi_attribute_list *out_list;
+  /* Some tags have 0 = don't care, 1 = strong requirement,
+     2 = weak requirement.  */
+  static const int order_312[3] = {3, 1, 2};
+  int i;
+
+  if (!elf32_arm_tdata (ibfd)->known_eabi_attributes[0].i)
+    {
+      /* This is the first object.  Copy the attributes.  */
+      copy_eabi_attributes (ibfd, obfd);
+      return TRUE;
+    }
+
+  /* Use the Tag_null value to indicate the attributes have been
+     initialized.  */
+  elf32_arm_tdata (ibfd)->known_eabi_attributes[0].i = 1;
+
+  in_attr = elf32_arm_tdata (ibfd)->known_eabi_attributes;
+  out_attr = elf32_arm_tdata (obfd)->known_eabi_attributes;
+  /* 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.  */
+      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)
+       {
+         _bfd_error_handler
+           (_("ERROR: %B uses VFP register arguments, %B does not"),
+            ibfd, obfd);
+         return FALSE;
+       }
+    }
+
+  for (i = 4; i < NUM_KNOWN_ATTRIBUTES; i++)
+    {
+      /* Merge this attribute with existing attributes.  */
+      switch (i)
+       {
+       case Tag_CPU_raw_name:
+       case Tag_CPU_name:
+         /* Use whichever has the greatest architecture requirements.  */
+         if (in_attr[Tag_CPU_arch].i > out_attr[Tag_CPU_arch].i)
+           out_attr[i].s = attr_strdup(obfd, in_attr[i].s);
+         break;
+
+       case Tag_ABI_optimization_goals:
+       case Tag_ABI_FP_optimization_goals:
+         /* Use the first value seen.  */
+         break;
+
+       case Tag_CPU_arch:
+       case Tag_ARM_ISA_use:
+       case Tag_THUMB_ISA_use:
+       case Tag_VFP_arch:
+       case Tag_WMMX_arch:
+       case Tag_NEON_arch:
+         /* ??? Do 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:
+         /* 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)
+           {
+             _bfd_error_handler
+               (_("ERROR: %B: Conflicting architecture profiles %c/%c"),
+                ibfd, in_attr[i].i, out_attr[i].i);
+             return FALSE;
+           }
+         if (in_attr[i].i)
+           out_attr[i].i = in_attr[i].i;
+         break;
+       case Tag_PCS_config:
+         if (out_attr[i].i == 0)
+           out_attr[i].i = in_attr[i].i;
+         else if (in_attr[i].i != 0 && out_attr[i].i != 0)
+           {
+             /* It's sometimes ok to mix different configs, so this is only
+                a warning.  */
+             _bfd_error_handler
+               (_("Warning: %B: Conflicting platform configuration"), ibfd);
+           }
+         break;
+       case Tag_ABI_PCS_R9_use:
+         if (out_attr[i].i != AEABI_R9_unused
+             && in_attr[i].i != AEABI_R9_unused)
+           {
+             _bfd_error_handler
+               (_("ERROR: %B: Conflicting use of R9"), ibfd);
+             return FALSE;
+           }
+         if (out_attr[i].i == AEABI_R9_unused)
+           out_attr[i].i = in_attr[i].i;
+         break;
+       case Tag_ABI_PCS_RW_data:
+         if (in_attr[i].i == AEABI_PCS_RW_data_SBrel
+             && out_attr[Tag_ABI_PCS_R9_use].i != AEABI_R9_SB
+             && out_attr[Tag_ABI_PCS_R9_use].i != AEABI_R9_unused)
+           {
+             _bfd_error_handler
+               (_("ERROR: %B: SB relative addressing conflicts with use of R9"),
+                ibfd);
+             return 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)
+           {
+             _bfd_error_handler
+               (_("ERROR: %B: Conflicting definitions of wchar_t"), ibfd);
+             return FALSE;
+           }
+         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])
+           out_attr[i].i = in_attr[i].i;
+         break;
+       case Tag_ABI_enum_size:
+         if (in_attr[i].i != AEABI_enum_unused)
+           {
+             if (out_attr[i].i == AEABI_enum_unused
+                 || out_attr[i].i == AEABI_enum_forced_wide)
+               {
+                 /* The existing object is compatible with anything.
+                    Use whatever requirements the new object has.  */
+                 out_attr[i].i = in_attr[i].i;
+               }
+             else if (in_attr[i].i != AEABI_enum_forced_wide
+                      && out_attr[i].i != in_attr[i].i)
+               {
+                 _bfd_error_handler
+                   (_("ERROR: %B: Conflicting enum sizes"), ibfd);
+               }
+           }
+         break;
+       case Tag_ABI_VFP_args:
+         /* Aready done.  */
+         break;
+       case Tag_ABI_WMMX_args:
+         if (in_attr[i].i != out_attr[i].i)
+           {
+             _bfd_error_handler
+               (_("ERROR: %B uses iWMMXt register arguments, %B does not"),
+                ibfd, obfd);
+             return FALSE;
+           }
+         break;
+       default: /* All known attributes should be explicitly covered.   */
+         abort ();
+       }
+    }
+
+  in_list = elf32_arm_tdata (ibfd)->other_eabi_attributes;
+  out_list = elf32_arm_tdata (ibfd)->other_eabi_attributes;
+  while (in_list && in_list->tag == Tag_compatibility)
+    {
+      in_attr = &in_list->attr;
+      if (in_attr->i == 0)
+       continue;
+      if (in_attr->i == 1)
+       {
+         _bfd_error_handler
+           (_("ERROR: %B: Must be processed by '%s' toolchain"),
+            ibfd, in_attr->s);
+         return FALSE;
+       }
+      if (!out_list || out_list->tag != Tag_compatibility
+         || strcmp (in_attr->s, out_list->attr.s) != 0)
+       {
+         /* Add this compatibility tag to the output.  */
+         elf32_arm_add_eabi_attr_compat (obfd, in_attr->i, in_attr->s);
+         continue;
+       }
+      out_attr = &out_list->attr;
+      /* Check all the input tags with the same identifier.  */
+      for (;;)
+       {
+         if (out_list->tag != Tag_compatibility
+             || in_attr->i != out_attr->i
+             || strcmp (in_attr->s, out_attr->s) != 0)
+           {
+             _bfd_error_handler
+               (_("ERROR: %B: Incompatible object tag '%s':%d"),
+                ibfd, in_attr->s, in_attr->i);
+             return FALSE;
+           }
+         in_list = in_list->next;
+         if (in_list->tag != Tag_compatibility
+             || strcmp (in_attr->s, in_list->attr.s) != 0)
+           break;
+         in_attr = &in_list->attr;
+         out_list = out_list->next;
+         if (out_list)
+           out_attr = &out_list->attr;
+       }
+
+      /* Check the output doesn't have extra tags with this identifier.  */
+      if (out_list && out_list->tag == Tag_compatibility
+         && strcmp (in_attr->s, out_list->attr.s) == 0)
+       {
+         _bfd_error_handler
+           (_("ERROR: %B: Incompatible object tag '%s':%d"),
+            ibfd, in_attr->s, out_list->attr.i);
+         return FALSE;
+       }
+    }
+
+  for (; in_list; in_list = in_list->next)
+    {
+      if ((in_list->tag & 128) < 64)
+       _bfd_error_handler
+         (_("Warning: %B: Unknown EABI object attribute %d"),
+          ibfd, in_list->tag);
+      break;
+    }
   return TRUE;
 }
 
@@ -4358,6 +5038,9 @@ elf32_arm_merge_private_bfd_data (bfd * ibfd, bfd * obfd)
       || bfd_get_flavour (obfd) != bfd_target_elf_flavour)
     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
@@ -4400,7 +5083,7 @@ elf32_arm_merge_private_bfd_data (bfd * ibfd, bfd * obfd)
 
   /* 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 incompatibility.  Do not short-circuit
+     cannot actually cause any incompatiblity.  Do not short-circuit
      dynamic objects; their section list may be emptied by
     elf_link_add_object_symbols.
 
@@ -4688,7 +5371,7 @@ elf32_arm_get_symbol_type (Elf_Internal_Sym * elf_sym, int type)
         This allows us to distinguish between data used by Thumb instructions
         and non-data (which is probably code) inside Thumb regions of an
         executable.  */
-      if (type != STT_OBJECT)
+      if (type != STT_OBJECT && type != STT_TLS)
        return ELF_ST_TYPE (elf_sym->st_info);
       break;
 
@@ -4912,7 +5595,12 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
       if (r_symndx < symtab_hdr->sh_info)
         h = NULL;
       else
-        h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+       {
+         h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+         while (h->root.type == bfd_link_hash_indirect
+                || h->root.type == bfd_link_hash_warning)
+           h = (struct elf_link_hash_entry *) h->root.u.i.link;
+       }
 
       eh = (struct elf32_arm_link_hash_entry *) h;
 
@@ -5109,13 +5797,15 @@ elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info,
                       easily.  Oh well.  */
 
                    asection *s;
+                   void *vpp;
+
                    s = bfd_section_from_r_symndx (abfd, &htab->sym_sec,
                                                   sec, r_symndx);
                    if (s == NULL)
                      return FALSE;
 
-                   head = ((struct elf32_arm_relocs_copied **)
-                           &elf_section_data (s)->local_dynrel);
+                   vpp = &elf_section_data (s)->local_dynrel;
+                   head = (struct elf32_arm_relocs_copied **) vpp;
                  }
 
                p = *head;
@@ -5382,6 +6072,13 @@ elf32_arm_adjust_dynamic_symbol (struct bfd_link_info * info,
   if (info->shared || globals->root.is_relocatable_executable)
     return TRUE;
 
+  if (h->size == 0)
+    {
+      (*_bfd_error_handler) (_("dynamic variable `%s' is zero size"),
+                            h->root.root.string);
+      return TRUE;
+    }
+
   /* We must allocate the symbol in our .dynbss section, which will
      become part of the .bss section of the executable.  There will be
      an entry for this symbol in the .dynsym section.  The dynamic
@@ -5636,9 +6333,22 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
 
       /* Also discard relocs on undefined weak syms with non-default
          visibility.  */
-      if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+      if (eh->relocs_copied != NULL
          && h->root.type == bfd_link_hash_undefweak)
-       eh->relocs_copied = NULL;
+       {
+         if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+           eh->relocs_copied = NULL;
+
+         /* Make sure undefined weak symbols are output as a dynamic
+            symbol in PIEs.  */
+         else if (h->dynindx == -1
+                  && !h->forced_local)
+           {
+             if (! bfd_elf_link_record_dynamic_symbol (info, h))
+               return FALSE;
+           }
+       }
+
       else if (htab->root.is_relocatable_executable && h->dynindx == -1
               && h->root.type == bfd_link_hash_new)
        {
@@ -5738,6 +6448,7 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
   htab = elf32_arm_hash_table (info);
   dynobj = elf_hash_table (info)->dynobj;
   BFD_ASSERT (dynobj != NULL);
+  check_use_blx (htab);
 
   if (elf_hash_table (info)->dynamic_sections_created)
     {
@@ -5769,10 +6480,7 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
        {
          struct elf32_arm_relocs_copied *p;
 
-         for (p = *((struct elf32_arm_relocs_copied **)
-                    &elf_section_data (s)->local_dynrel);
-              p != NULL;
-              p = p->next)
+         for (p = 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))
@@ -5847,7 +6555,6 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
   for (s = dynobj->sections; s != NULL; s = s->next)
     {
       const char * name;
-      bfd_boolean strip;
 
       if ((s->flags & SEC_LINKER_CREATED) == 0)
        continue;
@@ -5856,38 +6563,14 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
         of the dynobj section names depend upon the input files.  */
       name = bfd_get_section_name (dynobj, s);
 
-      strip = FALSE;
-
       if (strcmp (name, ".plt") == 0)
        {
-         if (s->size == 0)
-           {
-             /* Strip this section if we don't need it; see the
-                 comment below.  */
-             strip = TRUE;
-           }
-         else
-           {
-             /* Remember whether there is a PLT.  */
-             plt = TRUE;
-           }
+         /* Remember whether there is a PLT.  */
+         plt = s->size != 0;
        }
       else if (strncmp (name, ".rel", 4) == 0)
        {
-         if (s->size == 0)
-           {
-             /* If we don't need this section, strip it from the
-                output file.  This is mostly to handle .rel.bss and
-                .rel.plt.  We must create both sections in
-                create_dynamic_sections, because they must be created
-                before the linker maps input sections to output
-                sections.  The linker does that before
-                adjust_dynamic_symbol is called, and it is that
-                function which decides whether anything needs to go
-                into these sections.  */
-             strip = TRUE;
-           }
-         else
+         if (s->size != 0)
            {
              /* Remember whether there are any reloc sections other
                  than .rel.plt.  */
@@ -5899,21 +6582,34 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
              s->reloc_count = 0;
            }
        }
-      else if (strncmp (name, ".got", 4) != 0)
+      else if (strncmp (name, ".got", 4) != 0
+              && strcmp (name, ".dynbss") != 0)
        {
          /* It's not one of our sections, so don't allocate space.  */
          continue;
        }
 
-      if (strip)
+      if (s->size == 0)
        {
+         /* If we don't need this section, strip it from the
+            output file.  This is mostly to handle .rel.bss and
+            .rel.plt.  We must create both sections in
+            create_dynamic_sections, because they must be created
+            before the linker maps input sections to output
+            sections.  The linker does that before
+            adjust_dynamic_symbol is called, and it is that
+            function which decides whether anything needs to go
+            into these sections.  */
          s->flags |= SEC_EXCLUDE;
          continue;
        }
 
+      if ((s->flags & SEC_HAS_CONTENTS) == 0)
+       continue;
+
       /* Allocate memory for the section contents.  */
       s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size);
-      if (s->contents == NULL && s->size != 0)
+      if (s->contents == NULL)
        return FALSE;
     }
 
@@ -5927,7 +6623,7 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
 #define add_dynamic_entry(TAG, VAL) \
   _bfd_elf_add_dynamic_entry (info, TAG, VAL)
 
-      if (!info->shared)
+     if (info->executable)
        {
          if (!add_dynamic_entry (DT_DEBUG, 0))
            return FALSE;
@@ -5960,10 +6656,9 @@ elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED,
        {
          if (!add_dynamic_entry (DT_TEXTREL, 0))
            return FALSE;
-         info->flags |= DF_TEXTREL;
        }
     }
-#undef add_synamic_entry
+#undef add_dynamic_entry
 
   return TRUE;
 }
@@ -6171,7 +6866,7 @@ elf32_arm_finish_dynamic_symbol (bfd * output_bfd, struct bfd_link_info * info,
 
   /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute.  */
   if (strcmp (h->root.root.string, "_DYNAMIC") == 0
-      || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0)
+      || h == htab->root.hgot)
     sym->st_shndx = SHN_ABS;
 
   return TRUE;
@@ -6486,9 +7181,125 @@ elf32_arm_fake_sections (bfd * abfd, Elf_Internal_Shdr * hdr, asection * sec)
       hdr->sh_type = SHT_ARM_EXIDX;
       hdr->sh_flags |= SHF_LINK_ORDER;
     }
+  else if (strcmp(name, ".ARM.attributes") == 0)
+    {
+      hdr->sh_type = SHT_ARM_ATTRIBUTES;
+    }
   return TRUE;
 }
 
+/* Parse an Arm EABI attributes section.  */
+static void
+elf32_arm_parse_attributes (bfd *abfd, Elf_Internal_Shdr * hdr)
+{
+  bfd_byte *contents;
+  bfd_byte *p;
+  bfd_vma len;
+
+  contents = bfd_malloc (hdr->sh_size);
+  if (!contents)
+    return;
+  if (!bfd_get_section_contents (abfd, hdr->bfd_section, contents, 0,
+                                hdr->sh_size))
+    {
+      free (contents);
+      return;
+    }
+  p = contents;
+  if (*(p++) == 'A')
+    {
+      len = hdr->sh_size - 1;
+      while (len > 0)
+       {
+         int namelen;
+         bfd_vma section_len;
+
+         section_len = bfd_get_32 (abfd, p);
+         p += 4;
+         if (section_len > len)
+           section_len = len;
+         len -= section_len;
+         namelen = strlen ((char *)p) + 1;
+         section_len -= namelen + 4;
+         if (strcmp((char *)p, "aeabi") != 0)
+           {
+             /* Vendor section.  Ignore it.  */
+             p += namelen + section_len;
+           }
+         else
+           {
+             p += namelen;
+             while (section_len > 0)
+               {
+                 int tag;
+                 unsigned int n;
+                 unsigned int val;
+                 bfd_vma subsection_len;
+                 bfd_byte *end;
+
+                 tag = read_unsigned_leb128 (abfd, p, &n);
+                 p += n;
+                 subsection_len = bfd_get_32 (abfd, p);
+                 p += 4;
+                 if (subsection_len > section_len)
+                   subsection_len = section_len;
+                 section_len -= subsection_len;
+                 subsection_len -= n + 4;
+                 end = p + subsection_len;
+                 switch (tag)
+                   {
+                   case Tag_File:
+                     while (p < end)
+                       {
+                         bfd_boolean is_string;
+
+                         tag = read_unsigned_leb128 (abfd, p, &n);
+                         p += n;
+                         if (tag == 4 || tag == 5)
+                           is_string = 1;
+                         else if (tag < 32)
+                           is_string = 0;
+                         else
+                           is_string = (tag & 1) != 0;
+                         if (tag == Tag_compatibility)
+                           {
+                             val = read_unsigned_leb128 (abfd, p, &n);
+                             p += n;
+                             elf32_arm_add_eabi_attr_compat (abfd, val,
+                                                             (char *)p);
+                             p += strlen ((char *)p) + 1;
+                           }
+                         else if (is_string)
+                           {
+                             elf32_arm_add_eabi_attr_string (abfd, tag,
+                                                             (char *)p);
+                             p += strlen ((char *)p) + 1;
+                           }
+                         else
+                           {
+                             val = read_unsigned_leb128 (abfd, p, &n);
+                             p += n;
+                             elf32_arm_add_eabi_attr_int (abfd, tag, val);
+                           }
+                       }
+                     break;
+                   case Tag_Section:
+                   case Tag_Symbol:
+                     /* Don't have anywhere convenient to attach these.
+                        Fall through for now.  */
+                   default:
+                     /* Ignore things we don't kow about.  */
+                     p += subsection_len;
+                     subsection_len = 0;
+                     break;
+                   }
+               }
+           }
+       }
+    }
+  free (contents);
+}
+
 /* Handle an ARM specific section when reading an object file.  This is
    called when bfd_section_from_shdr finds a section with an unknown
    type.  */
@@ -6507,6 +7318,8 @@ elf32_arm_section_from_shdr (bfd *abfd,
   switch (hdr->sh_type)
     {
     case SHT_ARM_EXIDX:
+    case SHT_ARM_PREEMPTMAP:
+    case SHT_ARM_ATTRIBUTES:
       break;
 
     default:
@@ -6516,9 +7329,112 @@ elf32_arm_section_from_shdr (bfd *abfd,
   if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex))
     return FALSE;
 
+  if (hdr->sh_type == SHT_ARM_ATTRIBUTES)
+    elf32_arm_parse_attributes(abfd, hdr);
   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);
+  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);
+    }
+}
+
 /* Called for each symbol.  Builds a section map based on mapping symbols.
    Does not alter any of the symbols.  */
 
@@ -6531,6 +7447,8 @@ elf32_arm_output_symbol_hook (struct bfd_link_info *info,
 {
   int mapcount;
   elf32_arm_section_map *map;
+  elf32_arm_section_map *newmap;
+  _arm_elf_section_data *arm_data;
   struct elf32_arm_link_hash_table *globals;
 
   /* Only do this on final link.  */
@@ -6546,25 +7464,36 @@ elf32_arm_output_symbol_hook (struct bfd_link_info *info,
   if (! bfd_is_arm_mapping_symbol_name (name))
     return TRUE;
 
-  mapcount = ++(elf32_arm_section_data (input_sec)->mapcount);
-  map = elf32_arm_section_data (input_sec)->map;
+  /* If this section has not been allocated an _arm_elf_section_data
+     structure then we cannot record anything.  */
+  arm_data = get_arm_elf_section_data (input_sec);
+  if (arm_data == NULL)
+    return TRUE;
+
+  mapcount = arm_data->mapcount + 1;
+  map = arm_data->map;
+
   /* TODO: This may be inefficient, but we probably don't usually have many
      mapping symbols per section.  */
-  map = bfd_realloc (map, mapcount * sizeof (elf32_arm_section_map));
-  elf32_arm_section_data (input_sec)->map = map;
+  newmap = bfd_realloc (map, mapcount * sizeof (* map));
+  if (newmap != NULL)
+    {
+      arm_data->map = newmap;
+      arm_data->mapcount = mapcount;
+
+      newmap[mapcount - 1].vma = elfsym->st_value;
+      newmap[mapcount - 1].type = name[1];
+    }
 
-  map[mapcount - 1].vma = elfsym->st_value;
-  map[mapcount - 1].type = name[1];
   return TRUE;
 }
 
-
 /* Allocate target specific section data.  */
 
 static bfd_boolean
 elf32_arm_new_section_hook (bfd *abfd, asection *sec)
 {
-  struct _arm_elf_section_data *sdata;
+  _arm_elf_section_data *sdata;
   bfd_size_type amt = sizeof (*sdata);
 
   sdata = bfd_zalloc (abfd, amt);
@@ -6572,6 +7501,8 @@ elf32_arm_new_section_hook (bfd *abfd, asection *sec)
     return FALSE;
   sec->used_by_bfd = sdata;
 
+  record_section_with_arm_elf_section_data (sec);
+
   return _bfd_elf_new_section_hook (abfd, sec);
 }
 
@@ -6594,6 +7525,7 @@ elf32_arm_write_section (bfd *output_bfd ATTRIBUTE_UNUSED, asection *sec,
                         bfd_byte *contents)
 {
   int mapcount;
+  _arm_elf_section_data *arm_data;
   elf32_arm_section_map *map;
   bfd_vma ptr;
   bfd_vma end;
@@ -6601,14 +7533,19 @@ elf32_arm_write_section (bfd *output_bfd ATTRIBUTE_UNUSED, asection *sec,
   bfd_byte tmp;
   int i;
 
-  mapcount = elf32_arm_section_data (sec)->mapcount;
-  map = elf32_arm_section_data (sec)->map;
+  /* If this section has not been allocated an _arm_elf_section_data
+     structure then we cannot record anything.  */
+  arm_data = get_arm_elf_section_data (sec);
+  if (arm_data == NULL)
+    return FALSE;
+
+  mapcount = arm_data->mapcount;
+  map = arm_data->map;
 
   if (mapcount == 0)
     return FALSE;
 
-  qsort (map, mapcount, sizeof (elf32_arm_section_map),
-        elf32_arm_compare_mapping);
+  qsort (map, mapcount, sizeof (* map), elf32_arm_compare_mapping);
 
   offset = sec->output_section->vma + sec->output_offset;
   ptr = map[0].vma - offset;
@@ -6652,10 +7589,31 @@ elf32_arm_write_section (bfd *output_bfd ATTRIBUTE_UNUSED, asection *sec,
        }
       ptr = end;
     }
+
   free (map);
+  arm_data->mapcount = 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)
+{
+  bfd_map_over_sections (abfd, unrecord_section_via_map_over_sections, NULL);
+
+  return _bfd_elf_close_and_cleanup (abfd);
+}
+
 /* Display STT_ARM_TFUNC symbols as functions.  */
 
 static void
@@ -6814,6 +7772,8 @@ 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_final_link               elf32_arm_bfd_final_link
 
 #define elf_backend_get_symbol_type             elf32_arm_get_symbol_type
 #define elf_backend_gc_mark_hook                elf32_arm_gc_mark_hook
@@ -6937,8 +7897,8 @@ elf32_arm_symbian_link_hash_table_create (bfd *abfd)
   return ret;
 }     
 
-static struct bfd_elf_special_section const
-  symbian_special_sections_d[]=
+static const struct bfd_elf_special_section
+elf32_arm_symbian_special_sections[] =
 {
   /* In a BPABI executable, the dynamic linking sections do not go in
      the loadable read-only segment.  The post-linker may wish to
@@ -6947,93 +7907,17 @@ static struct bfd_elf_special_section const
   { ".dynamic",        8,  0, SHT_DYNAMIC,  0 },
   { ".dynstr",         7,  0, SHT_STRTAB,   0 },
   { ".dynsym",         7,  0, SHT_DYNSYM,   0 },
-  { NULL,              0,  0, 0,            0 }
-};
-
-static struct bfd_elf_special_section const
-  symbian_special_sections_g[]=
-{
-  /* In a BPABI executable, the dynamic linking sections do not go in
-     the loadable read-only segment.  The post-linker may wish to
-     refer to these sections, but they are not part of the final
-     program image.  */
   { ".got",            4,  0, SHT_PROGBITS, 0 },
-  { NULL,              0,  0, 0,            0 }
-};
-
-static struct bfd_elf_special_section const
-  symbian_special_sections_h[]=
-{
-  /* In a BPABI executable, the dynamic linking sections do not go in
-     the loadable read-only segment.  The post-linker may wish to
-     refer to these sections, but they are not part of the final
-     program image.  */
   { ".hash",           5,  0, SHT_HASH,     0 },
-  { NULL,              0,  0, 0,            0 }
-};
-
-static struct bfd_elf_special_section const
-  symbian_special_sections_i[]=
-{
   /* These sections do not need to be writable as the SymbianOS
      postlinker will arrange things so that no dynamic relocation is
      required.  */
   { ".init_array",    11,  0, SHT_INIT_ARRAY, SHF_ALLOC },
-  { NULL,              0,  0, 0,            0 }
-};
-
-static struct bfd_elf_special_section const
-  symbian_special_sections_f[]=
-{
-  /* These sections do not need to be writable as the SymbianOS
-     postlinker will arrange things so that no dynamic relocation is
-     required.  */
   { ".fini_array",    11,  0, SHT_FINI_ARRAY, SHF_ALLOC },
-  { NULL,              0,  0, 0,            0 }
-};
-
-static struct bfd_elf_special_section const
-  symbian_special_sections_p[]=
-{
-  /* These sections do not need to be writable as the SymbianOS
-     postlinker will arrange things so that no dynamic relocation is
-     required.  */
   { ".preinit_array", 14,  0, SHT_PREINIT_ARRAY, SHF_ALLOC },
   { NULL,              0,  0, 0,            0 }
 };
 
-static struct bfd_elf_special_section const *
-  elf32_arm_symbian_special_sections[27]=
-{
-  NULL,                                /* 'a' */
-  NULL,                                /* 'b' */
-  NULL,                                /* 'c' */
-  symbian_special_sections_d,  /* 'd' */
-  NULL,                                /* 'e' */
-  symbian_special_sections_f,  /* 'f' */
-  symbian_special_sections_g,  /* 'g' */
-  symbian_special_sections_h,  /* 'h' */
-  symbian_special_sections_i,  /* 'i' */
-  NULL,                                /* 'j' */
-  NULL,                                /* 'k' */
-  NULL,                                /* 'l' */
-  NULL,                                /* 'm' */
-  NULL,                                /* 'n' */
-  NULL,                                /* 'o' */
-  symbian_special_sections_p,  /* 'p' */
-  NULL,                                /* 'q' */
-  NULL,                                /* 'r' */
-  NULL,                                /* 's' */
-  NULL,                                /* 't' */
-  NULL,                                /* 'u' */
-  NULL,                                /* 'v' */
-  NULL,                                /* 'w' */
-  NULL,                                /* 'x' */
-  NULL,                                /* 'y' */
-  NULL,                                /* 'z' */
-  NULL                         /* other */
-};
-
 static void
 elf32_arm_symbian_begin_write_processing (bfd *abfd, 
                                          struct bfd_link_info *link_info
This page took 0.043263 seconds and 4 git commands to generate.