2007-03-13 H.J. Lu <hongjiu.lu@intel.com>
[deliverable/binutils-gdb.git] / bfd / elf.c
index 447074426976789ce850af664db428ffcbe0df3f..05d45e1058c0d47814418575830921595dd4dc42 100644 (file)
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -1,7 +1,7 @@
 /* ELF executable support for BFD.
 
    Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
-   2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+   2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -19,8 +19,8 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
 
-/*  SECTION
-
+/*
+SECTION
        ELF backends
 
        BFD support for ELF formats is being worked on.
@@ -206,16 +206,32 @@ bfd_elf_hash (const char *namearg)
   return h & 0xffffffff;
 }
 
+/* DT_GNU_HASH hash function.  Do not change this function; you will
+   cause invalid hash tables to be generated.  */
+
+unsigned long
+bfd_elf_gnu_hash (const char *namearg)
+{
+  const unsigned char *name = (const unsigned char *) namearg;
+  unsigned long h = 5381;
+  unsigned char ch;
+
+  while ((ch = *name++) != '\0')
+    h = (h << 5) + h + ch;
+  return h & 0xffffffff;
+}
+
 bfd_boolean
 bfd_elf_mkobject (bfd *abfd)
 {
-  /* This just does initialization.  */
-  /* coff_mkobject zalloc's space for tdata.coff_obj_data ...  */
-  elf_tdata (abfd) = bfd_zalloc (abfd, sizeof (struct elf_obj_tdata));
-  if (elf_tdata (abfd) == 0)
-    return FALSE;
-  /* Since everything is done at close time, do we need any
-     initialization?  */
+  if (abfd->tdata.any == NULL)
+    {
+      abfd->tdata.any = bfd_zalloc (abfd, sizeof (struct elf_obj_tdata));
+      if (abfd->tdata.any == NULL)
+       return FALSE;
+    }
+
+  elf_tdata (abfd)->program_header_size = (bfd_size_type) -1;
 
   return TRUE;
 }
@@ -340,7 +356,7 @@ bfd_elf_get_elf_syms (bfd *ibfd,
   pos = symtab_hdr->sh_offset + symoffset * extsym_size;
   if (extsym_buf == NULL)
     {
-      alloc_ext = bfd_malloc (amt);
+      alloc_ext = bfd_malloc2 (symcount, extsym_size);
       extsym_buf = alloc_ext;
     }
   if (extsym_buf == NULL
@@ -359,7 +375,8 @@ bfd_elf_get_elf_syms (bfd *ibfd,
       pos = shndx_hdr->sh_offset + symoffset * sizeof (Elf_External_Sym_Shndx);
       if (extshndx_buf == NULL)
        {
-         alloc_extshndx = bfd_malloc (amt);
+         alloc_extshndx = bfd_malloc2 (symcount,
+                                       sizeof (Elf_External_Sym_Shndx));
          extshndx_buf = alloc_extshndx;
        }
       if (extshndx_buf == NULL
@@ -373,8 +390,7 @@ bfd_elf_get_elf_syms (bfd *ibfd,
 
   if (intsym_buf == NULL)
     {
-      bfd_size_type amt = symcount * sizeof (Elf_Internal_Sym);
-      intsym_buf = bfd_malloc (amt);
+      intsym_buf = bfd_malloc2 (symcount, sizeof (Elf_Internal_Sym));
       if (intsym_buf == NULL)
        goto out;
     }
@@ -384,7 +400,15 @@ bfd_elf_get_elf_syms (bfd *ibfd,
   for (esym = extsym_buf, isym = intsym_buf, shndx = extshndx_buf;
        isym < isymend;
        esym += extsym_size, isym++, shndx = shndx != NULL ? shndx + 1 : NULL)
-    (*bed->s->swap_symbol_in) (ibfd, esym, shndx, isym);
+    if (!(*bed->s->swap_symbol_in) (ibfd, esym, shndx, isym))
+      {
+       symoffset += (esym - (bfd_byte *) extsym_buf) / extsym_size;
+       (*_bfd_error_handler) (_("%B symbol number %lu references "
+                                "nonexistent SHT_SYMTAB_SHNDX section"),
+                              ibfd, (unsigned long) symoffset);
+       intsym_buf = NULL;
+       goto out;
+      }
 
  out:
   if (alloc_ext != NULL)
@@ -494,10 +518,10 @@ setup_group (bfd *abfd, Elf_Internal_Shdr *hdr, asection *newsect)
          /* We keep a list of elf section headers for group sections,
             so we can find them quickly.  */
          bfd_size_type amt;
-         
+
          elf_tdata (abfd)->num_group = num_group;
-         amt = num_group * sizeof (Elf_Internal_Shdr *);
-         elf_tdata (abfd)->group_sect_ptr = bfd_alloc (abfd, amt);
+         elf_tdata (abfd)->group_sect_ptr
+           = bfd_alloc2 (abfd, num_group, sizeof (Elf_Internal_Shdr *));
          if (elf_tdata (abfd)->group_sect_ptr == NULL)
            return FALSE;
 
@@ -517,7 +541,8 @@ setup_group (bfd *abfd, Elf_Internal_Shdr *hdr, asection *newsect)
                  /* Read the raw contents.  */
                  BFD_ASSERT (sizeof (*dest) >= 4);
                  amt = shdr->sh_size * sizeof (*dest) / 4;
-                 shdr->contents = bfd_alloc (abfd, amt);
+                 shdr->contents = bfd_alloc2 (abfd, shdr->sh_size,
+                                              sizeof (*dest) / 4);
                  if (shdr->contents == NULL
                      || bfd_seek (abfd, shdr->sh_offset, SEEK_SET) != 0
                      || (bfd_bread (shdr->contents, shdr->sh_size, abfd)
@@ -625,12 +650,56 @@ setup_group (bfd *abfd, Elf_Internal_Shdr *hdr, asection *newsect)
 }
 
 bfd_boolean
-_bfd_elf_setup_group_pointers (bfd *abfd)
+_bfd_elf_setup_sections (bfd *abfd)
 {
   unsigned int i;
   unsigned int num_group = elf_tdata (abfd)->num_group;
   bfd_boolean result = TRUE;
+  asection *s;
+
+  /* Process SHF_LINK_ORDER.  */
+  for (s = abfd->sections; s != NULL; s = s->next)
+    {
+      Elf_Internal_Shdr *this_hdr = &elf_section_data (s)->this_hdr;
+      if ((this_hdr->sh_flags & SHF_LINK_ORDER) != 0)
+       {
+         unsigned int elfsec = this_hdr->sh_link;
+         /* FIXME: The old Intel compiler and old strip/objcopy may
+            not set the sh_link or sh_info fields.  Hence we could
+            get the situation where elfsec is 0.  */
+         if (elfsec == 0)
+           {
+             const struct elf_backend_data *bed
+               = get_elf_backend_data (abfd);
+             if (bed->link_order_error_handler)
+               bed->link_order_error_handler
+                 (_("%B: warning: sh_link not set for section `%A'"),
+                  abfd, s);
+           }
+         else
+           {
+             asection *link;
+
+             this_hdr = elf_elfsections (abfd)[elfsec];
 
+             /* PR 1991, 2008:
+                Some strip/objcopy may leave an incorrect value in
+                sh_link.  We don't want to proceed.  */
+             link = this_hdr->bfd_section;
+             if (link == NULL)
+               {
+                 (*_bfd_error_handler)
+                   (_("%B: sh_link [%d] in section `%A' is incorrect"),
+                    s->owner, s, elfsec);
+                 result = FALSE;
+               }
+
+             elf_linked_to_section (s) = link;
+           }
+       }
+    }
+
+  /* Process section groups.  */
   if (num_group == (unsigned) -1)
     return result;
 
@@ -755,22 +824,22 @@ _bfd_elf_make_section_from_shdr (bfd *abfd,
          int len;
        } debug_sections [] =
        {
-         { "debug",             5  },  /* 'd' */
+         { STRING_COMMA_LEN ("debug") },       /* 'd' */
          { NULL,                0  },  /* 'e' */
          { NULL,                0  },  /* 'f' */
-         { "gnu.linkonce.wi.", 17 },   /* 'g' */
+         { STRING_COMMA_LEN ("gnu.linkonce.wi.") },    /* 'g' */
          { NULL,                0  },  /* 'h' */
          { NULL,                0  },  /* 'i' */
          { NULL,                0  },  /* 'j' */
          { NULL,                0  },  /* 'k' */
-         { "line",              4  },  /* 'l' */
+         { STRING_COMMA_LEN ("line") },        /* 'l' */
          { NULL,                0  },  /* 'm' */
          { NULL,                0  },  /* 'n' */
          { NULL,                0  },  /* 'o' */
          { NULL,                0  },  /* 'p' */
          { NULL,                0  },  /* 'q' */
          { NULL,                0  },  /* 'r' */
-         { "stab",              4  }   /* 's' */
+         { STRING_COMMA_LEN ("stab") } /* 's' */
        };
       
       if (name [0] == '.')
@@ -791,7 +860,7 @@ _bfd_elf_make_section_from_shdr (bfd *abfd,
      The symbols will be defined as weak, so that multiple definitions
      are permitted.  The GNU linker extension is to actually discard
      all but one of the sections.  */
-  if (strncmp (name, ".gnu.linkonce", sizeof ".gnu.linkonce" - 1) == 0
+  if (CONST_STRNEQ (name, ".gnu.linkonce")
       && elf_next_in_group (newsect) == NULL)
     flags |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD;
 
@@ -1023,6 +1092,28 @@ _bfd_elf_copy_private_bfd_data (bfd *ibfd, bfd *obfd)
   return TRUE;
 }
 
+static const char *
+get_segment_type (unsigned int p_type)
+{
+  const char *pt;
+  switch (p_type)
+    {
+    case PT_NULL: pt = "NULL"; break;
+    case PT_LOAD: pt = "LOAD"; break;
+    case PT_DYNAMIC: pt = "DYNAMIC"; break;
+    case PT_INTERP: pt = "INTERP"; break;
+    case PT_NOTE: pt = "NOTE"; break;
+    case PT_SHLIB: pt = "SHLIB"; break;
+    case PT_PHDR: pt = "PHDR"; break;
+    case PT_TLS: pt = "TLS"; break;
+    case PT_GNU_EH_FRAME: pt = "EH_FRAME"; break;
+    case PT_GNU_STACK: pt = "STACK"; break;
+    case PT_GNU_RELRO: pt = "RELRO"; break;
+    default: pt = NULL; break;
+    }
+  return pt;
+}
+
 /* Print out the program headers.  */
 
 bfd_boolean
@@ -1042,23 +1133,13 @@ _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg)
       c = elf_elfheader (abfd)->e_phnum;
       for (i = 0; i < c; i++, p++)
        {
-         const char *pt;
+         const char *pt = get_segment_type (p->p_type);
          char buf[20];
 
-         switch (p->p_type)
+         if (pt == NULL)
            {
-           case PT_NULL: pt = "NULL"; break;
-           case PT_LOAD: pt = "LOAD"; break;
-           case PT_DYNAMIC: pt = "DYNAMIC"; break;
-           case PT_INTERP: pt = "INTERP"; break;
-           case PT_NOTE: pt = "NOTE"; break;
-           case PT_SHLIB: pt = "SHLIB"; break;
-           case PT_PHDR: pt = "PHDR"; break;
-           case PT_TLS: pt = "TLS"; break;
-           case PT_GNU_EH_FRAME: pt = "EH_FRAME"; break;
-           case PT_GNU_STACK: pt = "STACK"; break;
-           case PT_GNU_RELRO: pt = "RELRO"; break;
-           default: sprintf (buf, "0x%lx", p->p_type); pt = buf; break;
+             sprintf (buf, "0x%lx", p->p_type);
+             pt = buf;
            }
          fprintf (f, "%8s off    0x", pt);
          bfd_fprintf_vma (abfd, f, p->p_offset);
@@ -1182,6 +1263,7 @@ _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg)
            case DT_AUXILIARY: name = "AUXILIARY"; stringp = TRUE; break;
            case DT_USED: name = "USED"; break;
            case DT_FILTER: name = "FILTER"; stringp = TRUE; break;
+           case DT_GNU_HASH: name = "GNU_HASH"; break;
            }
 
          fprintf (f, "  %-11s ", name);
@@ -1219,8 +1301,9 @@ _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg)
       for (t = elf_tdata (abfd)->verdef; t != NULL; t = t->vd_nextdef)
        {
          fprintf (f, "%d 0x%2.2x 0x%8.8lx %s\n", t->vd_ndx,
-                  t->vd_flags, t->vd_hash, t->vd_nodename);
-         if (t->vd_auxptr->vda_nextptr != NULL)
+                  t->vd_flags, t->vd_hash,
+                  t->vd_nodename ? t->vd_nodename : "<corrupt>");
+         if (t->vd_auxptr != NULL && t->vd_auxptr->vda_nextptr != NULL)
            {
              Elf_Internal_Verdaux *a;
 
@@ -1228,7 +1311,8 @@ _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg)
              for (a = t->vd_auxptr->vda_nextptr;
                   a != NULL;
                   a = a->vda_nextptr)
-               fprintf (f, "%s ", a->vda_nodename);
+               fprintf (f, "%s ",
+                        a->vda_nodename ? a->vda_nodename : "<corrupt>");
              fprintf (f, "\n");
            }
        }
@@ -1243,10 +1327,12 @@ _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg)
        {
          Elf_Internal_Vernaux *a;
 
-         fprintf (f, _("  required from %s:\n"), t->vn_filename);
+         fprintf (f, _("  required from %s:\n"),
+                  t->vn_filename ? t->vn_filename : "<corrupt>");
          for (a = t->vn_auxptr; a != NULL; a = a->vna_nextptr)
            fprintf (f, "    0x%8.8lx 0x%2.2x %2.2d %s\n", a->vna_hash,
-                    a->vna_flags, a->vna_other, a->vna_nodename);
+                    a->vna_flags, a->vna_other,
+                    a->vna_nodename ? a->vna_nodename : "<corrupt>");
        }
     }
 
@@ -1424,12 +1510,11 @@ _bfd_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
    old indirect symbol.  Also used for copying flags to a weakdef.  */
 
 void
-_bfd_elf_link_hash_copy_indirect (const struct elf_backend_data *bed,
+_bfd_elf_link_hash_copy_indirect (struct bfd_link_info *info,
                                  struct elf_link_hash_entry *dir,
                                  struct elf_link_hash_entry *ind)
 {
-  bfd_signed_vma tmp;
-  bfd_signed_vma lowest_valid = bed->can_refcount;
+  struct elf_link_hash_table *htab;
 
   /* Copy down any references that we may have already seen to the
      symbol which just became indirect.  */
@@ -1446,33 +1531,32 @@ _bfd_elf_link_hash_copy_indirect (const struct elf_backend_data *bed,
 
   /* Copy over the global and procedure linkage table refcount entries.
      These may have been already set up by a check_relocs routine.  */
-  tmp = dir->got.refcount;
-  if (tmp < lowest_valid)
+  htab = elf_hash_table (info);
+  if (ind->got.refcount > htab->init_got_refcount.refcount)
     {
-      dir->got.refcount = ind->got.refcount;
-      ind->got.refcount = tmp;
+      if (dir->got.refcount < 0)
+       dir->got.refcount = 0;
+      dir->got.refcount += ind->got.refcount;
+      ind->got.refcount = htab->init_got_refcount.refcount;
     }
-  else
-    BFD_ASSERT (ind->got.refcount < lowest_valid);
 
-  tmp = dir->plt.refcount;
-  if (tmp < lowest_valid)
+  if (ind->plt.refcount > htab->init_plt_refcount.refcount)
     {
-      dir->plt.refcount = ind->plt.refcount;
-      ind->plt.refcount = tmp;
+      if (dir->plt.refcount < 0)
+       dir->plt.refcount = 0;
+      dir->plt.refcount += ind->plt.refcount;
+      ind->plt.refcount = htab->init_plt_refcount.refcount;
     }
-  else
-    BFD_ASSERT (ind->plt.refcount < lowest_valid);
 
-  if (dir->dynindx == -1)
+  if (ind->dynindx != -1)
     {
+      if (dir->dynindx != -1)
+       _bfd_elf_strtab_delref (htab->dynstr, dir->dynstr_index);
       dir->dynindx = ind->dynindx;
       dir->dynstr_index = ind->dynstr_index;
       ind->dynindx = -1;
       ind->dynstr_index = 0;
     }
-  else
-    BFD_ASSERT (ind->dynindx == -1);
 }
 
 void
@@ -1502,34 +1586,21 @@ _bfd_elf_link_hash_table_init
    bfd *abfd,
    struct bfd_hash_entry *(*newfunc) (struct bfd_hash_entry *,
                                      struct bfd_hash_table *,
-                                     const char *))
+                                     const char *),
+   unsigned int entsize)
 {
   bfd_boolean ret;
   int can_refcount = get_elf_backend_data (abfd)->can_refcount;
 
-  table->dynamic_sections_created = FALSE;
-  table->dynobj = NULL;
+  memset (table, 0, sizeof * table);
   table->init_got_refcount.refcount = can_refcount - 1;
   table->init_plt_refcount.refcount = can_refcount - 1;
   table->init_got_offset.offset = -(bfd_vma) 1;
   table->init_plt_offset.offset = -(bfd_vma) 1;
   /* The first dynamic symbol is a dummy.  */
   table->dynsymcount = 1;
-  table->dynstr = NULL;
-  table->bucketcount = 0;
-  table->needed = NULL;
-  table->hgot = NULL;
-  table->merge_info = NULL;
-  memset (&table->stab_info, 0, sizeof (table->stab_info));
-  memset (&table->eh_info, 0, sizeof (table->eh_info));
-  table->dynlocal = NULL;
-  table->runpath = NULL;
-  table->tls_sec = NULL;
-  table->tls_size = 0;
-  table->loaded = NULL;
-  table->is_relocatable_executable = FALSE;
-
-  ret = _bfd_link_hash_table_init (&table->root, abfd, newfunc);
+
+  ret = _bfd_link_hash_table_init (&table->root, abfd, newfunc, entsize);
   table->root.type = bfd_link_elf_hash_table;
 
   return ret;
@@ -1547,7 +1618,8 @@ _bfd_elf_link_hash_table_create (bfd *abfd)
   if (ret == NULL)
     return NULL;
 
-  if (! _bfd_elf_link_hash_table_init (ret, abfd, _bfd_elf_link_hash_newfunc))
+  if (! _bfd_elf_link_hash_table_init (ret, abfd, _bfd_elf_link_hash_newfunc,
+                                      sizeof (struct elf_link_hash_entry)))
     {
       free (ret);
       return NULL;
@@ -1581,7 +1653,7 @@ bfd_elf_get_dyn_lib_class (bfd *abfd)
 }
 
 void
-bfd_elf_set_dyn_lib_class (bfd *abfd, int lib_class)
+bfd_elf_set_dyn_lib_class (bfd *abfd, enum dynamic_lib_link_class lib_class)
 {
   if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
       && bfd_get_format (abfd) == bfd_object)
@@ -1761,6 +1833,7 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
     case SHT_FINI_ARRAY:       /* .fini_array section.  */
     case SHT_PREINIT_ARRAY:    /* .preinit_array section.  */
     case SHT_GNU_LIBLIST:      /* .gnu.liblist section.  */
+    case SHT_GNU_HASH:         /* .gnu.hash section.  */
       return _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex);
 
     case SHT_DYNAMIC:  /* Dynamic linking information.  */
@@ -1998,15 +2071,16 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
           represent such a section, so at least for now, we don't
           try.  We just present it as a normal section.  We also
           can't use it as a reloc section if it points to the null
-          section.  */
-       if (hdr->sh_link != elf_onesymtab (abfd) || hdr->sh_info == SHN_UNDEF)
+          section, an invalid section, or another reloc section.  */
+       if (hdr->sh_link != elf_onesymtab (abfd)
+           || hdr->sh_info == SHN_UNDEF
+           || (hdr->sh_info >= SHN_LORESERVE && hdr->sh_info <= SHN_HIRESERVE)
+           || hdr->sh_info >= num_sec
+           || elf_elfsections (abfd)[hdr->sh_info]->sh_type == SHT_REL
+           || elf_elfsections (abfd)[hdr->sh_info]->sh_type == SHT_RELA)
          return _bfd_elf_make_section_from_shdr (abfd, hdr, name,
                                                  shindex);
 
-       /* Prevent endless recursion on broken objects.  */
-       if (elf_elfsections (abfd)[hdr->sh_info]->sh_type == SHT_REL
-           || elf_elfsections (abfd)[hdr->sh_info]->sh_type == SHT_RELA)
-         return FALSE;
        if (! bfd_section_from_shdr (abfd, hdr->sh_info))
          return FALSE;
        target_sect = bfd_section_from_elf_index (abfd, hdr->sh_info);
@@ -2037,13 +2111,11 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
        abfd->flags |= HAS_RELOC;
        return TRUE;
       }
-      break;
 
     case SHT_GNU_verdef:
       elf_dynverdef (abfd) = shindex;
       elf_tdata (abfd)->dynverdef_hdr = *hdr;
       return _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex);
-      break;
 
     case SHT_GNU_versym:
       if (hdr->sh_entsize != sizeof (Elf_External_Versym))
@@ -2051,13 +2123,11 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
       elf_dynversym (abfd) = shindex;
       elf_tdata (abfd)->dynversym_hdr = *hdr;
       return _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex);
-      break;
 
     case SHT_GNU_verneed:
       elf_dynverref (abfd) = shindex;
       elf_tdata (abfd)->dynverref_hdr = *hdr;
       return _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex);
-      break;
 
     case SHT_SHLIB:
       return TRUE;
@@ -2097,8 +2167,52 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
 
     default:
       /* Check for any processor-specific section types.  */
