include/elf
[deliverable/binutils-gdb.git] / bfd / elflink.c
index cac22b7c720af62e4cbc6fc506ea3a1038ded691..54ad2af43aba5642b92487b371b4fa650ebe2fa1 100644 (file)
@@ -1,6 +1,7 @@
 /* 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.
 
 #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 *
@@ -309,7 +336,7 @@ _bfd_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
     }
 
   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
@@ -347,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,
-                                          (bed->default_use_rela_p
+                                          (bed->rela_plts_and_copies_p
                                            ? ".rela.bss" : ".rel.bss"),
                                           flags | SEC_READONLY);
          if (s == NULL
@@ -437,7 +464,7 @@ bfd_elf_link_record_dynamic_symbol (struct bfd_link_info *info,
 \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)
@@ -452,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)))
-      || (d != NULL 
+      || (d != NULL
          && h->root.type == bfd_link_hash_new
          && (*d->match) (&d->head, NULL, h->root.root.string)))
     h->dynamic = 1;
@@ -501,7 +528,7 @@ bfd_elf_record_link_assignment (bfd *output_bfd,
       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.  */
+        the versioned symbol point to this one.  */
       bed = get_elf_backend_data (output_bfd);
       hv = h;
       while (hv->root.type == bfd_link_hash_indirect
@@ -618,8 +645,7 @@ bfd_elf_link_record_local_dynamic_symbol (struct bfd_link_info *info,
     }
 
   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;
 
@@ -823,6 +849,54 @@ _bfd_elf_link_renumber_dynsyms (bfd *output_bfd,
   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
@@ -855,7 +929,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
   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;
@@ -882,9 +956,11 @@ _bfd_elf_merge_symbol (bfd *abfd,
     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.  */
-  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.  */
@@ -971,7 +1047,15 @@ _bfd_elf_merge_symbol (bfd *abfd,
            && 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
@@ -987,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
-      && !(bed->is_function_type (ELF_ST_TYPE (sym->st_info))
-          && bed->is_function_type (h->type)))
+      && !(newfunc && oldfunc))
     {
       *skip = TRUE;
       return TRUE;
@@ -1179,9 +1262,8 @@ _bfd_elf_merge_symbol (bfd *abfd,
   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
@@ -1230,7 +1312,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
       && (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;
@@ -1242,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
-      && !bed->is_function_type (h->type))
+      && !oldfunc)
     olddyncommon = TRUE;
   else
     olddyncommon = FALSE;
@@ -1302,8 +1384,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
       && 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;
@@ -1341,7 +1422,22 @@ _bfd_elf_merge_symbol (bfd *abfd,
 
   /* 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
@@ -1357,8 +1453,7 @@ _bfd_elf_merge_symbol (bfd *abfd,
   if (!newdyn
       && (newdef
          || (bfd_is_com_section (sec)
-             && (oldweak
-                 || bed->is_function_type (h->type))))
+             && (oldweak || oldfunc)))
       && olddyn
       && olddef
       && h->def_dynamic)
@@ -1378,7 +1473,17 @@ _bfd_elf_merge_symbol (bfd *abfd,
         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;
@@ -1459,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.  */
 
-bfd_boolean
+static bfd_boolean
 _bfd_elf_add_default_symbol (bfd *abfd,
                             struct bfd_link_info *info,
                             struct elf_link_hash_entry *h,
@@ -1693,10 +1798,84 @@ nondefault:
   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.  */
 
-bfd_boolean
+static bfd_boolean
 _bfd_elf_export_symbol (struct elf_link_hash_entry *h, void *data)
 {
   struct elf_info_failed *eif = data;
@@ -1716,29 +1895,12 @@ _bfd_elf_export_symbol (struct elf_link_hash_entry *h, void *data)
       && (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;
@@ -1755,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.  */
 
-bfd_boolean
+static bfd_boolean
 _bfd_elf_link_find_version_dependencies (struct elf_link_hash_entry *h,
                                         void *data)
 {
@@ -1776,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.  */
-  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;
@@ -1793,7 +1957,7 @@ _bfd_elf_link_find_version_dependencies (struct elf_link_hash_entry *h,
   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;
@@ -1801,12 +1965,12 @@ _bfd_elf_link_find_version_dependencies (struct elf_link_hash_entry *h,
        }
 
       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;
-  a = bfd_zalloc (rinfo->output_bfd, amt);
+  a = bfd_zalloc (rinfo->info->output_bfd, amt);
   if (a == NULL)
     {
       rinfo->failed = TRUE;
@@ -1837,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.  */
 
-bfd_boolean
+static bfd_boolean
 _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;
@@ -1868,7 +2032,7 @@ _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data)
   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)
     {
@@ -1951,7 +2115,7 @@ _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data)
            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;
@@ -1980,7 +2144,7 @@ _bfd_elf_link_assign_sym_version (struct elf_link_hash_entry *h, void *data)
             generating a shared archive.  Return an error.  */
          (*_bfd_error_handler)
            (_("%B: version node not found for symbol %s"),
-            sinfo->output_bfd, h->root.root.string);
+            info->output_bfd, h->root.root.string);
          bfd_set_error (bfd_error_bad_value);
          sinfo->failed = TRUE;
          return FALSE;
@@ -1994,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)
     {
-      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);
-           }
+      bfd_boolean hide;
 
-         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;
-           }
-       }
-
-      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;
@@ -2098,7 +2202,7 @@ elf_link_read_relocs_from_section (bfd *abfd,
     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);
 
@@ -2124,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;
-      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)
-           (_("%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);
@@ -2178,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)
-       internal_relocs = bfd_alloc (abfd, size);
+       internal_relocs = alloc2 = bfd_alloc (abfd, size);
       else
        internal_relocs = alloc2 = bfd_malloc (size);
       if (internal_relocs == NULL)
@@ -2226,14 +2343,19 @@ _bfd_elf_link_read_relocs (bfd *abfd,
   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.  */
 
-bfd_boolean
+static bfd_boolean
 _bfd_elf_link_size_reloc_section (bfd *abfd,
                                  Elf_Internal_Shdr *rel_hdr,
                                  asection *o)
@@ -2321,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_set_error (bfd_error_wrong_object_format);
+      bfd_set_error (bfd_error_wrong_format);
       return FALSE;
     }
 
@@ -2372,7 +2494,7 @@ _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.  */
 
-bfd_boolean
+static bfd_boolean
 _bfd_elf_fix_symbol_flags (struct elf_link_hash_entry *h,
                           struct elf_info_failed *eif)
 {
@@ -2515,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.  */
 
-bfd_boolean
+static bfd_boolean
 _bfd_elf_adjust_dynamic_symbol (struct elf_link_hash_entry *h, void *data)
 {
   struct elf_info_failed *eif = data;
@@ -2626,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);
+
+
+  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;
@@ -2684,7 +2814,7 @@ _bfd_elf_adjust_dynamic_copy (struct elf_link_hash_entry *h,
 /* 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;
@@ -2789,6 +2919,11 @@ _bfd_elf_symbol_refs_local_p (struct elf_link_hash_entry *h,
   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))
@@ -2817,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;
 
-  /* 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;
@@ -3088,6 +3219,16 @@ elf_add_dt_needed_tag (bfd *abfd,
   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)
@@ -3252,11 +3393,46 @@ elf_finalize_dynstr (bfd *output_bfd, struct bfd_link_info *info)
   return TRUE;
 }
 \f
+/* Return TRUE iff relocations for INPUT are compatible with OUTPUT.
+   The default is to only match when the INPUT and OUTPUT are exactly
+   the same target.  */
+
+bfd_boolean
+_bfd_elf_default_relocs_compatible (const bfd_target *input,
+                                   const bfd_target *output)
+{
+  return input == output;
+}
+
+/* Return TRUE iff relocations for INPUT are compatible with OUTPUT.
+   This version is used when different targets for the same architecture
+   are virtually identical.  */
+
+bfd_boolean
+_bfd_elf_relocs_compatible (const bfd_target *input,
+                           const bfd_target *output)
+{
+  const struct elf_backend_data *obed, *ibed;
+
+  if (input == output)
+    return TRUE;
+
+  ibed = xvec_get_elf_backend_data (input);
+  obed = xvec_get_elf_backend_data (output);
+
+  if (ibed->arch != obed->arch)
+    return FALSE;
+
+  /* If both backends are using this function, deem them compatible.  */
+  return ibed->relocs_compatible == obed->relocs_compatible;
+}
+
 /* Add symbols from an ELF object file to the linker hash table.  */
 
 static bfd_boolean
 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;
@@ -3302,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)
-         || htab->root.creator != abfd->xvec)
+         || info->output_bfd->xvec != abfd->xvec)
        {
          if (info->relocatable)
            bfd_set_error (bfd_error_invalid_operation);
@@ -3312,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
@@ -3399,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)
-         && htab->root.creator == abfd->xvec
+         && info->output_bfd->xvec == abfd->xvec
          && !htab->dynamic_sections_created)
        {
          if (! _bfd_elf_link_create_dynamic_sections (abfd, info))
@@ -3437,14 +3624,14 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
        {
          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);
-         if (elfsec == -1)
+         if (elfsec == SHN_BAD)
            goto error_free_dyn;
          shlink = elf_elfsections (abfd)[elfsec]->sh_link;
 
@@ -3785,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;
-      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)
@@ -3801,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 (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);
@@ -4187,42 +4369,8 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *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
@@ -4239,7 +4387,15 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
                    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)
