* elf-bfd.h (elf_backend_emit_relocs): Change prototype to return
[deliverable/binutils-gdb.git] / bfd / elflink.h
index 5ba86e77e5ad4f9d9b535951b1e0acd691fe8fb2..4c5661b4505fca05dd39eb1d57ac56997ab534d3 100644 (file)
@@ -1,5 +1,5 @@
 /* ELF linker support.
-   Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001
+   Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
    Free Software Foundation, Inc.
 
 This file is part of BFD, the Binary File Descriptor library.
@@ -27,39 +27,59 @@ struct elf_info_failed
 {
   boolean failed;
   struct bfd_link_info *info;
+  struct bfd_elf_version_tree *verdefs;
 };
 
+static boolean is_global_data_symbol_definition
+  PARAMS ((bfd *, Elf_Internal_Sym *));
+static boolean elf_link_is_defined_archive_symbol
+  PARAMS ((bfd *, carsym *));
 static boolean elf_link_add_object_symbols
   PARAMS ((bfd *, struct bfd_link_info *));
 static boolean elf_link_add_archive_symbols
   PARAMS ((bfd *, struct bfd_link_info *));
 static boolean elf_merge_symbol
-  PARAMS ((bfd *, struct bfd_link_info *, const char *, Elf_Internal_Sym *,
-          asection **, bfd_vma *, struct elf_link_hash_entry **,
-          boolean *, boolean *, boolean *, boolean));
+  PARAMS ((bfd *, struct bfd_link_info *, const char *,
+          Elf_Internal_Sym *, asection **, bfd_vma *,
+          struct elf_link_hash_entry **, boolean *, boolean *,
+          boolean *, boolean));
+static boolean elf_add_default_symbol
+  PARAMS ((bfd *, struct bfd_link_info *, struct elf_link_hash_entry *,
+          const char *, Elf_Internal_Sym *, asection **, bfd_vma *,
+          boolean *, boolean, boolean));
 static boolean elf_export_symbol
   PARAMS ((struct elf_link_hash_entry *, PTR));
+static boolean elf_finalize_dynstr
+  PARAMS ((bfd *, struct bfd_link_info *));
 static boolean elf_fix_symbol_flags
   PARAMS ((struct elf_link_hash_entry *, struct elf_info_failed *));
 static boolean elf_adjust_dynamic_symbol
   PARAMS ((struct elf_link_hash_entry *, PTR));
 static boolean elf_link_find_version_dependencies
   PARAMS ((struct elf_link_hash_entry *, PTR));
-static boolean elf_link_find_version_dependencies
-  PARAMS ((struct elf_link_hash_entry *, PTR));
 static boolean elf_link_assign_sym_version
   PARAMS ((struct elf_link_hash_entry *, PTR));
 static boolean elf_collect_hash_codes
   PARAMS ((struct elf_link_hash_entry *, PTR));
 static boolean elf_link_read_relocs_from_section
   PARAMS ((bfd *, Elf_Internal_Shdr *, PTR, Elf_Internal_Rela *));
-static void elf_link_output_relocs
+static size_t compute_bucket_count
+  PARAMS ((struct bfd_link_info *));
+static boolean elf_link_output_relocs
   PARAMS ((bfd *, asection *, Elf_Internal_Shdr *, Elf_Internal_Rela *));
 static boolean elf_link_size_reloc_section
   PARAMS ((bfd *, Elf_Internal_Shdr *, asection *));
 static void elf_link_adjust_relocs
   PARAMS ((bfd *, Elf_Internal_Shdr *, unsigned int,
           struct elf_link_hash_entry **));
+static int elf_link_sort_cmp1
+  PARAMS ((const void *, const void *));
+static int elf_link_sort_cmp2
+  PARAMS ((const void *, const void *));
+static size_t elf_link_sort_relocs
+  PARAMS ((bfd *, struct bfd_link_info *, asection **));
+static boolean elf_section_ignore_discarded_relocs
+  PARAMS ((asection *));
 
 /* Given an ELF BFD, add symbols to the global hash table as
    appropriate.  */
@@ -121,7 +141,7 @@ is_global_data_symbol_definition (abfd, sym)
 }
 
 /* Search the symbol table of the archive element of the archive ABFD
-   whoes archive map contains a mention of SYMDEF, and determine if
+   whose archive map contains a mention of SYMDEF, and determine if
    the symbol is defined in this element.  */
 static boolean
 elf_link_is_defined_archive_symbol (abfd, symdef)
@@ -129,13 +149,18 @@ elf_link_is_defined_archive_symbol (abfd, symdef)
      carsym * symdef;
 {
   Elf_Internal_Shdr * hdr;
+  Elf_Internal_Shdr * shndx_hdr;
   Elf_External_Sym *  esym;
   Elf_External_Sym *  esymend;
   Elf_External_Sym *  buf = NULL;
-  size_t symcount;
-  size_t extsymcount;
-  size_t extsymoff;
+  Elf_External_Sym_Shndx * shndx_buf = NULL;
+  Elf_External_Sym_Shndx * shndx;
+  bfd_size_type symcount;
+  bfd_size_type extsymcount;
+  bfd_size_type extsymoff;
   boolean result = false;
+  file_ptr pos;
+  bfd_size_type amt;
 
   abfd = _bfd_get_elt_at_filepos (abfd, symdef->file_offset);
   if (abfd == (bfd *) NULL)
@@ -153,9 +178,15 @@ elf_link_is_defined_archive_symbol (abfd, symdef)
 
   /* Select the appropriate symbol table.  */
   if ((abfd->flags & DYNAMIC) == 0 || elf_dynsymtab (abfd) == 0)
-    hdr = &elf_tdata (abfd)->symtab_hdr;
+    {
+      hdr = &elf_tdata (abfd)->symtab_hdr;
+      shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
+    }
   else
-    hdr = &elf_tdata (abfd)->dynsymtab_hdr;
+    {
+      hdr = &elf_tdata (abfd)->dynsymtab_hdr;
+      shndx_hdr = NULL;
+    }
 
   symcount = hdr->sh_size / sizeof (Elf_External_Sym);
 
@@ -172,33 +203,41 @@ elf_link_is_defined_archive_symbol (abfd, symdef)
       extsymoff = hdr->sh_info;
     }
 
-  buf = ((Elf_External_Sym *)
-        bfd_malloc (extsymcount * sizeof (Elf_External_Sym)));
+  amt = extsymcount * sizeof (Elf_External_Sym);
+  buf = (Elf_External_Sym *) bfd_malloc (amt);
   if (buf == NULL && extsymcount != 0)
     return false;
 
   /* Read in the symbol table.
      FIXME:  This ought to be cached somewhere.  */
-  if (bfd_seek (abfd,
-               hdr->sh_offset + extsymoff * sizeof (Elf_External_Sym),
-               SEEK_SET) != 0
-      || (bfd_read ((PTR) buf, sizeof (Elf_External_Sym), extsymcount, abfd)
-         != extsymcount * sizeof (Elf_External_Sym)))
+  pos = hdr->sh_offset + extsymoff * sizeof (Elf_External_Sym);
+  if (bfd_seek (abfd, pos, SEEK_SET) != 0
+      || bfd_bread ((PTR) buf, amt, abfd) != amt)
+    goto error_exit;
+
+  if (shndx_hdr != NULL && shndx_hdr->sh_size != 0)
     {
-      free (buf);
-      return false;
+      amt = extsymcount * sizeof (Elf_External_Sym_Shndx);
+      shndx_buf = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
+      if (shndx_buf == NULL && extsymcount != 0)
+       goto error_exit;
+
+      pos = shndx_hdr->sh_offset + extsymoff * sizeof (Elf_External_Sym_Shndx);
+      if (bfd_seek (abfd, pos, SEEK_SET) != 0
+         || bfd_bread ((PTR) shndx_buf, amt, abfd) != amt)
+       goto error_exit;
     }
 
   /* Scan the symbol table looking for SYMDEF.  */
   esymend = buf + extsymcount;
-  for (esym = buf;
+  for (esym = buf, shndx = shndx_buf;
        esym < esymend;
-       esym++)
+       esym++, shndx = (shndx != NULL ? shndx + 1 : NULL))
     {
       Elf_Internal_Sym sym;
       const char * name;
 
-      elf_swap_symbol_in (abfd, esym, sym);
+      elf_swap_symbol_in (abfd, esym, shndx, &sym);
 
       name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link, sym.st_name);
       if (name == (const char *) NULL)
@@ -211,7 +250,11 @@ elf_link_is_defined_archive_symbol (abfd, symdef)
        }
     }
 
-  free (buf);
+ error_exit:
+  if (shndx_buf != NULL)
+    free (shndx_buf);
+  if (buf != NULL)
+    free (buf);
 
   return result;
 }
@@ -251,6 +294,7 @@ elf_link_add_archive_symbols (abfd, info)
   boolean *included = NULL;
   carsym *symdefs;
   boolean loop;