-      return bed->elf_backend_section_from_shdr (abfd, hdr, name,
-                                                shindex);
+      if (bed->elf_backend_section_from_shdr (abfd, hdr, name, shindex))
+       return TRUE;
+
+      if (hdr->sh_type >= SHT_LOUSER && hdr->sh_type <= SHT_HIUSER)
+       {
+         if ((hdr->sh_flags & SHF_ALLOC) != 0)
+           /* FIXME: How to properly handle allocated section reserved
+              for applications?  */
+           (*_bfd_error_handler)
+             (_("%B: don't know how to handle allocated, application "
+                "specific section `%s' [0x%8x]"),
+              abfd, name, hdr->sh_type);
+         else
+           /* Allow sections reserved for applications.  */
+           return _bfd_elf_make_section_from_shdr (abfd, hdr, name,
+                                                   shindex);
+       }
+      else if (hdr->sh_type >= SHT_LOPROC
+              && hdr->sh_type <= SHT_HIPROC)
+       /* FIXME: We should handle this section.  */
+       (*_bfd_error_handler)
+         (_("%B: don't know how to handle processor specific section "
+            "`%s' [0x%8x]"),
+          abfd, name, hdr->sh_type);
+      else if (hdr->sh_type >= SHT_LOOS && hdr->sh_type <= SHT_HIOS)
+       {
+         /* Unrecognised OS-specific sections.  */
+         if ((hdr->sh_flags & SHF_OS_NONCONFORMING) != 0)
+           /* SHF_OS_NONCONFORMING indicates that special knowledge is
+              required to correctly process the section and the file should 
+              be rejected with an error message.  */
+           (*_bfd_error_handler)
+             (_("%B: don't know how to handle OS specific section "
+                "`%s' [0x%8x]"),
+              abfd, name, hdr->sh_type);
+         else
+           /* Otherwise it should be processed.  */
+           return _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex);
+       }
+      else
+       /* FIXME: We should handle this section.  */
+       (*_bfd_error_handler)
+         (_("%B: don't know how to handle section `%s' [0x%8x]"),
+          abfd, name, hdr->sh_type);
+
+      return FALSE;
     }
 
   return TRUE;
@@ -2158,108 +2272,111 @@ bfd_section_from_elf_index (bfd *abfd, unsigned int index)
 
 static const struct bfd_elf_special_section special_sections_b[] =
 {
-  { ".bss",            4, -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".bss"), -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE },
+  { NULL,                   0,  0, 0,            0 }
 };
 
 static const struct bfd_elf_special_section special_sections_c[] =
 {
-  { ".comment",        8,  0, SHT_PROGBITS, 0 },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".comment"), 0, SHT_PROGBITS, 0 },
+  { NULL,                       0, 0, 0,            0 }
 };
 
 static const struct bfd_elf_special_section special_sections_d[] =
 {
-  { ".data",           5, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
-  { ".data1",          6,  0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
-  { ".debug",          6,  0, SHT_PROGBITS, 0 },
-  { ".debug_line",    11,  0, SHT_PROGBITS, 0 },
-  { ".debug_info",    11,  0, SHT_PROGBITS, 0 },
-  { ".debug_abbrev",  13,  0, SHT_PROGBITS, 0 },
-  { ".debug_aranges", 14,  0, SHT_PROGBITS, 0 },
-  { ".dynamic",        8,  0, SHT_DYNAMIC,  SHF_ALLOC },
-  { ".dynstr",         7,  0, SHT_STRTAB,   SHF_ALLOC },
-  { ".dynsym",         7,  0, SHT_DYNSYM,   SHF_ALLOC },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".data"),         -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".data1"),         0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".debug"),         0, SHT_PROGBITS, 0 },
+  { STRING_COMMA_LEN (".debug_line"),    0, SHT_PROGBITS, 0 },
+  { STRING_COMMA_LEN (".debug_info"),    0, SHT_PROGBITS, 0 },
+  { STRING_COMMA_LEN (".debug_abbrev"),  0, SHT_PROGBITS, 0 },
+  { STRING_COMMA_LEN (".debug_aranges"), 0, SHT_PROGBITS, 0 },
+  { STRING_COMMA_LEN (".dynamic"),       0, SHT_DYNAMIC,  SHF_ALLOC },
+  { STRING_COMMA_LEN (".dynstr"),        0, SHT_STRTAB,   SHF_ALLOC },
+  { STRING_COMMA_LEN (".dynsym"),        0, SHT_DYNSYM,   SHF_ALLOC },
+  { NULL,                      0,        0, 0,            0 }
 };
 
 static const struct bfd_elf_special_section special_sections_f[] =
 {
-  { ".fini",           5,  0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { ".fini_array",    11,  0, SHT_FINI_ARRAY, SHF_ALLOC + SHF_WRITE },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".fini"),       0, SHT_PROGBITS,   SHF_ALLOC + SHF_EXECINSTR },
+  { STRING_COMMA_LEN (".fini_array"), 0, SHT_FINI_ARRAY, SHF_ALLOC + SHF_WRITE },
+  { NULL,                          0, 0, 0,              0 }
 };
 
 static const struct bfd_elf_special_section special_sections_g[] =
 {
-  { ".gnu.linkonce.b",15, -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE },
-  { ".got",            4,  0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
-  { ".gnu.version",   12,  0, SHT_GNU_versym, 0 },
-  { ".gnu.version_d", 14,  0, SHT_GNU_verdef, 0 },
-  { ".gnu.version_r", 14,  0, SHT_GNU_verneed, 0 },
-  { ".gnu.liblist",   12,  0, SHT_GNU_LIBLIST, SHF_ALLOC },
-  { ".gnu.conflict",  13,  0, SHT_RELA,     SHF_ALLOC },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".gnu.linkonce.b"), -2, SHT_NOBITS,      SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".got"),             0, SHT_PROGBITS,    SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".gnu.version"),     0, SHT_GNU_versym,  0 },
+  { STRING_COMMA_LEN (".gnu.version_d"),   0, SHT_GNU_verdef,  0 },
+  { STRING_COMMA_LEN (".gnu.version_r"),   0, SHT_GNU_verneed, 0 },
+  { STRING_COMMA_LEN (".gnu.liblist"),     0, SHT_GNU_LIBLIST, SHF_ALLOC },
+  { STRING_COMMA_LEN (".gnu.conflict"),    0, SHT_RELA,        SHF_ALLOC },
+  { STRING_COMMA_LEN (".gnu.hash"),        0, SHT_GNU_HASH,    SHF_ALLOC },
+  { NULL,                        0,        0, 0,               0 }
 };
 
 static const struct bfd_elf_special_section special_sections_h[] =
 {
-  { ".hash",           5,  0, SHT_HASH,     SHF_ALLOC },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".hash"), 0, SHT_HASH,     SHF_ALLOC },
+  { NULL,                    0, 0, 0,            0 }
 };
 
 static const struct bfd_elf_special_section special_sections_i[] =
 {
-  { ".init",           5,  0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { ".init_array",    11,  0, SHT_INIT_ARRAY, SHF_ALLOC + SHF_WRITE },
-  { ".interp",         7,  0, SHT_PROGBITS, 0 },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".init"),       0, SHT_PROGBITS,   SHF_ALLOC + SHF_EXECINSTR },
+  { STRING_COMMA_LEN (".init_array"), 0, SHT_INIT_ARRAY, SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".interp"),     0, SHT_PROGBITS,   0 },
+  { NULL,                      0,     0, 0,              0 }
 };
 
 static const struct bfd_elf_special_section special_sections_l[] =
 {
-  { ".line",           5,  0, SHT_PROGBITS, 0 },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".line"), 0, SHT_PROGBITS, 0 },
+  { NULL,                    0, 0, 0,            0 }
 };
 
 static const struct bfd_elf_special_section special_sections_n[] =
 {
-  { ".note.GNU-stack",15,  0, SHT_PROGBITS, 0 },
-  { ".note",           5, -1, SHT_NOTE,     0 },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".note.GNU-stack"), 0, SHT_PROGBITS, 0 },
+  { STRING_COMMA_LEN (".note"),          -1, SHT_NOTE,     0 },
+  { NULL,                    0,           0, 0,            0 }
 };
 
 static const struct bfd_elf_special_section special_sections_p[] =
 {
-  { ".preinit_array", 14,  0, SHT_PREINIT_ARRAY, SHF_ALLOC + SHF_WRITE },
-  { ".plt",            4,  0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".preinit_array"), 0, SHT_PREINIT_ARRAY, SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".plt"),           0, SHT_PROGBITS,      SHF_ALLOC + SHF_EXECINSTR },
+  { NULL,                   0,           0, 0,                 0 }
 };
 
 static const struct bfd_elf_special_section special_sections_r[] =
 {
-  { ".rodata",         7, -2, SHT_PROGBITS, SHF_ALLOC },
-  { ".rodata1",        8,  0, SHT_PROGBITS, SHF_ALLOC },
-  { ".rela",           5, -1, SHT_RELA,     0 },
-  { ".rel",            4, -1, SHT_REL,      0 },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".rodata"), -2, SHT_PROGBITS, SHF_ALLOC },
+  { STRING_COMMA_LEN (".rodata1"), 0, SHT_PROGBITS, SHF_ALLOC },
+  { STRING_COMMA_LEN (".rela"),   -1, SHT_RELA,     0 },
+  { STRING_COMMA_LEN (".rel"),    -1, SHT_REL,      0 },
+  { NULL,                   0,     0, 0,            0 }
 };
 
 static const struct bfd_elf_special_section special_sections_s[] =
 {
-  { ".shstrtab",       9,  0, SHT_STRTAB,   0 },
-  { ".strtab",         7,  0, SHT_STRTAB,   0 },
-  { ".symtab",         7,  0, SHT_SYMTAB,   0 },
-  { ".stabstr",        5,  3, SHT_STRTAB,   0 },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".shstrtab"), 0, SHT_STRTAB, 0 },
+  { STRING_COMMA_LEN (".strtab"),   0, SHT_STRTAB, 0 },
+  { STRING_COMMA_LEN (".symtab"),   0, SHT_SYMTAB, 0 },
+  /* See struct bfd_elf_special_section declaration for the semantics of
+     this special case where .prefix_length != strlen (.prefix).  */
+  { ".stabstr",                        5,  3, SHT_STRTAB, 0 },
+  { NULL,                       0,  0, 0,          0 }
 };
 
 static const struct bfd_elf_special_section special_sections_t[] =
 {
-  { ".text",           5, -2, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { ".tbss",           5, -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE + SHF_TLS },
-  { ".tdata",          6, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_TLS },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".text"),  -2, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
+  { STRING_COMMA_LEN (".tbss"),  -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE + SHF_TLS },
+  { STRING_COMMA_LEN (".tdata"), -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_TLS },
+  { NULL,                     0,  0, 0,            0 }
 };
 
 static const struct bfd_elf_special_section *special_sections[] =
@@ -2334,15 +2451,27 @@ _bfd_elf_get_special_section (const char *name,
 }
 
 const struct bfd_elf_special_section *
-_bfd_elf_get_sec_type_attr (bfd *abfd ATTRIBUTE_UNUSED, asection *sec)
+_bfd_elf_get_sec_type_attr (bfd *abfd, asection *sec)
 {
   int i;
   const struct bfd_elf_special_section *spec;
+  const struct elf_backend_data *bed;
 
   /* See if this is one of the special sections.  */
   if (sec->name == NULL)
     return NULL;
 
+  bed = get_elf_backend_data (abfd);
+  spec = bed->special_sections;
+  if (spec)
+    {
+      spec = _bfd_elf_get_special_section (sec->name,
+                                          bed->special_sections,
+                                          sec->use_rela_p);
+      if (spec != NULL)
+       return spec;
+    }
+
   if (sec->name[0] != '.')
     return NULL;
 
@@ -2378,10 +2507,13 @@ _bfd_elf_new_section_hook (bfd *abfd, asection *sec)
   bed = get_elf_backend_data (abfd);
   sec->use_rela_p = bed->default_use_rela_p;
 
-  /* When we read a file, we don't need section type and flags unless
-     it is a linker created section.  They will be overridden in
-     _bfd_elf_make_section_from_shdr anyway.  */
-  if (abfd->direction != read_direction
+  /* When we read a file, we don't need to set ELF section type and
+     flags.  They will be overridden in _bfd_elf_make_section_from_shdr
+     anyway.  We will set ELF section type and flags for all linker
+     created sections.  If user specifies BFD section flags, we will
+     set ELF section type and flags based on BFD section flags in
+     elf_fake_sections.  */
+  if ((!sec->flags && abfd->direction != read_direction)
       || (sec->flags & SEC_LINKER_CREATED) != 0)
     {
       ssect = (*bed->get_sec_type_attr) (abfd, sec);
@@ -2392,7 +2524,7 @@ _bfd_elf_new_section_hook (bfd *abfd, asection *sec)
        }
     }
 
-  return TRUE;
+  return _bfd_generic_new_section_hook (abfd, sec);
 }
 
 /* Create a new bfd section from an ELF program header.
@@ -2601,7 +2733,7 @@ elf_fake_sections (bfd *abfd, asection *asect, void *failedptrarg)
       return;
     }
 
-  this_hdr->sh_flags = 0;
+  /* Don't clear sh_flags. Assembler may set additional bits.  */
 
   if ((asect->flags & SEC_ALLOC) != 0
       || asect->user_set_vma)
@@ -2624,32 +2756,10 @@ elf_fake_sections (bfd *abfd, asection *asect, void *failedptrarg)
   if (this_hdr->sh_type == SHT_NULL)
     {
       if ((asect->flags & SEC_GROUP) != 0)
-       {
-         /* We also need to mark SHF_GROUP here for relocatable
-            link.  */
-         struct bfd_link_order *l;
-         asection *elt;
-
-         for (l = asect->map_head.link_order; l != NULL; l = l->next)
-           if (l->type == bfd_indirect_link_order
-               && (elt = elf_next_in_group (l->u.indirect.section)) != NULL)
-             do
-               {
-                 /* The name is not important. Anything will do.  */
-                 elf_group_name (elt->output_section) = "G";
-                 elf_section_flags (elt->output_section) |= SHF_GROUP;
-
-                 elt = elf_next_in_group (elt);
-                 /* During a relocatable link, the lists are
-                    circular.  */
-               }
-             while (elt != elf_next_in_group (l->u.indirect.section));
-
-         this_hdr->sh_type = SHT_GROUP;
-       }
+       this_hdr->sh_type = SHT_GROUP;
       else if ((asect->flags & SEC_ALLOC) != 0
-         && (((asect->flags & (SEC_LOAD | SEC_HAS_CONTENTS)) == 0)
-             || (asect->flags & SEC_NEVER_LOAD) != 0))
+              && (((asect->flags & (SEC_LOAD | SEC_HAS_CONTENTS)) == 0)
+                  || (asect->flags & SEC_NEVER_LOAD) != 0))
        this_hdr->sh_type = SHT_NOBITS;
       else
        this_hdr->sh_type = SHT_PROGBITS;
@@ -2722,6 +2832,10 @@ elf_fake_sections (bfd *abfd, asection *asect, void *failedptrarg)
     case SHT_GROUP:
       this_hdr->sh_entsize = 4;
       break;
+
+    case SHT_GNU_HASH:
+      this_hdr->sh_entsize = bed->s->arch_size == 64 ? 0 : 4;
+      break;
     }
 
   if ((asect->flags & SEC_ALLOC) != 0)
@@ -2742,16 +2856,18 @@ elf_fake_sections (bfd *abfd, asection *asect, void *failedptrarg)
   if ((asect->flags & SEC_THREAD_LOCAL) != 0)
     {
       this_hdr->sh_flags |= SHF_TLS;
-      if (asect->size == 0 && (asect->flags & SEC_HAS_CONTENTS) == 0)
+      if (asect->size == 0
+         && (asect->flags & SEC_HAS_CONTENTS) == 0)
        {
-         struct bfd_link_order *o;
+         struct bfd_link_order *o = asect->map_tail.link_order;
 
          this_hdr->sh_size = 0;
-         for (o = asect->map_head.link_order; o != NULL; o = o->next)
-           if (this_hdr->sh_size < o->offset + o->size)
+         if (o != NULL)
+           {
              this_hdr->sh_size = o->offset + o->size;
-         if (this_hdr->sh_size)
-           this_hdr->sh_type = SHT_NOBITS;
+             if (this_hdr->sh_size != 0)
+               this_hdr->sh_type = SHT_NOBITS;
+           }
        }
     }
 
@@ -2781,7 +2897,6 @@ bfd_elf_set_group_contents (bfd *abfd, asection *sec, void *failedptrarg)
   unsigned long symindx;
   asection *elt, *first;
   unsigned char *loc;
-  struct bfd_link_order *l;
   bfd_boolean gas;
 
   /* Ignore linker created group section.  See elfNN_ia64_object_p in
@@ -2850,22 +2965,6 @@ bfd_elf_set_group_contents (bfd *abfd, asection *sec, void *failedptrarg)
        break;
     }
 
-  /* If this is a relocatable link, then the above did nothing because
-     SEC is the output section.  Look through the input sections
-     instead.  */
-  for (l = sec->map_head.link_order; l != NULL; l = l->next)
-    if (l->type == bfd_indirect_link_order
-       && (elt = elf_next_in_group (l->u.indirect.section)) != NULL)
-      do
-       {
-         loc -= 4;
-         H_PUT_32 (abfd,
-                   elf_section_data (elt->output_section)->this_idx, loc);
-         elt = elf_next_in_group (elt);
-         /* During a relocatable link, the lists are circular.  */
-       }
-      while (elt != elf_next_in_group (l->u.indirect.section));
-
   if ((loc -= 4) != sec->contents)
     abort ();
 
@@ -2883,7 +2982,6 @@ assign_section_numbers (bfd *abfd, struct bfd_link_info *link_info)
   asection *sec;
   unsigned int section_number, secn;
   Elf_Internal_Shdr **i_shdrp;
-  bfd_size_type amt;
   struct bfd_elf_section_data *d;
 
   section_number = 1;
@@ -2987,13 +3085,11 @@ assign_section_numbers (bfd *abfd, struct bfd_link_info *link_info)
 
   /* Set up the list of section header pointers, in agreement with the
      indices.  */
-  amt = section_number * sizeof (Elf_Internal_Shdr *);
-  i_shdrp = bfd_zalloc (abfd, amt);
+  i_shdrp = bfd_zalloc2 (abfd, section_number, sizeof (Elf_Internal_Shdr *));
   if (i_shdrp == NULL)
     return FALSE;
 
-  amt = sizeof (Elf_Internal_Shdr);
-  i_shdrp[0] = bfd_zalloc (abfd, amt);
+  i_shdrp[0] = bfd_zalloc (abfd, sizeof (Elf_Internal_Shdr));
   if (i_shdrp[0] == NULL)
     {
       bfd_release (abfd, i_shdrp);
@@ -3048,67 +3144,60 @@ assign_section_numbers (bfd *abfd, struct bfd_link_info *link_info)
        {
          s = elf_linked_to_section (sec);
          if (s)
-           d->this_hdr.sh_link = elf_section_data (s)->this_idx;
-         else
            {
-             struct bfd_link_order *p;
-
-             /* Find out what the corresponding section in output
-                is.  */
-             for (p = sec->map_head.link_order; p != NULL; p = p->next)
+             /* elf_linked_to_section points to the input section.  */
+             if (link_info != NULL)
                {
-                 s = p->u.indirect.section;
-                 if (p->type == bfd_indirect_link_order
-                     && (bfd_get_flavour (s->owner)
-                         == bfd_target_elf_flavour))
+                 /* Check discarded linkonce section.  */
+                 if (elf_discarded_section (s))
                    {
-                     Elf_Internal_Shdr ** const elf_shdrp
-                       = elf_elfsections (s->owner);
-                     int elfsec
-                       = _bfd_elf_section_from_bfd_section (s->owner, s);
-                     elfsec = elf_shdrp[elfsec]->sh_link;
-                     /* PR 290:
-                        The Intel C compiler generates SHT_IA_64_UNWIND with
-                        SHF_LINK_ORDER.  But it doesn't set the sh_link or
-                        sh_info fields.  Hence we could get the situation
-                        where elfsec is 0.  */
-                     if (elfsec == 0)
-                       {
-                         const struct elf_backend_data *bed
-                           = get_elf_backend_data (abfd);
-                         if (bed->link_order_error_handler)
-                           bed->link_order_error_handler
-                             (_("%B: warning: sh_link not set for section `%A'"),
-                              abfd, s);
-                       }
-                     else
+                     asection *kept;
+                     (*_bfd_error_handler)
+                       (_("%B: sh_link of section `%A' points to discarded section `%A' of `%B'"),
+                        abfd, d->this_hdr.bfd_section,
+                        s, s->owner);
+                     /* Point to the kept section if it has the same
+                        size as the discarded one.  */
+                     kept = _bfd_elf_check_kept_section (s, link_info);
+                     if (kept == NULL)
                        {
-                         s = elf_shdrp[elfsec]->bfd_section;
-                         if (elf_discarded_section (s))
-                           {
-                             asection *kept;
-                              (*_bfd_error_handler)
-                                 (_("%B: sh_link of section `%A' points to discarded section `%A' of `%B'"),
-                                  abfd, d->this_hdr.bfd_section,
-                                  s, s->owner);
-                              /* Point to the kept section if it has
-                                 the same size as the discarded
-                                 one.  */
-                              kept = _bfd_elf_check_kept_section (s);
-                              if (kept == NULL)
-                                {
-                                  bfd_set_error (bfd_error_bad_value);
-                                  return FALSE;
-                                }
-                              s = kept;
-                           }
-                         s = s->output_section;
-                         BFD_ASSERT (s != NULL);
-                         d->this_hdr.sh_link = elf_section_data (s)->this_idx;
+                         bfd_set_error (bfd_error_bad_value);
+                         return FALSE;
                        }
-                     break;
+                     s = kept;
+                   }
+
+                 s = s->output_section;
+                 BFD_ASSERT (s != NULL);
+               }
+             else
+               {
+                 /* Handle objcopy. */
+                 if (s->output_section == NULL)
+                   {
+                     (*_bfd_error_handler)
+                       (_("%B: sh_link of section `%A' points to removed section `%A' of `%B'"),
+                        abfd, d->this_hdr.bfd_section, s, s->owner);
+                     bfd_set_error (bfd_error_bad_value);
+                     return FALSE;
                    }
+                 s = s->output_section;
                }
+             d->this_hdr.sh_link = elf_section_data (s)->this_idx;
+           }
+         else
+           {
+             /* PR 290:
+                The Intel C compiler generates SHT_IA_64_UNWIND with
+                SHF_LINK_ORDER.  But it doesn't set the sh_link or
+                sh_info fields.  Hence we could get the situation
+                where s is NULL.  */
+             const struct elf_backend_data *bed
+               = get_elf_backend_data (abfd);
+             if (bed->link_order_error_handler)
+               bed->link_order_error_handler
+                 (_("%B: warning: sh_link not set for section `%A'"),
+                  abfd, sec);
            }
        }
 
@@ -3142,7 +3231,7 @@ assign_section_numbers (bfd *abfd, struct bfd_link_info *link_info)
             string section.  We look for a section with the same name
             but without the trailing ``str'', and set its sh_link
             field to point to this section.  */
-         if (strncmp (sec->name, ".stab", sizeof ".stab" - 1) == 0
+         if (CONST_STRNEQ (sec->name, ".stab")
              && strcmp (sec->name + strlen (sec->name) - 3, "str") == 0)
            {
              size_t len;
@@ -3192,6 +3281,7 @@ assign_section_numbers (bfd *abfd, struct bfd_link_info *link_info)
          break;
 
        case SHT_HASH:
+       case SHT_GNU_HASH:
        case SHT_GNU_versym:
          /* sh_link is the section header index of the symbol table
             this hash table or version table is for.  */
@@ -3217,7 +3307,7 @@ assign_section_numbers (bfd *abfd, struct bfd_link_info *link_info)
 /* Map symbol from it's internal number to the external number, moving
    all local symbols to be at the head of the list.  */
 
-static int
+static bfd_boolean
 sym_is_global (bfd *abfd, asymbol *sym)
 {
   /* If the backend has a special mapping, use it.  */
@@ -3230,6 +3320,20 @@ sym_is_global (bfd *abfd, asymbol *sym)
          || bfd_is_com_section (bfd_get_section (sym)));
 }
 
+/* Don't output section symbols for sections that are not going to be
+   output.  Also, don't output section symbols for reloc and other
+   special sections.  */
+
+static bfd_boolean
+ignore_section_sym (bfd *abfd, asymbol *sym)
+{
+  return ((sym->flags & BSF_SECTION_SYM) != 0
+         && (sym->value != 0
+             || (sym->section->owner != abfd
+                 && (sym->section->output_section->owner != abfd
+                     || sym->section->output_offset != 0))));
+}
+
 static bfd_boolean
 elf_map_symbols (bfd *abfd)
 {
@@ -3244,7 +3348,6 @@ elf_map_symbols (bfd *abfd)
   unsigned int idx;
   asection *asect;
   asymbol **new_syms;
-  bfd_size_type amt;
 
 #ifdef DEBUG
   fprintf (stderr, "elf_map_symbols\n");
@@ -3258,8 +3361,7 @@ elf_map_symbols (bfd *abfd)
     }
 
   max_index++;
-  amt = max_index * sizeof (asymbol *);
-  sect_syms = bfd_zalloc (abfd, amt);
+  sect_syms = bfd_zalloc2 (abfd, max_index, sizeof (asymbol *));
   if (sect_syms == NULL)
     return FALSE;
   elf_section_syms (abfd) = sect_syms;
@@ -3272,51 +3374,29 @@ elf_map_symbols (bfd *abfd)
       asymbol *sym = syms[idx];
 
       if ((sym->flags & BSF_SECTION_SYM) != 0
-         && sym->value == 0)
+         && !ignore_section_sym (abfd, sym))
        {
-         asection *sec;
+         asection *sec = sym->section;
 
-         sec = sym->section;
+         if (sec->owner != abfd)
+           sec = sec->output_section;
 
-         if (sec->owner != NULL)
-           {
-             if (sec->owner != abfd)
-               {
-                 if (sec->output_offset != 0)
-                   continue;
-
-                 sec = sec->output_section;
-
-                 /* Empty sections in the input files may have had a
-                    section symbol created for them.  (See the comment
-                    near the end of _bfd_generic_link_output_symbols in
-                    linker.c).  If the linker script discards such
-                    sections then we will reach this point.  Since we know
-                    that we cannot avoid this case, we detect it and skip
-                    the abort and the assignment to the sect_syms array.
-                    To reproduce this particular case try running the
-                    linker testsuite test ld-scripts/weak.exp for an ELF
-                    port that uses the generic linker.  */
-                 if (sec->owner == NULL)
-                   continue;
-
-                 BFD_ASSERT (sec->owner == abfd);
-               }
-             sect_syms[sec->index] = syms[idx];
-           }
+         sect_syms[sec->index] = syms[idx];
        }
     }
 
   /* Classify all of the symbols.  */
   for (idx = 0; idx < symcount; idx++)
     {
+      if (ignore_section_sym (abfd, syms[idx]))
+       continue;
       if (!sym_is_global (abfd, syms[idx]))
        num_locals++;
       else
        num_globals++;
     }
 
