2006-09-16 Paul Brook <paul@codesourcery.com>
[deliverable/binutils-gdb.git] / bfd / elf.c
index 7f402c3cb32cec9a058192c1ee2451ceab7d417c..ce8f4bccb5fee237de22cf5e913e9559e5311895 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 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,38 +206,32 @@ bfd_elf_hash (const char *namearg)
   return h & 0xffffffff;
 }
 
-/* Read a specified number of bytes at a specified offset in an ELF
-   file, into a newly allocated buffer, and return a pointer to the
-   buffer.  */
+/* DT_GNU_HASH hash function.  Do not change this function; you will
+   cause invalid hash tables to be generated.  */
 
-static bfd_byte *
-elf_read (bfd *abfd, file_ptr offset, bfd_size_type size)
+unsigned long
+bfd_elf_gnu_hash (const char *namearg)
 {
-  bfd_byte *buf;
+  const unsigned char *name = (const unsigned char *) namearg;
+  unsigned long h = 5381;
+  unsigned char ch;
 
-  if ((buf = bfd_alloc (abfd, size)) == NULL)
-    return NULL;
-  if (bfd_seek (abfd, offset, SEEK_SET) != 0)
-    return NULL;
-  if (bfd_bread (buf, size, abfd) != size)
-    {
-      if (bfd_get_error () != bfd_error_system_call)
-       bfd_set_error (bfd_error_file_truncated);
-      return NULL;
-    }
-  return buf;
+  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;
 }
@@ -267,7 +261,21 @@ bfd_elf_get_str_section (bfd *abfd, unsigned int shindex)
       /* No cached one, attempt to read, and cache what we read.  */
       offset = i_shdrp[shindex]->sh_offset;
       shstrtabsize = i_shdrp[shindex]->sh_size;
-      shstrtab = elf_read (abfd, offset, shstrtabsize);
+
+      /* Allocate and clear an extra byte at the end, to prevent crashes
+        in case the string table is not terminated.  */
+      if (shstrtabsize + 1 == 0
+         || (shstrtab = bfd_alloc (abfd, shstrtabsize + 1)) == NULL
+         || bfd_seek (abfd, offset, SEEK_SET) != 0)
+       shstrtab = NULL;
+      else if (bfd_bread (shstrtab, shstrtabsize, abfd) != shstrtabsize)
+       {
+         if (bfd_get_error () != bfd_error_system_call)
+           bfd_set_error (bfd_error_file_truncated);
+         shstrtab = NULL;
+       }
+      else
+       shstrtab[shstrtabsize] = '\0';
       i_shdrp[shindex]->contents = shstrtab;
     }
   return (char *) shstrtab;
@@ -348,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
@@ -367,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
@@ -381,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;
     }
@@ -452,8 +460,11 @@ group_signature (bfd *abfd, Elf_Internal_Shdr *ghdr)
   Elf_External_Sym_Shndx eshndx;
   Elf_Internal_Sym isym;
 
-  /* First we need to ensure the symbol table is available.  */
-  if (! bfd_section_from_shdr (abfd, ghdr->sh_link))
+  /* First we need to ensure the symbol table is available.  Make sure
+     that it is a symbol table section.  */
+  hdr = elf_elfsections (abfd) [ghdr->sh_link];
+  if (hdr->sh_type != SHT_SYMTAB
+      || ! bfd_section_from_shdr (abfd, ghdr->sh_link))
     return NULL;
 
   /* Go read the symbol.  */
@@ -499,10 +510,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;
 
@@ -522,7 +533,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)
@@ -630,12 +642,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;
 
