bfd/
[deliverable/binutils-gdb.git] / bfd / elflink.c
index 6cf1ba9f67baf7cb837e3c343b0714482cbfcf40..4201e2865b0c2e72539d6bc66a1095eca85ab55e 100644 (file)
@@ -6,7 +6,7 @@
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
-   the Free Software Foundation; either version 2 of the License, or
+   the Free Software Foundation; either version 3 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
@@ -16,7 +16,8 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
-   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
+   Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+   MA 02110-1301, USA.  */
 
 #include "sysdep.h"
 #include "bfd.h"
@@ -451,7 +452,7 @@ bfd_elf_link_mark_dynamic_symbol (struct bfd_link_info *info,
        && (h->type == STT_OBJECT
           || (sym != NULL
               && ELF_ST_TYPE (sym->st_info) == STT_OBJECT)))
-      || (d != NULL 
+      || (d != NULL
          && h->root.type == bfd_link_hash_new
          && (*d->match) (&d->head, NULL, h->root.root.string)))
     h->dynamic = 1;
@@ -467,8 +468,9 @@ bfd_elf_record_link_assignment (bfd *output_bfd,
                                bfd_boolean provide,
                                bfd_boolean hidden)
 {
-  struct elf_link_hash_entry *h;
+  struct elf_link_hash_entry *h, *hv;
   struct elf_link_hash_table *htab;
+  const struct elf_backend_data *bed;
 
   if (!is_elf_hash_table (info->hash))
     return TRUE;
@@ -478,21 +480,43 @@ bfd_elf_record_link_assignment (bfd *output_bfd,
   if (h == NULL)
     return provide;
 
-  /* Since we're defining the symbol, don't let it seem to have not
-     been defined.  record_dynamic_symbol and size_dynamic_sections
-     may depend on this.  */
-  if (h->root.type == bfd_link_hash_undefweak
-      || h->root.type == bfd_link_hash_undefined)
+  switch (h->root.type)
     {
+    case bfd_link_hash_defined:
+    case bfd_link_hash_defweak:
+    case bfd_link_hash_common:
+      break;
+    case bfd_link_hash_undefweak:
+    case bfd_link_hash_undefined:
+      /* Since we're defining the symbol, don't let it seem to have not
+        been defined.  record_dynamic_symbol and size_dynamic_sections
+        may depend on this.  */
       h->root.type = bfd_link_hash_new;
       if (h->root.u.undef.next != NULL || htab->root.undefs_tail == &h->root)
        bfd_link_repair_undef_list (&htab->root);
-    }
-
-  if (h->root.type == bfd_link_hash_new)
-    {
+      break;
+    case bfd_link_hash_new:
       bfd_elf_link_mark_dynamic_symbol (info, h, NULL);
       h->non_elf = 0;
+      break;
+    case bfd_link_hash_indirect:
+      /* We had a versioned symbol in a dynamic library.  We make the
+        the versioned symbol point to this one.  */
+      bed = get_elf_backend_data (output_bfd);
+      hv = h;
+      while (hv->root.type == bfd_link_hash_indirect
+            || hv->root.type == bfd_link_hash_warning)
+       hv = (struct elf_link_hash_entry *) hv->root.u.i.link;
+      /* We don't need to update h->root.u since linker will set them
+        later.  */
+      h->root.type = bfd_link_hash_undefined;
+      hv->root.type = bfd_link_hash_indirect;
+      hv->root.u.i.link = (struct bfd_link_hash_entry *) h;
+      (*bed->elf_backend_copy_indirect_symbol) (info, h, hv);
+      break;
+    case bfd_link_hash_warning:
+      abort ();
+      break;
     }
 
   /* If this symbol is being provided by the linker script, and it is
@@ -1416,10 +1440,10 @@ _bfd_elf_merge_symbol (bfd *abfd,
         case, we make the versioned symbol point to the normal one.  */
       const struct elf_backend_data *bed = get_elf_backend_data (abfd);
       flip->root.type = h->root.type;
+      flip->root.u.undef.abfd = h->root.u.undef.abfd;
       h->root.type = bfd_link_hash_indirect;
       h->root.u.i.link = (struct bfd_link_hash_entry *) flip;
       (*bed->elf_backend_copy_indirect_symbol) (info, flip, h);
-      flip->root.u.undef.abfd = h->root.u.undef.abfd;
       if (h->def_dynamic)
        {
          h->def_dynamic = 0;
@@ -1783,6 +1807,11 @@ _bfd_elf_link_find_version_dependencies (struct elf_link_hash_entry *h,
 
   amt = sizeof *a;
   a = bfd_zalloc (rinfo->output_bfd, amt);
+  if (a == NULL)
+    {
+      rinfo->failed = TRUE;
+      return FALSE;
+    }
 
   /* Note that we are copying a string pointer here, and testing it
      above.  If bfd_elf_string_from_elf_section is ever changed to
@@ -1877,7 +1906,10 @@ _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data)
              len = p - h->root.root.string;
              alc = bfd_malloc (len);
              if (alc == NULL)
-               return FALSE;
+               {
+                 sinfo->failed = TRUE;
+                 return FALSE;
+               }
              memcpy (alc, h->root.root.string, len - 1);
              alc[len - 1] = '\0';
              if (alc[len - 2] == ELF_VER_CHR)
@@ -1947,7 +1979,7 @@ _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data)
          /* We could not find the version for a symbol when
             generating a shared archive.  Return an error.  */
          (*_bfd_error_handler)
-           (_("%B: undefined versioned symbol name %s"),
+           (_("%B: version node not found for symbol %s"),
             sinfo->output_bfd, h->root.root.string);
          bfd_set_error (bfd_error_bad_value);
          sinfo->failed = TRUE;
@@ -2344,7 +2376,7 @@ bfd_boolean
 _bfd_elf_fix_symbol_flags (struct elf_link_hash_entry *h,
                           struct elf_info_failed *eif)
 {
-  const struct elf_backend_data *bed = NULL;
+  const struct elf_backend_data *bed;
 
   /* If this symbol was mentioned in a non-ELF file, try to set
      DEF_REGULAR and REF_REGULAR correctly.  This is the only way to
@@ -2405,13 +2437,10 @@ _bfd_elf_fix_symbol_flags (struct elf_link_hash_entry *h,
     }
 
   /* Backend specific symbol fixup.  */
-  if (elf_hash_table (eif->info)->dynobj)
-    {
-      bed = get_elf_backend_data (elf_hash_table (eif->info)->dynobj);
-      if (bed->elf_backend_fixup_symbol
-         && !(*bed->elf_backend_fixup_symbol) (eif->info, h))
-       return FALSE;
-    }
+  bed = get_elf_backend_data (elf_hash_table (eif->info)->dynobj);
+  if (bed->elf_backend_fixup_symbol
+      && !(*bed->elf_backend_fixup_symbol) (eif->info, h))
+    return FALSE;
 
   /* If this is a final link, and the symbol was defined as a common
      symbol in a regular object file, and there was no definition in
@@ -2449,11 +2478,7 @@ _bfd_elf_fix_symbol_flags (struct elf_link_hash_entry *h,
      hide it from the dynamic linker.  */
   if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
       && h->root.type == bfd_link_hash_undefweak)
-    {
-      const struct elf_backend_data *bed;
-      bed = get_elf_backend_data (elf_hash_table (eif->info)->dynobj);
-      (*bed->elf_backend_hide_symbol) (eif->info, h, TRUE);
-    }
+    (*bed->elf_backend_hide_symbol) (eif->info, h, TRUE);
 
   /* If this is a weak defined symbol in a dynamic object, and we know
      the real definition in the dynamic object, copy interesting flags
@@ -2468,8 +2493,6 @@ _bfd_elf_fix_symbol_flags (struct elf_link_hash_entry *h,
 
       BFD_ASSERT (h->root.type == bfd_link_hash_defined
                  || h->root.type == bfd_link_hash_defweak);
-      BFD_ASSERT (weakdef->root.type == bfd_link_hash_defined
-                 || weakdef->root.type == bfd_link_hash_defweak);
       BFD_ASSERT (weakdef->def_dynamic);
 
       /* If the real definition is defined by a regular object file,
@@ -2478,8 +2501,11 @@ _bfd_elf_fix_symbol_flags (struct elf_link_hash_entry *h,
       if (weakdef->def_regular)
        h->u.weakdef = NULL;
       else
-       (*bed->elf_backend_copy_indirect_symbol) (eif->info, weakdef,
-                                                 h);
+       {
+         BFD_ASSERT (weakdef->root.type == bfd_link_hash_defined
+                     || weakdef->root.type == bfd_link_hash_defweak);
+         (*bed->elf_backend_copy_indirect_symbol) (eif->info, weakdef, h);
+       }
     }
 
   return TRUE;
@@ -2616,33 +2642,33 @@ bfd_boolean
 _bfd_elf_adjust_dynamic_copy (struct elf_link_hash_entry *h,
                              asection *dynbss)
 {
-  unsigned int power_of_two, orig_power_of_two;
+  unsigned int power_of_two;
   bfd_vma mask;
   asection *sec = h->root.u.def.section;
 
   /* The section aligment of definition is the maximum alignment
-     requirement of symbols defined in the section.  */
-  power_of_two = bfd_get_section_alignment (dynbss->owner, dynbss);
-  orig_power_of_two = bfd_get_section_alignment (sec->owner, sec);
+     requirement of symbols defined in the section.  Since we don't
+     know the symbol alignment requirement, we start with the
+     maximum alignment and check low bits of the symbol address
+     for the minimum alignment.  */
+  power_of_two = bfd_get_section_alignment (sec->owner, sec);
+  mask = ((bfd_vma) 1 << power_of_two) - 1;
+  while ((h->root.u.def.value & mask) != 0)
+    {
+       mask >>= 1;
+       --power_of_two;
+    }
 
-  if (orig_power_of_two > power_of_two)
+  if (power_of_two > bfd_get_section_alignment (dynbss->owner,
+                                               dynbss))
     {
       /* Adjust the section alignment if needed.  */
       if (! bfd_set_section_alignment (dynbss->owner, dynbss,
-                                      orig_power_of_two))
+                                      power_of_two))
        return FALSE;
     }
 
-  /* We make sure that the symbol will be aligned properly.  Since we
-     don't know its alignment requirement, we start with the maximum
-     alignment and check low bits of the symbol address for the
-     minimum alignment.  */
-  mask = ((bfd_vma) 1 << orig_power_of_two) - 1;
-  while ((h->root.u.def.value & mask) != 0)
-    {
-       mask >>= 1;
-       --orig_power_of_two;
-    }
+  /* We make sure that the symbol will be aligned properly.  */
   dynbss->size = BFD_ALIGN (dynbss->size, mask + 1);
 
   /* Define the symbol as being at this point in DYNBSS.  */
@@ -3226,6 +3252,40 @@ elf_finalize_dynstr (bfd *output_bfd, struct bfd_link_info *info)
   return TRUE;
 }
 \f
+/* Return TRUE iff relocations for INPUT are compatible with OUTPUT.
+   The default is to only match when the INPUT and OUTPUT are exactly
+   the same target.  */
+
+bfd_boolean
+_bfd_elf_default_relocs_compatible (const bfd_target *input,
+                                   const bfd_target *output)
+{
+  return input == output;
+}
+
+/* Return TRUE iff relocations for INPUT are compatible with OUTPUT.
+   This version is used when different targets for the same architecture
+   are virtually identical.  */
+
+bfd_boolean
+_bfd_elf_relocs_compatible (const bfd_target *input,
+                           const bfd_target *output)
+{
+  const struct elf_backend_data *obed, *ibed;
+
+  if (input == output)
+    return TRUE;
+
+  ibed = xvec_get_elf_backend_data (input);
+  obed = xvec_get_elf_backend_data (output);
+
+  if (ibed->arch != obed->arch)
+    return FALSE;
+
+  /* If both backends are using this function, deem them compatible.  */
+  return ibed->relocs_compatible == obed->relocs_compatible;
+}
+
 /* Add symbols from an ELF object file to the linker hash table.  */
 
 static bfd_boolean
@@ -3673,8 +3733,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
         tell it that we are about to handle an as-needed lib.  */
       if (!(*info->callbacks->notice) (info, NULL, abfd, NULL,
                                       notice_as_needed))
-       return FALSE;
-
+       goto error_free_vers;
 
       /* Clone the symbol table and sym hashes.  Remember some
         pointers into the symbol table, and dynamic symbol count.  */
@@ -4261,6 +4320,8 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
                      amt = ((isymend - isym + 1)
                             * sizeof (struct elf_link_hash_entry *));
                      nondeflt_vers = bfd_malloc (amt);
+                     if (!nondeflt_vers)
+                       goto error_free_vers;
                    }
                  nondeflt_vers[nondeflt_vers_cnt++] = h;
                }
@@ -4381,7 +4442,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
         tell it that symbols added for crefs may need to be removed.  */
       if (!(*info->callbacks->notice) (info, NULL, abfd, NULL,
                                       notice_not_needed))
-       return FALSE;
+       goto error_free_vers;
 
       free (old_tab);
       objalloc_free_block ((struct objalloc *) htab->root.table.memory,
@@ -4395,7 +4456,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
     {
       if (!(*info->callbacks->notice) (info, NULL, abfd, NULL,
                                       notice_needed))
-       return FALSE;
+       goto error_free_vers;
       free (old_tab);
       old_tab = NULL;
     }
@@ -4419,6 +4480,8 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 
          amt = p - h->root.root.string;
          shortname = bfd_malloc (amt + 1);
+         if (!shortname)
+           goto error_free_vers;
          memcpy (shortname, h->root.root.string, amt);
          shortname[amt] = '\0';
 
@@ -4610,8 +4673,8 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
      different format.  It probably can't be done.  */
   if (! dynamic
       && is_elf_hash_table (htab)
-      && htab->root.creator == abfd->xvec
-      && bed->check_relocs != NULL)
+      && bed->check_relocs != NULL
+      && (*bed->relocs_compatible) (abfd->xvec, htab->root.creator))
     {
       asection *o;
 
@@ -4963,13 +5026,19 @@ bfd_elf_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
     }
 }
 \f