-  /* We will be adding a section symbol for each BFD section.  Most normal
+  /* We will be adding a section symbol for each normal BFD section.  Most
      sections will already have a section symbol in outsymbols, but
      eg. SHT_GROUP sections will not, and we need the section symbol mapped
      at least in that case.  */
@@ -3332,8 +3412,7 @@ elf_map_symbols (bfd *abfd)
     }
 
   /* Now sort the symbols so the local symbols are first.  */
-  amt = (num_locals + num_globals) * sizeof (asymbol *);
-  new_syms = bfd_alloc (abfd, amt);
+  new_syms = bfd_alloc2 (abfd, num_locals + num_globals, sizeof (asymbol *));
 
   if (new_syms == NULL)
     return FALSE;
@@ -3343,6 +3422,8 @@ elf_map_symbols (bfd *abfd)
       asymbol *sym = syms[idx];
       unsigned int i;
 
+      if (ignore_section_sym (abfd, sym))
+       continue;
       if (!sym_is_global (abfd, sym))
        i = num_locals2++;
       else
@@ -3506,6 +3587,90 @@ _bfd_elf_compute_section_file_positions (bfd *abfd,
   return TRUE;
 }
 
+/* Make an initial estimate of the size of the program header.  If we
+   get the number wrong here, we'll redo section placement.  */
+
+static bfd_size_type
+get_program_header_size (bfd *abfd, struct bfd_link_info *info)
+{
+  size_t segs;
+  asection *s;
+  const struct elf_backend_data *bed;
+
+  /* Assume we will need exactly two PT_LOAD segments: one for text
+     and one for data.  */
+  segs = 2;
+
+  s = bfd_get_section_by_name (abfd, ".interp");
+  if (s != NULL && (s->flags & SEC_LOAD) != 0)
+    {
+      /* If we have a loadable interpreter section, we need a
+        PT_INTERP segment.  In this case, assume we also need a
+        PT_PHDR segment, although that may not be true for all
+        targets.  */
+      segs += 2;
+    }
+
+  if (bfd_get_section_by_name (abfd, ".dynamic") != NULL)
+    {
+      /* We need a PT_DYNAMIC segment.  */
+      ++segs;
+      
+      if (elf_tdata (abfd)->relro)
+       {
+         /* We need a PT_GNU_RELRO segment only when there is a
+            PT_DYNAMIC segment.  */
+         ++segs;
+       }
+    }
+
+  if (elf_tdata (abfd)->eh_frame_hdr)
+    {
+      /* We need a PT_GNU_EH_FRAME segment.  */
+      ++segs;
+    }
+
+  if (elf_tdata (abfd)->stack_flags)
+    {
+      /* We need a PT_GNU_STACK segment.  */
+      ++segs;
+    }
+
+  for (s = abfd->sections; s != NULL; s = s->next)
+    {
+      if ((s->flags & SEC_LOAD) != 0
+         && CONST_STRNEQ (s->name, ".note"))
+       {
+         /* We need a PT_NOTE segment.  */
+         ++segs;
+       }
+    }
+
+  for (s = abfd->sections; s != NULL; s = s->next)
+    {
+      if (s->flags & SEC_THREAD_LOCAL)
+       {
+         /* We need a PT_TLS segment.  */
+         ++segs;
+         break;
+       }
+    }
+
+  /* Let the backend count up any program headers it might need.  */
+  bed = get_elf_backend_data (abfd);
+  if (bed->elf_backend_additional_program_headers)
+    {
+      int a;
+
+      a = (*bed->elf_backend_additional_program_headers) (abfd, info);
+      if (a == -1)
+       abort ();
+      segs += a;
+    }
+
+  return segs * bed->s->sizeof_phdr;
+}
+
 /* Create a mapping from a set of sections to a program segment.  */
 
 static struct elf_segment_map *
@@ -3560,360 +3725,417 @@ _bfd_elf_make_dynamic_segment (bfd *abfd, asection *dynsec)
   return m;
 }
 
-/* Set up a mapping from BFD sections to program segments.  */
+/* Possibly add or remove segments from the segment map.  */
 
 static bfd_boolean
-map_sections_to_segments (bfd *abfd)
+elf_modify_segment_map (bfd *abfd, struct bfd_link_info *info)
 {
-  asection **sections = NULL;
-  asection *s;
-  unsigned int i;
-  unsigned int count;
-  struct elf_segment_map *mfirst;
-  struct elf_segment_map **pm;
-  struct elf_segment_map *m;
-  asection *last_hdr;
-  bfd_vma last_size;
-  unsigned int phdr_index;
-  bfd_vma maxpagesize;
-  asection **hdrpp;
-  bfd_boolean phdr_in_segment = TRUE;
-  bfd_boolean writable;
-  int tls_count = 0;
-  asection *first_tls = NULL;
-  asection *dynsec, *eh_frame_hdr;
-  bfd_size_type amt;
-
-  if (elf_tdata (abfd)->segment_map != NULL)
-    return TRUE;
+  struct elf_segment_map **m;
+  const struct elf_backend_data *bed;
 
-  if (bfd_count_sections (abfd) == 0)
-    return TRUE;
+  /* The placement algorithm assumes that non allocated sections are
+     not in PT_LOAD segments.  We ensure this here by removing such
+     sections from the segment map.  We also remove excluded
+     sections.  Finally, any PT_LOAD segment without sections is
+     removed.  */
+  m = &elf_tdata (abfd)->segment_map;
+  while (*m)
+    {
+      unsigned int i, new_count;
 
-  /* Select the allocated sections, and sort them.  */
+      for (new_count = 0, i = 0; i < (*m)->count; i++)
+       {
+         if (((*m)->sections[i]->flags & SEC_EXCLUDE) == 0
+             && (((*m)->sections[i]->flags & SEC_ALLOC) != 0
+                 || (*m)->p_type != PT_LOAD))
+           {
+             (*m)->sections[new_count] = (*m)->sections[i];
+             new_count++;
+           }
+       }
+      (*m)->count = new_count;
 
-  amt = bfd_count_sections (abfd) * sizeof (asection *);
-  sections = bfd_malloc (amt);
-  if (sections == NULL)
-    goto error_return;
+      if ((*m)->p_type == PT_LOAD && (*m)->count == 0)
+       *m = (*m)->next;
+      else
+       m = &(*m)->next;
+    }
 
-  i = 0;
-  for (s = abfd->sections; s != NULL; s = s->next)
+  bed = get_elf_backend_data (abfd);
+  if (bed->elf_backend_modify_segment_map != NULL)
     {
-      if ((s->flags & SEC_ALLOC) != 0)
-       {
-         sections[i] = s;
-         ++i;
-       }
+      if (!(*bed->elf_backend_modify_segment_map) (abfd, info))
+       return FALSE;
     }
-  BFD_ASSERT (i <= bfd_count_sections (abfd));
-  count = i;
 
-  qsort (sections, (size_t) count, sizeof (asection *), elf_sort_sections);
+  return TRUE;
+}
 
-  /* Build the mapping.  */
+/* Set up a mapping from BFD sections to program segments.  */
 
-  mfirst = NULL;
-  pm = &mfirst;
+bfd_boolean
+_bfd_elf_map_sections_to_segments (bfd *abfd, struct bfd_link_info *info)
+{
+  unsigned int count;
+  struct elf_segment_map *m;
+  asection **sections = NULL;
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
 
-  /* If we have a .interp section, then create a PT_PHDR segment for
-     the program headers and a PT_INTERP segment for the .interp
-     section.  */
-  s = bfd_get_section_by_name (abfd, ".interp");
-  if (s != NULL && (s->flags & SEC_LOAD) != 0)
+  if (elf_tdata (abfd)->segment_map == NULL
+      && bfd_count_sections (abfd) != 0)
     {
-      amt = sizeof (struct elf_segment_map);
-      m = bfd_zalloc (abfd, amt);
-      if (m == NULL)
-       goto error_return;
-      m->next = NULL;
-      m->p_type = PT_PHDR;
-      /* FIXME: UnixWare and Solaris set PF_X, Irix 5 does not.  */
-      m->p_flags = PF_R | PF_X;
-      m->p_flags_valid = 1;
-      m->includes_phdrs = 1;
+      asection *s;
+      unsigned int i;
+      struct elf_segment_map *mfirst;
+      struct elf_segment_map **pm;
+      asection *last_hdr;
+      bfd_vma last_size;
+      unsigned int phdr_index;
+      bfd_vma maxpagesize;
+      asection **hdrpp;
+      bfd_boolean phdr_in_segment = TRUE;
+      bfd_boolean writable;
+      int tls_count = 0;
+      asection *first_tls = NULL;
+      asection *dynsec, *eh_frame_hdr;
+      bfd_size_type amt;
 
-      *pm = m;
-      pm = &m->next;
+      /* Select the allocated sections, and sort them.  */
 
-      amt = sizeof (struct elf_segment_map);
-      m = bfd_zalloc (abfd, amt);
-      if (m == NULL)
+      sections = bfd_malloc2 (bfd_count_sections (abfd), sizeof (asection *));
+      if (sections == NULL)
        goto error_return;
-      m->next = NULL;
-      m->p_type = PT_INTERP;
-      m->count = 1;
-      m->sections[0] = s;
-
-      *pm = m;
-      pm = &m->next;
-    }
-
-  /* Look through the sections.  We put sections in the same program
-     segment when the start of the second section can be placed within
-     a few bytes of the end of the first section.  */
-  last_hdr = NULL;
-  last_size = 0;
-  phdr_index = 0;
-  maxpagesize = get_elf_backend_data (abfd)->maxpagesize;
-  writable = FALSE;
-  dynsec = bfd_get_section_by_name (abfd, ".dynamic");
-  if (dynsec != NULL
-      && (dynsec->flags & SEC_LOAD) == 0)
-    dynsec = NULL;
-
-  /* Deal with -Ttext or something similar such that the first section
-     is not adjacent to the program headers.  This is an
-     approximation, since at this point we don't know exactly how many
-     program headers we will need.  */
-  if (count > 0)
-    {
-      bfd_size_type phdr_size;
-
-      phdr_size = elf_tdata (abfd)->program_header_size;
-      if (phdr_size == 0)
-       phdr_size = get_elf_backend_data (abfd)->s->sizeof_phdr;
-      if ((abfd->flags & D_PAGED) == 0
-         || sections[0]->lma < phdr_size
-         || sections[0]->lma % maxpagesize < phdr_size % maxpagesize)
-       phdr_in_segment = FALSE;
-    }
-
-  for (i = 0, hdrpp = sections; i < count; i++, hdrpp++)
-    {
-      asection *hdr;
-      bfd_boolean new_segment;
-
-      hdr = *hdrpp;
 
-      /* See if this section and the last one will fit in the same
-         segment.  */
-
-      if (last_hdr == NULL)
+      i = 0;
+      for (s = abfd->sections; s != NULL; s = s->next)
        {
-         /* If we don't have a segment yet, then we don't need a new
-            one (we build the last one after this loop).  */
-         new_segment = FALSE;
+         if ((s->flags & SEC_ALLOC) != 0)
+           {
+             sections[i] = s;
+             ++i;
+           }
        }
-      else if (last_hdr->lma - last_hdr->vma != hdr->lma - hdr->vma)
+      BFD_ASSERT (i <= bfd_count_sections (abfd));
+      count = i;
+
+      qsort (sections, (size_t) count, sizeof (asection *), elf_sort_sections);
+
+      /* Build the mapping.  */
+
+      mfirst = NULL;
+      pm = &mfirst;
+
+      /* If we have a .interp section, then create a PT_PHDR segment for
+        the program headers and a PT_INTERP segment for the .interp
+        section.  */
+      s = bfd_get_section_by_name (abfd, ".interp");
+      if (s != NULL && (s->flags & SEC_LOAD) != 0)
        {
-         /* If this section has a different relation between the
-             virtual address and the load address, then we need a new
-             segment.  */
-         new_segment = TRUE;
-       }
-      else if (BFD_ALIGN (last_hdr->lma + last_size, maxpagesize)
-              < BFD_ALIGN (hdr->lma, maxpagesize))
-       {
-         /* If putting this section in this segment would force us to
-             skip a page in the segment, then we need a new segment.  */
-         new_segment = TRUE;
-       }
-      else if ((last_hdr->flags & (SEC_LOAD | SEC_THREAD_LOCAL)) == 0
-              && (hdr->flags & (SEC_LOAD | SEC_THREAD_LOCAL)) != 0)
-       {
-         /* We don't want to put a loadable section after a
-             nonloadable section in the same segment.
-             Consider .tbss sections as loadable for this purpose.  */
-         new_segment = TRUE;
-       }
-      else if ((abfd->flags & D_PAGED) == 0)
-       {
-         /* If the file is not demand paged, which means that we
-             don't require the sections to be correctly aligned in the
-             file, then there is no other reason for a new segment.  */
-         new_segment = FALSE;
-       }
-      else if (! writable
-              && (hdr->flags & SEC_READONLY) == 0
-              && (((last_hdr->lma + last_size - 1)
-                   & ~(maxpagesize - 1))
-                  != (hdr->lma & ~(maxpagesize - 1))))
-       {
-         /* We don't want to put a writable section in a read only
-             segment, unless they are on the same page in memory
-             anyhow.  We already know that the last section does not
-             bring us past the current section on the page, so the
-             only case in which the new section is not on the same
-             page as the previous section is when the previous section
-             ends precisely on a page boundary.  */
-         new_segment = TRUE;
+         amt = sizeof (struct elf_segment_map);
+         m = bfd_zalloc (abfd, amt);
+         if (m == NULL)
+           goto error_return;
+         m->next = NULL;
+         m->p_type = PT_PHDR;
+         /* FIXME: UnixWare and Solaris set PF_X, Irix 5 does not.  */
+         m->p_flags = PF_R | PF_X;
+         m->p_flags_valid = 1;
+         m->includes_phdrs = 1;
+
+         *pm = m;
+         pm = &m->next;
+
+         amt = sizeof (struct elf_segment_map);
+         m = bfd_zalloc (abfd, amt);
+         if (m == NULL)
+           goto error_return;
+         m->next = NULL;
+         m->p_type = PT_INTERP;
+         m->count = 1;
+         m->sections[0] = s;
+
+         *pm = m;
+         pm = &m->next;
        }
-      else
+
+      /* Look through the sections.  We put sections in the same program
+        segment when the start of the second section can be placed within
+        a few bytes of the end of the first section.  */
+      last_hdr = NULL;
+      last_size = 0;
+      phdr_index = 0;
+      maxpagesize = bed->maxpagesize;
+      writable = FALSE;
+      dynsec = bfd_get_section_by_name (abfd, ".dynamic");
+      if (dynsec != NULL
+         && (dynsec->flags & SEC_LOAD) == 0)
+       dynsec = NULL;
+
+      /* Deal with -Ttext or something similar such that the first section
+        is not adjacent to the program headers.  This is an
+        approximation, since at this point we don't know exactly how many
+        program headers we will need.  */
+      if (count > 0)
        {
-         /* Otherwise, we can use the same segment.  */
-         new_segment = FALSE;
+         bfd_size_type phdr_size = elf_tdata (abfd)->program_header_size;
+
+         if (phdr_size == (bfd_size_type) -1)
+           phdr_size = get_program_header_size (abfd, info);
+         if ((abfd->flags & D_PAGED) == 0
+             || sections[0]->lma < phdr_size
+             || sections[0]->lma % maxpagesize < phdr_size % maxpagesize)
+           phdr_in_segment = FALSE;
        }
 
-      if (! new_segment)
+      for (i = 0, hdrpp = sections; i < count; i++, hdrpp++)
        {
+         asection *hdr;
+         bfd_boolean new_segment;
+
+         hdr = *hdrpp;
+
+         /* See if this section and the last one will fit in the same
+            segment.  */
+
+         if (last_hdr == NULL)
+           {
+             /* If we don't have a segment yet, then we don't need a new
+                one (we build the last one after this loop).  */
+             new_segment = FALSE;
+           }
+         else if (last_hdr->lma - last_hdr->vma != hdr->lma - hdr->vma)
+           {
+             /* If this section has a different relation between the
+                virtual address and the load address, then we need a new
+                segment.  */
+             new_segment = TRUE;
+           }
+         else if (BFD_ALIGN (last_hdr->lma + last_size, maxpagesize)
+                  < BFD_ALIGN (hdr->lma, maxpagesize))
+           {
+             /* If putting this section in this segment would force us to
+                skip a page in the segment, then we need a new segment.  */
+             new_segment = TRUE;
+           }
+         else if ((last_hdr->flags & (SEC_LOAD | SEC_THREAD_LOCAL)) == 0
+                  && (hdr->flags & (SEC_LOAD | SEC_THREAD_LOCAL)) != 0)
+           {
+             /* We don't want to put a loadable section after a
+                nonloadable section in the same segment.
+                Consider .tbss sections as loadable for this purpose.  */
+             new_segment = TRUE;
+           }
+         else if ((abfd->flags & D_PAGED) == 0)
+           {
+             /* If the file is not demand paged, which means that we
+                don't require the sections to be correctly aligned in the
+                file, then there is no other reason for a new segment.  */
+             new_segment = FALSE;
+           }
+         else if (! writable
+                  && (hdr->flags & SEC_READONLY) == 0
+                  && (((last_hdr->lma + last_size - 1)
+                       & ~(maxpagesize - 1))
+                      != (hdr->lma & ~(maxpagesize - 1))))
+           {
+             /* We don't want to put a writable section in a read only
+                segment, unless they are on the same page in memory
+                anyhow.  We already know that the last section does not
+                bring us past the current section on the page, so the
+                only case in which the new section is not on the same
+                page as the previous section is when the previous section
+                ends precisely on a page boundary.  */
+             new_segment = TRUE;
+           }
+         else
+           {
+             /* Otherwise, we can use the same segment.  */
+             new_segment = FALSE;
+           }
+
+         /* Allow interested parties a chance to override our decision.  */
+         if (last_hdr && info->callbacks->override_segment_assignment)
+           new_segment = info->callbacks->override_segment_assignment (info, abfd, hdr, last_hdr, new_segment);
+
+         if (! new_segment)
+           {
+             if ((hdr->flags & SEC_READONLY) == 0)
+               writable = TRUE;
+             last_hdr = hdr;
+             /* .tbss sections effectively have zero size.  */
+             if ((hdr->flags & (SEC_THREAD_LOCAL | SEC_LOAD))
+                 != SEC_THREAD_LOCAL)
+               last_size = hdr->size;
+             else
+               last_size = 0;
+             continue;
+           }
+
+         /* We need a new program segment.  We must create a new program
+            header holding all the sections from phdr_index until hdr.  */
+
+         m = make_mapping (abfd, sections, phdr_index, i, phdr_in_segment);
+         if (m == NULL)
+           goto error_return;
+
+         *pm = m;
+         pm = &m->next;
+
          if ((hdr->flags & SEC_READONLY) == 0)
            writable = TRUE;
+         else
+           writable = FALSE;
+
          last_hdr = hdr;
          /* .tbss sections effectively have zero size.  */
          if ((hdr->flags & (SEC_THREAD_LOCAL | SEC_LOAD)) != SEC_THREAD_LOCAL)
            last_size = hdr->size;
          else
            last_size = 0;
-         continue;
+         phdr_index = i;
+         phdr_in_segment = FALSE;
        }
 
-      /* We need a new program segment.  We must create a new program
-         header holding all the sections from phdr_index until hdr.  */
+      /* Create a final PT_LOAD program segment.  */
+      if (last_hdr != NULL)
+       {
+         m = make_mapping (abfd, sections, phdr_index, i, phdr_in_segment);
+         if (m == NULL)
+           goto error_return;
 
-      m = make_mapping (abfd, sections, phdr_index, i, phdr_in_segment);
-      if (m == NULL)
-       goto error_return;
+         *pm = m;
+         pm = &m->next;
+       }
 
-      *pm = m;
-      pm = &m->next;
+      /* If there is a .dynamic section, throw in a PT_DYNAMIC segment.  */
+      if (dynsec != NULL)
+       {
+         m = _bfd_elf_make_dynamic_segment (abfd, dynsec);
+         if (m == NULL)
+           goto error_return;
+         *pm = m;
+         pm = &m->next;
+       }
 
-      if ((hdr->flags & SEC_READONLY) == 0)
-       writable = TRUE;
-      else
-       writable = FALSE;
+      /* For each loadable .note section, add a PT_NOTE segment.  We don't
+        use bfd_get_section_by_name, because if we link together
+        nonloadable .note sections and loadable .note sections, we will
+        generate two .note sections in the output file.  FIXME: Using
+        names for section types is bogus anyhow.  */
+      for (s = abfd->sections; s != NULL; s = s->next)
+       {
+         if ((s->flags & SEC_LOAD) != 0
+             && CONST_STRNEQ (s->name, ".note"))
+           {
+             amt = sizeof (struct elf_segment_map);
+             m = bfd_zalloc (abfd, amt);
+             if (m == NULL)
+               goto error_return;
+             m->next = NULL;
+             m->p_type = PT_NOTE;
+             m->count = 1;
+             m->sections[0] = s;
 
-      last_hdr = hdr;
-      /* .tbss sections effectively have zero size.  */
-      if ((hdr->flags & (SEC_THREAD_LOCAL | SEC_LOAD)) != SEC_THREAD_LOCAL)
-       last_size = hdr->size;
-      else
-       last_size = 0;
-      phdr_index = i;
-      phdr_in_segment = FALSE;
-    }
+             *pm = m;
+             pm = &m->next;
+           }
+         if (s->flags & SEC_THREAD_LOCAL)
+           {
+             if (! tls_count)
+               first_tls = s;
+             tls_count++;
+           }
+       }
 
-  /* Create a final PT_LOAD program segment.  */
-  if (last_hdr != NULL)
-    {
-      m = make_mapping (abfd, sections, phdr_index, i, phdr_in_segment);
-      if (m == NULL)
-       goto error_return;
+      /* If there are any SHF_TLS output sections, add PT_TLS segment.  */
+      if (tls_count > 0)
+       {
+         int i;
 
-      *pm = m;
-      pm = &m->next;
-    }
+         amt = sizeof (struct elf_segment_map);
+         amt += (tls_count - 1) * sizeof (asection *);
+         m = bfd_zalloc (abfd, amt);
+         if (m == NULL)
+           goto error_return;
+         m->next = NULL;
+         m->p_type = PT_TLS;
+         m->count = tls_count;
+         /* Mandated PF_R.  */
+         m->p_flags = PF_R;
+         m->p_flags_valid = 1;
+         for (i = 0; i < tls_count; ++i)
+           {
+             BFD_ASSERT (first_tls->flags & SEC_THREAD_LOCAL);
+             m->sections[i] = first_tls;
+             first_tls = first_tls->next;
+           }
 
-  /* If there is a .dynamic section, throw in a PT_DYNAMIC segment.  */
-  if (dynsec != NULL)
-    {
-      m = _bfd_elf_make_dynamic_segment (abfd, dynsec);
-      if (m == NULL)
-       goto error_return;
-      *pm = m;
-      pm = &m->next;
-    }
+         *pm = m;
+         pm = &m->next;
+       }
 
-  /* For each loadable .note section, add a PT_NOTE segment.  We don't
-     use bfd_get_section_by_name, because if we link together
-     nonloadable .note sections and loadable .note sections, we will
-     generate two .note sections in the output file.  FIXME: Using
-     names for section types is bogus anyhow.  */
-  for (s = abfd->sections; s != NULL; s = s->next)
-    {
-      if ((s->flags & SEC_LOAD) != 0
-         && strncmp (s->name, ".note", 5) == 0)
+      /* If there is a .eh_frame_hdr section, throw in a PT_GNU_EH_FRAME
+        segment.  */
+      eh_frame_hdr = elf_tdata (abfd)->eh_frame_hdr;
+      if (eh_frame_hdr != NULL
+         && (eh_frame_hdr->output_section->flags & SEC_LOAD) != 0)
        {
          amt = sizeof (struct elf_segment_map);
          m = bfd_zalloc (abfd, amt);
          if (m == NULL)
            goto error_return;
          m->next = NULL;
-         m->p_type = PT_NOTE;
+         m->p_type = PT_GNU_EH_FRAME;
          m->count = 1;
-         m->sections[0] = s;
+         m->sections[0] = eh_frame_hdr->output_section;
 
          *pm = m;
          pm = &m->next;
        }
-      if (s->flags & SEC_THREAD_LOCAL)
-       {
-         if (! tls_count)
-           first_tls = s;
-         tls_count++;
-       }
-    }
 
-  /* If there are any SHF_TLS output sections, add PT_TLS segment.  */
-  if (tls_count > 0)
-    {
-      int i;
-
-      amt = sizeof (struct elf_segment_map);
-      amt += (tls_count - 1) * sizeof (asection *);
-      m = bfd_zalloc (abfd, amt);
-      if (m == NULL)
-       goto error_return;
-      m->next = NULL;
-      m->p_type = PT_TLS;
-      m->count = tls_count;
-      /* Mandated PF_R.  */
-      m->p_flags = PF_R;
-      m->p_flags_valid = 1;
-      for (i = 0; i < tls_count; ++i)
+      if (elf_tdata (abfd)->stack_flags)
        {
-         BFD_ASSERT (first_tls->flags & SEC_THREAD_LOCAL);
-         m->sections[i] = first_tls;
-         first_tls = first_tls->next;
-       }
-
-      *pm = m;
-      pm = &m->next;
-    }
+         amt = sizeof (struct elf_segment_map);
+         m = bfd_zalloc (abfd, amt);
+         if (m == NULL)
+           goto error_return;
+         m->next = NULL;
+         m->p_type = PT_GNU_STACK;
+         m->p_flags = elf_tdata (abfd)->stack_flags;
+         m->p_flags_valid = 1;
 
-  /* If there is a .eh_frame_hdr section, throw in a PT_GNU_EH_FRAME
-     segment.  */
-  eh_frame_hdr = elf_tdata (abfd)->eh_frame_hdr;
-  if (eh_frame_hdr != NULL
-      && (eh_frame_hdr->output_section->flags & SEC_LOAD) != 0)
-    {
-      amt = sizeof (struct elf_segment_map);
-      m = bfd_zalloc (abfd, amt);
-      if (m == NULL)
-       goto error_return;
-      m->next = NULL;
-      m->p_type = PT_GNU_EH_FRAME;
-      m->count = 1;
-      m->sections[0] = eh_frame_hdr->output_section;
+         *pm = m;
+         pm = &m->next;
+       }
 
-      *pm = m;
-      pm = &m->next;
-    }
+      if (dynsec != NULL && elf_tdata (abfd)->relro)
+       {
+         /* We make a PT_GNU_RELRO segment only when there is a
+            PT_DYNAMIC segment.  */
+         amt = sizeof (struct elf_segment_map);
+         m = bfd_zalloc (abfd, amt);
+         if (m == NULL)
+           goto error_return;
+         m->next = NULL;
+         m->p_type = PT_GNU_RELRO;
+         m->p_flags = PF_R;
+         m->p_flags_valid = 1;
 
-  if (elf_tdata (abfd)->stack_flags)
-    {
-      amt = sizeof (struct elf_segment_map);
-      m = bfd_zalloc (abfd, amt);
-      if (m == NULL)
-       goto error_return;
-      m->next = NULL;
-      m->p_type = PT_GNU_STACK;
-      m->p_flags = elf_tdata (abfd)->stack_flags;
-      m->p_flags_valid = 1;
+         *pm = m;
+         pm = &m->next;
+       }
 
-      *pm = m;
-      pm = &m->next;
+      free (sections);
+      elf_tdata (abfd)->segment_map = mfirst;
     }
 