@@ -750,25 +806,45 @@ _bfd_elf_make_section_from_shdr (bfd *abfd,
   if ((hdr->sh_flags & SHF_TLS) != 0)
     flags |= SEC_THREAD_LOCAL;
 
-  /* The debugging sections appear to be recognized only by name, not
-     any sort of flag.  */
-  {
-    static const char *debug_sec_names [] =
+  if ((flags & SEC_ALLOC) == 0)
     {
-      ".debug",
-      ".gnu.linkonce.wi.",
-      ".line",
-      ".stab"
-    };
-    int i;
-
-    for (i = ARRAY_SIZE (debug_sec_names); i--;)
-      if (strncmp (name, debug_sec_names[i], strlen (debug_sec_names[i])) == 0)
-       break;
-
-    if (i >= 0)
-      flags |= SEC_DEBUGGING;
-  }
+      /* The debugging sections appear to be recognized only by name,
+        not any sort of flag.  Their SEC_ALLOC bits are cleared.  */
+      static const struct
+       {
+         const char *name;
+         int len;
+       } debug_sections [] =
+       {
+         { "debug",             5  },  /* 'd' */
+         { NULL,                0  },  /* 'e' */
+         { NULL,                0  },  /* 'f' */
+         { "gnu.linkonce.wi.", 16 },   /* 'g' */
+         { NULL,                0  },  /* 'h' */
+         { NULL,                0  },  /* 'i' */
+         { NULL,                0  },  /* 'j' */
+         { NULL,                0  },  /* 'k' */
+         { "line",              4  },  /* 'l' */
+         { NULL,                0  },  /* 'm' */
+         { NULL,                0  },  /* 'n' */
+         { NULL,                0  },  /* 'o' */
+         { NULL,                0  },  /* 'p' */
+         { NULL,                0  },  /* 'q' */
+         { NULL,                0  },  /* 'r' */
+         { "stab",              4  }   /* 's' */
+       };
+      
+      if (name [0] == '.')
+       {
+         int i = name [1] - 'd';
+         if (i >= 0
+             && i < (int) ARRAY_SIZE (debug_sections)
+             && debug_sections [i].name != NULL
+             && strncmp (&name [1], debug_sections [i].name,
+                         debug_sections [i].len) == 0)
+           flags |= SEC_DEBUGGING;
+       }
+    }
 
   /* As a GNU extension, if the name begins with .gnu.linkonce, we
      only link a single copy of the section.  This is used to support
@@ -1008,6 +1084,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
@@ -1027,23 +1125,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);
@@ -1167,6 +1255,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);
@@ -1204,8 +1293,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;
 
@@ -1213,7 +1303,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");
            }
        }
@@ -1228,10 +1319,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>");
        }
     }
 
@@ -1391,7 +1484,8 @@ _bfd_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
       /* Set local fields.  */
       ret->indx = -1;
       ret->dynindx = -1;
