bfd/
[deliverable/binutils-gdb.git] / bfd / elf.c
index 05fa1877682a9e019c1fb45a40c08323dd0f159c..2594e21576b47cf0aa9101747ec6fa4da0831443 100644 (file)
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -206,16 +206,32 @@ bfd_elf_hash (const char *namearg)
   return h & 0xffffffff;
 }
 
+/* DT_GNU_HASH hash function.  Do not change this function; you will
+   cause invalid hash tables to be generated.  */
+
+unsigned long
+bfd_elf_gnu_hash (const char *namearg)
+{
+  const unsigned char *name = (const unsigned char *) namearg;
+  unsigned long h = 5381;
+  unsigned char ch;
+
+  while ((ch = *name++) != '\0')
+    h = (h << 5) + h + ch;
+  return h & 0xffffffff;
+}
+
 bfd_boolean
 bfd_elf_mkobject (bfd *abfd)
 {
-  /* This just does initialization.  */
-  /* coff_mkobject zalloc's space for tdata.coff_obj_data ...  */
-  elf_tdata (abfd) = bfd_zalloc (abfd, sizeof (struct elf_obj_tdata));
-  if (elf_tdata (abfd) == 0)
-    return FALSE;
-  /* Since everything is done at close time, do we need any
-     initialization?  */
+  if (abfd->tdata.any == NULL)
+    {
+      abfd->tdata.any = bfd_zalloc (abfd, sizeof (struct elf_obj_tdata));
+      if (abfd->tdata.any == NULL)
+       return FALSE;
+    }
+
+  elf_tdata (abfd)->program_header_size = (bfd_size_type) -1;
 
   return TRUE;
 }
@@ -384,7 +400,15 @@ bfd_elf_get_elf_syms (bfd *ibfd,
   for (esym = extsym_buf, isym = intsym_buf, shndx = extshndx_buf;
        isym < isymend;
        esym += extsym_size, isym++, shndx = shndx != NULL ? shndx + 1 : NULL)
-    (*bed->s->swap_symbol_in) (ibfd, esym, shndx, isym);
+    if (!(*bed->s->swap_symbol_in) (ibfd, esym, shndx, isym))
+      {
+       symoffset += (esym - (bfd_byte *) extsym_buf) / extsym_size;
+       (*_bfd_error_handler) (_("%B symbol number %lu references "
+                                "nonexistent SHT_SYMTAB_SHNDX section"),
+                              ibfd, (unsigned long) symoffset);
+       intsym_buf = NULL;
+       goto out;
+      }
 
  out:
   if (alloc_ext != NULL)
@@ -800,22 +824,22 @@ _bfd_elf_make_section_from_shdr (bfd *abfd,
          int len;
        } debug_sections [] =
        {
-         { "debug",             5  },  /* 'd' */
+         { STRING_COMMA_LEN ("debug") },       /* 'd' */
          { NULL,                0  },  /* 'e' */
          { NULL,                0  },  /* 'f' */
-         { "gnu.linkonce.wi.", 17 },   /* 'g' */
+         { STRING_COMMA_LEN ("gnu.linkonce.wi.") },    /* 'g' */
          { NULL,                0  },  /* 'h' */
          { NULL,                0  },  /* 'i' */
          { NULL,                0  },  /* 'j' */
          { NULL,                0  },  /* 'k' */
-         { "line",              4  },  /* 'l' */
+         { STRING_COMMA_LEN ("line") },        /* 'l' */
          { NULL,                0  },  /* 'm' */
          { NULL,                0  },  /* 'n' */
          { NULL,                0  },  /* 'o' */
          { NULL,                0  },  /* 'p' */
          { NULL,                0  },  /* 'q' */
          { NULL,                0  },  /* 'r' */
-         { "stab",              4  }   /* 's' */
+         { STRING_COMMA_LEN ("stab") } /* 's' */
        };
       
       if (name [0] == '.')
@@ -836,7 +860,7 @@ _bfd_elf_make_section_from_shdr (bfd *abfd,
      The symbols will be defined as weak, so that multiple definitions
      are permitted.  The GNU linker extension is to actually discard
      all but one of the sections.  */
-  if (strncmp (name, ".gnu.linkonce", sizeof ".gnu.linkonce" - 1) == 0
+  if (CONST_STRNEQ (name, ".gnu.linkonce")
       && elf_next_in_group (newsect) == NULL)
     flags |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD;
 
@@ -1239,6 +1263,7 @@ _bfd_elf_print_private_bfd_data (bfd *abfd, void *farg)
            case DT_AUXILIARY: name = "AUXILIARY"; stringp = TRUE; break;
            case DT_USED: name = "USED"; break;
            case DT_FILTER: name = "FILTER"; stringp = TRUE; break;
+           case DT_GNU_HASH: name = "GNU_HASH"; break;
            }
 
          fprintf (f, "  %-11s ", name);
@@ -1579,6 +1604,7 @@ _bfd_elf_link_hash_table_init
   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));
@@ -1642,7 +1668,7 @@ bfd_elf_get_dyn_lib_class (bfd *abfd)
 }
 
 void
