]> Git Repo - binutils.git/blobdiff - bfd/elf.c
* config/sparc/xm-sparc.h (NEW_SUN_CORE): Remove, never used.
[binutils.git] / bfd / elf.c
index 95245f547322fb36090be26e7197234c42f5ab4b..e12a885c5ee8cbffddd9ad0f7984f1669fa66c54 100644 (file)
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -1,5 +1,5 @@
 /* ELF executable support for BFD.
-   Copyright 1993 Free Software Foundation, Inc.
+   Copyright 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
 
 This file is part of BFD, the Binary File Descriptor library.
 
@@ -38,13 +38,14 @@ SECTION
 #define ARCH_SIZE 0
 #include "elf-bfd.h"
 
-static file_ptr map_program_segments PARAMS ((bfd *, file_ptr,
-                                             Elf_Internal_Shdr *,
-                                             Elf_Internal_Shdr **,
-                                             bfd_size_type));
-static boolean assign_file_positions_except_relocs PARAMS ((bfd *, boolean));
+static INLINE struct elf_segment_map *make_mapping
+  PARAMS ((bfd *, asection **, unsigned int, unsigned int));
+static int elf_sort_sections PARAMS ((const PTR, const PTR));
+static boolean assign_file_positions_for_segments PARAMS ((bfd *));
+static boolean assign_file_positions_except_relocs PARAMS ((bfd *));
 static boolean prep_headers PARAMS ((bfd *));
 static boolean swap_out_syms PARAMS ((bfd *, struct bfd_strtab_hash **));
+static boolean copy_private_bfd_data PARAMS ((bfd *, bfd *));
 
 /* Standard ELF hash function.  Do not change this function; you will
    cause invalid hash tables to be generated.  (Well, you would if this
@@ -82,10 +83,7 @@ elf_read (abfd, offset, size)
   char *buf;
 
   if ((buf = bfd_alloc (abfd, size)) == NULL)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return NULL;
-    }
+    return NULL;
   if (bfd_seek (abfd, offset, SEEK_SET) == -1)
     return NULL;
   if (bfd_read ((PTR) buf, size, 1, abfd) != size)
@@ -106,10 +104,7 @@ elf_mkobject (abfd)
   elf_tdata (abfd) = (struct elf_obj_tdata *)
     bfd_zalloc (abfd, sizeof (struct elf_obj_tdata));
   if (elf_tdata (abfd) == 0)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return false;
-    }
+    return false;
   /* since everything is done at close time, do we need any
      initialization? */
 
@@ -219,6 +214,27 @@ _bfd_elf_make_section_from_shdr (abfd, hdr, name)
   if (! bfd_set_section_flags (abfd, newsect, flags))
     return false;
 
+  if ((flags & SEC_ALLOC) != 0)
+    {
+      Elf_Internal_Phdr *phdr;
+      unsigned int i;
+
+      /* Look through the phdrs to see if we need to adjust the lma.  */
+      phdr = elf_tdata (abfd)->phdr;
+      for (i = 0; i < elf_elfheader (abfd)->e_phnum; i++, phdr++)
+       {
+         if (phdr->p_type == PT_LOAD
+             && phdr->p_paddr != 0
+             && phdr->p_vaddr != phdr->p_paddr
+             && phdr->p_vaddr <= hdr->sh_addr
+             && phdr->p_vaddr + phdr->p_memsz >= hdr->sh_addr + hdr->sh_size)
+           {
+             newsect->lma += phdr->p_paddr - phdr->p_vaddr;
+             break;
+           }
+       }
+    }
+
   hdr->bfd_section = newsect;
   elf_section_data (newsect)->this_hdr = *hdr;
 
@@ -310,6 +326,163 @@ bfd_elf_generic_reloc (abfd,
   return bfd_reloc_continue;
 }
 \f
+/* Print out the program headers.  */
+
+boolean
+_bfd_elf_print_private_bfd_data (abfd, farg)
+     bfd *abfd;
+     PTR farg;
+{
+  FILE *f = (FILE *) farg;
+  Elf_Internal_Phdr *p;
+  asection *s;
+  bfd_byte *dynbuf = NULL;
+
+  p = elf_tdata (abfd)->phdr;
+  if (p != NULL)
+    {
+      unsigned int i, c;
+
+      fprintf (f, "\nProgram Header:\n");
+      c = elf_elfheader (abfd)->e_phnum;
+      for (i = 0; i < c; i++, p++)
+       {
+         const char *s;
+         char buf[20];
+
+         switch (p->p_type)
+           {
+           case PT_NULL: s = "NULL"; break;
+           case PT_LOAD: s = "LOAD"; break;
+           case PT_DYNAMIC: s = "DYNAMIC"; break;
+           case PT_INTERP: s = "INTERP"; break;
+           case PT_NOTE: s = "NOTE"; break;
+           case PT_SHLIB: s = "SHLIB"; break;
+           case PT_PHDR: s = "PHDR"; break;
+           default: sprintf (buf, "0x%lx", p->p_type); s = buf; break;
+           }
+         fprintf (f, "%8s off    0x", s);
+         fprintf_vma (f, p->p_offset);
+         fprintf (f, " vaddr 0x");
+         fprintf_vma (f, p->p_vaddr);
+         fprintf (f, " paddr 0x");
+         fprintf_vma (f, p->p_paddr);
+         fprintf (f, " align 2**%u\n", bfd_log2 (p->p_align));
+         fprintf (f, "         filesz 0x");
+         fprintf_vma (f, p->p_filesz);
+         fprintf (f, " memsz 0x");
+         fprintf_vma (f, p->p_memsz);
+         fprintf (f, " flags %c%c%c",
+                  (p->p_flags & PF_R) != 0 ? 'r' : '-',
+                  (p->p_flags & PF_W) != 0 ? 'w' : '-',
+                  (p->p_flags & PF_X) != 0 ? 'x' : '-');
+         if ((p->p_flags &~ (PF_R | PF_W | PF_X)) != 0)
+           fprintf (f, " %lx", p->p_flags &~ (PF_R | PF_W | PF_X));
+         fprintf (f, "\n");
+       }
+    }
+
+  s = bfd_get_section_by_name (abfd, ".dynamic");
+  if (s != NULL)
+    {
+      int elfsec;
+      unsigned long link;
+      bfd_byte *extdyn, *extdynend;
+      size_t extdynsize;
+      void (*swap_dyn_in) PARAMS ((bfd *, const PTR, Elf_Internal_Dyn *));
+
+      fprintf (f, "\nDynamic Section:\n");
+
+      dynbuf = (bfd_byte *) bfd_malloc (s->_raw_size);
+      if (dynbuf == NULL)
+       goto error_return;
+      if (! bfd_get_section_contents (abfd, s, (PTR) dynbuf, (file_ptr) 0,
+                                     s->_raw_size))
+       goto error_return;
+
+      elfsec = _bfd_elf_section_from_bfd_section (abfd, s);
+      if (elfsec == -1)
+       goto error_return;
+      link = elf_elfsections (abfd)[elfsec]->sh_link;
+
+      extdynsize = get_elf_backend_data (abfd)->s->sizeof_dyn;
+      swap_dyn_in = get_elf_backend_data (abfd)->s->swap_dyn_in;
+
+      extdyn = dynbuf;
+      extdynend = extdyn + s->_raw_size;
+      for (; extdyn < extdynend; extdyn += extdynsize)
+       {
+         Elf_Internal_Dyn dyn;
+         const char *name;
+         char ab[20];
+         boolean stringp;
+
+         (*swap_dyn_in) (abfd, (PTR) extdyn, &dyn);
+
+         if (dyn.d_tag == DT_NULL)
+           break;
+
+         stringp = false;
+         switch (dyn.d_tag)
+           {
+           default:
+             sprintf (ab, "0x%lx", (unsigned long) dyn.d_tag);
+             name = ab;
+             break;
+
+           case DT_NEEDED: name = "NEEDED"; stringp = true; break;
+           case DT_PLTRELSZ: name = "PLTRELSZ"; break;
+           case DT_PLTGOT: name = "PLTGOT"; break;
+           case DT_HASH: name = "HASH"; break;
+           case DT_STRTAB: name = "STRTAB"; break;
+           case DT_SYMTAB: name = "SYMTAB"; break;
+           case DT_RELA: name = "RELA"; break;
+           case DT_RELASZ: name = "RELASZ"; break;
+           case DT_RELAENT: name = "RELAENT"; break;
+           case DT_STRSZ: name = "STRSZ"; break;
+           case DT_SYMENT: name = "SYMENT"; break;
+           case DT_INIT: name = "INIT"; break;
+           case DT_FINI: name = "FINI"; break;
+           case DT_SONAME: name = "SONAME"; stringp = true; break;
+           case DT_RPATH: name = "RPATH"; stringp = true; break;
+           case DT_SYMBOLIC: name = "SYMBOLIC"; break;
+           case DT_REL: name = "REL"; break;
+           case DT_RELSZ: name = "RELSZ"; break;
+           case DT_RELENT: name = "RELENT"; break;
+           case DT_PLTREL: name = "PLTREL"; break;
+           case DT_DEBUG: name = "DEBUG"; break;
+           case DT_TEXTREL: name = "TEXTREL"; break;
+           case DT_JMPREL: name = "JMPREL"; break;
+           }
+
+         fprintf (f, "  %-11s ", name);
+         if (! stringp)
+           fprintf (f, "0x%lx", (unsigned long) dyn.d_un.d_val);
+         else
+           {
+             const char *string;
+
+             string = bfd_elf_string_from_elf_section (abfd, link,
+                                                       dyn.d_un.d_val);
+             if (string == NULL)
+               goto error_return;
+             fprintf (f, "%s", string);
+           }
+         fprintf (f, "\n");
+       }
+
+      free (dynbuf);
+      dynbuf = NULL;
+    }
+
+  return true;
+
+ error_return:
+  if (dynbuf != NULL)
+    free (dynbuf);
+  return false;
+}
+
 /* Display ELF-specific fields of a symbol.  */
 void
 bfd_elf_print_symbol (ignore_abfd, filep, symbol, how)