-      ret->got = ret->plt = htab->init_refcount;
+      ret->got = htab->init_got_refcount;
+      ret->plt = htab->init_plt_refcount;
       memset (&ret->size, 0, (sizeof (struct elf_link_hash_entry)
                              - offsetof (struct elf_link_hash_entry, size)));
       /* Assume that we have been called by a non-ELF symbol reader.
@@ -1408,12 +1502,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.  */
@@ -1430,33 +1523,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
@@ -1464,7 +1556,7 @@ _bfd_elf_link_hash_hide_symbol (struct bfd_link_info *info,
                                struct elf_link_hash_entry *h,
                                bfd_boolean force_local)
 {
-  h->plt = elf_hash_table (info)->init_offset;
+  h->plt = elf_hash_table (info)->init_plt_offset;
   h->needs_plt = 0;
   if (force_local)
     {
@@ -1486,23 +1578,25 @@ _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;
-  /* Make sure can_refcount is extended to the width and signedness of
-     init_refcount before we subtract one from it.  */
-  table->init_refcount.refcount = get_elf_backend_data (abfd)->can_refcount;
-  table->init_refcount.refcount -= 1;
-  table->init_offset.offset = -(bfd_vma) 1;
+  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->hplt = NULL;
   table->merge_info = NULL;
   memset (&table->stab_info, 0, sizeof (table->stab_info));
   memset (&table->eh_info, 0, sizeof (table->eh_info));
@@ -1513,7 +1607,7 @@ _bfd_elf_link_hash_table_init
   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;
@@ -1531,7 +1625,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;
@@ -1728,6 +1823,8 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
   name = bfd_elf_string_from_elf_section (abfd,
                                          elf_elfheader (abfd)->e_shstrndx,
                                          hdr->sh_name);
+  if (name == NULL)
+    return FALSE;
 
   switch (hdr->sh_type)
     {
@@ -1743,11 +1840,15 @@ 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.  */
       if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex))
        return FALSE;
+      if (hdr->sh_link > elf_numsections (abfd)
+         || elf_elfsections (abfd)[hdr->sh_link] == NULL)
+       return FALSE;
       if (elf_elfsections (abfd)[hdr->sh_link]->sh_type != SHT_STRTAB)
        {
          Elf_Internal_Shdr *dynsymhdr;
@@ -1782,7 +1883,8 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
       if (elf_onesymtab (abfd) == shindex)
        return TRUE;
 
-      BFD_ASSERT (hdr->sh_entsize == bed->s->sizeof_sym);
+      if (hdr->sh_entsize != bed->s->sizeof_sym)
+       return FALSE;
       BFD_ASSERT (elf_onesymtab (abfd) == 0);
       elf_onesymtab (abfd) = shindex;
       elf_tdata (abfd)->symtab_hdr = *hdr;
@@ -1833,7 +1935,8 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
       if (elf_dynsymtab (abfd) == shindex)
        return TRUE;
 
-      BFD_ASSERT (hdr->sh_entsize == bed->s->sizeof_sym);
+      if (hdr->sh_entsize != bed->s->sizeof_sym)
+       return FALSE;
       BFD_ASSERT (elf_dynsymtab (abfd) == 0);
       elf_dynsymtab (abfd) = shindex;
       elf_tdata (abfd)->dynsymtab_hdr = *hdr;
@@ -1895,6 +1998,9 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
              Elf_Internal_Shdr *hdr2 = elf_elfsections (abfd)[i];
              if (hdr2->sh_link == shindex)
                {
+                 /* Prevent endless recursion on broken objects.  */
+                 if (i == shindex)
+                   return FALSE;
                  if (! bfd_section_from_shdr (abfd, i))
                    return FALSE;
                  if (elf_onesymtab (abfd) == i)
@@ -1914,6 +2020,11 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
        Elf_Internal_Shdr *hdr2;
        unsigned int num_sec = elf_numsections (abfd);
 
+       if (hdr->sh_entsize
+           != (bfd_size_type) (hdr->sh_type == SHT_REL
+                               ? bed->s->sizeof_rel : bed->s->sizeof_rela))
+         return FALSE;
+
        /* Check for a bogus link to avoid crashing.  */
        if ((hdr->sh_link >= SHN_LORESERVE && hdr->sh_link <= SHN_HIRESERVE)
            || hdr->sh_link >= num_sec)
@@ -1967,8 +2078,13 @@ 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);
 
@@ -2011,16 +2127,16 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
       break;
 
     case SHT_GNU_versym:
+      if (hdr->sh_entsize != sizeof (Elf_External_Versym))
+       return FALSE;
       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;
@@ -2029,6 +2145,8 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
       /* We need a BFD section for objcopy and relocatable linking,
         and it's handy to have the signature available as the section
         name.  */
+      if (hdr->sh_entsize != GRP_ENTRY_SIZE)
+       return FALSE;
       name = group_signature (abfd, hdr);
       if (name == NULL)
        return FALSE;
@@ -2058,8 +2176,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;
@@ -2117,25 +2279,23 @@ bfd_section_from_elf_index (bfd *abfd, unsigned int index)
   return elf_elfsections (abfd)[index]->bfd_section;
 }
 
-static struct bfd_elf_special_section const special_sections[] =
+static const struct bfd_elf_special_section special_sections_b[] =
 {
   { ".bss",            4, -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE },
-  { ".gnu.linkonce.b",15, -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 }
+};
+
+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 },
-  { ".fini",           5,  0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { ".init",           5,  0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { ".line",           5,  0, SHT_PROGBITS, 0 },
-  { ".rodata",         7, -2, SHT_PROGBITS, SHF_ALLOC },
-  { ".rodata1",        8,  0, SHT_PROGBITS, SHF_ALLOC },
-  { ".tbss",           5, -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE + SHF_TLS },
-  { ".tdata",          6, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_TLS },
-  { ".text",           5, -2, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { ".init_array",    11,  0, SHT_INIT_ARRAY, SHF_ALLOC + SHF_WRITE },
-  { ".fini_array",    11,  0, SHT_FINI_ARRAY, SHF_ALLOC + SHF_WRITE },
-  { ".preinit_array", 14,  0, SHT_PREINIT_ARRAY, SHF_ALLOC + SHF_WRITE },
   { ".debug_line",    11,  0, SHT_PROGBITS, 0 },
   { ".debug_info",    11,  0, SHT_PROGBITS, 0 },
   { ".debug_abbrev",  13,  0, SHT_PROGBITS, 0 },
@@ -2143,45 +2303,133 @@ static struct bfd_elf_special_section const special_sections[] =
   { ".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 }
+};
+
+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 }
+};
+
+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 },
-  { ".hash",           5,  0, SHT_HASH,     SHF_ALLOC },
-  { ".interp",         7,  0, SHT_PROGBITS, 0 },
-  { ".plt",            4,  0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { ".shstrtab",       9,  0, SHT_STRTAB,   0 },
-  { ".strtab",         7,  0, SHT_STRTAB,   0 },
-  { ".symtab",         7,  0, SHT_SYMTAB,   0 },
   { ".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 },
+  { ".gnu.hash",       9,  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 }
+};
+
+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 }
+};
+
+static const struct bfd_elf_special_section special_sections_l[] =
+{
+  { ".line",           5,  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 }
+};
+
+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 }
+};
+
+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 }
+};
+
+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 },
-  { ".gnu.liblist",   12,  0, SHT_GNU_LIBLIST, SHF_ALLOC },
-  { ".gnu.conflict",  13,  0, SHT_RELA,     SHF_ALLOC },
   { NULL,              0,  0, 0,            0 }
 };
 