-bfd_elf_set_dyn_lib_class (bfd *abfd, int lib_class)
+bfd_elf_set_dyn_lib_class (bfd *abfd, enum dynamic_lib_link_class lib_class)
 {
   if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
       && bfd_get_format (abfd) == bfd_object)
@@ -1822,6 +1848,7 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
     case SHT_FINI_ARRAY:       /* .fini_array section.  */
     case SHT_PREINIT_ARRAY:    /* .preinit_array section.  */
     case SHT_GNU_LIBLIST:      /* .gnu.liblist section.  */
+    case SHT_GNU_HASH:         /* .gnu.hash section.  */
       return _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex);
 
     case SHT_DYNAMIC:  /* Dynamic linking information.  */
@@ -2099,13 +2126,11 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
        abfd->flags |= HAS_RELOC;
        return TRUE;
       }
-      break;
 
     case SHT_GNU_verdef:
       elf_dynverdef (abfd) = shindex;
       elf_tdata (abfd)->dynverdef_hdr = *hdr;
       return _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex);
-      break;
 
     case SHT_GNU_versym:
       if (hdr->sh_entsize != sizeof (Elf_External_Versym))
@@ -2182,11 +2207,20 @@ bfd_section_from_shdr (bfd *abfd, unsigned int shindex)
             "`%s' [0x%8x]"),
           abfd, name, hdr->sh_type);
       else if (hdr->sh_type >= SHT_LOOS && hdr->sh_type <= SHT_HIOS)