@@ -365,10 +538,7 @@ _bfd_elf_link_hash_newfunc (entry, table, string)
     ret = ((struct elf_link_hash_entry *)
           bfd_hash_allocate (table, sizeof (struct elf_link_hash_entry)));
   if (ret == (struct elf_link_hash_entry *) NULL)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return (struct bfd_hash_entry *) ret;
-    }
+    return (struct bfd_hash_entry *) ret;
 
   /* Call the allocation method of the superclass.  */
   ret = ((struct elf_link_hash_entry *)
@@ -422,10 +592,7 @@ _bfd_elf_link_hash_table_create (abfd)
   ret = ((struct elf_link_hash_table *)
         bfd_alloc (abfd, sizeof (struct elf_link_hash_table)));
   if (ret == (struct elf_link_hash_table *) NULL)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return NULL;
-    }
+    return NULL;
 
   if (! _bfd_elf_link_hash_table_init (ret, abfd, _bfd_elf_link_hash_newfunc))
     {
@@ -511,6 +678,7 @@ bfd_section_from_shdr (abfd, shindex)
     case SHT_DYNAMIC:  /* Dynamic linking information.  */
     case SHT_NOBITS:   /* .bss section.  */
     case SHT_HASH:     /* .hash section.  */
+    case SHT_NOTE:     /* .note section.  */
       return _bfd_elf_make_section_from_shdr (abfd, hdr, name);
 
     case SHT_SYMTAB:           /* A symbol table */
@@ -521,7 +689,7 @@ bfd_section_from_shdr (abfd, shindex)
       BFD_ASSERT (elf_onesymtab (abfd) == 0);
       elf_onesymtab (abfd) = shindex;
       elf_tdata (abfd)->symtab_hdr = *hdr;
-      elf_elfsections (abfd)[shindex] = &elf_tdata (abfd)->symtab_hdr;
+      elf_elfsections (abfd)[shindex] = hdr = &elf_tdata (abfd)->symtab_hdr;
       abfd->flags |= HAS_SYMS;
 
       /* Sometimes a shared object will map in the symbol table.  If
@@ -545,7 +713,7 @@ bfd_section_from_shdr (abfd, shindex)
       BFD_ASSERT (elf_dynsymtab (abfd) == 0);
       elf_dynsymtab (abfd) = shindex;
       elf_tdata (abfd)->dynsymtab_hdr = *hdr;
-      elf_elfsections (abfd)[shindex] = &elf_tdata (abfd)->dynsymtab_hdr;
+      elf_elfsections (abfd)[shindex] = hdr = &elf_tdata (abfd)->dynsymtab_hdr;
       abfd->flags |= HAS_SYMS;
 
       /* Besides being a symbol table, we also treat this as a regular
@@ -581,7 +749,7 @@ bfd_section_from_shdr (abfd, shindex)
                if (elf_dynsymtab (abfd) == i)
                  {
                    elf_tdata (abfd)->dynstrtab_hdr = *hdr;
-                   elf_elfsections (abfd)[shindex] =
+                   elf_elfsections (abfd)[shindex] = hdr =
                      &elf_tdata (abfd)->dynstrtab_hdr;
                    /* We also treat this as a regular section, so
                       that objcopy can handle it.  */
@@ -687,9 +855,6 @@ bfd_section_from_shdr (abfd, shindex)
       }
       break;
 
-    case SHT_NOTE:
-      break;
-
     case SHT_SHLIB:
       return true;
 
@@ -728,10 +893,7 @@ _bfd_elf_new_section_hook (abfd, sec)
 
   sdata = (struct bfd_elf_section_data *) bfd_alloc (abfd, sizeof (*sdata));
   if (!sdata)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return false;
-    }
+    return false;
   sec->used_by_bfd = (PTR) sdata;
   memset (sdata, 0, sizeof (*sdata));
   return true;
@@ -776,10 +938,7 @@ bfd_section_from_phdr (abfd, hdr, index)
   sprintf (namebuf, split ? "segment%da" : "segment%d", index);
   name = bfd_alloc (abfd, strlen (namebuf) + 1);
   if (!name)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return false;
-    }
+    return false;
   strcpy (name, namebuf);
   newsect = bfd_make_section (abfd, name);
   if (newsect == NULL)
@@ -810,10 +969,7 @@ bfd_section_from_phdr (abfd, hdr, index)
       sprintf (namebuf, "segment%db", index);
       name = bfd_alloc (abfd, strlen (namebuf) + 1);
       if (!name)
-       {
-         bfd_set_error (bfd_error_no_memory);
-         return false;
-       }
+       return false;
       strcpy (name, namebuf);
       newsect = bfd_make_section (abfd, name);
       if (newsect == NULL)
@@ -867,27 +1023,17 @@ elf_fake_sections (abfd, asect, failedptrarg)
 
   this_hdr->sh_flags = 0;
 
-  /* FIXME: This should really use vma, rather than lma.  However,
-     that would mean that the lma information was lost, which would
-     mean that the AT keyword in linker scripts would not work.
-     Fortunately, native scripts do not use the AT keyword, so we can
-     get away with using lma here.  The right way to handle this is to
-     1) read the program headers as well as the section headers, and
-     set the lma fields of the BFD sections based on the p_paddr
-     fields of the program headers, and 2) set the p_paddr fields of
-     the program headers based on the section lma fields when writing
-     them out.  */
   if ((asect->flags & SEC_ALLOC) != 0)
-    this_hdr->sh_addr = asect->lma;
+    this_hdr->sh_addr = asect->vma;
   else
     this_hdr->sh_addr = 0;
 
   this_hdr->sh_offset = 0;
   this_hdr->sh_size = asect->_raw_size;
   this_hdr->sh_link = 0;
-  this_hdr->sh_info = 0;
   this_hdr->sh_addralign = 1 << asect->alignment_power;
-  this_hdr->sh_entsize = 0;
+  /* The sh_entsize and sh_info fields may have been set already by
+     copy_private_section_data.  */
 
   this_hdr->bfd_section = asect;
   this_hdr->contents = NULL;
@@ -966,7 +1112,6 @@ elf_fake_sections (abfd, asect, failedptrarg)
       name = bfd_alloc (abfd, sizeof ".rela" + strlen (asect->name));
       if (name == NULL)
        {
-         bfd_set_error (bfd_error_no_memory);
          *failedptr = true;
          return;
        }
@@ -1035,17 +1180,13 @@ assign_section_numbers (abfd)
   i_shdrp = ((Elf_Internal_Shdr **)
             bfd_alloc (abfd, section_number * sizeof (Elf_Internal_Shdr *)));
   if (i_shdrp == NULL)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return false;
-    }
+    return false;
 
   i_shdrp[0] = ((Elf_Internal_Shdr *)
                bfd_alloc (abfd, sizeof (Elf_Internal_Shdr)));
   if (i_shdrp[0] == NULL)
     {
       bfd_release (abfd, i_shdrp);
-      bfd_set_error (bfd_error_no_memory);
       return false;
     }
   memset (i_shdrp[0], 0, sizeof (Elf_Internal_Shdr));
@@ -1117,12 +1258,9 @@ assign_section_numbers (abfd)
              char *alc;
 
              len = strlen (sec->name);
-             alc = (char *) malloc (len - 2);
+             alc = (char *) bfd_malloc (len - 2);
              if (alc == NULL)
-               {
-                 bfd_set_error (bfd_error_no_memory);
-                 return false;
-               }
+               return false;
              strncpy (alc, sec->name, len - 3);
              alc[len - 3] = '\0';
              s = bfd_get_section_by_name (abfd, alc);
@@ -1211,16 +1349,13 @@ elf_map_symbols (abfd)
   max_index++;
   sect_syms = (asymbol **) bfd_zalloc (abfd, max_index * sizeof (asymbol *));
   if (sect_syms == NULL)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return false;