-static const struct bfd_elf_special_section *
-get_special_section (const char *name,
-                    const struct bfd_elf_special_section *special_sections,
-                    unsigned int rela)
+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 }
+};
+
+static const struct bfd_elf_special_section *special_sections[] =
+{
+  special_sections_b,          /* 'b' */
+  special_sections_c,          /* 'b' */
+  special_sections_d,          /* 'd' */
+  NULL,                                /* 'e' */
+  special_sections_f,          /* 'f' */
+  special_sections_g,          /* 'g' */
+  special_sections_h,          /* 'h' */
+  special_sections_i,          /* 'i' */
+  NULL,                                /* 'j' */
+  NULL,                                /* 'k' */
+  special_sections_l,          /* 'l' */
+  NULL,                                /* 'm' */
+  special_sections_n,          /* 'n' */
+  NULL,                                /* 'o' */
+  special_sections_p,          /* 'p' */
+  NULL,                                /* 'q' */
+  special_sections_r,          /* 'r' */
+  special_sections_s,          /* 's' */
+  special_sections_t,          /* 't' */
+};
+
+const struct bfd_elf_special_section *
+_bfd_elf_get_special_section (const char *name,
+                             const struct bfd_elf_special_section *spec,
+                             unsigned int rela)
 {
   int i;
-  int len = strlen (name);
+  int len;
+
+  len = strlen (name);
 
-  for (i = 0; special_sections[i].prefix != NULL; i++)
+  for (i = 0; spec[i].prefix != NULL; i++)
     {
       int suffix_len;
-      int prefix_len = special_sections[i].prefix_length;
+      int prefix_len = spec[i].prefix_length;
 
       if (len < prefix_len)
        continue;
-      if (memcmp (name, special_sections[i].prefix, prefix_len) != 0)
+      if (memcmp (name, spec[i].prefix, prefix_len) != 0)
        continue;
 
-      suffix_len = special_sections[i].suffix_length;
+      suffix_len = spec[i].suffix_length;
       if (suffix_len <= 0)
        {
          if (name[prefix_len] != 0)
@@ -2190,7 +2438,7 @@ get_special_section (const char *name,
                continue;
              if (name[prefix_len] != '.'
                  && (suffix_len == -2
-                     || (rela && special_sections[i].type == SHT_REL)))
+                     || (rela && spec[i].type == SHT_REL)))
                continue;
            }
        }