-       /* FIXME: We should handle this section.  */
-       (*_bfd_error_handler)
-         (_("%B: don't know how to handle OS specific section "
-            "`%s' [0x%8x]"),
-          abfd, name, hdr->sh_type);
+       {
+         /* 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)
@@ -2253,108 +2287,111 @@ bfd_section_from_elf_index (bfd *abfd, unsigned int index)
 
 static const struct bfd_elf_special_section special_sections_b[] =
 {
-  { ".bss",            4, -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".bss"), -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE },
+  { NULL,                   0,  0, 0,            0 }
 };
 
 static const struct bfd_elf_special_section special_sections_c[] =
 {
-  { ".comment",        8,  0, SHT_PROGBITS, 0 },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".comment"), 0, SHT_PROGBITS, 0 },
+  { NULL,                       0, 0, 0,            0 }
 };
 
 static const struct bfd_elf_special_section special_sections_d[] =
 {
-  { ".data",           5, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
-  { ".data1",          6,  0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
-  { ".debug",          6,  0, SHT_PROGBITS, 0 },
-  { ".debug_line",    11,  0, SHT_PROGBITS, 0 },
-  { ".debug_info",    11,  0, SHT_PROGBITS, 0 },
-  { ".debug_abbrev",  13,  0, SHT_PROGBITS, 0 },
-  { ".debug_aranges", 14,  0, SHT_PROGBITS, 0 },
-  { ".dynamic",        8,  0, SHT_DYNAMIC,  SHF_ALLOC },
-  { ".dynstr",         7,  0, SHT_STRTAB,   SHF_ALLOC },
-  { ".dynsym",         7,  0, SHT_DYNSYM,   SHF_ALLOC },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".data"),         -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".data1"),         0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".debug"),         0, SHT_PROGBITS, 0 },
+  { STRING_COMMA_LEN (".debug_line"),    0, SHT_PROGBITS, 0 },
+  { STRING_COMMA_LEN (".debug_info"),    0, SHT_PROGBITS, 0 },
+  { STRING_COMMA_LEN (".debug_abbrev"),  0, SHT_PROGBITS, 0 },
+  { STRING_COMMA_LEN (".debug_aranges"), 0, SHT_PROGBITS, 0 },
+  { STRING_COMMA_LEN (".dynamic"),       0, SHT_DYNAMIC,  SHF_ALLOC },
+  { STRING_COMMA_LEN (".dynstr"),        0, SHT_STRTAB,   SHF_ALLOC },
+  { STRING_COMMA_LEN (".dynsym"),        0, SHT_DYNSYM,   SHF_ALLOC },
+  { NULL,                      0,        0, 0,            0 }
 };
 
 static const struct bfd_elf_special_section special_sections_f[] =
 {
-  { ".fini",           5,  0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { ".fini_array",    11,  0, SHT_FINI_ARRAY, SHF_ALLOC + SHF_WRITE },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".fini"),       0, SHT_PROGBITS,   SHF_ALLOC + SHF_EXECINSTR },
+  { STRING_COMMA_LEN (".fini_array"), 0, SHT_FINI_ARRAY, SHF_ALLOC + SHF_WRITE },
+  { NULL,                          0, 0, 0,              0 }
 };
 
 static const struct bfd_elf_special_section special_sections_g[] =
 {
-  { ".gnu.linkonce.b",15, -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE },
-  { ".got",            4,  0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
-  { ".gnu.version",   12,  0, SHT_GNU_versym, 0 },
-  { ".gnu.version_d", 14,  0, SHT_GNU_verdef, 0 },
-  { ".gnu.version_r", 14,  0, SHT_GNU_verneed, 0 },
-  { ".gnu.liblist",   12,  0, SHT_GNU_LIBLIST, SHF_ALLOC },
-  { ".gnu.conflict",  13,  0, SHT_RELA,     SHF_ALLOC },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".gnu.linkonce.b"), -2, SHT_NOBITS,      SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".got"),             0, SHT_PROGBITS,    SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".gnu.version"),     0, SHT_GNU_versym,  0 },
+  { STRING_COMMA_LEN (".gnu.version_d"),   0, SHT_GNU_verdef,  0 },
+  { STRING_COMMA_LEN (".gnu.version_r"),   0, SHT_GNU_verneed, 0 },
+  { STRING_COMMA_LEN (".gnu.liblist"),     0, SHT_GNU_LIBLIST, SHF_ALLOC },
+  { STRING_COMMA_LEN (".gnu.conflict"),    0, SHT_RELA,        SHF_ALLOC },
+  { STRING_COMMA_LEN (".gnu.hash"),        0, SHT_GNU_HASH,    SHF_ALLOC },
+  { NULL,                        0,        0, 0,               0 }
 };
 
 static const struct bfd_elf_special_section special_sections_h[] =
 {
-  { ".hash",           5,  0, SHT_HASH,     SHF_ALLOC },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".hash"), 0, SHT_HASH,     SHF_ALLOC },
+  { NULL,                    0, 0, 0,            0 }
 };
 
 static const struct bfd_elf_special_section special_sections_i[] =
 {
-  { ".init",           5,  0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { ".init_array",    11,  0, SHT_INIT_ARRAY, SHF_ALLOC + SHF_WRITE },
-  { ".interp",         7,  0, SHT_PROGBITS, 0 },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".init"),       0, SHT_PROGBITS,   SHF_ALLOC + SHF_EXECINSTR },
+  { STRING_COMMA_LEN (".init_array"), 0, SHT_INIT_ARRAY, SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".interp"),     0, SHT_PROGBITS,   0 },
+  { NULL,                      0,     0, 0,              0 }
 };
 
 static const struct bfd_elf_special_section special_sections_l[] =
 {
-  { ".line",           5,  0, SHT_PROGBITS, 0 },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".line"), 0, SHT_PROGBITS, 0 },
+  { NULL,                    0, 0, 0,            0 }
 };
 
 static const struct bfd_elf_special_section special_sections_n[] =
 {
-  { ".note.GNU-stack",15,  0, SHT_PROGBITS, 0 },
-  { ".note",           5, -1, SHT_NOTE,     0 },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".note.GNU-stack"), 0, SHT_PROGBITS, 0 },
+  { STRING_COMMA_LEN (".note"),          -1, SHT_NOTE,     0 },
+  { NULL,                    0,           0, 0,            0 }
 };
 
 static const struct bfd_elf_special_section special_sections_p[] =
 {
-  { ".preinit_array", 14,  0, SHT_PREINIT_ARRAY, SHF_ALLOC + SHF_WRITE },
-  { ".plt",            4,  0, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".preinit_array"), 0, SHT_PREINIT_ARRAY, SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".plt"),           0, SHT_PROGBITS,      SHF_ALLOC + SHF_EXECINSTR },
+  { NULL,                   0,           0, 0,                 0 }
 };
 
 static const struct bfd_elf_special_section special_sections_r[] =
 {
-  { ".rodata",         7, -2, SHT_PROGBITS, SHF_ALLOC },
-  { ".rodata1",        8,  0, SHT_PROGBITS, SHF_ALLOC },
-  { ".rela",           5, -1, SHT_RELA,     0 },
-  { ".rel",            4, -1, SHT_REL,      0 },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".rodata"), -2, SHT_PROGBITS, SHF_ALLOC },
+  { STRING_COMMA_LEN (".rodata1"), 0, SHT_PROGBITS, SHF_ALLOC },
+  { STRING_COMMA_LEN (".rela"),   -1, SHT_RELA,     0 },
+  { STRING_COMMA_LEN (".rel"),    -1, SHT_REL,      0 },
+  { NULL,                   0,     0, 0,            0 }
 };
 
 static const struct bfd_elf_special_section special_sections_s[] =
 {
-  { ".shstrtab",       9,  0, SHT_STRTAB,   0 },
-  { ".strtab",         7,  0, SHT_STRTAB,   0 },
-  { ".symtab",         7,  0, SHT_SYMTAB,   0 },
-  { ".stabstr",        5,  3, SHT_STRTAB,   0 },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".shstrtab"), 0, SHT_STRTAB, 0 },
+  { STRING_COMMA_LEN (".strtab"),   0, SHT_STRTAB, 0 },
+  { STRING_COMMA_LEN (".symtab"),   0, SHT_SYMTAB, 0 },
+  /* See struct bfd_elf_special_section declaration for the semantics of
+     this special case where .prefix_length != strlen (.prefix).  */
+  { ".stabstr",                        5,  3, SHT_STRTAB, 0 },
+  { NULL,                       0,  0, 0,          0 }
 };
 
 static const struct bfd_elf_special_section special_sections_t[] =
 {
-  { ".text",           5, -2, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
-  { ".tbss",           5, -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE + SHF_TLS },
-  { ".tdata",          6, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_TLS },
-  { NULL,              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".text"),  -2, SHT_PROGBITS, SHF_ALLOC + SHF_EXECINSTR },
+  { STRING_COMMA_LEN (".tbss"),  -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE + SHF_TLS },
+  { STRING_COMMA_LEN (".tdata"), -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE + SHF_TLS },
+  { NULL,                     0,  0, 0,            0 }
 };
 
 static const struct bfd_elf_special_section *special_sections[] =