-    }
+    return false;
   elf_section_syms (abfd) = sect_syms;
 
   for (idx = 0; idx < symcount; idx++)
     {
       if ((syms[idx]->flags & BSF_SECTION_SYM) != 0
-         && syms[idx]->value == 0)
+         && (syms[idx]->value + syms[idx]->section->vma) == 0)
        {
          asection *sec;
 
@@ -1291,10 +1426,7 @@ elf_map_symbols (abfd)
              bfd_alloc (abfd,
                         (num_locals + num_globals) * sizeof (asymbol *)));
   if (new_syms == NULL)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return false;
-    }
+    return false;
 
   for (idx = 0; idx < symcount; idx++)
     {
@@ -1333,6 +1465,43 @@ elf_map_symbols (abfd)
   return true;
 }
 
+/* Align to the maximum file alignment that could be required for any
+   ELF data structure.  */
+
+static INLINE file_ptr align_file_position PARAMS ((file_ptr, int));
+static INLINE file_ptr
+align_file_position (off, align)
+     file_ptr off;
+     int align;
+{
+  return (off + align - 1) & ~(align - 1);
+}
+
+/* Assign a file position to a section, optionally aligning to the
+   required section alignment.  */
+
+INLINE file_ptr
+_bfd_elf_assign_file_position_for_section (i_shdrp, offset, align)
+     Elf_Internal_Shdr *i_shdrp;
+     file_ptr offset;
+     boolean align;
+{
+  if (align)
+    {
+      unsigned int al;
+
+      al = i_shdrp->sh_addralign;
+      if (al > 1)
+       offset = BFD_ALIGN (offset, al);
+    }
+  i_shdrp->sh_offset = offset;
+  if (i_shdrp->bfd_section != NULL)
+    i_shdrp->bfd_section->filepos = offset;
+  if (i_shdrp->sh_type != SHT_NOBITS)
+    offset += i_shdrp->sh_size;
+  return offset;
+}
+
 /* Compute the file positions we are going to put the sections at, and
    otherwise prepare to begin writing out the ELF file.  If LINK_INFO
    is not NULL, this is being called by the ELF backend linker.  */
@@ -1366,7 +1535,7 @@ _bfd_elf_compute_section_file_positions (abfd, link_info)
     return false;
 
   /* The backend linker builds symbol table information itself.  */