+  bfd_size_type amt;
 
   if (! bfd_has_map (abfd))
     {
@@ -267,12 +311,14 @@ elf_link_add_archive_symbols (abfd, info)
   c = bfd_ardata (abfd)->symdef_count;
   if (c == 0)
     return true;
-  defined = (boolean *) bfd_malloc (c * sizeof (boolean));
-  included = (boolean *) bfd_malloc (c * sizeof (boolean));
+  amt = c;
+  amt *= sizeof (boolean);
+  defined = (boolean *) bfd_malloc (amt);
+  included = (boolean *) bfd_malloc (amt);
   if (defined == (boolean *) NULL || included == (boolean *) NULL)
     goto error_return;
-  memset (defined, 0, c * sizeof (boolean));
-  memset (included, 0, c * sizeof (boolean));
+  memset (defined, 0, (size_t) amt);
+  memset (included, 0, (size_t) amt);
 
   symdefs = bfd_ardata (abfd)->symdefs;
 
@@ -320,10 +366,10 @@ elf_link_add_archive_symbols (abfd, info)
              if (p == NULL || p[1] != ELF_VER_CHR)
                continue;
 
-             copy = bfd_alloc (abfd, p - symdef->name + 1);
+             copy = bfd_alloc (abfd, (bfd_size_type) (p - symdef->name + 1));
              if (copy == NULL)
                goto error_return;
-             memcpy (copy, symdef->name, p - symdef->name);
+             memcpy (copy, symdef->name, (size_t) (p - symdef->name));
              copy[p - symdef->name] = '\0';
 
              h = elf_link_hash_lookup (elf_hash_table (info), copy,
@@ -544,7 +590,7 @@ elf_merge_symbol (abfd, info, name, sym, psec, pvalue, sym_hash,
       asection *hsec;
 
       /* This code handles the special SHN_MIPS_{TEXT,DATA} section
-         indices used by MIPS ELF.  */
+        indices used by MIPS ELF.  */
       switch (h->root.type)
        {
        default:
@@ -654,10 +700,10 @@ elf_merge_symbol (abfd, info, name, sym, psec, pvalue, sym_hash,
       && sym->st_size != h->size)
     {
       /* Since we think we have two common symbols, issue a multiple
-         common warning if desired.  Note that we only warn if the
-         size is different.  If the size is the same, we simply let
-         the old symbol override the new one as normally happens with
-         symbols defined in dynamic objects.  */
+        common warning if desired.  Note that we only warn if the
+        size is different.  If the size is the same, we simply let
+        the old symbol override the new one as normally happens with
+        symbols defined in dynamic objects.  */
 
       if (! ((*info->callbacks->multiple_common)
             (info, h->root.root.string, oldbfd, bfd_link_hash_common,
@@ -707,10 +753,10 @@ elf_merge_symbol (abfd, info, name, sym, psec, pvalue, sym_hash,
       *size_change_ok = true;
 
       /* If we get here when the old symbol is a common symbol, then
-         we are explicitly letting it override a weak symbol or
-         function in a dynamic object, and we don't want to warn about
-         a type change.  If the old symbol is a defined symbol, a type
-         change warning may still be appropriate.  */
+        we are explicitly letting it override a weak symbol or
+        function in a dynamic object, and we don't want to warn about
+        a type change.  If the old symbol is a defined symbol, a type
+        change warning may still be appropriate.  */
 
       if (h->root.type == bfd_link_hash_common)
        *type_change_ok = true;
@@ -769,7 +815,7 @@ elf_merge_symbol (abfd, info, name, sym, psec, pvalue, sym_hash,
       olddyncommon = false;
 
       /* We again permit a type change when a common symbol may be
-         overriding a function.  */
+        overriding a function.  */
 
       if (bfd_is_com_section (sec))
        *type_change_ok = true;
@@ -781,11 +827,11 @@ elf_merge_symbol (abfd, info, name, sym, psec, pvalue, sym_hash,
       h->verinfo.vertree = NULL;
 
       /* In this special case, if H is the target of an indirection,
-         we want the caller to frob with H rather than with the
-         indirect symbol.  That will permit the caller to redefine the
-         target of the indirection, rather than the indirect symbol
-         itself.  FIXME: This will break the -y option if we store a
-         symbol with a different name.  */
+        we want the caller to frob with H rather than with the
+        indirect symbol.  That will permit the caller to redefine the
+        target of the indirection, rather than the indirect symbol
+        itself.  FIXME: This will break the -y option if we store a
+        symbol with a different name.  */
       *sym_hash = h;
     }
 
@@ -808,7 +854,7 @@ elf_merge_symbol (abfd, info, name, sym, psec, pvalue, sym_hash,
        return false;
 
       /* If the predumed common symbol in the dynamic object is
-         larger, pretend that the new symbol has its size.  */
+        larger, pretend that the new symbol has its size.  */
 
       if (h->size > *pvalue)
        *pvalue = h->size;
@@ -842,8 +888,8 @@ elf_merge_symbol (abfd, info, name, sym, psec, pvalue, sym_hash,
       && bind != STB_WEAK)
     {
       /* To make this work we have to frob the flags so that the rest
-         of the code does not think we are using the regular
-         definition.  */
+        of the code does not think we are using the regular
+        definition.  */
       if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0)
        h->elf_link_hash_flags |= ELF_LINK_HASH_REF_REGULAR;
       else if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0)
@@ -852,9 +898,9 @@ elf_merge_symbol (abfd, info, name, sym, psec, pvalue, sym_hash,
                                   | ELF_LINK_HASH_DEF_DYNAMIC);
 
       /* If H is the target of an indirection, we want the caller to
-         use H rather than the indirect symbol.  Otherwise if we are
-         defining a new indirect symbol we will wind up attaching it
-         to the entry we are overriding.  */
+        use H rather than the indirect symbol.  Otherwise if we are
+        defining a new indirect symbol we will wind up attaching it
+        to the entry we are overriding.  */
       *sym_hash = h;
     }
 
@@ -874,6 +920,243 @@ elf_merge_symbol (abfd, info, name, sym, psec, pvalue, sym_hash,
   return true;
 }
 
+/* This function is called to create an indirect symbol from the
+   default for the symbol with the default version if needed. The
+   symbol is described by H, NAME, SYM, SEC, VALUE, and OVERRIDE.  We
+   set DYNSYM if the new indirect symbol is dynamic. DT_NEEDED
+   indicates if it comes from a DT_NEEDED entry of a shared object.  */
+
+static boolean
+elf_add_default_symbol (abfd, info, h, name, sym, sec, value,
+                       dynsym, override, dt_needed)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     struct elf_link_hash_entry *h;
+     const char *name;
+     Elf_Internal_Sym *sym;
+     asection **sec;
+     bfd_vma *value;
+     boolean *dynsym;
+     boolean override;
+     boolean dt_needed;
+{
+  boolean type_change_ok;
+  boolean size_change_ok;
+  char *shortname;
+  struct elf_link_hash_entry *hi;
+  struct elf_backend_data *bed;
+  boolean collect;
+  boolean dynamic;
+  char *p;
+
+  /* If this symbol has a version, and it is the default version, we
+     create an indirect symbol from the default name to the fully
+     decorated name.  This will cause external references which do not
+     specify a version to be bound to this version of the symbol.  */
+  p = strchr (name, ELF_VER_CHR);
+  if (p == NULL || p[1] != ELF_VER_CHR)
+    return true;
+
+  if (override)
+    {
+      /* We are overridden by an old defition. We need to check if we
+        need to crreate the indirect symbol from the default name.  */
+      hi = elf_link_hash_lookup (elf_hash_table (info), name, true,
+                                false, false);
+      BFD_ASSERT (hi != NULL);
+      if (hi == h)
+       return true;
+      while (hi->root.type == bfd_link_hash_indirect
+            || hi->root.type == bfd_link_hash_warning)
+       {
+         hi = (struct elf_link_hash_entry *) hi->root.u.i.link;
+         if (hi == h)
+           return true;
+       }
+    }
+
+  bed = get_elf_backend_data (abfd);
+  collect = bed->collect;
+  dynamic = (abfd->flags & DYNAMIC) != 0;
+
+  shortname = bfd_hash_allocate (&info->hash->table,
+                                (size_t) (p - name + 1));
+  if (shortname == NULL)
+    return false;
+  strncpy (shortname, name, (size_t) (p - name));
+  shortname [p - name] = '\0';
+
+  /* We are going to create a new symbol.  Merge it with any existing
+     symbol with this name.  For the purposes of the merge, act as
+     though we were defining the symbol we just defined, although we
+     actually going to define an indirect symbol.  */
+  type_change_ok = false;
+  size_change_ok = false;
+  if (! elf_merge_symbol (abfd, info, shortname, sym, sec, value,
+                         &hi, &override, &type_change_ok,
+                         &size_change_ok, dt_needed))
+    return false;
+
+  if (! override)
+    {
+      if (! (_bfd_generic_link_add_one_symbol
+            (info, abfd, shortname, BSF_INDIRECT, bfd_ind_section_ptr,
+             (bfd_vma) 0, name, false, collect,
+             (struct bfd_link_hash_entry **) &hi)))
+       return false;
+    }
+  else
+    {
+      /* In this case the symbol named SHORTNAME is overriding the
+        indirect symbol we want to add.  We were planning on making
+        SHORTNAME an indirect symbol referring to NAME.  SHORTNAME
+        is the name without a version.  NAME is the fully versioned
+        name, and it is the default version.
+
+        Overriding means that we already saw a definition for the
+        symbol SHORTNAME in a regular object, and it is overriding
+        the symbol defined in the dynamic object.
+
+        When this happens, we actually want to change NAME, the
+        symbol we just added, to refer to SHORTNAME.  This will cause
+        references to NAME in the shared object to become references
+        to SHORTNAME in the regular object.  This is what we expect
+        when we override a function in a shared object: that the
+        references in the shared object will be mapped to the
+        definition in the regular object.  */
+
+      while (hi->root.type == bfd_link_hash_indirect
+            || hi->root.type == bfd_link_hash_warning)
+       hi = (struct elf_link_hash_entry *) hi->root.u.i.link;
+
+      h->root.type = bfd_link_hash_indirect;
+      h->root.u.i.link = (struct bfd_link_hash_entry *) hi;
+      if (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC)
+       {
+         h->elf_link_hash_flags &=~ ELF_LINK_HASH_DEF_DYNAMIC;
+         hi->elf_link_hash_flags |= ELF_LINK_HASH_REF_DYNAMIC;
+         if (hi->elf_link_hash_flags
+             & (ELF_LINK_HASH_REF_REGULAR
+                | ELF_LINK_HASH_DEF_REGULAR))
+           {
+             if (! _bfd_elf_link_record_dynamic_symbol (info, hi))
+               return false;
+           }
+       }
+
+      /* Now set HI to H, so that the following code will set the
+        other fields correctly.  */
+      hi = h;
+    }
+
+  /* If there is a duplicate definition somewhere, then HI may not
+     point to an indirect symbol.  We will have reported an error to
+     the user in that case.  */
+
+  if (hi->root.type == bfd_link_hash_indirect)
+    {
+      struct elf_link_hash_entry *ht;
+
+      /* If the symbol became indirect, then we assume that we have
+        not seen a definition before.  */
+      BFD_ASSERT ((hi->elf_link_hash_flags
+                  & (ELF_LINK_HASH_DEF_DYNAMIC
+                     | ELF_LINK_HASH_DEF_REGULAR)) == 0);
+
+      ht = (struct elf_link_hash_entry *) hi->root.u.i.link;
+      (*bed->elf_backend_copy_indirect_symbol) (ht, hi);
+
+      /* See if the new flags lead us to realize that the symbol must
+        be dynamic.  */
+      if (! *dynsym)
+       {
+         if (! dynamic)
+           {
+             if (info->shared
+                 || ((hi->elf_link_hash_flags
+                      & ELF_LINK_HASH_REF_DYNAMIC) != 0))
+               *dynsym = true;
+           }
+         else
+           {
+             if ((hi->elf_link_hash_flags
+                  & ELF_LINK_HASH_REF_REGULAR) != 0)
+               *dynsym = true;
+           }
+       }
+    }
+
+  /* We also need to define an indirection from the nondefault version
+     of the symbol.  */
+
+  shortname = bfd_hash_allocate (&info->hash->table, strlen (name));
+  if (shortname == NULL)
+    return false;
+  strncpy (shortname, name, (size_t) (p - name));
+  strcpy (shortname + (p - name), p + 1);
+
+  /* Once again, merge with any existing symbol.  */
+  type_change_ok = false;
+  size_change_ok = false;
+  if (! elf_merge_symbol (abfd, info, shortname, sym, sec, value,
+                         &hi, &override, &type_change_ok,
+                         &size_change_ok, dt_needed))
+    return false;
+
+  if (override)
+    {
+      /* Here SHORTNAME is a versioned name, so we don't expect to see
+        the type of override we do in the case above.  */
+      (*_bfd_error_handler)
+       (_("%s: warning: unexpected redefinition of `%s'"),
+        bfd_archive_filename (abfd), shortname);
+    }
+  else
+    {
+      if (! (_bfd_generic_link_add_one_symbol
+            (info, abfd, shortname, BSF_INDIRECT,
+             bfd_ind_section_ptr, (bfd_vma) 0, name, false,
+             collect, (struct bfd_link_hash_entry **) &hi)))
+       return false;
+
+      /* If there is a duplicate definition somewhere, then HI may not
+        point to an indirect symbol.  We will have reported an error
+        to the user in that case.  */
+
+      if (hi->root.type == bfd_link_hash_indirect)
+       {
+         /* If the symbol became indirect, then we assume that we have
+            not seen a definition before.  */
+         BFD_ASSERT ((hi->elf_link_hash_flags
+                      & (ELF_LINK_HASH_DEF_DYNAMIC
+                         | ELF_LINK_HASH_DEF_REGULAR)) == 0);
+
+         (*bed->elf_backend_copy_indirect_symbol) (h, hi);
+
+         /* See if the new flags lead us to realize that the symbol
+            must be dynamic.  */
+         if (! *dynsym)
+           {
+             if (! dynamic)
+               {
+                 if (info->shared
+                     || ((hi->elf_link_hash_flags
+                          & ELF_LINK_HASH_REF_DYNAMIC) != 0))
+                   *dynsym = true;
+               }
+             else
+               {
+                 if ((hi->elf_link_hash_flags
+                      & ELF_LINK_HASH_REF_REGULAR) != 0)
+                   *dynsym = true;
+               }
+           }
+       }
+    }
+
+  return true;
+}
+
 /* Add symbols from an ELF object file to the linker hash table.  */
 
 static boolean
@@ -889,13 +1172,15 @@ elf_link_add_object_symbols (abfd, info)
                                   asection *, const Elf_Internal_Rela *));
   boolean collect;
   Elf_Internal_Shdr *hdr;
-  size_t symcount;
-  size_t extsymcount;
-  size_t extsymoff;
+  Elf_Internal_Shdr *shndx_hdr;
+  bfd_size_type symcount;
+  bfd_size_type extsymcount;
+  bfd_size_type extsymoff;
   Elf_External_Sym *buf = NULL;
+  Elf_External_Sym_Shndx *shndx_buf = NULL;
+  Elf_External_Sym_Shndx *shndx;
   struct elf_link_hash_entry **sym_hash;
   boolean dynamic;
-  bfd_byte *dynver = NULL;
   Elf_External_Versym *extversym = NULL;
   Elf_External_Versym *ever;
   Elf_External_Dyn *dynbuf = NULL;
@@ -904,6 +1189,11 @@ elf_link_add_object_symbols (abfd, info)
   Elf_External_Sym *esymend;
   struct elf_backend_data *bed;
   boolean dt_needed;
+  struct elf_link_hash_table * hash_table;
+  file_ptr pos;
+  bfd_size_type amt;
+
+  hash_table = elf_hash_table (info);
 
   bed = get_elf_backend_data (abfd);
   add_symbol_hook = bed->elf_add_symbol_hook;
@@ -958,7 +1248,7 @@ elf_link_add_object_symbols (abfd, info)
                {
                  struct elf_link_hash_entry *h;
 
-                 h = elf_link_hash_lookup (elf_hash_table (info), name,
+                 h = elf_link_hash_lookup (hash_table, name,
                                            false, false, true);
 
                  /* FIXME: What about bfd_link_hash_common?  */
@@ -967,8 +1257,8 @@ elf_link_add_object_symbols (abfd, info)
                          || h->root.type == bfd_link_hash_defweak))
                    {
                      /* We don't want to issue this warning.  Clobber
-                         the section size so that the warning does not
-                         get copied into the output file.  */
+                        the section size so that the warning does not
+                        get copied into the output file.  */
                      s->_raw_size = 0;
                      continue;
                    }
@@ -992,7 +1282,7 @@ elf_link_add_object_symbols (abfd, info)
              if (! info->relocateable)
                {
                  /* Clobber the section size so that the warning does
-                     not get copied into the output file.  */
+                    not get copied into the output file.  */
                  s->_raw_size = 0;
                }
            }
@@ -1005,9 +1295,15 @@ elf_link_add_object_symbols (abfd, info)
      look at .symtab for a dynamic object.  */
 
   if (! dynamic || elf_dynsymtab (abfd) == 0)
-    hdr = &elf_tdata (abfd)->symtab_hdr;
+    {
+      hdr = &elf_tdata (abfd)->symtab_hdr;
+      shndx_hdr = &elf_tdata (abfd)->symtab_shndx_hdr;
+    }
   else
-    hdr = &elf_tdata (abfd)->dynsymtab_hdr;
+    {
+      hdr = &elf_tdata (abfd)->dynsymtab_hdr;
+      shndx_hdr = NULL;
+    }
 
   if (dynamic)
     {
@@ -1017,18 +1313,18 @@ elf_link_add_object_symbols (abfd, info)
        goto error_return;
 
       /* Read in the symbol versions, but don't bother to convert them
-         to internal format.  */
+        to internal format.  */
       if (elf_dynversym (abfd) != 0)
        {
          Elf_Internal_Shdr *versymhdr;
 
          versymhdr = &elf_tdata (abfd)->dynversym_hdr;
-         extversym = (Elf_External_Versym *) bfd_malloc (hdr->sh_size);
+         extversym = (Elf_External_Versym *) bfd_malloc (versymhdr->sh_size);
          if (extversym == NULL)
            goto error_return;
+         amt = versymhdr->sh_size;
          if (bfd_seek (abfd, versymhdr->sh_offset, SEEK_SET) != 0
-             || (bfd_read ((PTR) extversym, 1, versymhdr->sh_size, abfd)
-                 != versymhdr->sh_size))
+             || bfd_bread ((PTR) extversym, amt, abfd) != amt)
            goto error_return;
        }
     }
@@ -1049,16 +1345,23 @@ elf_link_add_object_symbols (abfd, info)
       extsymoff = hdr->sh_info;
     }
 
-  buf = ((Elf_External_Sym *)
-        bfd_malloc (extsymcount * sizeof (Elf_External_Sym)));
+  amt = extsymcount * sizeof (Elf_External_Sym);
+  buf = (Elf_External_Sym *) bfd_malloc (amt);
   if (buf == NULL && extsymcount != 0)
     goto error_return;
 
+  if (shndx_hdr != NULL && shndx_hdr->sh_size != 0)
+    {
+      amt = extsymcount * sizeof (Elf_External_Sym_Shndx);
+      shndx_buf = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
+      if (shndx_buf == NULL && extsymcount != 0)
+       goto error_return;
+    }
+
   /* We store a pointer to the hash table entry for each external
      symbol.  */
-  sym_hash = ((struct elf_link_hash_entry **)
-             bfd_alloc (abfd,
-                        extsymcount * sizeof (struct elf_link_hash_entry *)));
+  amt = extsymcount * sizeof (struct elf_link_hash_entry *);
+  sym_hash = (struct elf_link_hash_entry **) bfd_alloc (abfd, amt);
   if (sym_hash == NULL)
     goto error_return;
   elf_sym_hashes (abfd) = sym_hash;
@@ -1068,18 +1371,21 @@ elf_link_add_object_symbols (abfd, info)
   if (! dynamic)
     {
       /* If we are creating a shared library, create all the dynamic
-         sections immediately.  We need to attach them to something,
-         so we attach them to this BFD, provided it is the right
-         format.  FIXME: If there are no input BFD's of the same
-         format as the output, we can't make a shared library.  */
+        sections immediately.  We need to attach them to something,
+        so we attach them to this BFD, provided it is the right
+        format.  FIXME: If there are no input BFD's of the same
+        format as the output, we can't make a shared library.  */
       if (info->shared
-         && ! elf_hash_table (info)->dynamic_sections_created
+         && is_elf_hash_table (info)
+         && ! hash_table->dynamic_sections_created
          && abfd->xvec == info->hash->creator)
        {
          if (! elf_link_create_dynamic_sections (abfd, info))
            goto error_return;
        }
     }
+  else if (! is_elf_hash_table (info))
+    goto error_return;
   else
     {
       asection *s;
@@ -1103,7 +1409,7 @@ elf_link_add_object_symbols (abfd, info)
          if (*name == '\0')
            {
              if (elf_dt_soname (abfd) != NULL)
-               dt_needed = true;
+               dt_needed = true;
 
              add_needed = false;
            }
@@ -1114,11 +1420,11 @@ elf_link_add_object_symbols (abfd, info)
          Elf_External_Dyn *extdyn;
          Elf_External_Dyn *extdynend;
          int elfsec;
-         unsigned long link;
+         unsigned long shlink;
          int rpath;
          int runpath;
 
-         dynbuf = (Elf_External_Dyn *) bfd_malloc ((size_t) s->_raw_size);
+         dynbuf = (Elf_External_Dyn *) bfd_malloc (s->_raw_size);
          if (dynbuf == NULL)
            goto error_return;
 
@@ -1129,22 +1435,22 @@ elf_link_add_object_symbols (abfd, info)
          elfsec = _bfd_elf_section_from_bfd_section (abfd, s);
          if (elfsec == -1)
            goto error_return;
-         link = elf_elfsections (abfd)[elfsec]->sh_link;
+         shlink = elf_elfsections (abfd)[elfsec]->sh_link;
 
          {
            /* The shared libraries distributed with hpux11 have a bogus
               sh_link field for the ".dynamic" section.  This code detects
-              when LINK refers to a section that is not a string table and
-              tries to find the string table for the ".dynsym" section
+              when SHLINK refers to a section that is not a string table
+              and tries to find the string table for the ".dynsym" section
               instead.  */
-           Elf_Internal_Shdr *hdr = elf_elfsections (abfd)[link];
-           if (hdr->sh_type != SHT_STRTAB)
+           Elf_Internal_Shdr *shdr = elf_elfsections (abfd)[shlink];
+           if (shdr->sh_type != SHT_STRTAB)
              {
-               asection *s = bfd_get_section_by_name (abfd, ".dynsym");
-               int elfsec = _bfd_elf_section_from_bfd_section (abfd, s);
-               if (elfsec == -1)
+               asection *ds = bfd_get_section_by_name (abfd, ".dynsym");
+               int elfdsec = _bfd_elf_section_from_bfd_section (abfd, ds);
+               if (elfdsec == -1)
                  goto error_return;
-               link = elf_elfsections (abfd)[elfsec]->sh_link;
+               shlink = elf_elfsections (abfd)[elfdsec]->sh_link;
              }
          }
 
@@ -1159,8 +1465,8 @@ elf_link_add_object_symbols (abfd, info)
              elf_swap_dyn_in (abfd, extdyn, &dyn);
              if (dyn.d_tag == DT_SONAME)
                {
-                 name = bfd_elf_string_from_elf_section (abfd, link,
-                                                         dyn.d_un.d_val);
+                 unsigned int tagv = dyn.d_un.d_val;
+                 name = bfd_elf_string_from_elf_section (abfd, shlink, tagv);
                  if (name == NULL)
                    goto error_return;
                }
@@ -1168,21 +1474,21 @@ elf_link_add_object_symbols (abfd, info)
                {
                  struct bfd_link_needed_list *n, **pn;
                  char *fnm, *anm;
+                 unsigned int tagv = dyn.d_un.d_val;
 
-                 n = ((struct bfd_link_needed_list *)
-                      bfd_alloc (abfd, sizeof (struct bfd_link_needed_list)));
-                 fnm = bfd_elf_string_from_elf_section (abfd, link,
-                                                        dyn.d_un.d_val);
+                 amt = sizeof (struct bfd_link_needed_list);
+                 n = (struct bfd_link_needed_list *) bfd_alloc (abfd, amt);
+                 fnm = bfd_elf_string_from_elf_section (abfd, shlink, tagv);
                  if (n == NULL || fnm == NULL)
                    goto error_return;
-                 anm = bfd_alloc (abfd, strlen (fnm) + 1);
+                 anm = bfd_alloc (abfd, (bfd_size_type) strlen (fnm) + 1);
                  if (anm == NULL)
                    goto error_return;
                  strcpy (anm, fnm);
                  n->name = anm;
                  n->by = abfd;
                  n->next = NULL;
-                 for (pn = &elf_hash_table (info)->needed;
+                 for (pn = & hash_table->needed;
                       *pn != NULL;
                       pn = &(*pn)->next)
                    ;
@@ -1192,28 +1498,28 @@ elf_link_add_object_symbols (abfd, info)
                {
                  struct bfd_link_needed_list *n, **pn;
                  char *fnm, *anm;
+                 unsigned int tagv = dyn.d_un.d_val;
 
                  /* When we see DT_RPATH before DT_RUNPATH, we have
                     to clear runpath.  Do _NOT_ bfd_release, as that
                     frees all more recently bfd_alloc'd blocks as
                     well.  */
-                 if (rpath && elf_hash_table (info)->runpath)
-                   elf_hash_table (info)->runpath = NULL;
+                 if (rpath && hash_table->runpath)
+                   hash_table->runpath = NULL;
 
-                 n = ((struct bfd_link_needed_list *)
-                      bfd_alloc (abfd, sizeof (struct bfd_link_needed_list)));
-                 fnm = bfd_elf_string_from_elf_section (abfd, link,
-                                                        dyn.d_un.d_val);
+                 amt = sizeof (struct bfd_link_needed_list);
+                 n = (struct bfd_link_needed_list *) bfd_alloc (abfd, amt);
+                 fnm = bfd_elf_string_from_elf_section (abfd, shlink, tagv);
                  if (n == NULL || fnm == NULL)
                    goto error_return;
-                 anm = bfd_alloc (abfd, strlen (fnm) + 1);
+                 anm = bfd_alloc (abfd, (bfd_size_type) strlen (fnm) + 1);
                  if (anm == NULL)
                    goto error_return;
                  strcpy (anm, fnm);
                  n->name = anm;
                  n->by = abfd;
                  n->next = NULL;
-                 for (pn = &elf_hash_table (info)->runpath;
+                 for (pn = & hash_table->runpath;
                       *pn != NULL;
                       pn = &(*pn)->next)
                    ;
@@ -1223,24 +1529,24 @@ elf_link_add_object_symbols (abfd, info)
                }
              /* Ignore DT_RPATH if we have seen DT_RUNPATH.  */
              if (!runpath && dyn.d_tag == DT_RPATH)
-               {
+               {
                  struct bfd_link_needed_list *n, **pn;
                  char *fnm, *anm;
+                 unsigned int tagv = dyn.d_un.d_val;
 
-                 n = ((struct bfd_link_needed_list *)
-                      bfd_alloc (abfd, sizeof (struct bfd_link_needed_list)));
-                 fnm = bfd_elf_string_from_elf_section (abfd, link,
-                                                        dyn.d_un.d_val);
+                 amt = sizeof (struct bfd_link_needed_list);
+                 n = (struct bfd_link_needed_list *) bfd_alloc (abfd, amt);
+                 fnm = bfd_elf_string_from_elf_section (abfd, shlink, tagv);
                  if (n == NULL || fnm == NULL)
                    goto error_return;
-                 anm = bfd_alloc (abfd, strlen (fnm) + 1);
+                 anm = bfd_alloc (abfd, (bfd_size_type) strlen (fnm) + 1);
                  if (anm == NULL)
                    goto error_return;
                  strcpy (anm, fnm);
                  n->name = anm;
                  n->by = abfd;
                  n->next = NULL;
-                 for (pn = &elf_hash_table (info)->runpath;
+                 for (pn = & hash_table->runpath;
                       *pn != NULL;
                       pn = &(*pn)->next)
                    ;
@@ -1260,27 +1566,23 @@ elf_link_add_object_symbols (abfd, info)
         SEC_NEVER_LOAD flag is not the one we want, because that one
         still implies that the section takes up space in the output
         file.  */
-      abfd->sections = NULL;
-      abfd->section_count = 0;
+      bfd_section_list_clear (abfd);
 
       /* If this is the first dynamic object found in the link, create
         the special sections required for dynamic linking.  */
-      if (! elf_hash_table (info)->dynamic_sections_created)
-       {
-         if (! elf_link_create_dynamic_sections (abfd, info))
-           goto error_return;
-       }
+      if (! hash_table->dynamic_sections_created)
+       if (! elf_link_create_dynamic_sections (abfd, info))
+         goto error_return;
 
       if (add_needed)
        {
          /* Add a DT_NEEDED entry for this dynamic object.  */
-         oldsize = _bfd_stringtab_size (elf_hash_table (info)->dynstr);
-         strindex = _bfd_stringtab_add (elf_hash_table (info)->dynstr, name,
-                                        true, false);
+         oldsize = _bfd_elf_strtab_size (hash_table->dynstr);
+         strindex = _bfd_elf_strtab_add (hash_table->dynstr, name, false);
          if (strindex == (bfd_size_type) -1)
            goto error_return;
 
-         if (oldsize == _bfd_stringtab_size (elf_hash_table (info)->dynstr))
+         if (oldsize == _bfd_elf_strtab_size (hash_table->dynstr))
            {
              asection *sdyn;
              Elf_External_Dyn *dyncon, *dynconend;
@@ -1290,8 +1592,7 @@ elf_link_add_object_symbols (abfd, info)
                 have already included this dynamic object in the
                 link, just ignore it.  There is no reason to include
                 a particular dynamic object more than once.  */
-             sdyn = bfd_get_section_by_name (elf_hash_table (info)->dynobj,
-                                             ".dynamic");
+             sdyn = bfd_get_section_by_name (hash_table->dynobj, ".dynamic");
              BFD_ASSERT (sdyn != NULL);
 
              dyncon = (Elf_External_Dyn *) sdyn->contents;
@@ -1301,8 +1602,7 @@ elf_link_add_object_symbols (abfd, info)
                {
                  Elf_Internal_Dyn dyn;
 
-                 elf_swap_dyn_in (elf_hash_table (info)->dynobj, dyncon,
-                                  &dyn);
+                 elf_swap_dyn_in (hash_table->dynobj, dyncon, & dyn);
                  if (dyn.d_tag == DT_NEEDED
                      && dyn.d_un.d_val == strindex)
                    {
@@ -1310,36 +1610,46 @@ elf_link_add_object_symbols (abfd, info)
                        free (buf);
                      if (extversym != NULL)
                        free (extversym);
+                     _bfd_elf_strtab_delref (hash_table->dynstr, strindex);
                      return true;
                    }
                }
            }
 
-         if (! elf_add_dynamic_entry (info, DT_NEEDED, strindex))
+         if (! elf_add_dynamic_entry (info, (bfd_vma) DT_NEEDED, strindex))
            goto error_return;
        }
 
       /* Save the SONAME, if there is one, because sometimes the
-         linker emulation code will need to know it.  */
+        linker emulation code will need to know it.  */
       if (*name == '\0')
-       name = bfd_get_filename (abfd);
+       name = basename (bfd_get_filename (abfd));
       elf_dt_name (abfd) = name;
     }
 
-  if (bfd_seek (abfd,
-               hdr->sh_offset + extsymoff * sizeof (Elf_External_Sym),
-               SEEK_SET) != 0
-      || (bfd_read ((PTR) buf, sizeof (Elf_External_Sym), extsymcount, abfd)
-         != extsymcount * sizeof (Elf_External_Sym)))
+  pos = hdr->sh_offset + extsymoff * sizeof (Elf_External_Sym);
+  amt = extsymcount * sizeof (Elf_External_Sym);
+  if (bfd_seek (abfd, pos, SEEK_SET) != 0
+      || bfd_bread ((PTR) buf, amt, abfd) != amt)
     goto error_return;
 
+  if (shndx_hdr != NULL && shndx_hdr->sh_size != 0)
+    {
+      amt = extsymcount * sizeof (Elf_External_Sym_Shndx);
+      pos = shndx_hdr->sh_offset + extsymoff * sizeof (Elf_External_Sym_Shndx);
+      if (bfd_seek (abfd, pos, SEEK_SET) != 0
+         || bfd_bread ((PTR) shndx_buf, amt, abfd) != amt)
+       goto error_return;
+    }
+
   weaks = NULL;
 
   ever = extversym != NULL ? extversym + extsymoff : NULL;
   esymend = buf + extsymcount;
-  for (esym = buf;
+  for (esym = buf, shndx = shndx_buf;
        esym < esymend;
-       esym++, sym_hash++, ever = (ever != NULL ? ever + 1 : NULL))
+       esym++, sym_hash++, ever = (ever != NULL ? ever + 1 : NULL),
+        shndx = (shndx != NULL ? shndx + 1 : NULL))
     {
       Elf_Internal_Sym sym;
       int bind;
@@ -1352,8 +1662,11 @@ elf_link_add_object_symbols (abfd, info)
       boolean size_change_ok, type_change_ok;
       boolean new_weakdef;
       unsigned int old_alignment;
+      boolean override;
+
+      override = false;
 
-      elf_swap_symbol_in (abfd, esym, &sym);
+      elf_swap_symbol_in (abfd, esym, shndx, &sym);
 
       flags = BSF_NO_FLAGS;
       sec = NULL;
@@ -1384,7 +1697,7 @@ elf_link_add_object_symbols (abfd, info)
 
       if (sym.st_shndx == SHN_UNDEF)
        sec = bfd_und_section_ptr;
-      else if (sym.st_shndx > 0 && sym.st_shndx < SHN_LORESERVE)
+      else if (sym.st_shndx < SHN_LORESERVE || sym.st_shndx > SHN_HIRESERVE)
        {
          sec = section_from_elf_index (abfd, sym.st_shndx);
          if (sec == NULL)
@@ -1442,7 +1755,6 @@ elf_link_add_object_symbols (abfd, info)
        {
          Elf_Internal_Versym iver;
          unsigned int vernum = 0;
-         boolean override;
 
          if (ever != NULL)
            {
@@ -1450,15 +1762,16 @@ elf_link_add_object_symbols (abfd, info)
              vernum = iver.vs_vers & VERSYM_VERSION;
 
              /* If this is a hidden symbol, or if it is not version
-                 1, we append the version name to the symbol name.
-                 However, we do not modify a non-hidden absolute
-                 symbol, because it might be the version symbol
-                 itself.  FIXME: What if it isn't?  */
+                1, we append the version name to the symbol name.
+                However, we do not modify a non-hidden absolute
+                symbol, because it might be the version symbol
+                itself.  FIXME: What if it isn't?  */
              if ((iver.vs_vers & VERSYM_HIDDEN) != 0
                  || (vernum > 1 && ! bfd_is_abs_section (sec)))
                {
                  const char *verstr;
-                 int namelen, newlen;
+                 unsigned int namelen;
+                 bfd_size_type newlen;
                  char *newname, *p;
 
                  if (sym.st_shndx != SHN_UNDEF)
@@ -1467,7 +1780,7 @@ elf_link_add_object_symbols (abfd, info)
                        {
                          (*_bfd_error_handler)
                            (_("%s: %s: invalid version %u (max %d)"),
-                            bfd_get_filename (abfd), name, vernum,
+                            bfd_archive_filename (abfd), name, vernum,
                             elf_tdata (abfd)->dynverdef_hdr.sh_info);
                          bfd_set_error (bfd_error_bad_value);
                          goto error_return;
@@ -1508,7 +1821,7 @@ elf_link_add_object_symbols (abfd, info)
                        {
                          (*_bfd_error_handler)
                            (_("%s: %s: invalid needed version %d"),
-                            bfd_get_filename (abfd), name, vernum);
+                            bfd_archive_filename (abfd), name, vernum);
                          bfd_set_error (bfd_error_bad_value);
                          goto error_return;
                        }
@@ -1551,10 +1864,10 @@ elf_link_add_object_symbols (abfd, info)
            h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
          /* Remember the old alignment if this is a common symbol, so
-             that we don't reduce the alignment later on.  We can't
-             check later, because _bfd_generic_link_add_one_symbol
-             will set a default for the alignment which we want to
-             override.  */
+            that we don't reduce the alignment later on.  We can't
+            check later, because _bfd_generic_link_add_one_symbol
+            will set a default for the alignment which we want to
+            override.  */
          if (h->root.type == bfd_link_hash_common)
            old_alignment = h->root.u.c.p->alignment_power;
 
@@ -1629,16 +1942,16 @@ elf_link_add_object_symbols (abfd, info)
                (*_bfd_error_handler)
                  (_("Warning: size of symbol `%s' changed from %lu to %lu in %s"),
                   name, (unsigned long) h->size, (unsigned long) sym.st_size,
-                  bfd_get_filename (abfd));
+                  bfd_archive_filename (abfd));
 
              h->size = sym.st_size;
            }
 
          /* If this is a common symbol, then we always want H->SIZE
-             to be the size of the common symbol.  The code just above
-             won't fix the size if a common symbol becomes larger.  We
-             don't warn about a size change here, because that is
-             covered by --warn-common.  */
+            to be the size of the common symbol.  The code just above
+            won't fix the size if a common symbol becomes larger.  We
+            don't warn about a size change here, because that is
+            covered by --warn-common.  */
          if (h->root.type == bfd_link_hash_common)
            h->size = h->root.u.c.size;
 
@@ -1651,7 +1964,7 @@ elf_link_add_object_symbols (abfd, info)
                (*_bfd_error_handler)
                  (_("Warning: type of symbol `%s' changed from %d to %d in %s"),
                   name, h->type, ELF_ST_TYPE (sym.st_info),
-                  bfd_get_filename (abfd));
+                  bfd_archive_filename (abfd));
 
              h->type = ELF_ST_TYPE (sym.st_info);
            }
@@ -1668,8 +1981,8 @@ elf_link_add_object_symbols (abfd, info)
                h->other = sym.st_other;
 
              /* If neither has visibility, use the st_other of the
-                definition.  This is an arbitrary choice, since the
-                other bits have no general meaning.  */
+                definition.  This is an arbitrary choice, since the
+                other bits have no general meaning.  */
              if (!symvis && !hvis
                  && (definition || h->other == 0))
                h->other = sym.st_other;
@@ -1713,217 +2026,13 @@ elf_link_add_object_symbols (abfd, info)
 
          h->elf_link_hash_flags |= new_flag;
 
-         /* If this symbol has a version, and it is the default
-             version, we create an indirect symbol from the default
-             name to the fully decorated name.  This will cause
-             external references which do not specify a version to be
-             bound to this version of the symbol.  */
+         /* Check to see if we need to add an indirect symbol for
+            the default name.  */
          if (definition || h->root.type == bfd_link_hash_common)
-           {
-             char *p;
-
-             p = strchr (name, ELF_VER_CHR);
-             if (p != NULL && p[1] == ELF_VER_CHR)
-               {
-                 char *shortname;
-                 struct elf_link_hash_entry *hi;
-                 boolean override;
-
-                 shortname = bfd_hash_allocate (&info->hash->table,
-                                                p - name + 1);
-                 if (shortname == NULL)
-                   goto error_return;
-                 strncpy (shortname, name, p - name);
-                 shortname[p - name] = '\0';
-
-                 /* We are going to create a new symbol.  Merge it
-                     with any existing symbol with this name.  For the
-                     purposes of the merge, act as though we were
-                     defining the symbol we just defined, although we
-                     actually going to define an indirect symbol.  */
-                 type_change_ok = false;
-                 size_change_ok = false;
-                 if (! elf_merge_symbol (abfd, info, shortname, &sym, &sec,
-                                         &value, &hi, &override,
-                                         &type_change_ok,
-                                         &size_change_ok, dt_needed))
-                   goto error_return;
-
-                 if (! override)
-                   {
-                     if (! (_bfd_generic_link_add_one_symbol
-                            (info, abfd, shortname, BSF_INDIRECT,
-                             bfd_ind_section_ptr, (bfd_vma) 0, name, false,
-                             collect, (struct bfd_link_hash_entry **) &hi)))
-                       goto error_return;
-                   }
-                 else
-                   {
-                     /* In this case the symbol named SHORTNAME is
-                         overriding the indirect symbol we want to
-                         add.  We were planning on making SHORTNAME an
-                         indirect symbol referring to NAME.  SHORTNAME
-                         is the name without a version.  NAME is the
-                         fully versioned name, and it is the default
-                         version.
-
-                        Overriding means that we already saw a
-                        definition for the symbol SHORTNAME in a
-                        regular object, and it is overriding the
-                        symbol defined in the dynamic object.
-
-                        When this happens, we actually want to change
-                        NAME, the symbol we just added, to refer to
-                        SHORTNAME.  This will cause references to
-                        NAME in the shared object to become
-                        references to SHORTNAME in the regular
-                        object.  This is what we expect when we
-                        override a function in a shared object: that
-                        the references in the shared object will be
-                        mapped to the definition in the regular
-                        object.  */
-
-                     while (hi->root.type == bfd_link_hash_indirect
-                            || hi->root.type == bfd_link_hash_warning)
-                       hi = (struct elf_link_hash_entry *) hi->root.u.i.link;
-
-                     h->root.type = bfd_link_hash_indirect;
-                     h->root.u.i.link = (struct bfd_link_hash_entry *) hi;
-                     if (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC)
-                       {
-                         h->elf_link_hash_flags &=~ ELF_LINK_HASH_DEF_DYNAMIC;
-                         hi->elf_link_hash_flags |= ELF_LINK_HASH_REF_DYNAMIC;
-                         if (hi->elf_link_hash_flags
-                             & (ELF_LINK_HASH_REF_REGULAR
-                                | ELF_LINK_HASH_DEF_REGULAR))
-                           {
-                             if (! _bfd_elf_link_record_dynamic_symbol (info,
-                                                                        hi))
-                               goto error_return;
-                           }
-                       }
-
-                     /* Now set HI to H, so that the following code
-                         will set the other fields correctly.  */
-                     hi = h;
-                   }
-
-                 /* If there is a duplicate definition somewhere,
-                    then HI may not point to an indirect symbol.  We
-                    will have reported an error to the user in that
-                    case.  */
-
-                 if (hi->root.type == bfd_link_hash_indirect)
-                   {
-                     struct elf_link_hash_entry *ht;
-
-                     /* If the symbol became indirect, then we assume
-                        that we have not seen a definition before.  */
-                     BFD_ASSERT ((hi->elf_link_hash_flags
-                                  & (ELF_LINK_HASH_DEF_DYNAMIC
-                                     | ELF_LINK_HASH_DEF_REGULAR))
-                                 == 0);
-
-                     ht = (struct elf_link_hash_entry *) hi->root.u.i.link;
-                     (*bed->elf_backend_copy_indirect_symbol) (ht, hi);
-
-                     /* See if the new flags lead us to realize that
-                        the symbol must be dynamic.  */
-                     if (! dynsym)
-                       {
-                         if (! dynamic)
-                           {
-                             if (info->shared
-                                 || ((hi->elf_link_hash_flags
-                                      & ELF_LINK_HASH_REF_DYNAMIC)
-                                     != 0))
-                               dynsym = true;
-                           }
-                         else
-                           {
-                             if ((hi->elf_link_hash_flags
-                                  & ELF_LINK_HASH_REF_REGULAR) != 0)
-                               dynsym = true;
-                           }
-                       }
-                   }
-
-                 /* We also need to define an indirection from the
-                     nondefault version of the symbol.  */
-
-                 shortname = bfd_hash_allocate (&info->hash->table,
-                                                strlen (name));
-                 if (shortname == NULL)
-                   goto error_return;
-                 strncpy (shortname, name, p - name);
-                 strcpy (shortname + (p - name), p + 1);
-
-                 /* Once again, merge with any existing symbol.  */
-                 type_change_ok = false;
-                 size_change_ok = false;
-                 if (! elf_merge_symbol (abfd, info, shortname, &sym, &sec,
-                                         &value, &hi, &override,
-                                         &type_change_ok,
-                                         &size_change_ok, dt_needed))
-                   goto error_return;
-
-                 if (override)
-                   {
-                     /* Here SHORTNAME is a versioned name, so we
-                         don't expect to see the type of override we
-                         do in the case above.  */
-                     (*_bfd_error_handler)
-                       (_("%s: warning: unexpected redefinition of `%s'"),
-                        bfd_get_filename (abfd), shortname);
-                   }
-                 else
-                   {
-                     if (! (_bfd_generic_link_add_one_symbol
-                            (info, abfd, shortname, BSF_INDIRECT,
-                             bfd_ind_section_ptr, (bfd_vma) 0, name, false,
-                             collect, (struct bfd_link_hash_entry **) &hi)))
-                       goto error_return;
-
-                     /* If there is a duplicate definition somewhere,
-                         then HI may not point to an indirect symbol.
-                         We will have reported an error to the user in
-                         that case.  */
-
-                     if (hi->root.type == bfd_link_hash_indirect)
-                       {
-                         /* If the symbol became indirect, then we
-                             assume that we have not seen a definition
-                             before.  */
-                         BFD_ASSERT ((hi->elf_link_hash_flags
-                                      & (ELF_LINK_HASH_DEF_DYNAMIC
-                                         | ELF_LINK_HASH_DEF_REGULAR))
-                                     == 0);
-
-                         (*bed->elf_backend_copy_indirect_symbol) (h, hi);
-
-                         /* See if the new flags lead us to realize
-                             that the symbol must be dynamic.  */
-                         if (! dynsym)
-                           {
-                             if (! dynamic)
-                               {
-                                 if (info->shared
-                                     || ((hi->elf_link_hash_flags
-                                          & ELF_LINK_HASH_REF_DYNAMIC)
-                                         != 0))
-                                   dynsym = true;
-                               }
-                             else
-                               {
-                                 if ((hi->elf_link_hash_flags
-                                      & ELF_LINK_HASH_REF_REGULAR) != 0)
-                                   dynsym = true;
-                               }
-                           }
-                       }
-                   }
-               }
-           }
+           if (! elf_add_default_symbol (abfd, info, h, name, &sym,
+                                         &sec, &value, &dynsym,
+                                         override, dt_needed))
+             goto error_return;
 
          if (dynsym && h->dynindx == -1)
            {
@@ -1933,8 +2042,7 @@ elf_link_add_object_symbols (abfd, info)
                  && ! new_weakdef
                  && h->weakdef->dynindx == -1)
                {
-                 if (! _bfd_elf_link_record_dynamic_symbol (info,
-                                                            h->weakdef))
+                 if (! _bfd_elf_link_record_dynamic_symbol (info, h->weakdef))
                    goto error_return;
                }
            }
@@ -1946,8 +2054,7 @@ elf_link_add_object_symbols (abfd, info)
              {
              case STV_INTERNAL:
              case STV_HIDDEN:
-               h->elf_link_hash_flags |= ELF_LINK_FORCED_LOCAL;
-               (*bed->elf_backend_hide_symbol) (info, h);
+               (*bed->elf_backend_hide_symbol) (info, h, true);
                break;
              }
 
@@ -1958,25 +2065,26 @@ elf_link_add_object_symbols (abfd, info)
              bfd_size_type oldsize;
              bfd_size_type strindex;
 
+             if (! is_elf_hash_table (info))
+               goto error_return;
+
              /* The symbol from a DT_NEEDED object is referenced from
-                the regular object to create a dynamic executable. We
+                the regular object to create a dynamic executable. We
                 have to make sure there is a DT_NEEDED entry for it.  */
 
              dt_needed = false;
-             oldsize = _bfd_stringtab_size (elf_hash_table (info)->dynstr);
-             strindex = _bfd_stringtab_add (elf_hash_table (info)->dynstr,
-                                            elf_dt_soname (abfd),
-                                            true, false);
+             oldsize = _bfd_elf_strtab_size (hash_table->dynstr);
+             strindex = _bfd_elf_strtab_add (hash_table->dynstr,
+                                             elf_dt_soname (abfd), false);
              if (strindex == (bfd_size_type) -1)
                goto error_return;
 
-             if (oldsize
-                 == _bfd_stringtab_size (elf_hash_table (info)->dynstr))
+             if (oldsize == _bfd_elf_strtab_size (hash_table->dynstr))
                {
                  asection *sdyn;
                  Elf_External_Dyn *dyncon, *dynconend;
 
-                 sdyn = bfd_get_section_by_name (elf_hash_table (info)->dynobj,
+                 sdyn = bfd_get_section_by_name (hash_table->dynobj,
                                                  ".dynamic");
                  BFD_ASSERT (sdyn != NULL);
 
@@ -1987,14 +2095,14 @@ elf_link_add_object_symbols (abfd, info)
                    {
                      Elf_Internal_Dyn dyn;
 
-                     elf_swap_dyn_in (elf_hash_table (info)->dynobj,
+                     elf_swap_dyn_in (hash_table->dynobj,
                                       dyncon, &dyn);
                      BFD_ASSERT (dyn.d_tag != DT_NEEDED ||
                                  dyn.d_un.d_val != strindex);
                    }
                }
 
-             if (! elf_add_dynamic_entry (info, DT_NEEDED, strindex))
+             if (! elf_add_dynamic_entry (info, (bfd_vma) DT_NEEDED, strindex))
                goto error_return;
            }
        }
@@ -2057,10 +2165,10 @@ elf_link_add_object_symbols (abfd, info)
                }
 
              /* If the real definition is in the list of dynamic
-                 symbols, make sure the weak definition is put there
-                 as well.  If we don't do this, then the dynamic
-                 loader might not merge the entries for the real
-                 definition and the weak definition.  */
+                symbols, make sure the weak definition is put there
+                as well.  If we don't do this, then the dynamic
+                loader might not merge the entries for the real
+                definition and the weak definition.  */
              if (h->dynindx != -1
                  && hlook->dynindx == -1)
                {
@@ -2144,12 +2252,15 @@ elf_link_add_object_symbols (abfd, info)
       && ! info->relocateable
       && ! info->traditional_format
       && info->hash->creator->flavour == bfd_target_elf_flavour
+      && is_elf_hash_table (info)
       && (info->strip != strip_all && info->strip != strip_debugger))
     {
       asection *stab, *stabstr;
 
       stab = bfd_get_section_by_name (abfd, ".stab");
-      if (stab != NULL)
+      if (stab != NULL
+         && (stab->flags & SEC_MERGE) == 0
+         && !bfd_is_abs_section (stab->output_section))
        {
          stabstr = bfd_get_section_by_name (abfd, ".stabstr");
 
@@ -2159,14 +2270,37 @@ elf_link_add_object_symbols (abfd, info)
 
              secdata = elf_section_data (stab);
              if (! _bfd_link_section_stabs (abfd,
-                                            &elf_hash_table (info)->stab_info,
+                                            & hash_table->stab_info,
                                             stab, stabstr,
-                                            &secdata->stab_info))
+                                            &secdata->sec_info))
                goto error_return;
+             if (secdata->sec_info)
+               secdata->sec_info_type = ELF_INFO_TYPE_STABS;
            }
        }
     }
 
+  if (! info->relocateable && ! dynamic
+      && is_elf_hash_table (info))
+    {
+      asection *s;
+
+      for (s = abfd->sections; s != NULL; s = s->next)
+       if ((s->flags & SEC_MERGE) != 0
+           && !bfd_is_abs_section (s->output_section))
+         {
+           struct bfd_elf_section_data *secdata;
+
+           secdata = elf_section_data (s);
+           if (! _bfd_merge_section (abfd,
+                                     & hash_table->merge_info,
+                                     s, &secdata->sec_info))
+             goto error_return;
+           else if (secdata->sec_info)
+             secdata->sec_info_type = ELF_INFO_TYPE_MERGE;
+         }
+    }
+
   return true;
 
  error_return:
@@ -2174,8 +2308,6 @@ elf_link_add_object_symbols (abfd, info)
     free (buf);
   if (dynbuf != NULL)
     free (dynbuf);
-  if (dynver != NULL)
-    free (dynver);
   if (extversym != NULL)
     free (extversym);
   return false;
@@ -2198,6 +2330,9 @@ elf_link_create_dynamic_sections (abfd, info)
   struct elf_link_hash_entry *h;
   struct elf_backend_data *bed;
 
+  if (! is_elf_hash_table (info))
+    return false;
+
   if (elf_hash_table (info)->dynamic_sections_created)
     return true;
 
@@ -2222,6 +2357,16 @@ elf_link_create_dynamic_sections (abfd, info)
        return false;
     }
 
+  if (! info->traditional_format
+      && info->hash->creator->flavour == bfd_target_elf_flavour)
+    {
+      s = bfd_make_section (abfd, ".eh_frame_hdr");
+      if (s == NULL
+         || ! bfd_set_section_flags (abfd, s, flags | SEC_READONLY)
+         || ! bfd_set_section_alignment (abfd, s, 2))
+       return false;
+    }
+
   /* Create sections to hold version informations.  These are removed
      if they are not needed.  */
   s = bfd_make_section (abfd, ".gnu.version_d");
@@ -2256,7 +2401,7 @@ elf_link_create_dynamic_sections (abfd, info)
   /* Create a strtab to hold the dynamic symbol names.  */
   if (elf_hash_table (info)->dynstr == NULL)
     {
-      elf_hash_table (info)->dynstr = elf_stringtab_init ();
+      elf_hash_table (info)->dynstr = _bfd_elf_strtab_init ();
       if (elf_hash_table (info)->dynstr == NULL)
        return false;
     }
@@ -2319,9 +2464,12 @@ elf_add_dynamic_entry (info, tag, val)
   Elf_Internal_Dyn dyn;
   bfd *dynobj;
   asection *s;
-  size_t newsize;
+  bfd_size_type newsize;
   bfd_byte *newcontents;
 
+  if (! is_elf_hash_table (info))
+    return false;
+
   dynobj = elf_hash_table (info)->dynobj;
 
   s = bfd_get_section_by_name (dynobj, ".dynamic");
@@ -2353,10 +2501,17 @@ elf_link_record_local_dynamic_symbol (info, input_bfd, input_indx)
 {
   struct elf_link_local_dynamic_entry *entry;
   struct elf_link_hash_table *eht;
-  struct bfd_strtab_hash *dynstr;
+  struct elf_strtab_hash *dynstr;
   Elf_External_Sym esym;
+  Elf_External_Sym_Shndx eshndx;
+  Elf_External_Sym_Shndx *shndx;
   unsigned long dynstr_index;
   char *name;
+  file_ptr pos;
+  bfd_size_type amt;
+
+  if (! is_elf_hash_table (info))
+    return false;
 
   /* See if the entry exists already.  */
   for (entry = elf_hash_table (info)->dynlocal; entry ; entry = entry->next)
@@ -2364,19 +2519,28 @@ elf_link_record_local_dynamic_symbol (info, input_bfd, input_indx)
       return true;
 
   entry = (struct elf_link_local_dynamic_entry *)
-    bfd_alloc (input_bfd, sizeof (*entry));
+    bfd_alloc (input_bfd, (bfd_size_type) sizeof (*entry));
   if (entry == NULL)
     return false;
 
   /* Go find the symbol, so that we can find it's name.  */
-  if (bfd_seek (input_bfd,
-               (elf_tdata (input_bfd)->symtab_hdr.sh_offset
-                + input_indx * sizeof (Elf_External_Sym)),
-               SEEK_SET) != 0
-      || (bfd_read (&esym, sizeof (Elf_External_Sym), 1, input_bfd)
-         != sizeof (Elf_External_Sym)))
+  amt = sizeof (Elf_External_Sym);
+  pos = elf_tdata (input_bfd)->symtab_hdr.sh_offset + input_indx * amt;
+  if (bfd_seek (input_bfd, pos, SEEK_SET) != 0
+      || bfd_bread ((PTR) &esym, amt, input_bfd) != amt)
     return false;
-  elf_swap_symbol_in (input_bfd, &esym, &entry->isym);
+  shndx = NULL;
+  if (elf_tdata (input_bfd)->symtab_shndx_hdr.sh_size != 0)
+    {
+      amt = sizeof (Elf_External_Sym_Shndx);
+      pos = elf_tdata (input_bfd)->symtab_shndx_hdr.sh_offset;
+      pos += input_indx * amt;
+      shndx = &eshndx;
+      if (bfd_seek (input_bfd, pos, SEEK_SET) != 0
+         || bfd_bread ((PTR) shndx, amt, input_bfd) != amt)
+       return false;
+    }
+  elf_swap_symbol_in (input_bfd, &esym, shndx, &entry->isym);
 
   name = (bfd_elf_string_from_elf_section
          (input_bfd, elf_tdata (input_bfd)->symtab_hdr.sh_link,
@@ -2386,12 +2550,12 @@ elf_link_record_local_dynamic_symbol (info, input_bfd, input_indx)
   if (dynstr == NULL)
     {
       /* Create a strtab to hold the dynamic symbol names.  */
-      elf_hash_table (info)->dynstr = dynstr = _bfd_elf_stringtab_init ();
+      elf_hash_table (info)->dynstr = dynstr = _bfd_elf_strtab_init ();
       if (dynstr == NULL)
        return false;
     }
 
-  dynstr_index = _bfd_stringtab_add (dynstr, name, true, false);
+  dynstr_index = _bfd_elf_strtab_add (dynstr, name, false);
   if (dynstr_index == (unsigned long) -1)
     return false;
   entry->isym.st_name = dynstr_index;
@@ -2431,6 +2595,7 @@ elf_link_read_relocs_from_section (abfd, shdr, external_relocs,
      Elf_Internal_Rela *internal_relocs;
 {
   struct elf_backend_data *bed;
+  bfd_size_type amt;
 
   /* If there aren't any relocations, that's OK.  */
   if (!shdr)
@@ -2441,8 +2606,7 @@ elf_link_read_relocs_from_section (abfd, shdr, external_relocs,
     return false;
 
   /* Read the relocations.  */
-  if (bfd_read (external_relocs, 1, shdr->sh_size, abfd)
-      != shdr->sh_size)
+  if (bfd_bread (external_relocs, shdr->sh_size, abfd) != shdr->sh_size)
     return false;
 
   bed = get_elf_backend_data (abfd);
@@ -2456,13 +2620,13 @@ elf_link_read_relocs_from_section (abfd, shdr, external_relocs,
       Elf_Internal_Rel *irel;
 
       erel = (Elf_External_Rel *) external_relocs;
-      erelend = erel + shdr->sh_size / shdr->sh_entsize;
+      erelend = erel + NUM_SHDR_ENTRIES (shdr);
       irela = internal_relocs;
-      irel = bfd_alloc (abfd, (bed->s->int_rels_per_ext_rel
-                              * sizeof (Elf_Internal_Rel)));
+      amt = bed->s->int_rels_per_ext_rel * sizeof (Elf_Internal_Rel);
+      irel = bfd_alloc (abfd, amt);
       for (; erel < erelend; erel++, irela += bed->s->int_rels_per_ext_rel)
        {
-         unsigned char i;
+         unsigned int i;
 
          if (bed->s->swap_reloc_in)
            (*bed->s->swap_reloc_in) (abfd, (bfd_byte *) erel, irel);
@@ -2486,7 +2650,7 @@ elf_link_read_relocs_from_section (abfd, shdr, external_relocs,
       BFD_ASSERT (shdr->sh_entsize == sizeof (Elf_External_Rela));
 
       erela = (Elf_External_Rela *) external_relocs;
-      erelaend = erela + shdr->sh_size / shdr->sh_entsize;
+      erelaend = erela + NUM_SHDR_ENTRIES (shdr);
       irela = internal_relocs;
       for (; erela < erelaend; erela++, irela += bed->s->int_rels_per_ext_rel)
        {
@@ -2534,10 +2698,10 @@ NAME(_bfd_elf,link_read_relocs) (abfd, o, external_relocs, internal_relocs,
 
   if (internal_relocs == NULL)
     {
-      size_t size;
+      bfd_size_type size;
 
-      size = (o->reloc_count * bed->s->int_rels_per_ext_rel
-             * sizeof (Elf_Internal_Rela));
+      size = o->reloc_count;
+      size *= bed->s->int_rels_per_ext_rel * sizeof (Elf_Internal_Rela);
       if (keep_memory)
        internal_relocs = (Elf_Internal_Rela *) bfd_alloc (abfd, size);
       else
@@ -2548,10 +2712,10 @@ NAME(_bfd_elf,link_read_relocs) (abfd, o, external_relocs, internal_relocs,
 
   if (external_relocs == NULL)
     {
-      size_t size = (size_t) rel_hdr->sh_size;
+      bfd_size_type size = rel_hdr->sh_size;
 
       if (elf_section_data (o)->rel_hdr2)
-       size += (size_t) elf_section_data (o)->rel_hdr2->sh_size;
+       size += elf_section_data (o)->rel_hdr2->sh_size;
       alloc1 = (PTR) bfd_malloc (size);
       if (alloc1 == NULL)
        goto error_return;
@@ -2566,7 +2730,7 @@ NAME(_bfd_elf,link_read_relocs) (abfd, o, external_relocs, internal_relocs,
       (abfd,
        elf_section_data (o)->rel_hdr2,
        ((bfd_byte *) external_relocs) + rel_hdr->sh_size,
-       internal_relocs + (rel_hdr->sh_size / rel_hdr->sh_entsize
+       internal_relocs + (NUM_SHDR_ENTRIES (rel_hdr)
                          * bed->s->int_rels_per_ext_rel)))
     goto error_return;
 
@@ -2593,7 +2757,6 @@ NAME(_bfd_elf,link_read_relocs) (abfd, o, external_relocs, internal_relocs,
 /* Record an assignment to a symbol made by a linker script.  We need
    this in case some dynamic object refers to this symbol.  */
 
-/*ARGSUSED*/
 boolean
 NAME(bfd_elf,record_link_assignment) (output_bfd, info, name, provide)
      bfd *output_bfd ATTRIBUTE_UNUSED;
@@ -2611,7 +2774,7 @@ NAME(bfd_elf,record_link_assignment) (output_bfd, info, name, provide)
     return false;
 
   if (h->root.type == bfd_link_hash_new)
-    h->elf_link_hash_flags &=ELF_LINK_NON_ELF;
+    h->elf_link_hash_flags &= ~ELF_LINK_NON_ELF;
 
   /* If this symbol is being provided by the linker script, and it is
      currently defined by a dynamic object, but not by a regular
@@ -2633,10 +2796,6 @@ NAME(bfd_elf,record_link_assignment) (output_bfd, info, name, provide)
 
   h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
 
-  /* When possible, keep the original type of the symbol */
-  if (h->type == STT_NOTYPE)
-    h->type = STT_OBJECT;
-
   if (((h->elf_link_hash_flags & (ELF_LINK_HASH_DEF_DYNAMIC
                                  | ELF_LINK_HASH_REF_DYNAMIC)) != 0
        || info->shared)
@@ -2670,8 +2829,6 @@ struct elf_assign_sym_version_info
   struct bfd_link_info *info;
   /* Version tree.  */
   struct bfd_elf_version_tree *verdefs;
-  /* Whether we are exporting all dynamic symbols.  */
-  boolean export_dynamic;
   /* Whether we had a failure.  */
   boolean failed;
 };
@@ -2720,12 +2877,14 @@ compute_bucket_count (info)
   unsigned long int *hashcodes;
   unsigned long int *hashcodesp;
   unsigned long int i;
+  bfd_size_type amt;
 
   /* Compute the hash values for all exported symbols.  At the same
      time store the values in an array so that we could use them for
      optimizations.  */
-  hashcodes = (unsigned long int *) bfd_malloc (dynsymcount
-                                               * sizeof (unsigned long int));
+  amt = dynsymcount;
+  amt *= sizeof (unsigned long int);
+  hashcodes = (unsigned long int *) bfd_malloc (amt);
   if (hashcodes == NULL)
     return 0;
   hashcodesp = hashcodes;
@@ -2756,8 +2915,9 @@ compute_bucket_count (info)
 
       /* Create array where we count the collisions in.  We must use bfd_malloc
         since the size could be large.  */
-      counts = (unsigned long int *) bfd_malloc (maxsize
-                                                * sizeof (unsigned long int));
+      amt = maxsize;
+      amt *= sizeof (unsigned long int);
+      counts = (unsigned long int *) bfd_malloc (amt);
       if (counts == NULL)
        {
          free (hashcodes);
@@ -2853,13 +3013,12 @@ compute_bucket_count (info)
 
 boolean
 NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
-                                    export_dynamic, filter_shlib,
+                                    filter_shlib,
                                     auxiliary_filters, info, sinterpptr,
                                     verdefs)
      bfd *output_bfd;
      const char *soname;
      const char *rpath;
-     boolean export_dynamic;
      const char *filter_shlib;
      const char * const *auxiliary_filters;
      struct bfd_link_info *info;
@@ -2878,6 +3037,13 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
   if (info->hash->creator->flavour != bfd_target_elf_flavour)
     return true;
 
+  if (! is_elf_hash_table (info))
+    return false;
+
+  /* Any syms created from now on start with -1 in
+     got.refcount/offset and plt.refcount/offset.  */
+  elf_hash_table (info)->init_refcount = -1;
+
   /* The backend may have to create some sections regardless of whether
      we're dynamic or not.  */
   bed = get_elf_backend_data (output_bfd);
@@ -2892,6 +3058,9 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
   if (dynobj == NULL)
     return true;
 
+  if (! _bfd_elf_maybe_strip_eh_frame_hdr (info))
+    return false;
+
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       struct elf_info_failed eif;
@@ -2903,16 +3072,18 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
 
       if (soname != NULL)
        {
-         soname_indx = _bfd_stringtab_add (elf_hash_table (info)->dynstr,
-                                           soname, true, true);
+         soname_indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
+                                            soname, true);
          if (soname_indx == (bfd_size_type) -1
-             || ! elf_add_dynamic_entry (info, DT_SONAME, soname_indx))
+             || ! elf_add_dynamic_entry (info, (bfd_vma) DT_SONAME,
+                                         soname_indx))
            return false;
        }
 
       if (info->symbolic)
        {
-         if (! elf_add_dynamic_entry (info, DT_SYMBOLIC, 0))
+         if (! elf_add_dynamic_entry (info, (bfd_vma) DT_SYMBOLIC,
+                                      (bfd_vma) 0))
            return false;
          info->flags |= DF_SYMBOLIC;
        }
@@ -2921,12 +3092,15 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
        {
          bfd_size_type indx;
 
-         indx = _bfd_stringtab_add (elf_hash_table (info)->dynstr, rpath,
-                                    true, true);
+         indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr, rpath,
+                                     true);
+         if (info->new_dtags)
+           _bfd_elf_strtab_addref (elf_hash_table (info)->dynstr, indx);
          if (indx == (bfd_size_type) -1
-             || ! elf_add_dynamic_entry (info, DT_RPATH, indx)
+             || ! elf_add_dynamic_entry (info, (bfd_vma) DT_RPATH, indx)
              || (info->new_dtags
-                 && ! elf_add_dynamic_entry (info, DT_RUNPATH, indx)))
+                 && ! elf_add_dynamic_entry (info, (bfd_vma) DT_RUNPATH,
+                                             indx)))
            return false;
        }
 
@@ -2934,10 +3108,10 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
        {
          bfd_size_type indx;
 
-         indx = _bfd_stringtab_add (elf_hash_table (info)->dynstr,
-                                    filter_shlib, true, true);
+         indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
+                                     filter_shlib, true);
          if (indx == (bfd_size_type) -1
-             || ! elf_add_dynamic_entry (info, DT_FILTER, indx))
+             || ! elf_add_dynamic_entry (info, (bfd_vma) DT_FILTER, indx))
            return false;
        }
 
@@ -2949,24 +3123,25 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
            {
              bfd_size_type indx;
 
-             indx = _bfd_stringtab_add (elf_hash_table (info)->dynstr,
-                                        *p, true, true);
+             indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
+                                         *p, true);
              if (indx == (bfd_size_type) -1
-                 || ! elf_add_dynamic_entry (info, DT_AUXILIARY, indx))
+                 || ! elf_add_dynamic_entry (info, (bfd_vma) DT_AUXILIARY,
+                                             indx))
                return false;
            }
        }
 
+      eif.info = info;
+      eif.verdefs = verdefs;
+      eif.failed = false;
+
       /* If we are supposed to export all symbols into the dynamic symbol
-         table (this is not the normal case), then do so.  */
-      if (export_dynamic)
+        table (this is not the normal case), then do so.  */
+      if (info->export_dynamic)
        {
-         struct elf_info_failed eif;
-
-         eif.failed = false;
-         eif.info = info;
          elf_link_hash_traverse (elf_hash_table (info), elf_export_symbol,
-                                 (PTR) &eif);
+                                 (PTR) &eif);
          if (eif.failed)
            return false;
        }
@@ -2975,7 +3150,6 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
       asvinfo.output_bfd = output_bfd;
       asvinfo.info = info;
       asvinfo.verdefs = verdefs;
-      asvinfo.export_dynamic = export_dynamic;
       asvinfo.failed = false;
 
       elf_link_hash_traverse (elf_hash_table (info),
@@ -2986,8 +3160,6 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
 
       /* Find all symbols which were defined in a dynamic object and make
         the backend pick a reasonable value for them.  */
-      eif.failed = false;
-      eif.info = info;
       elf_link_hash_traverse (elf_hash_table (info),
                              elf_adjust_dynamic_symbol,
                              (PTR) &eif);
@@ -3009,7 +3181,7 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
          && (h->elf_link_hash_flags & (ELF_LINK_HASH_REF_REGULAR
                                        | ELF_LINK_HASH_DEF_REGULAR)) != 0)
        {
-         if (! elf_add_dynamic_entry (info, DT_INIT, 0))
+         if (! elf_add_dynamic_entry (info, (bfd_vma) DT_INIT, (bfd_vma) 0))
            return false;
        }
       h = (info->fini_function
@@ -3021,7 +3193,54 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
          && (h->elf_link_hash_flags & (ELF_LINK_HASH_REF_REGULAR
                                        | ELF_LINK_HASH_DEF_REGULAR)) != 0)
        {
-         if (! elf_add_dynamic_entry (info, DT_FINI, 0))
+         if (! elf_add_dynamic_entry (info, (bfd_vma) DT_FINI, (bfd_vma) 0))
+           return false;
+       }
+
+      if (bfd_get_section_by_name (output_bfd, ".preinit_array") != NULL)
+       {
+         /* DT_PREINIT_ARRAY is not allowed in shared library.  */
+         if (info->shared)
+           {
+             bfd *sub;
+             asection *o;
+
+             for (sub = info->input_bfds; sub != NULL;
+                  sub = sub->link_next)
+               for (o = sub->sections; o != NULL; o = o->next)
+                 if (elf_section_data (o)->this_hdr.sh_type
+                     == SHT_PREINIT_ARRAY)
+                   {
+                     (*_bfd_error_handler)
+                       (_("%s: .preinit_array section is not allowed in DSO"),
+                         bfd_archive_filename (sub));
+                     break;
+                   }
+
+             bfd_set_error (bfd_error_nonrepresentable_section);
+             return false;
+           }
+
+         if (!elf_add_dynamic_entry (info, (bfd_vma) DT_PREINIT_ARRAY,
+                                     (bfd_vma) 0)
+             || !elf_add_dynamic_entry (info, (bfd_vma) DT_PREINIT_ARRAYSZ,
+                                        (bfd_vma) 0))
+           return false;
+       }
+      if (bfd_get_section_by_name (output_bfd, ".init_array") != NULL)
+       {
+         if (!elf_add_dynamic_entry (info, (bfd_vma) DT_INIT_ARRAY,
+                                     (bfd_vma) 0)
+             || !elf_add_dynamic_entry (info, (bfd_vma) DT_INIT_ARRAYSZ,
+                                        (bfd_vma) 0))
+           return false;
+       }
+      if (bfd_get_section_by_name (output_bfd, ".fini_array") != NULL)
+       {
+         if (!elf_add_dynamic_entry (info, (bfd_vma) DT_FINI_ARRAY,
+                                     (bfd_vma) 0)
+             || !elf_add_dynamic_entry (info, (bfd_vma) DT_FINI_ARRAYSZ,
+                                        (bfd_vma) 0))
            return false;
        }
 
@@ -3034,13 +3253,13 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
        {
          bfd_size_type strsize;
 
-         strsize = _bfd_stringtab_size (elf_hash_table (info)->dynstr);
-         if (! elf_add_dynamic_entry (info, DT_HASH, 0)
-             || ! elf_add_dynamic_entry (info, DT_STRTAB, 0)
-             || ! elf_add_dynamic_entry (info, DT_SYMTAB, 0)
-             || ! elf_add_dynamic_entry (info, DT_STRSZ, strsize)
-             || ! elf_add_dynamic_entry (info, DT_SYMENT,
-                                         sizeof (Elf_External_Sym)))
+         strsize = _bfd_elf_strtab_size (elf_hash_table (info)->dynstr);
+         if (! elf_add_dynamic_entry (info, (bfd_vma) DT_HASH, (bfd_vma) 0)
+             || ! elf_add_dynamic_entry (info, (bfd_vma) DT_STRTAB, (bfd_vma) 0)
+             || ! elf_add_dynamic_entry (info, (bfd_vma) DT_SYMTAB, (bfd_vma) 0)
+             || ! elf_add_dynamic_entry (info, (bfd_vma) DT_STRSZ, strsize)
+             || ! elf_add_dynamic_entry (info, (bfd_vma) DT_SYMENT,
+                                         (bfd_vma) sizeof (Elf_External_Sym)))
            return false;
        }
     }
@@ -3053,19 +3272,24 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
 
   if (elf_hash_table (info)->dynamic_sections_created)
     {
-      size_t dynsymcount;
+      bfd_size_type dynsymcount;
       asection *s;
       size_t bucketcount = 0;
       size_t hash_entry_size;
+      unsigned int dtagcount;
 
       /* Set up the version definition section.  */
       s = bfd_get_section_by_name (dynobj, ".gnu.version_d");
       BFD_ASSERT (s != NULL);
 
       /* We may have created additional version definitions if we are
-         just linking a regular application.  */
+        just linking a regular application.  */
       verdefs = asvinfo.verdefs;
 
+      /* Skip anonymous version tag.  */
+      if (verdefs != NULL && verdefs->vernum == 0)
+       verdefs = verdefs->next;
+
       if (verdefs == NULL)
        _bfd_strip_section_from_output (info, s);
       else
@@ -3116,6 +3340,8 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
 
          if (soname_indx != (bfd_size_type) -1)
            {
+             _bfd_elf_strtab_addref (elf_hash_table (info)->dynstr,
+                                     soname_indx);
              def.vd_hash = bfd_elf_hash (soname);
              defaux.vda_name = soname_indx;
            }
@@ -3124,10 +3350,10 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
              const char *name;
              bfd_size_type indx;
 
-             name = output_bfd->filename;
+             name = basename (output_bfd->filename);
              def.vd_hash = bfd_elf_hash (name);
-             indx = _bfd_stringtab_add (elf_hash_table (info)->dynstr,
-                                           name, true, false);
+             indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
+                                         name, false);
              if (indx == (bfd_size_type) -1)
                return false;
              defaux.vda_name = indx;
@@ -3135,7 +3361,7 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
          defaux.vda_next = 0;
 
          _bfd_elf_swap_verdef_out (output_bfd, &def,
-                                   (Elf_External_Verdef *)p);
+                                   (Elf_External_Verdef *) p);
          p += sizeof (Elf_External_Verdef);
          _bfd_elf_swap_verdaux_out (output_bfd, &defaux,
                                     (Elf_External_Verdaux *) p);
@@ -3186,6 +3412,8 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
              p += sizeof (Elf_External_Verdef);
 
              defaux.vda_name = h->dynstr_index;
+             _bfd_elf_strtab_addref (elf_hash_table (info)->dynstr,
+                                     h->dynstr_index);
              if (t->deps == NULL)
                defaux.vda_next = 0;
              else
@@ -3205,7 +3433,11 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
                      defaux.vda_name = 0;
                    }
                  else
-                   defaux.vda_name = n->version_needed->name_indx;
+                   {
+                     defaux.vda_name = n->version_needed->name_indx;
+                     _bfd_elf_strtab_addref (elf_hash_table (info)->dynstr,
+                                             defaux.vda_name);
+                   }
                  if (n->next == NULL)
                    defaux.vda_next = 0;
                  else
@@ -3217,8 +3449,9 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
                }
            }
 
-         if (! elf_add_dynamic_entry (info, DT_VERDEF, 0)
-             || ! elf_add_dynamic_entry (info, DT_VERDEFNUM, cdefs))
+         if (! elf_add_dynamic_entry (info, (bfd_vma) DT_VERDEF, (bfd_vma) 0)
+             || ! elf_add_dynamic_entry (info, (bfd_vma) DT_VERDEFNUM,
+                                         (bfd_vma) cdefs))
            return false;
 
          elf_tdata (output_bfd)->cverdefs = cdefs;
@@ -3226,7 +3459,7 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
 
       if (info->new_dtags && info->flags)
        {
-         if (! elf_add_dynamic_entry (info, DT_FLAGS, info->flags))
+         if (! elf_add_dynamic_entry (info, (bfd_vma) DT_FLAGS, info->flags))
            return false;
        }
 
@@ -3236,7 +3469,8 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
            info->flags_1 &= ~ (DF_1_INITFIRST
                                | DF_1_NODELETE
                                | DF_1_NOOPEN);
-         if (! elf_add_dynamic_entry (info, DT_FLAGS_1, info->flags_1))
+         if (! elf_add_dynamic_entry (info, (bfd_vma) DT_FLAGS_1,
+                                      info->flags_1))
            return false;
        }
 
@@ -3283,7 +3517,7 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
              }
 
            s->_raw_size = size;
-           s->contents = (bfd_byte *) bfd_alloc (output_bfd, size);
+           s->contents = (bfd_byte *) bfd_alloc (output_bfd, s->_raw_size);
            if (s->contents == NULL)
              return false;
 
@@ -3302,13 +3536,11 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
 
                t->vn_version = VER_NEED_CURRENT;
                t->vn_cnt = caux;
-               if (elf_dt_name (t->vn_bfd) != NULL)
-                 indx = _bfd_stringtab_add (elf_hash_table (info)->dynstr,
-                                            elf_dt_name (t->vn_bfd),
-                                            true, false);
-               else
-                 indx = _bfd_stringtab_add (elf_hash_table (info)->dynstr,
-                                            t->vn_bfd->filename, true, false);
+               indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
+                                           elf_dt_name (t->vn_bfd) != NULL
+                                           ? elf_dt_name (t->vn_bfd)
+                                           : basename (t->vn_bfd->filename),
+                                           false);
                if (indx == (bfd_size_type) -1)
                  return false;
                t->vn_file = indx;
@@ -3326,8 +3558,8 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
                for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr)
                  {
                    a->vna_hash = bfd_elf_hash (a->vna_nodename);
-                   indx = _bfd_stringtab_add (elf_hash_table (info)->dynstr,
-                                              a->vna_nodename, true, false);
+                   indx = _bfd_elf_strtab_add (elf_hash_table (info)->dynstr,
+                                               a->vna_nodename, false);
                    if (indx == (bfd_size_type) -1)
                      return false;
                    a->vna_name = indx;
@@ -3342,8 +3574,10 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
                  }
              }
 