@@ -2810,6 +2847,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)
@@ -3132,7 +3173,7 @@ assign_section_numbers (bfd *abfd, struct bfd_link_info *link_info)
                         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);
+                     kept = _bfd_elf_check_kept_section (s, link_info);
                      if (kept == NULL)
                        {
                          bfd_set_error (bfd_error_bad_value);
@@ -3205,7 +3246,7 @@ assign_section_numbers (bfd *abfd, struct bfd_link_info *link_info)
             string section.  We look for a section with the same name
             but without the trailing ``str'', and set its sh_link
             field to point to this section.  */
-         if (strncmp (sec->name, ".stab", sizeof ".stab" - 1) == 0
+         if (CONST_STRNEQ (sec->name, ".stab")
              && strcmp (sec->name + strlen (sec->name) - 3, "str") == 0)
            {
              size_t len;
@@ -3255,6 +3296,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.  */
@@ -3560,6 +3602,90 @@ _bfd_elf_compute_section_file_positions (bfd *abfd,
   return TRUE;
 }
 
+/* Make an initial estimate of the size of the program header.  If we
+   get the number wrong here, we'll redo section placement.  */
+
+static bfd_size_type
+get_program_header_size (bfd *abfd, struct bfd_link_info *info)
+{
+  size_t segs;
+  asection *s;
+  const struct elf_backend_data *bed;
+
+  /* Assume we will need exactly two PT_LOAD segments: one for text
+     and one for data.  */
+  segs = 2;
+
+  s = bfd_get_section_by_name (abfd, ".interp");
+  if (s != NULL && (s->flags & SEC_LOAD) != 0)
+    {
+      /* If we have a loadable interpreter section, we need a
+        PT_INTERP segment.  In this case, assume we also need a
+        PT_PHDR segment, although that may not be true for all
+        targets.  */
+      segs += 2;
+    }
+
+  if (bfd_get_section_by_name (abfd, ".dynamic") != NULL)
+    {
+      /* We need a PT_DYNAMIC segment.  */
+      ++segs;
+      
+      if (elf_tdata (abfd)->relro)
+       {
+         /* We need a PT_GNU_RELRO segment only when there is a
+            PT_DYNAMIC segment.  */
+         ++segs;
+       }
+    }
+
+  if (elf_tdata (abfd)->eh_frame_hdr)
+    {
+      /* We need a PT_GNU_EH_FRAME segment.  */
+      ++segs;
+    }
+
+  if (elf_tdata (abfd)->stack_flags)
+    {
+      /* We need a PT_GNU_STACK segment.  */
+      ++segs;
+    }
+
+  for (s = abfd->sections; s != NULL; s = s->next)
+    {
+      if ((s->flags & SEC_LOAD) != 0
+         && CONST_STRNEQ (s->name, ".note"))
+       {
+         /* We need a PT_NOTE segment.  */
+         ++segs;
+       }
+    }
+
+  for (s = abfd->sections; s != NULL; s = s->next)
+    {
+      if (s->flags & SEC_THREAD_LOCAL)
+       {
+         /* We need a PT_TLS segment.  */
+         ++segs;
+         break;
+       }
+    }
+
+  /* Let the backend count up any program headers it might need.  */
+  bed = get_elf_backend_data (abfd);
+  if (bed->elf_backend_additional_program_headers)
+    {
+      int a;
+
+      a = (*bed->elf_backend_additional_program_headers) (abfd, info);
+      if (a == -1)
+       abort ();
+      segs += a;
+    }
+
+  return segs * bed->s->sizeof_phdr;
+}
+
 /* Create a mapping from a set of sections to a program segment.  */
 
 static struct elf_segment_map *
@@ -3614,359 +3740,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;
 
-  sections = bfd_malloc2 (bfd_count_sections (abfd), sizeof (asection *));
-  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))
+      i = 0;
+      for (s = abfd->sections; s != NULL; s = s->next)
        {
-         /* 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;
+         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
+             && CONST_STRNEQ (s->name, ".note"))
+           {
+             amt = sizeof (struct elf_segment_map);
+             m = bfd_zalloc (abfd, amt);
+             if (m == NULL)
+               goto error_return;
+             m->next = NULL;
+             m->p_type = PT_NOTE;
+             m->count = 1;
+             m->sections[0] = s;
 
-      last_hdr = hdr;
-      /* .tbss sections effectively have zero size.  */
-      if ((hdr->flags & (SEC_THREAD_LOCAL | SEC_LOAD)) != SEC_THREAD_LOCAL)
-       last_size = hdr->size;
-      else
-       last_size = 0;
-      phdr_index = i;
-      phdr_in_segment = FALSE;
-    }
+             *pm = m;
+             pm = &m->next;
+           }
+         if (s->flags & SEC_THREAD_LOCAL)
+           {
+             if (! tls_count)
+               first_tls = s;
+             tls_count++;
+           }
+       }
 