@@ -2199,41 +2447,58 @@ get_special_section (const char *name,
          if (len < prefix_len + suffix_len)
            continue;
          if (memcmp (name + len - suffix_len,
-                     special_sections[i].prefix + prefix_len,
+                     spec[i].prefix + prefix_len,
                      suffix_len) != 0)
            continue;
        }
-      return &special_sections[i];
+      return &spec[i];
     }
 
   return NULL;
 }
 
 const struct bfd_elf_special_section *
-_bfd_elf_get_sec_type_attr (bfd *abfd, const char *name)
+_bfd_elf_get_sec_type_attr (bfd *abfd, asection *sec)
 {
-  const struct elf_backend_data *bed = get_elf_backend_data (abfd);
-  const struct bfd_elf_special_section *ssect = NULL;
+  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 (name)
+  if (sec->name == NULL)
+    return NULL;
+
+  bed = get_elf_backend_data (abfd);
+  spec = bed->special_sections;
+  if (spec)
     {
-      unsigned int rela = bed->default_use_rela_p;
+      spec = _bfd_elf_get_special_section (sec->name,
+                                          bed->special_sections,
+                                          sec->use_rela_p);
+      if (spec != NULL)
+       return spec;
+    }
 
-      if (bed->special_sections)
-       ssect = get_special_section (name, bed->special_sections, rela);
+  if (sec->name[0] != '.')
+    return NULL;
 
-      if (! ssect)
-       ssect = get_special_section (name, special_sections, rela);
-    }
+  i = sec->name[1] - 'b';
+  if (i < 0 || i > 't' - 'b')
+    return NULL;
 
-  return ssect;
+  spec = special_sections[i];
+
+  if (spec == NULL)
+    return NULL;
+
+  return _bfd_elf_get_special_section (sec->name, spec, sec->use_rela_p);
 }
 
 bfd_boolean
 _bfd_elf_new_section_hook (bfd *abfd, asection *sec)
 {
   struct bfd_elf_section_data *sdata;
+  const struct elf_backend_data *bed;
   const struct bfd_elf_special_section *ssect;
 
   sdata = (struct bfd_elf_section_data *) sec->used_by_bfd;
@@ -2245,13 +2510,20 @@ _bfd_elf_new_section_hook (bfd *abfd, asection *sec)
       sec->used_by_bfd = sdata;
     }
 