-           if (! elf_add_dynamic_entry (info, DT_VERNEED, 0)
-               || ! elf_add_dynamic_entry (info, DT_VERNEEDNUM, crefs))
+           if (! elf_add_dynamic_entry (info, (bfd_vma) DT_VERNEED,
+                                        (bfd_vma) 0)
+               || ! elf_add_dynamic_entry (info, (bfd_vma) DT_VERNEEDNUM,
+                                           (bfd_vma) crefs))
              return false;
 
            elf_tdata (output_bfd)->cverrefs = crefs;
@@ -3375,7 +3609,7 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
          if (s->contents == NULL)
            return false;
 
-         if (! elf_add_dynamic_entry (info, DT_VERSYM, 0))
+         if (! elf_add_dynamic_entry (info, (bfd_vma) DT_VERSYM, (bfd_vma) 0))
            return false;
        }
 
@@ -3403,8 +3637,7 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
          isym.st_info = 0;
          isym.st_other = 0;
          isym.st_shndx = 0;
-         elf_swap_symbol_out (output_bfd, &isym,
-                              (PTR) (Elf_External_Sym *) s->contents);
+         elf_swap_symbol_out (output_bfd, &isym, (PTR) s->contents, (PTR) 0);
        }
 
       /* Compute the size of the hashing table.  As a side effect this
@@ -3420,33 +3653,185 @@ NAME(bfd_elf,size_dynamic_sections) (output_bfd, soname, rpath,
        return false;
       memset (s->contents, 0, (size_t) s->_raw_size);
 
-      bfd_put (8 * hash_entry_size, output_bfd, bucketcount, s->contents);
-      bfd_put (8 * hash_entry_size, output_bfd, dynsymcount,
+      bfd_put (8 * hash_entry_size, output_bfd, (bfd_vma) bucketcount,
+              s->contents);
+      bfd_put (8 * hash_entry_size, output_bfd, (bfd_vma) dynsymcount,
               s->contents + hash_entry_size);
 
       elf_hash_table (info)->bucketcount = bucketcount;
 
       s = bfd_get_section_by_name (dynobj, ".dynstr");
       BFD_ASSERT (s != NULL);
-      s->_raw_size = _bfd_stringtab_size (elf_hash_table (info)->dynstr);
 
-      if (! elf_add_dynamic_entry (info, DT_NULL, 0))
-       return false;
+      elf_finalize_dynstr (output_bfd, info);
+
+      s->_raw_size = _bfd_elf_strtab_size (elf_hash_table (info)->dynstr);
+
+      for (dtagcount = 0; dtagcount <= info->spare_dynamic_tags; ++dtagcount)
+       if (! elf_add_dynamic_entry (info, (bfd_vma) DT_NULL, (bfd_vma) 0))
+         return false;
     }
 
   return true;
 }
 \f
-/* Fix up the flags for a symbol.  This handles various cases which
-   can only be fixed after all the input files are seen.  This is
-   currently called by both adjust_dynamic_symbol and
-   assign_sym_version, which is unnecessary but perhaps more robust in
-   the face of future changes.  */
+/* This function is used to adjust offsets into .dynstr for
+   dynamic symbols.  This is called via elf_link_hash_traverse.  */
+
+static boolean elf_adjust_dynstr_offsets
+PARAMS ((struct elf_link_hash_entry *, PTR));
 
 static boolean