-  /* Create a final PT_LOAD program segment.  */
-  if (last_hdr != NULL)
-    {
-      m = make_mapping (abfd, sections, phdr_index, i, phdr_in_segment);
-      if (m == NULL)
-       goto error_return;
+      /* If there are any SHF_TLS output sections, add PT_TLS segment.  */
+      if (tls_count > 0)
+       {
+         int i;
 
-      *pm = m;
-      pm = &m->next;
-    }
+         amt = sizeof (struct elf_segment_map);
+         amt += (tls_count - 1) * sizeof (asection *);
+         m = bfd_zalloc (abfd, amt);
+         if (m == NULL)
+           goto error_return;
+         m->next = NULL;
+         m->p_type = PT_TLS;
+         m->count = tls_count;
+         /* Mandated PF_R.  */
+         m->p_flags = PF_R;
+         m->p_flags_valid = 1;
+         for (i = 0; i < tls_count; ++i)
+           {
+             BFD_ASSERT (first_tls->flags & SEC_THREAD_LOCAL);
+             m->sections[i] = first_tls;
+             first_tls = first_tls->next;
+           }
 
-  /* If there is a .dynamic section, throw in a PT_DYNAMIC segment.  */
-  if (dynsec != NULL)
-    {
-      m = _bfd_elf_make_dynamic_segment (abfd, dynsec);
-      if (m == NULL)
-       goto error_return;
-      *pm = m;
-      pm = &m->next;
-    }
+         *pm = m;
+         pm = &m->next;
+       }
 
-  /* For each loadable .note section, add a PT_NOTE segment.  We don't
-     use bfd_get_section_by_name, because if we link together
-     nonloadable .note sections and loadable .note sections, we will
-     generate two .note sections in the output file.  FIXME: Using
-     names for section types is bogus anyhow.  */
-  for (s = abfd->sections; s != NULL; s = s->next)
-    {
-      if ((s->flags & SEC_LOAD) != 0
-         && strncmp (s->name, ".note", 5) == 0)
+      /* If there is a .eh_frame_hdr section, throw in a PT_GNU_EH_FRAME
+        segment.  */
+      eh_frame_hdr = elf_tdata (abfd)->eh_frame_hdr;
+      if (eh_frame_hdr != NULL
+         && (eh_frame_hdr->output_section->flags & SEC_LOAD) != 0)
        {
          amt = sizeof (struct elf_segment_map);
          m = bfd_zalloc (abfd, amt);
          if (m == NULL)
            goto error_return;
          m->next = NULL;
-         m->p_type = PT_NOTE;
+         m->p_type = PT_GNU_EH_FRAME;
          m->count = 1;
-         m->sections[0] = s;
+         m->sections[0] = eh_frame_hdr->output_section;
 
          *pm = m;
          pm = &m->next;
        }
-      if (s->flags & SEC_THREAD_LOCAL)
-       {
-         if (! tls_count)
-           first_tls = s;
-         tls_count++;
-       }
-    }
-
-  /* If there are any SHF_TLS output sections, add PT_TLS segment.  */
-  if (tls_count > 0)
-    {
-      int i;
 
-      amt = sizeof (struct elf_segment_map);
-      amt += (tls_count - 1) * sizeof (asection *);
-      m = bfd_zalloc (abfd, amt);
-      if (m == NULL)
-       goto error_return;
-      m->next = NULL;
-      m->p_type = PT_TLS;
-      m->count = tls_count;
-      /* Mandated PF_R.  */
-      m->p_flags = PF_R;
-      m->p_flags_valid = 1;
-      for (i = 0; i < tls_count; ++i)
+      if (elf_tdata (abfd)->stack_flags)
        {
-         BFD_ASSERT (first_tls->flags & SEC_THREAD_LOCAL);
-         m->sections[i] = first_tls;
-         first_tls = first_tls->next;
-       }
-
-      *pm = m;
-      pm = &m->next;
-    }
+         amt = sizeof (struct elf_segment_map);
+         m = bfd_zalloc (abfd, amt);
+         if (m == NULL)
+           goto error_return;
+         m->next = NULL;
+         m->p_type = PT_GNU_STACK;
+         m->p_flags = elf_tdata (abfd)->stack_flags;
+         m->p_flags_valid = 1;
 
-  /* If there is a .eh_frame_hdr section, throw in a PT_GNU_EH_FRAME
-     segment.  */
-  eh_frame_hdr = elf_tdata (abfd)->eh_frame_hdr;
-  if (eh_frame_hdr != NULL
-      && (eh_frame_hdr->output_section->flags & SEC_LOAD) != 0)
-    {
-      amt = sizeof (struct elf_segment_map);
-      m = bfd_zalloc (abfd, amt);
-      if (m == NULL)
-       goto error_return;
-      m->next = NULL;
-      m->p_type = PT_GNU_EH_FRAME;
-      m->count = 1;
-      m->sections[0] = eh_frame_hdr->output_section;
+         *pm = m;
+         pm = &m->next;
+       }
 