-  /* 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
+  /* Indicate whether or not this section should use RELA relocations.  */
+  bed = get_elf_backend_data (abfd);
+  sec->use_rela_p = bed->default_use_rela_p;
+
+  /* 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 = _bfd_elf_get_sec_type_attr (abfd, sec->name);
+      ssect = (*bed->get_sec_type_attr) (abfd, sec);
       if (ssect != NULL)
        {
          elf_section_type (sec) = ssect->type;
@@ -2259,10 +2531,7 @@ _bfd_elf_new_section_hook (bfd *abfd, asection *sec)
        }
     }
 
-  /* Indicate whether or not this section should use RELA relocations.  */
-  sec->use_rela_p = get_elf_backend_data (abfd)->default_use_rela_p;
-
-  return TRUE;
+  return _bfd_generic_new_section_hook (abfd, sec);
 }
 
 /* Create a new bfd section from an ELF program header.
@@ -2471,7 +2740,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)
@@ -2494,32 +2763,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;
@@ -2592,6 +2839,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)
@@ -2612,16 +2863,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;
+           }
        }
     }
 
@@ -2651,7 +2904,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
@@ -2720,22 +2972,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 ();
 
@@ -2753,7 +2989,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;
@@ -2857,13 +3092,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);
@@ -2918,67 +3151,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);
+                     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);
            }
        }
 
@@ -3062,6 +3288,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.  */
@@ -3087,7 +3314,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.  */
@@ -3100,6 +3327,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)
 {
@@ -3114,7 +3355,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");
@@ -3128,8 +3368,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;
@@ -3142,51 +3381,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.  */
@@ -3202,8 +3419,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;
@@ -3213,6 +3429,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
@@ -3376,14 +3594,98 @@ _bfd_elf_compute_section_file_positions (bfd *abfd,
   return TRUE;
 }
 
-/* Create a mapping from a set of sections to a program segment.  */
+/* Make an initial estimate of the size of the program header.  If we
+   get the number wrong here, we'll redo section placement.  */
 
-static struct elf_segment_map *
-make_mapping (bfd *abfd,
-             asection **sections,
-             unsigned int from,
-             unsigned int to,
-             bfd_boolean phdr)
+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
+         && strncmp (s->name, ".note", 5) == 0)
+       {
+         /* 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 *
+make_mapping (bfd *abfd,
+             asection **sections,
+             unsigned int from,
+             unsigned int to,
+             bfd_boolean phdr)
 {
   struct elf_segment_map *m;
   unsigned int i;
@@ -3430,360 +3732,413 @@ _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)
-       {
-         /* 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)
+      i = 0;
+      for (s = abfd->sections; s != NULL; s = s->next)
        {
-         /* 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;
+         if ((s->flags & SEC_ALLOC) != 0)
+           {
+             sections[i] = s;
+             ++i;
+           }
        }
-      else if (! writable
-              && (hdr->flags & SEC_READONLY) == 0
-              && (((last_hdr->lma + last_size - 1)
-                   & ~(maxpagesize - 1))
-                  != (hdr->lma & ~(maxpagesize - 1))))
+      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)
        {
-         /* 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;
+           }
+
+         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
+             && strncmp (s->name, ".note", 5) == 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->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:
@@ -3868,126 +4223,74 @@ elf_sort_sections (const void *arg1, const void *arg2)
    off_offset = off % bed->maxpagesize;
    if (vma_offset < off_offset)
      adjustment = vma_offset + bed->maxpagesize - off_offset;
-   else
-     adjustment = vma_offset - off_offset;
-     
-   which can can be collapsed into the expression below.  */
-
-static file_ptr
-vma_page_aligned_bias (bfd_vma vma, ufile_ptr off, bfd_vma maxpagesize)
-{
-  return ((vma - off) % 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.  */
-
-static bfd_boolean
-assign_file_positions_for_segments (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.  */
-      for (m = elf_tdata (abfd)->segment_map;
-          m != NULL;
-          m = m->next)
-       {
-         unsigned int new_count;
-         unsigned int i;
-
-         if (m->p_type != PT_LOAD)
-           continue;
+   else
+     adjustment = vma_offset - off_offset;
+     
+   which can can be collapsed into the expression below.  */
 
-         new_count = 0;
-         for (i = 0; i < m->count; i ++)
-           {
-             if ((m->sections[i]->flags & SEC_ALLOC) != 0)
-               {
-                 if (i != new_count)
-                   m->sections[new_count] = m->sections[i];
+static file_ptr
+vma_page_aligned_bias (bfd_vma vma, ufile_ptr off, bfd_vma maxpagesize)
+{
+  return ((vma - off) % maxpagesize);
+}
 
-                 new_count ++;
-               }
-           }
+/* 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.  */
 
-         if (new_count != m->count)
-           m->count = new_count;
-       }
-    }
+static bfd_boolean
+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);
+  struct elf_segment_map *m;
+  Elf_Internal_Phdr *phdrs;
+  Elf_Internal_Phdr *p;
+  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
@@ -4012,27 +4315,58 @@ 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;
+
+      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
+       p->p_align = 0;
+
       if (p->p_type == PT_LOAD
          && m->count > 0)
        {
          bfd_size_type align;
          bfd_vma adjust;
+         unsigned int align_power = 0;
 
-         if ((abfd->flags & D_PAGED) != 0)
-           align = bed->maxpagesize;
-         else
+         for (i = 0, secpp = m->sections; i < m->count; i++, secpp++)
            {
-             unsigned int align_power = 0;
-             for (i = 0, secpp = m->sections; i < m->count; i++, secpp++)
-               {
-                 unsigned int secalign;
+             unsigned int secalign;
 
-                 secalign = bfd_get_section_alignment (abfd, *secpp);
-                 if (secalign > align_power)
-                   align_power = secalign;
-               }
-             align = (bfd_size_type) 1 << align_power;
+             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;
 
          adjust = vma_page_aligned_bias (m->sections[0]->vma, off, align);
          off += adjust;
@@ -4048,7 +4382,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)
@@ -4073,26 +4408,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;
@@ -4121,11 +4436,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)
@@ -4133,15 +4443,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;
 
@@ -4152,14 +4454,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;
@@ -4181,6 +4475,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;
@@ -4189,7 +4488,7 @@ 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)
@@ -4212,14 +4511,15 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
                }
              /* .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))
                {
                  /* The section VMA must equal the file position
                     modulo the page size.  */
                  bfd_size_type page = align;