-  if (link_info == NULL)
+  if (link_info == NULL && abfd->symcount > 0)
     {
       if (! swap_out_syms (abfd, &strtab))
        return false;
@@ -1381,19 +1550,30 @@ _bfd_elf_compute_section_file_positions (abfd, link_info)
   shstrtab_hdr->sh_entsize = 0;
   shstrtab_hdr->sh_link = 0;
   shstrtab_hdr->sh_info = 0;
-  /* sh_offset is set in assign_file_positions_for_symtabs_and_strtabs.  */
+  /* sh_offset is set in assign_file_positions_except_relocs.  */
   shstrtab_hdr->sh_addralign = 1;
 
-  if (!assign_file_positions_except_relocs (abfd,
-                                           link_info == NULL ? true : false))
+  if (!assign_file_positions_except_relocs (abfd))
     return false;
 
-  if (link_info == NULL)
+  if (link_info == NULL && abfd->symcount > 0)
     {
+      file_ptr off;
+      Elf_Internal_Shdr *hdr;
+
+      off = elf_tdata (abfd)->next_file_pos;
+
+      hdr = &elf_tdata (abfd)->symtab_hdr;
+      off = _bfd_elf_assign_file_position_for_section (hdr, off, true);
+
+      hdr = &elf_tdata (abfd)->strtab_hdr;
+      off = _bfd_elf_assign_file_position_for_section (hdr, off, true);
+
+      elf_tdata (abfd)->next_file_pos = off;
+
       /* Now that we know where the .strtab section goes, write it
          out.  */
-      if ((bfd_seek (abfd, elf_tdata (abfd)->strtab_hdr.sh_offset, SEEK_SET)
-          != 0)
+      if (bfd_seek (abfd, hdr->sh_offset, SEEK_SET) != 0
          || ! _bfd_stringtab_emit (abfd, strtab))
        return false;
       _bfd_stringtab_free (strtab);
@@ -1404,428 +1584,597 @@ _bfd_elf_compute_section_file_positions (abfd, link_info)
   return true;
 }
 
+/* Create a mapping from a set of sections to a program segment.  */
 
-/* Align to the maximum file alignment that could be required for any
-   ELF data structure.  */
-
-static INLINE file_ptr align_file_position PARAMS ((file_ptr, int));
-static INLINE file_ptr
-align_file_position (off, align)
-     file_ptr off;
-     int align;
+static INLINE struct elf_segment_map *
+make_mapping (abfd, sections, from, to)
+     bfd *abfd;
+     asection **sections;
+     unsigned int from;
+     unsigned int to;
 {
-  return (off + align - 1) & ~(align - 1);
-}
+  struct elf_segment_map *m;
+  unsigned int i;
+  asection **hdrpp;
 
-/* Assign a file position to a section, optionally aligning to the
-   required section alignment.  */
+  m = ((struct elf_segment_map *)
+       bfd_zalloc (abfd,
+                  (sizeof (struct elf_segment_map)
+                   + (to - from - 1) * sizeof (asection *))));
+  if (m == NULL)
+    return NULL;
+  m->next = NULL;
+  m->p_type = PT_LOAD;
+  for (i = from, hdrpp = sections + from; i < to; i++, hdrpp++)
+    m->sections[i - from] = *hdrpp;
+  m->count = to - from;
 
-INLINE file_ptr
-_bfd_elf_assign_file_position_for_section (i_shdrp, offset, align)
-     Elf_Internal_Shdr *i_shdrp;
-     file_ptr offset;
-     boolean align;
-{
-  if (align)
+  if (from == 0)
     {
-      unsigned int al;
-
-      al = i_shdrp->sh_addralign;
-      if (al > 1)
-       offset = BFD_ALIGN (offset, al);
+      /* Include the headers in the first PT_LOAD segment.  */
+      m->includes_filehdr = 1;
+      m->includes_phdrs = 1;
     }
-  i_shdrp->sh_offset = offset;
-  if (i_shdrp->bfd_section != NULL)
-    i_shdrp->bfd_section->filepos = offset;
-  if (i_shdrp->sh_type != SHT_NOBITS)
-    offset += i_shdrp->sh_size;
-  return offset;
-}
-
-/* Get the size of the program header.
 
-   SORTED_HDRS, if non-NULL, is an array of COUNT pointers to headers sorted
-   by VMA.  Non-allocated sections (!SHF_ALLOC) must appear last.  All
-   section VMAs and sizes are known so we can compute the correct value.
-   (??? This may not be perfectly true.  What cases do we miss?)
-
-   If SORTED_HDRS is NULL we assume there are two segments: text and data
-   (exclusive of .interp and .dynamic).
-
-   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).
+  return m;
+}
 
-   ??? User written scripts must either not use SIZEOF_HEADERS, or assume there
-   will be two segments.  */
+/* Set up a mapping from BFD sections to program segments.  */
 
-static bfd_size_type
-get_program_header_size (abfd, sorted_hdrs, count, maxpagesize)
+static boolean
+map_sections_to_segments (abfd)
      bfd *abfd;
-     Elf_Internal_Shdr **sorted_hdrs;
-     unsigned int count;
-     bfd_vma maxpagesize;
 {
-  size_t segs;
+  asection **sections = NULL;
   asection *s;
-  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;
+  unsigned int i;
+  unsigned int count;
+  struct elf_segment_map *mfirst;
+  struct elf_segment_map **pm;
+  struct elf_segment_map *m;
+  asection *last_hdr;
+  unsigned int phdr_index;
+  bfd_vma maxpagesize;
+  asection **hdrpp;
+
+  if (elf_tdata (abfd)->segment_map != NULL)
+    return true;
 
-  if (sorted_hdrs != NULL)
-    {
-      unsigned int i;
-      unsigned int last_type;
-      Elf_Internal_Shdr **hdrpp;
-      /* What we think the current segment's offset is.  */
-      bfd_vma p_offset;
-      /* What we think the current segment's address is.  */
-      bfd_vma p_vaddr;
-      /* How big we think the current segment is.  */
-      bfd_vma p_memsz;
-      /* What we think the current file offset is.  */
-      bfd_vma file_offset;
-      bfd_vma next_offset;
-
-      /* Scan the headers and compute the number of segments required.  This
-        code is intentionally similar to the code in map_program_segments.
-
-        The `sh_offset' field isn't valid at this point, so we keep our own
-        running total in `file_offset'.
-
-        This works because section VMAs are already known.  */
-
-      segs = 1;
-      /* Make sure the first section goes in the first segment.  */
-      file_offset = p_offset = sorted_hdrs[0]->sh_addr % maxpagesize;
-      p_vaddr = sorted_hdrs[0]->sh_addr;
-      p_memsz = 0;
-      last_type = SHT_PROGBITS;
-
-      for (i = 0, hdrpp = sorted_hdrs; i < count; i++, hdrpp++)
-       {
-         Elf_Internal_Shdr *hdr;
+  if (bfd_count_sections (abfd) == 0)
+    return true;
 
-         hdr = *hdrpp;
+  /* Select the allocated sections, and sort them.  */
 
-         /* Ignore any section which will not be part of the process
-            image.  */
-         if ((hdr->sh_flags & SHF_ALLOC) == 0)
-           continue;
-
-         /* Keep track of where this and the next sections go.
-            The section VMA must equal the file position modulo
-            the page size.  */
-         file_offset += (hdr->sh_addr - file_offset) % maxpagesize;
-         next_offset = file_offset;
-         if (hdr->sh_type != SHT_NOBITS)
-           next_offset = file_offset + hdr->sh_size;
-
-         /* If this section fits in the segment we are constructing, add
-            it in.  */
-         if ((file_offset - (p_offset + p_memsz)
-              == hdr->sh_addr - (p_vaddr + p_memsz))
-             && (last_type != SHT_NOBITS || hdr->sh_type == SHT_NOBITS))
-           {
-             bfd_size_type adjust;
+  sections = (asection **) bfd_malloc (bfd_count_sections (abfd)
+                                      * sizeof (asection *));
+  if (sections == NULL)
+    goto error_return;
 
-             adjust = hdr->sh_addr - (p_vaddr + p_memsz);
-             p_memsz += hdr->sh_size + adjust;
-             file_offset = next_offset;
-             last_type = hdr->sh_type;
-             continue;
-           }
+  i = 0;
+  for (s = abfd->sections; s != NULL; s = s->next)
+    {
+      if ((s->flags & SEC_ALLOC) != 0)
+       {
+         sections[i] = s;
+         ++i;
+       }
+    }
+  BFD_ASSERT (i <= bfd_count_sections (abfd));
+  count = i;
 
-         /* The section won't fit, start a new segment.  */
-         ++segs;
+  qsort (sections, (size_t) count, sizeof (asection *), elf_sort_sections);
 
-         /* Initialize the segment.  */
-         p_vaddr = hdr->sh_addr;
-         p_memsz = hdr->sh_size;
-         p_offset = file_offset;
-         file_offset = next_offset;
+  /* Build the mapping.  */
 
-         last_type = hdr->sh_type;
-       }
-    }
-  else
-    {
-      /* Assume we will need exactly two PT_LOAD segments: one for text
-        and one for data.  */
-      segs = 2;
-    }
+  mfirst = NULL;
+  pm = &mfirst;
 
+  /* If we have a .interp section, then create a PT_PHDR segment for
+     the program headers and a PT_INTERP segment for the .interp
+     section.  */
   s = bfd_get_section_by_name (abfd, ".interp");
   if (s != NULL && (s->flags & SEC_LOAD) != 0)
     {
-      /* If 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;
-    }
+      m = ((struct elf_segment_map *)
+          bfd_zalloc (abfd, sizeof (struct elf_segment_map)));
+      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;
 
-  if (bfd_get_section_by_name (abfd, ".dynamic") != NULL)
-    {
-      /* We need a PT_DYNAMIC segment.  */
-      ++segs;
+      *pm = m;
+      pm = &m->next;
+
+      m = ((struct elf_segment_map *)
+          bfd_zalloc (abfd, sizeof (struct elf_segment_map)));
+      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;
     }
 
-  /* Let the backend count up any program headers it might need.  */
-  if (bed->elf_backend_create_program_headers)
-    segs = ((*bed->elf_backend_create_program_headers)
-           (abfd, (Elf_Internal_Phdr *) NULL, segs));
+  /* 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;
+  phdr_index = 0;
+  maxpagesize = get_elf_backend_data (abfd)->maxpagesize;
+  for (i = 0, hdrpp = sections; i < count; i++, hdrpp++)
+    {
+      asection *hdr;
 
-  elf_tdata (abfd)->program_header_size = segs * bed->s->sizeof_phdr;
-  return elf_tdata (abfd)->program_header_size;
-}
+      hdr = *hdrpp;
 
-/* Create the program header.  OFF is the file offset where the
-   program header should be written.  FIRST is the first loadable ELF
-   section.  SORTED_HDRS is the ELF sections sorted by section
-   address.  PHDR_SIZE is the size of the program header as returned
-   by get_program_header_size.  */
+      /* See if this section and the last one will fit in the same
+         segment.  */
+      if (last_hdr == NULL
+         || ((BFD_ALIGN (last_hdr->lma + last_hdr->_raw_size, maxpagesize)
+              >= hdr->lma)
+             && ((last_hdr->flags & SEC_LOAD) != 0
+                 || (hdr->flags & SEC_LOAD) == 0)))
+       {
+         last_hdr = hdr;
+         continue;
+       }
 
-static file_ptr
-map_program_segments (abfd, off, first, sorted_hdrs, phdr_size)
-     bfd *abfd;
-     file_ptr off;
-     Elf_Internal_Shdr *first;
-     Elf_Internal_Shdr **sorted_hdrs;
-     bfd_size_type phdr_size;
-{
-  Elf_Internal_Phdr phdrs[10];
-  unsigned int phdr_count;
-  Elf_Internal_Phdr *phdr;
-  int phdr_size_adjust;
-  unsigned int i;
-  Elf_Internal_Shdr **hdrpp;
-  asection *sinterp, *sdyn;
-  unsigned int last_type;
-  Elf_Internal_Ehdr *i_ehdrp;
-  struct elf_backend_data *bed = get_elf_backend_data (abfd);
+      /* This section won't fit in the program segment.  We must
+         create a new program header holding all the sections from
+         phdr_index until hdr.  */
 
-  BFD_ASSERT ((abfd->flags & (EXEC_P | DYNAMIC)) != 0);
-  BFD_ASSERT (phdr_size / sizeof (Elf_Internal_Phdr)
-             <= sizeof phdrs / sizeof (phdrs[0]));
+      m = make_mapping (abfd, sections, phdr_index, i);
+      if (m == NULL)
+       goto error_return;
 
-  phdr_count = 0;
-  phdr = phdrs;
+      *pm = m;
+      pm = &m->next;
 
-  if (bed->want_hdr_in_seg)
-    phdr_size_adjust = first->sh_offset - phdr_size;
-  else
-    phdr_size_adjust = 0;
+      last_hdr = hdr;
+      phdr_index = i;
+    }
 
-  /* If we have a loadable .interp section, we must create a PT_INTERP
-     segment which must precede all PT_LOAD segments.  We assume that
-     we must also create a PT_PHDR segment, although that may not be
-     true for all targets.  */
-  sinterp = bfd_get_section_by_name (abfd, ".interp");
-  if (sinterp != NULL && (sinterp->flags & SEC_LOAD) != 0)
+  /* Create a final PT_LOAD program segment.  */
+  if (last_hdr != NULL)
     {
-      BFD_ASSERT (first != NULL);
+      m = make_mapping (abfd, sections, phdr_index, i);
+      if (m == NULL)
+       goto error_return;
 
-      phdr->p_type = PT_PHDR;
+      *pm = m;
+      pm = &m->next;
+    }
 
-      phdr->p_offset = off;
+  /* If there is a .dynamic section, throw in a PT_DYNAMIC segment.  */
+  s = bfd_get_section_by_name (abfd, ".dynamic");
+  if (s != NULL && (s->flags & SEC_LOAD) != 0)
+    {
+      m = ((struct elf_segment_map *)
+          bfd_zalloc (abfd, sizeof (struct elf_segment_map)));
+      if (m == NULL)
+       goto error_return;
+      m->next = NULL;
+      m->p_type = PT_DYNAMIC;
+      m->count = 1;
+      m->sections[0] = s;
 
-      /* Account for any adjustment made because of the alignment of
-        the first loadable section.  */
-      phdr_size_adjust = (first->sh_offset - phdr_size) - off;
-      BFD_ASSERT (phdr_size_adjust >= 0 && phdr_size_adjust < 128);
+      *pm = m;
+      pm = &m->next;
+    }
 
-      /* The program header precedes all loadable sections.  This lets
-        us compute its loadable address.  This depends on the linker
-        script.  */
-      phdr->p_vaddr = first->sh_addr - (phdr_size + phdr_size_adjust);
+  free (sections);
+  sections = NULL;
 
-      phdr->p_paddr = 0;
-      phdr->p_filesz = phdr_size;
-      phdr->p_memsz = phdr_size;
+  elf_tdata (abfd)->segment_map = mfirst;
+  return true;
 
-      /* FIXME: UnixWare and Solaris set PF_X, Irix 5 does not.  */
-      phdr->p_flags = PF_R | PF_X;
+ error_return:
+  if (sections != NULL)
+    free (sections);
+  return false;
+}
 
-      phdr->p_align = bed->s->file_align;
-      BFD_ASSERT ((phdr->p_vaddr - phdr->p_offset) % bed->s->file_align == 0);
+/* Sort sections by VMA.  */
 
-      /* Include the ELF header in the first loadable segment.  */
-      phdr_size_adjust += off;
+static int
+elf_sort_sections (arg1, arg2)
+     const PTR arg1;
+     const PTR arg2;
+{
+  const asection *sec1 = *(const asection **) arg1;
+  const asection *sec2 = *(const asection **) arg2;
 
-      ++phdr_count;
-      ++phdr;
+  if (sec1->vma < sec2->vma)
+    return -1;
+  else if (sec1->vma > sec2->vma)
+    return 1;
 
-      phdr->p_type = PT_INTERP;
-      phdr->p_offset = sinterp->filepos;
-      phdr->p_vaddr = sinterp->vma;
-      phdr->p_paddr = 0;
-      phdr->p_filesz = sinterp->_raw_size;
-      phdr->p_memsz = sinterp->_raw_size;
-      phdr->p_flags = PF_R;
-      phdr->p_align = 1 << bfd_get_section_alignment (abfd, sinterp);
+  /* Put !SEC_LOAD sections after SEC_LOAD ones.  */
 
-      ++phdr_count;
-      ++phdr;
-    }
+#define TOEND(x) (((x)->flags & SEC_LOAD) == 0)
 
-  /* Look through the sections to see how they will be divided into
-     program segments.  The sections must be arranged in order by
-     sh_addr for this to work correctly.  */
-  phdr->p_type = PT_NULL;
-  last_type = SHT_PROGBITS;
-  for (i = 1, hdrpp = sorted_hdrs;
-       i < elf_elfheader (abfd)->e_shnum;
-       i++, hdrpp++)
+  if (TOEND (sec1))
+    if (TOEND (sec2))
+      return sec1->target_index - sec2->target_index;
+    else 
+      return 1;
+
+  if (TOEND (sec2))
+    return -1;
+
+#undef TOEND
+
+  /* Sort by size, to put zero sized sections before others at the
+     same address.  */
+
+  if (sec1->_raw_size < sec2->_raw_size)
+    return -1;
+  if (sec1->_raw_size > sec2->_raw_size)
+    return 1;
+
+  return sec1->target_index - sec2->target_index;
+}
+
+/* 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 boolean
+assign_file_positions_for_segments (abfd)
+     bfd *abfd;
+{
+  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;
+  bfd_vma filehdr_vaddr, filehdr_paddr;
+  bfd_vma phdrs_vaddr, phdrs_paddr;
+  Elf_Internal_Phdr *p;
+
+  if (elf_tdata (abfd)->segment_map == NULL)
     {
-      Elf_Internal_Shdr *hdr;
+      if (! map_sections_to_segments (abfd))
+       return false;
+    }
 
-      hdr = *hdrpp;
+  if (bed->elf_backend_modify_segment_map)
+    {
+      if (! (*bed->elf_backend_modify_segment_map) (abfd))
+       return false;
+    }
 
-      /* Ignore any section which will not be part of the process
-        image.  */
-      if ((hdr->sh_flags & SHF_ALLOC) == 0)
-       continue;
+  count = 0;
+  for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
+    ++count;
+
+  elf_elfheader (abfd)->e_phoff = bed->s->sizeof_ehdr;
+  elf_elfheader (abfd)->e_phentsize = bed->s->sizeof_phdr;
+  elf_elfheader (abfd)->e_phnum = count;
+
+  if (count == 0)
+    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)
+       ("%s: Not enough room for program headers (allocated %u, need %u)",
+       bfd_get_filename (abfd), alloc, count));
+      bfd_set_error (bfd_error_bad_value);
+      return false;
+    }
 