-elf_fix_symbol_flags (h, eif)
+elf_adjust_dynstr_offsets (h, data)
      struct elf_link_hash_entry *h;
-     struct elf_info_failed *eif;
+     PTR data;
+{
+  struct elf_strtab_hash *dynstr = (struct elf_strtab_hash *) data;
+
+  if (h->root.type == bfd_link_hash_warning)
+    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+  if (h->dynindx != -1)
+    h->dynstr_index = _bfd_elf_strtab_offset (dynstr, h->dynstr_index);
+  return true;
+}
+
+/* Assign string offsets in .dynstr, update all structures referencing
+   them.  */
+
+static boolean
+elf_finalize_dynstr (output_bfd, info)
+     bfd *output_bfd;
+     struct bfd_link_info *info;
+{
+  struct elf_link_local_dynamic_entry *entry;
+  struct elf_strtab_hash *dynstr = elf_hash_table (info)->dynstr;
+  bfd *dynobj = elf_hash_table (info)->dynobj;
+  asection *sdyn;
+  bfd_size_type size;
+  Elf_External_Dyn *dyncon, *dynconend;
+
+  _bfd_elf_strtab_finalize (dynstr);
+  size = _bfd_elf_strtab_size (dynstr);
+
+  /* Update all .dynamic entries referencing .dynstr strings.  */
+  sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
+  BFD_ASSERT (sdyn != NULL);
+
+  dyncon = (Elf_External_Dyn *) sdyn->contents;
+  dynconend = (Elf_External_Dyn *) (sdyn->contents +
+                                   sdyn->_raw_size);
+  for (; dyncon < dynconend; dyncon++)
+    {
+      Elf_Internal_Dyn dyn;
+
+      elf_swap_dyn_in (dynobj, dyncon, & dyn);
+      switch (dyn.d_tag)
+       {
+       case DT_STRSZ:
+         dyn.d_un.d_val = size;
+         elf_swap_dyn_out (dynobj, & dyn, dyncon);
+         break;
+       case DT_NEEDED:
+       case DT_SONAME:
+       case DT_RPATH:
+       case DT_RUNPATH:
+       case DT_FILTER:
+       case DT_AUXILIARY:
+         dyn.d_un.d_val = _bfd_elf_strtab_offset (dynstr, dyn.d_un.d_val);
+         elf_swap_dyn_out (dynobj, & dyn, dyncon);
+         break;
+       default:
+         break;
+       }
+    }
+
+  /* Now update local dynamic symbols.  */
+  for (entry = elf_hash_table (info)->dynlocal; entry ; entry = entry->next)
+    entry->isym.st_name = _bfd_elf_strtab_offset (dynstr,
+                                                 entry->isym.st_name);
+
+  /* And the rest of dynamic symbols.  */
+  elf_link_hash_traverse (elf_hash_table (info),
+                         elf_adjust_dynstr_offsets, dynstr);
+
+  /* Adjust version definitions.  */
+  if (elf_tdata (output_bfd)->cverdefs)
+    {
+      asection *s;
+      bfd_byte *p;
+      bfd_size_type i;
+      Elf_Internal_Verdef def;
+      Elf_Internal_Verdaux defaux;
+
+      s = bfd_get_section_by_name (dynobj, ".gnu.version_d");
+      p = (bfd_byte *) s->contents;
+      do
+       {
+         _bfd_elf_swap_verdef_in (output_bfd, (Elf_External_Verdef *) p,
+                                  &def);
+         p += sizeof (Elf_External_Verdef);
+         for (i = 0; i < def.vd_cnt; ++i)
+           {
+             _bfd_elf_swap_verdaux_in (output_bfd,
+                                       (Elf_External_Verdaux *) p, &defaux);
+             defaux.vda_name = _bfd_elf_strtab_offset (dynstr,
+                                                       defaux.vda_name);
+             _bfd_elf_swap_verdaux_out (output_bfd,
+                                        &defaux, (Elf_External_Verdaux *) p);
+             p += sizeof (Elf_External_Verdaux);
+           }
+       }
+      while (def.vd_next);
+    }
+
+  /* Adjust version references.  */
+  if (elf_tdata (output_bfd)->verref)
+    {
+      asection *s;
+      bfd_byte *p;
+      bfd_size_type i;
+      Elf_Internal_Verneed need;
+      Elf_Internal_Vernaux needaux;
+
+      s = bfd_get_section_by_name (dynobj, ".gnu.version_r");
+      p = (bfd_byte *) s->contents;
+      do
+       {
+         _bfd_elf_swap_verneed_in (output_bfd, (Elf_External_Verneed *) p,
+                                   &need);
+         need.vn_file = _bfd_elf_strtab_offset (dynstr, need.vn_file);
+         _bfd_elf_swap_verneed_out (output_bfd, &need,
+                                    (Elf_External_Verneed *) p);
+         p += sizeof (Elf_External_Verneed);
+         for (i = 0; i < need.vn_cnt; ++i)
+           {
+             _bfd_elf_swap_vernaux_in (output_bfd,
+                                       (Elf_External_Vernaux *) p, &needaux);
+             needaux.vna_name = _bfd_elf_strtab_offset (dynstr,
+                                                        needaux.vna_name);
+             _bfd_elf_swap_vernaux_out (output_bfd,
+                                        &needaux,
+                                        (Elf_External_Vernaux *) p);
+             p += sizeof (Elf_External_Vernaux);
+           }
+       }
+      while (need.vn_next);
+    }
+
+  return true;
+}
+
+/* Fix up the flags for a symbol.  This handles various cases which
+   can only be fixed after all the input files are seen.  This is
+   currently called by both adjust_dynamic_symbol and
+   assign_sym_version, which is unnecessary but perhaps more robust in
+   the face of future changes.  */
+
+static boolean
+elf_fix_symbol_flags (h, eif)
+     struct elf_link_hash_entry *h;
+     struct elf_info_failed *eif;
 {
   /* If this symbol was mentioned in a non-ELF file, try to set
      DEF_REGULAR and REF_REGULAR correctly.  This is the only way to
@@ -3486,11 +3871,11 @@ elf_fix_symbol_flags (h, eif)
   else
     {
       /* Unfortunately, ELF_LINK_NON_ELF is only correct if the symbol
-         was first seen in a non-ELF file.  Fortunately, if the symbol
-         was first seen in an ELF file, we're probably OK unless the
-         symbol was defined in a non-ELF file.  Catch that case here.
-         FIXME: We're still in trouble if the symbol was first seen in
-         a dynamic object, and then later in a non-ELF regular object.  */
+        was first seen in a non-ELF file.  Fortunately, if the symbol
+        was first seen in an ELF file, we're probably OK unless the
+        symbol was defined in a non-ELF file.  Catch that case here.
+        FIXME: We're still in trouble if the symbol was first seen in
+        a dynamic object, and then later in a non-ELF regular object.  */
       if ((h->root.type == bfd_link_hash_defined
           || h->root.type == bfd_link_hash_defweak)
          && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
@@ -3518,16 +3903,27 @@ elf_fix_symbol_flags (h, eif)
   /* If -Bsymbolic was used (which means to bind references to global
      symbols to the definition within the shared object), and this
      symbol was defined in a regular object, then it actually doesn't
-     need a PLT entry.  Likewise, if the symbol has any kind of
-     visibility (internal, hidden, or protected), it doesn't need a
-     PLT.  */
+     need a PLT entry, and we can accomplish that by forcing it local.
+     Likewise, if the symbol has hidden or internal visibility.
+     FIXME: It might be that we also do not need a PLT for other
+     non-hidden visibilities, but we would have to tell that to the
+     backend specifically; we can't just clear PLT-related data here.  */
   if ((h->elf_link_hash_flags & ELF_LINK_HASH_NEEDS_PLT) != 0
       && eif->info->shared
-      && (eif->info->symbolic || ELF_ST_VISIBILITY (h->other))
+      && is_elf_hash_table (eif->info)
+      && (eif->info->symbolic
+         || ELF_ST_VISIBILITY (h->other) == STV_INTERNAL
+         || ELF_ST_VISIBILITY (h->other) == STV_HIDDEN)
       && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0)
     {
-      h->elf_link_hash_flags &=~ ELF_LINK_HASH_NEEDS_PLT;
-      h->plt.offset = (bfd_vma) -1;
+      struct elf_backend_data *bed;
+      boolean force_local;
+
+      bed = get_elf_backend_data (elf_hash_table (eif->info)->dynobj);
+
+      force_local = (ELF_ST_VISIBILITY (h->other) == STV_INTERNAL
+                    || ELF_ST_VISIBILITY (h->other) == STV_HIDDEN);
+      (*bed->elf_backend_hide_symbol) (eif->info, h, force_local);
     }
 
   /* If this is a weak defined symbol in a dynamic object, and we know
@@ -3550,11 +3946,12 @@ elf_fix_symbol_flags (h, eif)
       if ((weakdef->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0)
        h->weakdef = NULL;
       else
-       weakdef->elf_link_hash_flags |=
-         (h->elf_link_hash_flags
-          & (ELF_LINK_HASH_REF_REGULAR
-             | ELF_LINK_HASH_REF_REGULAR_NONWEAK
-             | ELF_LINK_NON_GOT_REF));
+       {
+         struct elf_backend_data *bed;
+
+         bed = get_elf_backend_data (elf_hash_table (eif->info)->dynobj);
+         (*bed->elf_backend_copy_indirect_symbol) (weakdef, h);
+       }
     }
 
   return true;
@@ -3573,10 +3970,24 @@ elf_adjust_dynamic_symbol (h, data)
   bfd *dynobj;
   struct elf_backend_data *bed;
 
+  if (h->root.type == bfd_link_hash_warning)
+    {
+      h->plt.offset = (bfd_vma) -1;
+      h->got.offset = (bfd_vma) -1;
+
+      /* When warning symbols are created, they **replace** the "real"
+        entry in the hash table, thus we never get to see the real
+        symbol in a hash traversal.  So look at it now.  */
+      h = (struct elf_link_hash_entry *) h->root.u.i.link;
+    }
+
   /* Ignore indirect symbols.  These are added by the versioning code.  */
   if (h->root.type == bfd_link_hash_indirect)
     return true;
 
+  if (! is_elf_hash_table (eif->info))
+    return false;
+
   /* Fix the symbol flags.  */
   if (! elf_fix_symbol_flags (h, eif))
     return false;
@@ -3686,14 +4097,45 @@ elf_export_symbol (h, data)
   if (h->root.type == bfd_link_hash_indirect)
     return true;
 
+  if (h->root.type == bfd_link_hash_warning)
+    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
   if (h->dynindx == -1
       && (h->elf_link_hash_flags
          & (ELF_LINK_HASH_DEF_REGULAR | ELF_LINK_HASH_REF_REGULAR)) != 0)
     {
-      if (! _bfd_elf_link_record_dynamic_symbol (eif->info, h))
+      struct bfd_elf_version_tree *t;
+      struct bfd_elf_version_expr *d;
+
+      for (t = eif->verdefs; t != NULL; t = t->next)
        {
-         eif->failed = true;
-         return false;
+         if (t->globals != NULL)
+           {
+             for (d = t->globals; d != NULL; d = d->next)
+               {
+                 if ((*d->match) (d, h->root.root.string))
+                   goto doit;
+               }
+           }
+
+         if (t->locals != NULL)
+           {
+             for (d = t->locals ; d != NULL; d = d->next)
+               {
+                 if ((*d->match) (d, h->root.root.string))
+                   return true;
+               }
+           }
+       }
+
+      if (!eif->verdefs)
+       {
+doit:
+         if (! _bfd_elf_link_record_dynamic_symbol (eif->info, h))
+           {
+             eif->failed = true;
+             return false;
+           }
        }
     }
 
@@ -3713,6 +4155,10 @@ elf_link_find_version_dependencies (h, data)
   struct elf_find_verdep_info *rinfo = (struct elf_find_verdep_info *) data;
   Elf_Internal_Verneed *t;
   Elf_Internal_Vernaux *a;
+  bfd_size_type amt;
+
+  if (h->root.type == bfd_link_hash_warning)
+    h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
   /* We only care about symbols defined in shared objects with version
      information.  */
@@ -3739,7 +4185,8 @@ elf_link_find_version_dependencies (h, data)
 
   if (t == NULL)
     {
-      t = (Elf_Internal_Verneed *) bfd_zalloc (rinfo->output_bfd, sizeof *t);
+      amt = sizeof *t;
+      t = (Elf_Internal_Verneed *) bfd_zalloc (rinfo->output_bfd, amt);
       if (t == NULL)
        {
          rinfo->failed = true;
@@ -3751,7 +4198,8 @@ elf_link_find_version_dependencies (h, data)
       elf_tdata (rinfo->output_bfd)->verref = t;
     }
 
-  a = (Elf_Internal_Vernaux *) bfd_zalloc (rinfo->output_bfd, sizeof *a);
+  amt = sizeof *a;
+  a = (Elf_Internal_Vernaux *) bfd_zalloc (rinfo->output_bfd, amt);
 
   /* Note that we are copying a string pointer here, and testing it
      above.  If bfd_elf_string_from_elf_section is ever changed to
@@ -3782,12 +4230,18 @@ elf_link_assign_sym_version (h, data)
      struct elf_link_hash_entry *h;
      PTR data;
 {
-  struct elf_assign_sym_version_info *sinfo =
-    (struct elf_assign_sym_version_info *) data;
-  struct bfd_link_info *info = sinfo->info;
+  struct elf_assign_sym_version_info *sinfo;
+  struct bfd_link_info *info;
   struct elf_backend_data *bed;
   struct elf_info_failed eif;
   char *p;
+  bfd_size_type amt;
+
+  sinfo = (struct elf_assign_sym_version_info *) data;
+  info = sinfo->info;
+
+  if (h->root.type == bfd_link_hash_warning)
+    h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
   /* Fix the symbol flags.  */
   eif.failed = false;
@@ -3814,7 +4268,7 @@ elf_link_assign_sym_version (h, data)
       hidden = true;
 
       /* There are two consecutive ELF_VER_CHR characters if this is
-         not a hidden symbol.  */
+        not a hidden symbol.  */
       ++p;
       if (*p == ELF_VER_CHR)
        {
@@ -3835,18 +4289,18 @@ elf_link_assign_sym_version (h, data)
        {
          if (strcmp (t->name, p) == 0)
            {
-             int len;
+             size_t len;
              char *alc;
              struct bfd_elf_version_expr *d;
 
              len = p - h->root.root.string;
-             alc = bfd_alloc (sinfo->output_bfd, len);
+             alc = bfd_malloc ((bfd_size_type) len);
              if (alc == NULL)
-               return false;
+               return false;
              strncpy (alc, h->root.root.string, len - 1);
              alc[len - 1] = '\0';
              if (alc[len - 2] == ELF_VER_CHR)
-               alc[len - 2] = '\0';
+               alc[len - 2] = '\0';
 
              h->verinfo.vertree = t;
              t->used = true;
@@ -3860,7 +4314,7 @@ elf_link_assign_sym_version (h, data)
                }
 
              /* See if there is anything to force this symbol to
-                 local scope.  */
+                local scope.  */
              if (d == NULL && t->locals != NULL)
                {
                  for (d = t->locals; d != NULL; d = d->next)
@@ -3869,13 +4323,9 @@ elf_link_assign_sym_version (h, data)
                        {
                          if (h->dynindx != -1
                              && info->shared
-                             && ! sinfo->export_dynamic)
+                             && ! info->export_dynamic)
                            {
-                             h->elf_link_hash_flags |= ELF_LINK_FORCED_LOCAL;
-                             (*bed->elf_backend_hide_symbol) (info, h);
-                             /* FIXME: The name of the symbol has
-                                already been recorded in the dynamic
-                                string table section.  */
+                             (*bed->elf_backend_hide_symbol) (info, h, true);
                            }
 
                          break;
@@ -3883,25 +4333,26 @@ elf_link_assign_sym_version (h, data)
                    }
                }
 
-             bfd_release (sinfo->output_bfd, alc);
+             free (alc);
              break;
            }
        }
 
       /* If we are building an application, we need to create a
-         version node for this version.  */
+        version node for this version.  */
       if (t == NULL && ! info->shared)
        {
          struct bfd_elf_version_tree **pp;
          int version_index;
 
          /* If we aren't going to export this symbol, we don't need
-             to worry about it.  */
+            to worry about it.  */
          if (h->dynindx == -1)
            return true;
 
+         amt = sizeof *t;
          t = ((struct bfd_elf_version_tree *)
-              bfd_alloc (sinfo->output_bfd, sizeof *t));
+              bfd_alloc (sinfo->output_bfd, amt));
          if (t == NULL)
            {
              sinfo->failed = true;
@@ -3917,6 +4368,9 @@ elf_link_assign_sym_version (h, data)
          t->used = true;
 
          version_index = 1;
+         /* Don't count anonymous version tag.  */
+         if (sinfo->verdefs != NULL && sinfo->verdefs->vernum == 0)
+           version_index = 0;
          for (pp = &sinfo->verdefs; *pp != NULL; pp = &(*pp)->next)
            ++version_index;
          t->vernum = version_index;
@@ -3928,7 +4382,7 @@ elf_link_assign_sym_version (h, data)
       else if (t == NULL)
        {
          /* We could not find the version for a symbol when
-             generating a shared archive.  Return an error.  */
+            generating a shared archive.  Return an error.  */
          (*_bfd_error_handler)
            (_("%s: undefined versioned symbol name %s"),
             bfd_get_filename (sinfo->output_bfd), h->root.root.string);
@@ -3950,8 +4404,8 @@ elf_link_assign_sym_version (h, data)
       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.  */
+        symbol is supposed to be local, then don't actually register
+        it.  */
       deflt = NULL;
       for (t = sinfo->verdefs; t != NULL; t = t->next)
        {
@@ -3981,13 +4435,9 @@ elf_link_assign_sym_version (h, data)
                      h->verinfo.vertree = t;
                      if (h->dynindx != -1
                          && info->shared
-                         && ! sinfo->export_dynamic)
+                         && ! info->export_dynamic)
                        {
-                         h->elf_link_hash_flags |= ELF_LINK_FORCED_LOCAL;
-                         (*bed->elf_backend_hide_symbol) (info, h);
-                         /* FIXME: The name of the symbol has already
-                            been recorded in the dynamic string table
-                            section.  */
+                         (*bed->elf_backend_hide_symbol) (info, h, true);
                        }
                      break;
                    }
@@ -4003,12 +4453,9 @@ elf_link_assign_sym_version (h, data)
          h->verinfo.vertree = deflt;
          if (h->dynindx != -1
              && info->shared
-             && ! sinfo->export_dynamic)
+             && ! info->export_dynamic)
            {
-             h->elf_link_hash_flags |= ELF_LINK_FORCED_LOCAL;
-             (*bed->elf_backend_hide_symbol) (info, h);
-             /* FIXME: The name of the symbol has already been
-                recorded in the dynamic string table section.  */
+             (*bed->elf_backend_hide_symbol) (info, h, true);
            }
        }
     }
@@ -4043,6 +4490,8 @@ struct elf_final_link_info
   /* Buffer large enough to hold external local symbols of any input
      BFD.  */
   Elf_External_Sym *external_syms;
+  /* And a buffer for symbol section indices.  */
+  Elf_External_Sym_Shndx *locsym_shndx;
   /* Buffer large enough to hold internal local symbols of any input
      BFD.  */
   Elf_Internal_Sym *internal_syms;
@@ -4054,6 +4503,8 @@ struct elf_final_link_info
   asection **sections;
   /* Buffer to hold swapped out symbols.  */
   Elf_External_Sym *symbuf;
+  /* And one for symbol section indices.  */
+  Elf_External_Sym_Shndx *symshndxbuf;
   /* Number of swapped out symbols in buffer.  */
   size_t symbuf_count;
   /* Number of symbols which fit in symbuf.  */
@@ -4067,6 +4518,8 @@ static boolean elf_link_flush_output_syms
   PARAMS ((struct elf_final_link_info *));
 static boolean elf_link_output_extsym
   PARAMS ((struct elf_link_hash_entry *, PTR));
+static boolean elf_link_sec_merge_syms
+  PARAMS ((struct elf_link_hash_entry *, PTR));
 static boolean elf_link_input_bfd
   PARAMS ((struct elf_final_link_info *, bfd *));
 static boolean elf_reloc_link_order
@@ -4091,8 +4544,8 @@ elf_link_size_reloc_section (abfd, rel_hdr, o)
      Elf_Internal_Shdr *rel_hdr;
      asection *o;
 {
-  register struct elf_link_hash_entry **p, **pend;
-  unsigned reloc_count;
+  bfd_size_type reloc_count;
+  bfd_size_type num_rel_hashes;
 
   /* Figure out how many relocations there will be.  */
   if (rel_hdr == &elf_section_data (o)->rel_hdr)
@@ -4100,6 +4553,10 @@ elf_link_size_reloc_section (abfd, rel_hdr, o)
   else
     reloc_count = elf_section_data (o)->rel_count2;
 
+  num_rel_hashes = o->reloc_count;
+  if (num_rel_hashes < reloc_count)
+    num_rel_hashes = reloc_count;
+
   /* That allows us to calculate the size of the section.  */
   rel_hdr->sh_size = rel_hdr->sh_entsize * reloc_count;
 
@@ -4113,18 +4570,18 @@ elf_link_size_reloc_section (abfd, rel_hdr, o)
 
   /* We only allocate one set of hash entries, so we only do it the
      first time we are called.  */
-  if (elf_section_data (o)->rel_hashes == NULL)
+  if (elf_section_data (o)->rel_hashes == NULL
+      && num_rel_hashes)
     {
+      struct elf_link_hash_entry **p;
+
       p = ((struct elf_link_hash_entry **)
-          bfd_malloc (o->reloc_count
-                      * sizeof (struct elf_link_hash_entry *)));
-      if (p == NULL && o->reloc_count != 0)
+          bfd_zmalloc (num_rel_hashes
+                       * sizeof (struct elf_link_hash_entry *)));
+      if (p == NULL)
        return false;
 
       elf_section_data (o)->rel_hashes = p;
-      pend = p + o->reloc_count;
-      for (; p < pend; p++)
-       *p = NULL;
     }
 
   return true;