-  if (elf_tdata (abfd)->relro)
-    {
-      amt = sizeof (struct elf_segment_map);
-      m = bfd_zalloc (abfd, amt);
-      if (m == NULL)
-       goto error_return;
-      m->next = NULL;
-      m->p_type = PT_GNU_RELRO;
-      m->p_flags = PF_R;
-      m->p_flags_valid = 1;
-
-      *pm = m;
-      pm = &m->next;
-    }
+  if (!elf_modify_segment_map (abfd, info))
+    return FALSE;
 
-  free (sections);
-  sections = NULL;
+  for (count = 0, m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
+    ++count;
+  elf_tdata (abfd)->program_header_size = count * bed->s->sizeof_phdr;
 
-  elf_tdata (abfd)->segment_map = mfirst;
   return TRUE;
 
  error_return:
@@ -4011,113 +4233,61 @@ vma_page_aligned_bias (bfd_vma vma, ufile_ptr off, bfd_vma maxpagesize)
 
 /* Assign file positions to the sections based on the mapping from
    sections to segments.  This function also sets up some fields in
-   the file header, and writes out the program headers.  */
+   the file header.  */
 
 static bfd_boolean
-assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
+assign_file_positions_for_load_sections (bfd *abfd,
+                                        struct bfd_link_info *link_info)
 {
   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
-  unsigned int count;
   struct elf_segment_map *m;
-  unsigned int alloc;
   Elf_Internal_Phdr *phdrs;
-  file_ptr off, voff;
-  bfd_vma filehdr_vaddr, filehdr_paddr;
-  bfd_vma phdrs_vaddr, phdrs_paddr;
   Elf_Internal_Phdr *p;
-  bfd_size_type amt;
-
-  if (elf_tdata (abfd)->segment_map == NULL)
-    {
-      if (! map_sections_to_segments (abfd))
-       return FALSE;
-    }
-  else
-    {
-      /* The placement algorithm assumes that non allocated sections are
-        not in PT_LOAD segments.  We ensure this here by removing such
-        sections from the segment map.  We also remove excluded
-        sections.  */
-      for (m = elf_tdata (abfd)->segment_map;
-          m != NULL;
-          m = m->next)
-       {
-         unsigned int new_count;
-         unsigned int i;
-
-         new_count = 0;
-         for (i = 0; i < m->count; i ++)
-           {
-             if ((m->sections[i]->flags & SEC_EXCLUDE) == 0
-                 && ((m->sections[i]->flags & SEC_ALLOC) != 0
-                     || m->p_type != PT_LOAD))
-               {
-                 if (i != new_count)
-                   m->sections[new_count] = m->sections[i];
-
-                 new_count ++;
-               }
-           }
-
-         if (new_count != m->count)
-           m->count = new_count;
-       }
-    }
+  file_ptr off, voff;
+  bfd_size_type maxpagesize;
+  unsigned int alloc;
+  unsigned int i;
 
-  if (bed->elf_backend_modify_segment_map)
-    {
-      if (! (*bed->elf_backend_modify_segment_map) (abfd, link_info))
-       return FALSE;
-    }
+  if (link_info == NULL
+      && !elf_modify_segment_map (abfd, link_info))
+    return FALSE;
 
-  count = 0;
+  alloc = 0;
   for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
-    ++count;
+    ++alloc;
 
   elf_elfheader (abfd)->e_phoff = bed->s->sizeof_ehdr;
   elf_elfheader (abfd)->e_phentsize = bed->s->sizeof_phdr;
-  elf_elfheader (abfd)->e_phnum = count;
+  elf_elfheader (abfd)->e_phnum = alloc;
+
+  if (elf_tdata (abfd)->program_header_size == (bfd_size_type) -1)
+    elf_tdata (abfd)->program_header_size = alloc * bed->s->sizeof_phdr;
+  else
+    BFD_ASSERT (elf_tdata (abfd)->program_header_size
+               >= alloc * bed->s->sizeof_phdr);
 
-  if (count == 0)
+  if (alloc == 0)
     {
       elf_tdata (abfd)->next_file_pos = bed->s->sizeof_ehdr;
       return TRUE;
     }
 
-  /* If we already counted the number of program segments, make sure
-     that we allocated enough space.  This happens when SIZEOF_HEADERS
-     is used in a linker script.  */
-  alloc = elf_tdata (abfd)->program_header_size / bed->s->sizeof_phdr;
-  if (alloc != 0 && count > alloc)
-    {
-      ((*_bfd_error_handler)
-       (_("%B: Not enough room for program headers (allocated %u, need %u)"),
-       abfd, alloc, count));
-      bfd_set_error (bfd_error_bad_value);
-      return FALSE;
-    }
-
-  if (alloc == 0)
-    alloc = count;
-
-  amt = alloc * sizeof (Elf_Internal_Phdr);
-  phdrs = bfd_alloc (abfd, amt);
+  phdrs = bfd_alloc2 (abfd, alloc, sizeof (Elf_Internal_Phdr));
+  elf_tdata (abfd)->phdr = phdrs;
   if (phdrs == NULL)
     return FALSE;
 
+  maxpagesize = 1;
+  if ((abfd->flags & D_PAGED) != 0)
+    maxpagesize = bed->maxpagesize;
+
   off = bed->s->sizeof_ehdr;
   off += alloc * bed->s->sizeof_phdr;
 
-  filehdr_vaddr = 0;
-  filehdr_paddr = 0;
-  phdrs_vaddr = 0;
-  phdrs_paddr = 0;
-
   for (m = elf_tdata (abfd)->segment_map, p = phdrs;
        m != NULL;
        m = m->next, p++)
     {
-      unsigned int i;
       asection **secpp;
 
       /* If elf_segment_map is not from map_sections_to_segments, the
@@ -4142,6 +4312,41 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
       p->p_type = m->p_type;
       p->p_flags = m->p_flags;
 
+      if (m->count == 0)
+       p->p_vaddr = 0;
+      else
+       p->p_vaddr = m->sections[0]->vma - m->p_vaddr_offset;
+
+      if (m->p_paddr_valid)
+       p->p_paddr = m->p_paddr;
+      else if (m->count == 0)
+       p->p_paddr = 0;
+      else
+       p->p_paddr = m->sections[0]->lma;
+
+      if (p->p_type == PT_LOAD
+         && (abfd->flags & D_PAGED) != 0)
+       {
+         /* p_align in demand paged PT_LOAD segments effectively stores
+            the maximum page size.  When copying an executable with
+            objcopy, we set m->p_align from the input file.  Use this
+            value for maxpagesize rather than bed->maxpagesize, which
+            may be different.  Note that we use maxpagesize for PT_TLS
+            segment alignment later in this function, so we are relying
+            on at least one PT_LOAD segment appearing before a PT_TLS
+            segment.  */
+         if (m->p_align_valid)
+           maxpagesize = m->p_align;
+
+         p->p_align = maxpagesize;
+       }
+      else if (m->count == 0)
+       p->p_align = 1 << bed->s->log_file_align;
+      else if (m->p_align_valid)
+       p->p_align = m->p_align;
+      else
+       p->p_align = 0;
+
       if (p->p_type == PT_LOAD
          && m->count > 0)
        {
@@ -4149,18 +4354,22 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
          bfd_vma adjust;
          unsigned int align_power = 0;
 
-         for (i = 0, secpp = m->sections; i < m->count; i++, secpp++)
+         if (m->p_align_valid)
+           align = p->p_align;
+         else
            {
-             unsigned int secalign;
-
-             secalign = bfd_get_section_alignment (abfd, *secpp);
-             if (secalign > align_power)
-               align_power = secalign;
+             for (i = 0, secpp = m->sections; i < m->count; i++, secpp++)
+               {
+                 unsigned int secalign;
+                 
+                 secalign = bfd_get_section_alignment (abfd, *secpp);
+                 if (secalign > align_power)
+                   align_power = secalign;
+               }
+             align = (bfd_size_type) 1 << align_power;
+             if (align < maxpagesize)
+               align = maxpagesize;
            }
-         align = (bfd_size_type) 1 << align_power;
-
-         if ((abfd->flags & D_PAGED) != 0 && bed->maxpagesize > align)
-           align = bed->maxpagesize;
 
          adjust = vma_page_aligned_bias (m->sections[0]->vma, off, align);
          off += adjust;
@@ -4176,7 +4385,8 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
                 .tbss, we need to look at the next section to decide
                 whether the segment has any loadable sections.  */
              i = 0;
-             while ((m->sections[i]->flags & SEC_LOAD) == 0)
+             while ((m->sections[i]->flags & SEC_LOAD) == 0
+                    && (m->sections[i]->flags & SEC_HAS_CONTENTS) == 0)
                {
                  if ((m->sections[i]->flags & SEC_THREAD_LOCAL) == 0
                      || ++i >= m->count)
@@ -4201,26 +4411,6 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
          return FALSE;
        }
 
-      if (m->count == 0)
-       p->p_vaddr = 0;
-      else
-       p->p_vaddr = m->sections[0]->vma;
-
-      if (m->p_paddr_valid)
-       p->p_paddr = m->p_paddr;
-      else if (m->count == 0)
-       p->p_paddr = 0;
-      else
-       p->p_paddr = m->sections[0]->lma;
-
-      if (p->p_type == PT_LOAD
-         && (abfd->flags & D_PAGED) != 0)
-       p->p_align = bed->maxpagesize;
-      else if (m->count == 0)
-       p->p_align = 1 << bed->s->log_file_align;
-      else
-       p->p_align = 0;
-
       p->p_offset = 0;
       p->p_filesz = 0;
       p->p_memsz = 0;
@@ -4229,7 +4419,6 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
        {
          if (! m->p_flags_valid)
            p->p_flags |= PF_R;
-         p->p_offset = 0;
          p->p_filesz = bed->s->sizeof_ehdr;
          p->p_memsz = bed->s->sizeof_ehdr;
          if (m->count > 0)
@@ -4249,11 +4438,6 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
              if (! m->p_paddr_valid)
                p->p_paddr -= off;
            }
-         if (p->p_type == PT_LOAD)
-           {
-             filehdr_vaddr = p->p_vaddr;
-             filehdr_paddr = p->p_paddr;
-           }
        }
 
       if (m->includes_phdrs)
@@ -4261,15 +4445,7 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
          if (! m->p_flags_valid)
            p->p_flags |= PF_R;
 
-         if (m->includes_filehdr)
-           {
-             if (p->p_type == PT_LOAD)
-               {
-                 phdrs_vaddr = p->p_vaddr + bed->s->sizeof_ehdr;
-                 phdrs_paddr = p->p_paddr + bed->s->sizeof_ehdr;
-               }
-           }
-         else
+         if (!m->includes_filehdr)
            {
              p->p_offset = bed->s->sizeof_ehdr;
 
@@ -4280,14 +4456,6 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
                  if (! m->p_paddr_valid)
                    p->p_paddr -= off - p->p_offset;
                }
-
-             if (p->p_type == PT_LOAD)
-               {
-                 phdrs_vaddr = p->p_vaddr;
-                 phdrs_paddr = p->p_paddr;
-               }
-             else
-               phdrs_vaddr = bed->maxpagesize + bed->s->sizeof_ehdr;
            }
 
          p->p_filesz += alloc * bed->s->sizeof_phdr;
@@ -4309,6 +4477,11 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
            }
        }
 
+      /* Set up p_filesz, p_memsz, p_align and p_flags from the section
+        maps.  Set filepos for sections in PT_LOAD segments, and in
+        core files, for sections in PT_NOTE segments.
+        assign_file_positions_for_non_load_sections will set filepos
+        for other sections and update p_filesz for other segments.  */
       for (i = 0, secpp = m->sections; i < m->count; i++, secpp++)
        {
          asection *sec;
@@ -4317,16 +4490,18 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
 
          sec = *secpp;
          flags = sec->flags;
-         align = 1 << bfd_get_section_alignment (abfd, sec);
+         align = (bfd_size_type) 1 << bfd_get_section_alignment (abfd, sec);
 
          if (p->p_type == PT_LOAD
              || p->p_type == PT_TLS)
            {
-             bfd_signed_vma adjust;
+             bfd_signed_vma adjust = sec->lma - (p->p_paddr + p->p_filesz);
 
-             if ((flags & SEC_LOAD) != 0)
+             if ((flags & SEC_LOAD) != 0
+                 || ((flags & SEC_ALLOC) != 0
+                     && ((flags & SEC_THREAD_LOCAL) == 0
+                         || p->p_type == PT_TLS)))
                {
-                 adjust = sec->lma - (p->p_paddr + p->p_filesz);
                  if (adjust < 0)
                    {
                      (*_bfd_error_handler)
@@ -4334,24 +4509,13 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
                         abfd, sec, (unsigned long) sec->lma);
                      adjust = 0;
                    }
-                 off += adjust;
-                 p->p_filesz += adjust;
-                 p->p_memsz += adjust;
-               }
-             /* .tbss is special.  It doesn't contribute to p_memsz of
-                normal segments.  */
-             else if ((flags & SEC_THREAD_LOCAL) == 0
-                      || p->p_type == PT_TLS)
-               {
-                 /* The section VMA must equal the file position
-                    modulo the page size.  */
-                 bfd_size_type page = align;
-                 if ((abfd->flags & D_PAGED) != 0 && bed->maxpagesize > page)
-                   page = bed->maxpagesize;
-                 adjust = vma_page_aligned_bias (sec->vma,
-                                                 p->p_vaddr + p->p_memsz,
-                                                 page);
                  p->p_memsz += adjust;
+
+                 if ((flags & SEC_LOAD) != 0)
+                   {
+                     off += adjust;
+                     p->p_filesz += adjust;
+                   }
                }
            }
 