-      /* If this section fits in the segment we are constructing, add
-        it in.  */
-      if (phdr->p_type != PT_NULL
-         && (hdr->sh_offset - (phdr->p_offset + phdr->p_memsz)
-             == hdr->sh_addr - (phdr->p_vaddr + phdr->p_memsz))
-         && (last_type != SHT_NOBITS || hdr->sh_type == SHT_NOBITS))
+  if (alloc == 0)
+    alloc = count;
+
+  phdrs = ((Elf_Internal_Phdr *)
+          bfd_alloc (abfd, alloc * sizeof (Elf_Internal_Phdr)));
+  if (phdrs == NULL)
+    return false;
+
+  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
+         sections may not be correctly ordered.  */
+      if (m->count > 0)
+       qsort (m->sections, (size_t) m->count, sizeof (asection *),
+              elf_sort_sections);
+
+      p->p_type = m->p_type;
+
+      if (m->p_flags_valid)
+       p->p_flags = m->p_flags;
+      else
+       p->p_flags = 0;
+
+      if (p->p_type == PT_LOAD
+         && m->count > 0
+         && (m->sections[0]->flags & SEC_LOAD) != 0)
+       off += (m->sections[0]->vma - off) % bed->maxpagesize;
+
+      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)
+       p->p_align = bed->maxpagesize;
+      else if (m->count == 0)
+       p->p_align = bed->s->file_align;
+      else
+       p->p_align = 0;
+
+      p->p_offset = 0;
+      p->p_filesz = 0;
+      p->p_memsz = 0;
+
+      if (m->includes_filehdr)
        {
-         bfd_size_type adjust;
-
-         adjust = hdr->sh_addr - (phdr->p_vaddr + phdr->p_memsz);
-         phdr->p_memsz += hdr->sh_size + adjust;
-         if (hdr->sh_type != SHT_NOBITS)
-           phdr->p_filesz += hdr->sh_size + adjust;
-         if ((hdr->sh_flags & SHF_WRITE) != 0)
-           phdr->p_flags |= PF_W;
-         if ((hdr->sh_flags & SHF_EXECINSTR) != 0)
-           phdr->p_flags |= PF_X;
-         last_type = hdr->sh_type;
-         continue;
+         if (! m->p_flags_valid)
+           p->p_flags |= PF_R;
+         p->p_offset = 0;
+         p->p_filesz = bed->s->sizeof_ehdr;
+         p->p_memsz = bed->s->sizeof_ehdr;
+         if (m->count > 0)
+           {
+             BFD_ASSERT (p->p_type == PT_LOAD);
+             p->p_vaddr -= off;
+             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;
+           }
        }
 
-      /* The section won't fit, start a new segment.  If we're already in one,
-        move to the next one.  */
-      if (phdr->p_type != PT_NULL)
+      if (m->includes_phdrs)
        {
-         ++phdr;
-         ++phdr_count;
+         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
+           {
+             p->p_offset = bed->s->sizeof_ehdr;
+             if (m->count > 0)
+               {
+                 BFD_ASSERT (p->p_type == PT_LOAD);
+                 p->p_vaddr -= off - p->p_offset;
+                 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;
+               }
+           }
+         p->p_filesz += alloc * bed->s->sizeof_phdr;
+         p->p_memsz += alloc * bed->s->sizeof_phdr;
        }
 
-      /* Initialize the segment.  */
-      phdr->p_type = PT_LOAD;
-      phdr->p_offset = hdr->sh_offset;
-      phdr->p_vaddr = hdr->sh_addr;
-      phdr->p_paddr = 0;
-      if (hdr->sh_type == SHT_NOBITS)
-       phdr->p_filesz = 0;
-      else
-       phdr->p_filesz = hdr->sh_size;
-      phdr->p_memsz = hdr->sh_size;
-      phdr->p_flags = PF_R;
-      if ((hdr->sh_flags & SHF_WRITE) != 0)
-       phdr->p_flags |= PF_W;
-      if ((hdr->sh_flags & SHF_EXECINSTR) != 0)
-       phdr->p_flags |= PF_X;
-      phdr->p_align = bed->maxpagesize;
-
-      if (hdr == first
-         && (bed->want_hdr_in_seg
-             || (sinterp != NULL
-                 && (sinterp->flags & SEC_LOAD) != 0)))
+      if (p->p_type == PT_LOAD)
        {
-         phdr->p_offset -= phdr_size + phdr_size_adjust;
-         phdr->p_vaddr -= phdr_size + phdr_size_adjust;
-         phdr->p_filesz += phdr_size + phdr_size_adjust;
-         phdr->p_memsz += phdr_size + phdr_size_adjust;
+         if (! m->includes_filehdr && ! m->includes_phdrs)
+           p->p_offset = off;
+         else
+           {
+             file_ptr adjust;
+
+             adjust = off - (p->p_offset + p->p_filesz);
+             p->p_filesz += adjust;
+             p->p_memsz += adjust;
+           }
        }
 