@@ -4144,6 +4601,24 @@ elf_link_adjust_relocs (abfd, rel_hdr, count, rel_hash)
 {
   unsigned int i;
   struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  Elf_Internal_Rel *irel;
+  Elf_Internal_Rela *irela;
+  bfd_size_type amt = sizeof (Elf_Internal_Rel) * bed->s->int_rels_per_ext_rel;
+
+  irel = (Elf_Internal_Rel *) bfd_zmalloc (amt);
+  if (irel == NULL)
+    {
+      (*_bfd_error_handler) (_("Error: out of memory"));
+      abort ();
+    }
+
+  amt = sizeof (Elf_Internal_Rela) * bed->s->int_rels_per_ext_rel;
+  irela = (Elf_Internal_Rela *) bfd_zmalloc (amt);
+  if (irela == NULL)
+    {
+      (*_bfd_error_handler) (_("Error: out of memory"));
+      abort ();
+    }
 
   for (i = 0; i < count; i++, rel_hash++)
     {
@@ -4155,41 +4630,258 @@ elf_link_adjust_relocs (abfd, rel_hdr, count, rel_hash)
       if (rel_hdr->sh_entsize == sizeof (Elf_External_Rel))
        {
          Elf_External_Rel *erel;
-         Elf_Internal_Rel irel;
+         unsigned int j;
 
          erel = (Elf_External_Rel *) rel_hdr->contents + i;
          if (bed->s->swap_reloc_in)
-           (*bed->s->swap_reloc_in) (abfd, (bfd_byte *) erel, &irel);
+           (*bed->s->swap_reloc_in) (abfd, (bfd_byte *) erel, irel);
          else
-           elf_swap_reloc_in (abfd, erel, &irel);
-         irel.r_info = ELF_R_INFO ((*rel_hash)->indx,
-                                   ELF_R_TYPE (irel.r_info));
+           elf_swap_reloc_in (abfd, erel, irel);
+
+         for (j = 0; j < bed->s->int_rels_per_ext_rel; j++)
+           irel[j].r_info = ELF_R_INFO ((*rel_hash)->indx,
+                                        ELF_R_TYPE (irel[j].r_info));
+
          if (bed->s->swap_reloc_out)
-           (*bed->s->swap_reloc_out) (abfd, &irel, (bfd_byte *) erel);
+           (*bed->s->swap_reloc_out) (abfd, irel, (bfd_byte *) erel);
          else
-           elf_swap_reloc_out (abfd, &irel, erel);
+           elf_swap_reloc_out (abfd, irel, erel);
        }
       else
        {
          Elf_External_Rela *erela;
-         Elf_Internal_Rela irela;
+         unsigned int j;
 
          BFD_ASSERT (rel_hdr->sh_entsize
                      == sizeof (Elf_External_Rela));
 
          erela = (Elf_External_Rela *) rel_hdr->contents + i;
          if (bed->s->swap_reloca_in)
-           (*bed->s->swap_reloca_in) (abfd, (bfd_byte *) erela, &irela);
+           (*bed->s->swap_reloca_in) (abfd, (bfd_byte *) erela, irela);
          else
-           elf_swap_reloca_in (abfd, erela, &irela);
-         irela.r_info = ELF_R_INFO ((*rel_hash)->indx,
-                                    ELF_R_TYPE (irela.r_info));
+           elf_swap_reloca_in (abfd, erela, irela);
+
+         for (j = 0; j < bed->s->int_rels_per_ext_rel; j++)
+           irela[j].r_info = ELF_R_INFO ((*rel_hash)->indx,
+                                      ELF_R_TYPE (irela[j].r_info));
+
          if (bed->s->swap_reloca_out)
-           (*bed->s->swap_reloca_out) (abfd, &irela, (bfd_byte *) erela);
+           (*bed->s->swap_reloca_out) (abfd, irela, (bfd_byte *) erela);
          else
-           elf_swap_reloca_out (abfd, &irela, erela);
+           elf_swap_reloca_out (abfd, irela, erela);
        }
     }
+
+  free (irel);
+  free (irela);
+}
+
+struct elf_link_sort_rela {
+  bfd_vma offset;
+  enum elf_reloc_type_class type;
+  union {
+    Elf_Internal_Rel rel;
+    Elf_Internal_Rela rela;
+  } u;
+};
+
+static int
+elf_link_sort_cmp1 (A, B)
+     const PTR A;
+     const PTR B;
+{
+  struct elf_link_sort_rela *a = (struct elf_link_sort_rela *) A;
+  struct elf_link_sort_rela *b = (struct elf_link_sort_rela *) B;
+  int relativea, relativeb;
+
+  relativea = a->type == reloc_class_relative;
+  relativeb = b->type == reloc_class_relative;
+
+  if (relativea < relativeb)
+    return 1;
+  if (relativea > relativeb)
+    return -1;
+  if (ELF_R_SYM (a->u.rel.r_info) < ELF_R_SYM (b->u.rel.r_info))
+    return -1;
+  if (ELF_R_SYM (a->u.rel.r_info) > ELF_R_SYM (b->u.rel.r_info))
+    return 1;
+  if (a->u.rel.r_offset < b->u.rel.r_offset)
+    return -1;
+  if (a->u.rel.r_offset > b->u.rel.r_offset)
+    return 1;
+  return 0;
+}
+
+static int
+elf_link_sort_cmp2 (A, B)
+     const PTR A;
+     const PTR B;
+{
+  struct elf_link_sort_rela *a = (struct elf_link_sort_rela *) A;
+  struct elf_link_sort_rela *b = (struct elf_link_sort_rela *) B;
+  int copya, copyb;
+
+  if (a->offset < b->offset)
+    return -1;
+  if (a->offset > b->offset)
+    return 1;
+  copya = (a->type == reloc_class_copy) * 2 + (a->type == reloc_class_plt);
+  copyb = (b->type == reloc_class_copy) * 2 + (b->type == reloc_class_plt);
+  if (copya < copyb)
+    return -1;
+  if (copya > copyb)
+    return 1;
+  if (a->u.rel.r_offset < b->u.rel.r_offset)
+    return -1;
+  if (a->u.rel.r_offset > b->u.rel.r_offset)
+    return 1;
+  return 0;
+}
+
+static size_t
+elf_link_sort_relocs (abfd, info, psec)
+     bfd *abfd;
+     struct bfd_link_info *info;
+     asection **psec;
+{
+  bfd *dynobj = elf_hash_table (info)->dynobj;
+  asection *reldyn, *o;
+  boolean rel = false;
+  bfd_size_type count, size;
+  size_t i, j, ret;
+  struct elf_link_sort_rela *rela;
+  struct elf_backend_data *bed = get_elf_backend_data (abfd);
+
+  reldyn = bfd_get_section_by_name (abfd, ".rela.dyn");
+  if (reldyn == NULL || reldyn->_raw_size == 0)
+    {
+      reldyn = bfd_get_section_by_name (abfd, ".rel.dyn");
+      if (reldyn == NULL || reldyn->_raw_size == 0)
+       return 0;
+      rel = true;
+      count = reldyn->_raw_size / sizeof (Elf_External_Rel);
+    }
+  else
+    count = reldyn->_raw_size / sizeof (Elf_External_Rela);
+
+  size = 0;
+  for (o = dynobj->sections; o != NULL; o = o->next)
+    if ((o->flags & (SEC_HAS_CONTENTS|SEC_LINKER_CREATED))
+       == (SEC_HAS_CONTENTS|SEC_LINKER_CREATED)
+       && o->output_section == reldyn)
+      size += o->_raw_size;
+
+  if (size != reldyn->_raw_size)
+    return 0;
+
+  rela = (struct elf_link_sort_rela *) bfd_zmalloc (sizeof (*rela) * count);
+  if (rela == NULL)
+    {
+      (*info->callbacks->warning)
+       (info, _("Not enough memory to sort relocations"), 0, abfd, 0,
+        (bfd_vma) 0);
+      return 0;
+    }
+
+  for (o = dynobj->sections; o != NULL; o = o->next)
+    if ((o->flags & (SEC_HAS_CONTENTS|SEC_LINKER_CREATED))
+       == (SEC_HAS_CONTENTS|SEC_LINKER_CREATED)
+       && o->output_section == reldyn)
+      {
+       if (rel)
+         {
+           Elf_External_Rel *erel, *erelend;
+           struct elf_link_sort_rela *s;
+
+           erel = (Elf_External_Rel *) o->contents;
+           erelend = (Elf_External_Rel *) (o->contents + o->_raw_size);
+           s = rela + o->output_offset / sizeof (Elf_External_Rel);
+           for (; erel < erelend; erel++, s++)
+             {
+               if (bed->s->swap_reloc_in)
+                 (*bed->s->swap_reloc_in) (abfd, (bfd_byte *) erel, &s->u.rel);
+               else
+                 elf_swap_reloc_in (abfd, erel, &s->u.rel);
+
+               s->type = (*bed->elf_backend_reloc_type_class) (&s->u.rela);
+             }
+         }
+       else
+         {
+           Elf_External_Rela *erela, *erelaend;
+           struct elf_link_sort_rela *s;
+
+           erela = (Elf_External_Rela *) o->contents;
+           erelaend = (Elf_External_Rela *) (o->contents + o->_raw_size);
+           s = rela + o->output_offset / sizeof (Elf_External_Rela);
+           for (; erela < erelaend; erela++, s++)
+             {
+               if (bed->s->swap_reloca_in)
+                 (*bed->s->swap_reloca_in) (dynobj, (bfd_byte *) erela,
+                                            &s->u.rela);
+               else
+                 elf_swap_reloca_in (dynobj, erela, &s->u.rela);
+
+               s->type = (*bed->elf_backend_reloc_type_class) (&s->u.rela);
+             }
+         }
+      }
+
+  qsort (rela, (size_t) count, sizeof (*rela), elf_link_sort_cmp1);
+  for (ret = 0; ret < count && rela[ret].type == reloc_class_relative; ret++)
+    ;
+  for (i = ret, j = ret; i < count; i++)
+    {
+      if (ELF_R_SYM (rela[i].u.rel.r_info) != ELF_R_SYM (rela[j].u.rel.r_info))
+       j = i;
+      rela[i].offset = rela[j].u.rel.r_offset;
+    }
+  qsort (rela + ret, (size_t) count - ret, sizeof (*rela), elf_link_sort_cmp2);
+
+  for (o = dynobj->sections; o != NULL; o = o->next)
+    if ((o->flags & (SEC_HAS_CONTENTS|SEC_LINKER_CREATED))
+       == (SEC_HAS_CONTENTS|SEC_LINKER_CREATED)
+       && o->output_section == reldyn)
+      {
+       if (rel)
+         {
+           Elf_External_Rel *erel, *erelend;
+           struct elf_link_sort_rela *s;
+
+           erel = (Elf_External_Rel *) o->contents;
+           erelend = (Elf_External_Rel *) (o->contents + o->_raw_size);
+           s = rela + o->output_offset / sizeof (Elf_External_Rel);
+           for (; erel < erelend; erel++, s++)
+             {
+               if (bed->s->swap_reloc_out)
+                 (*bed->s->swap_reloc_out) (abfd, &s->u.rel,
+                                            (bfd_byte *) erel);
+               else
+                 elf_swap_reloc_out (abfd, &s->u.rel, erel);
+             }
+         }
+       else
+         {
+           Elf_External_Rela *erela, *erelaend;
+           struct elf_link_sort_rela *s;
+
+           erela = (Elf_External_Rela *) o->contents;
+           erelaend = (Elf_External_Rela *) (o->contents + o->_raw_size);
+           s = rela + o->output_offset / sizeof (Elf_External_Rela);
+           for (; erela < erelaend; erela++, s++)
+             {
+               if (bed->s->swap_reloca_out)
+                 (*bed->s->swap_reloca_out) (dynobj, &s->u.rela,
+                                             (bfd_byte *) erela);
+               else
+                 elf_swap_reloca_out (dynobj, &s->u.rela, erela);
+             }
+         }
+      }
+
+  free (rela);
+  *psec = reldyn;
+  return ret;
 }
 
 /* Do the final step of an ELF link.  */
@@ -4200,15 +4892,17 @@ elf_bfd_final_link (abfd, info)
      struct bfd_link_info *info;
 {
   boolean dynamic;
+  boolean emit_relocs;
   bfd *dynobj;
   struct elf_final_link_info finfo;
   register asection *o;
   register struct bfd_link_order *p;
   register bfd *sub;
-  size_t max_contents_size;
-  size_t max_external_reloc_size;
-  size_t max_internal_reloc_count;
-  size_t max_sym_count;
+  bfd_size_type max_contents_size;
+  bfd_size_type max_external_reloc_size;
+  bfd_size_type max_internal_reloc_count;
+  bfd_size_type max_sym_count;
+  bfd_size_type max_sym_shndx_count;
   file_ptr off;
   Elf_Internal_Sym elfsym;
   unsigned int i;
@@ -4216,6 +4910,13 @@ elf_bfd_final_link (abfd, info)
   Elf_Internal_Shdr *symstrtab_hdr;
   struct elf_backend_data *bed = get_elf_backend_data (abfd);
   struct elf_outext_info eoinfo;
+  boolean merged;
+  size_t relativecount = 0;
+  asection *reldyn = 0;
+  bfd_size_type amt;
+
+  if (! is_elf_hash_table (info))
+    return false;
 
   if (info->shared)
     abfd->flags |= DYNAMIC;
@@ -4223,6 +4924,10 @@ elf_bfd_final_link (abfd, info)
   dynamic = elf_hash_table (info)->dynamic_sections_created;
   dynobj = elf_hash_table (info)->dynobj;
 
+  emit_relocs = (info->relocateable
+                || info->emitrelocations
+                || bed->elf_backend_emit_relocs);
+
   finfo.info = info;
   finfo.output_bfd = abfd;
   finfo.symstrtab = elf_stringtab_init ();
@@ -4248,10 +4953,12 @@ elf_bfd_final_link (abfd, info)
   finfo.external_relocs = NULL;
   finfo.internal_relocs = NULL;
   finfo.external_syms = NULL;
+  finfo.locsym_shndx = NULL;
   finfo.internal_syms = NULL;
   finfo.indices = NULL;
   finfo.sections = NULL;
   finfo.symbuf = NULL;
+  finfo.symshndxbuf = NULL;
   finfo.symbuf_count = 0;
 
   /* Count up the number of relocations we will output for each output
@@ -4261,6 +4968,8 @@ elf_bfd_final_link (abfd, info)
   max_external_reloc_size = 0;
   max_internal_reloc_count = 0;
   max_sym_count = 0;
+  max_sym_shndx_count = 0;
+  merged = false;
   for (o = abfd->sections; o != (asection *) NULL; o = o->next)
     {
       o->reloc_count = 0;
@@ -4282,8 +4991,25 @@ elf_bfd_final_link (abfd, info)
                 the linker has decided to not include.  */
              sec->linker_mark = true;
 
+             if (sec->flags & SEC_MERGE)
+               merged = true;
+
              if (info->relocateable || info->emitrelocations)
                o->reloc_count += sec->reloc_count;
+             else if (bed->elf_backend_count_relocs)
+               {
+                 Elf_Internal_Rela * relocs;
+
+                 relocs = (NAME(_bfd_elf,link_read_relocs)
+                           (abfd, sec, (PTR) NULL,
+                            (Elf_Internal_Rela *) NULL, info->keep_memory));
+
+                 o->reloc_count
+                   += (*bed->elf_backend_count_relocs) (sec, relocs);
+
+                 if (!info->keep_memory)
+                   free (relocs);
+               }
 
              if (sec->_raw_size > max_contents_size)
                max_contents_size = sec->_raw_size;
@@ -4306,6 +5032,10 @@ elf_bfd_final_link (abfd, info)
                  if (sym_count > max_sym_count)
                    max_sym_count = sym_count;
 
+                 if (sym_count > max_sym_shndx_count
+                     && elf_symtab_shndx (sec->owner) != 0)
+                   max_sym_shndx_count = sym_count;
+
                  if ((sec->flags & SEC_RELOC) != 0)
                    {
                      size_t ext_size;
@@ -4339,6 +5069,10 @@ elf_bfd_final_link (abfd, info)
        o->vma = 0;
     }
 
+  if (! info->relocateable && merged)
+    elf_link_hash_traverse (elf_hash_table (info),
+                           elf_link_sec_merge_syms, (PTR) abfd);
+
   /* Figure out the file positions for everything but the symbol table
      and the relocs.  We set symcount to force assign_section_numbers
      to create a symbol table.  */
@@ -4350,7 +5084,7 @@ elf_bfd_final_link (abfd, info)
   /* Figure out how many relocations we will have in each section.
      Just using RELOC_COUNT isn't good enough since that doesn't
      maintain a separate value for REL vs. RELA relocations.  */
-  if (info->relocateable || info->emitrelocations)
+  if (emit_relocs)
     for (sub = info->input_bfds; sub != NULL; sub = sub->link_next)
       for (o = sub->sections; o != NULL; o = o->next)
        {
@@ -4373,10 +5107,20 @@ elf_bfd_final_link (abfd, info)
                = elf_section_data (output_section);
              unsigned int *rel_count;
              unsigned int *rel_count2;
+             bfd_size_type entsize;
+             bfd_size_type entsize2;
 
-             /* We must be careful to add the relocation froms the
+             /* We must be careful to add the relocations from the
                 input section to the right output count.  */
-             if (esdi->rel_hdr.sh_entsize == esdo->rel_hdr.sh_entsize)
+             entsize = esdi->rel_hdr.sh_entsize;
+             entsize2 = esdi->rel_hdr2 ? esdi->rel_hdr2->sh_entsize : 0;
+             BFD_ASSERT ((entsize == sizeof (Elf_External_Rel)
+                          || entsize == sizeof (Elf_External_Rela))
+                         && entsize2 != entsize
+                         && (entsize2 == 0
+                             || entsize2 == sizeof (Elf_External_Rel)
+                             || entsize2 == sizeof (Elf_External_Rela)));
+             if (entsize == esdo->rel_hdr.sh_entsize)
                {
                  rel_count = &esdo->rel_count;
                  rel_count2 = &esdo->rel_count2;
@@ -4387,11 +5131,10 @@ elf_bfd_final_link (abfd, info)
                  rel_count2 = &esdo->rel_count;
                }
 
-             *rel_count += (esdi->rel_hdr.sh_size
-                            / esdi->rel_hdr.sh_entsize);
+             *rel_count += NUM_SHDR_ENTRIES (& esdi->rel_hdr);
              if (esdi->rel_hdr2)
-               *rel_count2 += (esdi->rel_hdr2->sh_size
-                               / esdi->rel_hdr2->sh_entsize);
+               *rel_count2 += NUM_SHDR_ENTRIES (esdi->rel_hdr2);
+             output_section->flags |= SEC_RELOC;
            }
        }
 
@@ -4451,14 +5194,24 @@ elf_bfd_final_link (abfd, info)
     finfo.symbuf_size = 20;
   else
     finfo.symbuf_size = max_sym_count;
-  finfo.symbuf = ((Elf_External_Sym *)
-                 bfd_malloc (finfo.symbuf_size * sizeof (Elf_External_Sym)));
+  amt = finfo.symbuf_size;
+  amt *= sizeof (Elf_External_Sym);
+  finfo.symbuf = (Elf_External_Sym *) bfd_malloc (amt);
   if (finfo.symbuf == NULL)
     goto error_return;
+  if (elf_numsections (abfd) > SHN_LORESERVE)
+    {
+      amt = finfo.symbuf_size;
+      amt *= sizeof (Elf_External_Sym_Shndx);
+      finfo.symshndxbuf = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
+      if (finfo.symshndxbuf == NULL)
+       goto error_return;
+    }
 
   /* Start writing out the symbol table.  The first symbol is always a
      dummy symbol.  */
-  if (info->strip != strip_all || info->relocateable || info->emitrelocations)
+  if (info->strip != strip_all
+      || emit_relocs)
     {
       elfsym.st_value = 0;
       elfsym.st_size = 0;
@@ -4491,12 +5244,13 @@ elf_bfd_final_link (abfd, info)
      symbols have no names.  We store the index of each one in the
      index field of the section, so that we can find it again when
      outputting relocs.  */
-  if (info->strip != strip_all || info->relocateable || info->emitrelocations)
+  if (info->strip != strip_all
+      || emit_relocs)
     {
       elfsym.st_size = 0;
       elfsym.st_info = ELF_ST_INFO (STB_LOCAL, STT_SECTION);
       elfsym.st_other = 0;
-      for (i = 1; i < elf_elfheader (abfd)->e_shnum; i++)
+      for (i = 1; i < elf_numsections (abfd); i++)
        {
          o = section_from_elf_index (abfd, i);
          if (o != NULL)
@@ -4509,34 +5263,66 @@ elf_bfd_final_link (abfd, info)
          if (! elf_link_output_sym (&finfo, (const char *) NULL,
                                     &elfsym, o))
            goto error_return;
+         if (i == SHN_LORESERVE)
+           i += SHN_HIRESERVE + 1 - SHN_LORESERVE;
        }
     }
 
   /* Allocate some memory to hold information read in from the input
      files.  */
-  finfo.contents = (bfd_byte *) bfd_malloc (max_contents_size);
-  finfo.external_relocs = (PTR) bfd_malloc (max_external_reloc_size);
-  finfo.internal_relocs = ((Elf_Internal_Rela *)
-                          bfd_malloc (max_internal_reloc_count
-                                      * sizeof (Elf_Internal_Rela)
-                                      * bed->s->int_rels_per_ext_rel));
-  finfo.external_syms = ((Elf_External_Sym *)
-                        bfd_malloc (max_sym_count
-                                    * sizeof (Elf_External_Sym)));
-  finfo.internal_syms = ((Elf_Internal_Sym *)
-                        bfd_malloc (max_sym_count
-                                    * sizeof (Elf_Internal_Sym)));
-  finfo.indices = (long *) bfd_malloc (max_sym_count * sizeof (long));
-  finfo.sections = ((asection **)
-                   bfd_malloc (max_sym_count * sizeof (asection *)));
-  if ((finfo.contents == NULL && max_contents_size != 0)
-      || (finfo.external_relocs == NULL && max_external_reloc_size != 0)
-      || (finfo.internal_relocs == NULL && max_internal_reloc_count != 0)
-      || (finfo.external_syms == NULL && max_sym_count != 0)
-      || (finfo.internal_syms == NULL && max_sym_count != 0)
-      || (finfo.indices == NULL && max_sym_count != 0)
-      || (finfo.sections == NULL && max_sym_count != 0))
-    goto error_return;
+  if (max_contents_size != 0)
+    {
+      finfo.contents = (bfd_byte *) bfd_malloc (max_contents_size);
+      if (finfo.contents == NULL)
+       goto error_return;
+    }
+
+  if (max_external_reloc_size != 0)
+    {
+      finfo.external_relocs = (PTR) bfd_malloc (max_external_reloc_size);
+      if (finfo.external_relocs == NULL)
+       goto error_return;
+    }
+
+  if (max_internal_reloc_count != 0)
+    {
+      amt = max_internal_reloc_count * bed->s->int_rels_per_ext_rel;
+      amt *= sizeof (Elf_Internal_Rela);
+      finfo.internal_relocs = (Elf_Internal_Rela *) bfd_malloc (amt);
+      if (finfo.internal_relocs == NULL)
+       goto error_return;
+    }
+
+  if (max_sym_count != 0)
+    {
+      amt = max_sym_count * sizeof (Elf_External_Sym);
+      finfo.external_syms = (Elf_External_Sym *) bfd_malloc (amt);
+      if (finfo.external_syms == NULL)
+       goto error_return;
+
+      amt = max_sym_count * sizeof (Elf_Internal_Sym);
+      finfo.internal_syms = (Elf_Internal_Sym *) bfd_malloc (amt);
+      if (finfo.internal_syms == NULL)
+       goto error_return;
+
+      amt = max_sym_count * sizeof (long);
+      finfo.indices = (long *) bfd_malloc (amt);
+      if (finfo.indices == NULL)
+       goto error_return;
+
+      amt = max_sym_count * sizeof (asection *);
+      finfo.sections = (asection **) bfd_malloc (amt);
+      if (finfo.sections == NULL)
+       goto error_return;
+    }
+
+  if (max_sym_shndx_count != 0)
+    {
+      amt = max_sym_shndx_count * sizeof (Elf_External_Sym_Shndx);
+      finfo.locsym_shndx = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
+      if (finfo.locsym_shndx == NULL)
+       goto error_return;
+    }
 
   /* Since ELF permits relocations to be against local symbols, we
      must have the local symbols available when we do the relocations.
@@ -4565,10 +5351,10 @@ elf_bfd_final_link (abfd, info)
       for (p = o->link_order_head; p != NULL; p = p->next)
        {
          if (p->type == bfd_indirect_link_order
-             && (bfd_get_flavour (p->u.indirect.section->owner)
-                 == bfd_target_elf_flavour))
+             && (bfd_get_flavour ((sub = p->u.indirect.section->owner))
+                 == bfd_target_elf_flavour)
+             && elf_elfheader (sub)->e_ident[EI_CLASS] == bed->s->elfclass)
            {
-             sub = p->u.indirect.section->owner;
              if (! sub->output_has_begun)
                {
                  if (! elf_link_input_bfd (&finfo, sub))
@@ -4590,28 +5376,25 @@ elf_bfd_final_link (abfd, info)
        }
     }
 
+  /* Output any global symbols that got converted to local in a
+     version script or due to symbol visibility.  We do this in a
+     separate step since ELF requires all local symbols to appear
+     prior to any global symbols.  FIXME: We should only do this if
+     some global symbols were, in fact, converted to become local.
+     FIXME: Will this work correctly with the Irix 5 linker?  */
+  eoinfo.failed = false;
+  eoinfo.finfo = &finfo;
+  eoinfo.localsyms = true;
+  elf_link_hash_traverse (elf_hash_table (info), elf_link_output_extsym,
+                         (PTR) &eoinfo);
+  if (eoinfo.failed)
+    return false;
+
   /* That wrote out all the local symbols.  Finish up the symbol table
      with the global symbols. Even if we want to strip everything we
      can, we still need to deal with those global symbols that got
      converted to local in a version script.  */
 
-  if (info->shared)
-    {
-      /* Output any global symbols that got converted to local in a
-         version script.  We do this in a separate step since ELF
-         requires all local symbols to appear prior to any global
-         symbols.  FIXME: We should only do this if some global
-         symbols were, in fact, converted to become local.  FIXME:
-         Will this work correctly with the Irix 5 linker?  */
-      eoinfo.failed = false;
-      eoinfo.finfo = &finfo;
-      eoinfo.localsyms = true;
-      elf_link_hash_traverse (elf_hash_table (info), elf_link_output_extsym,
-                             (PTR) &eoinfo);
-      if (eoinfo.failed)
-       return false;
-    }
-
   /* The sh_info field records the index of the first non local symbol.  */
   symtab_hdr->sh_info = bfd_get_symcount (abfd);
 
@@ -4620,7 +5403,7 @@ elf_bfd_final_link (abfd, info)
     {
       Elf_Internal_Sym sym;
       Elf_External_Sym *dynsym =
-       (Elf_External_Sym *)finfo.dynsym_sec->contents;
+       (Elf_External_Sym *) finfo.dynsym_sec->contents;
       long last_local = 0;
 
       /* Write out the section symbols for the output sections.  */
@@ -4636,13 +5419,14 @@ elf_bfd_final_link (abfd, info)
          for (s = abfd->sections; s != NULL; s = s->next)
            {
              int indx;
+             Elf_External_Sym *dest;
+
              indx = elf_section_data (s)->this_idx;
              BFD_ASSERT (indx > 0);
              sym.st_shndx = indx;
              sym.st_value = s->vma;
-
-             elf_swap_symbol_out (abfd, &sym,
-                                  dynsym + elf_section_data (s)->dynindx);
+             dest = dynsym + elf_section_data (s)->dynindx;
+             elf_swap_symbol_out (abfd, &sym, (PTR) dest, (PTR) 0);
            }
 
          last_local = bfd_count_sections (abfd);
@@ -4655,16 +5439,19 @@ elf_bfd_final_link (abfd, info)
          for (e = elf_hash_table (info)->dynlocal; e ; e = e->next)
            {
              asection *s;
+             Elf_External_Sym *dest;
 
              sym.st_size = e->isym.st_size;
              sym.st_other = e->isym.st_other;
 
              /* Copy the internal symbol as is.
                 Note that we saved a word of storage and overwrote
-                 the original st_name with the dynstr_index.  */
-              sym = e->isym;
+                the original st_name with the dynstr_index.  */
+             sym = e->isym;
 
-             if (e->isym.st_shndx > 0 && e->isym.st_shndx < SHN_LORESERVE)
+             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);
@@ -4679,7 +5466,8 @@ elf_bfd_final_link (abfd, info)
              if (last_local < e->dynindx)
                last_local = e->dynindx;
 
-             elf_swap_symbol_out (abfd, &sym, dynsym + e->dynindx);
+             dest = dynsym + e->dynindx;
+             elf_swap_symbol_out (abfd, &sym, (PTR) dest, (PTR) 0);
            }
        }
 