@@ -4259,7 +4415,7 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
                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);
@@ -4320,8 +4476,11 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
 
          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);
@@ -4596,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))
-                       goto error_return;
+                       {
+                       err_free_sym_hash:
+                         free (sorted_sym_hash);
+                         goto error_return;
+                       }
                    }
 
                  /* If the real definition is in the list of dynamic
@@ -4607,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))
-                       goto error_return;
+                       goto err_free_sym_hash;
                    }
                  break;
                }
@@ -4617,8 +4780,9 @@ elf_link_add_object_symbols (bfd *abfd, struct bfd_link_info *info)
       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
@@ -4639,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)
-      && htab->root.creator == abfd->xvec
-      && bed->check_relocs != NULL)
+      && bed->check_relocs != NULL
+      && (*bed->relocs_compatible) (abfd->xvec, info->output_bfd->xvec))
     {
       asection *o;
 
@@ -4997,7 +5161,7 @@ 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.  */
 
@@ -5200,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
-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;
-  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
@@ -5218,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 dynsymcount = elf_hash_table (info)->dynsymcount;
       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
@@ -5346,7 +5512,7 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
   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;
 
@@ -5372,7 +5538,7 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
        {
          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)
@@ -5509,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)
-         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;
 
-             name = d->symbol;
+             name = d->pattern;
              namelen = strlen (name);
              verstr = t->name;
              verlen = strlen (verstr);
@@ -5557,7 +5723,6 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
            }
 
       /* Attach all the symbols to their version information.  */
-      asvinfo.output_bfd = output_bfd;
       asvinfo.info = info;
       asvinfo.verdefs = verdefs;
       asvinfo.failed = FALSE;
@@ -5574,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)
-             if (!d->symver && !d->script)
+             if (d->literal && !d->symver && !d->script)
                {
                  (*_bfd_error_handler)
                    (_("%s: undefined version: %s"),
@@ -5962,7 +6127,6 @@ bfd_elf_size_dynamic_sections (bfd *output_bfd,
       {
        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)
@@ -6100,20 +6264,22 @@ _bfd_elf_init_2_index_sections (bfd *output_bfd, struct bfd_link_info *info)
 {
   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)
-    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))
       {
-       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)
-    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))
       {
-       elf_hash_table (info)->data_index_section = s;
+       elf_hash_table (info)->text_index_section = s;
        break;
       }
 