-      last_type = hdr->sh_type;
+      for (i = 0, secpp = m->sections; i < m->count; i++, secpp++)
+       {
+         asection *sec;
+         flagword flags;
+         bfd_size_type align;
+
+         sec = *secpp;
+         flags = sec->flags;
+
+         if (p->p_type == PT_LOAD)
+           {
+             bfd_vma adjust;
+
+             /* The section VMA must equal the file position modulo
+                 the page size.  */
+             if ((flags & SEC_LOAD) != 0)
+               {
+                 adjust = (sec->vma - off) % bed->maxpagesize;
+                 if (adjust != 0)
+                   {
+                     if (i == 0)
+                       abort ();
+                     p->p_memsz += adjust;
+                     if ((flags & SEC_LOAD) != 0)
+                       p->p_filesz += adjust;
+                     off += adjust;
+                   }
+               }
+
+             sec->filepos = off;
+
+             if ((flags & SEC_LOAD) != 0)
+               off += sec->_raw_size;
+           }
+
+         p->p_memsz += sec->_raw_size;
+
+         if ((flags & SEC_LOAD) != 0)
+           p->p_filesz += sec->_raw_size;
+
+         align = 1 << bfd_get_section_alignment (abfd, sec);
+         if (align > p->p_align)
+           p->p_align = align;
+
+         if (! m->p_flags_valid)
+           {
+             p->p_flags |= PF_R;
+             if ((flags & SEC_CODE) != 0)
+               p->p_flags |= PF_X;
+             if ((flags & SEC_READONLY) == 0)
+               p->p_flags |= PF_W;
+           }
+       }
     }
 
-  if (phdr->p_type != PT_NULL)
+  /* Now that we have set the section file positions, we can set up
+     the file positions for the non PT_LOAD segments.  */
+  for (m = elf_tdata (abfd)->segment_map, p = phdrs;
+       m != NULL;
+       m = m->next, p++)
     {
-      ++phdr;
-      ++phdr_count;
+      if (p->p_type != PT_LOAD && m->count > 0)
+       {
+         BFD_ASSERT (! m->includes_filehdr && ! m->includes_phdrs);
+         p->p_offset = m->sections[0]->filepos;
+       }
+      if (m->count == 0)
+       {
+         if (m->includes_filehdr)
+           {
+             p->p_vaddr = filehdr_vaddr;
+             if (! m->p_paddr_valid)
+               p->p_paddr = filehdr_paddr;
+           }
+         else if (m->includes_phdrs)
+           {
+             p->p_vaddr = phdrs_vaddr;
+             if (! m->p_paddr_valid)
+               p->p_paddr = phdrs_paddr;
+           }
+       }
     }
 
-  /* If we have a .dynamic section, create a PT_DYNAMIC segment.  */
-  sdyn = bfd_get_section_by_name (abfd, ".dynamic");
-  if (sdyn != NULL && (sdyn->flags & SEC_LOAD) != 0)
+  /* Clear out any program headers we allocated but did not use.  */
+  for (; count < alloc; count++, p++)
     {
-      phdr->p_type = PT_DYNAMIC;
-      phdr->p_offset = sdyn->filepos;
-      phdr->p_vaddr = sdyn->vma;
-      phdr->p_paddr = 0;
-      phdr->p_filesz = sdyn->_raw_size;
-      phdr->p_memsz = sdyn->_raw_size;
-      phdr->p_flags = PF_R;
-      if ((sdyn->flags & SEC_READONLY) == 0)
-       phdr->p_flags |= PF_W;
-      if ((sdyn->flags & SEC_CODE) != 0)
-       phdr->p_flags |= PF_X;
-      phdr->p_align = 1 << bfd_get_section_alignment (abfd, sdyn);
-
-      ++phdr;
-      ++phdr_count;
+      memset (p, 0, sizeof *p);
+      p->p_type = PT_NULL;
     }
 
-  /* Let the backend create additional program headers.  */
-  if (bed->elf_backend_create_program_headers)
-    phdr_count = (*bed->elf_backend_create_program_headers) (abfd,
-                                                            phdrs,
-                                                            phdr_count);
+  elf_tdata (abfd)->phdr = phdrs;
+
+  elf_tdata (abfd)->next_file_pos = off;
+
+  /* Write out the program headers.  */
+  if (bfd_seek (abfd, 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 (abfd)
+     bfd *abfd;
+{
+  size_t segs;
+  asection *s;
+  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;
 
-  /* Make sure the return value from get_program_header_size matches
-     what we computed here.  Actually, it's OK if we allocated too
-     much space in the program header.  */
-  if (phdr_count > phdr_size / bed->s->sizeof_phdr)
+  if (elf_tdata (abfd)->segment_map != NULL)
     {
-      ((*_bfd_error_handler)
-       ("%s: Not enough room for program headers (allocated %lu, need %u)",
-       bfd_get_filename (abfd),
-       (unsigned long) (phdr_size / bed->s->sizeof_phdr),
-       phdr_count));
-      bfd_set_error (bfd_error_bad_value);
-      return (file_ptr) -1;
+      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;
     }
 
-  /* Set up program header information.  */
-  i_ehdrp = elf_elfheader (abfd);
-  i_ehdrp->e_phentsize = bed->s->sizeof_phdr;
-  i_ehdrp->e_phoff = off;
-  i_ehdrp->e_phnum = phdr_count;
+  /* 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;
+    }
 
-  /* Save the program headers away.  I don't think anybody uses this
-     information right now.  */
-  elf_tdata (abfd)->phdr = ((Elf_Internal_Phdr *)
-                           bfd_alloc (abfd,
-                                      (phdr_count
-                                       * sizeof (Elf_Internal_Phdr))));
-  if (elf_tdata (abfd)->phdr == NULL && phdr_count != 0)
+  if (bfd_get_section_by_name (abfd, ".dynamic") != NULL)
     {
-      bfd_set_error (bfd_error_no_memory);
-      return (file_ptr) -1;
+      /* We need a PT_DYNAMIC segment.  */
+      ++segs;
     }
-  memcpy (elf_tdata (abfd)->phdr, phdrs,
-         phdr_count * sizeof (Elf_Internal_Phdr));
 
-  /* Write out the program headers.  */
-  if (bfd_seek (abfd, off, SEEK_SET) != 0)
-    return (file_ptr) -1;
+  /* Let the backend count up any program headers it might need.  */
+  if (bed->elf_backend_additional_program_headers)
+    {
+      int a;
 
-  if (bed->s->write_out_phdrs (abfd, phdrs, phdr_count) != 0)
-    return (file_ptr) -1;
+      a = (*bed->elf_backend_additional_program_headers) (abfd);
+      if (a == -1)
+       abort ();
+      segs += a;
+    }
 
-  return off + phdr_count * bed->s->sizeof_phdr;
+  elf_tdata (abfd)->program_header_size = segs * bed->s->sizeof_phdr;
+  return elf_tdata (abfd)->program_header_size;
 }
 
 /* Work out the file positions of all the sections.  This is called by
@@ -1837,15 +2186,11 @@ map_program_segments (abfd, off, first, sorted_hdrs, phdr_size)
    positions in assign_file_positions_for_relocs, which is called by
    write_object_contents and final_link.
 
-   If DOSYMS is false, we do not assign file positions for the symbol
-   table or the string table.  */
-
-static int elf_sort_hdrs PARAMS ((const PTR, const PTR));
+   We also don't set the positions of the .symtab and .strtab here.  */
 
 static boolean
-assign_file_positions_except_relocs (abfd, dosyms)
+assign_file_positions_except_relocs (abfd)
      bfd *abfd;
-     boolean dosyms;
 {
   struct elf_obj_tdata * const tdata = elf_tdata (abfd);
   Elf_Internal_Ehdr * const i_ehdrp = elf_elfheader (abfd);
@@ -1853,14 +2198,14 @@ assign_file_positions_except_relocs (abfd, dosyms)
   file_ptr off;
   struct elf_backend_data *bed = get_elf_backend_data (abfd);
 
-  /* Start after the ELF header.  */
-  off = i_ehdrp->e_ehsize;
-
   if ((abfd->flags & (EXEC_P | DYNAMIC)) == 0)
     {
       Elf_Internal_Shdr **hdrpp;
       unsigned int i;
 
+      /* Start after the ELF header.  */
+      off = i_ehdrp->e_ehsize;
+
       /* We are not creating an executable, which means that we are
         not creating a program header, and that the actual order of
         the sections in the file is unimportant.  */
@@ -1874,9 +2219,8 @@ assign_file_positions_except_relocs (abfd, dosyms)
              hdr->sh_offset = -1;
              continue;
            }
-         if (! dosyms
-             && (i == tdata->symtab_section
-                 || i == tdata->strtab_section))
+         if (i == tdata->symtab_section
+             || i == tdata->strtab_section)
            {
              hdr->sh_offset = -1;
              continue;
@@ -1887,97 +2231,45 @@ assign_file_positions_except_relocs (abfd, dosyms)
     }
   else
     {
-      file_ptr phdr_off;
-      bfd_size_type phdr_size;
-      bfd_vma maxpagesize;
-      size_t hdrppsize;
-      Elf_Internal_Shdr **sorted_hdrs;
-      Elf_Internal_Shdr **hdrpp;
       unsigned int i;
-      Elf_Internal_Shdr *first;
-      file_ptr phdr_map;
-
-      /* We are creating an executable.  */
-
-      maxpagesize = get_elf_backend_data (abfd)->maxpagesize;
-      if (maxpagesize == 0)
-       maxpagesize = 1;
-
-      /* We must sort the sections.  The GNU linker will always create
-        the sections in an appropriate order, but the Irix 5 linker
-        will not.  We don't include the dummy first section in the
-        sort.  We sort sections which are not SHF_ALLOC to the end.  */
-      hdrppsize = (i_ehdrp->e_shnum - 1) * sizeof (Elf_Internal_Shdr *);
-      sorted_hdrs = (Elf_Internal_Shdr **) malloc (hdrppsize);
-      if (sorted_hdrs == NULL)
-       {
-         bfd_set_error (bfd_error_no_memory);
-         return false;
-       }
+      Elf_Internal_Shdr **hdrpp;
 
-      memcpy (sorted_hdrs, i_shdrpp + 1, hdrppsize);
-      qsort (sorted_hdrs, (size_t) i_ehdrp->e_shnum - 1,
-            sizeof (Elf_Internal_Shdr *), elf_sort_hdrs);
-
-      /* We can't actually create the program header until we have set the
-        file positions for the sections, and we can't do that until we know
-        how big the header is going to be.  */
-      off = align_file_position (off, bed->s->file_align);
-      phdr_size = get_program_header_size (abfd,
-                                          sorted_hdrs, i_ehdrp->e_shnum - 1,
-                                          maxpagesize);
-      if (phdr_size == (bfd_size_type) -1)
+      /* Assign file positions for the loaded sections based on the
+         assignment of sections to segments.  */
+      if (! assign_file_positions_for_segments (abfd))
        return false;
 
-      /* Compute the file offsets of each section.  */
-      phdr_off = off;
-      off += phdr_size;
-      first = NULL;
-      for (i = 1, hdrpp = sorted_hdrs; i < i_ehdrp->e_shnum; i++, hdrpp++)
+      /* Assign file positions for the other sections.  */
+
+      off = elf_tdata (abfd)->next_file_pos;
+      for (i = 1, hdrpp = i_shdrpp + 1; i < i_ehdrp->e_shnum; i++, hdrpp++)
        {
          Elf_Internal_Shdr *hdr;
 
          hdr = *hdrpp;
-         if ((hdr->sh_flags & SHF_ALLOC) == 0)
-           {
-             if (hdr->sh_type == SHT_REL || hdr->sh_type == SHT_RELA)
-               {
-                 hdr->sh_offset = -1;
-                 continue;
-               }
-             if (! dosyms
-                 && (hdr == i_shdrpp[tdata->symtab_section]
-                     || hdr == i_shdrpp[tdata->strtab_section]))
-               {
-                 hdr->sh_offset = -1;
-                 continue;
-               }
-             off = _bfd_elf_assign_file_position_for_section (hdr, off,
-                                                              true);
-           }
-         else
+         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)
            {
-             if (first == NULL)
-               first = hdr;
-
-             /* The section VMA must equal the file position modulo
-                the page size.  This is required by the program
-                header.  */
-             off += (hdr->sh_addr - off) % maxpagesize;
+             ((*_bfd_error_handler)
+              ("%s: warning: allocated section `%s' not in segment",
+               bfd_get_filename (abfd),
+               (hdr->bfd_section == NULL
+                ? "*unknown*"
+                : hdr->bfd_section->name)));
+             off += (hdr->sh_addr - off) % bed->maxpagesize;
              off = _bfd_elf_assign_file_position_for_section (hdr, off,
                                                               false);
            }
-       }
-
-      /* Create the program header.  */
-      phdr_map = map_program_segments (abfd, phdr_off, first, sorted_hdrs,
-                                      phdr_size);
-      if (phdr_map == (file_ptr) -1)
-       return false;
-      BFD_ASSERT ((bfd_size_type) phdr_map
-                 <= (bfd_size_type) phdr_off + phdr_size);
-
-      free (sorted_hdrs);
+         else if (hdr->sh_type == SHT_REL
+                  || hdr->sh_type == SHT_RELA
+                  || hdr == i_shdrpp[tdata->symtab_section]
+                  || hdr == i_shdrpp[tdata->strtab_section])
+           hdr->sh_offset = -1;
+         else
+           off = _bfd_elf_assign_file_position_for_section (hdr, off, true);
+       }                  
     }
 
   /* Place the section headers.  */
@@ -1990,47 +2282,6 @@ assign_file_positions_except_relocs (abfd, dosyms)
   return true;
 }
 