-      *pm = m;
-      pm = &m->next;
-    }
+      if (dynsec != NULL && elf_tdata (abfd)->relro)
+       {
+         /* We make a PT_GNU_RELRO segment only when there is a
+            PT_DYNAMIC segment.  */
+         amt = sizeof (struct elf_segment_map);
+         m = bfd_zalloc (abfd, amt);
+         if (m == NULL)
+           goto error_return;
+         m->next = NULL;
+         m->p_type = PT_GNU_RELRO;
+         m->p_flags = PF_R;
+         m->p_flags_valid = 1;
 
-  if (elf_tdata (abfd)->stack_flags)
-    {
-      amt = sizeof (struct elf_segment_map);
-      m = bfd_zalloc (abfd, amt);
-      if (m == NULL)
-       goto error_return;
-      m->next = NULL;
-      m->p_type = PT_GNU_STACK;
-      m->p_flags = elf_tdata (abfd)->stack_flags;
-      m->p_flags_valid = 1;
+         *pm = m;
+         pm = &m->next;
+       }
 
-      *pm = m;
-      pm = &m->next;
+      free (sections);
+      elf_tdata (abfd)->segment_map = mfirst;
     }
 
-  if (elf_tdata (abfd)->relro)
-    {
-      amt = sizeof (struct elf_segment_map);
-      m = bfd_zalloc (abfd, amt);
-      if (m == NULL)
-       goto error_return;
-      m->next = NULL;
-      m->p_type = PT_GNU_RELRO;
-      m->p_flags = PF_R;
-      m->p_flags_valid = 1;
-
-      *pm = m;
-      pm = &m->next;
-    }
+  if (!elf_modify_segment_map (abfd, info))
+    return FALSE;
 
-  free (sections);
-  sections = NULL;
+  for (count = 0, m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
+    ++count;
+  elf_tdata (abfd)->program_header_size = count * bed->s->sizeof_phdr;
 
-  elf_tdata (abfd)->segment_map = mfirst;
   return TRUE;
 
  error_return:
@@ -4062,42 +4242,6 @@ vma_page_aligned_bias (bfd_vma vma, ufile_ptr off, bfd_vma maxpagesize)
   return ((vma - off) % maxpagesize);
 }
 
-static void
-print_segment_map (bfd *abfd)
-{
-  struct elf_segment_map *m;
-  unsigned int i, j;
-
-  fprintf (stderr, _(" Section to Segment mapping:\n"));
-  fprintf (stderr, _("  Segment              Sections...\n"));
-
-  for (i= 0, m = elf_tdata (abfd)->segment_map;
-       m != NULL;
-       i++, m = m->next)
-    {
-      const char *pt = get_segment_type (m->p_type);
-      char buf[32];
-
-      if (pt == NULL)
-       {
-         if (m->p_type >= PT_LOPROC && m->p_type <= PT_HIPROC)
-           sprintf (buf, "LOPROC+%7.7x",
-                    (unsigned int) (m->p_type - PT_LOPROC));
-         else if (m->p_type >= PT_LOOS && m->p_type <= PT_HIOS)
-           sprintf (buf, "LOOS+%7.7x",
-                    (unsigned int) (m->p_type - PT_LOOS));
-         else
-           snprintf (buf, sizeof (buf), "%8.8x",
-                     (unsigned int) m->p_type);
-         pt = buf;
-       }
-      fprintf (stderr, "  %2.2d: %14.14s:  ", i, pt);
-      for (j = 0; j < m->count; j++)
-       fprintf (stderr, "%s ", m->sections [j]->name);
-      putc ('\n',stderr);
-    }
-}
-
 /* 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.  */