+struct hash_codes_info
+{
+  unsigned long *hashcodes;
+  bfd_boolean error;
+};
+
 /* This function will be called though elf_link_hash_traverse to store
    all hash value of the exported symbols in an array.  */
 
 static bfd_boolean
 elf_collect_hash_codes (struct elf_link_hash_entry *h, void *data)
 {
-  unsigned long **valuep = data;
+  struct hash_codes_info *inf = data;
   const char *name;
   char *p;
   unsigned long ha;
@@ -4987,6 +5056,11 @@ elf_collect_hash_codes (struct elf_link_hash_entry *h, void *data)
   if (p != NULL)
     {
       alc = bfd_malloc (p - name + 1);
+      if (alc == NULL)
+       {
+         inf->error = TRUE;
+         return FALSE;
+       }
       memcpy (alc, name, p - name);
       alc[p - name] = '\0';
       name = alc;
@@ -4996,7 +5070,7 @@ elf_collect_hash_codes (struct elf_link_hash_entry *h, void *data)
   ha = bfd_elf_hash (name);
 
   /* Store the found hash value in the array given as the argument.  */
-  *(*valuep)++ = ha;
+  *(inf->hashcodes)++ = ha;
 
   /* And store it in the struct so that we can put it in the hash table
      later.  */
@@ -5026,6 +5100,7 @@ struct collect_gnu_hash_codes
   long int local_indx;
   long int shift1, shift2;
   unsigned long int mask;
+  bfd_boolean error;
 };
 
 /* This function will be called though elf_link_hash_traverse to store
@@ -5056,6 +5131,11 @@ elf_collect_gnu_hash_codes (struct elf_link_hash_entry *h, void *data)
   if (p != NULL)
     {
       alc = bfd_malloc (p - name + 1);
+      if (alc == NULL)
+       {
+         s->error = TRUE;
+         return FALSE;
+       }
       memcpy (alc, name, p - name);
       alc[p - name] = '\0';
       name = alc;
@@ -5310,7 +5390,6 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
     return TRUE;
 
   bed = get_elf_backend_data (output_bfd);
-  elf_tdata (output_bfd)->relro = info->relro;
   if (info->execstack)
     elf_tdata (output_bfd)->stack_flags = PF_R | PF_W | PF_X;
   else if (info->noexecstack)
@@ -5927,6 +6006,8 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
        elf_link_hash_traverse (elf_hash_table (info),
                                _bfd_elf_link_find_version_dependencies,
                                &sinfo);
+       if (sinfo.failed)
+         return FALSE;
 
        if (elf_tdata (output_bfd)->verref == NULL)
          s->flags |= SEC_EXCLUDE;
@@ -6148,7 +6229,7 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
       if (info->emit_hash)
        {
          unsigned long int *hashcodes;
-         unsigned long int *hashcodesp;
+         struct hash_codes_info hashinf;
          bfd_size_type amt;
          unsigned long int nsyms;
          size_t bucketcount;
@@ -6161,13 +6242,16 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
          hashcodes = bfd_malloc (amt);
          if (hashcodes == NULL)
            return FALSE;
-         hashcodesp = hashcodes;
+         hashinf.hashcodes = hashcodes;
+         hashinf.error = FALSE;
 
          /* Put all hash values in HASHCODES.  */
          elf_link_hash_traverse (elf_hash_table (info),
-                                 elf_collect_hash_codes, &hashcodesp);
+                                 elf_collect_hash_codes, &hashinf);
+         if (hashinf.error)
+           return FALSE;
 
-         nsyms = hashcodesp - hashcodes;
+         nsyms = hashinf.hashcodes - hashcodes;
          bucketcount
            = compute_bucket_count (info, hashcodes, nsyms, 0);
          free (hashcodes);
@@ -6216,6 +6300,8 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
          /* Put all hash values in HASHCODES.  */
          elf_link_hash_traverse (elf_hash_table (info),
                                  elf_collect_gnu_hash_codes, &cinfo);
+         if (cinfo.error)
+           return FALSE;
 
          bucketcount
            = compute_bucket_count (info, cinfo.hashcodes, cinfo.nsyms, 1);
@@ -6365,339 +6451,1030 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
 
   return TRUE;
 }
+\f
+/* Indicate that we are only retrieving symbol values from this
+   section.  */
 
-/* Final phase of ELF linker.  */
-
-/* A structure we use to avoid passing large numbers of arguments.  */
-
-struct elf_final_link_info
+void
+_bfd_elf_link_just_syms (asection *sec, struct bfd_link_info *info)
 {
-  /* General link information.  */
-  struct bfd_link_info *info;
-  /* Output BFD.  */
-  bfd *output_bfd;
-  /* Symbol string table.  */
-  struct bfd_strtab_hash *symstrtab;
-  /* .dynsym section.  */
-  asection *dynsym_sec;
-  /* .hash section.  */
-  asection *hash_sec;
-  /* symbol version section (.gnu.version).  */
-  asection *symver_sec;
-  /* Buffer large enough to hold contents of any section.  */
-  bfd_byte *contents;
-  /* Buffer large enough to hold external relocs of any section.  */
-  void *external_relocs;
-  /* Buffer large enough to hold internal relocs of any section.  */
-  Elf_Internal_Rela *internal_relocs;
-  /* Buffer large enough to hold external local symbols of any input
-     BFD.  */
-  bfd_byte *external_syms;
-  /* And a buffer for symbol section indices.  */
-  Elf_External_Sym_Shndx *locsym_shndx;
-  /* Buffer large enough to hold internal local symbols of any input
-     BFD.  */
-  Elf_Internal_Sym *internal_syms;
-  /* Array large enough to hold a symbol index for each local symbol
-     of any input BFD.  */
-  long *indices;
-  /* Array large enough to hold a section pointer for each local
-     symbol of any input BFD.  */
-  asection **sections;
-  /* Buffer to hold swapped out symbols.  */
-  bfd_byte *symbuf;
-  /* And one for symbol section indices.  */
-  Elf_External_Sym_Shndx *symshndxbuf;
-  /* Number of swapped out symbols in buffer.  */
-  size_t symbuf_count;
-  /* Number of symbols which fit in symbuf.  */
-  size_t symbuf_size;
-  /* And same for symshndxbuf.  */
-  size_t shndxbuf_size;
-};
+  if (is_elf_hash_table (info->hash))
+    sec->sec_info_type = ELF_INFO_TYPE_JUST_SYMS;
+  _bfd_generic_link_just_syms (sec, info);
+}
 
-/* This struct is used to pass information to elf_link_output_extsym.  */
+/* Make sure sec_info_type is cleared if sec_info is cleared too.  */
 
-struct elf_outext_info
+static void
+merge_sections_remove_hook (bfd *abfd ATTRIBUTE_UNUSED,
+                           asection *sec)
 {
-  bfd_boolean failed;
-  bfd_boolean localsyms;
-  struct elf_final_link_info *finfo;
-};
-
+  BFD_ASSERT (sec->sec_info_type == ELF_INFO_TYPE_MERGE);
+  sec->sec_info_type = ELF_INFO_TYPE_NONE;
+}
 
-/* Support for evaluating a complex relocation.
+/* Finish SHF_MERGE section merging.  */
 
-   Complex relocations are generalized, self-describing relocations.  The
-   implementation of them consists of two parts: complex symbols, and the
-   relocations themselves. 
+bfd_boolean
+_bfd_elf_merge_sections (bfd *abfd, struct bfd_link_info *info)
+{
+  bfd *ibfd;
+  asection *sec;
 
-   The relocations are use a reserved elf-wide relocation type code (R_RELC
-   external / BFD_RELOC_RELC internal) and an encoding of relocation field
-   information (start bit, end bit, word width, etc) into the addend.  This
-   information is extracted from CGEN-generated operand tables within gas.
+  if (!is_elf_hash_table (info->hash))
+    return FALSE;
 
-   Complex symbols are mangled symbols (BSF_RELC external / STT_RELC
-   internal) representing prefix-notation expressions, including but not
-   limited to those sorts of expressions normally encoded as addends in the
-   addend field.  The symbol mangling format is:
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+    if ((ibfd->flags & DYNAMIC) == 0)
+      for (sec = ibfd->sections; sec != NULL; sec = sec->next)
+       if ((sec->flags & SEC_MERGE) != 0
+           && !bfd_is_abs_section (sec->output_section))
+         {
+           struct bfd_elf_section_data *secdata;
 
-   <node> := <literal>
-          |  <unary-operator> ':' <node>
-          |  <binary-operator> ':' <node> ':' <node>
-         ;
+           secdata = elf_section_data (sec);
+           if (! _bfd_add_merge_section (abfd,
+                                         &elf_hash_table (info)->merge_info,
+                                         sec, &secdata->sec_info))
+             return FALSE;
+           else if (secdata->sec_info)
+             sec->sec_info_type = ELF_INFO_TYPE_MERGE;
+         }
 
-   <literal> := 's' <digits=N> ':' <N character symbol name>
-             |  'S' <digits=N> ':' <N character section name>
-            |  '#' <hexdigits>
-            ;
+  if (elf_hash_table (info)->merge_info != NULL)
+    _bfd_merge_sections (abfd, info, elf_hash_table (info)->merge_info,
+                        merge_sections_remove_hook);
+  return TRUE;
+}
 
-   <binary-operator> := as in C
-   <unary-operator> := as in C, plus "0-" for unambiguous negation.  */
+/* Create an entry in an ELF linker hash table.  */
 
-static void
-set_symbol_value (bfd *                         bfd_with_globals,
-                 struct elf_final_link_info *  finfo,    
-                 int                           symidx,
-                 bfd_vma                       val)
+struct bfd_hash_entry *
+_bfd_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
+                           struct bfd_hash_table *table,
+                           const char *string)
 {
-  bfd_boolean                    is_local;
-  Elf_Internal_Sym *             sym;
-  struct elf_link_hash_entry **  sym_hashes;
-  struct elf_link_hash_entry *   h;
-
-  sym_hashes = elf_sym_hashes (bfd_with_globals);
-  sym = finfo->internal_syms + symidx;  
-  is_local = ELF_ST_BIND(sym->st_info) == STB_LOCAL;
-  
-  if (is_local)
+  /* Allocate the structure if it has not already been allocated by a
+     subclass.  */
+  if (entry == NULL)
     {
-      /* It is a local symbol: move it to the
-        "absolute" section and give it a value.  */
-      sym->st_shndx = SHN_ABS;
-      sym->st_value = val;
+      entry = bfd_hash_allocate (table, sizeof (struct elf_link_hash_entry));
+      if (entry == NULL)
+       return entry;
     }
-  else 
-    {
-      /* It is a global symbol: set its link type
-        to "defined" and give it a value.  */
-      h = sym_hashes [symidx];   
-      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;
-      h->root.type = bfd_link_hash_defined;
-      h->root.u.def.value = val;
-      h->root.u.def.section = bfd_abs_section_ptr;
-    }
-}
-
-static bfd_boolean 
-resolve_symbol (const char *                  name,
-               bfd *                         input_bfd,
-               struct elf_final_link_info *  finfo,
-               bfd_vma *                     result,
-               size_t                        locsymcount)
-{
-  Elf_Internal_Sym *            sym;
-  struct bfd_link_hash_entry *  global_entry;
-  const char *                  candidate = NULL;
-  Elf_Internal_Shdr *           symtab_hdr;
-  asection *                    sec = NULL;
-  size_t                        i;
-  
-  symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
 
-  for (i = 0; i < locsymcount; ++ i)
+  /* Call the allocation method of the superclass.  */
+  entry = _bfd_link_hash_newfunc (entry, table, string);
+  if (entry != NULL)
     {
-      sym = finfo->internal_syms + i;
-      sec = finfo->sections [i];
+      struct elf_link_hash_entry *ret = (struct elf_link_hash_entry *) entry;
+      struct elf_link_hash_table *htab = (struct elf_link_hash_table *) table;
 
-      if (ELF_ST_BIND (sym->st_info) != STB_LOCAL)
-       continue;
+      /* Set local fields.  */
+      ret->indx = -1;
+      ret->dynindx = -1;
+      ret->got = htab->init_got_refcount;
+      ret->plt = htab->init_plt_refcount;
+      memset (&ret->size, 0, (sizeof (struct elf_link_hash_entry)
+                             - offsetof (struct elf_link_hash_entry, size)));
+      /* Assume that we have been called by a non-ELF symbol reader.
+        This flag is then reset by the code which reads an ELF input
+        file.  This ensures that a symbol created by a non-ELF symbol
+        reader will have the flag set correctly.  */
+      ret->non_elf = 1;
+    }
 
-      candidate = bfd_elf_string_from_elf_section (input_bfd,
-                                                  symtab_hdr->sh_link,
-                                                  sym->st_name);
-#ifdef DEBUG
-      printf ("Comparing string: '%s' vs. '%s' = 0x%x\n", 
-             name, candidate, (unsigned int)sym->st_value);
-#endif
-      if (candidate && strcmp (candidate, name) == 0)
-       {
-         * result = sym->st_value;
+  return entry;
+}
 
-         if (sym->st_shndx > SHN_UNDEF && 
-             sym->st_shndx < SHN_LORESERVE)
-           {
-#ifdef DEBUG
-             printf ("adjusting for sec '%s' @ 0x%x + 0x%x\n",
-                     sec->output_section->name, 
-                     (unsigned int)sec->output_section->vma, 
-                     (unsigned int)sec->output_offset);
-#endif
-             * result += sec->output_offset + sec->output_section->vma;
-           }
-#ifdef DEBUG
-         printf ("Found symbol with effective value %8.8x\n", (unsigned int)* result);
-#endif
-         return TRUE;
-       }
+/* Copy data from an indirect symbol to its direct symbol, hiding the
+   old indirect symbol.  Also used for copying flags to a weakdef.  */
+
+void
+_bfd_elf_link_hash_copy_indirect (struct bfd_link_info *info,
+                                 struct elf_link_hash_entry *dir,
+                                 struct elf_link_hash_entry *ind)
+{
+  struct elf_link_hash_table *htab;
+
+  /* Copy down any references that we may have already seen to the
+     symbol which just became indirect.  */
+
+  dir->ref_dynamic |= ind->ref_dynamic;
+  dir->ref_regular |= ind->ref_regular;
+  dir->ref_regular_nonweak |= ind->ref_regular_nonweak;
+  dir->non_got_ref |= ind->non_got_ref;
+  dir->needs_plt |= ind->needs_plt;
+  dir->pointer_equality_needed |= ind->pointer_equality_needed;
+
+  if (ind->root.type != bfd_link_hash_indirect)
+    return;
+
+  /* Copy over the global and procedure linkage table refcount entries.
+     These may have been already set up by a check_relocs routine.  */
+  htab = elf_hash_table (info);
+  if (ind->got.refcount > htab->init_got_refcount.refcount)
+    {
+      if (dir->got.refcount < 0)
+       dir->got.refcount = 0;
+      dir->got.refcount += ind->got.refcount;
+      ind->got.refcount = htab->init_got_refcount.refcount;
     }
 
-  /* Hmm, haven't found it yet. perhaps it is a global.  */
-  global_entry = bfd_link_hash_lookup (finfo->info->hash, name, FALSE, FALSE, TRUE);
-  if (!global_entry)
-    return FALSE;
-  
-  if (global_entry->type == bfd_link_hash_defined
-      || global_entry->type == bfd_link_hash_defweak)
+  if (ind->plt.refcount > htab->init_plt_refcount.refcount)
     {
-      * result = global_entry->u.def.value 
-       + global_entry->u.def.section->output_section->vma 
-       + global_entry->u.def.section->output_offset;
-#ifdef DEBUG
-      printf ("Found GLOBAL symbol '%s' with value %8.8x\n",
-             global_entry->root.string, (unsigned int)*result);
-#endif
-      return TRUE;
-    } 
+      if (dir->plt.refcount < 0)
+       dir->plt.refcount = 0;
+      dir->plt.refcount += ind->plt.refcount;
+      ind->plt.refcount = htab->init_plt_refcount.refcount;
+    }
 