@@ -4380,7 +4544,7 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
            {
              if (p->p_type == PT_LOAD)
                {
-                 sec->filepos = off;
+                 sec->filepos = off + voff;
                  /* FIXME: The SEC_HAS_CONTENTS test here dates back to
                     1997, and the exact reason for it isn't clear.  One
                     plausible explanation is that it is to work around
@@ -4405,34 +4569,29 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
                  p->p_filesz += sec->size;
                  p->p_memsz += sec->size;
                }
-             /* PR ld/594:  Sections in note segments which are not loaded
-                contribute to the file size but not the in-memory size.  */
-             else if (p->p_type == PT_NOTE
-                 && (flags & SEC_HAS_CONTENTS) != 0)
-               p->p_filesz += sec->size;
 
              /* .tbss is special.  It doesn't contribute to p_memsz of
                 normal segments.  */
-             else if ((flags & SEC_THREAD_LOCAL) == 0
-                      || p->p_type == PT_TLS)
+             else if ((flags & SEC_ALLOC) != 0
+                      && ((flags & SEC_THREAD_LOCAL) == 0
+                          || p->p_type == PT_TLS))
                p->p_memsz += sec->size;
 
              if (p->p_type == PT_TLS
                  && sec->size == 0
                  && (sec->flags & SEC_HAS_CONTENTS) == 0)
                {
-                 struct bfd_link_order *o;
-                 bfd_vma tbss_size = 0;
-
-                 for (o = sec->map_head.link_order; o != NULL; o = o->next)
-                   if (tbss_size < o->offset + o->size)
-                     tbss_size = o->offset + o->size;
-
-                 p->p_memsz += tbss_size;
+                 struct bfd_link_order *o = sec->map_tail.link_order;
+                 if (o != NULL)
+                   p->p_memsz += o->offset + o->size;
                }
 
-             if (align > p->p_align
-                 && (p->p_type != PT_LOAD || (abfd->flags & D_PAGED) == 0))
+             if (p->p_type == PT_GNU_RELRO)
+               p->p_align = 1;
+             else if (align > p->p_align
+                      && !m->p_align_valid
+                      && (p->p_type != PT_LOAD
+                          || (abfd->flags & D_PAGED) == 0))
                p->p_align = align;
            }
 
@@ -4447,34 +4606,133 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
        }
     }
 
+  elf_tdata (abfd)->next_file_pos = off;
+  return TRUE;
+}
+
+/* Assign file positions for the other sections.  */
+
+static bfd_boolean
+assign_file_positions_for_non_load_sections (bfd *abfd,
+                                            struct bfd_link_info *link_info)
+{
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  Elf_Internal_Shdr **i_shdrpp;
+  Elf_Internal_Shdr **hdrpp;
+  Elf_Internal_Phdr *phdrs;
+  Elf_Internal_Phdr *p;
+  struct elf_segment_map *m;
+  bfd_vma filehdr_vaddr, filehdr_paddr;
+  bfd_vma phdrs_vaddr, phdrs_paddr;
+  file_ptr off;
+  unsigned int num_sec;
+  unsigned int i;
+  unsigned int count;
+
+  i_shdrpp = elf_elfsections (abfd);
+  num_sec = elf_numsections (abfd);
+  off = elf_tdata (abfd)->next_file_pos;
+  for (i = 1, hdrpp = i_shdrpp + 1; i < num_sec; i++, hdrpp++)
+    {
+      struct elf_obj_tdata *tdata = elf_tdata (abfd);
+      Elf_Internal_Shdr *hdr;
+
+      hdr = *hdrpp;
+      if (hdr->bfd_section != NULL
+         && (hdr->bfd_section->filepos != 0
+             || (hdr->sh_type == SHT_NOBITS
+                 && hdr->contents == NULL)))
+       hdr->sh_offset = hdr->bfd_section->filepos;
+      else if ((hdr->sh_flags & SHF_ALLOC) != 0)
+       {
+         if (hdr->sh_size != 0)
+           ((*_bfd_error_handler)
+            (_("%B: warning: allocated section `%s' not in segment"),
+             abfd,
+             (hdr->bfd_section == NULL
+              ? "*unknown*"
+              : hdr->bfd_section->name)));
+         /* We don't need to page align empty sections.  */
+         if ((abfd->flags & D_PAGED) != 0 && hdr->sh_size != 0)
+           off += vma_page_aligned_bias (hdr->sh_addr, off,
+                                         bed->maxpagesize);
+         else
+           off += vma_page_aligned_bias (hdr->sh_addr, off,
+                                         hdr->sh_addralign);
+         off = _bfd_elf_assign_file_position_for_section (hdr, off,
+                                                          FALSE);
+       }
+      else if (((hdr->sh_type == SHT_REL || hdr->sh_type == SHT_RELA)
+               && hdr->bfd_section == NULL)
+              || hdr == i_shdrpp[tdata->symtab_section]
+              || hdr == i_shdrpp[tdata->symtab_shndx_section]
+              || hdr == i_shdrpp[tdata->strtab_section])
+       hdr->sh_offset = -1;
+      else
+       off = _bfd_elf_assign_file_position_for_section (hdr, off, TRUE);
+
+      if (i == SHN_LORESERVE - 1)
+       {
+         i += SHN_HIRESERVE + 1 - SHN_LORESERVE;
+         hdrpp += SHN_HIRESERVE + 1 - SHN_LORESERVE;
+       }
+    }
+
   /* Now that we have set the section file positions, we can set up
      the file positions for the non PT_LOAD segments.  */
+  count = 0;
+  filehdr_vaddr = 0;
+  filehdr_paddr = 0;
+  phdrs_vaddr = bed->maxpagesize + bed->s->sizeof_ehdr;
+  phdrs_paddr = 0;
+  phdrs = elf_tdata (abfd)->phdr;
   for (m = elf_tdata (abfd)->segment_map, p = phdrs;
        m != NULL;
        m = m->next, p++)
     {
-      if (p->p_type != PT_LOAD && m->count > 0)
+      ++count;
+      if (p->p_type != PT_LOAD)
+       continue;
+
+      if (m->includes_filehdr)
+       {
+         filehdr_vaddr = p->p_vaddr;
+         filehdr_paddr = p->p_paddr;
+       }
+      if (m->includes_phdrs)
        {
-         BFD_ASSERT (! m->includes_filehdr && ! m->includes_phdrs);
-         /* If the section has not yet been assigned a file position,
-            do so now.  The ARM BPABI requires that .dynamic section
-            not be marked SEC_ALLOC because it is not part of any
-            PT_LOAD segment, so it will not be processed above.  */
-         if (p->p_type == PT_DYNAMIC && m->sections[0]->filepos == 0)
+         phdrs_vaddr = p->p_vaddr;
+         phdrs_paddr = p->p_paddr;
+         if (m->includes_filehdr)
            {
-             unsigned int i;
-             Elf_Internal_Shdr ** const i_shdrpp = elf_elfsections (abfd);
-
-             i = 1;
-             while (i_shdrpp[i]->bfd_section != m->sections[0])
-               ++i;
-             off = (_bfd_elf_assign_file_position_for_section 
-                    (i_shdrpp[i], off, TRUE));
-             p->p_filesz = m->sections[0]->size;
+             phdrs_vaddr += bed->s->sizeof_ehdr;
+             phdrs_paddr += bed->s->sizeof_ehdr;
            }
-         p->p_offset = m->sections[0]->filepos;
        }
-      if (m->count == 0)
+    }
+
+  for (m = elf_tdata (abfd)->segment_map, p = phdrs;
+       m != NULL;
+       m = m->next, p++)
+    {
+      if (m->count != 0)
+       {
+         if (p->p_type != PT_LOAD
+             && (p->p_type != PT_NOTE || bfd_get_format (abfd) != bfd_core))
+           {
+             Elf_Internal_Shdr *hdr;
+             BFD_ASSERT (!m->includes_filehdr && !m->includes_phdrs);
+
+             hdr = &elf_section_data (m->sections[m->count - 1])->this_hdr;
+             p->p_filesz = (m->sections[m->count - 1]->filepos
+                            - m->sections[0]->filepos);
+             if (hdr->sh_type != SHT_NOBITS)
+               p->p_filesz += hdr->sh_size;
+
+             p->p_offset = m->sections[0]->filepos;
+           }
+       }
+      else
        {
          if (m->includes_filehdr)
            {
@@ -4489,162 +4747,42 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
                p->p_paddr = phdrs_paddr;
            }
          else if (p->p_type == PT_GNU_RELRO)
-           {
-             Elf_Internal_Phdr *lp;
-
-             for (lp = phdrs; lp < phdrs + count; ++lp)
-               {
-                 if (lp->p_type == PT_LOAD
-                     && lp->p_vaddr <= link_info->relro_end
-                     && lp->p_vaddr >= link_info->relro_start
-                     && lp->p_vaddr + lp->p_filesz
-                        >= link_info->relro_end)
-                   break;
-               }
-
-             if (lp < phdrs + count
-                 && link_info->relro_end > lp->p_vaddr)
-               {
-                 p->p_vaddr = lp->p_vaddr;
-                 p->p_paddr = lp->p_paddr;
-                 p->p_offset = lp->p_offset;
-                 p->p_filesz = link_info->relro_end - lp->p_vaddr;
-                 p->p_memsz = p->p_filesz;
-                 p->p_align = 1;
-                 p->p_flags = (lp->p_flags & ~PF_W);
-               }
-             else
-               {
-                 memset (p, 0, sizeof *p);
-                 p->p_type = PT_NULL;
-               }
-           }
-       }
-    }
-
-  /* Clear out any program headers we allocated but did not use.  */
-  for (; count < alloc; count++, p++)
-    {
-      memset (p, 0, sizeof *p);
-      p->p_type = PT_NULL;
-    }
-
-  elf_tdata (abfd)->phdr = phdrs;
-
-  elf_tdata (abfd)->next_file_pos = off;
-
-  /* Write out the program headers.  */
-  if (bfd_seek (abfd, (bfd_signed_vma) bed->s->sizeof_ehdr, SEEK_SET) != 0
-      || bed->s->write_out_phdrs (abfd, phdrs, alloc) != 0)
-    return FALSE;
-
-  return TRUE;
-}
-
-/* Get the size of the program header.
-
-   If this is called by the linker before any of the section VMA's are set, it
-   can't calculate the correct value for a strange memory layout.  This only
-   happens when SIZEOF_HEADERS is used in a linker script.  In this case,
-   SORTED_HDRS is NULL and we assume the normal scenario of one text and one
-   data segment (exclusive of .interp and .dynamic).
-
-   ??? User written scripts must either not use SIZEOF_HEADERS, or assume there
-   will be two segments.  */
-
-static bfd_size_type
-get_program_header_size (bfd *abfd)
-{
-  size_t segs;
-  asection *s;
-  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
-
-  /* We can't return a different result each time we're called.  */
-  if (elf_tdata (abfd)->program_header_size != 0)
-    return elf_tdata (abfd)->program_header_size;
-
-  if (elf_tdata (abfd)->segment_map != NULL)
-    {
-      struct elf_segment_map *m;
-
-      segs = 0;
-      for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
-       ++segs;
-      elf_tdata (abfd)->program_header_size = segs * bed->s->sizeof_phdr;
-      return elf_tdata (abfd)->program_header_size;
-    }
-
-  /* Assume we will need exactly two PT_LOAD segments: one for text
-     and one for data.  */
-  segs = 2;
-
-  s = bfd_get_section_by_name (abfd, ".interp");
-  if (s != NULL && (s->flags & SEC_LOAD) != 0)
-    {
-      /* If we have a loadable interpreter section, we need a
-        PT_INTERP segment.  In this case, assume we also need a
-        PT_PHDR segment, although that may not be true for all
-        targets.  */
-      segs += 2;
-    }
-
-  if (bfd_get_section_by_name (abfd, ".dynamic") != NULL)
-    {
-      /* We need a PT_DYNAMIC segment.  */
-      ++segs;
-    }
-
-  if (elf_tdata (abfd)->eh_frame_hdr)
-    {
-      /* We need a PT_GNU_EH_FRAME segment.  */
-      ++segs;
-    }
-
-  if (elf_tdata (abfd)->stack_flags)
-    {
-      /* We need a PT_GNU_STACK segment.  */
-      ++segs;
-    }
-
-  if (elf_tdata (abfd)->relro)
-    {
-      /* We need a PT_GNU_RELRO segment.  */
-      ++segs;
-    }
-
-  for (s = abfd->sections; s != NULL; s = s->next)
-    {
-      if ((s->flags & SEC_LOAD) != 0
-         && strncmp (s->name, ".note", 5) == 0)
-       {
-         /* We need a PT_NOTE segment.  */
-         ++segs;
-       }
-    }
+           {
+             Elf_Internal_Phdr *lp;
 
-  for (s = abfd->sections; s != NULL; s = s->next)
-    {
-      if (s->flags & SEC_THREAD_LOCAL)
-       {
-         /* We need a PT_TLS segment.  */
-         ++segs;
-         break;
+             for (lp = phdrs; lp < phdrs + count; ++lp)
+               {
+                 if (lp->p_type == PT_LOAD
+                     && lp->p_vaddr <= link_info->relro_end
+                     && lp->p_vaddr >= link_info->relro_start
+                     && (lp->p_vaddr + lp->p_filesz
+                         >= link_info->relro_end))
+                   break;
+               }
+
+             if (lp < phdrs + count
+                 && link_info->relro_end > lp->p_vaddr)
+               {
+                 p->p_vaddr = lp->p_vaddr;
+                 p->p_paddr = lp->p_paddr;
+                 p->p_offset = lp->p_offset;
+                 p->p_filesz = link_info->relro_end - lp->p_vaddr;
+                 p->p_memsz = p->p_filesz;
+                 p->p_align = 1;
+                 p->p_flags = (lp->p_flags & ~PF_W);
+               }
+             else
+               {
+                 memset (p, 0, sizeof *p);
+                 p->p_type = PT_NULL;
+               }
+           }
        }
     }
 
-  /* Let the backend count up any program headers it might need.  */
-  if (bed->elf_backend_additional_program_headers)
-    {
-      int a;
-
-      a = (*bed->elf_backend_additional_program_headers) (abfd);
-      if (a == -1)
-       abort ();
-      segs += a;
-    }
+  elf_tdata (abfd)->next_file_pos = off;
 
-  elf_tdata (abfd)->program_header_size = segs * bed->s->sizeof_phdr;
-  return elf_tdata (abfd)->program_header_size;
+  return TRUE;
 }
 
 /* Work out the file positions of all the sections.  This is called by
@@ -4665,16 +4803,16 @@ static bfd_boolean
 assign_file_positions_except_relocs (bfd *abfd,
                                     struct bfd_link_info *link_info)
 {
-  struct elf_obj_tdata * const tdata = elf_tdata (abfd);
-  Elf_Internal_Ehdr * const i_ehdrp = elf_elfheader (abfd);
-  Elf_Internal_Shdr ** const i_shdrpp = elf_elfsections (abfd);
-  unsigned int num_sec = elf_numsections (abfd);
+  struct elf_obj_tdata *tdata = elf_tdata (abfd);
+  Elf_Internal_Ehdr *i_ehdrp = elf_elfheader (abfd);
   file_ptr off;
   const struct elf_backend_data *bed = get_elf_backend_data (abfd);
 
   if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0
       && bfd_get_format (abfd) != bfd_core)
     {
+      Elf_Internal_Shdr ** const i_shdrpp = elf_elfsections (abfd);
+      unsigned int num_sec = elf_numsections (abfd);
       Elf_Internal_Shdr **hdrpp;
       unsigned int i;
 
@@ -4709,57 +4847,30 @@ assign_file_positions_except_relocs (bfd *abfd,
     }
   else
     {
-      unsigned int i;
-      Elf_Internal_Shdr **hdrpp;
+      unsigned int alloc;
 
       /* Assign file positions for the loaded sections based on the
          assignment of sections to segments.  */
-      if (! assign_file_positions_for_segments (abfd, link_info))
+      if (!assign_file_positions_for_load_sections (abfd, link_info))
        return FALSE;
 
-      /* Assign file positions for the other sections.  */
+      /* And for non-load sections.  */
+      if (!assign_file_positions_for_non_load_sections (abfd, link_info))
+       return FALSE;
 
-      off = elf_tdata (abfd)->next_file_pos;
-      for (i = 1, hdrpp = i_shdrpp + 1; i < num_sec; i++, hdrpp++)
+      if (bed->elf_backend_modify_program_headers != NULL)
        {
-         Elf_Internal_Shdr *hdr;
+         if (!(*bed->elf_backend_modify_program_headers) (abfd, link_info))
+           return FALSE;
+       }
 
-         hdr = *hdrpp;
-         if (hdr->bfd_section != NULL
-             && hdr->bfd_section->filepos != 0)
-           hdr->sh_offset = hdr->bfd_section->filepos;
-         else if ((hdr->sh_flags & SHF_ALLOC) != 0)
-           {
-             ((*_bfd_error_handler)
-              (_("%B: warning: allocated section `%s' not in segment"),
-               abfd,
-               (hdr->bfd_section == NULL
-                ? "*unknown*"
-                : hdr->bfd_section->name)));
-             if ((abfd->flags & D_PAGED) != 0)
-               off += vma_page_aligned_bias (hdr->sh_addr, off,
-                                             bed->maxpagesize);
-             else
-               off += vma_page_aligned_bias (hdr->sh_addr, off,
-                                             hdr->sh_addralign);
-             off = _bfd_elf_assign_file_position_for_section (hdr, off,
-                                                              FALSE);
-           }
-         else if (((hdr->sh_type == SHT_REL || hdr->sh_type == SHT_RELA)
-                   && hdr->bfd_section == NULL)
-                  || hdr == i_shdrpp[tdata->symtab_section]
-                  || hdr == i_shdrpp[tdata->symtab_shndx_section]
-                  || hdr == i_shdrpp[tdata->strtab_section])
-           hdr->sh_offset = -1;
-         else
-           off = _bfd_elf_assign_file_position_for_section (hdr, off, TRUE);
+      /* Write out the program headers.  */
+      alloc = tdata->program_header_size / bed->s->sizeof_phdr;
+      if (bfd_seek (abfd, (bfd_signed_vma) bed->s->sizeof_ehdr, SEEK_SET) != 0
+         || bed->s->write_out_phdrs (abfd, tdata->phdr, alloc) != 0)
+       return FALSE;
 
-         if (i == SHN_LORESERVE - 1)
-           {
-             i += SHN_HIRESERVE + 1 - SHN_LORESERVE;
-             hdrpp += SHN_HIRESERVE + 1 - SHN_LORESERVE;
-           }
-       }
+      off = tdata->next_file_pos;
     }
 
   /* Place the section headers.  */
@@ -4767,7 +4878,7 @@ assign_file_positions_except_relocs (bfd *abfd,
   i_ehdrp->e_shoff = off;
   off += i_ehdrp->e_shnum * i_ehdrp->e_shentsize;
 
-  elf_tdata (abfd)->next_file_pos = off;
+  tdata->next_file_pos = off;
 
   return TRUE;
 }
@@ -5006,13 +5117,14 @@ _bfd_elf_symbol_from_bfd_symbol (bfd *abfd, asymbol **asym_ptr_ptr)
       && (flags & BSF_SECTION_SYM)
       && asym_ptr->section)
     {
+      asection *sec;
       int indx;
 
-      if (asym_ptr->section->output_section != NULL)
-       indx = asym_ptr->section->output_section->index;
-      else
-       indx = asym_ptr->section->index;
-      if (indx < elf_num_section_syms (abfd)
+      sec = asym_ptr->section;
+      if (sec->owner != abfd && sec->output_section != NULL)
+       sec = sec->output_section;
+      if (sec->owner == abfd
+         && (indx = sec->index) < elf_num_section_syms (abfd)
          && elf_section_syms (abfd)[indx] != NULL)
        asym_ptr->udata.i = elf_section_syms (abfd)[indx]->udata.i;
     }
@@ -5043,10 +5155,10 @@ _bfd_elf_symbol_from_bfd_symbol (bfd *abfd, asymbol **asym_ptr_ptr)
   return idx;
 }
 
-/* Copy private BFD data.  This copies any program header information.  */
+/* Rewrite program header information.  */
 
 static bfd_boolean
-copy_private_bfd_data (bfd *ibfd, bfd *obfd)
+rewrite_elf_program_header (bfd *ibfd, bfd *obfd)
 {
   Elf_Internal_Ehdr *iehdr;
   struct elf_segment_map *map;
@@ -5062,13 +5174,6 @@ copy_private_bfd_data (bfd *ibfd, bfd *obfd)
   unsigned int phdr_adjust_num = 0;
   const struct elf_backend_data *bed;
 
-  if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
-      || bfd_get_flavour (obfd) != bfd_target_elf_flavour)
-    return TRUE;
-
-  if (elf_tdata (ibfd)->phdr == NULL)
-    return TRUE;
-
   bed = get_elf_backend_data (ibfd);
   iehdr = elf_elfheader (ibfd);
 
@@ -5108,7 +5213,7 @@ copy_private_bfd_data (bfd *ibfd, bfd *obfd)
    && bfd_get_format (ibfd) == bfd_core                                        \
    && s->vma == 0 && s->lma == 0                                       \
    && (bfd_vma) s->filepos >= p->p_offset                              \
-   && ((bfd_vma) s->filepos + s->size                          \
+   && ((bfd_vma) s->filepos + s->size                                  \
        <= p->p_offset + p->p_filesz))
 
   /* The complicated case when p_vaddr is 0 is to handle the Solaris
@@ -5122,7 +5227,7 @@ copy_private_bfd_data (bfd *ibfd, bfd *obfd)
    && (s->flags & SEC_HAS_CONTENTS) != 0                               \
    && s->size > 0                                                      \
    && (bfd_vma) s->filepos >= p->p_offset                              \
-   && ((bfd_vma) s->filepos + s->size                          \
+   && ((bfd_vma) s->filepos + s->size                                  \
        <= p->p_offset + p->p_filesz))
 
   /* Decide if the given section should be included in the given segment.
@@ -5137,13 +5242,12 @@ copy_private_bfd_data (bfd *ibfd, bfd *obfd)
        7. SHF_TLS sections are only in PT_TLS or PT_LOAD segments.
        8. PT_DYNAMIC should not contain empty sections at the beginning
           (with the possible exception of .dynamic).  */
-#define INCLUDE_SECTION_IN_SEGMENT(section, segment, bed)              \
+#define IS_SECTION_IN_INPUT_SEGMENT(section, segment, bed)             \
   ((((segment->p_paddr                                                 \
       ? IS_CONTAINED_BY_LMA (section, segment, segment->p_paddr)       \
       : IS_CONTAINED_BY_VMA (section, segment))                                \
      && (section->flags & SEC_ALLOC) != 0)                             \
     || IS_COREFILE_NOTE (segment, section))                            \
-   && section->output_section != NULL                                  \
    && segment->p_type != PT_GNU_STACK                                  \
    && (segment->p_type != PT_TLS                                       \
        || (section->flags & SEC_THREAD_LOCAL))                         \
@@ -5159,6 +5263,12 @@ copy_private_bfd_data (bfd *ibfd, bfd *obfd)
            == 0))                                                      \
    && ! section->segment_mark)
 