@@ -6215,7 +6381,10 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
          elf_link_hash_traverse (elf_hash_table (info),
                                  elf_collect_hash_codes, &hashinf);
          if (hashinf.error)
-           return FALSE;
+           {
+             free (hashcodes);
+             return FALSE;
+           }
 
          nsyms = hashinf.hashcodes - hashcodes;
          bucketcount
@@ -6267,7 +6436,10 @@ bfd_elf_size_dynsym_hash_dynstr (bfd *output_bfd, struct bfd_link_info *info)
          elf_link_hash_traverse (elf_hash_table (info),
                                  elf_collect_gnu_hash_codes, &cinfo);
          if (cinfo.error)
-           return FALSE;
+           {
+             free (cinfo.hashcodes);
+             return FALSE;
+           }
 
          bucketcount
            = compute_bucket_count (info, cinfo.hashcodes, cinfo.nsyms, 1);
@@ -6713,7 +6885,7 @@ bfd_elf_get_bfd_needed_list (bfd *abfd,
 {
   asection *s;
   bfd_byte *dynbuf = NULL;
-  int elfsec;
+  unsigned int elfsec;
   unsigned long shlink;
   bfd_byte *extdyn, *extdynend;
   size_t extdynsize;
@@ -6733,7 +6905,7 @@ bfd_elf_get_bfd_needed_list (bfd *abfd,
     goto error_return;
 
   elfsec = _bfd_elf_section_from_bfd_section (abfd, s);
-  if (elfsec == -1)
+  if (elfsec == SHN_BAD)
     goto error_return;
 
   shlink = elf_elfsections (abfd)[elfsec]->sh_link;
@@ -6834,7 +7006,7 @@ 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;
+  bfd_size_type i, shndx_count, total_size;
 
   indbuf = bfd_malloc2 (symcount, sizeof (*indbuf));
   if (indbuf == NULL)
@@ -6854,15 +7026,16 @@ elf_create_symbuf (bfd_size_type symcount, Elf_Internal_Sym *isymbuf)
       if (ind[0]->st_shndx != ind[1]->st_shndx)
        shndx_count++;
 
-  ssymbuf = bfd_malloc ((shndx_count + 1) * sizeof (*ssymbuf)
-                       + (indbufend - indbuf) * sizeof (*ssymbuf));
+  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);
+  ssym = (struct elf_symbuf_symbol *) (ssymbuf + shndx_count + 1);
   ssymbuf->ssym = NULL;
   ssymbuf->count = shndx_count;
   ssymbuf->st_shndx = 0;
@@ -6880,7 +7053,9 @@ elf_create_symbuf (bfd_size_type symcount, Elf_Internal_Sym *isymbuf)
       ssym->st_other = (*ind)->st_other;
       ssymhead->count++;
     }
-  BFD_ASSERT ((bfd_size_type) (ssymhead - ssymbuf) == shndx_count);
+  BFD_ASSERT ((bfd_size_type) (ssymhead - ssymbuf) == shndx_count
+             && (((bfd_hostptr_t) ssym - (bfd_hostptr_t) ssymbuf)
+                 == total_size));
 
   free (indbuf);
   return ssymbuf;
@@ -6889,7 +7064,7 @@ elf_create_symbuf (bfd_size_type symcount, Elf_Internal_Sym *isymbuf)
 /* Check if 2 sections define the same set of local and global
    symbols.  */
 
-bfd_boolean
+static bfd_boolean
 bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
                                   struct bfd_link_info *info)
 {
@@ -6902,19 +7077,12 @@ bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
   Elf_Internal_Sym *isym, *isymend;
   struct elf_symbol *symtable1 = NULL, *symtable2 = NULL;
   bfd_size_type count1, count2, i;
-  int shndx1, shndx2;
+  unsigned int shndx1, shndx2;
   bfd_boolean result;
 
   bfd1 = sec1->owner;
   bfd2 = sec2->owner;
 
-  /* If both are .gnu.linkonce sections, they have to have the same
-     section name.  */
-  if (CONST_STRNEQ (sec1->name, ".gnu.linkonce")
-      && CONST_STRNEQ (sec2->name, ".gnu.linkonce"))
-    return strcmp (sec1->name + sizeof ".gnu.linkonce",
-                  sec2->name + sizeof ".gnu.linkonce") == 0;
-
   /* Both sections have to be in ELF.  */
   if (bfd_get_flavour (bfd1) != bfd_target_elf_flavour
       || bfd_get_flavour (bfd2) != bfd_target_elf_flavour)
@@ -6923,18 +7091,9 @@ bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
   if (elf_section_type (sec1) != elf_section_type (sec2))
     return FALSE;
 
-  if ((elf_section_flags (sec1) & SHF_GROUP) != 0
-      && (elf_section_flags (sec2) & SHF_GROUP) != 0)
-    {
-      /* If both are members of section groups, they have to have the
-        same group name.  */
-      if (strcmp (elf_group_name (sec1), elf_group_name (sec2)) != 0)
-       return FALSE;
-    }
-
   shndx1 = _bfd_elf_section_from_bfd_section (bfd1, sec1);
   shndx2 = _bfd_elf_section_from_bfd_section (bfd2, sec2);
-  if (shndx1 == -1 || shndx2 == -1)
+  if (shndx1 == SHN_BAD || shndx2 == SHN_BAD)
     return FALSE;
 
   bed1 = get_elf_backend_data (bfd1);
@@ -6991,9 +7150,9 @@ bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
       while (lo < hi)
        {
          mid = (lo + hi) / 2;
-         if ((unsigned int) shndx1 < ssymbuf1[mid].st_shndx)
+         if (shndx1 < ssymbuf1[mid].st_shndx)
            hi = mid;
-         else if ((unsigned int) shndx1 > ssymbuf1[mid].st_shndx)
+         else if (shndx1 > ssymbuf1[mid].st_shndx)
            lo = mid + 1;
          else
            {
@@ -7010,9 +7169,9 @@ bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
       while (lo < hi)
        {
          mid = (lo + hi) / 2;
-         if ((unsigned int) shndx2 < ssymbuf2[mid].st_shndx)
+         if (shndx2 < ssymbuf2[mid].st_shndx)
            hi = mid;
-         else if ((unsigned int) shndx2 > ssymbuf2[mid].st_shndx)
+         else if (shndx2 > ssymbuf2[mid].st_shndx)
            lo = mid + 1;
          else
            {
@@ -7075,12 +7234,12 @@ bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
   /* Count definitions in the section.  */
   count1 = 0;
   for (isym = isymbuf1, isymend = isym + symcount1; isym < isymend; isym++)
-    if (isym->st_shndx == (unsigned int) shndx1)
+    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 == (unsigned int) shndx2)
+    if (isym->st_shndx == shndx2)
       symtable2[count2++].u.isym = isym;
 
   if (count1 == 0 || count2 == 0 || count1 != count2)
@@ -7203,7 +7362,7 @@ struct elf_outext_info
 
    Complex relocations are generalized, self-describing relocations.  The
    implementation of them consists of two parts: complex symbols, and the
-   relocations themselves. 
+   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
@@ -7229,11 +7388,11 @@ struct elf_outext_info
    <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)
+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;
@@ -7269,20 +7428,20 @@ set_symbol_value (bfd *                         bfd_with_globals,
   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;
-  
+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)
@@ -7314,35 +7473,36 @@ resolve_symbol (const char *                  name,
     }
 
   /* Hmm, haven't found it yet. perhaps it is a global.  */
-  global_entry = bfd_link_hash_lookup (finfo->info->hash, name, FALSE, FALSE, TRUE);
+  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;
+      *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)
+resolve_section (const char *name,
+                asection *sections,
+                bfd_vma *result)
 {
-  asection *    curr;
-  unsigned int  len;
+  asection *curr;
+  unsigned int len;
 
-  for (curr = sections; curr; curr = curr->next)    
+  for (curr = sections; curr; curr = curr->next)
     if (strcmp (curr->name, name) == 0)
       {
        *result = curr->vma;
@@ -7350,10 +7510,10 @@ resolve_section (const char *  name,
       }
 
   /* Hmm. still haven't found it. try pseudo-section names.  */
-  for (curr = sections; curr; curr = curr->next)    
+  for (curr = sections; curr; curr = curr->next)
     {
       len = strlen (curr->name);
-      if (len > strlen (name)) 
+      if (len > strlen (name))
        continue;
 
       if (strncmp (curr->name, name, len) == 0)
@@ -7367,46 +7527,45 @@ resolve_section (const char *  name,
          /* Insert more pseudo-section names here, if you like.  */
        }
     }
-  
+
   return FALSE;
 }
 
 static void
-undefined_reference (const char *  reftype,
-                    const char *  name)
+undefined_reference (const char *reftype, const char *name)
 {
-  _bfd_error_handler (_("undefined %s reference in complex symbol: %s"), reftype, 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)
-{
-  int           len;
-  int           symlen;
-  bfd_vma       a;
-  bfd_vma       b;
-  const int     bufsz = 4096;
-  char          symbuf [bufsz];
+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;
+  const char *symend;
+  bfd_boolean symbol_is_section = FALSE;
 
   len = strlen (sym);
   symend = sym + len;
 
-  if (len < 1 || len > bufsz)
+  if (len < 1 || len > sizeof (symbuf))
     {
       bfd_set_error (bfd_error_invalid_operation);
       return FALSE;
     }
-  
+
   switch (* sym)
     {
     case '.':
@@ -7421,27 +7580,27 @@ eval_symbol (bfd_vma *                     result,
 
     case 'S':
       symbol_is_section = TRUE;
-    case 's':      
+    case 's':
       ++sym;
       symlen = strtol (sym, (char **) symp, 10);
       sym = *symp + 1; /* Skip the trailing ':'.  */
 
-      if ((symend < sym) || ((symlen + 1) > bufsz))
+      if (symend < sym || symlen + 1 > sizeof (symbuf))
        {
          bfd_set_error (bfd_error_invalid_operation);
          return FALSE;
        }
 
       memcpy (symbuf, sym, symlen);
-      symbuf [symlen] = '\0';
+      symbuf[symlen] = '\0';
       *symp = sym + symlen;
-      
-      /* Is it always possible, with complex symbols, that gas "mis-guessed" 
+
+      /* Is it always possible, with complex symbols, that gas "mis-guessed"
         the symbol as a section, or vice-versa. so we're pretty liberal in our
         interpretation here; section means "try section first", not "must be a
         section", and likewise with symbol.  */
 
-      if (symbol_is_section) 
+      if (symbol_is_section)
        {
          if (!resolve_section (symbuf, finfo->output_bfd->sections, result)
              && !resolve_symbol (symbuf, input_bfd, finfo, result,
@@ -7450,8 +7609,8 @@ eval_symbol (bfd_vma *                     result,
              undefined_reference ("section", symbuf);
              return FALSE;
            }
-       } 
-      else 
+       }
+      else
        {
          if (!resolve_symbol (symbuf, input_bfd, finfo, result,
                               isymbuf, locsymcount)
@@ -7464,23 +7623,23 @@ eval_symbol (bfd_vma *                     result,
        }
 
       return TRUE;
-      
+
       /* All that remains are operators.  */
 
 #define UNARY_OP(op)                                           \
   if (strncmp (sym, #op, strlen (#op)) == 0)                   \
     {                                                          \
       sym += strlen (#op);                                     \
-      if (* sym == ':')                                                \
-        ++ sym;                                                        \
+      if (*sym == ':')                                         \
+       ++sym;                                                  \
       *symp = sym;                                             \
       if (!eval_symbol (&a, symp, input_bfd, finfo, dot,       \
                        isymbuf, locsymcount, signed_p))        \
-        return FALSE;                                          \
-      if (signed_p)                                             \
+       return FALSE;                                           \
+      if (signed_p)                                            \
        *result = op ((bfd_signed_vma) a);                      \
-      else                                                      \
-        * result = op a;                                        \
+      else                                                     \
+       *result = op a;                                         \
       return TRUE;                                             \
     }
 
@@ -7488,20 +7647,20 @@ eval_symbol (bfd_vma *                     result,
   if (strncmp (sym, #op, strlen (#op)) == 0)                   \
     {                                                          \
       sym += strlen (#op);                                     \
-      if (* sym == ':')                                                \
-        ++ sym;                                                        \
+      if (*sym == ':')                                         \
+       ++sym;                                                  \
       *symp = sym;                                             \
       if (!eval_symbol (&a, symp, input_bfd, finfo, dot,       \
                        isymbuf, locsymcount, signed_p))        \
-        return FALSE;                                          \
+       return FALSE;                                           \
       ++*symp;                                                 \
       if (!eval_symbol (&b, symp, input_bfd, finfo, dot,       \
                        isymbuf, locsymcount, signed_p))        \
-        return FALSE;                                          \
-      if (signed_p)                                             \
+       return FALSE;                                           \
+      if (signed_p)                                            \
        *result = ((bfd_signed_vma) a) op ((bfd_signed_vma) b); \
-      else                                                      \
-        * result = a op b;                                      \
+      else                                                     \
+       *result = a op b;                                       \
       return TRUE;                                             \
     }
 
@@ -7536,15 +7695,15 @@ eval_symbol (bfd_vma *                     result,
 }
 
 static void
-put_value (bfd_vma        size,
-          unsigned long  chunksz,
-          bfd *          input_bfd,
-          bfd_vma        x,
-          bfd_byte *     location)
+put_value (bfd_vma size,
+          unsigned long chunksz,
+          bfd *input_bfd,
+          bfd_vma x,
+          bfd_byte *location)
 {
   location += (size - chunksz);
 
-  for (; size; size -= chunksz, location -= chunksz, x >>= (chunksz * 8)) 
+  for (; size; size -= chunksz, location -= chunksz, x >>= (chunksz * 8))
     {
       switch (chunksz)
        {
@@ -7571,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;
 
-  for (; size; size -= chunksz, location += chunksz) 
+  for (; size; size -= chunksz, location += chunksz)
     {
       switch (chunksz)
        {
@@ -7607,17 +7766,16 @@ get_value (bfd_vma        size,
   return x;
 }
 
-static void 
-decode_complex_addend
-    (unsigned long * start,   /* in bits */
-     unsigned long * oplen,   /* in bits */
-     unsigned long * len,     /* in bits */
-     unsigned long * wordsz,  /* in bytes */
-     unsigned long * chunksz,  /* in bytes */
-     unsigned long * lsb0_p,
-     unsigned long * signed_p,
-     unsigned long * trunc_p,
-     unsigned long encoded)
+static void
+decode_complex_addend (unsigned long *start,   /* in bits */
+                      unsigned long *oplen,   /* in bits */
+                      unsigned long *len,     /* in bits */
+                      unsigned long *wordsz,  /* in bytes */
+                      unsigned long *chunksz, /* in bytes */
+                      unsigned long *lsb0_p,
+                      unsigned long *signed_p,
+                      unsigned long *trunc_p,
+                      unsigned long encoded)
 {
   * start     =  encoded        & 0x3F;
   * len       = (encoded >>  6) & 0x3F;
@@ -7629,25 +7787,26 @@ decode_complex_addend
   * trunc_p   = (encoded >> 29) & 1;
 }
 
-void
+bfd_reloc_status_type
 bfd_elf_perform_complex_relocation (bfd *input_bfd,
-                                   asection *input_section,
+                                   asection *input_section ATTRIBUTE_UNUSED,
                                    bfd_byte *contents,
                                    Elf_Internal_Rela *rel,
                                    bfd_vma relocation)
 {
   bfd_vma shift, x, mask;
   unsigned long start, oplen, len, wordsz, chunksz, lsb0_p, signed_p, trunc_p;
+  bfd_reloc_status_type r;
 
   /*  Perform this reloc, since it is complex.
       (this is not to say that it necessarily refers to a complex
       symbol; merely that it is a self-describing CGEN based reloc.
       i.e. the addend has the complete reloc information (bit start, end,
-      word size, etc) encoded within it.).  */ 
+      word size, etc) encoded within it.).  */
 
-  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;
 
@@ -7656,7 +7815,7 @@ bfd_elf_perform_complex_relocation (bfd *input_bfd,
   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: "
@@ -7667,21 +7826,15 @@ bfd_elf_perform_complex_relocation (bfd *input_bfd,
          oplen, x, mask,  relocation);
 #endif
 
+  r = bfd_reloc_ok;
   if (! trunc_p)
-    {
-      /* Now do an overflow check.  */
-      if (bfd_check_overflow ((signed_p ? 
-                              complain_overflow_signed : 
-                              complain_overflow_unsigned),
-                             len, 0, (8 * wordsz), 
-                             relocation) == bfd_reloc_overflow)
-       (*_bfd_error_handler) 
-         ("%s (%s + 0x%lx): relocation overflow: 0x%lx %sdoes not fit "
-          "within 0x%lx", 
-          input_bfd->filename, input_section->name, rel->r_offset,
-          relocation, (signed_p ? "(signed) " : ""), mask);
-    }
-         
+    /* Now do an overflow check.  */
+    r = bfd_check_overflow ((signed_p
+                            ? complain_overflow_signed
+                            : complain_overflow_unsigned),
+                           len, 0, (8 * wordsz),
+                           relocation);
+
   /* Do the deed.  */
   x = (x & ~(mask << shift)) | ((relocation & mask) << shift);
 
@@ -7690,10 +7843,11 @@ bfd_elf_perform_complex_relocation (bfd *input_bfd,
          "         shifted mask: %8.8lx\n"
          " shifted/masked reloc: %8.8lx\n"
          "               result: %8.8lx\n",
-         relocation, (mask << shift), 
+         relocation, (mask << shift),
          ((relocation & mask) << shift), x);
 #endif
   put_value (wordsz, chunksz, input_bfd, x, contents + rel->r_offset);
+  return r;
 }
 
 /* When performing a relocatable link, the input relocations are
@@ -8165,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);
-         finfo->symshndxbuf = destshndx = bfd_realloc (destshndx, amt * 2);
+         destshndx = bfd_realloc (destshndx, amt * 2);
          if (destshndx == NULL)
            return FALSE;
+         finfo->symshndxbuf = destshndx;
          memset ((char *) destshndx + amt, 0, amt);
          finfo->shndxbuf_size *= 2;
        }
@@ -8186,13 +8341,14 @@ elf_link_output_sym (struct elf_final_link_info *finfo,
 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
-         beyond 64k.  */
+        beyond 64k.  */
       (*_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;
     }
@@ -8523,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)
                  {
-                   /* 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);
+                     }
                  }
              }
          }
@@ -8596,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));
     }
 
+  /* 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
@@ -8787,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 != 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;
     }
@@ -8877,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;
-      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
        {
-         /* 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;
@@ -8931,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
-         && (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.  */
@@ -9000,6 +9169,63 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
          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;
@@ -9122,7 +9348,8 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
                                               sym, *ps);
                }
 
-             if (s_type == STT_RELC || s_type == STT_SRELC)
+             if ((s_type == STT_RELC || s_type == STT_SRELC)
+                 && !finfo->info->relocatable)
                {
                  bfd_vma val;
                  bfd_vma dot = (rel->r_offset
@@ -9491,6 +9718,7 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
        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,
@@ -9757,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_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;
@@ -9814,6 +10043,7 @@ elf_fixup_link_order (bfd *abfd, asection *o)
       offset += sections[n]->size;
     }
 
+  free (sections);
   return TRUE;
 }
 
@@ -9917,7 +10147,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
                 elf_link_input_bfd ignores this section.  */
              input_section->flags &= ~SEC_HAS_CONTENTS;
            }
-           
+
          attr_size = bfd_elf_obj_attr_size (abfd);
          if (attr_size)
            {
@@ -9973,22 +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)
-               {
-                 Elf_Internal_Rela * relocs;
-
-                 relocs = _bfd_elf_link_read_relocs (sec->owner, sec,
-                                                     NULL, NULL,
-                                                     info->keep_memory);
-
-                 if (relocs != NULL)
-                   {
-                     reloc_count
-                       = (*bed->elf_backend_count_relocs) (sec, relocs);
-
-                     if (elf_section_data (sec)->relocs != relocs)
-                       free (relocs);
-                   }
-               }
+               reloc_count = (*bed->elf_backend_count_relocs) (info, sec);
 
              if (sec->rawsize > max_contents_size)
                max_contents_size = sec->rawsize;
@@ -10046,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_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)
@@ -10145,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.  */
-  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);
@@ -10165,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;
-  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;
@@ -10215,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 (i == SHN_LORESERVE - 1)
-           i += SHN_HIRESERVE + 1 - SHN_LORESERVE;
        }
     }
 
@@ -10463,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;
 
-             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))
@@ -10639,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))
                  {
-                   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)
-                     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.  */
-                       dyn.d_un.d_val = 0;
+                       dyn.d_un.d_ptr = 0;
                      }
                    break;
                  }
@@ -10727,6 +10942,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
              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;
@@ -10739,9 +10955,9 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
                        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;
                        }
                    }
                }
@@ -10776,7 +10992,7 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
 
              if (dyn.d_tag == DT_TEXTREL)
                {
-                info->callbacks->einfo 
+                info->callbacks->einfo
                    (_("%P: warning: creating a DT_TEXTREL in a shared object.\n"));
                  break;
                }
@@ -10920,6 +11136,139 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info)
   return FALSE;
 }
 \f
+/* Initialize COOKIE for input bfd ABFD.  */
+
+static bfd_boolean
+init_reloc_cookie (struct elf_reloc_cookie *cookie,
+                  struct bfd_link_info *info, bfd *abfd)
+{
+  Elf_Internal_Shdr *symtab_hdr;
+  const struct elf_backend_data *bed;
+
+  bed = get_elf_backend_data (abfd);
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+
+  cookie->abfd = abfd;
+  cookie->sym_hashes = elf_sym_hashes (abfd);
+  cookie->bad_symtab = elf_bad_symtab (abfd);
+  if (cookie->bad_symtab)
+    {
+      cookie->locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym;
+      cookie->extsymoff = 0;
+    }
+  else
+    {
+      cookie->locsymcount = symtab_hdr->sh_info;
+      cookie->extsymoff = symtab_hdr->sh_info;
+    }
+
+  if (bed->s->arch_size == 32)
+    cookie->r_sym_shift = 8;
+  else
+    cookie->r_sym_shift = 32;
+
+  cookie->locsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
+  if (cookie->locsyms == NULL && cookie->locsymcount != 0)
+    {
+      cookie->locsyms = bfd_elf_get_elf_syms (abfd, symtab_hdr,
+                                             cookie->locsymcount, 0,
+                                             NULL, NULL, NULL);
+      if (cookie->locsyms == NULL)
+       {
+         info->callbacks->einfo (_("%P%X: can not read symbols: %E\n"));
+         return FALSE;
+       }
+      if (info->keep_memory)
+       symtab_hdr->contents = (bfd_byte *) cookie->locsyms;
+    }
+  return TRUE;
+}
+
+/* Free the memory allocated by init_reloc_cookie, if appropriate.  */
+
+static void
+fini_reloc_cookie (struct elf_reloc_cookie *cookie, bfd *abfd)
+{
+  Elf_Internal_Shdr *symtab_hdr;
+
+  symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+  if (cookie->locsyms != NULL
+      && symtab_hdr->contents != (unsigned char *) cookie->locsyms)
+    free (cookie->locsyms);
+}
+
+/* Initialize the relocation information in COOKIE for input section SEC
+   of input bfd ABFD.  */
+
+static bfd_boolean
+init_reloc_cookie_rels (struct elf_reloc_cookie *cookie,
+                       struct bfd_link_info *info, bfd *abfd,
+                       asection *sec)
+{
+  const struct elf_backend_data *bed;
+
+  if (sec->reloc_count == 0)
+    {
+      cookie->rels = NULL;
+      cookie->relend = NULL;
+    }
+  else
+    {
+      bed = get_elf_backend_data (abfd);
+
+      cookie->rels = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
+                                               info->keep_memory);
+      if (cookie->rels == NULL)
+       return FALSE;
+      cookie->rel = cookie->rels;
+      cookie->relend = (cookie->rels
+                       + sec->reloc_count * bed->s->int_rels_per_ext_rel);
+    }
+  cookie->rel = cookie->rels;
+  return TRUE;
+}
+
+/* Free the memory allocated by init_reloc_cookie_rels,
+   if appropriate.  */
+
+static void
+fini_reloc_cookie_rels (struct elf_reloc_cookie *cookie,
+                       asection *sec)
+{
+  if (cookie->rels && elf_section_data (sec)->relocs != cookie->rels)
+    free (cookie->rels);
+}
+
+/* Initialize the whole of COOKIE for input section SEC.  */
+
+static bfd_boolean
+init_reloc_cookie_for_section (struct elf_reloc_cookie *cookie,
+                              struct bfd_link_info *info,
+                              asection *sec)
+{
+  if (!init_reloc_cookie (cookie, info, sec->owner))
+    goto error1;
+  if (!init_reloc_cookie_rels (cookie, info, sec->owner, sec))
+    goto error2;
+  return TRUE;
+
+ error2:
+  fini_reloc_cookie (cookie, sec->owner);
+ error1:
+  return FALSE;
+}
+
+/* Free the memory allocated by init_reloc_cookie_for_section,
+   if appropriate.  */
+
+static void
+fini_reloc_cookie_for_section (struct elf_reloc_cookie *cookie,
+                              asection *sec)
+{
+  fini_reloc_cookie_rels (cookie, sec);
+  fini_reloc_cookie (cookie, sec->owner);
+}
+\f
 /* Garbage collect unused sections.  */
 
 /* Default gc_mark_hook.  */
@@ -10952,6 +11301,59 @@ _bfd_elf_gc_mark_hook (asection *sec,
   return NULL;
 }
 
+/* COOKIE->rel describes a relocation against section SEC, which is
+   a section we've decided to keep.  Return the section that contains
+   the relocation symbol, or NULL if no section contains it.  */
+
+asection *
+_bfd_elf_gc_mark_rsec (struct bfd_link_info *info, asection *sec,
+                      elf_gc_mark_hook_fn gc_mark_hook,
+                      struct elf_reloc_cookie *cookie)
+{
+  unsigned long r_symndx;
+  struct elf_link_hash_entry *h;
+
+  r_symndx = cookie->rel->r_info >> cookie->r_sym_shift;
+  if (r_symndx == 0)
+    return NULL;
+
+  if (r_symndx >= cookie->locsymcount
+      || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL)
+    {
+      h = cookie->sym_hashes[r_symndx - cookie->extsymoff];
+      while (h->root.type == bfd_link_hash_indirect
+            || h->root.type == bfd_link_hash_warning)
+       h = (struct elf_link_hash_entry *) h->root.u.i.link;
+      return (*gc_mark_hook) (sec, info, cookie->rel, h, NULL);
+    }
+
+  return (*gc_mark_hook) (sec, info, cookie->rel, NULL,
+                         &cookie->locsyms[r_symndx]);
+}
+
+/* COOKIE->rel describes a relocation against section SEC, which is
+   a section we've decided to keep.  Mark the section that contains
+   the relocation symbol.  */
+
+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.  */
@@ -10962,8 +11364,7 @@ _bfd_elf_gc_mark (struct bfd_link_info *info,
                  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;
 
@@ -10975,103 +11376,39 @@ _bfd_elf_gc_mark (struct bfd_link_info *info,
 
   /* 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
-       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);
        }
     }
 
@@ -11128,10 +11465,21 @@ elf_gc_sweep (bfd *abfd, struct bfd_link_info *info)
 
       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;
@@ -11321,6 +11669,29 @@ bfd_elf_gc_mark_dynamic_ref_symbol (struct elf_link_hash_entry *h, void *inf)
   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
@@ -11332,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
-      || info->relocatable
-      || info->emitrelocations
       || !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,
@@ -11377,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)
-    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);
@@ -11582,7 +11910,7 @@ bfd_elf_gc_record_vtentry (bfd *abfd ATTRIBUTE_UNUSED,
 
 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
@@ -11592,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;
+  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;
@@ -11599,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;
-      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;
@@ -11617,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;
-  unsigned int got_elt_size = bed->s->arch_size / 8;
   struct alloc_got_off_arg gofarg;
 
+  BFD_ASSERT (abfd == info->output_bfd);
+
   if (! is_elf_hash_table (info->hash))
     return FALSE;
 
@@ -11655,7 +11986,7 @@ bfd_elf_gc_common_finalize_got_offsets (bfd *abfd,
          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;
@@ -11665,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;
-  gofarg.got_elt_size = got_elt_size;
+  gofarg.info = info;
   elf_link_hash_traverse (elf_hash_table (info),
                          elf_gc_allocate_got_offsets,
                          &gofarg);
@@ -11735,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];
-         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;
     }
@@ -11757,16 +12085,15 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
 {
   struct elf_reloc_cookie cookie;
   asection *stab, *eh;
-  Elf_Internal_Shdr *symtab_hdr;
   const struct elf_backend_data *bed;
   bfd *abfd;
-  unsigned int count;
   bfd_boolean ret = FALSE;
 
   if (info->traditional_format
       || !is_elf_hash_table (info->hash))
     return FALSE;
 
+  _bfd_elf_begin_eh_frame_parsing (info);
   for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
     {
       if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
@@ -11799,96 +12126,39 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
          && bed->elf_backend_discard_info == NULL)
        continue;
 
-      symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
-      cookie.abfd = abfd;
-      cookie.sym_hashes = elf_sym_hashes (abfd);
-      cookie.bad_symtab = elf_bad_symtab (abfd);
-      if (cookie.bad_symtab)
-       {
-         cookie.locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym;
-         cookie.extsymoff = 0;
-       }
-      else
-       {
-         cookie.locsymcount = symtab_hdr->sh_info;
-         cookie.extsymoff = symtab_hdr->sh_info;
-       }
-
-      if (bed->s->arch_size == 32)
-       cookie.r_sym_shift = 8;
-      else
-       cookie.r_sym_shift = 32;
-
-      cookie.locsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
-      if (cookie.locsyms == NULL && cookie.locsymcount != 0)
-       {
-         cookie.locsyms = bfd_elf_get_elf_syms (abfd, symtab_hdr,
-                                                cookie.locsymcount, 0,
-                                                NULL, NULL, NULL);
-         if (cookie.locsyms == NULL)
-           {
-             info->callbacks->einfo (_("%P%X: can not read symbols: %E\n"));
-             return FALSE;
-           }
-       }
+      if (!init_reloc_cookie (&cookie, info, abfd))
+       return FALSE;
 
-      if (stab != NULL)
+      if (stab != NULL
+         && stab->reloc_count > 0
+         && init_reloc_cookie_rels (&cookie, info, abfd, stab))
        {
-         cookie.rels = NULL;
-         count = stab->reloc_count;
-         if (count != 0)
-           cookie.rels = _bfd_elf_link_read_relocs (abfd, stab, NULL, NULL,
-                                                    info->keep_memory);
-         if (cookie.rels != NULL)
-           {
-             cookie.rel = cookie.rels;
-             cookie.relend = cookie.rels;
-             cookie.relend += count * bed->s->int_rels_per_ext_rel;
-             if (_bfd_discard_section_stabs (abfd, stab,
-                                             elf_section_data (stab)->sec_info,
-                                             bfd_elf_reloc_symbol_deleted_p,
-                                             &cookie))
-               ret = TRUE;
-             if (elf_section_data (stab)->relocs != cookie.rels)
-               free (cookie.rels);
-           }
+         if (_bfd_discard_section_stabs (abfd, stab,
+                                         elf_section_data (stab)->sec_info,
+                                         bfd_elf_reloc_symbol_deleted_p,
+                                         &cookie))
+           ret = TRUE;
+         fini_reloc_cookie_rels (&cookie, stab);
        }
 
-      if (eh != NULL)
+      if (eh != NULL
+         && init_reloc_cookie_rels (&cookie, info, abfd, eh))
        {
-         cookie.rels = NULL;
-         count = eh->reloc_count;
-         if (count != 0)
-           cookie.rels = _bfd_elf_link_read_relocs (abfd, eh, NULL, NULL,
-                                                    info->keep_memory);
-         cookie.rel = cookie.rels;
-         cookie.relend = cookie.rels;
-         if (cookie.rels != NULL)
-           cookie.relend += count * bed->s->int_rels_per_ext_rel;
-
+         _bfd_elf_parse_eh_frame (abfd, info, eh, &cookie);
          if (_bfd_elf_discard_section_eh_frame (abfd, info, eh,
                                                 bfd_elf_reloc_symbol_deleted_p,
                                                 &cookie))
            ret = TRUE;
-
-         if (cookie.rels != NULL
-             && elf_section_data (eh)->relocs != cookie.rels)
-           free (cookie.rels);
+         fini_reloc_cookie_rels (&cookie, eh);
        }
 
       if (bed->elf_backend_discard_info != NULL
          && (*bed->elf_backend_discard_info) (abfd, &cookie, info))
        ret = TRUE;
 
-      if (cookie.locsyms != NULL
-         && symtab_hdr->contents != (unsigned char *) cookie.locsyms)
-       {
-         if (! info->keep_memory)
-           free (cookie.locsyms);
-         else
-           symtab_hdr->contents = (unsigned char *) cookie.locsyms;
-       }
+      fini_reloc_cookie (&cookie, abfd);
     }
+  _bfd_elf_end_eh_frame_parsing (info);
 
   if (info->eh_frame_hdr
       && !info->relocatable
@@ -11898,8 +12168,21 @@ bfd_elf_discard_info (bfd *output_bfd, struct bfd_link_info *info)
   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
-_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;
@@ -11939,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.  */
 
-  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)
@@ -11954,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)
-         && 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
@@ -12077,9 +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.  */
   if (! bfd_section_already_linked_table_insert (already_linked_list, sec))
-    info->callbacks->einfo (_("%F%P: already_linked_table: %E"));
+    info->callbacks->einfo (_("%F%P: already_linked_table: %E\n"));
 }
 
 bfd_boolean
@@ -12099,3 +12404,206 @@ _bfd_elf_common_section (asection *sec ATTRIBUTE_UNUSED)
 {
   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.065574 seconds and 4 git commands to generate.