-/* Sort the ELF headers by VMA.  We sort headers which are not
-   SHF_ALLOC to the end.  */
-static int
-elf_sort_hdrs (arg1, arg2)
-     const PTR arg1;
-     const PTR arg2;
-{
-  int ret;
-  const Elf_Internal_Shdr *hdr1 = *(const Elf_Internal_Shdr **) arg1;
-  const Elf_Internal_Shdr *hdr2 = *(const Elf_Internal_Shdr **) arg2;
-
-#define TOEND(x) (((x)->sh_flags & SHF_ALLOC)==0)
-
-  if (TOEND (hdr1))
-    if (TOEND (hdr2))
-      return 0;
-    else 
-      return 1;
-
-  if (TOEND (hdr2))
-    return -1;
-
-  if (hdr1->sh_addr < hdr2->sh_addr)
-    return -1;
-  else if (hdr1->sh_addr > hdr2->sh_addr)
-    return 1;
-
-  /* Put !SHT_NOBITS sections before SHT_NOBITS ones.
-     The main loop in map_program_segments requires this. */
-
-  ret = (hdr1->sh_type == SHT_NOBITS) - (hdr2->sh_type == SHT_NOBITS);
-
-  if (ret != 0)
-    return ret;
-  if (hdr1->sh_size < hdr2->sh_size)
-    return -1;
-  if (hdr1->sh_size > hdr2->sh_size)
-    return 1;
-  return 0;
-}
-
 static boolean
 prep_headers (abfd)
      bfd *abfd;
@@ -2058,7 +2309,7 @@ prep_headers (abfd)
 
   i_ehdrp->e_ident[EI_CLASS] = bed->s->elfclass;
   i_ehdrp->e_ident[EI_DATA] =
-    abfd->xvec->byteorder_big_p ? ELFDATA2MSB : ELFDATA2LSB;
+    bfd_big_endian (abfd) ? ELFDATA2MSB : ELFDATA2LSB;
   i_ehdrp->e_ident[EI_VERSION] = bed->s->ev_current;
 
   for (count = EI_PAD; count < EI_NIDENT; count++)
@@ -2325,6 +2576,207 @@ _bfd_elf_symbol_from_bfd_symbol (abfd, asym_ptr_ptr)
   return idx;
 }
 