-  if (global_entry->type == bfd_link_hash_common)
+  if (ind->dynindx != -1)
     {
-      *result = global_entry->u.def.value +
-       bfd_com_section_ptr->output_section->vma +
-       bfd_com_section_ptr->output_offset;
-#ifdef DEBUG
-      printf ("Found COMMON symbol '%s' with value %8.8x\n",
-             global_entry->root.string, (unsigned int)*result);
-#endif
-      return TRUE;
+      if (dir->dynindx != -1)
+       _bfd_elf_strtab_delref (htab->dynstr, dir->dynstr_index);
+      dir->dynindx = ind->dynindx;
+      dir->dynstr_index = ind->dynstr_index;
+      ind->dynindx = -1;
+      ind->dynstr_index = 0;
     }
-  
-  return FALSE;
 }
 
-static bfd_boolean
-resolve_section (const char *  name,
-                asection *    sections,
-                bfd_vma *     result)
+void
+_bfd_elf_link_hash_hide_symbol (struct bfd_link_info *info,
+                               struct elf_link_hash_entry *h,
+                               bfd_boolean force_local)
 {
-  asection *    curr;
-  unsigned int  len;
-
-  for (curr = sections; curr; curr = curr->next)    
-    if (strcmp (curr->name, name) == 0)
-      {
-       *result = curr->vma;
-       return TRUE;
-      }
-
-  /* Hmm. still haven't found it. try pseudo-section names.  */
-  for (curr = sections; curr; curr = curr->next)    
+  h->plt = elf_hash_table (info)->init_plt_offset;
+  h->needs_plt = 0;
+  if (force_local)
     {
-      len = strlen (curr->name);
-      if (len > strlen (name)) 
-       continue;
-
-      if (strncmp (curr->name, name, len) == 0)
+      h->forced_local = 1;
+      if (h->dynindx != -1)
        {
-         if (strncmp (".end", name + len, 4) == 0)
-           {
-             *result = curr->vma + curr->size;
-             return TRUE;
-           }
-
-         /* Insert more pseudo-section names here, if you like.  */
+         h->dynindx = -1;
+         _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr,
+                                 h->dynstr_index);
        }
     }
-  
-  return FALSE;
 }
 
-static void
-undefined_reference (const char *  reftype,
-                    const char *  name)
-{
-  _bfd_error_handler (_("undefined %s reference in complex symbol: %s"), reftype, name);
-}
+/* Initialize an ELF linker hash table.  */
 