@@ -4700,11 +5488,12 @@ elf_bfd_final_link (abfd, info)
      table, do it now.  */
   if (bed->elf_backend_output_arch_syms)
     {
-      if (! (*bed->elf_backend_output_arch_syms)
-             (abfd, info, (PTR) &finfo,
-              (boolean (*) PARAMS ((PTR, const char *,
-                           Elf_Internal_Sym *, asection *)))
-              elf_link_output_sym))
+      typedef boolean (*out_sym_func) PARAMS ((PTR, const char *,
+                                              Elf_Internal_Sym *,
+                                              asection *));
+
+      if (! ((*bed->elf_backend_output_arch_syms)
+            (abfd, info, (PTR) &finfo, (out_sym_func) elf_link_output_sym)))
        return false;
     }
 
@@ -4759,6 +5548,9 @@ elf_bfd_final_link (abfd, info)
       o->reloc_count = 0;
     }
 
+  if (dynamic && info->combreloc && dynobj != NULL)
+    relativecount = elf_link_sort_relocs (abfd, info, &reldyn);
+
   /* If we are linking against a dynamic object, or generating a
      shared library, finish up the dynamic linking information.  */
   if (dynamic)
@@ -4783,6 +5575,23 @@ elf_bfd_final_link (abfd, info)
            {
            default:
              break;
+           case DT_NULL:
+             if (relativecount > 0 && dyncon + 1 < dynconend)
+               {
+                 switch (elf_section_data (reldyn)->this_hdr.sh_type)
+                   {
+                   case SHT_REL: dyn.d_tag = DT_RELCOUNT; break;
+                   case SHT_RELA: dyn.d_tag = DT_RELACOUNT; break;
+                   default: break;
+                   }
+                 if (dyn.d_tag != DT_NULL)
+                   {
+                     dyn.d_un.d_val = relativecount;
+                     elf_swap_dyn_out (dynobj, &dyn, dyncon);
+                     relativecount = 0;
+                   }
+               }
+             break;
            case DT_INIT:
              name = info->init_function;
              goto get_sym;
@@ -4815,6 +5624,34 @@ elf_bfd_final_link (abfd, info)
              }
              break;
 
+           case DT_PREINIT_ARRAYSZ:
+             name = ".preinit_array";
+             goto get_size;
+           case DT_INIT_ARRAYSZ:
+             name = ".init_array";
+             goto get_size;
+           case DT_FINI_ARRAYSZ:
+             name = ".fini_array";
+           get_size:
+             o = bfd_get_section_by_name (abfd, name);
+             BFD_ASSERT (o != NULL);
+             if (o->_raw_size == 0)
+               (*_bfd_error_handler)
+                 (_("warning: %s section has zero size"), name);
+             dyn.d_un.d_val = o->_raw_size;
+             elf_swap_dyn_out (dynobj, &dyn, dyncon);
+             break;
+
+           case DT_PREINIT_ARRAY:
+             name = ".preinit_array";
+             goto get_vma;
+           case DT_INIT_ARRAY:
+             name = ".init_array";
+             goto get_vma;
+           case DT_FINI_ARRAY:
+             name = ".fini_array";
+             goto get_vma;
+
            case DT_HASH:
              name = ".hash";
              goto get_vma;
@@ -4848,7 +5685,7 @@ elf_bfd_final_link (abfd, info)
              else
                type = SHT_RELA;
              dyn.d_un.d_val = 0;
-             for (i = 1; i < elf_elfheader (abfd)->e_shnum; i++)
+             for (i = 1; i < elf_numsections (abfd); i++)
                {
                  Elf_Internal_Shdr *hdr;
 
@@ -4887,7 +5724,7 @@ elf_bfd_final_link (abfd, info)
          if ((o->flags & SEC_LINKER_CREATED) == 0)
            {
              /* At this point, we are only interested in sections
-                 created by elf_link_create_dynamic_sections.  */
+                created by elf_link_create_dynamic_sections.  */
              continue;
            }
          if ((elf_section_data (o->output_section)->this_hdr.sh_type
@@ -4895,20 +5732,19 @@ elf_bfd_final_link (abfd, info)
              || strcmp (bfd_get_section_name (abfd, o), ".dynstr") != 0)
            {
              if (! bfd_set_section_contents (abfd, o->output_section,
-                                             o->contents, o->output_offset,
+                                             o->contents,
+                                             (file_ptr) o->output_offset,
                                              o->_raw_size))
                goto error_return;
            }
          else
            {
-             file_ptr off;
-
              /* The contents of the .dynstr section are actually in a
-                 stringtab.  */
+                stringtab.  */
              off = elf_section_data (o->output_section)->this_hdr.sh_offset;
              if (bfd_seek (abfd, off, SEEK_SET) != 0
-                 || ! _bfd_stringtab_emit (abfd,
-                                           elf_hash_table (info)->dynstr))
+                 || ! _bfd_elf_strtab_emit (abfd,
+                                            elf_hash_table (info)->dynstr))
                goto error_return;
            }
        }
@@ -4921,6 +5757,19 @@ elf_bfd_final_link (abfd, info)
        goto error_return;
     }
 
+  if (info->eh_frame_hdr && elf_hash_table (info)->dynobj)
+    {
+      o = bfd_get_section_by_name (elf_hash_table (info)->dynobj,
+                                  ".eh_frame_hdr");
+      if (o
+         && (elf_section_data (o)->sec_info_type
+             == ELF_INFO_TYPE_EH_FRAME_HDR))
+       {
+         if (! _bfd_elf_write_section_eh_frame_hdr (abfd, o))
+           goto error_return;
+       }
+    }
+
   if (finfo.symstrtab != NULL)
     _bfd_stringtab_free (finfo.symstrtab);
   if (finfo.contents != NULL)
@@ -4931,6 +5780,8 @@ elf_bfd_final_link (abfd, info)
     free (finfo.internal_relocs);
   if (finfo.external_syms != NULL)
     free (finfo.external_syms);
+  if (finfo.locsym_shndx != NULL)
+    free (finfo.locsym_shndx);
   if (finfo.internal_syms != NULL)
     free (finfo.internal_syms);
   if (finfo.indices != NULL)
@@ -4939,6 +5790,8 @@ elf_bfd_final_link (abfd, info)
     free (finfo.sections);
   if (finfo.symbuf != NULL)
     free (finfo.symbuf);