+/* Copy private BFD data.  This copies any program header information.  */
+
+static boolean
+copy_private_bfd_data (ibfd, obfd)
+     bfd *ibfd;
+     bfd *obfd;
+{
+  Elf_Internal_Ehdr *iehdr;
+  struct elf_segment_map *mfirst;
+  struct elf_segment_map **pm;
+  Elf_Internal_Phdr *p;
+  unsigned int i, c;
+
+  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;
+
+  iehdr = elf_elfheader (ibfd);
+
+  mfirst = NULL;
+  pm = &mfirst;
+
+  c = elf_elfheader (ibfd)->e_phnum;
+  for (i = 0, p = elf_tdata (ibfd)->phdr; i < c; i++, p++)
+    {
+      unsigned int csecs;
+      asection *s;
+      struct elf_segment_map *m;
+      unsigned int isec;
+
+      csecs = 0;
+
+      /* The complicated case when p_vaddr is 0 is to handle the
+        Solaris linker, which generates a PT_INTERP section with
+        p_vaddr and p_memsz set to 0.  */
+      for (s = ibfd->sections; s != NULL; s = s->next)
+       if (((s->vma >= p->p_vaddr
+             && (s->vma + s->_raw_size <= p->p_vaddr + p->p_memsz
+                 || s->vma + s->_raw_size <= p->p_vaddr + p->p_filesz))
+            || (p->p_vaddr == 0
+                && p->p_filesz > 0
+                && (s->flags & SEC_HAS_CONTENTS) != 0
+                && (bfd_vma) s->filepos >= p->p_offset
+                && ((bfd_vma) s->filepos + s->_raw_size
+                    <= p->p_offset + p->p_filesz)))
+           && (s->flags & SEC_ALLOC) != 0
+           && s->output_section != NULL)
+         ++csecs;
+
+      m = ((struct elf_segment_map *)
+          bfd_alloc (obfd,
+                     (sizeof (struct elf_segment_map)
+                      + (csecs - 1) * sizeof (asection *))));
+      if (m == NULL)
+       return false;
+
+      m->next = NULL;
+      m->p_type = p->p_type;
+      m->p_flags = p->p_flags;
+      m->p_flags_valid = 1;
+      m->p_paddr = p->p_paddr;
+      m->p_paddr_valid = 1;
+
+      m->includes_filehdr = (p->p_offset == 0
+                            && p->p_filesz >= iehdr->e_ehsize);
+
+      m->includes_phdrs = (p->p_offset <= (bfd_vma) iehdr->e_phoff
+                          && (p->p_offset + p->p_filesz
+                              >= ((bfd_vma) iehdr->e_phoff
+                                  + iehdr->e_phnum * iehdr->e_phentsize)));
+
+      isec = 0;
+      for (s = ibfd->sections; s != NULL; s = s->next)
+       {
+         if (((s->vma >= p->p_vaddr
+               && (s->vma + s->_raw_size <= p->p_vaddr + p->p_memsz
+                   || s->vma + s->_raw_size <= p->p_vaddr + p->p_filesz))
+              || (p->p_vaddr == 0
+                  && p->p_filesz > 0
+                  && (s->flags & SEC_HAS_CONTENTS) != 0
+                  && (bfd_vma) s->filepos >= p->p_offset
+                  && ((bfd_vma) s->filepos + s->_raw_size
+                      <= p->p_offset + p->p_filesz)))
+             && (s->flags & SEC_ALLOC) != 0
+             && s->output_section != NULL)
+           {
+             m->sections[isec] = s->output_section;
+             ++isec;
+           }
+       }
+      BFD_ASSERT (isec == csecs);
+      m->count = csecs;
+
+      *pm = m;
+      pm = &m->next;
+    }
+
+  elf_tdata (obfd)->segment_map = mfirst;
+
+  return true;
+}
+
+/* Copy private section information.  This copies over the entsize
+   field, and sometimes the info field.  */
+
+boolean
+_bfd_elf_copy_private_section_data (ibfd, isec, obfd, osec)
+     bfd *ibfd;
+     asection *isec;
+     bfd *obfd;
+     asection *osec;
+{
+  Elf_Internal_Shdr *ihdr, *ohdr;
+
+  if (ibfd->xvec->flavour != bfd_target_elf_flavour
+      || obfd->xvec->flavour != bfd_target_elf_flavour)
+    return true;
+
+  /* Copy over private BFD data if it has not already been copied.
+     This must be done here, rather than in the copy_private_bfd_data
+     entry point, because the latter is called after the section
+     contents have been set, which means that the program headers have
+     already been worked out.  */
+  if (elf_tdata (obfd)->segment_map == NULL
+      && elf_tdata (ibfd)->phdr != NULL)
+    {
+      asection *s;
+
+      /* Only set up the segments when all the sections have been set
+         up.  */
+      for (s = ibfd->sections; s != NULL; s = s->next)
+       if (s->output_section == NULL)
+         break;
+      if (s == NULL)
+       {
+         if (! copy_private_bfd_data (ibfd, obfd))
+           return false;
+       }
+    }
+
+  ihdr = &elf_section_data (isec)->this_hdr;
+  ohdr = &elf_section_data (osec)->this_hdr;
+
+  ohdr->sh_entsize = ihdr->sh_entsize;
+
+  if (ihdr->sh_type == SHT_SYMTAB
+      || ihdr->sh_type == SHT_DYNSYM)
+    ohdr->sh_info = ihdr->sh_info;
+
+  return true;
+}
+
+/* Copy private symbol information.  If this symbol is in a section
+   which we did not map into a BFD section, try to map the section
+   index correctly.  We use special macro definitions for the mapped
+   section indices; these definitions are interpreted by the
+   swap_out_syms function.  */
+
+#define MAP_ONESYMTAB (SHN_LORESERVE - 1)
+#define MAP_DYNSYMTAB (SHN_LORESERVE - 2)
+#define MAP_STRTAB (SHN_LORESERVE - 3)
+#define MAP_SHSTRTAB (SHN_LORESERVE - 4)
+
+boolean
+_bfd_elf_copy_private_symbol_data (ibfd, isymarg, obfd, osymarg)
+     bfd *ibfd;
+     asymbol *isymarg;
+     bfd *obfd;
+     asymbol *osymarg;
+{
+  elf_symbol_type *isym, *osym;
+
+  isym = elf_symbol_from (ibfd, isymarg);
+  osym = elf_symbol_from (obfd, osymarg);
+
+  if (isym != NULL
+      && osym != NULL
+      && bfd_is_abs_section (isym->symbol.section))
+    {
+      unsigned int shndx;
+
+      shndx = isym->internal_elf_sym.st_shndx;
+      if (shndx == elf_onesymtab (ibfd))
+       shndx = MAP_ONESYMTAB;
+      else if (shndx == elf_dynsymtab (ibfd))
+       shndx = MAP_DYNSYMTAB;
+      else if (shndx == elf_tdata (ibfd)->strtab_section)
+       shndx = MAP_STRTAB;
+      else if (shndx == elf_tdata (ibfd)->shstrtab_section)
+       shndx = MAP_SHSTRTAB;
+      osym->internal_elf_sym.st_shndx = shndx;
+    }
+
+  return true;
+}
+
+/* Swap out the symbols.  */
+
 static boolean
 swap_out_syms (abfd, sttp)
      bfd *abfd;
@@ -2362,10 +2814,7 @@ swap_out_syms (abfd, sttp)
     outbound_syms = bfd_alloc (abfd,
                               (1 + symcount) * bed->s->sizeof_sym);
     if (outbound_syms == NULL)
-      {
-       bfd_set_error (bfd_error_no_memory);
-       return false;
-      }
+      return false;
     symtab_hdr->contents = (PTR) outbound_syms;
 
     /* now generate the data (for "contents") */
@@ -2429,21 +2878,56 @@ swap_out_syms (abfd, sttp)
            value += sec->vma;
            sym.st_value = value;
            sym.st_size = type_ptr ? type_ptr->internal_elf_sym.st_size : 0;
-           sym.st_shndx = shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
-           if (shndx == -1)
+
+           if (bfd_is_abs_section (sec)
+               && type_ptr != NULL
+               && type_ptr->internal_elf_sym.st_shndx != 0)
              {
-               asection *sec2;
-               /* Writing this would be a hell of a lot easier if we had
-                  some decent documentation on bfd, and knew what to expect
-                  of the library, and what to demand of applications.  For
-                  example, it appears that `objcopy' might not set the
-                  section of a symbol to be a section that is actually in
-                  the output file.  */
-               sec2 = bfd_get_section_by_name (abfd, sec->name);
-               BFD_ASSERT (sec2 != 0);
-               sym.st_shndx = shndx = _bfd_elf_section_from_bfd_section (abfd, sec2);
-               BFD_ASSERT (shndx != -1);
+               /* This symbol is in a real ELF section which we did
+                   not create as a BFD section.  Undo the mapping done
+                   by copy_private_symbol_data.  */
+               shndx = type_ptr->internal_elf_sym.st_shndx;
+               switch (shndx)
+                 {
+                 case MAP_ONESYMTAB:
+                   shndx = elf_onesymtab (abfd);
+                   break;
+                 case MAP_DYNSYMTAB:
+                   shndx = elf_dynsymtab (abfd);
+                   break;
+                 case MAP_STRTAB:
+                   shndx = elf_tdata (abfd)->strtab_section;
+                   break;
+                 case MAP_SHSTRTAB:
+                   shndx = elf_tdata (abfd)->shstrtab_section;
+                   break;
+                 default:
+                   break;
+                 }
+             }
+           else
+             {
+               shndx = _bfd_elf_section_from_bfd_section (abfd, sec);
+
+               if (shndx == -1)
+                 {
+                   asection *sec2;
+
+                   /* Writing this would be a hell of a lot easier if
+                      we had some decent documentation on bfd, and
+                      knew what to expect of the library, and what to
+                      demand of applications.  For example, it
+                      appears that `objcopy' might not set the
+                      section of a symbol to be a section that is
+                      actually in the output file.  */
+                   sec2 = bfd_get_section_by_name (abfd, sec->name);
+                   BFD_ASSERT (sec2 != 0);
+                   shndx = _bfd_elf_section_from_bfd_section (abfd, sec2);
+                   BFD_ASSERT (shndx != -1);
+                 }
              }
+
+           sym.st_shndx = shndx;
          }
 
        if (bfd_is_com_section (syms[idx]->section))
@@ -2597,10 +3081,7 @@ _bfd_elf_make_empty_symbol (abfd)
 
   newsym = (elf_symbol_type *) bfd_zalloc (abfd, sizeof (elf_symbol_type));
   if (!newsym)
-    {
-      bfd_set_error (bfd_error_no_memory);
-      return NULL;
-    }
+    return NULL;
   else
     {
       newsym->symbol.the_bfd = abfd;
@@ -2713,8 +3194,7 @@ _bfd_elf_sizeof_headers (abfd, reloc)
 
   ret = get_elf_backend_data (abfd)->s->sizeof_ehdr;
   if (! reloc)
-    ret += get_program_header_size (abfd, (Elf_Internal_Shdr **) NULL, 0,
-                                   (bfd_vma) 0);
+    ret += get_program_header_size (abfd);
   return ret;
 }
 
This page took 0.086467 seconds and 4 git commands to generate.