include/elf
[deliverable/binutils-gdb.git] / bfd / elflink.c
index b58cfa6485c069ca5f675cc73fedcc82cdd43dc1..54ad2af43aba5642b92487b371b4fa650ebe2fa1 100644 (file)
@@ -1,12 +1,13 @@
 /* ELF linking support for BFD.
    Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
 /* ELF linking support for BFD.
    Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-   2005, 2006, 2007 Free Software Foundation, Inc.
+   2005, 2006, 2007, 2008, 2009
+   Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
    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
 
    This file is part of BFD, the Binary File Descriptor library.
 
    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,
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
@@ -16,7 +17,8 @@
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
 
    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"
 
 #include "sysdep.h"
 #include "bfd.h"
 #include "libiberty.h"
 #include "objalloc.h"
 
 #include "libiberty.h"
 #include "objalloc.h"
 
+/* This struct is used to pass information to routines called via
+   elf_link_hash_traverse which must return failure.  */
+
+struct elf_info_failed
+{
+  struct bfd_link_info *info;
+  struct bfd_elf_version_tree *verdefs;
+  bfd_boolean failed;
+};
+
+/* This structure is used to pass information to
+   _bfd_elf_link_find_version_dependencies.  */
+
+struct elf_find_verdep_info
+{
+  /* General link information.  */
+  struct bfd_link_info *info;
+  /* The number of dependencies.  */
+  unsigned int vers;
+  /* Whether we had a failure.  */
+  bfd_boolean failed;
+};
+
+static bfd_boolean _bfd_elf_fix_symbol_flags
+  (struct elf_link_hash_entry *, struct elf_info_failed *);
+
 /* Define a symbol in a dynamic linkage section.  */
 
 struct elf_link_hash_entry *
 /* Define a symbol in a dynamic linkage section.  */
 
 struct elf_link_hash_entry *