+/* If the output section of a section in the input segment is NULL,
+   it is removed from the corresponding output segment.   */
+#define INCLUDE_SECTION_IN_SEGMENT(section, segment, bed)              \
+  (IS_SECTION_IN_INPUT_SEGMENT (section, segment, bed)         \
+   && section->output_section != NULL)
+
   /* Returns TRUE iff seg1 starts after the end of seg2.  */
 #define SEGMENT_AFTER_SEGMENT(seg1, seg2, field)                       \
   (seg1->field >= SEGMENT_END (seg2, seg2->field))
@@ -5266,22 +5376,33 @@ copy_private_bfd_data (bfd *ibfd, bfd *obfd)
       bfd_vma       suggested_lma;
       unsigned int  j;
       bfd_size_type amt;
+      asection *    first_section;
 
       if (segment->p_type == PT_NULL)
        continue;
 
+      first_section = NULL;
       /* Compute how many sections might be placed into this segment.  */
       for (section = ibfd->sections, section_count = 0;
           section != NULL;
           section = section->next)
-       if (INCLUDE_SECTION_IN_SEGMENT (section, segment, bed))
-         ++section_count;
+       {
+         /* Find the first section in the input segment, which may be
+            removed from the corresponding output segment.   */
+         if (IS_SECTION_IN_INPUT_SEGMENT (section, segment, bed))
+           {
+             if (first_section == NULL)
+               first_section = section;
+             if (section->output_section != NULL)
+               ++section_count;
+           }
+       }
 
       /* Allocate a segment map big enough to contain
         all of the sections we have selected.  */
       amt = sizeof (struct elf_segment_map);
       amt += ((bfd_size_type) section_count - 1) * sizeof (asection *);
-      map = bfd_alloc (obfd, amt);
+      map = bfd_zalloc (obfd, amt);
       if (map == NULL)
        return FALSE;
 
@@ -5291,8 +5412,15 @@ copy_private_bfd_data (bfd *ibfd, bfd *obfd)
       map->p_type        = segment->p_type;
       map->p_flags       = segment->p_flags;
       map->p_flags_valid = 1;
-      map->p_paddr       = segment->p_paddr;
-      map->p_paddr_valid = 1;
+
+      /* If the first section in the input segment is removed, there is
+        no need to preserve segment physical address in the corresponding
+        output segment.  */
+      if (!first_section || first_section->output_section != NULL)
+       {
+         map->p_paddr = segment->p_paddr;
+         map->p_paddr_valid = 1;
+       }
 
       /* Determine if this segment contains the ELF file header
         and if it contains the program headers themselves.  */
@@ -5361,8 +5489,7 @@ copy_private_bfd_data (bfd *ibfd, bfd *obfd)
 
       /* Gcc 2.96 miscompiles this code on mips. Don't do casting here
         to work around this long long bug.  */
-      amt = section_count * sizeof (asection *);
-      sections = bfd_malloc (amt);
+      sections = bfd_malloc2 (section_count, sizeof (asection *));
       if (sections == NULL)
        return FALSE;
 
@@ -5439,7 +5566,14 @@ copy_private_bfd_data (bfd *ibfd, bfd *obfd)
          map->count = section_count;
          *pointer_to_map = map;
          pointer_to_map = &map->next;
-
+         
+         if (matching_lma != map->p_paddr
+             && !map->includes_filehdr && !map->includes_phdrs)
+           /* There is some padding before the first section in the
+              segment.  So, we must account for that in the output
+              segment's vma.  */
+           map->p_vaddr_offset = matching_lma - map->p_paddr;
+         
          free (sections);
          continue;
        }
@@ -5589,43 +5723,321 @@ copy_private_bfd_data (bfd *ibfd, bfd *obfd)
       free (sections);
     }
 
-  /* The Solaris linker creates program headers in which all the
-     p_paddr fields are zero.  When we try to objcopy or strip such a
-     file, we get confused.  Check for this case, and if we find it
-     reset the p_paddr_valid fields.  */
-  for (map = map_first; map != NULL; map = map->next)
-    if (map->p_paddr != 0)
-      break;
-  if (map == NULL)
-    for (map = map_first; map != NULL; map = map->next)
-      map->p_paddr_valid = 0;
-
-  elf_tdata (obfd)->segment_map = map_first;
+  /* The Solaris linker creates program headers in which all the
+     p_paddr fields are zero.  When we try to objcopy or strip such a
+     file, we get confused.  Check for this case, and if we find it
+     reset the p_paddr_valid fields.  */
+  for (map = map_first; map != NULL; map = map->next)
+    if (map->p_paddr != 0)
+      break;
+  if (map == NULL)
+    for (map = map_first; map != NULL; map = map->next)
+      map->p_paddr_valid = 0;
+
+  elf_tdata (obfd)->segment_map = map_first;
+
+  /* If we had to estimate the number of program headers that were
+     going to be needed, then check our estimate now and adjust
+     the offset if necessary.  */
+  if (phdr_adjust_seg != NULL)
+    {
+      unsigned int count;
+
+      for (count = 0, map = map_first; map != NULL; map = map->next)
+       count++;
+
+      if (count > phdr_adjust_num)
+       phdr_adjust_seg->p_paddr
+         -= (count - phdr_adjust_num) * iehdr->e_phentsize;
+    }
+
+#undef SEGMENT_END
+#undef SECTION_SIZE
+#undef IS_CONTAINED_BY_VMA
+#undef IS_CONTAINED_BY_LMA
+#undef IS_COREFILE_NOTE
+#undef IS_SOLARIS_PT_INTERP
+#undef IS_SECTION_IN_INPUT_SEGMENT
+#undef INCLUDE_SECTION_IN_SEGMENT
+#undef SEGMENT_AFTER_SEGMENT
+#undef SEGMENT_OVERLAPS
+  return TRUE;
+}
+
+/* Copy ELF program header information.  */
+
+static bfd_boolean
+copy_elf_program_header (bfd *ibfd, bfd *obfd)
+{
+  Elf_Internal_Ehdr *iehdr;
+  struct elf_segment_map *map;
+  struct elf_segment_map *map_first;
+  struct elf_segment_map **pointer_to_map;
+  Elf_Internal_Phdr *segment;
+  unsigned int i;
+  unsigned int num_segments;
+  bfd_boolean phdr_included = FALSE;
+
+  iehdr = elf_elfheader (ibfd);
+
+  map_first = NULL;
+  pointer_to_map = &map_first;
+
+  num_segments = elf_elfheader (ibfd)->e_phnum;
+  for (i = 0, segment = elf_tdata (ibfd)->phdr;
+       i < num_segments;
+       i++, segment++)
+    {
+      asection *section;
+      unsigned int section_count;
+      bfd_size_type amt;
+      Elf_Internal_Shdr *this_hdr;
+      asection *first_section = NULL;
+
+      /* FIXME: Do we need to copy PT_NULL segment?  */
+      if (segment->p_type == PT_NULL)
+       continue;
+
+      /* Compute how many sections are in this segment.  */
+      for (section = ibfd->sections, section_count = 0;
+          section != NULL;
+          section = section->next)
+       {
+         this_hdr = &(elf_section_data(section)->this_hdr);
+         if (ELF_IS_SECTION_IN_SEGMENT_FILE (this_hdr, segment))
+           {
+             if (!first_section)
+               first_section = section;
+             section_count++;
+           }
+       }
+
+      /* Allocate a segment map big enough to contain
+        all of the sections we have selected.  */
+      amt = sizeof (struct elf_segment_map);
+      if (section_count != 0)
+       amt += ((bfd_size_type) section_count - 1) * sizeof (asection *);
+      map = bfd_zalloc (obfd, amt);
+      if (map == NULL)
+       return FALSE;
+
+      /* Initialize the fields of the output segment map with the
+        input segment.  */
+      map->next = NULL;
+      map->p_type = segment->p_type;
+      map->p_flags = segment->p_flags;
+      map->p_flags_valid = 1;
+      map->p_paddr = segment->p_paddr;
+      map->p_paddr_valid = 1;
+      map->p_align = segment->p_align;
+      map->p_align_valid = 1;
+      map->p_vaddr_offset = 0;
+
+      /* Determine if this segment contains the ELF file header
+        and if it contains the program headers themselves.  */
+      map->includes_filehdr = (segment->p_offset == 0
+                              && segment->p_filesz >= iehdr->e_ehsize);
+
+      map->includes_phdrs = 0;
+      if (! phdr_included || segment->p_type != PT_LOAD)
+       {
+         map->includes_phdrs =
+           (segment->p_offset <= (bfd_vma) iehdr->e_phoff
+            && (segment->p_offset + segment->p_filesz
+                >= ((bfd_vma) iehdr->e_phoff
+                    + iehdr->e_phnum * iehdr->e_phentsize)));
+
+         if (segment->p_type == PT_LOAD && map->includes_phdrs)
+           phdr_included = TRUE;
+       }
+
+      if (!map->includes_phdrs && !map->includes_filehdr)
+       /* There is some other padding before the first section.  */
+       map->p_vaddr_offset = ((first_section ? first_section->lma : 0)
+                              - segment->p_paddr);
+      
+      if (section_count != 0)
+       {
+         unsigned int isec = 0;
+
+         for (section = first_section;
+              section != NULL;
+              section = section->next)
+           {
+             this_hdr = &(elf_section_data(section)->this_hdr);
+             if (ELF_IS_SECTION_IN_SEGMENT_FILE (this_hdr, segment))
+               {
+                 map->sections[isec++] = section->output_section;
+                 if (isec == section_count)
+                   break;
+               }
+           }
+       }
+
+      map->count = section_count;
+      *pointer_to_map = map;
+      pointer_to_map = &map->next;
+    }
+
+  elf_tdata (obfd)->segment_map = map_first;
+  return TRUE;
+}
+
+/* Copy private BFD data.  This copies or rewrites ELF program header
+   information.  */
+
+static bfd_boolean
+copy_private_bfd_data (bfd *ibfd, bfd *obfd)
+{
+  if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
+      || bfd_get_flavour (obfd) != bfd_target_elf_flavour)
+    return TRUE;
+
+  if (elf_tdata (ibfd)->phdr == NULL)
+    return TRUE;
+
+  if (ibfd->xvec == obfd->xvec)
+    {
+      /* Check to see if any sections in the input BFD
+        covered by ELF program header have changed.  */
+      Elf_Internal_Phdr *segment;
+      asection *section, *osec;
+      unsigned int i, num_segments;
+      Elf_Internal_Shdr *this_hdr;
+
+      /* Initialize the segment mark field.  */
+      for (section = obfd->sections; section != NULL;
+          section = section->next)
+       section->segment_mark = FALSE;
+
+      num_segments = elf_elfheader (ibfd)->e_phnum;
+      for (i = 0, segment = elf_tdata (ibfd)->phdr;
+          i < num_segments;
+          i++, segment++)
+       {
+         /* This is a different version of the IS_SOLARIS_PT_INTERP
+            macro to the one defined in rewrite_elf_program_header().  */
+#define IS_SOLARIS_PT_INTERP(p)                        \
+  (p->p_type == PT_INTERP                      \
+   && p->p_vaddr == 0                          \
+   && p->p_paddr == 0                          \
+   && p->p_memsz == 0                          \
+   && p->p_filesz > 0)
+         /* PR binutils/3535.  The Solaris interpreter program header
+            needs special treatment, so we always rewrite the headers
+            when one is detected.  */
+         if (IS_SOLARIS_PT_INTERP (segment))
+           goto rewrite;
+         
+         for (section = ibfd->sections;
+              section != NULL; section = section->next)
+           {
+             /* We mark the output section so that we know it comes
+                from the input BFD.  */
+             osec = section->output_section;
+             if (osec)
+               osec->segment_mark = TRUE;
+
+             /* Check if this section is covered by the segment.  */
+             this_hdr = &(elf_section_data(section)->this_hdr);
+             if (ELF_IS_SECTION_IN_SEGMENT_FILE (this_hdr, segment))
+               {
+                 /* FIXME: Check if its output section is changed or
+                    removed.  What else do we need to check?  */
+                 if (osec == NULL
+                     || section->flags != osec->flags
+                     || section->lma != osec->lma
+                     || section->vma != osec->vma
+                     || section->size != osec->size
+                     || section->rawsize != osec->rawsize
+                     || section->alignment_power != osec->alignment_power)
+                   goto rewrite;
+               }
+           }
+       }
+
+      /* Check to see if any output section do not come from the
+        input BFD.  */
+      for (section = obfd->sections; section != NULL;
+          section = section->next)
+       {
+         if (section->segment_mark == FALSE)
+           goto rewrite;
+         else
+           section->segment_mark = FALSE;
+       }
+
+      return copy_elf_program_header (ibfd, obfd);
+    }
+
+rewrite:
+  return rewrite_elf_program_header (ibfd, obfd);
+}
+
+/* Initialize private output section information from input section.  */
+
+bfd_boolean
+_bfd_elf_init_private_section_data (bfd *ibfd,
+                                   asection *isec,
+                                   bfd *obfd,
+                                   asection *osec,
+                                   struct bfd_link_info *link_info)
+
+{
+  Elf_Internal_Shdr *ihdr, *ohdr;
+  bfd_boolean need_group = link_info == NULL || link_info->relocatable;
+
+  if (ibfd->xvec->flavour != bfd_target_elf_flavour
+      || obfd->xvec->flavour != bfd_target_elf_flavour)
+    return TRUE;
+
+  /* Don't copy the output ELF section type from input if the
+     output BFD section flags have been set to something different.
+     elf_fake_sections will set ELF section type based on BFD
+     section flags.  */
+  if (osec->flags == isec->flags || !osec->flags)
+    {
+      BFD_ASSERT (osec->flags == isec->flags 
+                 || (!osec->flags
+                     && elf_section_type (osec) == SHT_NULL));
+      elf_section_type (osec) = elf_section_type (isec);
+    }
+
+  /* FIXME: Is this correct for all OS/PROC specific flags?  */
+  elf_section_flags (osec) |= (elf_section_flags (isec)
+                              & (SHF_MASKOS | SHF_MASKPROC));
 
-  /* If we had to estimate the number of program headers that were
-     going to be needed, then check our estimate now and adjust
-     the offset if necessary.  */
-  if (phdr_adjust_seg != NULL)
+  /* Set things up for objcopy and relocatable link.  The output
+     SHT_GROUP section will have its elf_next_in_group pointing back
+     to the input group members.  Ignore linker created group section.
+     See elfNN_ia64_object_p in elfxx-ia64.c.  */
+  if (need_group)
     {
-      unsigned int count;
+      if (elf_sec_group (isec) == NULL
+         || (elf_sec_group (isec)->flags & SEC_LINKER_CREATED) == 0)
+       {
+         if (elf_section_flags (isec) & SHF_GROUP)
+           elf_section_flags (osec) |= SHF_GROUP;
+         elf_next_in_group (osec) = elf_next_in_group (isec);
+         elf_group_name (osec) = elf_group_name (isec);
+       }
+    }
 
-      for (count = 0, map = map_first; map != NULL; map = map->next)
-       count++;
+  ihdr = &elf_section_data (isec)->this_hdr;
 
-      if (count > phdr_adjust_num)
-       phdr_adjust_seg->p_paddr
-         -= (count - phdr_adjust_num) * iehdr->e_phentsize;
+  /* We need to handle elf_linked_to_section for SHF_LINK_ORDER. We
+     don't use the output section of the linked-to section since it
+     may be NULL at this point.  */
+  if ((ihdr->sh_flags & SHF_LINK_ORDER) != 0)
+    {
+      ohdr = &elf_section_data (osec)->this_hdr;
+      ohdr->sh_flags |= SHF_LINK_ORDER;
+      elf_linked_to_section (osec) = elf_linked_to_section (isec);
     }
 
-#undef SEGMENT_END
-#undef SECTION_SIZE
-#undef IS_CONTAINED_BY_VMA
-#undef IS_CONTAINED_BY_LMA
-#undef IS_COREFILE_NOTE
-#undef IS_SOLARIS_PT_INTERP
-#undef INCLUDE_SECTION_IN_SEGMENT
-#undef SEGMENT_AFTER_SEGMENT
-#undef SEGMENT_OVERLAPS
+  osec->use_rela_p = isec->use_rela_p;
+
   return TRUE;
 }
 
@@ -5655,20 +6067,8 @@ _bfd_elf_copy_private_section_data (bfd *ibfd,
       || ihdr->sh_type == SHT_GNU_verdef)
     ohdr->sh_info = ihdr->sh_info;
 
-  /* Set things up for objcopy.  The output SHT_GROUP section will
-     have its elf_next_in_group pointing back to the input group
-     members.  Ignore linker created group section.  See
-     elfNN_ia64_object_p in elfxx-ia64.c.  */
-  if (elf_sec_group (isec) == NULL
-      || (elf_sec_group (isec)->flags & SEC_LINKER_CREATED) == 0)
-    {
-      elf_next_in_group (osec) = elf_next_in_group (isec);
-      elf_group_name (osec) = elf_group_name (isec);
-    }
-
-  osec->use_rela_p = isec->use_rela_p;
-
-  return TRUE;
+  return _bfd_elf_init_private_section_data (ibfd, isec, obfd, osec,
+                                            NULL);
 }
 
 /* Copy private header information.  */
@@ -5676,6 +6076,8 @@ _bfd_elf_copy_private_section_data (bfd *ibfd,
 bfd_boolean
 _bfd_elf_copy_private_header_data (bfd *ibfd, bfd *obfd)
 {
+  asection *isec;
+
   if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour
       || bfd_get_flavour (obfd) != bfd_target_elf_flavour)
     return TRUE;
@@ -5691,6 +6093,27 @@ _bfd_elf_copy_private_header_data (bfd *ibfd, bfd *obfd)
        return FALSE;
     }
 
+  /* _bfd_elf_copy_private_section_data copied over the SHF_GROUP flag
+     but this might be wrong if we deleted the group section.  */
+  for (isec = ibfd->sections; isec != NULL; isec = isec->next)
+    if (elf_section_type (isec) == SHT_GROUP
+       && isec->output_section == NULL)
+      {
+       asection *first = elf_next_in_group (isec);
+       asection *s = first;
+       while (s != NULL)
+         {
+           if (s->output_section != NULL)
+             {
+               elf_section_flags (s->output_section) &= ~SHF_GROUP;
+               elf_group_name (s->output_section) = NULL;
+             }
+           s = elf_next_in_group (s);
+           if (s == first)
+             break;
+         }
+      }
+
   return TRUE;
 }
 
@@ -5784,8 +6207,7 @@ swap_out_syms (bfd *abfd,
   symstrtab_hdr = &elf_tdata (abfd)->strtab_hdr;
   symstrtab_hdr->sh_type = SHT_STRTAB;
 
-  amt = (bfd_size_type) (1 + symcount) * bed->s->sizeof_sym;
-  outbound_syms = bfd_alloc (abfd, amt);
+  outbound_syms = bfd_alloc2 (abfd, 1 + symcount, bed->s->sizeof_sym);
   if (outbound_syms == NULL)
     {
       _bfd_stringtab_free (stt);
@@ -5798,7 +6220,8 @@ swap_out_syms (bfd *abfd,
   if (symtab_shndx_hdr->sh_name != 0)
     {
       amt = (bfd_size_type) (1 + symcount) * sizeof (Elf_External_Sym_Shndx);
-      outbound_shndx = bfd_zalloc (abfd, amt);
+      outbound_shndx = bfd_zalloc2 (abfd, 1 + symcount,
+                                   sizeof (Elf_External_Sym_Shndx));
       if (outbound_shndx == NULL)
        {
          _bfd_stringtab_free (stt);
@@ -5963,6 +6386,10 @@ Unable to find equivalent output section for symbol '%s' from section '%s'"),
        type = STT_FUNC;
       else if ((flags & BSF_OBJECT) != 0)
        type = STT_OBJECT;
+      else if ((flags & BSF_RELC) != 0)
+       type = STT_RELC;
+      else if ((flags & BSF_SRELC) != 0)
+       type = STT_SRELC;
       else
        type = STT_NOTYPE;
 
@@ -6211,7 +6638,6 @@ bfd_boolean
 _bfd_elf_slurp_version_tables (bfd *abfd, bfd_boolean default_imported_symver)
 {
   bfd_byte *contents = NULL;
-  bfd_size_type amt;
   unsigned int freeidx = 0;
 
   if (elf_dynverref (abfd) != 0)
@@ -6220,11 +6646,12 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bfd_boolean default_imported_symver)
       Elf_External_Verneed *everneed;
       Elf_Internal_Verneed *iverneed;
       unsigned int i;
+      bfd_byte *contents_end;
 
       hdr = &elf_tdata (abfd)->dynverref_hdr;
 
-      amt = (bfd_size_type) hdr->sh_info * sizeof (Elf_Internal_Verneed);
-      elf_tdata (abfd)->verref = bfd_zalloc (abfd, amt);
+      elf_tdata (abfd)->verref = bfd_zalloc2 (abfd, hdr->sh_info,
+                                             sizeof (Elf_Internal_Verneed));
       if (elf_tdata (abfd)->verref == NULL)
        goto error_return;
 
@@ -6232,11 +6659,22 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bfd_boolean default_imported_symver)
 
       contents = bfd_malloc (hdr->sh_size);
       if (contents == NULL)
-       goto error_return;
+       {
+error_return_verref:
+         elf_tdata (abfd)->verref = NULL;
+         elf_tdata (abfd)->cverrefs = 0;
+         goto error_return;
+       }
       if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) != 0
          || bfd_bread (contents, hdr->sh_size, abfd) != hdr->sh_size)
-       goto error_return;
+       goto error_return_verref;
 
+      if (hdr->sh_info && hdr->sh_size < sizeof (Elf_External_Verneed))
+       goto error_return_verref;
+
+      BFD_ASSERT (sizeof (Elf_External_Verneed)
+                 == sizeof (Elf_External_Vernaux));
+      contents_end = contents + hdr->sh_size - sizeof (Elf_External_Verneed);
       everneed = (Elf_External_Verneed *) contents;
       iverneed = elf_tdata (abfd)->verref;
       for (i = 0; i < hdr->sh_info; i++, iverneed++)
@@ -6253,11 +6691,21 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bfd_boolean default_imported_symver)
            bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
                                             iverneed->vn_file);
          if (iverneed->vn_filename == NULL)
-           goto error_return;
+           goto error_return_verref;
+
+         if (iverneed->vn_cnt == 0)
+           iverneed->vn_auxptr = NULL;
+         else
+           {
+             iverneed->vn_auxptr = bfd_alloc2 (abfd, iverneed->vn_cnt,
+                                               sizeof (Elf_Internal_Vernaux));
+             if (iverneed->vn_auxptr == NULL)
+               goto error_return_verref;
+           }
 
-         amt = iverneed->vn_cnt;
-         amt *= sizeof (Elf_Internal_Vernaux);
-         iverneed->vn_auxptr = bfd_alloc (abfd, amt);
+         if (iverneed->vn_aux
+             > (size_t) (contents_end - (bfd_byte *) everneed))
+           goto error_return_verref;
 
          evernaux = ((Elf_External_Vernaux *)
                      ((bfd_byte *) everneed + iverneed->vn_aux));