@@ -4112,84 +4256,31 @@ assign_file_positions_for_load_sections (bfd *abfd,
   Elf_Internal_Phdr *p;
   file_ptr off, voff;
   bfd_size_type maxpagesize;
-  unsigned int count;
   unsigned int alloc;
   unsigned int i;
 
-  if (elf_tdata (abfd)->segment_map == NULL)
-    {
-      if (! map_sections_to_segments (abfd))
-       return FALSE;
-    }
-  else
-    {
-      /* The placement algorithm assumes that non allocated sections are
-        not in PT_LOAD segments.  We ensure this here by removing such
-        sections from the segment map.  We also remove excluded
-        sections.  */
-      for (m = elf_tdata (abfd)->segment_map;
-          m != NULL;
-          m = m->next)
-       {
-         unsigned int new_count;
-
-         new_count = 0;
-         for (i = 0; i < m->count; i ++)
-           {
-             if ((m->sections[i]->flags & SEC_EXCLUDE) == 0
-                 && ((m->sections[i]->flags & SEC_ALLOC) != 0
-                     || m->p_type != PT_LOAD))
-               {
-                 if (i != new_count)
-                   m->sections[new_count] = m->sections[i];
-
-                 new_count ++;
-               }
-           }
-
-         if (new_count != m->count)
-           m->count = new_count;
-       }
-    }
-
-  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;
-
-  if (count == 0)
-    {
-      elf_tdata (abfd)->next_file_pos = bed->s->sizeof_ehdr;
-      return TRUE;
-    }
+  elf_elfheader (abfd)->e_phnum = alloc;
 
-  /* 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));
-      print_segment_map (abfd);
-      bfd_set_error (bfd_error_bad_value);
-      return FALSE;
-    }
+  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 (alloc == 0)
     {
-      alloc = count;
-      elf_tdata (abfd)->program_header_size = alloc * bed->s->sizeof_phdr;
+      elf_tdata (abfd)->next_file_pos = bed->s->sizeof_ehdr;
+      return TRUE;
     }
 
   phdrs = bfd_alloc2 (abfd, alloc, sizeof (Elf_Internal_Phdr));
@@ -4299,7 +4390,8 @@ assign_file_positions_for_load_sections (bfd *abfd,
                 .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)
@@ -4468,7 +4560,7 @@ assign_file_positions_for_load_sections (bfd *abfd,
            {
              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
@@ -4510,9 +4602,11 @@ assign_file_positions_for_load_sections (bfd *abfd,
                    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;
            }
 
@@ -4527,13 +4621,6 @@ assign_file_positions_for_load_sections (bfd *abfd,
        }
     }
 
-  /* 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)->next_file_pos = off;
   return TRUE;
 }
@@ -4567,17 +4654,21 @@ assign_file_positions_for_non_load_sections (bfd *abfd,
 
       hdr = *hdrpp;
       if (hdr->bfd_section != NULL
-         && hdr->bfd_section->filepos != 0)
+         && (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)
+         if (hdr->sh_size != 0)
+           ((*_bfd_error_handler)
+            (_("%B: warning: allocated section `%s' not in segment"),
+             abfd,
+             (hdr->bfd_section == NULL
+              ? "*unknown*"
+              : hdr->bfd_section->name)));
+         /* We don't need to page align empty sections.  */
+         if ((abfd->flags & D_PAGED) != 0 && hdr->sh_size != 0)
            off += vma_page_aligned_bias (hdr->sh_addr, off,
                                          bed->maxpagesize);
          else
@@ -4679,8 +4770,8 @@ assign_file_positions_for_non_load_sections (bfd *abfd,
                  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;
                }
 
@@ -4709,112 +4800,6 @@ assign_file_positions_for_non_load_sections (bfd *abfd,
   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, struct bfd_link_info *info)
-{
-  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;
-       }
-    }
-
-  /* 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, info);
-      if (a == -1)
-       abort ();
-      segs += a;
-    }
-
-  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
    _bfd_elf_compute_section_file_positions.  All the section sizes and
    VMAs must be known before this is called.
@@ -4888,6 +4873,12 @@ assign_file_positions_except_relocs (bfd *abfd,
       if (!assign_file_positions_for_non_load_sections (abfd, link_info))
        return FALSE;
 
+      if (bed->elf_backend_modify_program_headers != NULL)
+       {
+         if (!(*bed->elf_backend_modify_program_headers) (abfd, link_info))
+           return FALSE;
+       }
+
       /* 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
@@ -5410,7 +5401,7 @@ rewrite_elf_program_header (bfd *ibfd, bfd *obfd)
         all of the sections we have selected.  */
       amt = sizeof (struct elf_segment_map);
       amt += ((bfd_size_type) section_count - 1) * sizeof (asection *);
-      map = bfd_alloc (obfd, amt);
+      map = bfd_zalloc (obfd, amt);
       if (map == NULL)
        return FALSE;
 
@@ -5805,7 +5796,7 @@ copy_elf_program_header (bfd *ibfd, bfd *obfd)
       amt = sizeof (struct elf_segment_map);
       if (section_count != 0)
        amt += ((bfd_size_type) section_count - 1) * sizeof (asection *);
-      map = bfd_alloc (obfd, amt);
+      map = bfd_zalloc (obfd, amt);
       if (map == NULL)
        return FALSE;
 
@@ -5959,9 +5950,17 @@ _bfd_elf_init_private_section_data (bfd *ibfd,
      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);