@@ -308,7 +336,7 @@ _bfd_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
     }
 
   s = bfd_make_section_with_flags (abfd,
     }
 
   s = bfd_make_section_with_flags (abfd,
-                                  (bed->default_use_rela_p
+                                  (bed->rela_plts_and_copies_p
                                    ? ".rela.plt" : ".rel.plt"),
                                   flags | SEC_READONLY);
   if (s == NULL
                                    ? ".rela.plt" : ".rel.plt"),
                                   flags | SEC_READONLY);
   if (s == NULL
@@ -346,7 +374,7 @@ _bfd_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
       if (! info->shared)
        {
          s = bfd_make_section_with_flags (abfd,
       if (! info->shared)
        {
          s = bfd_make_section_with_flags (abfd,
-                                          (bed->default_use_rela_p
+                                          (bed->rela_plts_and_copies_p
                                            ? ".rela.bss" : ".rel.bss"),
                                           flags | SEC_READONLY);
          if (s == NULL
                                            ? ".rela.bss" : ".rel.bss"),
                                           flags | SEC_READONLY);
          if (s == NULL
@@ -436,7 +464,7 @@ bfd_elf_link_record_dynamic_symbol (struct bfd_link_info *info,
 \f
 /* Mark a symbol dynamic.  */
 
 \f
 /* Mark a symbol dynamic.  */
 
-void
+static void
 bfd_elf_link_mark_dynamic_symbol (struct bfd_link_info *info,
                                  struct elf_link_hash_entry *h,
                                  Elf_Internal_Sym *sym)
 bfd_elf_link_mark_dynamic_symbol (struct bfd_link_info *info,
                                  struct elf_link_hash_entry *h,
                                  Elf_Internal_Sym *sym)
@@ -451,7 +479,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)))
        && (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;
          && h->root.type == bfd_link_hash_new
          && (*d->match) (&d->head, NULL, h->root.root.string)))
     h->dynamic = 1;
@@ -467,8 +495,9 @@ bfd_elf_record_link_assignment (bfd *output_bfd,
                                bfd_boolean provide,
                                bfd_boolean hidden)
 {
                                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;
   struct elf_link_hash_table *htab;
+  const struct elf_backend_data *bed;
 
   if (!is_elf_hash_table (info->hash))
     return TRUE;
 
   if (!is_elf_hash_table (info->hash))
     return TRUE;
@@ -478,21 +507,43 @@ bfd_elf_record_link_assignment (bfd *output_bfd,
   if (h == NULL)
     return provide;
 
   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);
       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;
       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
     }
 
   /* If this symbol is being provided by the linker script, and it is
@@ -594,8 +645,7 @@ bfd_elf_link_record_local_dynamic_symbol (struct bfd_link_info *info,
     }
 
   if (entry->isym.st_shndx != SHN_UNDEF
     }
 
   if (entry->isym.st_shndx != SHN_UNDEF
-      && (entry->isym.st_shndx < SHN_LORESERVE
-         || entry->isym.st_shndx > SHN_HIRESERVE))
+      && entry->isym.st_shndx < SHN_LORESERVE)
     {
       asection *s;
 
     {
       asection *s;
 
@@ -799,6 +849,54 @@ _bfd_elf_link_renumber_dynsyms (bfd *output_bfd,
   return dynsymcount;
 }
 
   return dynsymcount;
 }
 
+/* Merge st_other field.  */
+
+static void
+elf_merge_st_other (bfd *abfd, struct elf_link_hash_entry *h,
+                   Elf_Internal_Sym *isym, bfd_boolean definition,
+                   bfd_boolean dynamic)
+{
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+
+  /* If st_other has a processor-specific meaning, specific
+     code might be needed here. We never merge the visibility
+     attribute with the one from a dynamic object.  */
+  if (bed->elf_backend_merge_symbol_attribute)
+    (*bed->elf_backend_merge_symbol_attribute) (h, isym, definition,
+                                               dynamic);
+
+  /* If this symbol has default visibility and the user has requested
+     we not re-export it, then mark it as hidden.  */
+  if (definition
+      && !dynamic
+      && (abfd->no_export
+         || (abfd->my_archive && abfd->my_archive->no_export))
+      && ELF_ST_VISIBILITY (isym->st_other) != STV_INTERNAL)
+    isym->st_other = (STV_HIDDEN
+                     | (isym->st_other & ~ELF_ST_VISIBILITY (-1)));
+
+  if (!dynamic && ELF_ST_VISIBILITY (isym->st_other) != 0)
+    {
+      unsigned char hvis, symvis, other, nvis;
+
+      /* Only merge the visibility. Leave the remainder of the
+        st_other field to elf_backend_merge_symbol_attribute.  */
+      other = h->other & ~ELF_ST_VISIBILITY (-1);
+
+      /* Combine visibilities, using the most constraining one.  */
+      hvis = ELF_ST_VISIBILITY (h->other);
+      symvis = ELF_ST_VISIBILITY (isym->st_other);
+      if (! hvis)
+       nvis = symvis;
+      else if (! symvis)
+       nvis = hvis;
+      else
+       nvis = hvis < symvis ? hvis : symvis;
+
+      h->other = other | nvis;
+    }
+}
+
 /* This function is called when we want to define a new symbol.  It
    handles the various cases which arise when we find a definition in
    a dynamic object, or when there is already a definition in a
 /* This function is called when we want to define a new symbol.  It
    handles the various cases which arise when we find a definition in
    a dynamic object, or when there is already a definition in a
@@ -831,7 +929,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
   int bind;
   bfd *oldbfd;
   bfd_boolean newdyn, olddyn, olddef, newdef, newdyncommon, olddyncommon;
   int bind;
   bfd *oldbfd;
   bfd_boolean newdyn, olddyn, olddef, newdef, newdyncommon, olddyncommon;
-  bfd_boolean newweak, oldweak;
+  bfd_boolean newweak, oldweak, newfunc, oldfunc;
   const struct elf_backend_data *bed;
 
   *skip = FALSE;
   const struct elf_backend_data *bed;
 
   *skip = FALSE;
@@ -858,9 +956,11 @@ _bfd_elf_merge_symbol (bfd *abfd,
     return FALSE;
   *sym_hash = h;
 
     return FALSE;
   *sym_hash = h;
 
+  bed = get_elf_backend_data (abfd);
+
   /* This code is for coping with dynamic objects, and is only useful
      if we are doing an ELF link.  */
   /* This code is for coping with dynamic objects, and is only useful
      if we are doing an ELF link.  */
-  if (info->hash->creator != abfd->xvec)
+  if (!(*bed->relocs_compatible) (abfd->xvec, info->output_bfd->xvec))
     return TRUE;
 
   /* For merging, we only care about real symbols.  */
     return TRUE;
 
   /* For merging, we only care about real symbols.  */
@@ -947,7 +1047,15 @@ _bfd_elf_merge_symbol (bfd *abfd,
            && h->root.type != bfd_link_hash_undefweak
            && h->root.type != bfd_link_hash_common);
 
            && h->root.type != bfd_link_hash_undefweak
            && h->root.type != bfd_link_hash_common);
 
-  bed = get_elf_backend_data (abfd);
+  /* NEWFUNC and OLDFUNC indicate whether the new or old symbol,
+     respectively, appear to be a function.  */
+
+  newfunc = (ELF_ST_TYPE (sym->st_info) != STT_NOTYPE
+            && bed->is_function_type (ELF_ST_TYPE (sym->st_info)));
+
+  oldfunc = (h->type != STT_NOTYPE
+            && bed->is_function_type (h->type));
+
   /* When we try to create a default indirect symbol from the dynamic
      definition with the default version, we skip it if its type and
      the type of existing regular definition mismatch.  We only do it
   /* When we try to create a default indirect symbol from the dynamic
      definition with the default version, we skip it if its type and
      the type of existing regular definition mismatch.  We only do it
@@ -963,8 +1071,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
       && ELF_ST_TYPE (sym->st_info) != h->type
       && ELF_ST_TYPE (sym->st_info) != STT_NOTYPE
       && h->type != STT_NOTYPE
       && ELF_ST_TYPE (sym->st_info) != h->type
       && ELF_ST_TYPE (sym->st_info) != STT_NOTYPE
       && h->type != STT_NOTYPE
-      && !(bed->is_function_type (ELF_ST_TYPE (sym->st_info))
-          && bed->is_function_type (h->type)))
+      && !(newfunc && oldfunc))
     {
       *skip = TRUE;
       return TRUE;
     {
       *skip = TRUE;
       return TRUE;
@@ -1155,9 +1262,8 @@ _bfd_elf_merge_symbol (bfd *abfd,
   if (olddef && newdyn)
     oldweak = FALSE;
 
   if (olddef && newdyn)
     oldweak = FALSE;
 
-  /* Allow changes between different types of funciton symbol.  */
-  if (bed->is_function_type (ELF_ST_TYPE (sym->st_info))
-      && bed->is_function_type (h->type))
+  /* Allow changes between different types of function symbol.  */
+  if (newfunc && oldfunc)
     *type_change_ok = TRUE;
 
   /* It's OK to change the type if either the existing symbol or the
     *type_change_ok = TRUE;
 
   /* It's OK to change the type if either the existing symbol or the
@@ -1206,7 +1312,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
       && (sec->flags & SEC_ALLOC) != 0
       && (sec->flags & SEC_LOAD) == 0
       && sym->st_size > 0
       && (sec->flags & SEC_ALLOC) != 0
       && (sec->flags & SEC_LOAD) == 0
       && sym->st_size > 0
-      && !bed->is_function_type (ELF_ST_TYPE (sym->st_info)))
+      && !newfunc)
     newdyncommon = TRUE;
   else
     newdyncommon = FALSE;
     newdyncommon = TRUE;
   else
     newdyncommon = FALSE;
@@ -1218,7 +1324,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
       && (h->root.u.def.section->flags & SEC_ALLOC) != 0
       && (h->root.u.def.section->flags & SEC_LOAD) == 0
       && h->size > 0
       && (h->root.u.def.section->flags & SEC_ALLOC) != 0
       && (h->root.u.def.section->flags & SEC_LOAD) == 0
       && h->size > 0
-      && !bed->is_function_type (h->type))
+      && !oldfunc)
     olddyncommon = TRUE;
   else
     olddyncommon = FALSE;
     olddyncommon = TRUE;
   else
     olddyncommon = FALSE;
@@ -1278,8 +1384,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
       && newdef
       && (olddef
          || (h->root.type == bfd_link_hash_common
       && newdef
       && (olddef
          || (h->root.type == bfd_link_hash_common
-             && (newweak
-                 || bed->is_function_type (ELF_ST_TYPE (sym->st_info))))))
+             && (newweak || newfunc))))
     {
       *override = TRUE;
       newdef = FALSE;
     {
       *override = TRUE;
       newdef = FALSE;
@@ -1317,7 +1422,22 @@ _bfd_elf_merge_symbol (bfd *abfd,
 
   /* Skip weak definitions of symbols that are already defined.  */
   if (newdef && olddef && newweak)
 
   /* Skip weak definitions of symbols that are already defined.  */
   if (newdef && olddef && newweak)
-    *skip = TRUE;
+    {
+      *skip = TRUE;
+
+      /* Merge st_other.  If the symbol already has a dynamic index,
+        but visibility says it should not be visible, turn it into a
+        local symbol.  */
+      elf_merge_st_other (abfd, h, sym, newdef, newdyn);
+      if (h->dynindx != -1)
+       switch (ELF_ST_VISIBILITY (h->other))
+         {
+         case STV_INTERNAL:
+         case STV_HIDDEN:
+           (*bed->elf_backend_hide_symbol) (info, h, TRUE);
+           break;
+         }
+    }
 
   /* If the old symbol is from a dynamic object, and the new symbol is
      a definition which is not from a dynamic object, then the new
 
   /* If the old symbol is from a dynamic object, and the new symbol is
      a definition which is not from a dynamic object, then the new
@@ -1333,8 +1453,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
   if (!newdyn
       && (newdef
          || (bfd_is_com_section (sec)
   if (!newdyn
       && (newdef
          || (bfd_is_com_section (sec)
-             && (oldweak
-                 || bed->is_function_type (h->type))))
+             && (oldweak || oldfunc)))
       && olddyn
       && olddef
       && h->def_dynamic)
       && olddyn
       && olddef
       && h->def_dynamic)
@@ -1354,7 +1473,17 @@ _bfd_elf_merge_symbol (bfd *abfd,
         overriding a function.  */
 
       if (bfd_is_com_section (sec))
         overriding a function.  */
 
       if (bfd_is_com_section (sec))
-       *type_change_ok = TRUE;
+       {
+         if (oldfunc)
+           {
+             /* If a common symbol overrides a function, make sure
+                that it isn't defined dynamically nor has type
+                function.  */
+             h->def_dynamic = 0;
+             h->type = STT_NOTYPE;
+           }
+         *type_change_ok = TRUE;
+       }
 
       if ((*sym_hash)->root.type == bfd_link_hash_indirect)
        flip = *sym_hash;
 
       if ((*sym_hash)->root.type == bfd_link_hash_indirect)
        flip = *sym_hash;
@@ -1416,10 +1545,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;
         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);
       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;
       if (h->def_dynamic)
        {
          h->def_dynamic = 0;
@@ -1435,7 +1564,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
    symbol is described by H, NAME, SYM, PSEC, VALUE, and OVERRIDE.  We
    set DYNSYM if the new indirect symbol is dynamic.  */
 
    symbol is described by H, NAME, SYM, PSEC, VALUE, and OVERRIDE.  We
    set DYNSYM if the new indirect symbol is dynamic.  */
 
-bfd_boolean
+static bfd_boolean
 _bfd_elf_add_default_symbol (bfd *abfd,
                             struct bfd_link_info *info,
                             struct elf_link_hash_entry *h,
 _bfd_elf_add_default_symbol (bfd *abfd,
                             struct bfd_link_info *info,
                             struct elf_link_hash_entry *h,
@@ -1669,10 +1798,84 @@ nondefault:
   return TRUE;
 }
 \f
   return TRUE;
 }
 \f
+static struct bfd_elf_version_tree *
+find_version_for_sym (struct bfd_elf_version_tree *verdefs,
+                     const char *sym_name,
+                     bfd_boolean *hide)
+{
+  struct bfd_elf_version_tree *t;
+  struct bfd_elf_version_tree *local_ver, *global_ver, *exist_ver;
+
+  local_ver = NULL;
+  global_ver = NULL;
+  exist_ver = NULL;
+  for (t = verdefs; t != NULL; t = t->next)
+    {
+      if (t->globals.list != NULL)
+       {
+         struct bfd_elf_version_expr *d = NULL;
+
+         while ((d = (*t->match) (&t->globals, d, sym_name)) != NULL)
+           {
+             global_ver = t;
+             if (d->symver)
+               exist_ver = t;
+             d->script = 1;
+             /* If the match is a wildcard pattern, keep looking for
+                a more explicit, perhaps even local, match.  */
+             if (d->literal)
+               break;
+           }
+
+         if (d != NULL)
+           break;
+       }
+
+      if (t->locals.list != NULL)
+       {
+         struct bfd_elf_version_expr *d = NULL;
+
+         while ((d = (*t->match) (&t->locals, d, sym_name)) != NULL)
+           {
+             local_ver = t;
+             /* If the match is a wildcard pattern, keep looking for
+                a more explicit, perhaps even global, match.  */
+             if (d->literal)
+               {
+                 /* An exact match overrides a global wildcard.  */
+                 global_ver = NULL;
+                 break;
+               }
+           }
+
+         if (d != NULL)
+           break;
+       }
+    }
+
+  if (global_ver != NULL)
+    {
+      /* If we already have a versioned symbol that matches the
+        node for this symbol, then we don't want to create a
+        duplicate from the unversioned symbol.  Instead hide the
+        unversioned symbol.  */
+      *hide = exist_ver == global_ver;
+      return global_ver;
+    }
+
+  if (local_ver != NULL)
+    {
+      *hide = TRUE;
+      return local_ver;
+    }
+
+  return NULL;
+}
+
 /* This routine is used to export all defined symbols into the dynamic
    symbol table.  It is called via elf_link_hash_traverse.  */
 
 /* This routine is used to export all defined symbols into the dynamic
    symbol table.  It is called via elf_link_hash_traverse.  */
 
-bfd_boolean
+static bfd_boolean
 _bfd_elf_export_symbol (struct elf_link_hash_entry *h, void *data)
 {
   struct elf_info_failed *eif = data;
 _bfd_elf_export_symbol (struct elf_link_hash_entry *h, void *data)
 {
   struct elf_info_failed *eif = data;
@@ -1692,29 +1895,12 @@ _bfd_elf_export_symbol (struct elf_link_hash_entry *h, void *data)
       && (h->def_regular
          || h->ref_regular))
     {
       && (h->def_regular
          || h->ref_regular))
     {
-      struct bfd_elf_version_tree *t;
-      struct bfd_elf_version_expr *d;
-
-      for (t = eif->verdefs; t != NULL; t = t->next)
-       {
-         if (t->globals.list != NULL)
-           {
-             d = (*t->match) (&t->globals, NULL, h->root.root.string);
-             if (d != NULL)
-               goto doit;
-           }
+      bfd_boolean hide;
 
 
-         if (t->locals.list != NULL)
-           {
-             d = (*t->match) (&t->locals, NULL, h->root.root.string);
-             if (d != NULL)
-               return TRUE;
-           }
-       }
-
-      if (!eif->verdefs)
+      if (eif->verdefs == NULL
+         || (find_version_for_sym (eif->verdefs, h->root.root.string, &hide)
+             && !hide))
        {
        {
-       doit:
          if (! bfd_elf_link_record_dynamic_symbol (eif->info, h))
            {
              eif->failed = TRUE;
          if (! bfd_elf_link_record_dynamic_symbol (eif->info, h))
            {
              eif->failed = TRUE;
@@ -1731,7 +1917,7 @@ _bfd_elf_export_symbol (struct elf_link_hash_entry *h, void *data)
    dependencies.  This will be put into the .gnu.version_r section.
    This function is called via elf_link_hash_traverse.  */
 
    dependencies.  This will be put into the .gnu.version_r section.
    This function is called via elf_link_hash_traverse.  */
 
-bfd_boolean
+static bfd_boolean
 _bfd_elf_link_find_version_dependencies (struct elf_link_hash_entry *h,
                                         void *data)
 {
 _bfd_elf_link_find_version_dependencies (struct elf_link_hash_entry *h,
                                         void *data)
 {
@@ -1752,7 +1938,9 @@ _bfd_elf_link_find_version_dependencies (struct elf_link_hash_entry *h,
     return TRUE;
 
   /* See if we already know about this version.  */
     return TRUE;
 
   /* See if we already know about this version.  */
-  for (t = elf_tdata (rinfo->output_bfd)->verref; t != NULL; t = t->vn_nextref)
+  for (t = elf_tdata (rinfo->info->output_bfd)->verref;
+       t != NULL;
+       t = t->vn_nextref)
     {
       if (t->vn_bfd != h->verinfo.verdef->vd_bfd)
        continue;
     {
       if (t->vn_bfd != h->verinfo.verdef->vd_bfd)
        continue;
@@ -1769,7 +1957,7 @@ _bfd_elf_link_find_version_dependencies (struct elf_link_hash_entry *h,
   if (t == NULL)
     {
       amt = sizeof *t;
   if (t == NULL)
     {
       amt = sizeof *t;
-      t = bfd_zalloc (rinfo->output_bfd, amt);
+      t = bfd_zalloc (rinfo->info->output_bfd, amt);
       if (t == NULL)
        {
          rinfo->failed = TRUE;
       if (t == NULL)
        {
          rinfo->failed = TRUE;
@@ -1777,12 +1965,17 @@ _bfd_elf_link_find_version_dependencies (struct elf_link_hash_entry *h,
        }
 
       t->vn_bfd = h->verinfo.verdef->vd_bfd;
        }
 
       t->vn_bfd = h->verinfo.verdef->vd_bfd;
-      t->vn_nextref = elf_tdata (rinfo->output_bfd)->verref;
-      elf_tdata (rinfo->output_bfd)->verref = t;
+      t->vn_nextref = elf_tdata (rinfo->info->output_bfd)->verref;
+      elf_tdata (rinfo->info->output_bfd)->verref = t;
     }
 
   amt = sizeof *a;
     }
 
   amt = sizeof *a;
-  a = bfd_zalloc (rinfo->output_bfd, amt);
+  a = bfd_zalloc (rinfo->info->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
 
   /* Note that we are copying a string pointer here, and testing it
      above.  If bfd_elf_string_from_elf_section is ever changed to
@@ -1808,10 +2001,10 @@ _bfd_elf_link_find_version_dependencies (struct elf_link_hash_entry *h,
    files, so until that point we don't know which symbols should be
    local.  This function is called via elf_link_hash_traverse.  */
 
    files, so until that point we don't know which symbols should be
    local.  This function is called via elf_link_hash_traverse.  */
 
-bfd_boolean
+static bfd_boolean
 _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data)
 {
 _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data)
 {
-  struct elf_assign_sym_version_info *sinfo;
+  struct elf_info_failed *sinfo;
   struct bfd_link_info *info;
   const struct elf_backend_data *bed;
   struct elf_info_failed eif;
   struct bfd_link_info *info;
   const struct elf_backend_data *bed;
   struct elf_info_failed eif;
@@ -1839,7 +2032,7 @@ _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data)
   if (!h->def_regular)
     return TRUE;
 
   if (!h->def_regular)
     return TRUE;
 
-  bed = get_elf_backend_data (sinfo->output_bfd);
+  bed = get_elf_backend_data (info->output_bfd);
   p = strchr (h->root.root.string, ELF_VER_CHR);
   if (p != NULL && h->verinfo.vertree == NULL)
     {
   p = strchr (h->root.root.string, ELF_VER_CHR);
   if (p != NULL && h->verinfo.vertree == NULL)
     {
@@ -1877,7 +2070,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)
              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)
              memcpy (alc, h->root.root.string, len - 1);
              alc[len - 1] = '\0';
              if (alc[len - 2] == ELF_VER_CHR)
@@ -1919,7 +2115,7 @@ _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data)
            return TRUE;
 
          amt = sizeof *t;
            return TRUE;
 
          amt = sizeof *t;
-         t = bfd_zalloc (sinfo->output_bfd, amt);
+         t = bfd_zalloc (info->output_bfd, amt);
          if (t == NULL)
            {
              sinfo->failed = TRUE;
          if (t == NULL)
            {
              sinfo->failed = TRUE;
@@ -1947,8 +2143,8 @@ _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)
          /* 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"),
-            sinfo->output_bfd, h->root.root.string);
+           (_("%B: version node not found for symbol %s"),
+            info->output_bfd, h->root.root.string);
          bfd_set_error (bfd_error_bad_value);
          sinfo->failed = TRUE;
          return FALSE;
          bfd_set_error (bfd_error_bad_value);
          sinfo->failed = TRUE;
          return FALSE;
@@ -1962,72 +2158,12 @@ _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data)
      something.  */
   if (h->verinfo.vertree == NULL && sinfo->verdefs != NULL)
     {
      something.  */
   if (h->verinfo.vertree == NULL && sinfo->verdefs != NULL)
     {
-      struct bfd_elf_version_tree *t;
-      struct bfd_elf_version_tree *local_ver;
-      struct bfd_elf_version_expr *d;
-
-      /* See if can find what version this symbol is in.  If the
-        symbol is supposed to be local, then don't actually register
-        it.  */
-      local_ver = NULL;
-      for (t = sinfo->verdefs; t != NULL; t = t->next)
-       {
-         if (t->globals.list != NULL)
-           {
-             bfd_boolean matched;
-
-             matched = FALSE;
-             d = NULL;
-             while ((d = (*t->match) (&t->globals, d,
-                                      h->root.root.string)) != NULL)
-               if (d->symver)
-                 matched = TRUE;
-               else
-                 {
-                   /* There is a version without definition.  Make
-                      the symbol the default definition for this
-                      version.  */
-                   h->verinfo.vertree = t;
-                   local_ver = NULL;
-                   d->script = 1;
-                   break;
-                 }
-             if (d != NULL)
-               break;
-             else if (matched)
-               /* There is no undefined version for this symbol. Hide the
-                  default one.  */
-               (*bed->elf_backend_hide_symbol) (info, h, TRUE);
-           }
-
-         if (t->locals.list != NULL)
-           {
-             d = NULL;
-             while ((d = (*t->match) (&t->locals, d,
-                                      h->root.root.string)) != NULL)
-               {
-                 local_ver = t;
-                 /* If the match is "*", keep looking for a more
-                    explicit, perhaps even global, match.
-                    XXX: Shouldn't this be !d->wildcard instead?  */
-                 if (d->pattern[0] != '*' || d->pattern[1] != '\0')
-                   break;
-               }
-
-             if (d != NULL)
-               break;
-           }
-       }
+      bfd_boolean hide;
 
 
-      if (local_ver != NULL)
-       {
-         h->verinfo.vertree = local_ver;
-         if (h->dynindx != -1
-             && ! info->export_dynamic)
-           {
-             (*bed->elf_backend_hide_symbol) (info, h, TRUE);
-           }
-       }
+      h->verinfo.vertree = find_version_for_sym (sinfo->verdefs,
+                                                h->root.root.string, &hide);
+      if (h->verinfo.vertree != NULL && hide)
+       (*bed->elf_backend_hide_symbol) (info, h, TRUE);
     }
 
   return TRUE;
     }
 
   return TRUE;
@@ -2066,7 +2202,7 @@ elf_link_read_relocs_from_section (bfd *abfd,
     return FALSE;
 
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
     return FALSE;
 
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
-  nsyms = symtab_hdr->sh_size / symtab_hdr->sh_entsize;
+  nsyms = NUM_SHDR_ENTRIES (symtab_hdr);
 
   bed = get_elf_backend_data (abfd);
 
 
   bed = get_elf_backend_data (abfd);
 
@@ -2092,11 +2228,24 @@ elf_link_read_relocs_from_section (bfd *abfd,
       r_symndx = ELF32_R_SYM (irela->r_info);
       if (bed->s->arch_size == 64)
        r_symndx >>= 24;
       r_symndx = ELF32_R_SYM (irela->r_info);
       if (bed->s->arch_size == 64)
        r_symndx >>= 24;
-      if ((size_t) r_symndx >= nsyms)
+      if (nsyms > 0)
+       {
+         if ((size_t) r_symndx >= nsyms)
+           {
+             (*_bfd_error_handler)
+               (_("%B: bad reloc symbol index (0x%lx >= 0x%lx)"
+                  " for offset 0x%lx in section `%A'"),
+                abfd, sec,
+                (unsigned long) r_symndx, (unsigned long) nsyms, irela->r_offset);
+             bfd_set_error (bfd_error_bad_value);
+             return FALSE;
+           }
+       }
+      else if (r_symndx != 0)
        {
          (*_bfd_error_handler)
        {
          (*_bfd_error_handler)
-           (_("%B: bad reloc symbol index (0x%lx >= 0x%lx)"
-              " for offset 0x%lx in section `%A'"),
+           (_("%B: non-zero symbol index (0x%lx) for offset 0x%lx in section `%A'"
+              " when the object file has no symbol table"),
             abfd, sec,
             (unsigned long) r_symndx, (unsigned long) nsyms, irela->r_offset);
          bfd_set_error (bfd_error_bad_value);
             abfd, sec,
             (unsigned long) r_symndx, (unsigned long) nsyms, irela->r_offset);
          bfd_set_error (bfd_error_bad_value);
@@ -2146,7 +2295,7 @@ _bfd_elf_link_read_relocs (bfd *abfd,
       size = o->reloc_count;
       size *= bed->s->int_rels_per_ext_rel * sizeof (Elf_Internal_Rela);
       if (keep_memory)
       size = o->reloc_count;
       size *= bed->s->int_rels_per_ext_rel * sizeof (Elf_Internal_Rela);
       if (keep_memory)
-       internal_relocs = bfd_alloc (abfd, size);
+       internal_relocs = alloc2 = bfd_alloc (abfd, size);
       else
        internal_relocs = alloc2 = bfd_malloc (size);
       if (internal_relocs == NULL)
       else
        internal_relocs = alloc2 = bfd_malloc (size);
       if (internal_relocs == NULL)
@@ -2194,14 +2343,19 @@ _bfd_elf_link_read_relocs (bfd *abfd,
   if (alloc1 != NULL)
     free (alloc1);
   if (alloc2 != NULL)
   if (alloc1 != NULL)
     free (alloc1);
   if (alloc2 != NULL)
-    free (alloc2);
+    {
+      if (keep_memory)
+       bfd_release (abfd, alloc2);
+      else
+       free (alloc2);
+    }
   return NULL;
 }
 
 /* Compute the size of, and allocate space for, REL_HDR which is the
    section header for a section containing relocations for O.  */
 
   return NULL;
 }
 
 /* Compute the size of, and allocate space for, REL_HDR which is the
    section header for a section containing relocations for O.  */
 
-bfd_boolean
+static bfd_boolean
 _bfd_elf_link_size_reloc_section (bfd *abfd,
                                  Elf_Internal_Shdr *rel_hdr,
                                  asection *o)
 _bfd_elf_link_size_reloc_section (bfd *abfd,
                                  Elf_Internal_Shdr *rel_hdr,
                                  asection *o)
@@ -2289,7 +2443,7 @@ _bfd_elf_link_output_relocs (bfd *output_bfd,
       (*_bfd_error_handler)
        (_("%B: relocation size mismatch in %B section %A"),
         output_bfd, input_section->owner, input_section);
       (*_bfd_error_handler)
        (_("%B: relocation size mismatch in %B section %A"),
         output_bfd, input_section->owner, input_section);
-      bfd_set_error (bfd_error_wrong_object_format);
+      bfd_set_error (bfd_error_wrong_format);
       return FALSE;
     }
 
       return FALSE;
     }
 
@@ -2340,11 +2494,11 @@ _bfd_elf_link_hash_fixup_symbol (struct bfd_link_info *info,
    assign_sym_version, which is unnecessary but perhaps more robust in
    the face of future changes.  */
 
    assign_sym_version, which is unnecessary but perhaps more robust in
    the face of future changes.  */
 
-bfd_boolean
+static bfd_boolean
 _bfd_elf_fix_symbol_flags (struct elf_link_hash_entry *h,
                           struct elf_info_failed *eif)
 {
 _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
 
   /* 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 +2559,10 @@ _bfd_elf_fix_symbol_flags (struct elf_link_hash_entry *h,
     }
 
   /* Backend specific symbol fixup.  */
     }
 
   /* 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
 
   /* 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 +2600,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)
      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
 
   /* 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 +2615,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 (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,
       BFD_ASSERT (weakdef->def_dynamic);
 
       /* If the real definition is defined by a regular object file,
@@ -2478,8 +2623,11 @@ _bfd_elf_fix_symbol_flags (struct elf_link_hash_entry *h,
       if (weakdef->def_regular)
        h->u.weakdef = NULL;
       else
       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;
     }
 
   return TRUE;
@@ -2489,7 +2637,7 @@ _bfd_elf_fix_symbol_flags (struct elf_link_hash_entry *h,
    called via elf_link_hash_traverse, and also calls itself
    recursively.  */
 
    called via elf_link_hash_traverse, and also calls itself
    recursively.  */
 
-bfd_boolean
+static bfd_boolean
 _bfd_elf_adjust_dynamic_symbol (struct elf_link_hash_entry *h, void *data)
 {
   struct elf_info_failed *eif = data;
 _bfd_elf_adjust_dynamic_symbol (struct elf_link_hash_entry *h, void *data)
 {
   struct elf_info_failed *eif = data;
@@ -2600,6 +2748,14 @@ _bfd_elf_adjust_dynamic_symbol (struct elf_link_hash_entry *h, void *data)
 
   dynobj = elf_hash_table (eif->info)->dynobj;
   bed = get_elf_backend_data (dynobj);
 
   dynobj = elf_hash_table (eif->info)->dynobj;
   bed = get_elf_backend_data (dynobj);
+
+
+  if (h->type == STT_GNU_IFUNC
+      && (bed->elf_osabi == ELFOSABI_LINUX
+         /* GNU/Linux is still using the default value 0.  */
+         || bed->elf_osabi == ELFOSABI_NONE))
+    h->needs_plt = 1;
+
   if (! (*bed->elf_backend_adjust_dynamic_symbol) (eif->info, h))
     {
       eif->failed = TRUE;
   if (! (*bed->elf_backend_adjust_dynamic_symbol) (eif->info, h))
     {
       eif->failed = TRUE;
@@ -2609,10 +2765,56 @@ _bfd_elf_adjust_dynamic_symbol (struct elf_link_hash_entry *h, void *data)
   return TRUE;
 }
 
   return TRUE;
 }
 
+/* Adjust the dynamic symbol, H, for copy in the dynamic bss section,
+   DYNBSS.  */
+
+bfd_boolean
+_bfd_elf_adjust_dynamic_copy (struct elf_link_hash_entry *h,
+                             asection *dynbss)
+{
+  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.  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 (power_of_two > bfd_get_section_alignment (dynbss->owner,
+                                               dynbss))
+    {
+      /* Adjust the section alignment if needed.  */
+      if (! bfd_set_section_alignment (dynbss->owner, dynbss,
+                                      power_of_two))
+       return FALSE;
+    }
+
+  /* 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.  */
+  h->root.u.def.section = dynbss;
+  h->root.u.def.value = dynbss->size;
+
+  /* Increment the size of DYNBSS to make room for the symbol.  */
+  dynbss->size += h->size;
+
+  return TRUE;
+}
+
 /* Adjust all external symbols pointing into SEC_MERGE sections
    to reflect the object merging within the sections.  */
 
 /* Adjust all external symbols pointing into SEC_MERGE sections
    to reflect the object merging within the sections.  */
 
-bfd_boolean
+static bfd_boolean
 _bfd_elf_link_sec_merge_syms (struct elf_link_hash_entry *h, void *data)
 {
   asection *sec;
 _bfd_elf_link_sec_merge_syms (struct elf_link_hash_entry *h, void *data)
 {
   asection *sec;
@@ -2717,6 +2919,11 @@ _bfd_elf_symbol_refs_local_p (struct elf_link_hash_entry *h,
   if (h == NULL)
     return TRUE;
 
   if (h == NULL)
     return TRUE;
 
+  /* STV_HIDDEN or STV_INTERNAL ones must be local.  */
+  if (ELF_ST_VISIBILITY (h->other) == STV_HIDDEN
+      || ELF_ST_VISIBILITY (h->other) == STV_INTERNAL)
+    return TRUE;
+
   /* Common symbols that become definitions don't get the DEF_REGULAR
      flag set, so test it first, and don't bail out.  */
   if (ELF_COMMON_DEF_P (h))
   /* Common symbols that become definitions don't get the DEF_REGULAR
      flag set, so test it first, and don't bail out.  */
   if (ELF_COMMON_DEF_P (h))
@@ -2745,10 +2952,6 @@ _bfd_elf_symbol_refs_local_p (struct elf_link_hash_entry *h,
   if (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
     return FALSE;
 
   if (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
     return FALSE;
 
-  /* However, STV_HIDDEN or STV_INTERNAL ones must be local.  */
-  if (ELF_ST_VISIBILITY (h->other) != STV_PROTECTED)
-    return TRUE;
-
   hash_table = elf_hash_table (info);
   if (!is_elf_hash_table (hash_table))
     return TRUE;
   hash_table = elf_hash_table (info);
   if (!is_elf_hash_table (hash_table))
     return TRUE;
@@ -3016,6 +3219,16 @@ elf_add_dt_needed_tag (bfd *abfd,
   return 0;
 }
 
   return 0;
 }
 
+static bfd_boolean
+on_needed_list (const char *soname, struct bfd_link_needed_list *needed)
+{
+  for (; needed != NULL; needed = needed->next)
+    if (strcmp (soname, needed->name) == 0)
+      return TRUE;
+
+  return FALSE;
+}
+
 /* Sort symbol by value and section.  */
 static int
 elf_sort_symbol (const void *arg1, const void *arg2)
 /* Sort symbol by value and section.  */
 static int
 elf_sort_symbol (const void *arg1, const void *arg2)
@@ -3180,11 +3393,46 @@ elf_finalize_dynstr (bfd *output_bfd, struct bfd_link_info *info)
   return TRUE;
 }
 \f
   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
 elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 {
 /* Add symbols from an ELF object file to the linker hash table.  */
 
 static bfd_boolean
 elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 {
+  Elf_Internal_Ehdr *ehdr;
   Elf_Internal_Shdr *hdr;
   bfd_size_type symcount;
   bfd_size_type extsymcount;
   Elf_Internal_Shdr *hdr;
   bfd_size_type symcount;
   bfd_size_type extsymcount;
@@ -3230,7 +3478,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
         the format of the output file.  */
       if (info->relocatable
          || !is_elf_hash_table (htab)
         the format of the output file.  */
       if (info->relocatable
          || !is_elf_hash_table (htab)
-         || htab->root.creator != abfd->xvec)
+         || info->output_bfd->xvec != abfd->xvec)
        {
          if (info->relocatable)
            bfd_set_error (bfd_error_invalid_operation);
        {
          if (info->relocatable)
            bfd_set_error (bfd_error_invalid_operation);
@@ -3240,6 +3488,17 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
        }
     }
 
        }
     }
 
+  ehdr = elf_elfheader (abfd);
+  if (info->warn_alternate_em
+      && bed->elf_machine_code != ehdr->e_machine
+      && ((bed->elf_machine_alt1 != 0
+          && ehdr->e_machine == bed->elf_machine_alt1)
+         || (bed->elf_machine_alt2 != 0
+             && ehdr->e_machine == bed->elf_machine_alt2)))
+    info->callbacks->einfo
+      (_("%P: alternate ELF machine code found (%d) in %B, expecting %d\n"),
+       ehdr->e_machine, abfd, bed->elf_machine_code);
+
   /* As a GNU extension, any input sections which are named
      .gnu.warning.SYMBOL are treated as warning symbols for the given
      symbol.  This differs from .gnu.warning sections, which generate
   /* As a GNU extension, any input sections which are named
      .gnu.warning.SYMBOL are treated as warning symbols for the given
      symbol.  This differs from .gnu.warning sections, which generate
@@ -3327,7 +3586,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
         format as the output, we can't make a shared library.  */
       if (info->shared
          && is_elf_hash_table (htab)
         format as the output, we can't make a shared library.  */
       if (info->shared
          && is_elf_hash_table (htab)
-         && htab->root.creator == abfd->xvec
+         && info->output_bfd->xvec == abfd->xvec
          && !htab->dynamic_sections_created)
        {
          if (! _bfd_elf_link_create_dynamic_sections (abfd, info))
          && !htab->dynamic_sections_created)
        {
          if (! _bfd_elf_link_create_dynamic_sections (abfd, info))
@@ -3365,14 +3624,14 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
        {
          bfd_byte *dynbuf;
          bfd_byte *extdyn;
        {
          bfd_byte *dynbuf;
          bfd_byte *extdyn;
-         int elfsec;
+         unsigned int elfsec;
          unsigned long shlink;
 
          if (!bfd_malloc_and_get_section (abfd, s, &dynbuf))
            goto error_free_dyn;
 
          elfsec = _bfd_elf_section_from_bfd_section (abfd, s);
          unsigned long shlink;
 
          if (!bfd_malloc_and_get_section (abfd, s, &dynbuf))
            goto error_free_dyn;
 
          elfsec = _bfd_elf_section_from_bfd_section (abfd, s);
-         if (elfsec == -1)
+         if (elfsec == SHN_BAD)
            goto error_free_dyn;
          shlink = elf_elfsections (abfd)[elfsec]->sh_link;
 
            goto error_free_dyn;
          shlink = elf_elfsections (abfd)[elfsec]->sh_link;
 
@@ -3627,8 +3886,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))
         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.  */
 
       /* Clone the symbol table and sym hashes.  Remember some
         pointers into the symbol table, and dynamic symbol count.  */
@@ -3714,8 +3972,16 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 
       if (isym->st_shndx == SHN_UNDEF)
        sec = bfd_und_section_ptr;
 
       if (isym->st_shndx == SHN_UNDEF)
        sec = bfd_und_section_ptr;
-      else if (isym->st_shndx < SHN_LORESERVE
-              || isym->st_shndx > SHN_HIRESERVE)
+      else if (isym->st_shndx == SHN_ABS)
+       sec = bfd_abs_section_ptr;
+      else if (isym->st_shndx == SHN_COMMON)
+       {
+         sec = bfd_com_section_ptr;
+         /* What ELF calls the size we call the value.  What ELF
+            calls the value we call the alignment.  */
+         value = isym->st_size;
+       }
+      else
        {
          sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
          if (sec == NULL)
        {
          sec = bfd_section_from_elf_index (abfd, isym->st_shndx);
          if (sec == NULL)
@@ -3730,19 +3996,6 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
          else if ((abfd->flags & (EXEC_P | DYNAMIC)) != 0)
            value -= sec->vma;
        }
          else if ((abfd->flags & (EXEC_P | DYNAMIC)) != 0)
            value -= sec->vma;
        }
-      else if (isym->st_shndx == SHN_ABS)
-       sec = bfd_abs_section_ptr;
-      else if (isym->st_shndx == SHN_COMMON)
-       {
-         sec = bfd_com_section_ptr;
-         /* What ELF calls the size we call the value.  What ELF
-            calls the value we call the alignment.  */
-         value = isym->st_size;
-       }
-      else
-       {
-         /* Leave it up to the processor backend.  */
-       }
 
       name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
                                              isym->st_name);
 
       name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
                                              isym->st_name);
@@ -4116,42 +4369,8 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
              h->type = ELF_ST_TYPE (isym->st_info);
            }
 
              h->type = ELF_ST_TYPE (isym->st_info);
            }
 
-         /* If st_other has a processor-specific meaning, specific
-            code might be needed here. We never merge the visibility
-            attribute with the one from a dynamic object.  */
-         if (bed->elf_backend_merge_symbol_attribute)
-           (*bed->elf_backend_merge_symbol_attribute) (h, isym, definition,
-                                                       dynamic);
-
-         /* If this symbol has default visibility and the user has requested
-            we not re-export it, then mark it as hidden.  */
-         if (definition && !dynamic
-             && (abfd->no_export
-                 || (abfd->my_archive && abfd->my_archive->no_export))
-             && ELF_ST_VISIBILITY (isym->st_other) != STV_INTERNAL)
-           isym->st_other = (STV_HIDDEN
-                             | (isym->st_other & ~ELF_ST_VISIBILITY (-1)));
-
-         if (ELF_ST_VISIBILITY (isym->st_other) != 0 && !dynamic)
-           {
-             unsigned char hvis, symvis, other, nvis;
-
-             /* Only merge the visibility. Leave the remainder of the
-                st_other field to elf_backend_merge_symbol_attribute.  */
-             other = h->other & ~ELF_ST_VISIBILITY (-1);
-
-             /* Combine visibilities, using the most constraining one.  */
-             hvis   = ELF_ST_VISIBILITY (h->other);
-             symvis = ELF_ST_VISIBILITY (isym->st_other);
-             if (! hvis)
-               nvis = symvis;
-             else if (! symvis)
-               nvis = hvis;
-             else
-               nvis = hvis < symvis ? hvis : symvis;
-
-             h->other = other | nvis;
-           }
+         /* Merge st_other field.  */
+         elf_merge_st_other (abfd, h, isym, definition, dynamic);
 
          /* Set a flag in the hash table entry indicating the type of
             reference or definition we just found.  Keep a count of
 
          /* Set a flag in the hash table entry indicating the type of
             reference or definition we just found.  Keep a count of
@@ -4168,7 +4387,15 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
                    h->ref_regular_nonweak = 1;
                }
              else
                    h->ref_regular_nonweak = 1;
                }
              else
-               h->def_regular = 1;
+               {
+                 h->def_regular = 1;
+                 if (h->def_dynamic)
+                   {
+                     h->def_dynamic = 0;
+                     h->ref_dynamic = 1;
+                     h->dynamic_def = 1;
+                   }
+               }
              if (! info->executable
                  || h->def_dynamic
                  || h->ref_dynamic)
              if (! info->executable
                  || h->def_dynamic
                  || h->ref_dynamic)
@@ -4188,7 +4415,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
                dynsym = TRUE;
            }
 
                dynsym = TRUE;
            }
 
-         if (definition && (sec->flags & SEC_DEBUGGING))
+         if (definition && (sec->flags & SEC_DEBUGGING) && !info->relocatable)
            {
              /* We don't want to make debug symbol dynamic.  */
              (*bed->elf_backend_hide_symbol) (info, h, TRUE);
            {
              /* We don't want to make debug symbol dynamic.  */
              (*bed->elf_backend_hide_symbol) (info, h, TRUE);
@@ -4215,6 +4442,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);
                      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;
                }
                    }
                  nondeflt_vers[nondeflt_vers_cnt++] = h;
                }
@@ -4247,8 +4476,11 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 
          if (!add_needed
              && definition
 
          if (!add_needed
              && definition
-             && dynsym
-             && h->ref_regular)
+             && ((dynsym
+                  && h->ref_regular)
+                 || (h->ref_dynamic
+                     && (elf_dyn_lib_class (abfd) & DYN_AS_NEEDED) != 0
+                     && !on_needed_list (elf_dt_name (abfd), htab->needed))))
            {
              int ret;
              const char *soname = elf_dt_name (abfd);
            {
              int ret;
              const char *soname = elf_dt_name (abfd);
@@ -4335,7 +4567,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))
         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,
 
       free (old_tab);
       objalloc_free_block ((struct objalloc *) htab->root.table.memory,
@@ -4349,7 +4581,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
     {
       if (!(*info->callbacks->notice) (info, NULL, abfd, NULL,
                                       notice_needed))
     {
       if (!(*info->callbacks->notice) (info, NULL, abfd, NULL,
                                       notice_needed))
-       return FALSE;
+       goto error_free_vers;
       free (old_tab);
       old_tab = NULL;
     }
       free (old_tab);
       old_tab = NULL;
     }
@@ -4373,6 +4605,8 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 
          amt = p - h->root.root.string;
          shortname = bfd_malloc (amt + 1);
 
          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';
 
          memcpy (shortname, h->root.root.string, amt);
          shortname[amt] = '\0';
 
@@ -4521,7 +4755,11 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
                  if (hlook->dynindx != -1 && h->dynindx == -1)
                    {
                      if (! bfd_elf_link_record_dynamic_symbol (info, h))
                  if (hlook->dynindx != -1 && h->dynindx == -1)
                    {
                      if (! bfd_elf_link_record_dynamic_symbol (info, h))
-                       goto error_return;
+                       {
+                       err_free_sym_hash:
+                         free (sorted_sym_hash);
+                         goto error_return;
+                       }
                    }
 
                  /* If the real definition is in the list of dynamic
                    }
 
                  /* If the real definition is in the list of dynamic
@@ -4532,7 +4770,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
                  if (h->dynindx != -1 && hlook->dynindx == -1)
                    {
                      if (! bfd_elf_link_record_dynamic_symbol (info, hlook))
                  if (h->dynindx != -1 && hlook->dynindx == -1)
                    {
                      if (! bfd_elf_link_record_dynamic_symbol (info, hlook))
-                       goto error_return;
+                       goto err_free_sym_hash;
                    }
                  break;
                }
                    }
                  break;
                }
@@ -4542,8 +4780,9 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
       free (sorted_sym_hash);
     }
 
       free (sorted_sym_hash);
     }
 
-  if (bed->check_directives)
-    (*bed->check_directives) (abfd, info);
+  if (bed->check_directives
+      && !(*bed->check_directives) (abfd, info))
+    return FALSE;
 
   /* If this object is the same format as the output object, and it is
      not a shared library, then let the backend look through the
 
   /* If this object is the same format as the output object, and it is
      not a shared library, then let the backend look through the
@@ -4564,8 +4803,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)
      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, info->output_bfd->xvec))
     {
       asection *o;
 
     {
       asection *o;
 
@@ -4917,13 +5156,19 @@ bfd_elf_link_add_symbols (bfd *abfd, struct bfd_link_info *info)
     }
 }
 \f
     }
 }
 \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)
 {
 /* 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;
   const char *name;
   char *p;
   unsigned long ha;
@@ -4941,6 +5186,11 @@ elf_collect_hash_codes (struct elf_link_hash_entry *h, void *data)
   if (p != NULL)
     {
       alc = bfd_malloc (p - name + 1);
   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;
       memcpy (alc, name, p - name);
       alc[p - name] = '\0';
       name = alc;
@@ -4950,7 +5200,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.  */
   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.  */
 
   /* And store it in the struct so that we can put it in the hash table
      later.  */
@@ -4980,6 +5230,7 @@ struct collect_gnu_hash_codes
   long int local_indx;
   long int shift1, shift2;
   unsigned long int mask;
   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
 };
 
 /* This function will be called though elf_link_hash_traverse to store
@@ -5010,6 +5261,11 @@ elf_collect_gnu_hash_codes (struct elf_link_hash_entry *h, void *data)
   if (p != NULL)
     {
       alc = bfd_malloc (p - name + 1);
   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;
       memcpy (alc, name, p - name);
       alc[p - name] = '\0';
       name = alc;
@@ -5108,13 +5364,13 @@ static const size_t elf_buckets[] =
    Therefore the result is always a good payoff between few collisions
    (= short chain lengths) and table size.  */
 static size_t
    Therefore the result is always a good payoff between few collisions
    (= short chain lengths) and table size.  */
 static size_t
-compute_bucket_count (struct bfd_link_info *info, unsigned long int *hashcodes,
-                     unsigned long int nsyms, int gnu_hash)
+compute_bucket_count (struct bfd_link_info *info,
+                     unsigned long int *hashcodes ATTRIBUTE_UNUSED,
+                     unsigned long int nsyms,
+                     int gnu_hash)
 {
 {
-  size_t dynsymcount = elf_hash_table (info)->dynsymcount;
   size_t best_size = 0;
   unsigned long int i;
   size_t best_size = 0;
   unsigned long int i;
-  bfd_size_type amt;
 
   /* We have a problem here.  The following code to optimize the table
      size requires an integer type with more the 32 bits.  If
 
   /* We have a problem here.  The following code to optimize the table
      size requires an integer type with more the 32 bits.  If
@@ -5126,8 +5382,10 @@ compute_bucket_count (struct bfd_link_info *info, unsigned long int *hashcodes,
       size_t maxsize;
       BFD_HOST_U_64_BIT best_chlen = ~((BFD_HOST_U_64_BIT) 0);
       bfd *dynobj = elf_hash_table (info)->dynobj;
       size_t maxsize;
       BFD_HOST_U_64_BIT best_chlen = ~((BFD_HOST_U_64_BIT) 0);
       bfd *dynobj = elf_hash_table (info)->dynobj;
+      size_t dynsymcount = elf_hash_table (info)->dynsymcount;
       const struct elf_backend_data *bed = get_elf_backend_data (dynobj);
       unsigned long int *counts;
       const struct elf_backend_data *bed = get_elf_backend_data (dynobj);
       unsigned long int *counts;
+      bfd_size_type amt;
 
       /* Possible optimization parameters: if we have NSYMS symbols we say
         that the hashing table must at least have NSYMS/4 and at most
 
       /* Possible optimization parameters: if we have NSYMS symbols we say
         that the hashing table must at least have NSYMS/4 and at most
@@ -5254,7 +5512,7 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
   bfd_size_type soname_indx;
   bfd *dynobj;
   const struct elf_backend_data *bed;
   bfd_size_type soname_indx;
   bfd *dynobj;
   const struct elf_backend_data *bed;
-  struct elf_assign_sym_version_info asvinfo;
+  struct elf_info_failed asvinfo;
 
   *sinterpptr = NULL;
 
 
   *sinterpptr = NULL;
 
@@ -5264,7 +5522,6 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
     return TRUE;
 
   bed = get_elf_backend_data (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)
   if (info->execstack)
     elf_tdata (output_bfd)->stack_flags = PF_R | PF_W | PF_X;
   else if (info->noexecstack)
@@ -5281,7 +5538,7 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
        {
          asection *s;
 
        {
          asection *s;
 
-         if (inputobj->flags & (DYNAMIC | BFD_LINKER_CREATED))
+         if (inputobj->flags & (DYNAMIC | EXEC_P | BFD_LINKER_CREATED))
            continue;
          s = bfd_get_section_by_name (inputobj, ".note.GNU-stack");
          if (s)
            continue;
          s = bfd_get_section_by_name (inputobj, ".note.GNU-stack");
          if (s)
@@ -5418,14 +5675,14 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
       /* Make all global versions with definition.  */
       for (t = verdefs; t != NULL; t = t->next)
        for (d = t->globals.list; d != NULL; d = d->next)
       /* Make all global versions with definition.  */
       for (t = verdefs; t != NULL; t = t->next)
        for (d = t->globals.list; d != NULL; d = d->next)
-         if (!d->symver && d->symbol)
+         if (!d->symver && d->literal)
            {
              const char *verstr, *name;
              size_t namelen, verlen, newlen;
              char *newname, *p;
              struct elf_link_hash_entry *newh;
 
            {
              const char *verstr, *name;
              size_t namelen, verlen, newlen;
              char *newname, *p;
              struct elf_link_hash_entry *newh;
 
-             name = d->symbol;
+             name = d->pattern;
              namelen = strlen (name);
              verstr = t->name;
              verlen = strlen (verstr);
              namelen = strlen (name);
              verstr = t->name;
              verlen = strlen (verstr);
@@ -5466,7 +5723,6 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
            }
 
       /* Attach all the symbols to their version information.  */
            }
 
       /* Attach all the symbols to their version information.  */
-      asvinfo.output_bfd = output_bfd;
       asvinfo.info = info;
       asvinfo.verdefs = verdefs;
       asvinfo.failed = FALSE;
       asvinfo.info = info;
       asvinfo.verdefs = verdefs;
       asvinfo.failed = FALSE;
@@ -5483,7 +5739,7 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
          all_defined = TRUE;
          for (t = verdefs; t != NULL; t = t->next)
            for (d = t->globals.list; d != NULL; d = d->next)
          all_defined = TRUE;
          for (t = verdefs; t != NULL; t = t->next)
            for (d = t->globals.list; d != NULL; d = d->next)
-             if (!d->symver && !d->script)
+             if (d->literal && !d->symver && !d->script)
                {
                  (*_bfd_error_handler)
                    (_("%s: undefined version: %s"),
                {
                  (*_bfd_error_handler)
                    (_("%s: undefined version: %s"),
@@ -5871,7 +6127,6 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
       {
        struct elf_find_verdep_info sinfo;
 
       {
        struct elf_find_verdep_info sinfo;
 
-       sinfo.output_bfd = output_bfd;
        sinfo.info = info;
        sinfo.vers = elf_tdata (output_bfd)->cverdefs;
        if (sinfo.vers == 0)
        sinfo.info = info;
        sinfo.vers = elf_tdata (output_bfd)->cverdefs;
        if (sinfo.vers == 0)
@@ -5881,6 +6136,8 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
        elf_link_hash_traverse (elf_hash_table (info),
                                _bfd_elf_link_find_version_dependencies,
                                &sinfo);
        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;
 
        if (elf_tdata (output_bfd)->verref == NULL)
          s->flags |= SEC_EXCLUDE;
@@ -6007,20 +6264,22 @@ _bfd_elf_init_2_index_sections (bfd *output_bfd, struct bfd_link_info *info)
 {
   asection *s;
 
 {
   asection *s;
 
+  /* Data first, since setting text_index_section changes
+     _bfd_elf_link_omit_section_dynsym.  */
   for (s = output_bfd->sections; s != NULL; s = s->next)
   for (s = output_bfd->sections; s != NULL; s = s->next)
-    if (((s->flags & (SEC_EXCLUDE | SEC_ALLOC | SEC_READONLY))
-        == (SEC_ALLOC | SEC_READONLY))
+    if (((s->flags & (SEC_EXCLUDE | SEC_ALLOC | SEC_READONLY)) == SEC_ALLOC)
        && !_bfd_elf_link_omit_section_dynsym (output_bfd, info, s))
       {
        && !_bfd_elf_link_omit_section_dynsym (output_bfd, info, s))
       {
-       elf_hash_table (info)->text_index_section = s;
+       elf_hash_table (info)->data_index_section = s;
        break;
       }
 
   for (s = output_bfd->sections; s != NULL; s = s->next)
        break;
       }
 
   for (s = output_bfd->sections; s != NULL; s = s->next)
-    if (((s->flags & (SEC_EXCLUDE | SEC_ALLOC | SEC_READONLY)) == SEC_ALLOC)
+    if (((s->flags & (SEC_EXCLUDE | SEC_ALLOC | SEC_READONLY))
+        == (SEC_ALLOC | SEC_READONLY))
        && !_bfd_elf_link_omit_section_dynsym (output_bfd, info, s))
       {
        && !_bfd_elf_link_omit_section_dynsym (output_bfd, info, s))
       {
-       elf_hash_table (info)->data_index_section = s;
+       elf_hash_table (info)->text_index_section = s;
        break;
       }
 
        break;
       }
 
@@ -6102,7 +6361,7 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
       if (info->emit_hash)
        {
          unsigned long int *hashcodes;
       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;
          bfd_size_type amt;
          unsigned long int nsyms;
          size_t bucketcount;
@@ -6115,13 +6374,19 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
          hashcodes = bfd_malloc (amt);
          if (hashcodes == NULL)
            return FALSE;
          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),
 
          /* 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)
+           {
+             free (hashcodes);
+             return FALSE;
+           }
 
 
-         nsyms = hashcodesp - hashcodes;
+         nsyms = hashinf.hashcodes - hashcodes;
          bucketcount
            = compute_bucket_count (info, hashcodes, nsyms, 0);
          free (hashcodes);
          bucketcount
            = compute_bucket_count (info, hashcodes, nsyms, 0);
          free (hashcodes);
@@ -6170,6 +6435,11 @@ 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);
          /* Put all hash values in HASHCODES.  */
          elf_link_hash_traverse (elf_hash_table (info),
                                  elf_collect_gnu_hash_codes, &cinfo);
+         if (cinfo.error)
+           {
+             free (cinfo.hashcodes);
+             return FALSE;
+           }
 
          bucketcount
            = compute_bucket_count (info, cinfo.hashcodes, cinfo.nsyms, 1);
 
          bucketcount
            = compute_bucket_count (info, cinfo.hashcodes, cinfo.nsyms, 1);
@@ -6319,339 +6589,1033 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
 
   return TRUE;
 }
 
   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)
+/* Initialize an ELF linker hash table.  */
+
+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_error_handler (_("undefined %s reference in complex symbol: %s"), reftype, name);
+  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;
 }
 
 }
 
-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;
+/* Create an ELF linker hash table.  */
 
 
-  len = strlen (sym);
-  symend = sym + len;
+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 (len < 1 || len > bufsz)
+  if (! _bfd_elf_link_hash_table_init (ret, abfd, _bfd_elf_link_hash_newfunc,
+                                      sizeof (struct elf_link_hash_entry)))
     {
     {
-      bfd_set_error (bfd_error_invalid_operation);
-      return FALSE;
+      free (ret);
+      return NULL;
     }
     }
-  
-  switch (* sym)
-    {
-    case '.':
-      * result = addr + section_offset;
-      * advanced = sym + 1;
-      return TRUE;
-
-    case '#':
-      ++ sym;
-      * result = strtoul (sym, advanced, 16);
-      return TRUE;
 
 
-    case 'S':
-      symbol_is_section = TRUE;
-    case 's':      
-      ++ sym;
-      symlen = strtol (sym, &sym, 10);
-      ++ sym; /* Skip the trailing ':'.  */
+  return &ret->root;
+}
 
 
-      if ((symend < sym) || ((symlen + 1) > bufsz))
-       {
-         bfd_set_error (bfd_error_invalid_operation);
-         return FALSE;
-       }
+/* 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.  */
 
 
-      memcpy (symbuf, sym, symlen);
-      symbuf [symlen] = '\0';
-      * advanced = 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.  */
+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;
+}
 
 
-      if (symbol_is_section) 
-       {
-         if ((resolve_section (symbuf, finfo->output_bfd->sections, result) != TRUE)
-             && (resolve_symbol (symbuf, input_bfd, finfo, result, locsymcount) != TRUE))
+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;
+  unsigned 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 == SHN_BAD)
+    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, total_size;
+
+  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++;
+
+  total_size = ((shndx_count + 1) * sizeof (*ssymbuf)
+               + (indbufend - indbuf) * sizeof (*ssym));
+  ssymbuf = bfd_malloc (total_size);
+  if (ssymbuf == NULL)
+    {
+      free (indbuf);
+      return NULL;
+    }
+
+  ssym = (struct elf_symbuf_symbol *) (ssymbuf + shndx_count + 1);
+  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
+             && (((bfd_hostptr_t) ssym - (bfd_hostptr_t) ssymbuf)
+                 == total_size));
+
+  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;
+  unsigned 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 == SHN_BAD || shndx2 == SHN_BAD)
+    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 (shndx1 < ssymbuf1[mid].st_shndx)
+           hi = mid;
+         else if (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 (shndx2 < ssymbuf2[mid].st_shndx)
+           hi = mid;
+         else if (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 == shndx1)
+      symtable1[count1++].u.isym = isym;
+
+  count2 = 0;
+  for (isym = isymbuf2, isymend = isym + symcount2; isym < isymend; isym++)
+    if (isym->st_shndx == 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 > sizeof (symbuf))
+    {
+      bfd_set_error (bfd_error_invalid_operation);
+      return FALSE;
+    }
+
+  switch (* sym)
+    {
+    case '.':
+      *result = dot;
+      *symp = sym + 1;
+      return TRUE;
+
+    case '#':
+      ++sym;
+      *result = strtoul (sym, (char **) symp, 16);
+      return TRUE;
+
+    case 'S':
+      symbol_is_section = TRUE;
+    case 's':
+      ++sym;
+      symlen = strtol (sym, (char **) symp, 10);
+      sym = *symp + 1; /* Skip the trailing ':'.  */
+
+      if (symend < sym || symlen + 1 > sizeof (symbuf))
+       {
+         bfd_set_error (bfd_error_invalid_operation);
+         return FALSE;
+       }
+
+      memcpy (symbuf, sym, symlen);
+      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 (!resolve_section (symbuf, finfo->output_bfd->sections, result)
+             && !resolve_symbol (symbuf, input_bfd, finfo, result,
+                                 isymbuf, locsymcount))
            {
              undefined_reference ("section", symbuf);
              return FALSE;
            }
            {
              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;
            {
              undefined_reference ("symbol", symbuf);
              return FALSE;
@@ -6659,24 +7623,23 @@ eval_symbol (bfd_vma *                     result,
        }
 
       return TRUE;
        }
 
       return TRUE;
-      
+
       /* All that remains are operators.  */
 
 #define UNARY_OP(op)                                           \
   if (strncmp (sym, #op, strlen (#op)) == 0)                   \
     {                                                          \
       sym += strlen (#op);                                     \
       /* 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;                                             \
     }
 
       return TRUE;                                             \
     }
 
@@ -6684,22 +7647,20 @@ eval_symbol (bfd_vma *                     result,
   if (strncmp (sym, #op, strlen (#op)) == 0)                   \
     {                                                          \
       sym += strlen (#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;                                          \
-      ++ 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;                                             \
     }
 
       return TRUE;                                             \
     }
 
@@ -6733,150 +7694,16 @@ eval_symbol (bfd_vma *                     result,
     }
 }
 
     }
 }
 
-/* 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);
-    }
-
-  /* If nothing went wrong, then we adjusted 
-     everything we wanted to adjust.  */
-  return result;
-}
-
 static void
 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);
 
 {
   location += (size - chunksz);
 
-  for (; size; size -= chunksz, location -= chunksz, x >>= (chunksz * 8)) 
+  for (; size; size -= chunksz, location -= chunksz, x >>= (chunksz * 8))
     {
       switch (chunksz)
        {
     {
       switch (chunksz)
        {
@@ -6903,15 +7730,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;
 
 {
   bfd_vma x = 0;
 
-  for (; size; size -= chunksz, location += chunksz) 
+  for (; size; size -= chunksz, location += chunksz)
     {
       switch (chunksz)
        {
     {
       switch (chunksz)
        {
@@ -6939,17 +7766,16 @@ get_value (bfd_vma        size,
   return x;
 }
 
   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;
 {
   * start     =  encoded        & 0x3F;
   * len       = (encoded >>  6) & 0x3F;
@@ -6961,89 +7787,26 @@ decode_complex_addend
   * trunc_p   = (encoded >> 29) & 1;
 }
 
   * 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,
 
   /*  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;
-
-      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;
+      word size, etc) encoded within it.).  */
 
 
-         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;
 
 
   mask = (((1L << (len - 1)) - 1) << 1) | 1;
 
@@ -7052,7 +7815,7 @@ bfd_elf_perform_complex_relocation
   else
     shift = (8 * wordsz) - (start + len);
 
   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: "
 
 #ifdef DEBUG
   printf ("Doing complex reloc: "
@@ -7063,21 +7826,15 @@ bfd_elf_perform_complex_relocation
          oplen, x, mask,  relocation);
 #endif
 
          oplen, x, mask,  relocation);
 #endif
 
+  r = bfd_reloc_ok;
   if (! trunc_p)
   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);
 
   /* Do the deed.  */
   x = (x & ~(mask << shift)) | ((relocation & mask) << shift);
 
@@ -7086,10 +7843,11 @@ bfd_elf_perform_complex_relocation
          "         shifted mask: %8.8lx\n"
          " shifted/masked reloc: %8.8lx\n"
          "               result: %8.8lx\n",
          "         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);
          ((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
 }
 
 /* When performing a relocatable link, the input relocations are
@@ -7561,9 +8319,10 @@ elf_link_output_sym (struct elf_final_link_info *finfo,
          bfd_size_type amt;
 
          amt = finfo->shndxbuf_size * sizeof (Elf_External_Sym_Shndx);
          bfd_size_type amt;
 
          amt = finfo->shndxbuf_size * sizeof (Elf_External_Sym_Shndx);
-         finfo->symshndxbuf = destshndx = bfd_realloc (destshndx, amt * 2);
+         destshndx = bfd_realloc (destshndx, amt * 2);
          if (destshndx == NULL)
            return FALSE;
          if (destshndx == NULL)
            return FALSE;
+         finfo->symshndxbuf = destshndx;
          memset ((char *) destshndx + amt, 0, amt);
          finfo->shndxbuf_size *= 2;
        }
          memset ((char *) destshndx + amt, 0, amt);
          finfo->shndxbuf_size *= 2;
        }
@@ -7582,13 +8341,14 @@ elf_link_output_sym (struct elf_final_link_info *finfo,
 static bfd_boolean
 check_dynsym (bfd *abfd, Elf_Internal_Sym *sym)
 {
 static bfd_boolean
 check_dynsym (bfd *abfd, Elf_Internal_Sym *sym)
 {
-  if (sym->st_shndx > SHN_HIRESERVE)
+  if (sym->st_shndx >= (SHN_LORESERVE & 0xffff)
+      && sym->st_shndx < SHN_LORESERVE)
     {
       /* The gABI doesn't support dynamic symbols in output sections
     {
       /* The gABI doesn't support dynamic symbols in output sections
-         beyond 64k.  */
+        beyond 64k.  */
       (*_bfd_error_handler)
        (_("%B: Too many sections: %d (>= %d)"),
       (*_bfd_error_handler)
        (_("%B: Too many sections: %d (>= %d)"),
-        abfd, bfd_count_sections (abfd), SHN_LORESERVE);
+        abfd, bfd_count_sections (abfd), SHN_LORESERVE & 0xffff);
       bfd_set_error (bfd_error_nonrepresentable_section);
       return FALSE;
     }
       bfd_set_error (bfd_error_nonrepresentable_section);
       return FALSE;
     }
@@ -7919,10 +8679,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)
                  {
                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);
+                     }
                  }
              }
          }
                  }
              }
          }
@@ -7992,6 +8757,15 @@ elf_link_output_extsym (struct elf_link_hash_entry *h, void *data)
       sym.st_info = ELF_ST_INFO (bindtype, ELF_ST_TYPE (sym.st_info));
     }
 
       sym.st_info = ELF_ST_INFO (bindtype, ELF_ST_TYPE (sym.st_info));
     }
 
+  /* If this is a symbol defined in a dynamic library, don't use the
+     symbol size from the dynamic library.  Relinking an executable
+     against a new library may introduce gratuitous changes in the
+     executable's symbols if we keep the size.  */
+  if (sym.st_shndx == SHN_UNDEF
+      && !h->def_regular
+      && h->def_dynamic)
+    sym.st_size = 0;
+
   /* If a non-weak symbol with non-default visibility is not defined
      locally, it is a fatal error.  */
   if (! finfo->info->relocatable
   /* If a non-weak symbol with non-default visibility is not defined
      locally, it is a fatal error.  */
   if (! finfo->info->relocatable
@@ -8183,7 +8957,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->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;
     }
        kept = NULL;
       sec->kept_section = kept;
     }
@@ -8198,7 +8974,7 @@ _bfd_elf_check_kept_section (asection *sec, struct bfd_link_info *info)
 static bfd_boolean
 elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
 {
 static bfd_boolean
 elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
 {
-  bfd_boolean (*relocate_section)
+  int (*relocate_section)
     (bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
      Elf_Internal_Rela *, Elf_Internal_Sym *, asection **);
   bfd *output_bfd;
     (bfd *, struct bfd_link_info *, bfd *, asection *, bfd_byte *,
      Elf_Internal_Rela *, Elf_Internal_Sym *, asection **);
   bfd *output_bfd;
@@ -8212,7 +8988,6 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
   asection **ppsection;
   asection *o;
   const struct elf_backend_data *bed;
   asection **ppsection;
   asection *o;
   const struct elf_backend_data *bed;
-  bfd_boolean emit_relocs;
   struct elf_link_hash_entry **sym_hashes;
 
   output_bfd = finfo->output_bfd;
   struct elf_link_hash_entry **sym_hashes;
 
   output_bfd = finfo->output_bfd;
@@ -8225,9 +9000,6 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
   if ((input_bfd->flags & DYNAMIC) != 0)
     return TRUE;
 
   if ((input_bfd->flags & DYNAMIC) != 0)
     return TRUE;
 
-  emit_relocs = (finfo->info->relocatable
-                || finfo->info->emitrelocations);
-
   symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
   if (elf_bad_symtab (input_bfd))
     {
   symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
   if (elf_bad_symtab (input_bfd))
     {
@@ -8251,15 +9023,6 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
       if (isymbuf == NULL)
        return FALSE;
     }
       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
 
   /* Find local symbol sections and adjust values of symbols in
      SEC_MERGE sections.  Write out those local symbols we know are
@@ -8286,28 +9049,26 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
 
       if (isym->st_shndx == SHN_UNDEF)
        isec = bfd_und_section_ptr;
 
       if (isym->st_shndx == SHN_UNDEF)
        isec = bfd_und_section_ptr;
-      else if (isym->st_shndx < SHN_LORESERVE
-              || isym->st_shndx > SHN_HIRESERVE)
-       {
-         isec = bfd_section_from_elf_index (input_bfd, isym->st_shndx);
-         if (isec
-             && isec->sec_info_type == ELF_INFO_TYPE_MERGE
-             && ELF_ST_TYPE (isym->st_info) != STT_SECTION)
-           isym->st_value =
-             _bfd_merged_section_offset (output_bfd, &isec,
-                                         elf_section_data (isec)->sec_info,
-                                         isym->st_value);
-       }
       else if (isym->st_shndx == SHN_ABS)
        isec = bfd_abs_section_ptr;
       else if (isym->st_shndx == SHN_COMMON)
        isec = bfd_com_section_ptr;
       else
        {
       else if (isym->st_shndx == SHN_ABS)
        isec = bfd_abs_section_ptr;
       else if (isym->st_shndx == SHN_COMMON)
        isec = bfd_com_section_ptr;
       else
        {
-         /* Don't attempt to output symbols with st_shnx in the
-            reserved range other than SHN_ABS and SHN_COMMON.  */
-         *ppsection = NULL;
-         continue;
+         isec = bfd_section_from_elf_index (input_bfd, isym->st_shndx);
+         if (isec == NULL)
+           {
+             /* Don't attempt to output symbols with st_shnx in the
+                reserved range other than SHN_ABS and SHN_COMMON.  */
+             *ppsection = NULL;
+             continue;
+           }
+         else if (isec->sec_info_type == ELF_INFO_TYPE_MERGE
+                  && ELF_ST_TYPE (isym->st_info) != STT_SECTION)
+           isym->st_value =
+             _bfd_merged_section_offset (output_bfd, &isec,
+                                         elf_section_data (isec)->sec_info,
+                                         isym->st_value);
        }
 
       *ppsection = isec;
        }
 
       *ppsection = isec;
@@ -8340,10 +9101,9 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
       /* If this symbol is defined in a section which we are
         discarding, we don't need to keep it.  */
       if (isym->st_shndx != SHN_UNDEF
       /* If this symbol is defined in a section which we are
         discarding, we don't need to keep it.  */
       if (isym->st_shndx != SHN_UNDEF
-         && (isym->st_shndx < SHN_LORESERVE || isym->st_shndx > SHN_HIRESERVE)
-         && (isec == NULL
-             || bfd_section_removed_from_list (output_bfd,
-                                               isec->output_section)))
+         && isym->st_shndx < SHN_LORESERVE
+         && bfd_section_removed_from_list (output_bfd,
+                                           isec->output_section))
        continue;
 
       /* Get the name of the symbol.  */
        continue;
 
       /* Get the name of the symbol.  */
@@ -8397,9 +9157,6 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
        return FALSE;
     }
 
        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)
   /* Relocate the contents of each section.  */
   sym_hashes = elf_sym_hashes (input_bfd);
   for (o = input_bfd->sections; o != NULL; o = o->next)
@@ -8412,6 +9169,63 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
          continue;
        }
 
          continue;
        }
 
+      if (finfo->info->relocatable
+         && (o->flags & (SEC_LINKER_CREATED | SEC_GROUP)) == SEC_GROUP)
+       {
+         /* Deal with the group signature symbol.  */
+         struct bfd_elf_section_data *sec_data = elf_section_data (o);
+         unsigned long symndx = sec_data->this_hdr.sh_info;
+         asection *osec = o->output_section;
+
+         if (symndx >= locsymcount
+             || (elf_bad_symtab (input_bfd)
+                 && finfo->sections[symndx] == NULL))
+           {
+             struct elf_link_hash_entry *h = sym_hashes[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;
+             /* Arrange for symbol to be output.  */
+             h->indx = -2;
+             elf_section_data (osec)->this_hdr.sh_info = -2;
+           }
+         else if (ELF_ST_TYPE (isymbuf[symndx].st_info) == STT_SECTION)
+           {
+             /* We'll use the output section target_index.  */
+             asection *sec = finfo->sections[symndx]->output_section;
+             elf_section_data (osec)->this_hdr.sh_info = sec->target_index;
+           }
+         else
+           {
+             if (finfo->indices[symndx] == -1)
+               {
+                 /* Otherwise output the local symbol now.  */
+                 Elf_Internal_Sym sym = isymbuf[symndx];
+                 asection *sec = finfo->sections[symndx]->output_section;
+                 const char *name;
+
+                 name = bfd_elf_string_from_elf_section (input_bfd,
+                                                         symtab_hdr->sh_link,
+                                                         sym.st_name);
+                 if (name == NULL)
+                   return FALSE;
+
+                 sym.st_shndx = _bfd_elf_section_from_bfd_section (output_bfd,
+                                                                   sec);
+                 if (sym.st_shndx == SHN_BAD)
+                   return FALSE;
+
+                 sym.st_value += o->output_offset;
+
+                 finfo->indices[symndx] = bfd_get_symcount (output_bfd);
+                 if (! elf_link_output_sym (finfo, name, &sym, o, NULL))
+                   return FALSE;
+               }
+             elf_section_data (osec)->this_hdr.sh_info
+               = finfo->indices[symndx];
+           }
+       }
+
       if ((o->flags & SEC_HAS_CONTENTS) == 0
          || (o->size == 0 && (o->flags & SEC_RELOC) == 0))
        continue;
       if ((o->flags & SEC_HAS_CONTENTS) == 0
          || (o->size == 0 && (o->flags & SEC_RELOC) == 0))
        continue;
@@ -8441,8 +9255,11 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
       if ((o->flags & SEC_RELOC) != 0)
        {
          Elf_Internal_Rela *internal_relocs;
       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;
          bfd_vma r_type_mask;
          int r_sym_shift;
+         int action_discarded;
+         int ret;
 
          /* Get the swapped relocs.  */
          internal_relocs
 
          /* Get the swapped relocs.  */
          internal_relocs
@@ -8463,76 +9280,108 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
              r_sym_shift = 32;
            }
 
              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))
          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)
+                 && !finfo->info->relocatable)
+               {
+                 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);
                  /* 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"),
                        (*finfo->info->callbacks->einfo)
                          (_("%X`%s' referenced in section `%A' of %B: "
                             "defined in discarded section `%A' of %B\n"),
@@ -8544,7 +9393,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.  */
                         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;
 
                        {
                          asection *kept;
 
@@ -8580,14 +9429,17 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
             corresponding to the output section, which will require
             the addend to be adjusted.  */
 
             corresponding to the output section, which will require
             the addend to be adjusted.  */
 
-         if (! (*relocate_section) (output_bfd, finfo->info,
+         ret = (*relocate_section) (output_bfd, finfo->info,
                                     input_bfd, o, contents,
                                     internal_relocs,
                                     isymbuf,
                                     input_bfd, o, contents,
                                     internal_relocs,
                                     isymbuf,
-                                    finfo->sections))
+                                    finfo->sections);
+         if (!ret)
            return FALSE;
 
            return FALSE;
 
-         if (emit_relocs)
+         if (ret == 2
+             || finfo->info->relocatable
+             || finfo->info->emitrelocations)
            {
              Elf_Internal_Rela *irela;
              Elf_Internal_Rela *irelaend;
            {
              Elf_Internal_Rela *irela;
              Elf_Internal_Rela *irelaend;
@@ -8866,6 +9718,7 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
        default:
          {
            if (! (o->flags & SEC_EXCLUDE)
        default:
          {
            if (! (o->flags & SEC_EXCLUDE)
+               && ! (o->output_section->flags & SEC_NEVER_LOAD)
                && ! bfd_set_section_contents (output_bfd, o->output_section,
                                               contents,
                                               (file_ptr) o->output_offset,
                && ! bfd_set_section_contents (output_bfd, o->output_section,
                                               contents,
                                               (file_ptr) o->output_offset,
@@ -9132,7 +9985,8 @@ elf_fixup_link_order (bfd *abfd, asection *o)
              && elf_elfheader (sub)->e_ident[EI_CLASS] == bed->s->elfclass
              && (elfsec = _bfd_elf_section_from_bfd_section (sub, s))
              && elfsec < elf_numsections (sub)
              && elf_elfheader (sub)->e_ident[EI_CLASS] == bed->s->elfclass
              && (elfsec = _bfd_elf_section_from_bfd_section (sub, s))
              && elfsec < elf_numsections (sub)
-             && elf_elfsections (sub)[elfsec]->sh_flags & SHF_LINK_ORDER)
+             && elf_elfsections (sub)[elfsec]->sh_flags & SHF_LINK_ORDER
+             && elf_elfsections (sub)[elfsec]->sh_link < elf_numsections (sub))
            {
              seen_linkorder++;
              linkorder_sec = s;
            {
              seen_linkorder++;
              linkorder_sec = s;
@@ -9165,7 +10019,9 @@ elf_fixup_link_order (bfd *abfd, asection *o)
     return TRUE;
 
   sections = (struct bfd_link_order **)
     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)
   seen_linkorder = 0;
 
   for (p = o->map_head.link_order; p != NULL; p = p->next)
@@ -9181,12 +10037,13 @@ elf_fixup_link_order (bfd *abfd, asection *o)
   for (n = 0; n < seen_linkorder; n++)
     {
       s = sections[n]->u.indirect.section;
   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;
     }
 
       s->output_offset = offset;
       sections[n]->offset = offset;
       offset += sections[n]->size;
     }
 
+  free (sections);
   return TRUE;
 }
 
   return TRUE;
 }
 
@@ -9220,6 +10077,9 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   size_t relativecount = 0;
   asection *reldyn = 0;
   bfd_size_type amt;
   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;
 
   if (! is_elf_hash_table (info->hash))
     return FALSE;
@@ -9267,6 +10127,40 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   finfo.symbuf_count = 0;
   finfo.shndxbuf_size = 0;
 
   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.  */
   /* 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.  */
@@ -9309,17 +10203,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
              if (info->relocatable || info->emitrelocations)
                reloc_count = sec->reloc_count;
              else if (bed->elf_backend_count_relocs)
              if (info->relocatable || info->emitrelocations)
                reloc_count = sec->reloc_count;
              else if (bed->elf_backend_count_relocs)
-               {
-                 Elf_Internal_Rela * relocs;
-
-                 relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
-                                                     info->keep_memory);
-
-                 reloc_count = (*bed->elf_backend_count_relocs) (sec, relocs);
-
-                 if (elf_section_data (o)->relocs != relocs)
-                   free (relocs);
-               }
+               reloc_count = (*bed->elf_backend_count_relocs) (info, sec);
 
              if (sec->rawsize > max_contents_size)
                max_contents_size = sec->rawsize;
 
              if (sec->rawsize > max_contents_size)
                max_contents_size = sec->rawsize;
@@ -9377,8 +10261,13 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
              bfd_size_type entsize1;
 
              entsize1 = esdi->rel_hdr.sh_entsize;
              bfd_size_type entsize1;
 
              entsize1 = esdi->rel_hdr.sh_entsize;
-             BFD_ASSERT (entsize1 == bed->s->sizeof_rel
-                         || entsize1 == bed->s->sizeof_rela);
+             /* PR 9827: If the header size has not been set yet then
+                assume that it will match the output section's reloc type.  */
+             if (entsize1 == 0)
+               entsize1 = o->use_rela_p ? bed->s->sizeof_rela : bed->s->sizeof_rel;
+             else
+               BFD_ASSERT (entsize1 == bed->s->sizeof_rel
+                           || entsize1 == bed->s->sizeof_rela);
              same_size = !o->use_rela_p == (entsize1 == bed->s->sizeof_rel);
 
              if (!same_size)
              same_size = !o->use_rela_p == (entsize1 == bed->s->sizeof_rel);
 
              if (!same_size)
@@ -9476,7 +10365,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   /* sh_link is set in assign_section_numbers.  */
   /* sh_info is set below.  */
   /* sh_offset is set just below.  */
   /* sh_link is set in assign_section_numbers.  */
   /* sh_info is set below.  */
   /* sh_offset is set just below.  */
-  symtab_hdr->sh_addralign = 1 << bed->s->log_file_align;
+  symtab_hdr->sh_addralign = (bfd_vma) 1 << bed->s->log_file_align;
 
   off = elf_tdata (abfd)->next_file_pos;
   off = _bfd_elf_assign_file_position_for_section (symtab_hdr, off, TRUE);
 
   off = elf_tdata (abfd)->next_file_pos;
   off = _bfd_elf_assign_file_position_for_section (symtab_hdr, off, TRUE);
@@ -9496,7 +10385,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   finfo.symbuf = bfd_malloc (amt);
   if (finfo.symbuf == NULL)
     goto error_return;
   finfo.symbuf = bfd_malloc (amt);
   if (finfo.symbuf == NULL)
     goto error_return;
-  if (elf_numsections (abfd) > SHN_LORESERVE)
+  if (elf_numsections (abfd) > (SHN_LORESERVE & 0xFFFF))
     {
       /* Wild guess at number of output symbols.  realloc'd as needed.  */
       amt = 2 * max_sym_count + elf_numsections (abfd) + 1000;
     {
       /* Wild guess at number of output symbols.  realloc'd as needed.  */
       amt = 2 * max_sym_count + elf_numsections (abfd) + 1000;
@@ -9546,8 +10435,6 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
              if (!elf_link_output_sym (&finfo, NULL, &elfsym, o, NULL))
                goto error_return;
            }
              if (!elf_link_output_sym (&finfo, NULL, &elfsym, o, NULL))
                goto error_return;
            }
-         if (i == SHN_LORESERVE - 1)
-           i += SHN_HIRESERVE + 1 - SHN_LORESERVE;
        }
     }
 
        }
     }
 
@@ -9794,13 +10681,10 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
                 the original st_name with the dynstr_index.  */
              sym = e->isym;
 
                 the original st_name with the dynstr_index.  */
              sym = e->isym;
 
-             if (e->isym.st_shndx != SHN_UNDEF
-                 && (e->isym.st_shndx < SHN_LORESERVE
-                     || e->isym.st_shndx > SHN_HIRESERVE))
+             s = bfd_section_from_elf_index (e->input_bfd,
+                                             e->isym.st_shndx);
+             if (s != NULL)
                {
                {
-                 s = bfd_section_from_elf_index (e->input_bfd,
-                                                 e->isym.st_shndx);
-
                  sym.st_shndx =
                    elf_section_data (s->output_section)->this_idx;
                  if (! check_dynsym (abfd, &sym))
                  sym.st_shndx =
                    elf_section_data (s->output_section)->this_idx;
                  if (! check_dynsym (abfd, &sym))
@@ -9970,16 +10854,16 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
                    && (h->root.type == bfd_link_hash_defined
                        || h->root.type == bfd_link_hash_defweak))
                  {
                    && (h->root.type == bfd_link_hash_defined
                        || h->root.type == bfd_link_hash_defweak))
                  {
-                   dyn.d_un.d_val = h->root.u.def.value;
+                   dyn.d_un.d_ptr = h->root.u.def.value;
                    o = h->root.u.def.section;
                    if (o->output_section != NULL)
                    o = h->root.u.def.section;
                    if (o->output_section != NULL)
-                     dyn.d_un.d_val += (o->output_section->vma
+                     dyn.d_un.d_ptr += (o->output_section->vma
                                         + o->output_offset);
                    else
                      {
                        /* The symbol is imported from another shared
                           library and does not apply to this one.  */
                                         + o->output_offset);
                    else
                      {
                        /* The symbol is imported from another shared
                           library and does not apply to this one.  */
-                       dyn.d_un.d_val = 0;
+                       dyn.d_un.d_ptr = 0;
                      }
                    break;
                  }
                      }
                    break;
                  }
@@ -10058,6 +10942,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
              else
                type = SHT_RELA;
              dyn.d_un.d_val = 0;
              else
                type = SHT_RELA;
              dyn.d_un.d_val = 0;
+             dyn.d_un.d_ptr = 0;
              for (i = 1; i < elf_numsections (abfd); i++)
                {
                  Elf_Internal_Shdr *hdr;
              for (i = 1; i < elf_numsections (abfd); i++)
                {
                  Elf_Internal_Shdr *hdr;
@@ -10070,9 +10955,9 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
                        dyn.d_un.d_val += hdr->sh_size;
                      else
                        {
                        dyn.d_un.d_val += hdr->sh_size;
                      else
                        {
-                         if (dyn.d_un.d_val == 0
-                             || hdr->sh_addr < dyn.d_un.d_val)
-                           dyn.d_un.d_val = hdr->sh_addr;
+                         if (dyn.d_un.d_ptr == 0
+                             || hdr->sh_addr < dyn.d_un.d_ptr)
+                           dyn.d_un.d_ptr = hdr->sh_addr;
                        }
                    }
                }
                        }
                    }
                }
@@ -10107,7 +10992,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 
              if (dyn.d_tag == DT_TEXTREL)
                {
 
              if (dyn.d_tag == DT_TEXTREL)
                {
-                info->callbacks->einfo 
+                info->callbacks->einfo
                    (_("%P: warning: creating a DT_TEXTREL in a shared object.\n"));
                  break;
                }
                    (_("%P: warning: creating a DT_TEXTREL in a shared object.\n"));
                  break;
                }
@@ -10206,6 +11091,16 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 
   elf_tdata (abfd)->linker = TRUE;
 
 
   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:
   return TRUE;
 
  error_return:
@@ -10241,6 +11136,139 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   return FALSE;
 }
 \f
   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.  */
 /* Garbage collect unused sections.  */
 
 /* Default gc_mark_hook.  */
@@ -10273,6 +11301,59 @@ _bfd_elf_gc_mark_hook (asection *sec,
   return NULL;
 }
 
   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.  */
+
+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)
+{
+  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 (!_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.  */
 /* 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.  */
@@ -10283,8 +11364,7 @@ _bfd_elf_gc_mark (struct bfd_link_info *info,
                  elf_gc_mark_hook_fn gc_mark_hook)
 {
   bfd_boolean ret;
                  elf_gc_mark_hook_fn gc_mark_hook)
 {
   bfd_boolean ret;
-  bfd_boolean is_eh;
-  asection *group_sec;
+  asection *group_sec, *eh_frame;
 
   sec->gc_mark = 1;
 
 
   sec->gc_mark = 1;
 
@@ -10296,103 +11376,39 @@ _bfd_elf_gc_mark (struct bfd_link_info *info,
 
   /* Look through the section relocs.  */
   ret = TRUE;
 
   /* Look through the section relocs.  */
   ret = TRUE;
-  is_eh = strcmp (sec->name, ".eh_frame") == 0;
-  if ((sec->flags & SEC_RELOC) != 0 && sec->reloc_count > 0)
+  eh_frame = elf_eh_frame_section (sec->owner);
+  if ((sec->flags & SEC_RELOC) != 0
+      && sec->reloc_count > 0
+      && sec != eh_frame)
     {
     {
-      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;
+      struct elf_reloc_cookie cookie;
 
 
-      isym = (Elf_Internal_Sym *) symtab_hdr->contents;
-      if (isym == NULL && nlocsyms != 0)
+      if (!init_reloc_cookie_for_section (&cookie, info, sec))
+       ret = FALSE;
+      else
        {
        {
-         isym = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, nlocsyms, 0,
-                                      NULL, NULL, NULL);
-         if (isym == NULL)
-           return FALSE;
+         for (; cookie.rel < cookie.relend; cookie.rel++)
+           if (!_bfd_elf_gc_mark_reloc (info, sec, gc_mark_hook, &cookie))
+             {
+               ret = FALSE;
+               break;
+             }
+         fini_reloc_cookie_for_section (&cookie, sec);
        }
        }
+    }
 
 
-      /* 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;
+  if (ret && eh_frame && elf_fde_list (sec))
+    {
+      struct elf_reloc_cookie cookie;
 
 
-      if (bed->s->arch_size == 32)
-       r_sym_shift = 8;
+      if (!init_reloc_cookie_for_section (&cookie, info, eh_frame))
+       ret = FALSE;
       else
       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;
+         if (!_bfd_elf_gc_mark_fdes (info, sec, eh_frame,
+                                     gc_mark_hook, &cookie))
+           ret = FALSE;
+         fini_reloc_cookie_for_section (&cookie, eh_frame);
        }
     }
 
        }
     }
 
@@ -10449,10 +11465,21 @@ elf_gc_sweep (bfd *abfd, struct bfd_link_info *info)
 
       for (o = sub->sections; o != NULL; o = o->next)
        {
 
       for (o = sub->sections; o != NULL; o = o->next)
        {
-         /* Keep debug and special sections.  */
-         if ((o->flags & (SEC_DEBUGGING | SEC_LINKER_CREATED)) != 0
-             || (o->flags & (SEC_ALLOC | SEC_LOAD | SEC_RELOC)) == 0)
-           o->gc_mark = 1;
+         /* When any section in a section group is kept, we keep all
+            sections in the section group.  If the first member of
+            the section group is excluded, we will also exclude the
+            group section.  */
+         if (o->flags & SEC_GROUP)
+           {
+             asection *first = elf_next_in_group (o);
+             o->gc_mark = first->gc_mark;
+           }
+         else if ((o->flags & (SEC_DEBUGGING | SEC_LINKER_CREATED)) != 0
+                  || (o->flags & (SEC_ALLOC | SEC_LOAD | SEC_RELOC)) == 0)
+           {
+             /* Keep debug and special sections.  */
+             o->gc_mark = 1;
+           }
 
          if (o->gc_mark)
            continue;
 
          if (o->gc_mark)
            continue;
@@ -10465,7 +11492,7 @@ elf_gc_sweep (bfd *abfd, struct bfd_link_info *info)
             to remove a section from the output.  */
          o->flags |= SEC_EXCLUDE;
 
             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
            _bfd_error_handler (_("Removing unused section '%s' in file '%B'"), sub, o->name);
 
          /* But we also have to update some of the relocation
@@ -10642,6 +11669,29 @@ bfd_elf_gc_mark_dynamic_ref_symbol (struct elf_link_hash_entry *h, void *inf)
   return TRUE;
 }
 
   return TRUE;
 }
 
+/* Keep all sections containing symbols undefined on the command-line,
+   and the section containing the entry symbol.  */
+
+void
+_bfd_elf_gc_keep (struct bfd_link_info *info)
+{
+  struct bfd_sym_chain *sym;
+
+  for (sym = info->gc_sym_list; sym != NULL; sym = sym->next)
+    {
+      struct elf_link_hash_entry *h;
+
+      h = elf_link_hash_lookup (elf_hash_table (info), sym->name,
+                               FALSE, FALSE, FALSE);
+
+      if (h != NULL
+         && (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak)
+         && !bfd_is_abs_section (h->root.u.def.section))
+       h->root.u.def.section->flags |= SEC_KEEP;
+    }
+}
+
 /* Do mark and sweep of unused sections.  */
 
 bfd_boolean
 /* Do mark and sweep of unused sections.  */
 
 bfd_boolean
@@ -10653,14 +11703,33 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
 
   if (!bed->can_gc_sections
   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
 
   if (!bed->can_gc_sections
-      || info->relocatable
-      || info->emitrelocations
       || !is_elf_hash_table (info->hash))
     {
       (*_bfd_error_handler)(_("Warning: gc-sections option ignored"));
       return TRUE;
     }
 
       || !is_elf_hash_table (info->hash))
     {
       (*_bfd_error_handler)(_("Warning: gc-sections option ignored"));
       return TRUE;
     }
 
+  bed->gc_keep (info);
+
+  /* Try to parse each bfd's .eh_frame section.  Point elf_eh_frame_section
+     at the .eh_frame section if we can mark the FDEs individually.  */
+  _bfd_elf_begin_eh_frame_parsing (info);
+  for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
+    {
+      asection *sec;
+      struct elf_reloc_cookie cookie;
+
+      sec = bfd_get_section_by_name (sub, ".eh_frame");
+      if (sec && init_reloc_cookie_for_section (&cookie, info, sec))
+       {
+         _bfd_elf_parse_eh_frame (sub, info, sec, &cookie);
+         if (elf_section_data (sec)->sec_info)
+           elf_eh_frame_section (sub) = sec;
+         fini_reloc_cookie_for_section (&cookie, sec);
+       }
+    }
+  _bfd_elf_end_eh_frame_parsing (info);
+
   /* Apply transitive closure to the vtable entry usage info.  */
   elf_link_hash_traverse (elf_hash_table (info),
                          elf_gc_propagate_vtable_entries_used,
   /* Apply transitive closure to the vtable entry usage info.  */
   elf_link_hash_traverse (elf_hash_table (info),
                          elf_gc_propagate_vtable_entries_used,
@@ -10698,69 +11767,7 @@ bfd_elf_gc_sections (bfd *abfd, struct bfd_link_info *info)
 
   /* Allow the backend to mark additional target specific sections.  */
   if (bed->gc_mark_extra_sections)
 
   /* Allow the backend to mark additional target specific sections.  */
   if (bed->gc_mark_extra_sections)
-    bed->gc_mark_extra_sections(info, gc_mark_hook);
-
-  /* ... again for sections marked from eh_frame.  */
-  for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
-    {
-      asection *o;
-
-      if (bfd_get_flavour (sub) != bfd_target_elf_flavour)
-       continue;
-
-      /* Keep .gcc_except_table.* if the associated .text.* (or the
-        associated .gnu.linkonce.t.* if .text.* doesn't exist) is
-        marked.  This isn't very nice, but the proper solution,
-        splitting .eh_frame up and using comdat doesn't pan out
-        easily due to needing special relocs to handle the
-        difference of two symbols in separate sections.
-        Don't keep code sections referenced by .eh_frame.  */
-#define TEXT_PREFIX                    ".text."
-#define TEXT_PREFIX2                   ".gnu.linkonce.t."
-#define GCC_EXCEPT_TABLE_PREFIX                ".gcc_except_table."
-      for (o = sub->sections; o != NULL; o = o->next)
-       if (!o->gc_mark && o->gc_mark_from_eh && (o->flags & SEC_CODE) == 0)
-         {
-           if (CONST_STRNEQ (o->name, GCC_EXCEPT_TABLE_PREFIX))
-             {
-               char *fn_name;
-               const char *sec_name;
-               asection *fn_text;
-               unsigned o_name_prefix_len , fn_name_prefix_len, tmp;
-
-               o_name_prefix_len = strlen (GCC_EXCEPT_TABLE_PREFIX);
-               sec_name = o->name + o_name_prefix_len;
-               fn_name_prefix_len = strlen (TEXT_PREFIX);
-               tmp = strlen (TEXT_PREFIX2);
-               if (tmp > fn_name_prefix_len)
-                 fn_name_prefix_len = tmp;
-               fn_name
-                 = bfd_malloc (fn_name_prefix_len + strlen (sec_name) + 1);
-               if (fn_name == NULL)
-                 return FALSE;
-
-               /* Try the first prefix.  */
-               sprintf (fn_name, "%s%s", TEXT_PREFIX, sec_name);
-               fn_text = bfd_get_section_by_name (sub, fn_name);
-
-               /* Try the second prefix.  */
-               if (fn_text == NULL)
-                 {
-                   sprintf (fn_name, "%s%s", TEXT_PREFIX2, sec_name);
-                   fn_text = bfd_get_section_by_name (sub, fn_name);
-                 }
-
-               free (fn_name);
-               if (fn_text == NULL || !fn_text->gc_mark)
-                 continue;
-             }
-
-           /* If not using specially named exception table section,
-              then keep whatever we are using.  */
-           if (!_bfd_elf_gc_mark (info, o, gc_mark_hook))
-             return FALSE;
-         }
-    }
+    bed->gc_mark_extra_sections (info, gc_mark_hook);
 
   /* ... and mark SEC_EXCLUDE for those that go.  */
   return elf_gc_sweep (abfd, info);
 
   /* ... and mark SEC_EXCLUDE for those that go.  */
   return elf_gc_sweep (abfd, info);
@@ -10903,7 +11910,7 @@ bfd_elf_gc_record_vtentry (bfd *abfd ATTRIBUTE_UNUSED,
 
 struct alloc_got_off_arg {
   bfd_vma gotoff;
 
 struct alloc_got_off_arg {
   bfd_vma gotoff;
-  unsigned int got_elt_size;
+  struct bfd_link_info *info;
 };
 
 /* We need a special top-level link routine to convert got reference counts
 };
 
 /* We need a special top-level link routine to convert got reference counts
@@ -10913,6 +11920,8 @@ static bfd_boolean
 elf_gc_allocate_got_offsets (struct elf_link_hash_entry *h, void *arg)
 {
   struct alloc_got_off_arg *gofarg = arg;
 elf_gc_allocate_got_offsets (struct elf_link_hash_entry *h, void *arg)
 {
   struct alloc_got_off_arg *gofarg = arg;
+  bfd *obfd = gofarg->info->output_bfd;
+  const struct elf_backend_data *bed = get_elf_backend_data (obfd);
 
   if (h->root.type == bfd_link_hash_warning)
     h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
   if (h->root.type == bfd_link_hash_warning)
     h = (struct elf_link_hash_entry *) h->root.u.i.link;
@@ -10920,7 +11929,7 @@ elf_gc_allocate_got_offsets (struct elf_link_hash_entry *h, void *arg)
   if (h->got.refcount > 0)
     {
       h->got.offset = gofarg->gotoff;
   if (h->got.refcount > 0)
     {
       h->got.offset = gofarg->gotoff;
-      gofarg->gotoff += gofarg->got_elt_size;
+      gofarg->gotoff += bed->got_elt_size (obfd, gofarg->info, h, NULL, 0);
     }
   else
     h->got.offset = (bfd_vma) -1;
     }
   else
     h->got.offset = (bfd_vma) -1;
@@ -10938,9 +11947,10 @@ bfd_elf_gc_common_finalize_got_offsets (bfd *abfd,
   bfd *i;
   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
   bfd_vma gotoff;
   bfd *i;
   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
   bfd_vma gotoff;
-  unsigned int got_elt_size = bed->s->arch_size / 8;
   struct alloc_got_off_arg gofarg;
 
   struct alloc_got_off_arg gofarg;
 
+  BFD_ASSERT (abfd == info->output_bfd);
+
   if (! is_elf_hash_table (info->hash))
     return FALSE;
 
   if (! is_elf_hash_table (info->hash))
     return FALSE;
 
@@ -10976,7 +11986,7 @@ bfd_elf_gc_common_finalize_got_offsets (bfd *abfd,
          if (local_got[j] > 0)
            {
              local_got[j] = gotoff;
          if (local_got[j] > 0)
            {
              local_got[j] = gotoff;
-             gotoff += got_elt_size;
+             gotoff += bed->got_elt_size (abfd, info, NULL, i, j);
            }
          else
            local_got[j] = (bfd_vma) -1;
            }
          else
            local_got[j] = (bfd_vma) -1;
@@ -10986,7 +11996,7 @@ bfd_elf_gc_common_finalize_got_offsets (bfd *abfd,
   /* Then the global .got entries.  .plt refcounts are handled by
      adjust_dynamic_symbol  */
   gofarg.gotoff = gotoff;
   /* Then the global .got entries.  .plt refcounts are handled by
      adjust_dynamic_symbol  */
   gofarg.gotoff = gotoff;
-  gofarg.got_elt_size = got_elt_size;
+  gofarg.info = info;
   elf_link_hash_traverse (elf_hash_table (info),
                          elf_gc_allocate_got_offsets,
                          &gofarg);
   elf_link_hash_traverse (elf_hash_table (info),
                          elf_gc_allocate_got_offsets,
                          &gofarg);
@@ -11056,12 +12066,9 @@ bfd_elf_reloc_symbol_deleted_p (bfd_vma offset, void *cookie)
 
          /* Need to: get the symbol; get the section.  */
          isym = &rcookie->locsyms[r_symndx];
 
          /* Need to: get the symbol; get the section.  */
          isym = &rcookie->locsyms[r_symndx];
-         if (isym->st_shndx < SHN_LORESERVE || isym->st_shndx > SHN_HIRESERVE)
-           {
-             isec = bfd_section_from_elf_index (rcookie->abfd, isym->st_shndx);
-             if (isec != NULL && elf_discarded_section (isec))
-               return TRUE;
-           }
+         isec = bfd_section_from_elf_index (rcookie->abfd, isym->st_shndx);
+         if (isec != NULL && elf_discarded_section (isec))
+           return TRUE;
        }
       return FALSE;
     }
        }
       return FALSE;
     }
@@ -11078,16 +12085,15 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 {
   struct elf_reloc_cookie cookie;
   asection *stab, *eh;
 {
   struct elf_reloc_cookie cookie;
   asection *stab, *eh;
-  Elf_Internal_Shdr *symtab_hdr;
   const struct elf_backend_data *bed;
   bfd *abfd;
   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_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)
   for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
     {
       if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
@@ -11120,93 +12126,39 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
          && bed->elf_backend_discard_info == NULL)
        continue;
 
          && 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)
-           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 (_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 (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
 
   if (info->eh_frame_hdr
       && !info->relocatable
@@ -11216,8 +12168,21 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
   return ret;
 }
 
   return ret;
 }
 
+/* For a SHT_GROUP section, return the group signature.  For other
+   sections, return the normal section name.  */
+
+static const char *
+section_signature (asection *sec)
+{
+  if ((sec->flags & SEC_GROUP) != 0
+      && elf_next_in_group (sec) != NULL
+      && elf_group_name (elf_next_in_group (sec)) != NULL)
+    return elf_group_name (elf_next_in_group (sec));
+  return sec->name;
+}
+
 void
 void
-_bfd_elf_section_already_linked (bfd *abfd, struct bfd_section *sec,
+_bfd_elf_section_already_linked (bfd *abfd, asection *sec,
                                 struct bfd_link_info *info)
 {
   flagword flags;
                                 struct bfd_link_info *info)
 {
   flagword flags;
@@ -11257,7 +12222,7 @@ _bfd_elf_section_already_linked (bfd *abfd, struct bfd_section *sec,
      causes trouble for MIPS ELF, which relies on link once semantics
      to handle the .reginfo section correctly.  */
 
      causes trouble for MIPS ELF, which relies on link once semantics
      to handle the .reginfo section correctly.  */
 
-  name = bfd_get_section_name (abfd, sec);
+  name = section_signature (sec);
 
   if (CONST_STRNEQ (name, ".gnu.linkonce.")
       && (p = strchr (name + sizeof (".gnu.linkonce.") - 1, '.')) != NULL)
 
   if (CONST_STRNEQ (name, ".gnu.linkonce.")
       && (p = strchr (name + sizeof (".gnu.linkonce.") - 1, '.')) != NULL)
@@ -11272,7 +12237,7 @@ _bfd_elf_section_already_linked (bfd *abfd, struct bfd_section *sec,
       /* We may have 2 different types of sections on the list: group
         sections and linkonce sections.  Match like sections.  */
       if ((flags & SEC_GROUP) == (l->sec->flags & SEC_GROUP)
       /* We may have 2 different types of sections on the list: group
         sections and linkonce sections.  Match like sections.  */
       if ((flags & SEC_GROUP) == (l->sec->flags & SEC_GROUP)
-         && strcmp (name, l->sec->name) == 0
+         && strcmp (name, section_signature (l->sec)) == 0
          && bfd_coff_get_comdat_section (l->sec->owner, l->sec) == NULL)
        {
          /* The section has already been linked.  See if we should
          && bfd_coff_get_comdat_section (l->sec->owner, l->sec) == NULL)
        {
          /* The section has already been linked.  See if we should
@@ -11395,8 +12360,31 @@ _bfd_elf_section_already_linked (bfd *abfd, struct bfd_section *sec,
            }
        }
 
            }
        }
 
+  /* Do not complain on unresolved relocations in `.gnu.linkonce.r.F'
+     referencing its discarded `.gnu.linkonce.t.F' counterpart - g++-3.4
+     specific as g++-4.x is using COMDAT groups (without the `.gnu.linkonce'
+     prefix) instead.  `.gnu.linkonce.r.*' were the `.rodata' part of its
+     matching `.gnu.linkonce.t.*'.  If `.gnu.linkonce.r.F' is not discarded
+     but its `.gnu.linkonce.t.F' is discarded means we chose one-only
+     `.gnu.linkonce.t.F' section from a different bfd not requiring any
+     `.gnu.linkonce.r.F'.  Thus `.gnu.linkonce.r.F' should be discarded.
+     The reverse order cannot happen as there is never a bfd with only the
+     `.gnu.linkonce.r.F' section.  The order of sections in a bfd does not
+     matter as here were are looking only for cross-bfd sections.  */
+
+  if ((flags & SEC_GROUP) == 0 && CONST_STRNEQ (name, ".gnu.linkonce.r."))
+    for (l = already_linked_list->entry; l != NULL; l = l->next)
+      if ((l->sec->flags & SEC_GROUP) == 0
+         && CONST_STRNEQ (l->sec->name, ".gnu.linkonce.t."))
+       {
+         if (abfd != l->sec->owner)
+           sec->output_section = bfd_abs_section_ptr;
+         break;
+       }
+
   /* This is the first section with this name.  Record it.  */
   /* 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\n"));
 }
 
 bfd_boolean
 }
 
 bfd_boolean
@@ -11416,3 +12404,206 @@ _bfd_elf_common_section (asection *sec ATTRIBUTE_UNUSED)
 {
   return bfd_com_section_ptr;
 }
 {
   return bfd_com_section_ptr;
 }
+
+bfd_vma
+_bfd_elf_default_got_elt_size (bfd *abfd,
+                              struct bfd_link_info *info ATTRIBUTE_UNUSED,
+                              struct elf_link_hash_entry *h ATTRIBUTE_UNUSED,
+                              bfd *ibfd ATTRIBUTE_UNUSED,
+                              unsigned long symndx ATTRIBUTE_UNUSED)
+{
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  return bed->s->arch_size / 8;
+}
+
+/* Routines to support the creation of dynamic relocs.  */
+
+/* Return true if NAME is a name of a relocation
+   section associated with section S.  */
+
+static bfd_boolean
+is_reloc_section (bfd_boolean rela, const char * name, asection * s)
+{
+  if (rela)
+    return CONST_STRNEQ (name, ".rela")
+      && strcmp (bfd_get_section_name (NULL, s), name + 5) == 0;
+
+  return CONST_STRNEQ (name, ".rel")
+    && strcmp (bfd_get_section_name (NULL, s), name + 4) == 0;
+}
+
+/* Returns the name of the dynamic reloc section associated with SEC.  */
+
+static const char *
+get_dynamic_reloc_section_name (bfd *       abfd,
+                               asection *  sec,
+                               bfd_boolean is_rela)
+{
+  const char * name;
+  unsigned int strndx = elf_elfheader (abfd)->e_shstrndx;
+  unsigned int shnam = elf_section_data (sec)->rel_hdr.sh_name;
+
+  name = bfd_elf_string_from_elf_section (abfd, strndx, shnam);
+  if (name == NULL)
+    return NULL;
+
+  if (! is_reloc_section (is_rela, name, sec))
+    {
+      static bfd_boolean complained = FALSE;
+
+      if (! complained)
+       {
+         (*_bfd_error_handler)
+           (_("%B: bad relocation section name `%s\'"),  abfd, name);
+         complained = TRUE;
+       }
+      name = NULL;
+    }
+
+  return name;
+}
+
+/* Returns the dynamic reloc section associated with SEC.
+   If necessary compute the name of the dynamic reloc section based
+   on SEC's name (looked up in ABFD's string table) and the setting
+   of IS_RELA.  */
+
+asection *
+_bfd_elf_get_dynamic_reloc_section (bfd *       abfd,
+                                   asection *  sec,
+                                   bfd_boolean is_rela)
+{
+  asection * reloc_sec = elf_section_data (sec)->sreloc;
+
+  if (reloc_sec == NULL)
+    {
+      const char * name = get_dynamic_reloc_section_name (abfd, sec, is_rela);
+
+      if (name != NULL)
+       {
+         reloc_sec = bfd_get_section_by_name (abfd, name);
+
+         if (reloc_sec != NULL)
+           elf_section_data (sec)->sreloc = reloc_sec;
+       }
+    }
+
+  return reloc_sec;
+}
+
+/* Returns the dynamic reloc section associated with SEC.  If the
+   section does not exist it is created and attached to the DYNOBJ
+   bfd and stored in the SRELOC field of SEC's elf_section_data
+   structure.
+   
+   ALIGNMENT is the alignment for the newly created section and
+   IS_RELA defines whether the name should be .rela.<SEC's name>
+   or .rel.<SEC's name>.  The section name is looked up in the
+   string table associated with ABFD.  */
+
+asection *
+_bfd_elf_make_dynamic_reloc_section (asection *         sec,
+                                    bfd *              dynobj,
+                                    unsigned int       alignment,
+                                    bfd *              abfd,
+                                    bfd_boolean        is_rela)
+{
+  asection * reloc_sec = elf_section_data (sec)->sreloc;
+
+  if (reloc_sec == NULL)
+    {
+      const char * name = get_dynamic_reloc_section_name (abfd, sec, is_rela);
+
+      if (name == NULL)
+       return NULL;
+
+      reloc_sec = bfd_get_section_by_name (dynobj, name);
+
+      if (reloc_sec == NULL)
+       {
+         flagword flags;
+
+         flags = (SEC_HAS_CONTENTS | SEC_READONLY | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+         if ((sec->flags & SEC_ALLOC) != 0)
+           flags |= SEC_ALLOC | SEC_LOAD;
+
+         reloc_sec = bfd_make_section_with_flags (dynobj, name, flags);
+         if (reloc_sec != NULL)
+           {
+             if (! bfd_set_section_alignment (dynobj, reloc_sec, alignment))
+               reloc_sec = NULL;
+           }
+       }
+
+      elf_section_data (sec)->sreloc = reloc_sec;
+    }
+
+  return reloc_sec;
+}
+
+/* Returns the name of the ifunc using dynamic reloc section associated with SEC.  */
+#define IFUNC_INFIX ".ifunc"
+
+static const char *
+get_ifunc_reloc_section_name (bfd *       abfd,
+                             asection *  sec)
+{
+  const char *  dot;
+  char *  name;
+  const char *  base_name;
+  unsigned int  strndx = elf_elfheader (abfd)->e_shstrndx;
+  unsigned int  shnam = elf_section_data (sec)->rel_hdr.sh_name;
+
+  base_name = bfd_elf_string_from_elf_section (abfd, strndx, shnam);
+  if (base_name == NULL)
+    return NULL;
+
+  dot = strchr (base_name + 1, '.');
+  name = bfd_alloc (abfd, strlen (base_name) + strlen (IFUNC_INFIX) + 1);
+  sprintf (name, "%.*s%s%s", (int)(dot - base_name), base_name, IFUNC_INFIX, dot);
+
+  return name;
+}
+
+/* Like _bfd_elf_make_dynamic_reloc_section but it creates a
+   section for holding relocs against symbols with the STT_GNU_IFUNC
+   type.  The section is attached to the OWNER bfd but it is created
+   with a name based on SEC from ABFD.  */
+
+asection *
+_bfd_elf_make_ifunc_reloc_section (bfd *         abfd,
+                                  asection *    sec,
+                                  bfd *         owner,
+                                  unsigned int  align)
+{
+  asection * reloc_sec = elf_section_data (sec)->indirect_relocs;
+
+  if (reloc_sec == NULL)
+    {
+      const char * name = get_ifunc_reloc_section_name (abfd, sec);
+
+      if (name == NULL)
+       return NULL;
+
+      reloc_sec = bfd_get_section_by_name (owner, name);
+
+      if (reloc_sec == NULL)
+       {
+         flagword flags;
+
+         flags = (SEC_HAS_CONTENTS | SEC_READONLY | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+         if ((sec->flags & SEC_ALLOC) != 0)
+           flags |= SEC_ALLOC | SEC_LOAD;
+
+         reloc_sec = bfd_make_section_with_flags (owner, name, flags);
+         
+         if (reloc_sec != NULL
+             && ! bfd_set_section_alignment (owner, reloc_sec, align))
+           reloc_sec = NULL;
+       }
+
+      elf_section_data (sec)->indirect_relocs = reloc_sec;
+    }
+
+  return reloc_sec;
+}
This page took 0.085859 seconds and 4 git commands to generate.