@@ -6270,13 +6718,17 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bfd_boolean default_imported_symver)
                bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
                                                 ivernaux->vna_name);
              if (ivernaux->vna_nodename == NULL)
-               goto error_return;
+               goto error_return_verref;
 
              if (j + 1 < iverneed->vn_cnt)
                ivernaux->vna_nextptr = ivernaux + 1;
              else
                ivernaux->vna_nextptr = NULL;
 
+             if (ivernaux->vna_next
+                 > (size_t) (contents_end - (bfd_byte *) evernaux))
+               goto error_return_verref;
+
              evernaux = ((Elf_External_Vernaux *)
                          ((bfd_byte *) evernaux + ivernaux->vna_next));
 
@@ -6289,6 +6741,10 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bfd_boolean default_imported_symver)
          else
            iverneed->vn_nextref = NULL;
 
+         if (iverneed->vn_next
+             > (size_t) (contents_end - (bfd_byte *) everneed))
+           goto error_return_verref;
+
          everneed = ((Elf_External_Verneed *)
                      ((bfd_byte *) everneed + iverneed->vn_next));
        }
@@ -6306,6 +6762,7 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bfd_boolean default_imported_symver)
       Elf_Internal_Verdef iverdefmem;
       unsigned int i;
       unsigned int maxidx;
+      bfd_byte *contents_end_def, *contents_end_aux;
 
       hdr = &elf_tdata (abfd)->dynverdef_hdr;
 
@@ -6316,6 +6773,16 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bfd_boolean default_imported_symver)
          || bfd_bread (contents, hdr->sh_size, abfd) != hdr->sh_size)
        goto error_return;
 
+      if (hdr->sh_info && hdr->sh_size < sizeof (Elf_External_Verdef))
+       goto error_return;
+
+      BFD_ASSERT (sizeof (Elf_External_Verdef)
+                 >= sizeof (Elf_External_Verdaux));
+      contents_end_def = contents + hdr->sh_size
+                        - sizeof (Elf_External_Verdef);
+      contents_end_aux = contents + hdr->sh_size
+                        - sizeof (Elf_External_Verdaux);
+
       /* We know the number of entries in the section but not the maximum
         index.  Therefore we have to run through all entries and find
         the maximum.  */
@@ -6328,6 +6795,10 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bfd_boolean default_imported_symver)
          if ((iverdefmem.vd_ndx & ((unsigned) VERSYM_VERSION)) > maxidx)
            maxidx = iverdefmem.vd_ndx & ((unsigned) VERSYM_VERSION);
 
+         if (iverdefmem.vd_next
+             > (size_t) (contents_end_def - (bfd_byte *) everdef))
+           goto error_return;
+
          everdef = ((Elf_External_Verdef *)
                     ((bfd_byte *) everdef + iverdefmem.vd_next));
        }
@@ -6339,8 +6810,8 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bfd_boolean default_imported_symver)
          else
            freeidx = ++maxidx;
        }
-      amt = (bfd_size_type) maxidx * sizeof (Elf_Internal_Verdef);
-      elf_tdata (abfd)->verdef = bfd_zalloc (abfd, amt);
+      elf_tdata (abfd)->verdef = bfd_zalloc2 (abfd, maxidx,
+                                             sizeof (Elf_Internal_Verdef));
       if (elf_tdata (abfd)->verdef == NULL)
        goto error_return;
 
@@ -6356,15 +6827,32 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bfd_boolean default_imported_symver)
 
          _bfd_elf_swap_verdef_in (abfd, everdef, &iverdefmem);
 
+         if ((iverdefmem.vd_ndx & VERSYM_VERSION) == 0)
+           {
+error_return_verdef:
+             elf_tdata (abfd)->verdef = NULL;
+             elf_tdata (abfd)->cverdefs = 0;
+             goto error_return;
+           }
+
          iverdef = &iverdefarr[(iverdefmem.vd_ndx & VERSYM_VERSION) - 1];
          memcpy (iverdef, &iverdefmem, sizeof (Elf_Internal_Verdef));
 
          iverdef->vd_bfd = abfd;
 
-         amt = (bfd_size_type) iverdef->vd_cnt * sizeof (Elf_Internal_Verdaux);
-         iverdef->vd_auxptr = bfd_alloc (abfd, amt);
-         if (iverdef->vd_auxptr == NULL)
-           goto error_return;
+         if (iverdef->vd_cnt == 0)
+           iverdef->vd_auxptr = NULL;
+         else
+           {
+             iverdef->vd_auxptr = bfd_alloc2 (abfd, iverdef->vd_cnt,
+                                              sizeof (Elf_Internal_Verdaux));
+             if (iverdef->vd_auxptr == NULL)
+               goto error_return_verdef;
+           }
+
+         if (iverdef->vd_aux
+             > (size_t) (contents_end_aux - (bfd_byte *) everdef))
+           goto error_return_verdef;
 
          everdaux = ((Elf_External_Verdaux *)
                      ((bfd_byte *) everdef + iverdef->vd_aux));
@@ -6377,20 +6865,25 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bfd_boolean default_imported_symver)
                bfd_elf_string_from_elf_section (abfd, hdr->sh_link,
                                                 iverdaux->vda_name);
              if (iverdaux->vda_nodename == NULL)
-               goto error_return;
+               goto error_return_verdef;
 
              if (j + 1 < iverdef->vd_cnt)
                iverdaux->vda_nextptr = iverdaux + 1;
              else
                iverdaux->vda_nextptr = NULL;
 
+             if (iverdaux->vda_next
+                 > (size_t) (contents_end_aux - (bfd_byte *) everdaux))
+               goto error_return_verdef;
+
              everdaux = ((Elf_External_Verdaux *)
                          ((bfd_byte *) everdaux + iverdaux->vda_next));
            }
 
-         iverdef->vd_nodename = iverdef->vd_auxptr->vda_nodename;
+         if (iverdef->vd_cnt)
+           iverdef->vd_nodename = iverdef->vd_auxptr->vda_nodename;
 
-         if (i + 1 < hdr->sh_info)
+         if ((size_t) (iverdef - iverdefarr) + 1 < maxidx)
            iverdef->vd_nextdef = iverdef + 1;
          else
            iverdef->vd_nextdef = NULL;
@@ -6409,8 +6902,8 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bfd_boolean default_imported_symver)
       else
        freeidx++;
 
-      amt = (bfd_size_type) freeidx * sizeof (Elf_Internal_Verdef);
-      elf_tdata (abfd)->verdef = bfd_zalloc (abfd, amt);
+      elf_tdata (abfd)->verdef = bfd_zalloc2 (abfd, freeidx,
+                                             sizeof (Elf_Internal_Verdef));
       if (elf_tdata (abfd)->verdef == NULL)
        goto error_return;
 
@@ -6434,10 +6927,11 @@ _bfd_elf_slurp_version_tables (bfd *abfd, bfd_boolean default_imported_symver)
 
       iverdef->vd_nodename = bfd_elf_get_dt_soname (abfd);
       if (iverdef->vd_nodename == NULL)
-       goto error_return;
+       goto error_return_verdef;
       iverdef->vd_nextdef = NULL;
-      amt = (bfd_size_type) sizeof (Elf_Internal_Verdaux);
-      iverdef->vd_auxptr = bfd_alloc (abfd, amt);
+      iverdef->vd_auxptr = bfd_alloc (abfd, sizeof (Elf_Internal_Verdaux));
+      if (iverdef->vd_auxptr == NULL)
+       goto error_return_verdef;
 
       iverdaux = iverdef->vd_auxptr;
       iverdaux->vda_nodename = iverdef->vd_nodename;