-                 if ((abfd->flags & D_PAGED) != 0)
-                   page = bed->maxpagesize;
+                 if (page < maxpagesize)
+                   page = maxpagesize;
                  adjust = vma_page_aligned_bias (sec->vma,
                                                  p->p_vaddr + p->p_memsz,
                                                  page);
@@ -4252,7 +4552,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
@@ -4277,34 +4577,28 @@ 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
+                      && (p->p_type != PT_LOAD
+                          || (abfd->flags & D_PAGED) == 0))
                p->p_align = align;
            }
 
@@ -4319,34 +4613,131 @@ 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)
+       {
+         ((*_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);
+
+      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)
        {
-         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)
+         filehdr_vaddr = p->p_vaddr;
+         filehdr_paddr = p->p_paddr;
+       }
+      if (m->includes_phdrs)
+       {
+         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)
            {
@@ -4369,8 +4760,8 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
                  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)
+                     && (lp->p_vaddr + lp->p_filesz
+                         >= link_info->relro_end))
                    break;
                }
 
@@ -4387,136 +4778,16 @@ assign_file_positions_for_segments (bfd *abfd, struct bfd_link_info *link_info)
                }
              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;
-       }
-    }
-
-  for (s = abfd->sections; s != NULL; s = s->next)
-    {
-      if (s->flags & SEC_THREAD_LOCAL)
-       {
-         /* We need a PT_TLS segment.  */
-         ++segs;
-         break;
+                 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
@@ -4537,16 +4808,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;
 
@@ -4581,57 +4852,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.  */
@@ -4639,7 +4883,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;
 }
@@ -4804,8 +5048,9 @@ _bfd_elf_write_object_contents (bfd *abfd)
     }
 
   /* Write out the section header names.  */
-  if (bfd_seek (abfd, elf_tdata (abfd)->shstrtab_hdr.sh_offset, SEEK_SET) != 0
-      || ! _bfd_elf_strtab_emit (abfd, elf_shstrtab (abfd)))
+  if (elf_shstrtab (abfd) != NULL
+      && (bfd_seek (abfd, elf_tdata (abfd)->shstrtab_hdr.sh_offset, SEEK_SET) != 0
+          || ! _bfd_elf_strtab_emit (abfd, elf_shstrtab (abfd))))
     return FALSE;
 
   if (bed->elf_backend_final_write_processing)
@@ -4877,13 +5122,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;
     }
@@ -4914,10 +5160,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;
@@ -4933,13 +5179,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);
 
@@ -5232,8 +5471,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;
 
@@ -5500,6 +5738,245 @@ copy_private_bfd_data (bfd *ibfd, bfd *obfd)
   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;