+  if (finfo.symshndxbuf != NULL)
+    free (finfo.symbuf);
   for (o = abfd->sections; o != NULL; o = o->next)
     {
       if ((o->flags & SEC_RELOC) != 0
@@ -4961,6 +5814,8 @@ elf_bfd_final_link (abfd, info)
     free (finfo.internal_relocs);
   if (finfo.external_syms != NULL)
     free (finfo.external_syms);
+  if (finfo.locsym_shndx != NULL)
+    free (finfo.locsym_shndx);
   if (finfo.internal_syms != NULL)
     free (finfo.internal_syms);
   if (finfo.indices != NULL)
@@ -4969,6 +5824,8 @@ elf_bfd_final_link (abfd, info)
     free (finfo.sections);
   if (finfo.symbuf != NULL)
     free (finfo.symbuf);
+  if (finfo.symshndxbuf != NULL)
+    free (finfo.symbuf);
   for (o = abfd->sections; o != NULL; o = o->next)
     {
       if ((o->flags & SEC_RELOC) != 0
@@ -4988,6 +5845,9 @@ elf_link_output_sym (finfo, name, elfsym, input_sec)
      Elf_Internal_Sym *elfsym;
      asection *input_sec;
 {
+  Elf_External_Sym *dest;
+  Elf_External_Sym_Shndx *destshndx;
+
   boolean (*output_symbol_hook) PARAMS ((bfd *,
                                         struct bfd_link_info *info,
                                         const char *,
@@ -5010,8 +5870,7 @@ elf_link_output_sym (finfo, name, elfsym, input_sec)
   else
     {
       elfsym->st_name = (unsigned long) _bfd_stringtab_add (finfo->symstrtab,
-                                                           name, true,
-                                                           false);
+                                                           name, true, false);
       if (elfsym->st_name == (unsigned long) -1)
        return false;
     }
@@ -5022,8 +5881,11 @@ elf_link_output_sym (finfo, name, elfsym, input_sec)
        return false;
     }
 
-  elf_swap_symbol_out (finfo->output_bfd, elfsym,
-                      (PTR) (finfo->symbuf + finfo->symbuf_count));
+  dest = finfo->symbuf + finfo->symbuf_count;
+  destshndx = finfo->symshndxbuf;
+  if (destshndx != NULL)
+    destshndx += finfo->symbuf_count;
+  elf_swap_symbol_out (finfo->output_bfd, elfsym, (PTR) dest, (PTR) destshndx);
   ++finfo->symbuf_count;
 
   ++ bfd_get_symcount (finfo->output_bfd);
@@ -5039,18 +5901,31 @@ elf_link_flush_output_syms (finfo)
 {
   if (finfo->symbuf_count > 0)
     {
-      Elf_Internal_Shdr *symtab;
+      Elf_Internal_Shdr *hdr;
+      file_ptr pos;
+      bfd_size_type amt;
+
+      hdr = &elf_tdata (finfo->output_bfd)->symtab_hdr;
+      pos = hdr->sh_offset + hdr->sh_size;
+      amt = finfo->symbuf_count * sizeof (Elf_External_Sym);
+      if (bfd_seek (finfo->output_bfd, pos, SEEK_SET) != 0
+         || bfd_bwrite ((PTR) finfo->symbuf, amt, finfo->output_bfd) != amt)
+       return false;
 
-      symtab = &elf_tdata (finfo->output_bfd)->symtab_hdr;
+      hdr->sh_size += amt;
 
-      if (bfd_seek (finfo->output_bfd, symtab->sh_offset + symtab->sh_size,
-                   SEEK_SET) != 0
-         || (bfd_write ((PTR) finfo->symbuf, finfo->symbuf_count,
-                        sizeof (Elf_External_Sym), finfo->output_bfd)
-             != finfo->symbuf_count * sizeof (Elf_External_Sym)))
-       return false;
+      if (finfo->symshndxbuf != NULL)
+       {
+         hdr = &elf_tdata (finfo->output_bfd)->symtab_shndx_hdr;
+         pos = hdr->sh_offset + hdr->sh_size;
+         amt = finfo->symbuf_count * sizeof (Elf_External_Sym_Shndx);
+         if (bfd_seek (finfo->output_bfd, pos, SEEK_SET) != 0
+             || (bfd_bwrite ((PTR) finfo->symshndxbuf, amt, finfo->output_bfd)
+                 != amt))
+           return false;
 
-      symtab->sh_size += finfo->symbuf_count * sizeof (Elf_External_Sym);
+         hdr->sh_size += amt;
+       }
 
       finfo->symbuf_count = 0;
     }
@@ -5058,6 +5933,36 @@ elf_link_flush_output_syms (finfo)
   return true;
 }
 
+/* Adjust all external symbols pointing into SEC_MERGE sections
+   to reflect the object merging within the sections.  */
+
+static boolean
+elf_link_sec_merge_syms (h, data)
+     struct elf_link_hash_entry *h;
+     PTR data;
+{
+  asection *sec;
+
+  if (h->root.type == bfd_link_hash_warning)
+    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+  if ((h->root.type == bfd_link_hash_defined
+       || h->root.type == bfd_link_hash_defweak)
+      && ((sec = h->root.u.def.section)->flags & SEC_MERGE)
+      && elf_section_data (sec)->sec_info_type == ELF_INFO_TYPE_MERGE)
+    {
+      bfd *output_bfd = (bfd *) data;
+
+      h->root.u.def.value =
+       _bfd_merged_section_offset (output_bfd,
+                                   &h->root.u.def.section,
+                                   elf_section_data (sec)->sec_info,
+                                   h->root.u.def.value, (bfd_vma) 0);
+    }
+
+  return true;
+}
+
 /* Add an external symbol to the symbol table.  This is called from
    the hash table traversal routine.  When generating a shared object,
    we go through the symbol table twice.  The first time we output
@@ -5076,6 +5981,13 @@ elf_link_output_extsym (h, data)
   Elf_Internal_Sym sym;
   asection *input_sec;
 
+  if (h->root.type == bfd_link_hash_warning)
+    {
+      h = (struct elf_link_hash_entry *) h->root.u.i.link;
+      if (h->root.type == bfd_link_hash_new)
+       return true;
+    }
+
   /* Decide whether to output this symbol in this pass.  */
   if (eoinfo->localsyms)
     {
@@ -5097,15 +6009,14 @@ elf_link_output_extsym (h, data)
      warnings for them.  */
   if (! finfo->info->relocateable
       && ! finfo->info->allow_shlib_undefined
-      && ! (finfo->info->shared
-           && !finfo->info->no_undefined)
+      && ! finfo->info->shared
       && h->root.type == bfd_link_hash_undefined
       && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_DYNAMIC) != 0
       && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) == 0)
     {
       if (! ((*finfo->info->callbacks->undefined_symbol)
             (finfo->info, h->root.root.string, h->root.u.undef.abfd,
-             (asection *) NULL, 0, true)))
+             (asection *) NULL, (bfd_vma) 0, true)))
        {
          eoinfo->failed = true;
          return false;
@@ -5154,14 +6065,11 @@ elf_link_output_extsym (h, data)
     {
     default:
     case bfd_link_hash_new:
+    case bfd_link_hash_warning:
       abort ();
       return false;
 
     case bfd_link_hash_undefined:
-      input_sec = bfd_und_section_ptr;
-      sym.st_shndx = SHN_UNDEF;
-      break;
-
     case bfd_link_hash_undefweak:
       input_sec = bfd_und_section_ptr;
       sym.st_shndx = SHN_UNDEF;
@@ -5176,7 +6084,7 @@ elf_link_output_extsym (h, data)
            sym.st_shndx =
              _bfd_elf_section_from_bfd_section (finfo->output_bfd,
                                                 input_sec->output_section);
-           if (sym.st_shndx == (unsigned short) -1)
+           if (sym.st_shndx == SHN_BAD)
              {
                (*_bfd_error_handler)
                  (_("%s: could not find output section %s for input section %s"),
@@ -5212,29 +6120,22 @@ elf_link_output_extsym (h, data)
 
     case bfd_link_hash_indirect:
       /* These symbols are created by symbol versioning.  They point
-         to the decorated version of the name.  For example, if the
-         symbol foo@@GNU_1.2 is the default, which should be used when
-         foo is used with no version, then we add an indirect symbol
-         foo which points to foo@@GNU_1.2.  We ignore these symbols,
-         since the indirected symbol is already in the hash table.  */
+        to the decorated version of the name.  For example, if the
+        symbol foo@@GNU_1.2 is the default, which should be used when
+        foo is used with no version, then we add an indirect symbol
+        foo which points to foo@@GNU_1.2.  We ignore these symbols,
+        since the indirected symbol is already in the hash table.  */
       return true;
-
-    case bfd_link_hash_warning:
-      /* We can't represent these symbols in ELF, although a warning
-         symbol may have come from a .gnu.warning.SYMBOL section.  We
-         just put the target symbol in the hash table.  If the target
-         symbol does not really exist, don't do anything.  */
-      if (h->root.u.i.link->type == bfd_link_hash_new)
-       return true;
-      return (elf_link_output_extsym
-             ((struct elf_link_hash_entry *) h->root.u.i.link, data));
     }
 
   /* Give the processor backend a chance to tweak the symbol value,
      and also to finish up anything that needs to be done for this
-     symbol.  */
+     symbol.  FIXME: Not calling elf_backend_finish_dynamic_symbol for
+     forced local syms when non-shared is due to a historical quirk.  */
   if ((h->dynindx != -1
        || (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0)
+      && (finfo->info->shared
+         || (h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) == 0)
       && elf_hash_table (finfo->info)->dynamic_sections_created)
     {
       struct elf_backend_data *bed;
@@ -5256,8 +6157,8 @@ elf_link_output_extsym (h, data)
      finish_dynamic_symbol routine gets through with it.  */
   if (sym.st_shndx == SHN_UNDEF
       && (h->elf_link_hash_flags & ELF_LINK_HASH_REF_REGULAR) != 0
-      && (ELF_ST_BIND(sym.st_info) == STB_GLOBAL
-         || ELF_ST_BIND(sym.st_info) == STB_WEAK))
+      && (ELF_ST_BIND (sym.st_info) == STB_GLOBAL
+         || ELF_ST_BIND (sym.st_info) == STB_WEAK))
     {
       int bindtype;
 
@@ -5270,8 +6171,9 @@ elf_link_output_extsym (h, data)
 
   /* If a symbol is not defined locally, we clear the visibility
      field.  */
-  if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
-    sym.st_other ^= ELF_ST_VISIBILITY(sym.st_other);
+  if (! finfo->info->relocateable
+      && (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
+    sym.st_other ^= ELF_ST_VISIBILITY (sym.st_other);
 
   /* If this symbol should be put in the .dynsym section, then put it
      there now.  We have already know the symbol index.  We also fill
@@ -5284,13 +6186,11 @@ elf_link_output_extsym (h, data)
       size_t hash_entry_size;
       bfd_byte *bucketpos;
       bfd_vma chain;
+      Elf_External_Sym *esym;
 
       sym.st_name = h->dynstr_index;
-
-      elf_swap_symbol_out (finfo->output_bfd, &sym,
-                          (PTR) (((Elf_External_Sym *)
-                                  finfo->dynsym_sec->contents)
-                                 + h->dynindx));
+      esym = (Elf_External_Sym *) finfo->dynsym_sec->contents + h->dynindx;
+      elf_swap_symbol_out (finfo->output_bfd, &sym, (PTR) esym, (PTR) 0);
 
       bucketcount = elf_hash_table (finfo->info)->bucketcount;
       bucket = h->elf_hash_value % bucketcount;
@@ -5299,7 +6199,8 @@ elf_link_output_extsym (h, data)
       bucketpos = ((bfd_byte *) finfo->hash_sec->contents
                   + (bucket + 2) * hash_entry_size);
       chain = bfd_get (8 * hash_entry_size, finfo->output_bfd, bucketpos);
-      bfd_put (8 * hash_entry_size, finfo->output_bfd, h->dynindx, bucketpos);
+      bfd_put (8 * hash_entry_size, finfo->output_bfd, (bfd_vma) h->dynindx,
+              bucketpos);
       bfd_put (8 * hash_entry_size, finfo->output_bfd, chain,
               ((bfd_byte *) finfo->hash_sec->contents
                + (bucketcount + 2 + h->dynindx) * hash_entry_size));
@@ -5307,6 +6208,7 @@ elf_link_output_extsym (h, data)
       if (finfo->symver_sec != NULL && finfo->symver_sec->contents != NULL)
        {
          Elf_Internal_Versym iversym;
+         Elf_External_Versym *eversym;
 
          if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
            {
@@ -5326,10 +6228,9 @@ elf_link_output_extsym (h, data)
          if ((h->elf_link_hash_flags & ELF_LINK_HIDDEN) != 0)
            iversym.vs_vers |= VERSYM_HIDDEN;
 
-         _bfd_elf_swap_versym_out (finfo->output_bfd, &iversym,
-                                   (((Elf_External_Versym *)
-                                     finfo->symver_sec->contents)
-                                    + h->dynindx));
+         eversym = (Elf_External_Versym *) finfo->symver_sec->contents;
+         eversym += h->dynindx;
+         _bfd_elf_swap_versym_out (finfo->output_bfd, &iversym, eversym);
        }
     }
 
@@ -5353,7 +6254,7 @@ elf_link_output_extsym (h, data)
    originated from the section given by INPUT_REL_HDR) to the
    OUTPUT_BFD.  */
 
-static void
+static boolean
 elf_link_output_relocs (output_bfd, input_section, input_rel_hdr,
                        internal_relocs)
      bfd *output_bfd;
@@ -5367,6 +6268,7 @@ elf_link_output_relocs (output_bfd, input_section, input_rel_hdr,
   asection *output_section;
   unsigned int *rel_countp = NULL;
   struct elf_backend_data *bed;
+  bfd_size_type amt;
 
   output_section = input_section->output_section;
   output_rel_hdr = NULL;
@@ -5384,38 +6286,63 @@ elf_link_output_relocs (output_bfd, input_section, input_rel_hdr,
       output_rel_hdr = elf_section_data (output_section)->rel_hdr2;
       rel_countp = &elf_section_data (output_section)->rel_count2;
     }
-
-  BFD_ASSERT (output_rel_hdr != NULL);
+  else
+    {
+      (*_bfd_error_handler) (
+        _("%s: relocation size mismatch in %s section %s"),
+        bfd_get_filename (output_bfd),
+        bfd_archive_filename (input_section->owner),
+        input_section->name);
+      bfd_set_error (bfd_error_wrong_object_format);
+      return false;
+    }
 
   bed = get_elf_backend_data (output_bfd);
   irela = internal_relocs;
-  irelaend = irela + input_rel_hdr->sh_size / input_rel_hdr->sh_entsize;
+  irelaend = irela + NUM_SHDR_ENTRIES (input_rel_hdr)
+                    * bed->s->int_rels_per_ext_rel;
+
   if (input_rel_hdr->sh_entsize == sizeof (Elf_External_Rel))
     {
       Elf_External_Rel *erel;
+      Elf_Internal_Rel *irel;
+
+      amt = bed->s->int_rels_per_ext_rel * sizeof (Elf_Internal_Rel);
+      irel = (Elf_Internal_Rel *) bfd_zmalloc (amt);
+      if (irel == NULL)
+       {
+         (*_bfd_error_handler) (_("Error: out of memory"));
+         abort ();
+       }
 
       erel = ((Elf_External_Rel *) output_rel_hdr->contents + *rel_countp);
-      for (; irela < irelaend; irela++, erel++)
+      for (; irela < irelaend; irela += bed->s->int_rels_per_ext_rel, erel++)
        {
-         Elf_Internal_Rel irel;
+         unsigned int i;
+
+         for (i = 0; i < bed->s->int_rels_per_ext_rel; i++)
+           {
+             irel[i].r_offset = irela[i].r_offset;
+             irel[i].r_info = irela[i].r_info;
+             BFD_ASSERT (irela[i].r_addend == 0);
+           }
 
-         irel.r_offset = irela->r_offset;
-         irel.r_info = irela->r_info;
-         BFD_ASSERT (irela->r_addend == 0);
          if (bed->s->swap_reloc_out)
-           (*bed->s->swap_reloc_out) (output_bfd, &irel, (PTR) erel);
+           (*bed->s->swap_reloc_out) (output_bfd, irel, (PTR) erel);
          else
-           elf_swap_reloc_out (output_bfd, &irel, erel);
+           elf_swap_reloc_out (output_bfd, irel, erel);
        }
+
+      free (irel);
     }
   else
     {
       Elf_External_Rela *erela;
 
-      BFD_ASSERT (input_rel_hdr->sh_entsize
-                 == sizeof (Elf_External_Rela));
+      BFD_ASSERT (input_rel_hdr->sh_entsize == sizeof (Elf_External_Rela));
+
       erela = ((Elf_External_Rela *) output_rel_hdr->contents + *rel_countp);
-      for (; irela < irelaend; irela++, erela++)
+      for (; irela < irelaend; irela += bed->s->int_rels_per_ext_rel, erela++)
        if (bed->s->swap_reloca_out)
          (*bed->s->swap_reloca_out) (output_bfd, irela, (PTR) erela);
        else
@@ -5424,7 +6351,9 @@ elf_link_output_relocs (output_bfd, input_section, input_rel_hdr,
 
   /* Bump the counter, so that we know where to add the next set of
      relocations.  */
-  *rel_countp += input_rel_hdr->sh_size / input_rel_hdr->sh_entsize;
+  *rel_countp += NUM_SHDR_ENTRIES (input_rel_hdr);
+
+  return true;
 }
 
 /* Link an input file into the linker output file.  This function
@@ -5443,16 +6372,21 @@ elf_link_input_bfd (finfo, input_bfd)
                                       Elf_Internal_Sym *, asection **));
   bfd *output_bfd;
   Elf_Internal_Shdr *symtab_hdr;
+  Elf_Internal_Shdr *shndx_hdr;
   size_t locsymcount;
   size_t extsymoff;
   Elf_External_Sym *external_syms;
   Elf_External_Sym *esym;
   Elf_External_Sym *esymend;
+  Elf_External_Sym_Shndx *shndx_buf;
+  Elf_External_Sym_Shndx *shndx;
   Elf_Internal_Sym *isym;
   long *pindex;
   asection **ppsection;
   asection *o;
   struct elf_backend_data *bed;
+  boolean emit_relocs;
+  struct elf_link_hash_entry **sym_hashes;
 
   output_bfd = finfo->output_bfd;
   bed = get_elf_backend_data (output_bfd);
@@ -5464,6 +6398,10 @@ elf_link_input_bfd (finfo, input_bfd)
   if ((input_bfd->flags & DYNAMIC) != 0)
     return true;
 
+  emit_relocs = (finfo->info->relocateable
+                || finfo->info->emitrelocations
+                || bed->elf_backend_emit_relocs);
+
   symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
   if (elf_bad_symtab (input_bfd))
     {
@@ -5483,28 +6421,38 @@ elf_link_input_bfd (finfo, input_bfd)
     external_syms = NULL;
   else
     {
+      bfd_size_type amt = locsymcount * sizeof (Elf_External_Sym);
       external_syms = finfo->external_syms;
       if (bfd_seek (input_bfd, symtab_hdr->sh_offset, SEEK_SET) != 0
-         || (bfd_read (external_syms, sizeof (Elf_External_Sym),
-                       locsymcount, input_bfd)
-             != locsymcount * sizeof (Elf_External_Sym)))
+         || bfd_bread (external_syms, amt, input_bfd) != amt)
+       return false;
+    }
+
+  shndx_hdr = &elf_tdata (input_bfd)->symtab_shndx_hdr;
+  shndx_buf = NULL;
+  if (shndx_hdr->sh_size != 0 && locsymcount != 0)
+    {
+      bfd_size_type amt = locsymcount * sizeof (Elf_External_Sym_Shndx);
+      shndx_buf = finfo->locsym_shndx;
+      if (bfd_seek (input_bfd, shndx_hdr->sh_offset, SEEK_SET) != 0
+         || bfd_bread (shndx_buf, amt, input_bfd) != amt)
        return false;
     }
 
   /* Swap in the local symbols and write out the ones which we know
      are going into the output file.  */
-  esym = external_syms;
-  esymend = esym + locsymcount;
-  isym = finfo->internal_syms;
-  pindex = finfo->indices;
-  ppsection = finfo->sections;
-  for (; esym < esymend; esym++, isym++, pindex++, ppsection++)
+  for (esym = external_syms, esymend = esym + locsymcount,
+        isym = finfo->internal_syms, pindex = finfo->indices,
+        ppsection = finfo->sections, shndx = shndx_buf;
+       esym < esymend;
+       esym++, isym++, pindex++, ppsection++,
+        shndx = (shndx != NULL ? shndx + 1 : NULL))
     {
       asection *isec;
       const char *name;
       Elf_Internal_Sym osym;
 
-      elf_swap_symbol_in (input_bfd, esym, isym);
+      elf_swap_symbol_in (input_bfd, esym, shndx, isym);
       *pindex = -1;
 
       if (elf_bad_symtab (input_bfd))
@@ -5516,24 +6464,24 @@ elf_link_input_bfd (finfo, input_bfd)
            }
        }
 
-      name = NULL;
       if (isym->st_shndx == SHN_UNDEF)
-        {
-         isec = bfd_und_section_ptr;
-         name = isec->name;
-       }
-      else if (isym->st_shndx > 0 && isym->st_shndx < SHN_LORESERVE)
-       isec = section_from_elf_index (input_bfd, isym->st_shndx);
-      else if (isym->st_shndx == SHN_ABS)
+       isec = bfd_und_section_ptr;
+      else if (isym->st_shndx < SHN_LORESERVE
+              || isym->st_shndx > SHN_HIRESERVE)
        {
-         isec = bfd_abs_section_ptr;
-         name = isec->name;
+         isec = section_from_elf_index (input_bfd, isym->st_shndx);
+         if (isec
+             && elf_section_data (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, (bfd_vma) 0);
        }
+      else if (isym->st_shndx == SHN_ABS)
+       isec = bfd_abs_section_ptr;
       else if (isym->st_shndx == SHN_COMMON)
-       {
-         isec = bfd_com_section_ptr;
-         name = isec->name;
-       }
+       isec = bfd_com_section_ptr;
       else
        {
          /* Who knows?  */
@@ -5548,41 +6496,6 @@ elf_link_input_bfd (finfo, input_bfd)
 
       if (ELF_ST_TYPE (isym->st_info) == STT_SECTION)
        {
-         asection *ksec;
-
-         /* Save away all section symbol values.  */
-         if (isec != NULL)
-           {
-             if (name)
-               {
-                 if (isec->symbol->value != isym->st_value)
-                   (*_bfd_error_handler)
-                     (_("%s: invalid section symbol index 0x%x (%s) ingored"),
-                      bfd_get_filename (input_bfd), isym->st_shndx,
-                      name);
-                 continue;
-               }
-             isec->symbol->value = isym->st_value;
-           }
-
-         /* If this is a discarded link-once section symbol, update
-            it's value to that of the kept section symbol.  The
-            linker will keep the first of any matching link-once
-            sections, so we should have already seen it's section
-            symbol.  I trust no-one will have the bright idea of
-            re-ordering the bfd list...  */
-         if (isec != NULL
-             && (bfd_get_section_flags (input_bfd, isec) & SEC_LINK_ONCE) != 0
-             && (ksec = isec->kept_section) != NULL)
-           {
-             isym->st_value = ksec->symbol->value;
-
-             /* That put the value right, but the section info is all
-                wrong.  I hope this works.  */
-             isec->output_offset = ksec->output_offset;
-             isec->output_section = ksec->output_section;
-           }
-
          /* We never output section symbols.  Instead, we use the
             section symbol of the corresponding section in the output
             file.  */
@@ -5603,12 +6516,11 @@ elf_link_input_bfd (finfo, input_bfd)
        continue;
 
       /* If this symbol is defined in a section which we are
-         discarding, we don't need to keep it, but note that
-         linker_mark is only reliable for sections that have contents.
-         For the benefit of the MIPS ELF linker, we check SEC_EXCLUDE
-         as well as linker_mark.  */
-      if (isym->st_shndx > 0
-         && isym->st_shndx < SHN_LORESERVE
+        discarding, we don't need to keep it, but note that
+        linker_mark is only reliable for sections that have contents.
+        For the benefit of the MIPS ELF linker, we check SEC_EXCLUDE
+        as well as linker_mark.  */
+      if ((isym->st_shndx < SHN_LORESERVE || isym->st_shndx > SHN_HIRESERVE)
          && isec != NULL
          && ((! isec->linker_mark && (isec->flags & SEC_HAS_CONTENTS) != 0)
              || (! finfo->info->relocateable
@@ -5625,7 +6537,9 @@ elf_link_input_bfd (finfo, input_bfd)
       if ((finfo->info->strip == strip_some
           && (bfd_hash_lookup (finfo->info->keep_hash, name, false, false)
               == NULL))
-         || (finfo->info->discard == discard_l
+         || (((finfo->info->discard == discard_sec_merge
+               && (isec->flags & SEC_MERGE) && ! finfo->info->relocateable)
+              || finfo->info->discard == discard_l)
              && bfd_is_local_label_name (input_bfd, name)))
        continue;
 
@@ -5636,7 +6550,7 @@ elf_link_input_bfd (finfo, input_bfd)
       /* Adjust the section index for the output file.  */
       osym.st_shndx = _bfd_elf_section_from_bfd_section (output_bfd,
                                                         isec->output_section);
-      if (osym.st_shndx == (unsigned short) -1)
+      if (osym.st_shndx == SHN_BAD)
        return false;
 
       *pindex = bfd_get_symcount (output_bfd);
@@ -5657,6 +6571,7 @@ elf_link_input_bfd (finfo, input_bfd)
     }
 
   /* Relocate the contents of each section.  */
+  sym_hashes = elf_sym_hashes (input_bfd);
   for (o = input_bfd->sections; o != NULL; o = o->next)
     {
       bfd_byte *contents;
@@ -5679,9 +6594,9 @@ elf_link_input_bfd (finfo, input_bfd)
        }
 
       /* Get the contents of the section.  They have been cached by a
-         relaxation routine.  Note that o is a section in an input
-         file, so the contents field will not have been set by any of
-         the routines which work on output files.  */
+        relaxation routine.  Note that o is a section in an input
+        file, so the contents field will not have been set by any of
+        the routines which work on output files.  */
       if (elf_section_data (o)->this_hdr.contents != NULL)
        contents = elf_section_data (o)->this_hdr.contents;
       else
@@ -5704,6 +6619,117 @@ elf_link_input_bfd (finfo, input_bfd)
              && o->reloc_count > 0)
            return false;
 
+         /* Run through the relocs looking for any against symbols
+            from discarded sections and section symbols from
+            removed link-once sections.  Complain about relocs
+            against discarded sections.  Zero relocs against removed
+            link-once sections.  We should really complain if
+            anything in the final link tries to use it, but
+            DWARF-based exception handling might have an entry in
+            .eh_frame to describe a routine in the linkonce section,
+            and it turns out to be hard to remove the .eh_frame
+            entry too.  FIXME.  */
+         if (!finfo->info->relocateable
+             && !elf_section_ignore_discarded_relocs (o))
+           {
+             Elf_Internal_Rela *rel, *relend;
+
+             rel = internal_relocs;
+             relend = rel + o->reloc_count * bed->s->int_rels_per_ext_rel;
+             for ( ; rel < relend; rel++)
+               {
+                 unsigned long r_symndx = ELF_R_SYM (rel->r_info);
+
+                 if (r_symndx >= locsymcount
+                     || (elf_bad_symtab (input_bfd)
+                         && finfo->sections[r_symndx] == NULL))
+                   {
+                     struct elf_link_hash_entry *h;
+
+                     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;
+
+                     /* Complain if the definition comes from a
+                        discarded section.  */
+                     if ((h->root.type == bfd_link_hash_defined
+                          || h->root.type == bfd_link_hash_defweak)
+                         && elf_discarded_section (h->root.u.def.section))
+                       {
+#if BFD_VERSION_DATE < 20031005
+                         if ((o->flags & SEC_DEBUGGING) != 0)
+                           {
+#if BFD_VERSION_DATE > 20021005
+                             (*finfo->info->callbacks->warning)
+                               (finfo->info,
+                                _("warning: relocation against removed section; zeroing"),
+                                NULL, input_bfd, o, rel->r_offset);
+#endif
+                             BFD_ASSERT (r_symndx != 0);
+                             memset (rel, 0, sizeof (*rel));
+                           }
+                         else
+#endif
+                           {
+                             if (! ((*finfo->info->callbacks->undefined_symbol)
+                                    (finfo->info, h->root.root.string,
+                                     input_bfd, o, rel->r_offset,
+                                     true)))
+                               return false;
+                           }
+                       }
+                   }
+                 else
+                   {
+                     asection *sec = finfo->sections[r_symndx];
+
+                     if (sec != NULL && elf_discarded_section (sec))
+                       {
+#if BFD_VERSION_DATE < 20031005
+                         if ((o->flags & SEC_DEBUGGING) != 0
+                             || (sec->flags & SEC_LINK_ONCE) != 0)
+                           {
+#if BFD_VERSION_DATE > 20021005
+                             (*finfo->info->callbacks->warning)
+                               (finfo->info,
+                                _("warning: relocation against removed section"),
+                                NULL, input_bfd, o, rel->r_offset);
+#endif
+                             BFD_ASSERT (r_symndx != 0);
+                             rel->r_info
+                               = ELF_R_INFO (0, ELF_R_TYPE (rel->r_info));
+                             rel->r_addend = 0;
+                           }
+                         else
+#endif
+                           {
+                             boolean ok;
+                             const char *msg
+                               = _("local symbols in discarded section %s");
+                             bfd_size_type amt
+                               = strlen (sec->name) + strlen (msg) - 1;
+                             char *buf = (char *) bfd_malloc (amt);
+
+                             if (buf != NULL)
+                               sprintf (buf, msg, sec->name);
+                             else
+                               buf = (char *) sec->name;
+                             ok = (*finfo->info->callbacks
+                                   ->undefined_symbol) (finfo->info, buf,
+                                                        input_bfd, o,
+                                                        rel->r_offset,
+                                                        true);
+                             if (buf != sec->name)
+                               free (buf);
+                             if (!ok)
+                               return false;
+                           }
+                       }
+                   }
+               }
+           }
+
          /* Relocate the section by invoking a back end routine.
 
             The back end routine is responsible for adjusting the
@@ -5731,31 +6757,45 @@ elf_link_input_bfd (finfo, input_bfd)
                                     finfo->sections))
            return false;
 
-         if (finfo->info->relocateable || finfo->info->emitrelocations)
+         if (emit_relocs)
            {
              Elf_Internal_Rela *irela;
              Elf_Internal_Rela *irelaend;
              struct elf_link_hash_entry **rel_hash;
              Elf_Internal_Shdr *input_rel_hdr;
+             unsigned int next_erel;
+             boolean (*reloc_emitter) PARAMS ((bfd *, asection *,
+                                               Elf_Internal_Shdr *,
+                                               Elf_Internal_Rela *));
+             boolean rela_normal;
+
+             input_rel_hdr = &elf_section_data (o)->rel_hdr;
+             rela_normal = (bed->rela_normal
+                            && (input_rel_hdr->sh_entsize
+                                == sizeof (Elf_External_Rela)));
 
              /* Adjust the reloc addresses and symbol indices.  */
 
              irela = internal_relocs;
-             irelaend =
-               irela + o->reloc_count * bed->s->int_rels_per_ext_rel;
+             irelaend = irela + o->reloc_count * bed->s->int_rels_per_ext_rel;
              rel_hash = (elf_section_data (o->output_section)->rel_hashes
                          + elf_section_data (o->output_section)->rel_count
                          + elf_section_data (o->output_section)->rel_count2);
-             for (; irela < irelaend; irela++, rel_hash++)
+             for (next_erel = 0; irela < irelaend; irela++, next_erel++)
                {
                  unsigned long r_symndx;
-                 Elf_Internal_Sym *isym;
                  asection *sec;
 
+                 if (next_erel == bed->s->int_rels_per_ext_rel)
+                   {
+                     rel_hash++;
+                     next_erel = 0;
+                   }
+
                  irela->r_offset += o->output_offset;
 
                  /* Relocs in an executable have to be virtual addresses.  */
-                 if (finfo->info->emitrelocations)
+                 if (!finfo->info->relocateable)
                    irela->r_offset += o->output_section->vma;
 
                  r_symndx = ELF_R_SYM (irela->r_info);
@@ -5768,7 +6808,7 @@ elf_link_input_bfd (finfo, input_bfd)
                          && finfo->sections[r_symndx] == NULL))
                    {
                      struct elf_link_hash_entry *rh;
-                     long indx;
+                     unsigned long indx;
 
                      /* This is a reloc against a global symbol.  We
                         have not yet output all the local symbols, so
@@ -5806,10 +6846,9 @@ elf_link_input_bfd (finfo, input_bfd)
                         processor specific section.  If we have
                         discarded a section, the output_section will
                         be the absolute section.  */
-                     if (sec != NULL
-                         && (bfd_is_abs_section (sec)
-                             || (sec->output_section != NULL
-                                 && bfd_is_abs_section (sec->output_section))))
+                     if (bfd_is_abs_section (sec)
+                         || (sec != NULL
+                             && bfd_is_abs_section (sec->output_section)))
                        r_symndx = 0;
                      else if (sec == NULL || sec->owner == NULL)
                        {
@@ -5821,12 +6860,17 @@ elf_link_input_bfd (finfo, input_bfd)
                          r_symndx = sec->output_section->target_index;
                          BFD_ASSERT (r_symndx != 0);
                        }
+
+                     /* Adjust the addend according to where the
+                        section winds up in the output section.  */ 
+                     if (rela_normal)
+                       irela->r_addend += sec->output_offset;
                    }
                  else
                    {
                      if (finfo->indices[r_symndx] == -1)
                        {
-                         unsigned long link;
+                         unsigned long shlink;
                          const char *name;
                          asection *osec;
 
@@ -5840,10 +6884,9 @@ elf_link_input_bfd (finfo, input_bfd)
                          /* This symbol was skipped earlier, but
                             since it is needed by a reloc, we
                             must output it now.  */
-                         link = symtab_hdr->sh_link;
-                         name = bfd_elf_string_from_elf_section (input_bfd,
-                                                                 link,
-                                                                 isym->st_name);
+                         shlink = symtab_hdr->sh_link;
+                         name = (bfd_elf_string_from_elf_section
+                                 (input_bfd, shlink, isym->st_name));
                          if (name == NULL)
                            return false;
 
@@ -5851,14 +6894,15 @@ elf_link_input_bfd (finfo, input_bfd)
                          isym->st_shndx =
                            _bfd_elf_section_from_bfd_section (output_bfd,
                                                               osec);
-                         if (isym->st_shndx == (unsigned short) -1)
+                         if (isym->st_shndx == SHN_BAD)
                            return false;
 
                          isym->st_value += sec->output_offset;
                          if (! finfo->info->relocateable)
                            isym->st_value += osec->vma;
 
-                         finfo->indices[r_symndx] = bfd_get_symcount (output_bfd);
+                         finfo->indices[r_symndx]
+                           = bfd_get_symcount (output_bfd);
 
                          if (! elf_link_output_sym (finfo, name, isym, sec))
                            return false;
@@ -5872,37 +6916,75 @@ elf_link_input_bfd (finfo, input_bfd)
                }
 
              /* Swap out the relocs.  */
-             input_rel_hdr = &elf_section_data (o)->rel_hdr;
-             elf_link_output_relocs (output_bfd, o,
-                                     input_rel_hdr,
-                                     internal_relocs);
-             internal_relocs
-               += input_rel_hdr->sh_size / input_rel_hdr->sh_entsize;
+             if (bed->elf_backend_emit_relocs
+                 && !(finfo->info->relocateable
+                      || finfo->info->emitrelocations))
+               reloc_emitter = bed->elf_backend_emit_relocs;
+             else
+               reloc_emitter = elf_link_output_relocs;
+
+             if (! (*reloc_emitter) (output_bfd, o, input_rel_hdr,
+                                     internal_relocs))
+               return false;
+
              input_rel_hdr = elf_section_data (o)->rel_hdr2;
              if (input_rel_hdr)
-               elf_link_output_relocs (output_bfd, o,
-                                       input_rel_hdr,
-                                       internal_relocs);
+               {
+                 internal_relocs += (NUM_SHDR_ENTRIES (input_rel_hdr)
+                                     * bed->s->int_rels_per_ext_rel);
+                 if (! (*reloc_emitter) (output_bfd, o, input_rel_hdr,
+                                         internal_relocs))
+                   return false;
+               }
+
            }
        }
 
       /* Write out the modified section contents.  */
-      if (elf_section_data (o)->stab_info == NULL)
-       {
-         if (! (o->flags & SEC_EXCLUDE) &&
-             ! bfd_set_section_contents (output_bfd, o->output_section,
-                                         contents, o->output_offset,
-                                         (o->_cooked_size != 0
-                                          ? o->_cooked_size
-                                          : o->_raw_size)))
-           return false;
+      if (bed->elf_backend_write_section
+         && (*bed->elf_backend_write_section) (output_bfd, o, contents))
+       {
+         /* Section written out.  */
        }
-      else
+      else switch (elf_section_data (o)->sec_info_type)
        {
+       case ELF_INFO_TYPE_STABS:
          if (! (_bfd_write_section_stabs
-                (output_bfd, &elf_hash_table (finfo->info)->stab_info,
-                 o, &elf_section_data (o)->stab_info, contents)))
+                (output_bfd,
+                 &elf_hash_table (finfo->info)->stab_info,
+                 o, &elf_section_data (o)->sec_info, contents)))
+           return false;
+         break;
+       case ELF_INFO_TYPE_MERGE:
+         if (! (_bfd_write_merged_section
+                (output_bfd, o, elf_section_data (o)->sec_info)))
            return false;
+         break;
+       case ELF_INFO_TYPE_EH_FRAME:
+         {
+           asection *ehdrsec;
+
+           ehdrsec
+             = bfd_get_section_by_name (elf_hash_table (finfo->info)->dynobj,
+                                        ".eh_frame_hdr");
+           if (! (_bfd_elf_write_section_eh_frame (output_bfd, o, ehdrsec,
+                                                   contents)))
+             return false;
+         }
+         break;
+       default:
+         {
+           bfd_size_type sec_size;
+
+           sec_size = (o->_cooked_size != 0 ? o->_cooked_size : o->_raw_size);
+           if (! (o->flags & SEC_EXCLUDE)
+               && ! bfd_set_section_contents (output_bfd, o->output_section,
+                                              contents,
+                                              (file_ptr) o->output_offset,
+                                              sec_size))
+             return false;
+         }
+         break;
        }
     }
 
@@ -5953,7 +7035,7 @@ elf_reloc_link_order (output_bfd, info, output_section, link_order)
       struct elf_link_hash_entry *h;
 
       /* Treat a reloc against a defined symbol as though it were
-         actually against the section.  */
+        actually against the section.  */
       h = ((struct elf_link_hash_entry *)
           bfd_wrapped_link_hash_lookup (output_bfd, info,
                                         link_order->u.reloc.p->u.name,
@@ -5968,8 +7050,8 @@ elf_reloc_link_order (output_bfd, info, output_section, link_order)
          indx = section->output_section->target_index;
          *rel_hash_ptr = NULL;
          /* It seems that we ought to add the symbol value to the
-             addend here, but in practice it has already been added
-             because it was passed to constructor_callback.  */
+            addend here, but in practice it has already been added
+            because it was passed to constructor_callback.  */
          addend += section->output_section->vma + section->output_offset;
        }
       else if (h != NULL)
@@ -5998,28 +7080,31 @@ elf_reloc_link_order (output_bfd, info, output_section, link_order)
       bfd_reloc_status_type rstat;
       bfd_byte *buf;
       boolean ok;
+      const char *sym_name;
 
       size = bfd_get_reloc_size (howto);
       buf = (bfd_byte *) bfd_zmalloc (size);
       if (buf == (bfd_byte *) NULL)
        return false;
-      rstat = _bfd_relocate_contents (howto, output_bfd, addend, buf);
+      rstat = _bfd_relocate_contents (howto, output_bfd, (bfd_vma) addend, buf);
       switch (rstat)
        {
        case bfd_reloc_ok:
          break;
+
        default:
        case bfd_reloc_outofrange:
          abort ();
+
        case bfd_reloc_overflow:
+         if (link_order->type == bfd_section_reloc_link_order)
+           sym_name = bfd_section_name (output_bfd,
+                                        link_order->u.reloc.p->u.section);
+         else
+           sym_name = link_order->u.reloc.p->u.name;
          if (! ((*info->callbacks->reloc_overflow)
-                (info,
-                 (link_order->type == bfd_section_reloc_link_order
-                  ? bfd_section_name (output_bfd,
-                                      link_order->u.reloc.p->u.section)
-                  : link_order->u.reloc.p->u.name),
-                 howto->name, addend, (bfd *) NULL, (asection *) NULL,
-                 (bfd_vma) 0)))
+                (info, sym_name, howto->name, addend,
+                 (bfd *) NULL, (asection *) NULL, (bfd_vma) 0)))
            {
              free (buf);
              return false;
@@ -6044,32 +7129,54 @@ elf_reloc_link_order (output_bfd, info, output_section, link_order)
 
   if (rel_hdr->sh_type == SHT_REL)
     {
-      Elf_Internal_Rel irel;
+      bfd_size_type size;
+      Elf_Internal_Rel *irel;
       Elf_External_Rel *erel;
+      unsigned int i;
+
+      size = bed->s->int_rels_per_ext_rel * sizeof (Elf_Internal_Rel);
+      irel = (Elf_Internal_Rel *) bfd_zmalloc (size);
+      if (irel == NULL)
+       return false;
+
+      for (i = 0; i < bed->s->int_rels_per_ext_rel; i++)
+       irel[i].r_offset = offset;
+      irel[0].r_info = ELF_R_INFO (indx, howto->type);
 
-      irel.r_offset = offset;
-      irel.r_info = ELF_R_INFO (indx, howto->type);
       erel = ((Elf_External_Rel *) rel_hdr->contents
              + elf_section_data (output_section)->rel_count);
+
       if (bed->s->swap_reloc_out)
-       (*bed->s->swap_reloc_out) (output_bfd, &irel, (bfd_byte *) erel);
+       (*bed->s->swap_reloc_out) (output_bfd, irel, (bfd_byte *) erel);
       else
-       elf_swap_reloc_out (output_bfd, &irel, erel);
+       elf_swap_reloc_out (output_bfd, irel, erel);
+
+      free (irel);
     }
   else
     {
-      Elf_Internal_Rela irela;
+      bfd_size_type size;
+      Elf_Internal_Rela *irela;
       Elf_External_Rela *erela;
+      unsigned int i;
+
+      size = bed->s->int_rels_per_ext_rel * sizeof (Elf_Internal_Rela);
+      irela = (Elf_Internal_Rela *) bfd_zmalloc (size);
+      if (irela == NULL)
+       return false;
+
+      for (i = 0; i < bed->s->int_rels_per_ext_rel; i++)
+       irela[i].r_offset = offset;
+      irela[0].r_info = ELF_R_INFO (indx, howto->type);
+      irela[0].r_addend = addend;
 
-      irela.r_offset = offset;
-      irela.r_info = ELF_R_INFO (indx, howto->type);
-      irela.r_addend = addend;
       erela = ((Elf_External_Rela *) rel_hdr->contents
               + elf_section_data (output_section)->rel_count);
+
       if (bed->s->swap_reloca_out)
-       (*bed->s->swap_reloca_out) (output_bfd, &irela, (bfd_byte *) erela);
+       (*bed->s->swap_reloca_out) (output_bfd, irela, (bfd_byte *) erela);
       else
-       elf_swap_reloca_out (output_bfd, &irela, erela);
+       elf_swap_reloca_out (output_bfd, irela, erela);
     }
 
   ++elf_section_data (output_section)->rel_count;
@@ -6089,14 +7196,15 @@ elf_create_pointer_linker_section (abfd, info, lsect, h, rel)
 {
   elf_linker_section_pointers_t **ptr_linker_section_ptr = NULL;
   elf_linker_section_pointers_t *linker_section_ptr;
-  unsigned long r_symndx = ELF_R_SYM (rel->r_info);;
+  unsigned long r_symndx = ELF_R_SYM (rel->r_info);
+  bfd_size_type amt;
 
   BFD_ASSERT (lsect != NULL);
 
-  /* Is this a global symbol? */
+  /* Is this a global symbol?  */
   if (h != NULL)
     {
-      /* Has this symbol already been allocated, if so, our work is done */
+      /* Has this symbol already been allocated?  If so, our work is done.  */
       if (_bfd_elf_find_pointer_linker_section (h->linker_section_pointer,
                                                rel->r_addend,
                                                lsect->which))
@@ -6113,29 +7221,30 @@ elf_create_pointer_linker_section (abfd, info, lsect, h, rel)
       if (lsect->rel_section)
        lsect->rel_section->_raw_size += sizeof (Elf_External_Rela);
     }
-
-  else  /* Allocation of a pointer to a local symbol */
+  else
     {
+      /* Allocation of a pointer to a local symbol.  */
       elf_linker_section_pointers_t **ptr = elf_local_ptr_offsets (abfd);
 
-      /* Allocate a table to hold the local symbols if first time */
+      /* Allocate a table to hold the local symbols if first time */
       if (!ptr)
        {
          unsigned int num_symbols = elf_tdata (abfd)->symtab_hdr.sh_info;
          register unsigned int i;
 
-         ptr = (elf_linker_section_pointers_t **)
-           bfd_alloc (abfd, num_symbols * sizeof (elf_linker_section_pointers_t *));
+         amt = num_symbols;
+         amt *= sizeof (elf_linker_section_pointers_t *);
+         ptr = (elf_linker_section_pointers_t **) bfd_alloc (abfd, amt);
 
          if (!ptr)
            return false;
 
          elf_local_ptr_offsets (abfd) = ptr;
          for (i = 0; i < num_symbols; i++)
-           ptr[i] = (elf_linker_section_pointers_t *)0;
+           ptr[i] = (elf_linker_section_pointers_t *) 0;
        }
 
-      /* Has this symbol already been allocated, if so, our work is done */
+      /* Has this symbol already been allocated?  If so, our work is done.  */
       if (_bfd_elf_find_pointer_linker_section (ptr[r_symndx],
                                                rel->r_addend,
                                                lsect->which))
@@ -6153,11 +7262,11 @@ elf_create_pointer_linker_section (abfd, info, lsect, h, rel)
        }
     }
 
-  /* Allocate space for a pointer in the linker section, and allocate a new pointer record
-     from internal memory.  */
+  /* Allocate space for a pointer in the linker section, and allocate
+     a new pointer record from internal memory.  */
   BFD_ASSERT (ptr_linker_section_ptr != NULL);
-  linker_section_ptr = (elf_linker_section_pointers_t *)
-    bfd_alloc (abfd, sizeof (elf_linker_section_pointers_t));
+  amt = sizeof (elf_linker_section_pointers_t);
+  linker_section_ptr = (elf_linker_section_pointers_t *) bfd_alloc (abfd, amt);
 
   if (!linker_section_ptr)
     return false;
@@ -6171,17 +7280,19 @@ elf_create_pointer_linker_section (abfd, info, lsect, h, rel)
 #if 0
   if (lsect->hole_size && lsect->hole_offset < lsect->max_hole_offset)
     {
-      linker_section_ptr->offset = lsect->section->_raw_size - lsect->hole_size + (ARCH_SIZE / 8);
+      linker_section_ptr->offset = (lsect->section->_raw_size
+                                   - lsect->hole_size + (ARCH_SIZE / 8));
       lsect->hole_offset += ARCH_SIZE / 8;
       lsect->sym_offset  += ARCH_SIZE / 8;
-      if (lsect->sym_hash)     /* Bump up symbol value if needed */
+      if (lsect->sym_hash)
        {
+         /* Bump up symbol value if needed.  */
          lsect->sym_hash->root.u.def.value += ARCH_SIZE / 8;
 #ifdef DEBUG
          fprintf (stderr, "Bump up %s by %ld, current value = %ld\n",
                   lsect->sym_hash->root.root.string,
-                  (long)ARCH_SIZE / 8,
-                  (long)lsect->sym_hash->root.u.def.value);
+                  (long) ARCH_SIZE / 8,
+                  (long) lsect->sym_hash->root.u.def.value);
 #endif
        }
     }
@@ -6192,8 +7303,10 @@ elf_create_pointer_linker_section (abfd, info, lsect, h, rel)
   lsect->section->_raw_size += ARCH_SIZE / 8;
 
 #ifdef DEBUG
-  fprintf (stderr, "Create pointer in linker section %s, offset = %ld, section size = %ld\n",
-          lsect->name, (long)linker_section_ptr->offset, (long)lsect->section->_raw_size);
+  fprintf (stderr,
+          "Create pointer in linker section %s, offset = %ld, section size = %ld\n",
+          lsect->name, (long) linker_section_ptr->offset,
+          (long) lsect->section->_raw_size);
 #endif
 
   return true;
@@ -6206,10 +7319,11 @@ elf_create_pointer_linker_section (abfd, info, lsect, h, rel)
 #define bfd_put_ptr(BFD,VAL,ADDR) bfd_put_32 (BFD, VAL, ADDR)
 #endif
 
-/* Fill in the address for a pointer generated in alinker section.  */
+/* Fill in the address for a pointer generated in a linker section.  */
 
 bfd_vma
-elf_finish_pointer_linker_section (output_bfd, input_bfd, info, lsect, h, relocation, rel, relative_reloc)
+elf_finish_pointer_linker_section (output_bfd, input_bfd, info, lsect, h,
+                                  relocation, rel, relative_reloc)
      bfd *output_bfd;
      bfd *input_bfd;
      struct bfd_link_info *info;
@@ -6223,11 +7337,13 @@ elf_finish_pointer_linker_section (output_bfd, input_bfd, info, lsect, h, reloca
 
   BFD_ASSERT (lsect != NULL);
 
-  if (h != NULL)               /* global symbol */
+  if (h != NULL)
     {
-      linker_section_ptr = _bfd_elf_find_pointer_linker_section (h->linker_section_pointer,
-                                                                rel->r_addend,
-                                                                lsect->which);
+      /* Handle global symbol.  */
+      linker_section_ptr = (_bfd_elf_find_pointer_linker_section
+                           (h->linker_section_pointer,
+                            rel->r_addend,
+                            lsect->which));
 
       BFD_ASSERT (linker_section_ptr != NULL);
 
@@ -6247,23 +7363,27 @@ elf_finish_pointer_linker_section (output_bfd, input_bfd, info, lsect, h, reloca
          if (!linker_section_ptr->written_address_p)
            {
              linker_section_ptr->written_address_p = true;
-             bfd_put_ptr (output_bfd, relocation + linker_section_ptr->addend,
-                         lsect->section->contents + linker_section_ptr->offset);
+             bfd_put_ptr (output_bfd,
+                          relocation + linker_section_ptr->addend,
+                          (lsect->section->contents
+                           + linker_section_ptr->offset));
            }
        }
     }
-  else                         /* local symbol */
+  else
     {
+      /* Handle local symbol.  */
       unsigned long r_symndx = ELF_R_SYM (rel->r_info);
       BFD_ASSERT (elf_local_ptr_offsets (input_bfd) != NULL);
       BFD_ASSERT (elf_local_ptr_offsets (input_bfd)[r_symndx] != NULL);
-      linker_section_ptr = _bfd_elf_find_pointer_linker_section (elf_local_ptr_offsets (input_bfd)[r_symndx],
-                                                                rel->r_addend,
-                                                                lsect->which);
+      linker_section_ptr = (_bfd_elf_find_pointer_linker_section
+                           (elf_local_ptr_offsets (input_bfd)[r_symndx],
+                            rel->r_addend,
+                            lsect->which));
 
       BFD_ASSERT (linker_section_ptr != NULL);
 
-      /* Write out pointer if it hasn't been rewritten out before */
+      /* Write out pointer if it hasn't been rewritten out before */
       if (!linker_section_ptr->written_address_p)
        {
          linker_section_ptr->written_address_p = true;
@@ -6273,25 +7393,43 @@ elf_finish_pointer_linker_section (output_bfd, input_bfd, info, lsect, h, reloca
          if (info->shared)
            {
              asection *srel = lsect->rel_section;
-             Elf_Internal_Rela outrel;
+             Elf_Internal_Rela *outrel;
+             Elf_External_Rela *erel;
+             struct elf_backend_data *bed = get_elf_backend_data (output_bfd);
+             unsigned int i;
+             bfd_size_type amt;
+
+             amt = sizeof (Elf_Internal_Rela) * bed->s->int_rels_per_ext_rel;
+             outrel = (Elf_Internal_Rela *) bfd_zmalloc (amt);
+             if (outrel == NULL)
+               {
+                 (*_bfd_error_handler) (_("Error: out of memory"));
+                 return 0;
+               }
 
-             /* We need to generate a relative reloc for the dynamic linker.  */
+             /* We need to generate a relative reloc for the dynamic
+                linker.  */
              if (!srel)
-               lsect->rel_section = srel = bfd_get_section_by_name (elf_hash_table (info)->dynobj,
-                                                                    lsect->rel_name);
+               {
+                 srel = bfd_get_section_by_name (elf_hash_table (info)->dynobj,
+                                                 lsect->rel_name);
+                 lsect->rel_section = srel;
+               }
 
              BFD_ASSERT (srel != NULL);
 
-             outrel.r_offset = (lsect->section->output_section->vma
-                                + lsect->section->output_offset
-                                + linker_section_ptr->offset);
-             outrel.r_info = ELF_R_INFO (0, relative_reloc);
-             outrel.r_addend = 0;
-             elf_swap_reloca_out (output_bfd, &outrel,
-                                  (((Elf_External_Rela *)
-                                    lsect->section->contents)
-                                   + elf_section_data (lsect->section)->rel_count));
+             for (i = 0; i < bed->s->int_rels_per_ext_rel; i++)
+               outrel[i].r_offset = (lsect->section->output_section->vma
+                                     + lsect->section->output_offset
+                                     + linker_section_ptr->offset);
+             outrel[0].r_info = ELF_R_INFO (0, relative_reloc);
+             outrel[0].r_addend = 0;
+             erel = (Elf_External_Rela *) lsect->section->contents;
+             erel += elf_section_data (lsect->section)->rel_count;
+             elf_swap_reloca_out (output_bfd, outrel, erel);
              ++elf_section_data (lsect->section)->rel_count;
+
+             free (outrel);
            }
        }
     }
@@ -6302,8 +7440,9 @@ elf_finish_pointer_linker_section (output_bfd, input_bfd, info, lsect, h, reloca
                - lsect->sym_offset);
 
 #ifdef DEBUG
-  fprintf (stderr, "Finish pointer in linker section %s, offset = %ld (0x%lx)\n",
-          lsect->name, (long)relocation, (long)relocation);
+  fprintf (stderr,
+          "Finish pointer in linker section %s, offset = %ld (0x%lx)\n",
+          lsect->name, (long) relocation, (long) relocation);
 #endif
 
   /* Subtract out the addend, because it will get added back in by the normal
@@ -6338,7 +7477,8 @@ static boolean elf_gc_smash_unused_vtentry_relocs
   PARAMS ((struct elf_link_hash_entry *h, PTR dummy));
 
 /* The mark phase of garbage collection.  For a given section, mark
-   it, and all the sections which define symbols to which it refers.  */
+   it and any sections in this section's group, and all the sections
+   which define symbols to which it refers.  */
 
 static boolean
 elf_gc_mark (info, sec, gc_mark_hook)
@@ -6348,20 +7488,29 @@ elf_gc_mark (info, sec, gc_mark_hook)
        PARAMS ((bfd *, struct bfd_link_info *, Elf_Internal_Rela *,
                struct elf_link_hash_entry *, Elf_Internal_Sym *));
 {
-  boolean ret = true;
+  boolean ret;
+  asection *group_sec;
 
   sec->gc_mark = 1;
 
-  /* Look through the section relocs.  */
+  /* Mark all the sections in the group.  */
+  group_sec = elf_section_data (sec)->next_in_group;
+  if (group_sec && !group_sec->gc_mark)
+    if (!elf_gc_mark (info, group_sec, gc_mark_hook))
+      return false;
 
+  /* Look through the section relocs.  */
+  ret = true;
   if ((sec->flags & SEC_RELOC) != 0 && sec->reloc_count > 0)
     {
       Elf_Internal_Rela *relstart, *rel, *relend;
       Elf_Internal_Shdr *symtab_hdr;
+      Elf_Internal_Shdr *shndx_hdr;
       struct elf_link_hash_entry **sym_hashes;
       size_t nlocsyms;
       size_t extsymoff;
       Elf_External_Sym *locsyms, *freesyms = NULL;
+      Elf_External_Sym_Shndx *locsym_shndx;
       bfd *input_bfd = sec->owner;
       struct elf_backend_data *bed = get_elf_backend_data (input_bfd);
 
@@ -6375,29 +7524,39 @@ elf_gc_mark (info, sec, gc_mark_hook)
       if (elf_bad_symtab (input_bfd))
        {
          nlocsyms = symtab_hdr->sh_size / sizeof (Elf_External_Sym);
-          extsymoff = 0;
+         extsymoff = 0;
        }
       else
        extsymoff = nlocsyms = symtab_hdr->sh_info;
+
       if (symtab_hdr->contents)
        locsyms = (Elf_External_Sym *) symtab_hdr->contents;
       else if (nlocsyms == 0)
        locsyms = NULL;
       else
        {
-         locsyms = freesyms =
-           bfd_malloc (nlocsyms * sizeof (Elf_External_Sym));
+         bfd_size_type amt = nlocsyms * sizeof (Elf_External_Sym);
+         locsyms = freesyms = bfd_malloc (amt);
          if (freesyms == NULL
              || bfd_seek (input_bfd, symtab_hdr->sh_offset, SEEK_SET) != 0
-             || (bfd_read (locsyms, sizeof (Elf_External_Sym),
-                           nlocsyms, input_bfd)
-                 != nlocsyms * sizeof (Elf_External_Sym)))
+             || bfd_bread (locsyms, amt, input_bfd) != amt)
            {
              ret = false;
              goto out1;
            }
        }
 
+      shndx_hdr = &elf_tdata (input_bfd)->symtab_shndx_hdr;
+      locsym_shndx = NULL;
+      if (shndx_hdr->sh_size != 0 && nlocsyms != 0)
+       {
+         bfd_size_type amt = nlocsyms * sizeof (Elf_External_Sym_Shndx);
+         locsym_shndx = (Elf_External_Sym_Shndx *) bfd_malloc (amt);
+         if (bfd_seek (input_bfd, shndx_hdr->sh_offset, SEEK_SET) != 0
+             || bfd_bread (locsym_shndx, amt, input_bfd) != amt)
+           return false;
+       }
+
       /* Read the relocations.  */
       relstart = (NAME(_bfd_elf,link_read_relocs)
                  (sec->owner, sec, NULL, (Elf_Internal_Rela *) NULL,
@@ -6422,13 +7581,16 @@ elf_gc_mark (info, sec, gc_mark_hook)
 
          if (elf_bad_symtab (sec->owner))
            {
-             elf_swap_symbol_in (input_bfd, &locsyms[r_symndx], &s);
+             elf_swap_symbol_in (input_bfd,
+                                 locsyms + r_symndx,
+                                 locsym_shndx + (locsym_shndx ? r_symndx : 0),
+                                 &s);
              if (ELF_ST_BIND (s.st_info) == STB_LOCAL)
-               rsec = (*gc_mark_hook) (sec->owner, info, rel, NULL, &s);
+               rsec = (*gc_mark_hook) (sec->owner, info, rel, NULL, &s);
              else
                {
-                 h = sym_hashes[r_symndx - extsymoff];
-                 rsec = (*gc_mark_hook) (sec->owner, info, rel, h, NULL);
+                 h = sym_hashes[r_symndx - extsymoff];
+                 rsec = (*gc_mark_hook) (sec->owner, info, rel, h, NULL);
                }
            }
          else if (r_symndx >= nlocsyms)
@@ -6438,16 +7600,23 @@ elf_gc_mark (info, sec, gc_mark_hook)
            }
          else
            {
-             elf_swap_symbol_in (input_bfd, &locsyms[r_symndx], &s);
+             elf_swap_symbol_in (input_bfd,
+                                 locsyms + r_symndx,
+                                 locsym_shndx + (locsym_shndx ? r_symndx : 0),
+                                 &s);
              rsec = (*gc_mark_hook) (sec->owner, info, rel, NULL, &s);
            }
 
          if (rsec && !rsec->gc_mark)
-           if (!elf_gc_mark (info, rsec, gc_mark_hook))
-             {
-               ret = false;
-               goto out2;
-             }
+           {
+             if (bfd_get_flavour (rsec->owner) != bfd_target_elf_flavour)
+               rsec->gc_mark = 1;
+             else if (!elf_gc_mark (info, rsec, gc_mark_hook))
+               {
+                 ret = false;
+                 goto out2;
+               }
+           }
        }
 
     out2:
@@ -6546,6 +7715,9 @@ elf_gc_sweep_symbol (h, idxptr)
 {
   int *idx = (int *) idxptr;
 
+  if (h->root.type == bfd_link_hash_warning)
+    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
   if (h->dynindx != -1
       && ((h->root.type != bfd_link_hash_defined
           && h->root.type != bfd_link_hash_defweak)
@@ -6563,6 +7735,9 @@ elf_gc_propagate_vtable_entries_used (h, okp)
      struct elf_link_hash_entry *h;
      PTR okp;
 {
+  if (h->root.type == bfd_link_hash_warning)
+    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
   /* Those that are not vtables.  */
   if (h->vtable_parent == NULL)
     return true;
@@ -6596,11 +7771,17 @@ elf_gc_propagate_vtable_entries_used (h, okp)
       pu = h->vtable_parent->vtable_entries_used;
       if (pu != NULL)
        {
-         n = h->vtable_parent->vtable_entries_size / FILE_ALIGN;
-         while (--n != 0)
+         asection *sec = h->root.u.def.section;
+         struct elf_backend_data *bed = get_elf_backend_data (sec->owner);
+         int file_align = bed->s->file_align;
+
+         n = h->vtable_parent->vtable_entries_size / file_align;
+         while (n--)
            {
-             if (*pu) *cu = true;
-             pu++, cu++;
+             if (*pu)
+               *cu = true;
+             pu++;
+             cu++;
            }
        }
     }
@@ -6617,6 +7798,10 @@ elf_gc_smash_unused_vtentry_relocs (h, okp)
   bfd_vma hstart, hend;
   Elf_Internal_Rela *relstart, *relend, *rel;
   struct elf_backend_data *bed;
+  int file_align;
+
+  if (h->root.type == bfd_link_hash_warning)
+    h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
   /* Take care of both those symbols that do not describe vtables as
      well as those that are not loaded.  */
@@ -6633,8 +7818,10 @@ elf_gc_smash_unused_vtentry_relocs (h, okp)
   relstart = (NAME(_bfd_elf,link_read_relocs)
              (sec->owner, sec, NULL, (Elf_Internal_Rela *) NULL, true));
   if (!relstart)
-    return *(boolean *)okp = false;
+    return *(boolean *) okp = false;
   bed = get_elf_backend_data (sec->owner);
+  file_align = bed->s->file_align;
+
   relend = relstart + sec->reloc_count * bed->s->int_rels_per_ext_rel;
 
   for (rel = relstart; rel < relend; ++rel)
@@ -6644,7 +7831,7 @@ elf_gc_smash_unused_vtentry_relocs (h, okp)
        if (h->vtable_entries_used
            && (rel->r_offset - hstart) < h->vtable_entries_size)
          {
-           bfd_vma entry = (rel->r_offset - hstart) / FILE_ALIGN;
+           bfd_vma entry = (rel->r_offset - hstart) / file_align;
            if (h->vtable_entries_used[entry])
              continue;
          }
@@ -6665,8 +7852,8 @@ elf_gc_sections (abfd, info)
   boolean ok = true;
   bfd *sub;
   asection * (*gc_mark_hook)
-    PARAMS ((bfd *abfd, struct bfd_link_info *, Elf_Internal_Rela *,
-             struct elf_link_hash_entry *h, Elf_Internal_Sym *));
+    PARAMS ((bfd *, struct bfd_link_info *, Elf_Internal_Rela *,
+            struct elf_link_hash_entry *h, Elf_Internal_Sym *));
 
   if (!get_elf_backend_data (abfd)->can_gc_sections
       || info->relocateable || info->emitrelocations
@@ -6700,13 +7887,13 @@ elf_gc_sections (abfd, info)
       for (o = sub->sections; o != NULL; o = o->next)
        {
          if (o->flags & SEC_KEEP)
-           if (!elf_gc_mark (info, o, gc_mark_hook))
+           if (!elf_gc_mark (info, o, gc_mark_hook))
              return false;
        }
     }
 
   /* ... and mark SEC_EXCLUDE for those that go.  */
-  if (!elf_gc_sweep(info, get_elf_backend_data (abfd)->gc_sweep_hook))
+  if (!elf_gc_sweep (info, get_elf_backend_data (abfd)->gc_sweep_hook))
     return false;
 
   return true;
@@ -6748,12 +7935,12 @@ elf_gc_record_vtinherit (abfd, sec, h, offset)
     }
 
   (*_bfd_error_handler) ("%s: %s+%lu: No symbol found for INHERIT",
-                        bfd_get_filename (abfd), sec->name,
-                        (unsigned long)offset);
+                        bfd_archive_filename (abfd), sec->name,
+                        (unsigned long) offset);
   bfd_set_error (bfd_error_invalid_operation);
   return false;
 
-win:
+ win:
   if (!h)
     {
       /* This *should* only be the absolute section.  It could potentially
@@ -6778,6 +7965,9 @@ elf_gc_record_vtentry (abfd, sec, h, addend)
      struct elf_link_hash_entry *h;
      bfd_vma addend;
 {
+  struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  int file_align = bed->s->file_align;
+
   if (addend >= h->vtable_entries_size)
     {
       size_t size, bytes;
@@ -6800,22 +7990,23 @@ elf_gc_record_vtentry (abfd, sec, h, addend)
 
       /* Allocate one extra entry for use as a "done" flag for the
         consolidation pass.  */
-      bytes = (size / FILE_ALIGN + 1) * sizeof (boolean);
+      bytes = (size / file_align + 1) * sizeof (boolean);
 
       if (ptr)
        {
-         ptr = bfd_realloc (ptr - 1, bytes);
+         ptr = bfd_realloc (ptr - 1, (bfd_size_type) bytes);
 
          if (ptr != NULL)
            {
              size_t oldbytes;
 
-             oldbytes = (h->vtable_entries_size/FILE_ALIGN + 1) * sizeof (boolean);
-             memset (((char *)ptr) + oldbytes, 0, bytes - oldbytes);
+             oldbytes = ((h->vtable_entries_size / file_align + 1)
+                         * sizeof (boolean));
+             memset (((char *) ptr) + oldbytes, 0, bytes - oldbytes);
            }
        }
       else
-       ptr = bfd_zmalloc (bytes);
+       ptr = bfd_zmalloc ((bfd_size_type) bytes);
 
       if (ptr == NULL)
        return false;
@@ -6825,7 +8016,7 @@ elf_gc_record_vtentry (abfd, sec, h, addend)
       h->vtable_entries_size = size;
     }
 
-  h->vtable_entries_used[addend / FILE_ALIGN] = true;
+  h->vtable_entries_used[addend / file_align] = true;
 
   return true;
 }
@@ -6899,6 +8090,9 @@ elf_gc_allocate_got_offsets (h, offarg)
 {
   bfd_vma *off = (bfd_vma *) offarg;
 
+  if (h->root.type == bfd_link_hash_warning)
+    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
   if (h->got.refcount > 0)
     {
       h->got.offset = off[0];
@@ -6939,6 +8133,9 @@ elf_collect_hash_codes (h, data)
   unsigned long ha;
   char *alc = NULL;
 
+  if (h->root.type == bfd_link_hash_warning)
+    h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
   /* Ignore indirect symbols.  These are added by the versioning code.  */
   if (h->dynindx == -1)
     return true;
@@ -6947,8 +8144,8 @@ elf_collect_hash_codes (h, data)
   p = strchr (name, ELF_VER_CHR);
   if (p != NULL)
     {
-      alc = bfd_malloc (p - name + 1);
-      memcpy (alc, name, p - name);
+      alc = bfd_malloc ((bfd_size_type) (p - name + 1));
+      memcpy (alc, name, (size_t) (p - name));
       alc[p - name] = '\0';
       name = alc;
     }
@@ -6968,3 +8165,279 @@ elf_collect_hash_codes (h, data)
 
   return true;
 }
+
+boolean
+elf_reloc_symbol_deleted_p (offset, cookie)
+     bfd_vma offset;
+     PTR cookie;
+{
+  struct elf_reloc_cookie *rcookie = (struct elf_reloc_cookie *) cookie;
+
+  if (rcookie->bad_symtab)
+    rcookie->rel = rcookie->rels;
+
+  for (; rcookie->rel < rcookie->relend; rcookie->rel++)
+    {
+      unsigned long r_symndx = ELF_R_SYM (rcookie->rel->r_info);
+      Elf_Internal_Sym isym;
+
+      if (! rcookie->bad_symtab)
+       if (rcookie->rel->r_offset > offset)
+         return false;
+      if (rcookie->rel->r_offset != offset)
+       continue;
+
+      if (rcookie->locsyms && r_symndx < rcookie->locsymcount)
+       {
+         Elf_External_Sym *lsym;
+         Elf_External_Sym_Shndx *lshndx;
+
+         lsym = (Elf_External_Sym *) rcookie->locsyms + r_symndx;
+         lshndx = (Elf_External_Sym_Shndx *) rcookie->locsym_shndx;
+         if (lshndx != NULL)
+           lshndx += r_symndx;
+         elf_swap_symbol_in (rcookie->abfd, lsym, lshndx, &isym);
+       }
+
+      if (r_symndx >= rcookie->locsymcount
+         || (rcookie->locsyms
+             && ELF_ST_BIND (isym.st_info) != STB_LOCAL))
+       {
+         struct elf_link_hash_entry *h;
+
+         h = rcookie->sym_hashes[r_symndx - rcookie->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;
+
+         if ((h->root.type == bfd_link_hash_defined
+              || h->root.type == bfd_link_hash_defweak)
+             && elf_discarded_section (h->root.u.def.section))
+           return true;
+         else
+           return false;
+       }
+      else if (rcookie->locsyms)
+       {
+         /* It's not a relocation against a global symbol,
+            but it could be a relocation against a local
+            symbol for a discarded section.  */
+         asection *isec;
+
+         /* Need to: get the symbol; get the section.  */
+         if (isym.st_shndx < SHN_LORESERVE || isym.st_shndx > SHN_HIRESERVE)
+           {
+             isec = section_from_elf_index (rcookie->abfd, isym.st_shndx);
+             if (isec != NULL && elf_discarded_section (isec))
+               return true;
+           }
+       }
+      return false;
+    }
+  return false;
+}
+
+/* Discard unneeded references to discarded sections.
+   Returns true if any section's size was changed.  */
+/* This function assumes that the relocations are in sorted order,
+   which is true for all known assemblers.  */
+
+boolean
+elf_bfd_discard_info (output_bfd, info)
+     bfd *output_bfd;
+     struct bfd_link_info *info;
+{
+  struct elf_reloc_cookie cookie;
+  asection *stab, *eh, *ehdr;
+  Elf_Internal_Shdr *symtab_hdr;
+  Elf_Internal_Shdr *shndx_hdr;
+  Elf_External_Sym *freesyms;
+  struct elf_backend_data *bed;
+  bfd *abfd;
+  boolean ret = false;
+  boolean strip = info->strip == strip_all || info->strip == strip_debugger;
+
+  if (info->relocateable
+      || info->traditional_format
+      || info->hash->creator->flavour != bfd_target_elf_flavour
+      || ! is_elf_hash_table (info))
+    return false;
+
+  ehdr = NULL;
+  if (elf_hash_table (info)->dynobj != NULL)
+    ehdr = bfd_get_section_by_name (elf_hash_table (info)->dynobj,
+                                   ".eh_frame_hdr");
+
+  for (abfd = info->input_bfds; abfd != NULL; abfd = abfd->link_next)
+    {
+      if (bfd_get_flavour (abfd) != bfd_target_elf_flavour)
+       continue;
+
+      bed = get_elf_backend_data (abfd);
+
+      if ((abfd->flags & DYNAMIC) != 0)
+       continue;
+
+      eh = NULL;
+      if (ehdr)
+       {
+         eh = bfd_get_section_by_name (abfd, ".eh_frame");
+         if (eh && (eh->_raw_size == 0
+                    || bfd_is_abs_section (eh->output_section)))
+           eh = NULL;
+       }
+
+      stab = NULL;
+      if (!strip)
+       {
+         stab = bfd_get_section_by_name (abfd, ".stab");
+         if (stab && (stab->_raw_size == 0
+                      || bfd_is_abs_section (stab->output_section)))
+           stab = NULL;
+       }
+      if ((! stab
+          || elf_section_data(stab)->sec_info_type != ELF_INFO_TYPE_STABS)
+         && ! eh
+         && (strip || ! bed->elf_backend_discard_info))
+       continue;
+
+      symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+      shndx_hdr = &elf_tdata (abfd)->symtab_shndx_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 / sizeof (Elf_External_Sym);
+         cookie.extsymoff = 0;
+       }
+      else
+       {
+         cookie.locsymcount = symtab_hdr->sh_info;
+         cookie.extsymoff = symtab_hdr->sh_info;
+       }
+
+      freesyms = NULL;
+      if (symtab_hdr->contents)
+       cookie.locsyms = (void *) symtab_hdr->contents;
+      else if (cookie.locsymcount == 0)
+       cookie.locsyms = NULL;
+      else
+       {
+         bfd_size_type amt = cookie.locsymcount * sizeof (Elf_External_Sym);
+         cookie.locsyms = bfd_malloc (amt);
+         if (cookie.locsyms == NULL)
+           return false;
+         freesyms = cookie.locsyms;
+         if (bfd_seek (abfd, symtab_hdr->sh_offset, SEEK_SET) != 0
+             || bfd_bread (cookie.locsyms, amt, abfd) != amt)
+           {
+           error_ret_free_loc:
+             free (cookie.locsyms);
+             return false;
+           }
+       }
+
+      cookie.locsym_shndx = NULL;
+      if (shndx_hdr->sh_size != 0 && cookie.locsymcount != 0)
+       {
+         bfd_size_type amt;
+         amt = cookie.locsymcount * sizeof (Elf_External_Sym_Shndx);
+         cookie.locsym_shndx = bfd_malloc (amt);
+         if (cookie.locsym_shndx == NULL)
+           goto error_ret_free_loc;
+         if (bfd_seek (abfd, shndx_hdr->sh_offset, SEEK_SET) != 0
+             || bfd_bread (cookie.locsym_shndx, amt, abfd) != amt)
+           {
+             free (cookie.locsym_shndx);
+             goto error_ret_free_loc;
+           }
+       }
+
+      if (stab)
+       {
+         cookie.rels = (NAME(_bfd_elf,link_read_relocs)
+                        (abfd, stab, (PTR) NULL,
+                         (Elf_Internal_Rela *) NULL,
+                         info->keep_memory));
+         if (cookie.rels)
+           {
+             cookie.rel = cookie.rels;
+             cookie.relend =
+               cookie.rels + stab->reloc_count * bed->s->int_rels_per_ext_rel;
+             if (_bfd_discard_section_stabs (abfd, stab,
+                                             elf_section_data (stab)->sec_info,
+                                             elf_reloc_symbol_deleted_p,
+                                             &cookie))
+               ret = true;
+             if (! info->keep_memory)
+               free (cookie.rels);
+           }
+       }
+
+      if (eh)
+       {
+         cookie.rels = NULL;
+         cookie.rel = NULL;
+         cookie.relend = NULL;
+         if (eh->reloc_count)
+           cookie.rels = (NAME(_bfd_elf,link_read_relocs)
+                          (abfd, eh, (PTR) NULL, (Elf_Internal_Rela *) NULL,
+                           info->keep_memory));
+         if (cookie.rels)
+           {
+             cookie.rel = cookie.rels;
+             cookie.relend =
+               cookie.rels + eh->reloc_count * bed->s->int_rels_per_ext_rel;
+           }
+         if (_bfd_elf_discard_section_eh_frame (abfd, info, eh, ehdr,
+                                                elf_reloc_symbol_deleted_p,
+                                                &cookie))
+           ret = true;
+         if (! info->keep_memory)
+           free (cookie.rels);
+       }
+
+      if (bed->elf_backend_discard_info)
+       {
+         if (bed->elf_backend_discard_info (abfd, &cookie, info))
+           ret = true;
+       }
+
+      if (cookie.locsym_shndx != NULL)
+       free (cookie.locsym_shndx);
+
+      if (freesyms != NULL)
+       free (freesyms);
+    }
+
+  if (ehdr && _bfd_elf_discard_section_eh_frame_hdr (output_bfd, info, ehdr))
+    ret = true;
+  return ret;
+}
+
+static boolean
+elf_section_ignore_discarded_relocs (sec)
+     asection *sec;
+{
+  struct elf_backend_data *bed;
+
+  switch (elf_section_data (sec)->sec_info_type)
+    {
+    case ELF_INFO_TYPE_STABS:
+    case ELF_INFO_TYPE_EH_FRAME:
+      return true;
+    default:
+      break;
+    }
+
+  bed = get_elf_backend_data (sec->owner);
+  if (bed->elf_backend_ignore_discarded_relocs != NULL
+      && (*bed->elf_backend_ignore_discarded_relocs) (sec))
+    return true;
+
+  return false;
+}
This page took 0.095699 seconds and 4 git commands to generate.