+  if (osec->flags == isec->flags || !osec->flags)
+    {
+      BFD_ASSERT (osec->flags == isec->flags 
+                 || (!osec->flags
+                     && elf_section_type (osec) == SHT_NULL));
+      elf_section_type (osec) = elf_section_type (isec);
+    }
+
+  /* FIXME: Is this correct for all OS/PROC specific flags?  */
+  elf_section_flags (osec) |= (elf_section_flags (isec)
+                              & (SHF_MASKOS | SHF_MASKPROC));
 
   /* Set things up for objcopy and relocatable link.  The output
      SHT_GROUP section will have its elf_next_in_group pointing back
@@ -6031,6 +6030,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;
@@ -6046,6 +6047,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;
 }
 
@@ -7118,11 +7140,29 @@ _bfd_elf_find_inliner_info (bfd *abfd,
 int
 _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;
 
-  ret = get_elf_backend_data (abfd)->s->sizeof_ehdr;
   if (!info->relocatable)
-    ret += get_program_header_size (abfd, info);
+    {
+      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;
+    }
+
   return ret;
 }
 
@@ -8080,7 +8120,7 @@ elfcore_write_note (bfd  *abfd,
 
       namesz = strlen (name) + 1;
       bed = get_elf_backend_data (abfd);
-      pad = -namesz & ((1 << bed->s->log_file_align) - 1);
+      pad = -namesz & 3;
     }
 
   newspace = 12 + namesz + pad + size;
@@ -8269,12 +8309,12 @@ elfcore_read_notes (bfd *abfd, file_ptr offset, bfd_size_type size)
       in.descdata = in.namedata + BFD_ALIGN (in.namesz, 4);
       in.descpos = offset + (in.descdata - buf);
 
-      if (strncmp (in.namedata, "NetBSD-CORE", 11) == 0)
+      if (CONST_STRNEQ (in.namedata, "NetBSD-CORE"))
         {
           if (! elfcore_grok_netbsd_note (abfd, &in))
             goto error;
         }
-      else if (strncmp (in.namedata, "QNX", 3) == 0)
+      else if (CONST_STRNEQ (in.namedata, "QNX"))
        {
          if (! elfcore_grok_nto_note (abfd, &in))
            goto error;
@@ -8634,7 +8674,8 @@ elf_sym_name_compare (const void *arg1, const void *arg2)
    symbols.  */
 
 bfd_boolean
-bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2)
+bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2,
+                                  struct bfd_link_info *info)
 {
   bfd *bfd1, *bfd2;
   const struct elf_backend_data *bed1, *bed2;
@@ -8653,10 +8694,8 @@ bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2)
 
   /* If both are .gnu.linkonce sections, they have to have the same
      section name.  */
-  if (strncmp (sec1->name, ".gnu.linkonce",
-              sizeof ".gnu.linkonce" - 1) == 0
-      && strncmp (sec2->name, ".gnu.linkonce",
-                 sizeof ".gnu.linkonce" - 1) == 0)
+  if (CONST_STRNEQ (sec1->name, ".gnu.linkonce")
+      && CONST_STRNEQ (sec2->name, ".gnu.linkonce"))
     return strcmp (sec1->name + sizeof ".gnu.linkonce",
                   sec2->name + sizeof ".gnu.linkonce") == 0;
 
@@ -8692,21 +8731,37 @@ bfd_elf_match_symbols_in_sections (asection *sec1, asection *sec2)
   if (symcount1 == 0 || symcount2 == 0)
     return FALSE;
 
-  isymbuf1 = bfd_elf_get_elf_syms (bfd1, hdr1, symcount1, 0,
-                                  NULL, NULL, NULL);
-  isymbuf2 = bfd_elf_get_elf_syms (bfd2, hdr2, symcount2, 0,
-                                  NULL, NULL, NULL);
-
   result = FALSE;
-  if (isymbuf1 == NULL || isymbuf2 == NULL)
-    goto done;
+  isymbuf1 = elf_tdata (bfd1)->symbuf;
+  isymbuf2 = elf_tdata (bfd2)->symbuf;
 
-  /* Sort symbols by binding and section. Global definitions are at
-     the beginning.  */
-  qsort (isymbuf1, symcount1, sizeof (Elf_Internal_Sym),
-        elf_sort_elf_symbol);
-  qsort (isymbuf2, symcount2, sizeof (Elf_Internal_Sym),
-        elf_sort_elf_symbol);
+  if (isymbuf1 == NULL)
+    {
+      isymbuf1 = bfd_elf_get_elf_syms (bfd1, hdr1, symcount1, 0,
+                                      NULL, NULL, NULL);
+      if (isymbuf1 == NULL)
+       goto done;
+      /* Sort symbols by binding and section. Global definitions are at
+        the beginning.  */
+      qsort (isymbuf1, symcount1, sizeof (Elf_Internal_Sym),
+            elf_sort_elf_symbol);
+      if (!info->reduce_memory_overheads)
+       elf_tdata (bfd1)->symbuf = isymbuf1;
+    }
+
+  if (isymbuf2 == NULL)
+    {
+      isymbuf2 = bfd_elf_get_elf_syms (bfd2, hdr2, symcount2, 0,
+                                      NULL, NULL, NULL);
+      if (isymbuf2 == NULL)
+       goto done;
+      /* Sort symbols by binding and section. Global definitions are at
+        the beginning.  */
+      qsort (isymbuf2, symcount2, sizeof (Elf_Internal_Sym),
+            elf_sort_elf_symbol);
+      if (!info->reduce_memory_overheads)
+       elf_tdata (bfd2)->symbuf = isymbuf2;
+    }
 
   /* Count definitions in the section.  */
   count1 = 0;
@@ -8790,10 +8845,13 @@ done:
     free (symtable1);
   if (symtable2)
     free (symtable2);
-  if (isymbuf1)
-    free (isymbuf1);
-  if (isymbuf2)
-    free (isymbuf2);
+  if (info->reduce_memory_overheads)
+    {
+      if (isymbuf1)
+       free (isymbuf1);
+      if (isymbuf2)
+       free (isymbuf2);
+    }
 
   return result;
 }
This page took 0.045873 seconds and 4 git commands to generate.