@@ -6574,8 +7068,6 @@ elf_find_function (bfd *abfd ATTRIBUTE_UNUSED,
          if (state == symbol_seen)
            state = file_after_symbol_seen;
          continue;
-       case STT_SECTION:
-         continue;
        case STT_NOTYPE:
        case STT_FUNC:
          if (bfd_get_section (&q->symbol) == section
@@ -6584,12 +7076,10 @@ elf_find_function (bfd *abfd ATTRIBUTE_UNUSED,
            {
              func = (asymbol *) q;
              low_func = q->symbol.value;
-             if (file == NULL)
-               filename = NULL;
-             else if (ELF_ST_BIND (q->internal_elf_sym.st_info) != STB_LOCAL
-                      && state == file_after_symbol_seen)
-               filename = NULL;
-             else
+             filename = NULL;
+             if (file != NULL
+                 && (ELF_ST_BIND (q->internal_elf_sym.st_info) == STB_LOCAL
+                     || state != file_after_symbol_seen))
                filename = bfd_asymbol_name (file);
            }
          break;
@@ -6698,13 +7188,31 @@ _bfd_elf_find_inliner_info (bfd *abfd,
 }
 
 int
-_bfd_elf_sizeof_headers (bfd *abfd, bfd_boolean reloc)
+_bfd_elf_sizeof_headers (bfd *abfd, struct bfd_link_info *info)
 {
-  int ret;
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+  int ret = bed->s->sizeof_ehdr;
+
+  if (!info->relocatable)
+    {
+      bfd_size_type phdr_size = elf_tdata (abfd)->program_header_size;
+
+      if (phdr_size == (bfd_size_type) -1)
+       {
+         struct elf_segment_map *m;
+
+         phdr_size = 0;
+         for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
+           phdr_size += bed->s->sizeof_phdr;
+
+         if (phdr_size == 0)
+           phdr_size = get_program_header_size (abfd, info);
+       }
+
+      elf_tdata (abfd)->program_header_size = phdr_size;
+      ret += phdr_size;
+    }
 
-  ret = get_elf_backend_data (abfd)->s->sizeof_ehdr;
-  if (! reloc)
-    ret += get_program_header_size (abfd);
   return ret;
 }
 
@@ -6840,7 +7348,7 @@ _bfd_elf_close_and_cleanup (bfd *abfd)
 {
   if (bfd_get_format (abfd) == bfd_object)
     {
-      if (elf_shstrtab (abfd) != NULL)
+      if (elf_tdata (abfd) != NULL && elf_shstrtab (abfd) != NULL)
        _bfd_elf_strtab_free (elf_shstrtab (abfd));
       _bfd_dwarf2_cleanup_debug_info (abfd);
     }
@@ -6894,13 +7402,12 @@ elfcore_maybe_make_sect (bfd *abfd, char *name, asection *sect)
   if (bfd_get_section_by_name (abfd, name) != NULL)
     return TRUE;
 
-  sect2 = bfd_make_section (abfd, name);
+  sect2 = bfd_make_section_with_flags (abfd, name, sect->flags);
   if (sect2 == NULL)
     return FALSE;
 
   sect2->size = sect->size;
   sect2->filepos = sect->filepos;
-  sect2->flags = sect->flags;
   sect2->alignment_power = sect->alignment_power;
   return TRUE;
 }
@@ -6932,12 +7439,12 @@ _bfd_elfcore_make_pseudosection (bfd *abfd,
     return FALSE;
   memcpy (threaded_name, buf, len);
 
-  sect = bfd_make_section_anyway (abfd, threaded_name);
+  sect = bfd_make_section_anyway_with_flags (abfd, threaded_name,
+                                            SEC_HAS_CONTENTS);
   if (sect == NULL)
     return FALSE;
   sect->size = size;
   sect->filepos = filepos;
-  sect->flags = SEC_HAS_CONTENTS;
   sect->alignment_power = 2;
 
   return elfcore_maybe_make_sect (abfd, name, sect);
@@ -7216,7 +7723,7 @@ elfcore_grok_lwpstatus (bfd *abfd, Elf_Internal_Note *note)
     return FALSE;
   memcpy (name, buf, len);
 
-  sect = bfd_make_section_anyway (abfd, name);
+  sect = bfd_make_section_anyway_with_flags (abfd, name, SEC_HAS_CONTENTS);
   if (sect == NULL)
     return FALSE;
 
@@ -7231,7 +7738,6 @@ elfcore_grok_lwpstatus (bfd *abfd, Elf_Internal_Note *note)
   sect->filepos = note->descpos + offsetof (lwpstatus_t, pr_reg);
 #endif
 
-  sect->flags = SEC_HAS_CONTENTS;
   sect->alignment_power = 2;
 
   if (!elfcore_maybe_make_sect (abfd, ".reg", sect))
@@ -7246,7 +7752,7 @@ elfcore_grok_lwpstatus (bfd *abfd, Elf_Internal_Note *note)
     return FALSE;
   memcpy (name, buf, len);
 
-  sect = bfd_make_section_anyway (abfd, name);
+  sect = bfd_make_section_anyway_with_flags (abfd, name, SEC_HAS_CONTENTS);
   if (sect == NULL)
     return FALSE;
 
@@ -7261,7 +7767,6 @@ elfcore_grok_lwpstatus (bfd *abfd, Elf_Internal_Note *note)
   sect->filepos = note->descpos + offsetof (lwpstatus_t, pr_fpreg);
 #endif
 
-  sect->flags = SEC_HAS_CONTENTS;
   sect->alignment_power = 2;
 
   return elfcore_maybe_make_sect (abfd, ".reg2", sect);
@@ -7302,7 +7807,7 @@ elfcore_grok_win32pstatus (bfd *abfd, Elf_Internal_Note *note)
 
       memcpy (name, buf, len);
 
-      sect = bfd_make_section_anyway (abfd, name);
+      sect = bfd_make_section_anyway_with_flags (abfd, name, SEC_HAS_CONTENTS);
       if (sect == NULL)
        return FALSE;
 
@@ -7310,7 +7815,6 @@ elfcore_grok_win32pstatus (bfd *abfd, Elf_Internal_Note *note)
       sect->filepos = (note->descpos
                       + offsetof (struct win32_pstatus,
                                   data.thread_info.thread_context));
-      sect->flags = SEC_HAS_CONTENTS;
       sect->alignment_power = 2;
 
       if (pstatus.data.thread_info.is_active_thread)
@@ -7330,14 +7834,13 @@ elfcore_grok_win32pstatus (bfd *abfd, Elf_Internal_Note *note)
 
       memcpy (name, buf, len);
 
-      sect = bfd_make_section_anyway (abfd, name);
+      sect = bfd_make_section_anyway_with_flags (abfd, name, SEC_HAS_CONTENTS);
 
       if (sect == NULL)
        return FALSE;
 
       sect->size = note->descsz;
       sect->filepos = note->descpos;
-      sect->flags = SEC_HAS_CONTENTS;
       sect->alignment_power = 2;
       break;
 
@@ -7407,13 +7910,13 @@ elfcore_grok_note (bfd *abfd, Elf_Internal_Note *note)
 
     case NT_AUXV:
       {
-       asection *sect = bfd_make_section_anyway (abfd, ".auxv");
+       asection *sect = bfd_make_section_anyway_with_flags (abfd, ".auxv",
+                                                            SEC_HAS_CONTENTS);
 
        if (sect == NULL)
          return FALSE;
        sect->size = note->descsz;
        sect->filepos = note->descpos;
-       sect->flags = SEC_HAS_CONTENTS;
        sect->alignment_power = 1 + bfd_get_arch_size (abfd) / 32;
 
        return TRUE;
@@ -7521,7 +8024,7 @@ elfcore_grok_netbsd_note (bfd *abfd, Elf_Internal_Note *note)
 }
 
 static bfd_boolean
-elfcore_grok_nto_status (bfd *abfd, Elf_Internal_Note *note, pid_t *tid)
+elfcore_grok_nto_status (bfd *abfd, Elf_Internal_Note *note, long *tid)
 {
   void *ddata = note->descdata;
   char buf[100];
@@ -7553,20 +8056,19 @@ elfcore_grok_nto_status (bfd *abfd, Elf_Internal_Note *note, pid_t *tid)
     elf_tdata (abfd)->core_lwpid = *tid;
 
   /* Make a ".qnx_core_status/%d" section.  */
-  sprintf (buf, ".qnx_core_status/%ld", (long) *tid);
+  sprintf (buf, ".qnx_core_status/%ld", *tid);
 
   name = bfd_alloc (abfd, strlen (buf) + 1);
   if (name == NULL)
     return FALSE;
   strcpy (name, buf);
 
-  sect = bfd_make_section_anyway (abfd, name);
+  sect = bfd_make_section_anyway_with_flags (abfd, name, SEC_HAS_CONTENTS);
   if (sect == NULL)
     return FALSE;
 
   sect->size            = note->descsz;
   sect->filepos         = note->descpos;
-  sect->flags           = SEC_HAS_CONTENTS;
   sect->alignment_power = 2;
 
   return (elfcore_maybe_make_sect (abfd, ".qnx_core_status", sect));
@@ -7575,7 +8077,7 @@ elfcore_grok_nto_status (bfd *abfd, Elf_Internal_Note *note, pid_t *tid)
 static bfd_boolean
 elfcore_grok_nto_regs (bfd *abfd,
                       Elf_Internal_Note *note,
-                      pid_t tid,
+                      long tid,
                       char *base)
 {
   char buf[100];
@@ -7583,20 +8085,19 @@ elfcore_grok_nto_regs (bfd *abfd,
   asection *sect;
 
   /* Make a "(base)/%d" section.  */
-  sprintf (buf, "%s/%ld", base, (long) tid);
+  sprintf (buf, "%s/%ld", base, tid);
 
   name = bfd_alloc (abfd, strlen (buf) + 1);
   if (name == NULL)
     return FALSE;
   strcpy (name, buf);
 
-  sect = bfd_make_section_anyway (abfd, name);
+  sect = bfd_make_section_anyway_with_flags (abfd, name, SEC_HAS_CONTENTS);
   if (sect == NULL)
     return FALSE;
 
   sect->size            = note->descsz;
   sect->filepos         = note->descpos;
-  sect->flags           = SEC_HAS_CONTENTS;
   sect->alignment_power = 2;
 
   /* This is the current thread.  */
@@ -7617,7 +8118,7 @@ elfcore_grok_nto_note (bfd *abfd, Elf_Internal_Note *note)
   /* Every GREG section has a STATUS section before it.  Store the
      tid from the previous call to pass down to the next gregs
      function.  */
-  static pid_t tid = 1;
+  static long tid = 1;
 
   switch (note->type)
     {
@@ -7637,45 +8138,42 @@ elfcore_grok_nto_note (bfd *abfd, Elf_Internal_Note *note)
 /* Function: elfcore_write_note
 
    Inputs:
-     buffer to hold note
+     buffer to hold note, and current size of buffer
      name of note
      type of note
      data for note
      size of data for note
 
+   Writes note to end of buffer.  ELF64 notes are written exactly as
+   for ELF32, despite the current (as of 2006) ELF gabi specifying
+   that they ought to have 8-byte namesz and descsz field, and have
+   8-byte alignment.  Other writers, eg. Linux kernel, do the same.
+
    Return:
-   End of buffer containing note.  */
+   Pointer to realloc'd buffer, *BUFSIZ updated.  */
 
 char *
-elfcore_write_note (bfd  *abfd,
+elfcore_write_note (bfd *abfd,
                    char *buf,
-                   int  *bufsiz,
+                   int *bufsiz,
                    const char *name,
-                   int  type,
+                   int type,
                    const void *input,
-                   int  size)
+                   int size)
 {
   Elf_External_Note *xnp;
   size_t namesz;
-  size_t pad;
   size_t newspace;
-  char *p, *dest;
+  char *dest;
 
   namesz = 0;
-  pad = 0;
   if (name != NULL)
-    {
-      const struct elf_backend_data *bed;
-
-      namesz = strlen (name) + 1;
-      bed = get_elf_backend_data (abfd);
-      pad = -namesz & ((1 << bed->s->log_file_align) - 1);
-    }
+    namesz = strlen (name) + 1;
 
-  newspace = 12 + namesz + pad + size;
+  newspace = 12 + ((namesz + 3) & -4) + ((size + 3) & -4);
 
-  p = realloc (buf, *bufsiz + newspace);
-  dest = p + *bufsiz;
+  buf = realloc (buf, *bufsiz + newspace);
+  dest = buf + *bufsiz;
   *bufsiz += newspace;
   xnp = (Elf_External_Note *) dest;
   H_PUT_32 (abfd, namesz, xnp->namesz);
@@ -7686,14 +8184,20 @@ elfcore_write_note (bfd  *abfd,
     {
       memcpy (dest, name, namesz);
       dest += namesz;
-      while (pad != 0)
+      while (namesz & 3)
        {
          *dest++ = '\0';
-         --pad;
+         ++namesz;
        }
     }
   memcpy (dest, input, size);
-  return p;
+  dest += size;
+  while (size & 3)
+    {
+      *dest++ = '\0';
+      ++size;
+    }
+  return buf;
 }
 
 #if defined (HAVE_PRPSINFO_T) || defined (HAVE_PSINFO_T)
@@ -7704,22 +8208,52 @@ elfcore_write_prpsinfo (bfd  *abfd,
                        const char *fname,
                        const char *psargs)
 {
-  int note_type;
-  char *note_name = "CORE";
+  const char *note_name = "CORE";
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+
+  if (bed->elf_backend_write_core_note != NULL)
+    {
+      char *ret;
+      ret = (*bed->elf_backend_write_core_note) (abfd, buf, bufsiz,
+                                                NT_PRPSINFO, fname, psargs);
+      if (ret != NULL)
+       return ret;
+    }
+
+#if defined (HAVE_PRPSINFO32_T) || defined (HAVE_PSINFO32_T)
+  if (bed->s->elfclass == ELFCLASS32)
+    {
+#if defined (HAVE_PSINFO32_T)
+      psinfo32_t data;
+      int note_type = NT_PSINFO;
+#else
+      prpsinfo32_t data;
+      int note_type = NT_PRPSINFO;
+#endif
 
+      memset (&data, 0, sizeof (data));
+      strncpy (data.pr_fname, fname, sizeof (data.pr_fname));
+      strncpy (data.pr_psargs, psargs, sizeof (data.pr_psargs));
+      return elfcore_write_note (abfd, buf, bufsiz,
+                                note_name, note_type, &data, sizeof (data));
+    }
+  else
+#endif
+    {
 #if defined (HAVE_PSINFO_T)
-  psinfo_t  data;
-  note_type = NT_PSINFO;
+      psinfo_t data;
+      int note_type = NT_PSINFO;
 #else
-  prpsinfo_t data;
-  note_type = NT_PRPSINFO;
+      prpsinfo_t data;
+      int note_type = NT_PRPSINFO;
 #endif
 
-  memset (&data, 0, sizeof (data));
-  strncpy (data.pr_fname, fname, sizeof (data.pr_fname));
-  strncpy (data.pr_psargs, psargs, sizeof (data.pr_psargs));
-  return elfcore_write_note (abfd, buf, bufsiz,
-                            note_name, note_type, &data, sizeof (data));
+      memset (&data, 0, sizeof (data));
+      strncpy (data.pr_fname, fname, sizeof (data.pr_fname));
+      strncpy (data.pr_psargs, psargs, sizeof (data.pr_psargs));
+      return elfcore_write_note (abfd, buf, bufsiz,
+                                note_name, note_type, &data, sizeof (data));
+    }
 }
 #endif /* PSINFO_T or PRPSINFO_T */
 
@@ -7732,15 +8266,43 @@ elfcore_write_prstatus (bfd *abfd,
                        int cursig,
                        const void *gregs)
 {
-  prstatus_t prstat;
-  char *note_name = "CORE";
+  const char *note_name = "CORE";
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
 
-  memset (&prstat, 0, sizeof (prstat));
-  prstat.pr_pid = pid;
-  prstat.pr_cursig = cursig;
-  memcpy (&prstat.pr_reg, gregs, sizeof (prstat.pr_reg));
-  return elfcore_write_note (abfd, buf, bufsiz,
-                            note_name, NT_PRSTATUS, &prstat, sizeof (prstat));
+  if (bed->elf_backend_write_core_note != NULL)
+    {
+      char *ret;
+      ret = (*bed->elf_backend_write_core_note) (abfd, buf, bufsiz,
+                                                NT_PRSTATUS,
+                                                pid, cursig, gregs);
+      if (ret != NULL)
+       return ret;
+    }
+
+#if defined (HAVE_PRSTATUS32_T)
+  if (bed->s->elfclass == ELFCLASS32)
+    {
+      prstatus32_t prstat;
+
+      memset (&prstat, 0, sizeof (prstat));
+      prstat.pr_pid = pid;
+      prstat.pr_cursig = cursig;
+      memcpy (&prstat.pr_reg, gregs, sizeof (prstat.pr_reg));
+      return elfcore_write_note (abfd, buf, bufsiz, note_name,
+                                NT_PRSTATUS, &prstat, sizeof (prstat));
+    }
+  else
+#endif
+    {
+      prstatus_t prstat;
+
+      memset (&prstat, 0, sizeof (prstat));
+      prstat.pr_pid = pid;
+      prstat.pr_cursig = cursig;
+      memcpy (&prstat.pr_reg, gregs, sizeof (prstat.pr_reg));
+      return elfcore_write_note (abfd, buf, bufsiz, note_name,
+                                NT_PRSTATUS, &prstat, sizeof (prstat));
+    }
 }
 #endif /* HAVE_PRSTATUS_T */
 
@@ -7754,7 +8316,7 @@ elfcore_write_lwpstatus (bfd *abfd,
                         const void *gregs)
 {
   lwpstatus_t lwpstat;
-  char *note_name = "CORE";
+  const char *note_name = "CORE";
 
   memset (&lwpstat, 0, sizeof (lwpstat));
   lwpstat.pr_lwpid  = pid >> 16;
@@ -7781,17 +8343,34 @@ elfcore_write_pstatus (bfd *abfd,
                       char *buf,
                       int *bufsiz,
                       long pid,
-                      int cursig,
-                      const void *gregs)
+                      int cursig ATTRIBUTE_UNUSED,
+                      const void *gregs ATTRIBUTE_UNUSED)
 {
-  pstatus_t pstat;
-  char *note_name = "CORE";
+  const char *note_name = "CORE";
+#if defined (HAVE_PSTATUS32_T)
+  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
 
-  memset (&pstat, 0, sizeof (pstat));
-  pstat.pr_pid = pid & 0xffff;
-  buf = elfcore_write_note (abfd, buf, bufsiz, note_name,
-                           NT_PSTATUS, &pstat, sizeof (pstat));
-  return buf;
+  if (bed->s->elfclass == ELFCLASS32)
+    {
+      pstatus32_t pstat;
+
+      memset (&pstat, 0, sizeof (pstat));
+      pstat.pr_pid = pid & 0xffff;
+      buf = elfcore_write_note (abfd, buf, bufsiz, note_name,
+                               NT_PSTATUS, &pstat, sizeof (pstat));
+      return buf;
+    }
+  else
+#endif
+    {
+      pstatus_t pstat;
+
+      memset (&pstat, 0, sizeof (pstat));
+      pstat.pr_pid = pid & 0xffff;
+      buf = elfcore_write_note (abfd, buf, bufsiz, note_name,
+                               NT_PSTATUS, &pstat, sizeof (pstat));
+      return buf;
+    }
 }
 #endif /* HAVE_PSTATUS_T */
 
@@ -7802,7 +8381,7 @@ elfcore_write_prfpreg (bfd *abfd,
                       const void *fpregs,
                       int size)
 {
-  char *note_name = "CORE";
+  const char *note_name = "CORE";
   return elfcore_write_note (abfd, buf, bufsiz,
                             note_name, NT_FPREGSET, fpregs, size);
 }
@@ -7858,12 +8437,12 @@ elfcore_read_notes (bfd *abfd, file_ptr offset, bfd_size_type size)
       in.descdata = in.namedata + BFD_ALIGN (in.namesz, 4);
       in.descpos = offset + (in.descdata - buf);
 
-      if (strncmp (in.namedata, "NetBSD-CORE", 11) == 0)
+      if (CONST_STRNEQ (in.namedata, "NetBSD-CORE"))
         {
           if (! elfcore_grok_netbsd_note (abfd, &in))
             goto error;
         }
-      else if (strncmp (in.namedata, "QNX", 3) == 0)
+      else if (CONST_STRNEQ (in.namedata, "QNX"))
        {
          if (! elfcore_grok_nto_note (abfd, &in))
            goto error;
@@ -8160,6 +8739,10 @@ _bfd_elf_get_synthetic_symtab (bfd *abfd,
        continue;
 
       *s = **p->sym_ptr_ptr;
+      /* Undefined syms won't have BSF_LOCAL or BSF_GLOBAL set.  Since
+        we are defining a symbol, ensure one of them is set.  */
+      if ((s->flags & BSF_LOCAL) == 0)
+       s->flags |= BSF_GLOBAL;
       s->section = plt;
       s->value = addr - plt->vma;
       s->name = names;
@@ -8174,39 +8757,41 @@ _bfd_elf_get_synthetic_symtab (bfd *abfd,
   return n;
 }
 
-/* Sort symbol by binding and section. We want to put definitions
-   sorted by section at the beginning.  */
-
-static int
-elf_sort_elf_symbol (const void *arg1, const void *arg2)
+struct elf_symbuf_symbol
 {
-  const Elf_Internal_Sym *s1;
-  const Elf_Internal_Sym *s2;
-  int shndx;
-
-  /* Make sure that undefined symbols are at the end.  */
-  s1 = (const Elf_Internal_Sym *) arg1;
-  if (s1->st_shndx == SHN_UNDEF)
-    return 1;
-  s2 = (const Elf_Internal_Sym *) arg2;
-  if (s2->st_shndx == SHN_UNDEF)
-    return -1;
-
-  /* Sorted by section index.  */
-  shndx = s1->st_shndx - s2->st_shndx;
-  if (shndx != 0)
-    return shndx;
+  unsigned long st_name;       /* Symbol name, index in string tbl */
+  unsigned char st_info;       /* Type and binding attributes */
+  unsigned char st_other;      /* Visibilty, and target specific */
+};
 
-  /* Sorted by binding.  */
-  return ELF_ST_BIND (s1->st_info)  - ELF_ST_BIND (s2->st_info);
-}
+struct elf_symbuf_head
+{
+  struct elf_symbuf_symbol *ssym;
+  bfd_size_type count;
+  unsigned int st_shndx;
+};
 
 struct elf_symbol
 {
-  Elf_Internal_Sym *sym;
+  union
+    {
+      Elf_Internal_Sym *isym;
+      struct elf_symbuf_symbol *ssym;
+    } u;
   const char *name;
 };
 
+/* Sort references to symbols by ascending section number.  */
+
+static int
+elf_sort_elf_symbol (const void *arg1, const void *arg2)
+{
+  const Elf_Internal_Sym *s1 = *(const Elf_Internal_Sym **) arg1;
+  const Elf_Internal_Sym *s2 = *(const Elf_Internal_Sym **) arg2;
+
+  return s1->st_shndx - s2->st_shndx;
+}
+
 static int
 elf_sym_name_compare (const void *arg1, const void *arg2)
 {
@@ -8215,20 +8800,79 @@ elf_sym_name_compare (const void *arg1, const void *arg2)
   return strcmp (s1->name, s2->name);
 }
 
+static struct elf_symbuf_head *
+elf_create_symbuf (bfd_size_type symcount, Elf_Internal_Sym *isymbuf)
+{
+  Elf_Internal_Sym **ind, **indbufend, **indbuf
+    = bfd_malloc2 (symcount, sizeof (*indbuf));
+  struct elf_symbuf_symbol *ssym;
+  struct elf_symbuf_head *ssymbuf, *ssymhead;
+  bfd_size_type i, shndx_count;
+
+  if (indbuf == NULL)
+    return NULL;
+
+  for (ind = indbuf, i = 0; i < symcount; i++)
+    if (isymbuf[i].st_shndx != SHN_UNDEF)
+      *ind++ = &isymbuf[i];
+  indbufend = ind;
+
+  qsort (indbuf, indbufend - indbuf, sizeof (Elf_Internal_Sym *),
+        elf_sort_elf_symbol);
+
+  shndx_count = 0;
+  if (indbufend > indbuf)
+    for (ind = indbuf, shndx_count++; ind < indbufend - 1; ind++)
+      if (ind[0]->st_shndx != ind[1]->st_shndx)
+       shndx_count++;
+
+  ssymbuf = bfd_malloc ((shndx_count + 1) * sizeof (*ssymbuf)
+                       + (indbufend - indbuf) * sizeof (*ssymbuf));
+  if (ssymbuf == NULL)
+    {
+      free (indbuf);
+      return NULL;
+    }
+
+  ssym = (struct elf_symbuf_symbol *) (ssymbuf + shndx_count);
+  ssymbuf->ssym = NULL;
+  ssymbuf->count = shndx_count;
+  ssymbuf->st_shndx = 0;
+  for (ssymhead = ssymbuf, ind = indbuf; ind < indbufend; ssym++, ind++)
+    {
+      if (ind == indbuf || ssymhead->st_shndx != (*ind)->st_shndx)
+       {
+         ssymhead++;
+         ssymhead->ssym = ssym;
+         ssymhead->count = 0;
+         ssymhead->st_shndx = (*ind)->st_shndx;
+       }
+      ssym->st_name = (*ind)->st_name;
+      ssym->st_info = (*ind)->st_info;
+      ssym->st_other = (*ind)->st_other;
+      ssymhead->count++;
+    }
+  BFD_ASSERT ((bfd_size_type) (ssymhead - ssymbuf) == shndx_count);
+
+  free (indbuf);
+  return ssymbuf;
+}
+
 /* Check if 2 sections define the same set of local and global
    symbols.  */
 
 bfd_boolean
-bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2)
+bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
+                                  struct bfd_link_info *info)
 {
   bfd *bfd1, *bfd2;
   const struct elf_backend_data *bed1, *bed2;
   Elf_Internal_Shdr *hdr1, *hdr2;
   bfd_size_type symcount1, symcount2;
   Elf_Internal_Sym *isymbuf1, *isymbuf2;
-  Elf_Internal_Sym *isymstart1 = NULL, *isymstart2 = NULL, *isym;
-  Elf_Internal_Sym *isymend;
-  struct elf_symbol *symp, *symtable1 = NULL, *symtable2 = NULL;
+  struct elf_symbuf_head *ssymbuf1, *ssymbuf2;
+  Elf_Internal_Sym *isym, *isymend;
+  struct elf_symbol *symtable1 = NULL, *symtable2 = NULL;
   bfd_size_type count1, count2, i;
   int shndx1, shndx2;
   bfd_boolean result;
@@ -8238,10 +8882,8 @@ bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2)
 
   /* If both are .gnu.linkonce sections, they have to have the same
      section name.  */
-  if (strncmp (sec1->name, ".gnu.linkonce",
-              sizeof ".gnu.linkonce" - 1) == 0
-      && strncmp (sec2->name, ".gnu.linkonce",
-                 sizeof ".gnu.linkonce" - 1) == 0)
+  if (CONST_STRNEQ (sec1->name, ".gnu.linkonce")
+      && CONST_STRNEQ (sec2->name, ".gnu.linkonce"))
     return strcmp (sec1->name + sizeof ".gnu.linkonce",
                   sec2->name + sizeof ".gnu.linkonce") == 0;
 
@@ -8277,84 +8919,155 @@ bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2)
   if (symcount1 == 0 || symcount2 == 0)
     return FALSE;
 
-  isymbuf1 = bfd_elf_get_elf_syms (bfd1, hdr1, symcount1, 0,
-                                  NULL, NULL, NULL);
-  isymbuf2 = bfd_elf_get_elf_syms (bfd2, hdr2, symcount2, 0,
-                                  NULL, NULL, NULL);
-
   result = FALSE;
-  if (isymbuf1 == NULL || isymbuf2 == NULL)
-    goto done;
+  isymbuf1 = NULL;
+  isymbuf2 = NULL;
+  ssymbuf1 = elf_tdata (bfd1)->symbuf;
+  ssymbuf2 = elf_tdata (bfd2)->symbuf;
 
-  /* Sort symbols by binding and section. Global definitions are at
-     the beginning.  */
-  qsort (isymbuf1, symcount1, sizeof (Elf_Internal_Sym),
-        elf_sort_elf_symbol);
-  qsort (isymbuf2, symcount2, sizeof (Elf_Internal_Sym),
-        elf_sort_elf_symbol);
+  if (ssymbuf1 == NULL)
+    {
+      isymbuf1 = bfd_elf_get_elf_syms (bfd1, hdr1, symcount1, 0,
+                                      NULL, NULL, NULL);
+      if (isymbuf1 == NULL)
+       goto done;
 
-  /* Count definitions in the section.  */
-  count1 = 0;
-  for (isym = isymbuf1, isymend = isym + symcount1;
-       isym < isymend; isym++)
+      if (!info->reduce_memory_overheads)
+       elf_tdata (bfd1)->symbuf = ssymbuf1
+         = elf_create_symbuf (symcount1, isymbuf1);
+    }
+
+  if (ssymbuf1 == NULL || ssymbuf2 == NULL)
     {
-      if (isym->st_shndx == (unsigned int) shndx1)
-       {
-         if (count1 == 0)
-           isymstart1 = isym;
-         count1++;
-       }
+      isymbuf2 = bfd_elf_get_elf_syms (bfd2, hdr2, symcount2, 0,
+                                      NULL, NULL, NULL);
+      if (isymbuf2 == NULL)
+       goto done;
 
-      if (count1 && isym->st_shndx != (unsigned int) shndx1)
-       break;
+      if (ssymbuf1 != NULL && !info->reduce_memory_overheads)
+       elf_tdata (bfd2)->symbuf = ssymbuf2
+         = elf_create_symbuf (symcount2, isymbuf2);
     }
 
-  count2 = 0;
-  for (isym = isymbuf2, isymend = isym + symcount2;
-       isym < isymend; isym++)
+  if (ssymbuf1 != NULL && ssymbuf2 != NULL)
     {
-      if (isym->st_shndx == (unsigned int) shndx2)
+      /* Optimized faster version.  */
+      bfd_size_type lo, hi, mid;
+      struct elf_symbol *symp;
+      struct elf_symbuf_symbol *ssym, *ssymend;
+
+      lo = 0;
+      hi = ssymbuf1->count;
+      ssymbuf1++;
+      count1 = 0;
+      while (lo < hi)
        {
-         if (count2 == 0)
-           isymstart2 = isym;
-         count2++;
+         mid = (lo + hi) / 2;
+         if ((unsigned int) shndx1 < ssymbuf1[mid].st_shndx)
+           hi = mid;
+         else if ((unsigned int) shndx1 > ssymbuf1[mid].st_shndx)
+           lo = mid + 1;
+         else
+           {
+             count1 = ssymbuf1[mid].count;
+             ssymbuf1 += mid;
+             break;
+           }
        }
 
-      if (count2 && isym->st_shndx != (unsigned int) shndx2)
-       break;
+      lo = 0;
+      hi = ssymbuf2->count;
+      ssymbuf2++;
+      count2 = 0;
+      while (lo < hi)
+       {
+         mid = (lo + hi) / 2;
+         if ((unsigned int) shndx2 < ssymbuf2[mid].st_shndx)
+           hi = mid;
+         else if ((unsigned int) shndx2 > ssymbuf2[mid].st_shndx)
+           lo = mid + 1;
+         else
+           {
+             count2 = ssymbuf2[mid].count;
+             ssymbuf2 += mid;
+             break;
+           }
+       }
+
+      if (count1 == 0 || count2 == 0 || count1 != count2)
+       goto done;
+
+      symtable1 = bfd_malloc (count1 * sizeof (struct elf_symbol));
+      symtable2 = bfd_malloc (count2 * sizeof (struct elf_symbol));
+      if (symtable1 == NULL || symtable2 == NULL)
+       goto done;
+
+      symp = symtable1;
+      for (ssym = ssymbuf1->ssym, ssymend = ssym + count1;
+          ssym < ssymend; ssym++, symp++)
+       {
+         symp->u.ssym = ssym;
+         symp->name = bfd_elf_string_from_elf_section (bfd1,
+                                                       hdr1->sh_link,
+                                                       ssym->st_name);
+       }
+
+      symp = symtable2;
+      for (ssym = ssymbuf2->ssym, ssymend = ssym + count2;
+          ssym < ssymend; ssym++, symp++)
+       {
+         symp->u.ssym = ssym;
+         symp->name = bfd_elf_string_from_elf_section (bfd2,
+                                                       hdr2->sh_link,
+                                                       ssym->st_name);
+       }
+
+      /* Sort symbol by name.  */
+      qsort (symtable1, count1, sizeof (struct elf_symbol),
+            elf_sym_name_compare);
+      qsort (symtable2, count1, sizeof (struct elf_symbol),
+            elf_sym_name_compare);
+
+      for (i = 0; i < count1; i++)
+       /* Two symbols must have the same binding, type and name.  */
+       if (symtable1 [i].u.ssym->st_info != symtable2 [i].u.ssym->st_info
+           || symtable1 [i].u.ssym->st_other != symtable2 [i].u.ssym->st_other
+           || strcmp (symtable1 [i].name, symtable2 [i].name) != 0)
+         goto done;
+
+      result = TRUE;
+      goto done;
     }
 
-  if (count1 == 0 || count2 == 0 || count1 != count2)
+  symtable1 = bfd_malloc (symcount1 * sizeof (struct elf_symbol));
+  symtable2 = bfd_malloc (symcount2 * sizeof (struct elf_symbol));
+  if (symtable1 == NULL || symtable2 == NULL)
     goto done;
 
-  symtable1 = bfd_malloc (count1 * sizeof (struct elf_symbol));
-  symtable2 = bfd_malloc (count1 * sizeof (struct elf_symbol));
+  /* Count definitions in the section.  */
+  count1 = 0;
+  for (isym = isymbuf1, isymend = isym + symcount1; isym < isymend; isym++)
+    if (isym->st_shndx == (unsigned int) shndx1)
+      symtable1[count1++].u.isym = isym;
+
+  count2 = 0;
+  for (isym = isymbuf2, isymend = isym + symcount2; isym < isymend; isym++)
+    if (isym->st_shndx == (unsigned int) shndx2)
+      symtable2[count2++].u.isym = isym;
 
-  if (symtable1 == NULL || symtable2 == NULL)
+  if (count1 == 0 || count2 == 0 || count1 != count2)
     goto done;
 
-  symp = symtable1;
-  for (isym = isymstart1, isymend = isym + count1;
-       isym < isymend; isym++)
-    {
-      symp->sym = isym;
-      symp->name = bfd_elf_string_from_elf_section (bfd1,
-                                                   hdr1->sh_link,
-                                                   isym->st_name);
-      symp++;
-    }
-  symp = symtable2;
-  for (isym = isymstart2, isymend = isym + count1;
-       isym < isymend; isym++)
-    {
-      symp->sym = isym;
-      symp->name = bfd_elf_string_from_elf_section (bfd2,
-                                                   hdr2->sh_link,
-                                                   isym->st_name);
-      symp++;
-    }
-  
+  for (i = 0; i < count1; i++)
+    symtable1[i].name
+      = bfd_elf_string_from_elf_section (bfd1, hdr1->sh_link,
+                                        symtable1[i].u.isym->st_name);
+
+  for (i = 0; i < count2; i++)
+    symtable2[i].name
+      = bfd_elf_string_from_elf_section (bfd2, hdr2->sh_link,
+                                        symtable2[i].u.isym->st_name);
+
   /* Sort symbol by name.  */
   qsort (symtable1, count1, sizeof (struct elf_symbol),
         elf_sym_name_compare);
@@ -8363,8 +9076,8 @@ bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2)
 
   for (i = 0; i < count1; i++)
     /* Two symbols must have the same binding, type and name.  */
-    if (symtable1 [i].sym->st_info != symtable2 [i].sym->st_info
-       || symtable1 [i].sym->st_other != symtable2 [i].sym->st_other
+    if (symtable1 [i].u.isym->st_info != symtable2 [i].u.isym->st_info
+       || symtable1 [i].u.isym->st_other != symtable2 [i].u.isym->st_other
        || strcmp (symtable1 [i].name, symtable2 [i].name) != 0)
       goto done;
 
@@ -8382,3 +9095,34 @@ done:
 
   return result;
 }
+
+/* It is only used by x86-64 so far.  */
+asection _bfd_elf_large_com_section
+  = BFD_FAKE_SECTION (_bfd_elf_large_com_section,
+                     SEC_IS_COMMON, NULL, "LARGE_COMMON", 0);
+
+/* Return TRUE if 2 section types are compatible.  */
+
+bfd_boolean
+_bfd_elf_match_sections_by_type (bfd *abfd, const asection *asec,
+                                bfd *bbfd, const asection *bsec)
+{
+  if (asec == NULL
+      || bsec == NULL
+      || abfd->xvec->flavour != bfd_target_elf_flavour
+      || bbfd->xvec->flavour != bfd_target_elf_flavour)
+    return TRUE;
+
+  return elf_section_type (asec) == elf_section_type (bsec);
+}
+
+void
+_bfd_elf_set_osabi (bfd * abfd,
+                   struct bfd_link_info * link_info ATTRIBUTE_UNUSED)
+{
+  Elf_Internal_Ehdr * i_ehdrp; /* ELF file header, internal form.  */
+
+  i_ehdrp = elf_elfheader (abfd);
+
+  i_ehdrp->e_ident[EI_OSABI] = get_elf_backend_data (abfd)->elf_osabi;
+}
This page took 0.125438 seconds and 4 git commands to generate.