+
+      /* 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))
+           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_alloc (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;
+
+      /* 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 (section_count != 0)
+       {
+         unsigned int isec = 0;
+
+         for (section = ibfd->sections;
+              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;
+           }
+       }
+
+      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 if any sections in the input BFD covered by ELF program
+        header are 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++)
+       {
+         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 doesn't 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 == 0 && elf_section_type (osec) == SHT_NULL))
+    elf_section_type (osec) = elf_section_type (isec);
+
+  /* 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)
+    {
+      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);
+       }
+    }
+
+  ihdr = &elf_section_data (isec)->this_hdr;
+
+  /* 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);
+    }
+
+  osec->use_rela_p = isec->use_rela_p;
+
+  return TRUE;
+}
+
 /* Copy private section information.  This copies over the entsize
    field, and sometimes the info field.  */
 
@@ -5526,20 +6003,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.  */
@@ -5547,6 +6012,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;
@@ -5562,6 +6029,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;
 }
 
@@ -5655,8 +6143,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);
@@ -5669,7 +6156,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);
@@ -6082,7 +6570,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)
@@ -6091,11 +6578,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;
 
@@ -6103,11 +6591,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++)
@@ -6124,11 +6623,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));
@@ -6141,13 +6650,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));
 
@@ -6160,6 +6673,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));
        }
@@ -6177,6 +6694,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;
 
@@ -6187,6 +6705,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.  */
@@ -6199,6 +6727,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));
        }
@@ -6210,8 +6742,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;
 
@@ -6227,15 +6759,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));
@@ -6248,20 +6797,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;
@@ -6280,8 +6834,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;
 
@@ -6305,10 +6859,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;
@@ -6445,8 +7000,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
@@ -6455,12 +7008,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;
@@ -6538,14 +7089,62 @@ _bfd_elf_find_nearest_line (bfd *abfd,
   return TRUE;
 }
 
+/* Find the line for a symbol.  */
+
+bfd_boolean
+_bfd_elf_find_line (bfd *abfd, asymbol **symbols, asymbol *symbol,
+                   const char **filename_ptr, unsigned int *line_ptr)
+{
+  return _bfd_dwarf2_find_line (abfd, symbols, symbol,
+                               filename_ptr, line_ptr, 0,
+                               &elf_tdata (abfd)->dwarf2_find_line_info);
+}
+
+/* After a call to bfd_find_nearest_line, successive calls to
+   bfd_find_inliner_info can be used to get source information about
+   each level of function inlining that terminated at the address
+   passed to bfd_find_nearest_line.  Currently this is only supported
+   for DWARF2 with appropriate DWARF3 extensions. */
+
+bfd_boolean
+_bfd_elf_find_inliner_info (bfd *abfd,
+                           const char **filename_ptr,
+                           const char **functionname_ptr,
+                           unsigned int *line_ptr)
+{
+  bfd_boolean found;
+  found = _bfd_dwarf2_find_inliner_info (abfd, filename_ptr,
+                                        functionname_ptr, line_ptr,
+                                        & elf_tdata (abfd)->dwarf2_find_line_info);
+  return found;
+}
+
 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;
 }
 
@@ -6681,7 +7280,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);
     }
@@ -6735,13 +7334,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;
 }
@@ -6773,12 +7371,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);
@@ -7057,7 +7655,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;
 
@@ -7072,7 +7670,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))
@@ -7087,7 +7684,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;
 
@@ -7102,7 +7699,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);
@@ -7143,7 +7739,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;
 
@@ -7151,7 +7747,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)
@@ -7171,14 +7766,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;
 
@@ -7248,13 +7842,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;
@@ -7362,7 +7956,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];
@@ -7394,20 +7988,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));
@@ -7416,7 +8009,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];
@@ -7424,20 +8017,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.  */
@@ -7458,7 +8050,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)
     {
@@ -7622,8 +8214,8 @@ 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";
@@ -8001,6 +8593,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;
@@ -8223,3 +8819,23 @@ 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);
+}
This page took 0.075118 seconds and 4 git commands to generate.