-static bfd_boolean
-eval_symbol (bfd_vma *                     result,
-            char *                        sym,
-            char **                       advanced,
-            bfd *                         input_bfd,
-            struct elf_final_link_info *  finfo,
-            bfd_vma                       addr,
-            bfd_vma                       section_offset,
-            size_t                        locsymcount,
-            int                           signed_p)
-{
-  int           len;
-  int           symlen;
-  bfd_vma       a;
-  bfd_vma       b;
-  const int     bufsz = 4096;
-  char          symbuf [bufsz];
-  const char *  symend;
-  bfd_boolean   symbol_is_section = FALSE;
+bfd_boolean
+_bfd_elf_link_hash_table_init
+  (struct elf_link_hash_table *table,
+   bfd *abfd,
+   struct bfd_hash_entry *(*newfunc) (struct bfd_hash_entry *,
+                                     struct bfd_hash_table *,
+                                     const char *),
+   unsigned int entsize)
+{
+  bfd_boolean ret;
+  int can_refcount = get_elf_backend_data (abfd)->can_refcount;
+
+  memset (table, 0, sizeof * table);
+  table->init_got_refcount.refcount = can_refcount - 1;
+  table->init_plt_refcount.refcount = can_refcount - 1;
+  table->init_got_offset.offset = -(bfd_vma) 1;
+  table->init_plt_offset.offset = -(bfd_vma) 1;
+  /* The first dynamic symbol is a dummy.  */
+  table->dynsymcount = 1;
+
+  ret = _bfd_link_hash_table_init (&table->root, abfd, newfunc, entsize);
+  table->root.type = bfd_link_elf_hash_table;
+
+  return ret;
+}
+
+/* Create an ELF linker hash table.  */
+
+struct bfd_link_hash_table *
+_bfd_elf_link_hash_table_create (bfd *abfd)
+{
+  struct elf_link_hash_table *ret;
+  bfd_size_type amt = sizeof (struct elf_link_hash_table);
+
+  ret = bfd_malloc (amt);
+  if (ret == NULL)
+    return NULL;
+
+  if (! _bfd_elf_link_hash_table_init (ret, abfd, _bfd_elf_link_hash_newfunc,
+                                      sizeof (struct elf_link_hash_entry)))
+    {
+      free (ret);
+      return NULL;
+    }
+
+  return &ret->root;
+}
+
+/* This is a hook for the ELF emulation code in the generic linker to
+   tell the backend linker what file name to use for the DT_NEEDED
+   entry for a dynamic object.  */
+
+void
+bfd_elf_set_dt_needed_name (bfd *abfd, const char *name)
+{
+  if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+      && bfd_get_format (abfd) == bfd_object)
+    elf_dt_name (abfd) = name;
+}
+
+int
+bfd_elf_get_dyn_lib_class (bfd *abfd)
+{
+  int lib_class;
+  if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+      && bfd_get_format (abfd) == bfd_object)
+    lib_class = elf_dyn_lib_class (abfd);
+  else
+    lib_class = 0;
+  return lib_class;
+}
+
+void
+bfd_elf_set_dyn_lib_class (bfd *abfd, enum dynamic_lib_link_class lib_class)
+{
+  if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+      && bfd_get_format (abfd) == bfd_object)
+    elf_dyn_lib_class (abfd) = lib_class;
+}
+
+/* Get the list of DT_NEEDED entries for a link.  This is a hook for
+   the linker ELF emulation code.  */
+
+struct bfd_link_needed_list *
+bfd_elf_get_needed_list (bfd *abfd ATTRIBUTE_UNUSED,
+                        struct bfd_link_info *info)
+{
+  if (! is_elf_hash_table (info->hash))
+    return NULL;
+  return elf_hash_table (info)->needed;
+}
+
+/* Get the list of DT_RPATH/DT_RUNPATH entries for a link.  This is a
+   hook for the linker ELF emulation code.  */
+
+struct bfd_link_needed_list *
+bfd_elf_get_runpath_list (bfd *abfd ATTRIBUTE_UNUSED,
+                         struct bfd_link_info *info)
+{
+  if (! is_elf_hash_table (info->hash))
+    return NULL;
+  return elf_hash_table (info)->runpath;
+}
+
+/* Get the name actually used for a dynamic object for a link.  This
+   is the SONAME entry if there is one.  Otherwise, it is the string
+   passed to bfd_elf_set_dt_needed_name, or it is the filename.  */
+
+const char *
+bfd_elf_get_dt_soname (bfd *abfd)
+{
+  if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+      && bfd_get_format (abfd) == bfd_object)
+    return elf_dt_name (abfd);
+  return NULL;
+}
+
+/* Get the list of DT_NEEDED entries from a BFD.  This is a hook for
+   the ELF linker emulation code.  */
+
+bfd_boolean
+bfd_elf_get_bfd_needed_list (bfd *abfd,
+                            struct bfd_link_needed_list **pneeded)
+{
+  asection *s;
+  bfd_byte *dynbuf = NULL;
+  int elfsec;
+  unsigned long shlink;
+  bfd_byte *extdyn, *extdynend;
+  size_t extdynsize;
+  void (*swap_dyn_in) (bfd *, const void *, Elf_Internal_Dyn *);
+
+  *pneeded = NULL;
+
+  if (bfd_get_flavour (abfd) != bfd_target_elf_flavour
+      || bfd_get_format (abfd) != bfd_object)
+    return TRUE;
+
+  s = bfd_get_section_by_name (abfd, ".dynamic");
+  if (s == NULL || s->size == 0)
+    return TRUE;
+
+  if (!bfd_malloc_and_get_section (abfd, s, &dynbuf))
+    goto error_return;
+
+  elfsec = _bfd_elf_section_from_bfd_section (abfd, s);
+  if (elfsec == -1)
+    goto error_return;
+
+  shlink = elf_elfsections (abfd)[elfsec]->sh_link;
+
+  extdynsize = get_elf_backend_data (abfd)->s->sizeof_dyn;
+  swap_dyn_in = get_elf_backend_data (abfd)->s->swap_dyn_in;
+
+  extdyn = dynbuf;
+  extdynend = extdyn + s->size;
+  for (; extdyn < extdynend; extdyn += extdynsize)
+    {
+      Elf_Internal_Dyn dyn;
+
+      (*swap_dyn_in) (abfd, extdyn, &dyn);
+
+      if (dyn.d_tag == DT_NULL)
+       break;
+
+      if (dyn.d_tag == DT_NEEDED)
+       {
+         const char *string;
+         struct bfd_link_needed_list *l;
+         unsigned int tagv = dyn.d_un.d_val;
+         bfd_size_type amt;
+
+         string = bfd_elf_string_from_elf_section (abfd, shlink, tagv);
+         if (string == NULL)
+           goto error_return;
+
+         amt = sizeof *l;
+         l = bfd_alloc (abfd, amt);
+         if (l == NULL)
+           goto error_return;
+
+         l->by = abfd;
+         l->name = string;
+         l->next = *pneeded;
+         *pneeded = l;
+       }
+    }
+
+  free (dynbuf);
+
+  return TRUE;
+
+ error_return:
+  if (dynbuf != NULL)
+    free (dynbuf);
+  return FALSE;
+}
+
+struct elf_symbuf_symbol
+{
+  unsigned long st_name;       /* Symbol name, index in string tbl */
+  unsigned char st_info;       /* Type and binding attributes */
+  unsigned char st_other;      /* Visibilty, and target specific */
+};
+
+struct elf_symbuf_head
+{
+  struct elf_symbuf_symbol *ssym;
+  bfd_size_type count;
+  unsigned int st_shndx;
+};
+
+struct elf_symbol
+{
+  union
+    {
+      Elf_Internal_Sym *isym;
+      struct elf_symbuf_symbol *ssym;
+    } u;
+  const char *name;
+};
+
+/* Sort references to symbols by ascending section number.  */
+
+static int
+elf_sort_elf_symbol (const void *arg1, const void *arg2)
+{
+  const Elf_Internal_Sym *s1 = *(const Elf_Internal_Sym **) arg1;
+  const Elf_Internal_Sym *s2 = *(const Elf_Internal_Sym **) arg2;
+
+  return s1->st_shndx - s2->st_shndx;
+}
+
+static int
+elf_sym_name_compare (const void *arg1, const void *arg2)
+{
+  const struct elf_symbol *s1 = (const struct elf_symbol *) arg1;
+  const struct elf_symbol *s2 = (const struct elf_symbol *) arg2;
+  return strcmp (s1->name, s2->name);
+}
+
+static struct elf_symbuf_head *
+elf_create_symbuf (bfd_size_type symcount, Elf_Internal_Sym *isymbuf)
+{
+  Elf_Internal_Sym **ind, **indbufend, **indbuf;
+  struct elf_symbuf_symbol *ssym;
+  struct elf_symbuf_head *ssymbuf, *ssymhead;
+  bfd_size_type i, shndx_count;
+
+  indbuf = bfd_malloc2 (symcount, sizeof (*indbuf));
+  if (indbuf == NULL)
+    return NULL;
+
+  for (ind = indbuf, i = 0; i < symcount; i++)
+    if (isymbuf[i].st_shndx != SHN_UNDEF)
+      *ind++ = &isymbuf[i];
+  indbufend = ind;
+
+  qsort (indbuf, indbufend - indbuf, sizeof (Elf_Internal_Sym *),
+        elf_sort_elf_symbol);
+
+  shndx_count = 0;
+  if (indbufend > indbuf)
+    for (ind = indbuf, shndx_count++; ind < indbufend - 1; ind++)
+      if (ind[0]->st_shndx != ind[1]->st_shndx)
+       shndx_count++;
+
+  ssymbuf = bfd_malloc ((shndx_count + 1) * sizeof (*ssymbuf)
+                       + (indbufend - indbuf) * sizeof (*ssymbuf));
+  if (ssymbuf == NULL)
+    {
+      free (indbuf);
+      return NULL;
+    }
+
+  ssym = (struct elf_symbuf_symbol *) (ssymbuf + shndx_count);
+  ssymbuf->ssym = NULL;
+  ssymbuf->count = shndx_count;
+  ssymbuf->st_shndx = 0;
+  for (ssymhead = ssymbuf, ind = indbuf; ind < indbufend; ssym++, ind++)
+    {
+      if (ind == indbuf || ssymhead->st_shndx != (*ind)->st_shndx)
+       {
+         ssymhead++;
+         ssymhead->ssym = ssym;
+         ssymhead->count = 0;
+         ssymhead->st_shndx = (*ind)->st_shndx;
+       }
+      ssym->st_name = (*ind)->st_name;
+      ssym->st_info = (*ind)->st_info;
+      ssym->st_other = (*ind)->st_other;
+      ssymhead->count++;
+    }
+  BFD_ASSERT ((bfd_size_type) (ssymhead - ssymbuf) == shndx_count);
+
+  free (indbuf);
+  return ssymbuf;
+}
+
+/* Check if 2 sections define the same set of local and global
+   symbols.  */
+
+static bfd_boolean
+bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
+                                  struct bfd_link_info *info)
+{
+  bfd *bfd1, *bfd2;
+  const struct elf_backend_data *bed1, *bed2;
+  Elf_Internal_Shdr *hdr1, *hdr2;
+  bfd_size_type symcount1, symcount2;
+  Elf_Internal_Sym *isymbuf1, *isymbuf2;
+  struct elf_symbuf_head *ssymbuf1, *ssymbuf2;
+  Elf_Internal_Sym *isym, *isymend;
+  struct elf_symbol *symtable1 = NULL, *symtable2 = NULL;
+  bfd_size_type count1, count2, i;
+  int shndx1, shndx2;
+  bfd_boolean result;
+
+  bfd1 = sec1->owner;
+  bfd2 = sec2->owner;
+
+  /* Both sections have to be in ELF.  */
+  if (bfd_get_flavour (bfd1) != bfd_target_elf_flavour
+      || bfd_get_flavour (bfd2) != bfd_target_elf_flavour)
+    return FALSE;
+
+  if (elf_section_type (sec1) != elf_section_type (sec2))
+    return FALSE;
+
+  shndx1 = _bfd_elf_section_from_bfd_section (bfd1, sec1);
+  shndx2 = _bfd_elf_section_from_bfd_section (bfd2, sec2);
+  if (shndx1 == -1 || shndx2 == -1)
+    return FALSE;
+
+  bed1 = get_elf_backend_data (bfd1);
+  bed2 = get_elf_backend_data (bfd2);
+  hdr1 = &elf_tdata (bfd1)->symtab_hdr;
+  symcount1 = hdr1->sh_size / bed1->s->sizeof_sym;
+  hdr2 = &elf_tdata (bfd2)->symtab_hdr;
+  symcount2 = hdr2->sh_size / bed2->s->sizeof_sym;
+
+  if (symcount1 == 0 || symcount2 == 0)
+    return FALSE;
+
+  result = FALSE;
+  isymbuf1 = NULL;
+  isymbuf2 = NULL;
+  ssymbuf1 = elf_tdata (bfd1)->symbuf;
+  ssymbuf2 = elf_tdata (bfd2)->symbuf;
+
+  if (ssymbuf1 == NULL)
+    {
+      isymbuf1 = bfd_elf_get_elf_syms (bfd1, hdr1, symcount1, 0,
+                                      NULL, NULL, NULL);
+      if (isymbuf1 == NULL)
+       goto done;
+
+      if (!info->reduce_memory_overheads)
+       elf_tdata (bfd1)->symbuf = ssymbuf1
+         = elf_create_symbuf (symcount1, isymbuf1);
+    }
+
+  if (ssymbuf1 == NULL || ssymbuf2 == NULL)
+    {
+      isymbuf2 = bfd_elf_get_elf_syms (bfd2, hdr2, symcount2, 0,
+                                      NULL, NULL, NULL);
+      if (isymbuf2 == NULL)
+       goto done;
+
+      if (ssymbuf1 != NULL && !info->reduce_memory_overheads)
+       elf_tdata (bfd2)->symbuf = ssymbuf2
+         = elf_create_symbuf (symcount2, isymbuf2);
+    }
+
+  if (ssymbuf1 != NULL && ssymbuf2 != NULL)
+    {
+      /* Optimized faster version.  */
+      bfd_size_type lo, hi, mid;
+      struct elf_symbol *symp;
+      struct elf_symbuf_symbol *ssym, *ssymend;
+
+      lo = 0;
+      hi = ssymbuf1->count;
+      ssymbuf1++;
+      count1 = 0;
+      while (lo < hi)
+       {
+         mid = (lo + hi) / 2;
+         if ((unsigned int) shndx1 < ssymbuf1[mid].st_shndx)
+           hi = mid;
+         else if ((unsigned int) shndx1 > ssymbuf1[mid].st_shndx)
+           lo = mid + 1;
+         else
+           {
+             count1 = ssymbuf1[mid].count;
+             ssymbuf1 += mid;
+             break;
+           }
+       }
+
+      lo = 0;
+      hi = ssymbuf2->count;
+      ssymbuf2++;
+      count2 = 0;
+      while (lo < hi)
+       {
+         mid = (lo + hi) / 2;
+         if ((unsigned int) shndx2 < ssymbuf2[mid].st_shndx)
+           hi = mid;
+         else if ((unsigned int) shndx2 > ssymbuf2[mid].st_shndx)
+           lo = mid + 1;
+         else
+           {
+             count2 = ssymbuf2[mid].count;
+             ssymbuf2 += mid;
+             break;
+           }
+       }
+
+      if (count1 == 0 || count2 == 0 || count1 != count2)
+       goto done;
+
+      symtable1 = bfd_malloc (count1 * sizeof (struct elf_symbol));
+      symtable2 = bfd_malloc (count2 * sizeof (struct elf_symbol));
+      if (symtable1 == NULL || symtable2 == NULL)
+       goto done;
+
+      symp = symtable1;
+      for (ssym = ssymbuf1->ssym, ssymend = ssym + count1;
+          ssym < ssymend; ssym++, symp++)
+       {
+         symp->u.ssym = ssym;
+         symp->name = bfd_elf_string_from_elf_section (bfd1,
+                                                       hdr1->sh_link,
+                                                       ssym->st_name);
+       }
+
+      symp = symtable2;
+      for (ssym = ssymbuf2->ssym, ssymend = ssym + count2;
+          ssym < ssymend; ssym++, symp++)
+       {
+         symp->u.ssym = ssym;
+         symp->name = bfd_elf_string_from_elf_section (bfd2,
+                                                       hdr2->sh_link,
+                                                       ssym->st_name);
+       }
+
+      /* Sort symbol by name.  */
+      qsort (symtable1, count1, sizeof (struct elf_symbol),
+            elf_sym_name_compare);
+      qsort (symtable2, count1, sizeof (struct elf_symbol),
+            elf_sym_name_compare);
+
+      for (i = 0; i < count1; i++)
+       /* Two symbols must have the same binding, type and name.  */
+       if (symtable1 [i].u.ssym->st_info != symtable2 [i].u.ssym->st_info
+           || symtable1 [i].u.ssym->st_other != symtable2 [i].u.ssym->st_other
+           || strcmp (symtable1 [i].name, symtable2 [i].name) != 0)
+         goto done;
+
+      result = TRUE;
+      goto done;
+    }
+
+  symtable1 = bfd_malloc (symcount1 * sizeof (struct elf_symbol));
+  symtable2 = bfd_malloc (symcount2 * sizeof (struct elf_symbol));
+  if (symtable1 == NULL || symtable2 == NULL)
+    goto done;
+
+  /* Count definitions in the section.  */
+  count1 = 0;
+  for (isym = isymbuf1, isymend = isym + symcount1; isym < isymend; isym++)
+    if (isym->st_shndx == (unsigned int) shndx1)
+      symtable1[count1++].u.isym = isym;
+
+  count2 = 0;
+  for (isym = isymbuf2, isymend = isym + symcount2; isym < isymend; isym++)
+    if (isym->st_shndx == (unsigned int) shndx2)
+      symtable2[count2++].u.isym = isym;
+
+  if (count1 == 0 || count2 == 0 || count1 != count2)
+    goto done;
+
+  for (i = 0; i < count1; i++)
+    symtable1[i].name
+      = bfd_elf_string_from_elf_section (bfd1, hdr1->sh_link,
+                                        symtable1[i].u.isym->st_name);
+
+  for (i = 0; i < count2; i++)
+    symtable2[i].name
+      = bfd_elf_string_from_elf_section (bfd2, hdr2->sh_link,
+                                        symtable2[i].u.isym->st_name);
+
+  /* Sort symbol by name.  */
+  qsort (symtable1, count1, sizeof (struct elf_symbol),
+        elf_sym_name_compare);
+  qsort (symtable2, count1, sizeof (struct elf_symbol),
+        elf_sym_name_compare);
+
+  for (i = 0; i < count1; i++)
+    /* Two symbols must have the same binding, type and name.  */
+    if (symtable1 [i].u.isym->st_info != symtable2 [i].u.isym->st_info
+       || symtable1 [i].u.isym->st_other != symtable2 [i].u.isym->st_other
+       || strcmp (symtable1 [i].name, symtable2 [i].name) != 0)
+      goto done;
+
+  result = TRUE;
+
+done:
+  if (symtable1)
+    free (symtable1);
+  if (symtable2)
+    free (symtable2);
+  if (isymbuf1)
+    free (isymbuf1);
+  if (isymbuf2)
+    free (isymbuf2);
+
+  return result;
+}
+
+/* Return TRUE if 2 section types are compatible.  */
+
+bfd_boolean
+_bfd_elf_match_sections_by_type (bfd *abfd, const asection *asec,
+                                bfd *bbfd, const asection *bsec)
+{
+  if (asec == NULL
+      || bsec == NULL
+      || abfd->xvec->flavour != bfd_target_elf_flavour
+      || bbfd->xvec->flavour != bfd_target_elf_flavour)
+    return TRUE;
+
+  return elf_section_type (asec) == elf_section_type (bsec);
+}
+\f
+/* Final phase of ELF linker.  */
+
+/* A structure we use to avoid passing large numbers of arguments.  */
+
+struct elf_final_link_info
+{
+  /* General link information.  */
+  struct bfd_link_info *info;
+  /* Output BFD.  */
+  bfd *output_bfd;
+  /* Symbol string table.  */
+  struct bfd_strtab_hash *symstrtab;
+  /* .dynsym section.  */
+  asection *dynsym_sec;
+  /* .hash section.  */
+  asection *hash_sec;
+  /* symbol version section (.gnu.version).  */
+  asection *symver_sec;
+  /* Buffer large enough to hold contents of any section.  */
+  bfd_byte *contents;
+  /* Buffer large enough to hold external relocs of any section.  */
+  void *external_relocs;
+  /* Buffer large enough to hold internal relocs of any section.  */
+  Elf_Internal_Rela *internal_relocs;
+  /* Buffer large enough to hold external local symbols of any input
+     BFD.  */
+  bfd_byte *external_syms;
+  /* And a buffer for symbol section indices.  */
+  Elf_External_Sym_Shndx *locsym_shndx;
+  /* Buffer large enough to hold internal local symbols of any input
+     BFD.  */
+  Elf_Internal_Sym *internal_syms;
+  /* Array large enough to hold a symbol index for each local symbol
+     of any input BFD.  */
+  long *indices;
+  /* Array large enough to hold a section pointer for each local
+     symbol of any input BFD.  */
+  asection **sections;
+  /* Buffer to hold swapped out symbols.  */
+  bfd_byte *symbuf;
+  /* And one for symbol section indices.  */
+  Elf_External_Sym_Shndx *symshndxbuf;
+  /* Number of swapped out symbols in buffer.  */
+  size_t symbuf_count;
+  /* Number of symbols which fit in symbuf.  */
+  size_t symbuf_size;
+  /* And same for symshndxbuf.  */
+  size_t shndxbuf_size;
+};
+
+/* This struct is used to pass information to elf_link_output_extsym.  */
+
+struct elf_outext_info
+{
+  bfd_boolean failed;
+  bfd_boolean localsyms;
+  struct elf_final_link_info *finfo;
+};
+
+
+/* Support for evaluating a complex relocation.
+
+   Complex relocations are generalized, self-describing relocations.  The
+   implementation of them consists of two parts: complex symbols, and the
+   relocations themselves.
+
+   The relocations are use a reserved elf-wide relocation type code (R_RELC
+   external / BFD_RELOC_RELC internal) and an encoding of relocation field
+   information (start bit, end bit, word width, etc) into the addend.  This
+   information is extracted from CGEN-generated operand tables within gas.
+
+   Complex symbols are mangled symbols (BSF_RELC external / STT_RELC
+   internal) representing prefix-notation expressions, including but not
+   limited to those sorts of expressions normally encoded as addends in the
+   addend field.  The symbol mangling format is:
+
+   <node> := <literal>
+          |  <unary-operator> ':' <node>
+          |  <binary-operator> ':' <node> ':' <node>
+         ;
+
+   <literal> := 's' <digits=N> ':' <N character symbol name>
+             |  'S' <digits=N> ':' <N character section name>
+            |  '#' <hexdigits>
+            ;
+
+   <binary-operator> := as in C
+   <unary-operator> := as in C, plus "0-" for unambiguous negation.  */
+
+static void
+set_symbol_value (bfd *bfd_with_globals,
+                 Elf_Internal_Sym *isymbuf,
+                 size_t locsymcount,
+                 size_t symidx,
+                 bfd_vma val)
+{
+  struct elf_link_hash_entry **sym_hashes;
+  struct elf_link_hash_entry *h;
+  size_t extsymoff = locsymcount;
+
+  if (symidx < locsymcount)
+    {
+      Elf_Internal_Sym *sym;
+
+      sym = isymbuf + symidx;
+      if (ELF_ST_BIND (sym->st_info) == STB_LOCAL)
+       {
+         /* It is a local symbol: move it to the
+            "absolute" section and give it a value.  */
+         sym->st_shndx = SHN_ABS;
+         sym->st_value = val;
+         return;
+       }
+      BFD_ASSERT (elf_bad_symtab (bfd_with_globals));
+      extsymoff = 0;
+    }
+
+  /* It is a global symbol: set its link type
+     to "defined" and give it a value.  */
+
+  sym_hashes = elf_sym_hashes (bfd_with_globals);
+  h = sym_hashes [symidx - extsymoff];
+  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;
+  h->root.type = bfd_link_hash_defined;
+  h->root.u.def.value = val;
+  h->root.u.def.section = bfd_abs_section_ptr;
+}
+
+static bfd_boolean
+resolve_symbol (const char *name,
+               bfd *input_bfd,
+               struct elf_final_link_info *finfo,
+               bfd_vma *result,
+               Elf_Internal_Sym *isymbuf,
+               size_t locsymcount)
+{
+  Elf_Internal_Sym *sym;
+  struct bfd_link_hash_entry *global_entry;
+  const char *candidate = NULL;
+  Elf_Internal_Shdr *symtab_hdr;
+  size_t i;
+
+  symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
+
+  for (i = 0; i < locsymcount; ++ i)
+    {
+      sym = isymbuf + i;
+
+      if (ELF_ST_BIND (sym->st_info) != STB_LOCAL)
+       continue;
+
+      candidate = bfd_elf_string_from_elf_section (input_bfd,
+                                                  symtab_hdr->sh_link,
+                                                  sym->st_name);
+#ifdef DEBUG
+      printf ("Comparing string: '%s' vs. '%s' = 0x%lx\n",
+             name, candidate, (unsigned long) sym->st_value);
+#endif
+      if (candidate && strcmp (candidate, name) == 0)
+       {
+         asection *sec = finfo->sections [i];
+
+         *result = _bfd_elf_rel_local_sym (input_bfd, sym, &sec, 0);
+         *result += sec->output_offset + sec->output_section->vma;
+#ifdef DEBUG
+         printf ("Found symbol with value %8.8lx\n",
+                 (unsigned long) *result);
+#endif
+         return TRUE;
+       }
+    }
+
+  /* Hmm, haven't found it yet. perhaps it is a global.  */
+  global_entry = bfd_link_hash_lookup (finfo->info->hash, name,
+                                      FALSE, FALSE, TRUE);
+  if (!global_entry)
+    return FALSE;
+
+  if (global_entry->type == bfd_link_hash_defined
+      || global_entry->type == bfd_link_hash_defweak)
+    {
+      *result = (global_entry->u.def.value
+                + global_entry->u.def.section->output_section->vma
+                + global_entry->u.def.section->output_offset);
+#ifdef DEBUG
+      printf ("Found GLOBAL symbol '%s' with value %8.8lx\n",
+             global_entry->root.string, (unsigned long) *result);
+#endif
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static bfd_boolean
+resolve_section (const char *name,
+                asection *sections,
+                bfd_vma *result)
+{
+  asection *curr;
+  unsigned int len;
+
+  for (curr = sections; curr; curr = curr->next)
+    if (strcmp (curr->name, name) == 0)
+      {
+       *result = curr->vma;
+       return TRUE;
+      }
+
+  /* Hmm. still haven't found it. try pseudo-section names.  */
+  for (curr = sections; curr; curr = curr->next)
+    {
+      len = strlen (curr->name);
+      if (len > strlen (name))
+       continue;
+
+      if (strncmp (curr->name, name, len) == 0)
+       {
+         if (strncmp (".end", name + len, 4) == 0)
+           {
+             *result = curr->vma + curr->size;
+             return TRUE;
+           }
+
+         /* Insert more pseudo-section names here, if you like.  */
+       }
+    }
+
+  return FALSE;
+}
+
+static void
+undefined_reference (const char *reftype, const char *name)
+{
+  _bfd_error_handler (_("undefined %s reference in complex symbol: %s"),
+                     reftype, name);
+}
+
+static bfd_boolean
+eval_symbol (bfd_vma *result,
+            const char **symp,
+            bfd *input_bfd,
+            struct elf_final_link_info *finfo,
+            bfd_vma dot,
+            Elf_Internal_Sym *isymbuf,
+            size_t locsymcount,
+            int signed_p)
+{
+  size_t len;
+  size_t symlen;
+  bfd_vma a;
+  bfd_vma b;
+  char symbuf[4096];
+  const char *sym = *symp;
+  const char *symend;
+  bfd_boolean symbol_is_section = FALSE;
 
   len = strlen (sym);
   symend = sym + len;
 
-  if (len < 1 || len > bufsz)
+  if (len < 1 || len > sizeof (symbuf))
     {
       bfd_set_error (bfd_error_invalid_operation);
       return FALSE;
     }
-  
+
   switch (* sym)
     {
     case '.':
-      * result = addr + section_offset;
-      * advanced = sym + 1;
+      *result = dot;
+      *symp = sym + 1;
       return TRUE;
 
     case '#':
-      ++ sym;
-      * result = strtoul (sym, advanced, 16);
+      ++sym;
+      *result = strtoul (sym, (char **) symp, 16);
       return TRUE;
 
     case 'S':
       symbol_is_section = TRUE;
-    case 's':      
-      ++ sym;
-      symlen = strtol (sym, &sym, 10);
-      ++ sym; /* Skip the trailing ':'.  */
+    case 's':
+      ++sym;
+      symlen = strtol (sym, (char **) symp, 10);
+      sym = *symp + 1; /* Skip the trailing ':'.  */
 
-      if ((symend < sym) || ((symlen + 1) > bufsz))
+      if (symend < sym || symlen + 1 > sizeof (symbuf))
        {
          bfd_set_error (bfd_error_invalid_operation);
          return FALSE;
        }
 
       memcpy (symbuf, sym, symlen);
-      symbuf [symlen] = '\0';
-      * advanced = sym + symlen;
-      
-      /* Is it always possible, with complex symbols, that gas "mis-guessed" 
+      symbuf[symlen] = '\0';
+      *symp = sym + symlen;
+
+      /* Is it always possible, with complex symbols, that gas "mis-guessed"
         the symbol as a section, or vice-versa. so we're pretty liberal in our
         interpretation here; section means "try section first", not "must be a
         section", and likewise with symbol.  */
 
-      if (symbol_is_section) 
+      if (symbol_is_section)
        {
-         if ((resolve_section (symbuf, finfo->output_bfd->sections, result) != TRUE)
-             && (resolve_symbol (symbuf, input_bfd, finfo, result, locsymcount) != TRUE))
+         if (!resolve_section (symbuf, finfo->output_bfd->sections, result)
+             && !resolve_symbol (symbuf, input_bfd, finfo, result,
+                                 isymbuf, locsymcount))
            {
              undefined_reference ("section", symbuf);
              return FALSE;
            }
-       } 
-      else 
+       }
+      else
        {
-         if ((resolve_symbol (symbuf, input_bfd, finfo, result, locsymcount) != TRUE)
-             && (resolve_section (symbuf, finfo->output_bfd->sections,
-                                  result) != TRUE))
+         if (!resolve_symbol (symbuf, input_bfd, finfo, result,
+                              isymbuf, locsymcount)
+             && !resolve_section (symbuf, finfo->output_bfd->sections,
+                                  result))
            {
              undefined_reference ("symbol", symbuf);
              return FALSE;
@@ -6705,24 +7482,23 @@ eval_symbol (bfd_vma *                     result,
        }
 
       return TRUE;
-      
+
       /* All that remains are operators.  */
 
 #define UNARY_OP(op)                                           \
   if (strncmp (sym, #op, strlen (#op)) == 0)                   \
     {                                                          \
       sym += strlen (#op);                                     \
-      if (* sym == ':')                                                \
-        ++ sym;                                                        \
-      if (eval_symbol (& a, sym, & sym, input_bfd, finfo, addr, \
-                       section_offset, locsymcount, signed_p)   \
-                                                    != TRUE)   \
-        return FALSE;                                          \
-      if (signed_p)                                             \
-        * result = op ((signed)a);                             \
-      else                                                      \
-        * result = op a;                                        \
-      * advanced = sym;                                        \
+      if (*sym == ':')                                         \
+       ++sym;                                                  \
+      *symp = sym;                                             \
+      if (!eval_symbol (&a, symp, input_bfd, finfo, dot,       \
+                       isymbuf, locsymcount, signed_p))        \
+       return FALSE;                                           \
+      if (signed_p)                                            \
+       *result = op ((bfd_signed_vma) a);                      \
+      else                                                     \
+       *result = op a;                                         \
       return TRUE;                                             \
     }
 
@@ -6730,22 +7506,20 @@ eval_symbol (bfd_vma *                     result,
   if (strncmp (sym, #op, strlen (#op)) == 0)                   \
     {                                                          \
       sym += strlen (#op);                                     \
-      if (* sym == ':')                                                \
-        ++ sym;                                                        \
-      if (eval_symbol (& a, sym, & sym, input_bfd, finfo, addr, \
-                       section_offset, locsymcount, signed_p)   \
-                                                     != TRUE)  \
-        return FALSE;                                          \
-      ++ sym;                                                  \
-      if (eval_symbol (& b, sym, & sym, input_bfd, finfo, addr, \
-                       section_offset, locsymcount, signed_p)   \
-                                                     != TRUE)  \
-        return FALSE;                                          \
-      if (signed_p)                                             \
-        * result = ((signed) a) op ((signed) b);               \
-      else                                                      \
-        * result = a op b;                                      \
-      * advanced = sym;                                                \
+      if (*sym == ':')                                         \
+       ++sym;                                                  \
+      *symp = sym;                                             \
+      if (!eval_symbol (&a, symp, input_bfd, finfo, dot,       \
+                       isymbuf, locsymcount, signed_p))        \
+       return FALSE;                                           \
+      ++*symp;                                                 \
+      if (!eval_symbol (&b, symp, input_bfd, finfo, dot,       \
+                       isymbuf, locsymcount, signed_p))        \
+       return FALSE;                                           \
+      if (signed_p)                                            \
+       *result = ((bfd_signed_vma) a) op ((bfd_signed_vma) b); \
+      else                                                     \
+       *result = a op b;                                       \
       return TRUE;                                             \
     }
 
@@ -6766,163 +7540,29 @@ eval_symbol (bfd_vma *                     result,
       BINARY_OP (%);
       BINARY_OP (^);
       BINARY_OP (|);
-      BINARY_OP (&);
-      BINARY_OP (+);
-      BINARY_OP (-);
-      BINARY_OP (<);
-      BINARY_OP (>);
-#undef UNARY_OP
-#undef BINARY_OP
-      _bfd_error_handler (_("unknown operator '%c' in complex symbol"), * sym);
-      bfd_set_error (bfd_error_invalid_operation);
-      return FALSE;
-    }
-}
-
-/* Entry point to evaluator, called from elf_link_input_bfd.  */
-
-static bfd_boolean
-evaluate_complex_relocation_symbols (bfd * input_bfd,
-                                    struct elf_final_link_info * finfo,
-                                    size_t locsymcount)
-{
-  const struct elf_backend_data * bed;
-  Elf_Internal_Shdr *             symtab_hdr;
-  struct elf_link_hash_entry **   sym_hashes;
-  asection *                      reloc_sec;
-  bfd_boolean                     result = TRUE;
-
-  /* For each section, we're going to check and see if it has any
-     complex relocations, and we're going to evaluate any of them
-     we can.  */
-
-  if (finfo->info->relocatable)
-    return TRUE;
-
-  symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
-  sym_hashes = elf_sym_hashes (input_bfd);
-  bed = get_elf_backend_data (input_bfd);
-
-  for (reloc_sec = input_bfd->sections; reloc_sec; reloc_sec = reloc_sec->next)
-    {
-      Elf_Internal_Rela * internal_relocs;
-      unsigned long i;
-
-      /* This section was omitted from the link.  */
-      if (! reloc_sec->linker_mark)
-       continue;
-
-      /* Only process sections containing relocs.  */
-      if ((reloc_sec->flags & SEC_RELOC) == 0)
-       continue;
-
-      if (reloc_sec->reloc_count == 0)
-       continue;
-
-      /* Read in the relocs for this section.  */
-      internal_relocs
-       = _bfd_elf_link_read_relocs (input_bfd, reloc_sec, NULL,
-                                    (Elf_Internal_Rela *) NULL,
-                                    FALSE);
-      if (internal_relocs == NULL)
-       continue;
-
-      for (i = reloc_sec->reloc_count; i--;)
-       {
-         Elf_Internal_Rela * rel;
-         char * sym_name;
-         bfd_vma index;
-         Elf_Internal_Sym * sym;
-         bfd_vma result;
-         bfd_vma section_offset;
-         bfd_vma addr;
-         int signed_p = 0;
-
-         rel = internal_relocs + i;
-         section_offset = reloc_sec->output_section->vma
-           + reloc_sec->output_offset;
-         addr = rel->r_offset;
-
-         index = ELF32_R_SYM (rel->r_info);
-         if (bed->s->arch_size == 64)
-           index >>= 24;
-
-         if (index == STN_UNDEF)
-           continue;
-
-         if (index < locsymcount)
-           {
-             /* The symbol is local.  */
-             sym = finfo->internal_syms + index;
-
-             /* We're only processing STT_RELC or STT_SRELC type symbols.  */
-             if ((ELF_ST_TYPE (sym->st_info) != STT_RELC) &&
-                 (ELF_ST_TYPE (sym->st_info) != STT_SRELC))
-               continue;
-
-             sym_name = bfd_elf_string_from_elf_section
-               (input_bfd, symtab_hdr->sh_link, sym->st_name);
-
-             signed_p = (ELF_ST_TYPE (sym->st_info) == STT_SRELC);
-           }
-         else
-           {
-             /* The symbol is global.  */
-             struct elf_link_hash_entry * h;
-
-             if (elf_bad_symtab (input_bfd))
-               continue;
-
-             h = sym_hashes [index - locsymcount];
-             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;
-
-             if (h->type != STT_RELC && h->type != STT_SRELC)
-               continue;
-
-             signed_p = (h->type == STT_SRELC);
-             sym_name = (char *) h->root.root.string;
-           }
-#ifdef DEBUG
-         printf ("Encountered a complex symbol!");
-         printf (" (input_bfd %s, section %s, reloc %ld\n",
-                 input_bfd->filename, reloc_sec->name, i);
-         printf (" symbol: idx  %8.8lx, name %s\n",
-                 index, sym_name);
-         printf (" reloc : info %8.8lx, addr %8.8lx\n",
-                 rel->r_info, addr);
-         printf (" Evaluating '%s' ...\n ", sym_name);
-#endif
-         if (eval_symbol (& result, sym_name, & sym_name, input_bfd, 
-                          finfo, addr, section_offset, locsymcount,
-                          signed_p))
-           /* Symbol evaluated OK.  Update to absolute value.  */
-           set_symbol_value (input_bfd, finfo, index, result);
-
-         else
-           result = FALSE;
-       }
-
-      if (internal_relocs != elf_section_data (reloc_sec)->relocs)
-       free (internal_relocs);
+      BINARY_OP (&);
+      BINARY_OP (+);
+      BINARY_OP (-);
+      BINARY_OP (<);
+      BINARY_OP (>);
+#undef UNARY_OP
+#undef BINARY_OP
+      _bfd_error_handler (_("unknown operator '%c' in complex symbol"), * sym);
+      bfd_set_error (bfd_error_invalid_operation);
+      return FALSE;
     }
-
-  /* If nothing went wrong, then we adjusted 
-     everything we wanted to adjust.  */
-  return result;
 }
 
 static void
-put_value (bfd_vma        size,
-          unsigned long  chunksz,
-          bfd *          input_bfd,
-          bfd_vma        x,
-          bfd_byte *     location)
+put_value (bfd_vma size,
+          unsigned long chunksz,
+          bfd *input_bfd,
+          bfd_vma x,
+          bfd_byte *location)
 {
   location += (size - chunksz);
 
-  for (; size; size -= chunksz, location -= chunksz, x >>= (chunksz * 8)) 
+  for (; size; size -= chunksz, location -= chunksz, x >>= (chunksz * 8))
     {
       switch (chunksz)
        {
@@ -6949,15 +7589,15 @@ put_value (bfd_vma        size,
     }
 }
 
-static bfd_vma 
-get_value (bfd_vma        size,
-          unsigned long  chunksz,
-          bfd *          input_bfd,
-          bfd_byte *     location)
+static bfd_vma
+get_value (bfd_vma size,
+          unsigned long chunksz,
+          bfd *input_bfd,
+          bfd_byte *location)
 {
   bfd_vma x = 0;
 
-  for (; size; size -= chunksz, location += chunksz) 
+  for (; size; size -= chunksz, location += chunksz)
     {
       switch (chunksz)
        {
@@ -6985,17 +7625,16 @@ get_value (bfd_vma        size,
   return x;
 }
 
-static void 
-decode_complex_addend
-    (unsigned long * start,   /* in bits */
-     unsigned long * oplen,   /* in bits */
-     unsigned long * len,     /* in bits */
-     unsigned long * wordsz,  /* in bytes */
-     unsigned long * chunksz,  /* in bytes */
-     unsigned long * lsb0_p,
-     unsigned long * signed_p,
-     unsigned long * trunc_p,
-     unsigned long encoded)
+static void
+decode_complex_addend (unsigned long *start,   /* in bits */
+                      unsigned long *oplen,   /* in bits */
+                      unsigned long *len,     /* in bits */
+                      unsigned long *wordsz,  /* in bytes */
+                      unsigned long *chunksz, /* in bytes */
+                      unsigned long *lsb0_p,
+                      unsigned long *signed_p,
+                      unsigned long *trunc_p,
+                      unsigned long encoded)
 {
   * start     =  encoded        & 0x3F;
   * len       = (encoded >>  6) & 0x3F;
@@ -7007,89 +7646,26 @@ decode_complex_addend
   * trunc_p   = (encoded >> 29) & 1;
 }
 
-void
-bfd_elf_perform_complex_relocation
-    (bfd *                   output_bfd ATTRIBUTE_UNUSED,
-     struct bfd_link_info *  info,
-     bfd *                   input_bfd,
-     asection *              input_section,
-     bfd_byte *              contents,
-     Elf_Internal_Rela *     rel,
-     Elf_Internal_Sym *      local_syms,
-     asection **             local_sections)
-{
-  const struct elf_backend_data * bed;
-  Elf_Internal_Shdr * symtab_hdr;
-  asection * sec;
-  bfd_vma relocation = 0, shift, x;
-  bfd_vma r_symndx;
-  bfd_vma mask;
-  unsigned long start, oplen, len, wordsz, 
-    chunksz, lsb0_p, signed_p, trunc_p;
+bfd_reloc_status_type
+bfd_elf_perform_complex_relocation (bfd *input_bfd,
+                                   asection *input_section ATTRIBUTE_UNUSED,
+                                   bfd_byte *contents,
+                                   Elf_Internal_Rela *rel,
+                                   bfd_vma relocation)
+{
+  bfd_vma shift, x, mask;
+  unsigned long start, oplen, len, wordsz, chunksz, lsb0_p, signed_p, trunc_p;
+  bfd_reloc_status_type r;
 
   /*  Perform this reloc, since it is complex.
       (this is not to say that it necessarily refers to a complex
       symbol; merely that it is a self-describing CGEN based reloc.
       i.e. the addend has the complete reloc information (bit start, end,
-      word size, etc) encoded within it.).  */ 
-  r_symndx = ELF32_R_SYM (rel->r_info);
-  bed = get_elf_backend_data (input_bfd);
-  if (bed->s->arch_size == 64)
-    r_symndx >>= 24;
-
-#ifdef DEBUG
-  printf ("Performing complex relocation %ld...\n", r_symndx);
-#endif
-
-  symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
-  if (r_symndx < symtab_hdr->sh_info)
-    {
-      /* The symbol is local.  */
-      Elf_Internal_Sym * sym;
-
-      sym = local_syms + r_symndx;
-      sec = local_sections [r_symndx];
-      relocation = sym->st_value;
-      if (sym->st_shndx > SHN_UNDEF && 
-         sym->st_shndx < SHN_LORESERVE)
-       relocation += (sec->output_offset +
-                      sec->output_section->vma);
-    }
-  else
-    {
-      /* The symbol is global.  */
-      struct elf_link_hash_entry **sym_hashes;
-      struct elf_link_hash_entry * h;
-
-      sym_hashes = elf_sym_hashes (input_bfd);
-      h = sym_hashes [r_symndx];
-
-      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;
+      word size, etc) encoded within it.).  */
 
-      if (h->root.type == bfd_link_hash_defined
-         || h->root.type == bfd_link_hash_defweak)
-       {
-         sec = h->root.u.def.section;
-         relocation = h->root.u.def.value;
-
-         if (! bfd_is_abs_section (sec))
-           relocation += (sec->output_section->vma 
-                          + sec->output_offset); 
-       }
-      if (h->root.type == bfd_link_hash_undefined
-         && !((*info->callbacks->undefined_symbol)
-              (info, h->root.root.string, input_bfd,
-               input_section, rel->r_offset,
-               info->unresolved_syms_in_objects == RM_GENERATE_ERROR
-               || ELF_ST_VISIBILITY (h->other))))
-       return;
-    }
-
-  decode_complex_addend (& start, & oplen, & len, & wordsz, 
-                        & chunksz, & lsb0_p, & signed_p, 
-                        & trunc_p, rel->r_addend);
+  decode_complex_addend (&start, &oplen, &len, &wordsz,
+                        &chunksz, &lsb0_p, &signed_p,
+                        &trunc_p, rel->r_addend);
 
   mask = (((1L << (len - 1)) - 1) << 1) | 1;
 
@@ -7098,7 +7674,7 @@ bfd_elf_perform_complex_relocation
   else
     shift = (8 * wordsz) - (start + len);
 
-  x = get_value (wordsz, chunksz, input_bfd, contents + rel->r_offset);          
+  x = get_value (wordsz, chunksz, input_bfd, contents + rel->r_offset);
 
 #ifdef DEBUG
   printf ("Doing complex reloc: "
@@ -7109,21 +7685,15 @@ bfd_elf_perform_complex_relocation
          oplen, x, mask,  relocation);
 #endif
 
+  r = bfd_reloc_ok;
   if (! trunc_p)
-    {
-      /* Now do an overflow check.  */
-      if (bfd_check_overflow ((signed_p ? 
-                              complain_overflow_signed : 
-                              complain_overflow_unsigned),
-                             len, 0, (8 * wordsz), 
-                             relocation) == bfd_reloc_overflow)
-       (*_bfd_error_handler) 
-         ("%s (%s + 0x%lx): relocation overflow: 0x%lx %sdoes not fit "
-          "within 0x%lx", 
-          input_bfd->filename, input_section->name, rel->r_offset,
-          relocation, (signed_p ? "(signed) " : ""), mask);
-    }
-         
+    /* Now do an overflow check.  */
+    r = bfd_check_overflow ((signed_p
+                            ? complain_overflow_signed
+                            : complain_overflow_unsigned),
+                           len, 0, (8 * wordsz),
+                           relocation);
+
   /* Do the deed.  */
   x = (x & ~(mask << shift)) | ((relocation & mask) << shift);
 
@@ -7132,10 +7702,11 @@ bfd_elf_perform_complex_relocation
          "         shifted mask: %8.8lx\n"
          " shifted/masked reloc: %8.8lx\n"
          "               result: %8.8lx\n",
-         relocation, (mask << shift), 
+         relocation, (mask << shift),
          ((relocation & mask) << shift), x);
 #endif
   put_value (wordsz, chunksz, input_bfd, x, contents + rel->r_offset);
+  return r;
 }
 
 /* When performing a relocatable link, the input relocations are
@@ -7631,7 +8202,7 @@ check_dynsym (bfd *abfd, Elf_Internal_Sym *sym)
   if (sym->st_shndx > SHN_HIRESERVE)
     {
       /* The gABI doesn't support dynamic symbols in output sections
-         beyond 64k.  */
+        beyond 64k.  */
       (*_bfd_error_handler)
        (_("%B: Too many sections: %d (>= %d)"),
         abfd, bfd_count_sections (abfd), SHN_LORESERVE);
@@ -7965,10 +8536,15 @@ elf_link_output_extsym (struct elf_link_hash_entry *h, void *data)
                sym.st_value += input_sec->output_section->vma;
                if (h->type == STT_TLS)
                  {
-                   /* STT_TLS symbols are relative to PT_TLS segment
-                      base.  */
-                   BFD_ASSERT (elf_hash_table (finfo->info)->tls_sec != NULL);
-                   sym.st_value -= elf_hash_table (finfo->info)->tls_sec->vma;
+                   asection *tls_sec = elf_hash_table (finfo->info)->tls_sec;
+                   if (tls_sec != NULL)
+                     sym.st_value -= tls_sec->vma;
+                   else
+                     {
+                       /* The TLS section may have been garbage collected.  */
+                       BFD_ASSERT (finfo->info->gc_sections
+                                   && !input_sec->gc_mark);
+                     }
                  }
              }
          }
@@ -8229,7 +8805,9 @@ _bfd_elf_check_kept_section (asection *sec, struct bfd_link_info *info)
     {
       if ((kept->flags & SEC_GROUP) != 0)
        kept = match_group_member (sec, kept, info);
-      if (kept != NULL && sec->size != kept->size)
+      if (kept != NULL
+         && ((sec->rawsize != 0 ? sec->rawsize : sec->size)
+             != (kept->rawsize != 0 ? kept->rawsize : kept->size)))
        kept = NULL;
       sec->kept_section = kept;
     }
@@ -8293,15 +8871,6 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
       if (isymbuf == NULL)
        return FALSE;
     }
-  /* evaluate_complex_relocation_symbols looks for symbols in
-     finfo->internal_syms.  */
-  else if (isymbuf != NULL && locsymcount != 0)
-    {
-      bfd_elf_get_elf_syms (input_bfd, symtab_hdr, locsymcount, 0,
-                           finfo->internal_syms,
-                           finfo->external_syms,
-                           finfo->locsym_shndx);
-    }
 
   /* Find local symbol sections and adjust values of symbols in
      SEC_MERGE sections.  Write out those local symbols we know are
@@ -8439,9 +9008,6 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
        return FALSE;
     }
 
-  if (! evaluate_complex_relocation_symbols (input_bfd, finfo, locsymcount))
-    return FALSE;
-
   /* Relocate the contents of each section.  */
   sym_hashes = elf_sym_hashes (input_bfd);
   for (o = input_bfd->sections; o != NULL; o = o->next)
@@ -8483,8 +9049,10 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
       if ((o->flags & SEC_RELOC) != 0)
        {
          Elf_Internal_Rela *internal_relocs;
+         Elf_Internal_Rela *rel, *relend;
          bfd_vma r_type_mask;
          int r_sym_shift;
+         int action_discarded;
          int ret;
 
          /* Get the swapped relocs.  */
@@ -8506,76 +9074,107 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
              r_sym_shift = 32;
            }
 
-         /* Run through the relocs looking for any against symbols
-            from discarded sections and section symbols from
-            removed link-once sections.  Complain about relocs
-            against discarded sections.  Zero relocs against removed
-            link-once sections.  */
+         action_discarded = -1;
          if (!elf_section_ignore_discarded_relocs (o))
+           action_discarded = (*bed->action_discarded) (o);
+
+         /* Run through the relocs evaluating complex reloc symbols and
+            looking for relocs against symbols from discarded sections
+            or section symbols from removed link-once sections.
+            Complain about relocs against discarded sections.  Zero
+            relocs against removed link-once sections.  */
+
+         rel = internal_relocs;
+         relend = rel + o->reloc_count * bed->s->int_rels_per_ext_rel;
+         for ( ; rel < relend; rel++)
            {
-             Elf_Internal_Rela *rel, *relend;
-             unsigned int action = (*bed->action_discarded) (o);
+             unsigned long r_symndx = rel->r_info >> r_sym_shift;
+             unsigned int s_type;
+             asection **ps, *sec;
+             struct elf_link_hash_entry *h = NULL;
+             const char *sym_name;
 
-             rel = internal_relocs;
-             relend = rel + o->reloc_count * bed->s->int_rels_per_ext_rel;
-             for ( ; rel < relend; rel++)
-               {
-                 unsigned long r_symndx = rel->r_info >> r_sym_shift;
-                 asection **ps, *sec;
-                 struct elf_link_hash_entry *h = NULL;
-                 const char *sym_name;
+             if (r_symndx == STN_UNDEF)
+               continue;
 
-                 if (r_symndx == STN_UNDEF)
-                   continue;
+             if (r_symndx >= locsymcount
+                 || (elf_bad_symtab (input_bfd)
+                     && finfo->sections[r_symndx] == NULL))
+               {
+                 h = sym_hashes[r_symndx - extsymoff];
 
-                 if (r_symndx >= locsymcount
-                     || (elf_bad_symtab (input_bfd)
-                         && finfo->sections[r_symndx] == NULL))
+                 /* Badly formatted input files can contain relocs that
+                    reference non-existant symbols.  Check here so that
+                    we do not seg fault.  */
+                 if (h == NULL)
                    {
-                     h = sym_hashes[r_symndx - extsymoff];
+                     char buffer [32];
 
-                     /* Badly formatted input files can contain relocs that
-                        reference non-existant symbols.  Check here so that
-                        we do not seg fault.  */
-                     if (h == NULL)
-                       {
-                         char buffer [32];
+                     sprintf_vma (buffer, rel->r_info);
+                     (*_bfd_error_handler)
+                       (_("error: %B contains a reloc (0x%s) for section %A "
+                          "that references a non-existent global symbol"),
+                        input_bfd, o, buffer);
+                     bfd_set_error (bfd_error_bad_value);
+                     return FALSE;
+                   }
 
-                         sprintf_vma (buffer, rel->r_info);
-                         (*_bfd_error_handler)
-                           (_("error: %B contains a reloc (0x%s) for section %A "
-                              "that references a non-existent global symbol"),
-                            input_bfd, o, buffer);
-                         bfd_set_error (bfd_error_bad_value);
-                         return FALSE;
-                       }
+                 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;
 
-                     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;
+                 s_type = h->type;
 
-                     if (h->root.type != bfd_link_hash_defined
-                         && h->root.type != bfd_link_hash_defweak)
-                       continue;
+                 ps = NULL;
+                 if (h->root.type == bfd_link_hash_defined
+                     || h->root.type == bfd_link_hash_defweak)
+                   ps = &h->root.u.def.section;
 
-                     ps = &h->root.u.def.section;
-                     sym_name = h->root.root.string;
-                   }
-                 else
-                   {
-                     Elf_Internal_Sym *sym = isymbuf + r_symndx;
-                     ps = &finfo->sections[r_symndx];
-                     sym_name = bfd_elf_sym_name (input_bfd,
-                                                  symtab_hdr,
-                                                  sym, *ps);
-                   }
+                 sym_name = h->root.root.string;
+               }
+             else
+               {
+                 Elf_Internal_Sym *sym = isymbuf + r_symndx;
+
+                 s_type = ELF_ST_TYPE (sym->st_info);
+                 ps = &finfo->sections[r_symndx];
+                 sym_name = bfd_elf_sym_name (input_bfd, symtab_hdr,
+                                              sym, *ps);
+               }
+
+             if (s_type == STT_RELC || s_type == STT_SRELC)
+               {
+                 bfd_vma val;
+                 bfd_vma dot = (rel->r_offset
+                                + o->output_offset + o->output_section->vma);
+#ifdef DEBUG
+                 printf ("Encountered a complex symbol!");
+                 printf (" (input_bfd %s, section %s, reloc %ld\n",
+                         input_bfd->filename, o->name, rel - internal_relocs);
+                 printf (" symbol: idx  %8.8lx, name %s\n",
+                         r_symndx, sym_name);
+                 printf (" reloc : info %8.8lx, addr %8.8lx\n",
+                         (unsigned long) rel->r_info,
+                         (unsigned long) rel->r_offset);
+#endif
+                 if (!eval_symbol (&val, &sym_name, input_bfd, finfo, dot,
+                                   isymbuf, locsymcount, s_type == STT_SRELC))
+                   return FALSE;
+
+                 /* Symbol evaluated OK.  Update to absolute value.  */
+                 set_symbol_value (input_bfd, isymbuf, locsymcount,
+                                   r_symndx, val);
+                 continue;
+               }
 
+             if (action_discarded != -1 && ps != NULL)
+               {
                  /* Complain if the definition comes from a
                     discarded section.  */
                  if ((sec = *ps) != NULL && elf_discarded_section (sec))
                    {
                      BFD_ASSERT (r_symndx != 0);
-                     if (action & COMPLAIN)
+                     if (action_discarded & COMPLAIN)
                        (*finfo->info->callbacks->einfo)
                          (_("%X`%s' referenced in section `%A' of %B: "
                             "defined in discarded section `%A' of %B\n"),
@@ -8587,7 +9186,7 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
                         FIXME: This is quite broken.  Modifying the
                         symbol here means we will be changing all later
                         uses of the symbol, not just in this section.  */
-                     if (action & PRETEND)
+                     if (action_discarded & PRETEND)
                        {
                          asection *kept;
 
@@ -9211,7 +9810,9 @@ elf_fixup_link_order (bfd *abfd, asection *o)
     return TRUE;
 
   sections = (struct bfd_link_order **)
-    xmalloc (seen_linkorder * sizeof (struct bfd_link_order *));
+    bfd_malloc (seen_linkorder * sizeof (struct bfd_link_order *));
+  if (sections == NULL)
+    return FALSE;
   seen_linkorder = 0;
 
   for (p = o->map_head.link_order; p != NULL; p = p->next)
@@ -9227,7 +9828,7 @@ elf_fixup_link_order (bfd *abfd, asection *o)
   for (n = 0; n < seen_linkorder; n++)
     {
       s = sections[n]->u.indirect.section;
-      offset &= ~(bfd_vma)((1 << s->alignment_power) - 1);
+      offset &= ~(bfd_vma) 0 << s->alignment_power;
       s->output_offset = offset;
       sections[n]->offset = offset;
       offset += sections[n]->size;
@@ -9266,6 +9867,9 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   size_t relativecount = 0;
   asection *reldyn = 0;
   bfd_size_type amt;
+  asection *attr_section = NULL;
+  bfd_vma attr_size = 0;
+  const char *std_attrs_section;
 
   if (! is_elf_hash_table (info->hash))
     return FALSE;
@@ -9313,6 +9917,40 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   finfo.symbuf_count = 0;
   finfo.shndxbuf_size = 0;
 
+  /* The object attributes have been merged.  Remove the input
+     sections from the link, and set the contents of the output
+     secton.  */
+  std_attrs_section = get_elf_backend_data (abfd)->obj_attrs_section;
+  for (o = abfd->sections; o != NULL; o = o->next)
+    {
+      if ((std_attrs_section && strcmp (o->name, std_attrs_section) == 0)
+         || strcmp (o->name, ".gnu.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;
+           }
+
+         attr_size = bfd_elf_obj_attr_size (abfd);
+         if (attr_size)
+           {
+             bfd_set_section_size (abfd, o, attr_size);
+             attr_section = o;
+             /* Skip this section later on.  */
+             o->map_head.link_order = NULL;
+           }
+         else
+           o->flags |= SEC_EXCLUDE;
+       }
+    }
+
   /* Count up the number of relocations we will output for each output
      section, so that we know the sizes of the reloc sections.  We
      also figure out some maximum sizes.  */
@@ -10158,7 +10796,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 
              if (dyn.d_tag == DT_TEXTREL)
                {
-                info->callbacks->einfo 
+                info->callbacks->einfo
                    (_("%P: warning: creating a DT_TEXTREL in a shared object.\n"));
                  break;
                }
@@ -10257,6 +10895,16 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 
   elf_tdata (abfd)->linker = TRUE;
 
+  if (attr_section)
+    {
+      bfd_byte *contents = bfd_malloc (attr_size);
+      if (contents == NULL)
+       return FALSE;   /* Bail out and fail.  */
+      bfd_elf_set_obj_attr_contents (abfd, contents, attr_size);
+      bfd_set_section_contents (abfd, attr_section, contents, 0, attr_size);
+      free (contents);
+    }
+
   return TRUE;
 
  error_return:
@@ -10292,6 +10940,139 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   return FALSE;
 }
 \f
+/* Initialize COOKIE for input bfd ABFD.  */
+
+static bfd_boolean
+init_reloc_cookie (struct elf_reloc_cookie *cookie,
+                  struct bfd_link_info *info, bfd *abfd)
+{
+  Elf_Internal_Shdr *symtab_hdr;
+  const struct elf_backend_data *bed;
+
+  bed = get_elf_backend_data (abfd);
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+
+  cookie->abfd = abfd;
+  cookie->sym_hashes = elf_sym_hashes (abfd);
+  cookie->bad_symtab = elf_bad_symtab (abfd);
+  if (cookie->bad_symtab)
+    {
+      cookie->locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym;
+      cookie->extsymoff = 0;
+    }
+  else
+    {
+      cookie->locsymcount = symtab_hdr->sh_info;
+      cookie->extsymoff = symtab_hdr->sh_info;
+    }
+
+  if (bed->s->arch_size == 32)
+    cookie->r_sym_shift = 8;
+  else
+    cookie->r_sym_shift = 32;
+
+  cookie->locsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
+  if (cookie->locsyms == NULL && cookie->locsymcount != 0)
+    {
+      cookie->locsyms = bfd_elf_get_elf_syms (abfd, symtab_hdr,
+                                             cookie->locsymcount, 0,
+                                             NULL, NULL, NULL);
+      if (cookie->locsyms == NULL)
+       {
+         info->callbacks->einfo (_("%P%X: can not read symbols: %E\n"));
+         return FALSE;
+       }
+      if (info->keep_memory)
+       symtab_hdr->contents = (bfd_byte *) cookie->locsyms;
+    }
+  return TRUE;
+}
+
+/* Free the memory allocated by init_reloc_cookie, if appropriate.  */
+
+static void
+fini_reloc_cookie (struct elf_reloc_cookie *cookie, bfd *abfd)
+{
+  Elf_Internal_Shdr *symtab_hdr;
+
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  if (cookie->locsyms != NULL
+      && symtab_hdr->contents != (unsigned char *) cookie->locsyms)
+    free (cookie->locsyms);
+}
+
+/* Initialize the relocation information in COOKIE for input section SEC
+   of input bfd ABFD.  */
+
+static bfd_boolean
+init_reloc_cookie_rels (struct elf_reloc_cookie *cookie,
+                       struct bfd_link_info *info, bfd *abfd,
+                       asection *sec)
+{
+  const struct elf_backend_data *bed;
+
+  if (sec->reloc_count == 0)
+    {
+      cookie->rels = NULL;
+      cookie->relend = NULL;
+    }
+  else
+    {
+      bed = get_elf_backend_data (abfd);
+
+      cookie->rels = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
+                                               info->keep_memory);
+      if (cookie->rels == NULL)
+       return FALSE;
+      cookie->rel = cookie->rels;
+      cookie->relend = (cookie->rels
+                       + sec->reloc_count * bed->s->int_rels_per_ext_rel);
+    }
+  cookie->rel = cookie->rels;
+  return TRUE;
+}
+
+/* Free the memory allocated by init_reloc_cookie_rels,
+   if appropriate.  */
+
+static void
+fini_reloc_cookie_rels (struct elf_reloc_cookie *cookie,
+                       asection *sec)
+{
+  if (cookie->rels && elf_section_data (sec)->relocs != cookie->rels)
+    free (cookie->rels);
+}
+
+/* Initialize the whole of COOKIE for input section SEC.  */
+
+static bfd_boolean
+init_reloc_cookie_for_section (struct elf_reloc_cookie *cookie,
+                              struct bfd_link_info *info,
+                              asection *sec)
+{
+  if (!init_reloc_cookie (cookie, info, sec->owner))
+    goto error1;
+  if (!init_reloc_cookie_rels (cookie, info, sec->owner, sec))
+    goto error2;
+  return TRUE;
+
+ error2:
+  fini_reloc_cookie (cookie, sec->owner);
+ error1:
+  return FALSE;
+}
+
+/* Free the memory allocated by init_reloc_cookie_for_section,
+   if appropriate.  */
+
+static void
+fini_reloc_cookie_for_section (struct elf_reloc_cookie *cookie,
+                              asection *sec)
+{
+  fini_reloc_cookie_rels (cookie, sec);
+  fini_reloc_cookie (cookie, sec->owner);
+}
+\f
 /* Garbage collect unused sections.  */
 
 /* Default gc_mark_hook.  */
@@ -10324,6 +11105,63 @@ _bfd_elf_gc_mark_hook (asection *sec,
   return NULL;
 }
 
+/* COOKIE->rel describes a relocation against section SEC, which is
+   a section we've decided to keep.  Return the section that contains
+   the relocation symbol, or NULL if no section contains it.  */
+
+asection *
+_bfd_elf_gc_mark_rsec (struct bfd_link_info *info, asection *sec,
+                      elf_gc_mark_hook_fn gc_mark_hook,
+                      struct elf_reloc_cookie *cookie)
+{
+  unsigned long r_symndx;
+  struct elf_link_hash_entry *h;
+
+  r_symndx = cookie->rel->r_info >> cookie->r_sym_shift;
+  if (r_symndx == 0)
+    return NULL;
+
+  if (r_symndx >= cookie->locsymcount
+      || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL)
+    {
+      h = cookie->sym_hashes[r_symndx - cookie->extsymoff];
+      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;
+      return (*gc_mark_hook) (sec, info, cookie->rel, h, NULL);
+    }
+
+  return (*gc_mark_hook) (sec, info, cookie->rel, NULL,
+                         &cookie->locsyms[r_symndx]);
+}
+
+/* COOKIE->rel describes a relocation against section SEC, which is
+   a section we've decided to keep.  Mark the section that contains
+   the relocation symbol.  IS_EH is true if the mark comes from
+   .eh_frame.  */
+
+bfd_boolean
+_bfd_elf_gc_mark_reloc (struct bfd_link_info *info,
+                       asection *sec,
+                       elf_gc_mark_hook_fn gc_mark_hook,
+                       struct elf_reloc_cookie *cookie,
+                       bfd_boolean is_eh)
+{
+  asection *rsec;
+
+  rsec = _bfd_elf_gc_mark_rsec (info, sec, gc_mark_hook, cookie);
+  if (rsec && !rsec->gc_mark)
+    {
+      if (bfd_get_flavour (rsec->owner) != bfd_target_elf_flavour)
+       rsec->gc_mark = 1;
+      else if (is_eh)
+       rsec->gc_mark_from_eh = 1;
+      else if (!_bfd_elf_gc_mark (info, rsec, gc_mark_hook))
+       return FALSE;
+    }
+  return TRUE;
+}
+
 /* The mark phase of garbage collection.  For a given section, mark
    it and any sections in this section's group, and all the sections
    which define symbols to which it refers.  */
@@ -10350,103 +11188,22 @@ _bfd_elf_gc_mark (struct bfd_link_info *info,
   is_eh = strcmp (sec->name, ".eh_frame") == 0;
   if ((sec->flags & SEC_RELOC) != 0 && sec->reloc_count > 0)
     {
-      Elf_Internal_Rela *relstart, *rel, *relend;
-      Elf_Internal_Shdr *symtab_hdr;
-      struct elf_link_hash_entry **sym_hashes;
-      size_t nlocsyms;
-      size_t extsymoff;
-      bfd *input_bfd = sec->owner;
-      const struct elf_backend_data *bed = get_elf_backend_data (input_bfd);
-      Elf_Internal_Sym *isym = NULL;
-      int r_sym_shift;
-
-      symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
-      sym_hashes = elf_sym_hashes (input_bfd);
-
-      /* Read the local symbols.  */
-      if (elf_bad_symtab (input_bfd))
-       {
-         nlocsyms = symtab_hdr->sh_size / bed->s->sizeof_sym;
-         extsymoff = 0;
-       }
-      else
-       extsymoff = nlocsyms = symtab_hdr->sh_info;
-
-      isym = (Elf_Internal_Sym *) symtab_hdr->contents;
-      if (isym == NULL && nlocsyms != 0)
-       {
-         isym = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, nlocsyms, 0,
-                                      NULL, NULL, NULL);
-         if (isym == NULL)
-           return FALSE;
-       }
-
-      /* Read the relocations.  */
-      relstart = _bfd_elf_link_read_relocs (input_bfd, sec, NULL, NULL,
-                                           info->keep_memory);
-      if (relstart == NULL)
-       {
-         ret = FALSE;
-         goto out1;
-       }
-      relend = relstart + sec->reloc_count * bed->s->int_rels_per_ext_rel;
+      struct elf_reloc_cookie cookie;
 
-      if (bed->s->arch_size == 32)
-       r_sym_shift = 8;
+      if (!init_reloc_cookie_for_section (&cookie, info, sec))
+       ret = FALSE;
       else
-       r_sym_shift = 32;
-
-      for (rel = relstart; rel < relend; rel++)
-       {
-         unsigned long r_symndx;
-         asection *rsec;
-         struct elf_link_hash_entry *h;
-
-         r_symndx = rel->r_info >> r_sym_shift;
-         if (r_symndx == 0)
-           continue;
-
-         if (r_symndx >= nlocsyms
-             || ELF_ST_BIND (isym[r_symndx].st_info) != STB_LOCAL)
-           {
-             h = sym_hashes[r_symndx - extsymoff];
-             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;
-             rsec = (*gc_mark_hook) (sec, info, rel, h, NULL);
-           }
-         else
-           {
-             rsec = (*gc_mark_hook) (sec, info, rel, NULL, &isym[r_symndx]);
-           }
-
-         if (rsec && !rsec->gc_mark)
-           {
-             if (bfd_get_flavour (rsec->owner) != bfd_target_elf_flavour)
-               rsec->gc_mark = 1;
-             else if (is_eh)
-               rsec->gc_mark_from_eh = 1;
-             else if (!_bfd_elf_gc_mark (info, rsec, gc_mark_hook))
-               {
-                 ret = FALSE;
-                 goto out2;
-               }
-           }
-       }
-
-    out2:
-      if (elf_section_data (sec)->relocs != relstart)
-       free (relstart);
-    out1:
-      if (isym != NULL && symtab_hdr->contents != (unsigned char *) isym)
        {
-         if (! info->keep_memory)
-           free (isym);
-         else
-           symtab_hdr->contents = (unsigned char *) isym;
+         for (; cookie.rel < cookie.relend; cookie.rel++)
+           if (!_bfd_elf_gc_mark_reloc (info, sec, gc_mark_hook,
+                                        &cookie, is_eh))
+             {
+               ret = FALSE;
+               break;
+             }
+         fini_reloc_cookie_for_section (&cookie, sec);
        }
     }
-
   return ret;
 }
 
@@ -10516,7 +11273,7 @@ elf_gc_sweep (bfd *abfd, struct bfd_link_info *info)
             to remove a section from the output.  */
          o->flags |= SEC_EXCLUDE;
 
-         if (info->print_gc_sections == TRUE)
+         if (info->print_gc_sections && o->size != 0)
            _bfd_error_handler (_("Removing unused section '%s' in file '%B'"), sub, o->name);
 
          /* But we also have to update some of the relocation
@@ -11129,16 +11886,15 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 {
   struct elf_reloc_cookie cookie;
   asection *stab, *eh;
-  Elf_Internal_Shdr *symtab_hdr;
   const struct elf_backend_data *bed;
   bfd *abfd;
-  unsigned int count;
   bfd_boolean ret = FALSE;
 
   if (info->traditional_format
       || !is_elf_hash_table (info->hash))
     return FALSE;
 
+  _bfd_elf_begin_eh_frame_parsing (info);
   for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
     {
       if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
@@ -11171,96 +11927,39 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
          && bed->elf_backend_discard_info == NULL)
        continue;
 
-      symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
-      cookie.abfd = abfd;
-      cookie.sym_hashes = elf_sym_hashes (abfd);
-      cookie.bad_symtab = elf_bad_symtab (abfd);
-      if (cookie.bad_symtab)
-       {
-         cookie.locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym;
-         cookie.extsymoff = 0;
-       }
-      else
-       {
-         cookie.locsymcount = symtab_hdr->sh_info;
-         cookie.extsymoff = symtab_hdr->sh_info;
-       }
-
-      if (bed->s->arch_size == 32)
-       cookie.r_sym_shift = 8;
-      else
-       cookie.r_sym_shift = 32;
-
-      cookie.locsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
-      if (cookie.locsyms == NULL && cookie.locsymcount != 0)
-       {
-         cookie.locsyms = bfd_elf_get_elf_syms (abfd, symtab_hdr,
-                                                cookie.locsymcount, 0,
-                                                NULL, NULL, NULL);
-         if (cookie.locsyms == NULL)
-           {
-             info->callbacks->einfo (_("%P%X: can not read symbols: %E\n"));
-             return FALSE;
-           }
-       }
+      if (!init_reloc_cookie (&cookie, info, abfd))
+       return FALSE;
 
-      if (stab != NULL)
+      if (stab != NULL
+         && stab->reloc_count > 0
+         && init_reloc_cookie_rels (&cookie, info, abfd, stab))
        {
-         cookie.rels = NULL;
-         count = stab->reloc_count;
-         if (count != 0)
-           cookie.rels = _bfd_elf_link_read_relocs (abfd, stab, NULL, NULL,
-                                                    info->keep_memory);
-         if (cookie.rels != NULL)
-           {
-             cookie.rel = cookie.rels;
-             cookie.relend = cookie.rels;
-             cookie.relend += count * bed->s->int_rels_per_ext_rel;
-             if (_bfd_discard_section_stabs (abfd, stab,
-                                             elf_section_data (stab)->sec_info,
-                                             bfd_elf_reloc_symbol_deleted_p,
-                                             &cookie))
-               ret = TRUE;
-             if (elf_section_data (stab)->relocs != cookie.rels)
-               free (cookie.rels);
-           }
+         if (_bfd_discard_section_stabs (abfd, stab,
+                                         elf_section_data (stab)->sec_info,
+                                         bfd_elf_reloc_symbol_deleted_p,
+                                         &cookie))
+           ret = TRUE;
+         fini_reloc_cookie_rels (&cookie, stab);
        }
 
-      if (eh != NULL)
+      if (eh != NULL
+         && init_reloc_cookie_rels (&cookie, info, abfd, eh))
        {
-         cookie.rels = NULL;
-         count = eh->reloc_count;
-         if (count != 0)
-           cookie.rels = _bfd_elf_link_read_relocs (abfd, eh, NULL, NULL,
-                                                    info->keep_memory);
-         cookie.rel = cookie.rels;
-         cookie.relend = cookie.rels;
-         if (cookie.rels != NULL)
-           cookie.relend += count * bed->s->int_rels_per_ext_rel;
-
+         _bfd_elf_parse_eh_frame (abfd, info, eh, &cookie);
          if (_bfd_elf_discard_section_eh_frame (abfd, info, eh,
                                                 bfd_elf_reloc_symbol_deleted_p,
                                                 &cookie))
            ret = TRUE;
-
-         if (cookie.rels != NULL
-             && elf_section_data (eh)->relocs != cookie.rels)
-           free (cookie.rels);
+         fini_reloc_cookie_rels (&cookie, eh);
        }
 
       if (bed->elf_backend_discard_info != NULL
          && (*bed->elf_backend_discard_info) (abfd, &cookie, info))
        ret = TRUE;
 
-      if (cookie.locsyms != NULL
-         && symtab_hdr->contents != (unsigned char *) cookie.locsyms)
-       {
-         if (! info->keep_memory)
-           free (cookie.locsyms);
-         else
-           symtab_hdr->contents = (unsigned char *) cookie.locsyms;
-       }
+      fini_reloc_cookie (&cookie, abfd);
     }
+  _bfd_elf_end_eh_frame_parsing (info);
 
   if (info->eh_frame_hdr
       && !info->relocatable
@@ -11450,7 +12149,8 @@ _bfd_elf_section_already_linked (bfd *abfd, struct bfd_section *sec,
        }
 
   /* This is the first section with this name.  Record it.  */
-  bfd_section_already_linked_table_insert (already_linked_list, sec);
+  if (! bfd_section_already_linked_table_insert (already_linked_list, sec))
+    info->callbacks->einfo (_("%F%P: already_linked_table: %E"));
 }
 
 bfd_boolean
This page took 0.065759 seconds and 4 git commands to generate.