* elf-bfd.h (struct elf_obj_tdata): Add segment_map field.
authorIan Lance Taylor <ian@airs.com>
Tue, 28 Nov 1995 22:02:13 +0000 (22:02 +0000)
committerIan Lance Taylor <ian@airs.com>
Tue, 28 Nov 1995 22:02:13 +0000 (22:02 +0000)
* elf.c (make_mapping): New static function.
(map_sections_to_segments): New static function.
(elf_sort_sections): New static function.
(assign_file_positions_for_segments): New static function.
(map_program_segments): Remove.
(get_program_header_size): Remove sorted_hdrs, count, and
maxpagesize parameters.  Simplify.
(assign_file_positions_except_relocs): When generating an
executable, use assign_file_positions_for_segments.
(elf_sort_hdrs): Remove.
(_bfd_elf_sizeof_headers): Remove eliminated parameters from call
to get_program_header_size.

bfd/ChangeLog
bfd/elf-bfd.h [new file with mode: 0644]
bfd/elf.c

index 85f39df729bb14f61a40e6a1e136f66e2c4de8f4..18e09b5b0ac963a3d608fcccecee4c6f5dff315a 100644 (file)
@@ -1,3 +1,19 @@
+Tue Nov 28 16:59:50 1995  Ian Lance Taylor  <ian@cygnus.com>
+
+       * elf-bfd.h (struct elf_obj_tdata): Add segment_map field.
+       * elf.c (make_mapping): New static function.
+       (map_sections_to_segments): New static function.
+       (elf_sort_sections): New static function.
+       (assign_file_positions_for_segments): New static function.
+       (map_program_segments): Remove.
+       (get_program_header_size): Remove sorted_hdrs, count, and
+       maxpagesize parameters.  Simplify.
+       (assign_file_positions_except_relocs): When generating an
+       executable, use assign_file_positions_for_segments.
+       (elf_sort_hdrs): Remove.
+       (_bfd_elf_sizeof_headers): Remove eliminated parameters from call
+       to get_program_header_size.
+
 Mon Nov 27 12:27:46 1995  Ian Lance Taylor  <ian@cygnus.com>
 
        * hp300hpux.c (MY(callback)): Set lma as well as vma.
diff --git a/bfd/elf-bfd.h b/bfd/elf-bfd.h
new file mode 100644 (file)
index 0000000..e7baaa9
--- /dev/null
@@ -0,0 +1,726 @@
+/* BFD back-end data structures for ELF files.
+   Copyright (C) 1992, 1993 Free Software Foundation, Inc.
+   Written by Cygnus Support.
+
+This file is part of BFD, the Binary File Descriptor library.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
+
+#ifndef _LIBELF_H_
+#define _LIBELF_H_ 1
+
+#include "elf/common.h"
+#include "elf/internal.h"
+#include "elf/external.h"
+#include "bfdlink.h"
+
+/* If size isn't specified as 64 or 32, NAME macro should fail.  */
+#ifndef NAME
+#if ARCH_SIZE==64
+#define NAME(x,y) CAT4(x,64,_,y)
+#endif
+#if ARCH_SIZE==32
+#define NAME(x,y) CAT4(x,32,_,y)
+#endif
+#endif
+
+#ifndef NAME
+#define NAME(x,y) CAT4(x,NOSIZE,_,y)
+#endif
+
+#define ElfNAME(X)     NAME(Elf,X)
+#define elfNAME(X)     NAME(elf,X)
+
+/* Information held for an ELF symbol.  The first field is the
+   corresponding asymbol.  Every symbol is an ELF file is actually a
+   pointer to this structure, although it is often handled as a
+   pointer to an asymbol.  */
+
+typedef struct
+{
+  /* The BFD symbol.  */
+  asymbol symbol;
+  /* ELF symbol information.  */
+  Elf_Internal_Sym internal_elf_sym;
+  /* Backend specific information.  */
+  union
+    {
+      unsigned int hppa_arg_reloc;
+      PTR mips_extr;
+      PTR any;
+    }
+  tc_data;
+} elf_symbol_type;
+\f
+/* ELF linker hash table entries.  */
+
+struct elf_link_hash_entry
+{
+  struct bfd_link_hash_entry root;
+
+  /* Symbol index in output file.  This is initialized to -1.  It is
+     set to -2 if the symbol is used by a reloc.  */
+  long indx;
+
+  /* Symbol size.  */
+  bfd_size_type size;
+
+  /* Symbol index as a dynamic symbol.  Initialized to -1, and remains
+     -1 if this is not a dynamic symbol.  */
+  long dynindx;
+
+  /* String table index in .dynstr if this is a dynamic symbol.  */
+  unsigned long dynstr_index;
+
+  /* If this is a weak defined symbol from a dynamic object, this
+     field points to a defined symbol with the same value, if there is
+     one.  Otherwise it is NULL.  */
+  struct elf_link_hash_entry *weakdef;
+
+  /* If this symbol requires an entry in the global offset table, the
+     processor specific backend uses this field to hold the offset
+     into the .got section.  If this field is -1, then the symbol does
+     not require a global offset table entry.  */
+  bfd_vma got_offset;
+
+  /* If this symbol requires an entry in the procedure linkage table,
+     the processor specific backend uses these two fields to hold the
+     offset into the procedure linkage section and the offset into the
+     .got section.  If plt_offset is -1, then the symbol does not
+     require an entry in the procedure linkage table.  */
+  bfd_vma plt_offset;
+
+  /* Symbol type (STT_NOTYPE, STT_OBJECT, etc.).  */
+  char type;
+
+  /* Some flags; legal values follow.  */
+  unsigned char elf_link_hash_flags;
+  /* Symbol is referenced by a non-shared object.  */
+#define ELF_LINK_HASH_REF_REGULAR 01
+  /* Symbol is defined by a non-shared object.  */
+#define ELF_LINK_HASH_DEF_REGULAR 02
+  /* Symbol is referenced by a shared object.  */
+#define ELF_LINK_HASH_REF_DYNAMIC 04
+  /* Symbol is defined by a shared object.  */
+#define ELF_LINK_HASH_DEF_DYNAMIC 010
+  /* Dynamic symbol has been adjustd.  */
+#define ELF_LINK_HASH_DYNAMIC_ADJUSTED 020
+  /* Symbol needs a copy reloc.  */
+#define ELF_LINK_HASH_NEEDS_COPY 040
+  /* Symbol needs a procedure linkage table entry.  */
+#define ELF_LINK_HASH_NEEDS_PLT 0100
+};
+
+/* ELF linker hash table.  */
+
+struct elf_link_hash_table
+{
+  struct bfd_link_hash_table root;
+  /* Whether we have created the special dynamic sections required
+     when linking against or generating a shared object.  */
+  boolean dynamic_sections_created;
+  /* The BFD used to hold special sections created by the linker.
+     This will be the first BFD found which requires these sections to
+     be created.  */
+  bfd *dynobj;
+  /* The number of symbols found in the link which must be put into
+     the .dynsym section.  */
+  bfd_size_type dynsymcount;
+  /* The string table of dynamic symbols, which becomes the .dynstr
+     section.  */
+  struct bfd_strtab_hash *dynstr;
+  /* The number of buckets in the hash table in the .hash section.
+     This is based on the number of dynamic symbols.  */
+  bfd_size_type bucketcount;
+  /* A linked list of DT_NEEDED names found in dynamic objects
+     included in the link.  */
+  struct bfd_link_needed_list *needed;
+};
+
+/* Look up an entry in an ELF linker hash table.  */
+
+#define elf_link_hash_lookup(table, string, create, copy, follow)      \
+  ((struct elf_link_hash_entry *)                                      \
+   bfd_link_hash_lookup (&(table)->root, (string), (create),           \
+                        (copy), (follow)))
+
+/* Traverse an ELF linker hash table.  */
+
+#define elf_link_hash_traverse(table, func, info)                      \
+  (bfd_link_hash_traverse                                              \
+   (&(table)->root,                                                    \
+    (boolean (*) PARAMS ((struct bfd_link_hash_entry *, PTR))) (func), \
+    (info)))
+
+/* Get the ELF linker hash table from a link_info structure.  */
+
+#define elf_hash_table(p) ((struct elf_link_hash_table *) ((p)->hash))
+\f
+/* Constant information held for an ELF backend.  */
+
+struct elf_size_info {
+  unsigned char sizeof_ehdr, sizeof_phdr, sizeof_shdr;
+  unsigned char sizeof_rel, sizeof_rela, sizeof_sym, sizeof_dyn, sizeof_note;
+
+  unsigned char arch_size, file_align;
+  unsigned char elfclass, ev_current;
+  int (*write_out_phdrs) PARAMS ((bfd *, Elf_Internal_Phdr *, int));
+  boolean (*write_shdrs_and_ehdr) PARAMS ((bfd *));
+  void (*write_relocs) PARAMS ((bfd *, asection *, PTR));
+  void (*swap_symbol_out) PARAMS ((bfd *, Elf_Internal_Sym *, PTR));
+  boolean (*slurp_reloc_table) PARAMS ((bfd *, asection *, asymbol **));
+  long (*slurp_symbol_table) PARAMS ((bfd *, asymbol **, boolean));
+};
+
+#define elf_symbol_from(ABFD,S) \
+       (((S)->the_bfd->xvec->flavour == bfd_target_elf_flavour \
+         && (S)->the_bfd->tdata.elf_obj_data != 0) \
+        ? (elf_symbol_type *) (S) \
+        : 0)
+
+struct elf_backend_data
+{
+  /* Whether the backend uses REL or RELA relocations.  FIXME: some
+     ELF backends use both.  When we need to support one, this whole
+     approach will need to be changed.  */
+  int use_rela_p;
+
+  /* The architecture for this backend.  */
+  enum bfd_architecture arch;
+
+  /* The ELF machine code (EM_xxxx) for this backend.  */
+  int elf_machine_code;
+
+  /* The maximum page size for this backend.  */
+  bfd_vma maxpagesize;
+
+  /* This is true if the linker should act like collect and gather
+     global constructors and destructors by name.  This is true for
+     MIPS ELF because the Irix 5 tools can not handle the .init
+     section.  */
+  boolean collect;
+
+  /* A function to translate an ELF RELA relocation to a BFD arelent
+     structure.  */
+  void (*elf_info_to_howto) PARAMS ((bfd *, arelent *,
+                                    Elf_Internal_Rela *));
+
+  /* A function to translate an ELF REL relocation to a BFD arelent
+     structure.  */
+  void (*elf_info_to_howto_rel) PARAMS ((bfd *, arelent *,
+                                        Elf_Internal_Rel *));
+
+  /* A function to determine whether a symbol is global when
+     partitioning the symbol table into local and global symbols.
+     This should be NULL for most targets, in which case the correct
+     thing will be done.  MIPS ELF, at least on the Irix 5, has
+     special requirements.  */
+  boolean (*elf_backend_sym_is_global) PARAMS ((bfd *, asymbol *));
+
+  /* The remaining functions are hooks which are called only if they
+     are not NULL.  */
+
+  /* A function to permit a backend specific check on whether a
+     particular BFD format is relevant for an object file, and to
+     permit the backend to set any global information it wishes.  When
+     this is called elf_elfheader is set, but anything else should be
+     used with caution.  If this returns false, the check_format
+     routine will return a bfd_error_wrong_format error.  */
+  boolean (*elf_backend_object_p) PARAMS ((bfd *));
+
+  /* A function to do additional symbol processing when reading the
+     ELF symbol table.  This is where any processor-specific special
+     section indices are handled.  */
+  void (*elf_backend_symbol_processing) PARAMS ((bfd *, asymbol *));
+
+  /* A function to do additional symbol processing after reading the
+     entire ELF symbol table.  */
+  boolean (*elf_backend_symbol_table_processing) PARAMS ((bfd *,
+                                                         elf_symbol_type *,
+                                                         unsigned int));
+
+  /* A function to do additional processing on the ELF section header
+     just before writing it out.  This is used to set the flags and
+     type fields for some sections, or to actually write out data for
+     unusual sections.  */
+  boolean (*elf_backend_section_processing) PARAMS ((bfd *,
+                                                    Elf32_Internal_Shdr *));
+
+  /* A function to handle unusual section types when creating BFD
+     sections from ELF sections.  */
+  boolean (*elf_backend_section_from_shdr) PARAMS ((bfd *,
+                                                   Elf32_Internal_Shdr *,
+                                                   char *));
+
+  /* A function to set up the ELF section header for a BFD section in
+     preparation for writing it out.  This is where the flags and type
+     fields are set for unusual sections.  */
+  boolean (*elf_backend_fake_sections) PARAMS ((bfd *, Elf32_Internal_Shdr *,
+                                               asection *));
+
+  /* A function to get the ELF section index for a BFD section.  If
+     this returns true, the section was found.  If it is a normal ELF
+     section, *RETVAL should be left unchanged.  If it is not a normal
+     ELF section *RETVAL should be set to the SHN_xxxx index.  */
+  boolean (*elf_backend_section_from_bfd_section)
+    PARAMS ((bfd *, Elf32_Internal_Shdr *, asection *, int *retval));
+
+  /* If this field is not NULL, it is called by the add_symbols phase
+     of a link just before adding a symbol to the global linker hash
+     table.  It may modify any of the fields as it wishes.  If *NAME
+     is set to NULL, the symbol will be skipped rather than being
+     added to the hash table.  This function is responsible for
+     handling all processor dependent symbol bindings and section
+     indices, and must set at least *FLAGS and *SEC for each processor
+     dependent case; failure to do so will cause a link error.  */
+  boolean (*elf_add_symbol_hook)
+    PARAMS ((bfd *abfd, struct bfd_link_info *info,
+            const Elf_Internal_Sym *, const char **name,
+            flagword *flags, asection **sec, bfd_vma *value));
+
+  /* If this field is not NULL, it is called by the elf_link_output_sym
+     phase of a link for each symbol which will appear in the object file.  */
+  boolean (*elf_backend_link_output_symbol_hook)
+    PARAMS ((bfd *, struct bfd_link_info *info, const char *,
+            Elf_Internal_Sym *, asection *));
+
+  /* The CREATE_DYNAMIC_SECTIONS function is called by the ELF backend
+     linker the first time it encounters a dynamic object in the link.
+     This function must create any sections required for dynamic
+     linking.  The ABFD argument is a dynamic object.  The .interp,
+     .dynamic, .dynsym, .dynstr, and .hash functions have already been
+     created, and this function may modify the section flags if
+     desired.  This function will normally create the .got and .plt
+     sections, but different backends have different requirements.  */
+  boolean (*elf_backend_create_dynamic_sections)
+    PARAMS ((bfd *abfd, struct bfd_link_info *info));
+
+  /* The CHECK_RELOCS function is called by the add_symbols phase of
+     the ELF backend linker.  It is called once for each section with
+     relocs of an object file, just after the symbols for the object
+     file have been added to the global linker hash table.  The
+     function must look through the relocs and do any special handling
+     required.  This generally means allocating space in the global
+     offset table, and perhaps allocating space for a reloc.  The
+     relocs are always passed as Rela structures; if the section
+     actually uses Rel structures, the r_addend field will always be
+     zero.  */
+  boolean (*check_relocs)
+    PARAMS ((bfd *abfd, struct bfd_link_info *info, asection *o,
+            const Elf_Internal_Rela *relocs));
+
+  /* The ADJUST_DYNAMIC_SYMBOL function is called by the ELF backend
+     linker for every symbol which is defined by a dynamic object and
+     referenced by a regular object.  This is called after all the
+     input files have been seen, but before the SIZE_DYNAMIC_SECTIONS
+     function has been called.  The hash table entry should be
+     bfd_link_hash_defined ore bfd_link_hash_defweak, and it should be
+     defined in a section from a dynamic object.  Dynamic object
+     sections are not included in the final link, and this function is
+     responsible for changing the value to something which the rest of
+     the link can deal with.  This will normally involve adding an
+     entry to the .plt or .got or some such section, and setting the
+     symbol to point to that.  */
+  boolean (*elf_backend_adjust_dynamic_symbol)
+    PARAMS ((struct bfd_link_info *info, struct elf_link_hash_entry *h));
+
+  /* The SIZE_DYNAMIC_SECTIONS function is called by the ELF backend
+     linker after all the linker input files have been seen but before
+     the sections sizes have been set.  This is called after
+     ADJUST_DYNAMIC_SYMBOL has been called on all appropriate symbols.
+     It is only called when linking against a dynamic object.  It must
+     set the sizes of the dynamic sections, and may fill in their
+     contents as well.  The generic ELF linker can handle the .dynsym,
+     .dynstr and .hash sections.  This function must handle the
+     .interp section and any sections created by the
+     CREATE_DYNAMIC_SECTIONS entry point.  */
+  boolean (*elf_backend_size_dynamic_sections)
+    PARAMS ((bfd *output_bfd, struct bfd_link_info *info));
+
+  /* The RELOCATE_SECTION function is called by the ELF backend linker
+     to handle the relocations for a section.
+
+     The relocs are always passed as Rela structures; if the section
+     actually uses Rel structures, the r_addend field will always be
+     zero.
+
+     This function is responsible for adjust the section contents as
+     necessary, and (if using Rela relocs and generating a
+     relocateable output file) adjusting the reloc addend as
+     necessary.
+
+     This function does not have to worry about setting the reloc
+     address or the reloc symbol index.
+
+     LOCAL_SYMS is a pointer to the swapped in local symbols.
+
+     LOCAL_SECTIONS is an array giving the section in the input file
+     corresponding to the st_shndx field of each local symbol.
+
+     The global hash table entry for the global symbols can be found
+     via elf_sym_hashes (input_bfd).
+
+     When generating relocateable output, this function must handle
+     STB_LOCAL/STT_SECTION symbols specially.  The output symbol is
+     going to be the section symbol corresponding to the output
+     section, which means that the addend must be adjusted
+     accordingly.  */
+  boolean (*elf_backend_relocate_section)
+    PARAMS ((bfd *output_bfd, struct bfd_link_info *info,
+            bfd *input_bfd, asection *input_section, bfd_byte *contents,
+            Elf_Internal_Rela *relocs, Elf_Internal_Sym *local_syms,
+            asection **local_sections));
+
+  /* The FINISH_DYNAMIC_SYMBOL function is called by the ELF backend
+     linker just before it writes a symbol out to the .dynsym section.
+     The processor backend may make any required adjustment to the
+     symbol.  It may also take the opportunity to set contents of the
+     dynamic sections.  Note that FINISH_DYNAMIC_SYMBOL is called on
+     all .dynsym symbols, while ADJUST_DYNAMIC_SYMBOL is only called
+     on those symbols which are defined by a dynamic object.  */
+  boolean (*elf_backend_finish_dynamic_symbol)
+    PARAMS ((bfd *output_bfd, struct bfd_link_info *info,
+            struct elf_link_hash_entry *h, Elf_Internal_Sym *sym));
+
+  /* The FINISH_DYNAMIC_SECTIONS function is called by the ELF backend
+     linker just before it writes all the dynamic sections out to the
+     output file.  The FINISH_DYNAMIC_SYMBOL will have been called on
+     all dynamic symbols.  */
+  boolean (*elf_backend_finish_dynamic_sections)
+    PARAMS ((bfd *output_bfd, struct bfd_link_info *info));
+
+  /* A function to do any beginning processing needed for the ELF file
+     before building the ELF headers and computing file positions.  */
+  void (*elf_backend_begin_write_processing)
+    PARAMS ((bfd *, struct bfd_link_info *));
+
+  /* A function to do any final processing needed for the ELF file
+     before writing it out.  The LINKER argument is true if this BFD
+     was created by the ELF backend linker.  */
+  void (*elf_backend_final_write_processing)
+    PARAMS ((bfd *, boolean linker));
+
+  /* A function to create any special program headers required by the
+     backend.  PHDRS are the program headers, and PHDR_COUNT is the
+     number of them.  If PHDRS is NULL, this just counts headers
+     without creating them.  This returns an updated value for
+     PHDR_COUNT.  */
+  int (*elf_backend_create_program_headers)
+    PARAMS ((bfd *, Elf_Internal_Phdr *phdrs, int phdr_count));
+
+  /* The swapping table to use when dealing with ECOFF information.
+     Used for the MIPS ELF .mdebug section.  */
+  const struct ecoff_debug_swap *elf_backend_ecoff_debug_swap;
+
+  /* Alternate EM_xxxx machine codes for this backend.  */
+  int elf_machine_alt1;
+  int elf_machine_alt2;
+
+  const struct elf_size_info *s;
+
+  unsigned want_got_plt : 1;
+  unsigned plt_readonly : 1;
+  unsigned want_plt_sym : 1;
+
+  /* Put ELF and program headers in the first loadable segment.  */
+  unsigned want_hdr_in_seg : 1;
+};
+
+/* Information stored for each BFD section in an ELF file.  This
+   structure is allocated by elf_new_section_hook.  */
+
+struct bfd_elf_section_data {
+  /* The ELF header for this section.  */
+  Elf_Internal_Shdr this_hdr;
+  /* The ELF header for the reloc section associated with this
+     section, if any.  */
+  Elf_Internal_Shdr rel_hdr;
+  /* The ELF section number of this section.  Only used for an output
+     file.  */
+  int this_idx;
+  /* The ELF section number of the reloc section associated with this
+     section, if any.  Only used for an output file.  */
+  int rel_idx;
+  /* Used by the backend linker to store the symbol hash table entries
+     associated with relocs against global symbols.  */
+  struct elf_link_hash_entry **rel_hashes;
+  /* A pointer to the swapped relocs.  If the section uses REL relocs,
+     rather than RELA, all the r_addend fields will be zero.  This
+     pointer may be NULL.  It is used by the backend linker.  */
+  Elf_Internal_Rela *relocs;
+  /* Used by the backend linker when generating a shared library to
+     record the dynamic symbol index for a section symbol
+     corresponding to this section.  */
+  long dynindx;
+};
+
+#define elf_section_data(sec)  ((struct bfd_elf_section_data*)sec->used_by_bfd)
+
+#define get_elf_backend_data(abfd) \
+  ((struct elf_backend_data *) (abfd)->xvec->backend_data)
+
+/* Some private data is stashed away for future use using the tdata pointer
+   in the bfd structure.  */
+
+struct elf_obj_tdata
+{
+  Elf_Internal_Ehdr elf_header[1];     /* Actual data, but ref like ptr */
+  Elf_Internal_Shdr **elf_sect_ptr;
+  Elf_Internal_Phdr *phdr;
+  struct elf_segment_map *segment_map;
+  struct bfd_strtab_hash *strtab_ptr;
+  int num_locals;
+  int num_globals;
+  asymbol **section_syms;      /* STT_SECTION symbols for each section */
+  Elf_Internal_Shdr symtab_hdr;
+  Elf_Internal_Shdr shstrtab_hdr;
+  Elf_Internal_Shdr strtab_hdr;
+  Elf_Internal_Shdr dynsymtab_hdr;
+  Elf_Internal_Shdr dynstrtab_hdr;
+  unsigned int symtab_section, shstrtab_section;
+  unsigned int strtab_section, dynsymtab_section;
+  file_ptr next_file_pos;
+  void *prstatus;              /* The raw /proc prstatus structure */
+  void *prpsinfo;              /* The raw /proc prpsinfo structure */
+  bfd_vma gp;                  /* The gp value (MIPS only, for now) */
+  unsigned int gp_size;                /* The gp size (MIPS only, for now) */
+
+  /* This is set to true if the object was created by the backend
+     linker.  */
+  boolean linker;
+
+  /* A mapping from external symbols to entries in the linker hash
+     table, used when linking.  This is indexed by the symbol index
+     minus the sh_info field of the symbol table header.  */
+  struct elf_link_hash_entry **sym_hashes;
+
+  /* A mapping from local symbols to offsets into the global offset
+     table, used when linking.  This is indexed by the symbol index.  */
+  bfd_vma *local_got_offsets;
+
+  /* The linker ELF emulation code needs to let the backend ELF linker
+     know what filename should be used for a dynamic object if the
+     dynamic object is found using a search.  This field is used to
+     hold that information.  */
+  const char *dt_needed_name;
+
+  /* Irix 5 often screws up the symbol table, sorting local symbols
+     after global symbols.  This flag is set if the symbol table in
+     this BFD appears to be screwed up.  If it is, we ignore the
+     sh_info field in the symbol table header, and always read all the
+     symbols.  */
+  boolean bad_symtab;
+
+  /* Records the result of `get_program_header_size'.  */
+  bfd_size_type program_header_size;
+
+  /* Used by MIPS ELF find_nearest_line entry point.  The structure
+     could be included directly in this one, but there's no point to
+     wasting the memory just for the infrequently called
+     find_nearest_line.  */
+  struct mips_elf_find_line *find_line_info;
+
+  /* Used by PowerPC to determine if the e_flags field has been intiialized */
+  boolean ppc_flags_init;
+};
+
+#define elf_tdata(bfd)         ((bfd) -> tdata.elf_obj_data)
+#define elf_elfheader(bfd)     (elf_tdata(bfd) -> elf_header)
+#define elf_elfsections(bfd)   (elf_tdata(bfd) -> elf_sect_ptr)
+#define elf_shstrtab(bfd)      (elf_tdata(bfd) -> strtab_ptr)
+#define elf_onesymtab(bfd)     (elf_tdata(bfd) -> symtab_section)
+#define elf_dynsymtab(bfd)     (elf_tdata(bfd) -> dynsymtab_section)
+#define elf_num_locals(bfd)    (elf_tdata(bfd) -> num_locals)
+#define elf_num_globals(bfd)   (elf_tdata(bfd) -> num_globals)
+#define elf_section_syms(bfd)  (elf_tdata(bfd) -> section_syms)
+#define core_prpsinfo(bfd)     (elf_tdata(bfd) -> prpsinfo)
+#define core_prstatus(bfd)     (elf_tdata(bfd) -> prstatus)
+#define elf_gp(bfd)            (elf_tdata(bfd) -> gp)
+#define elf_gp_size(bfd)       (elf_tdata(bfd) -> gp_size)
+#define elf_sym_hashes(bfd)    (elf_tdata(bfd) -> sym_hashes)
+#define elf_local_got_offsets(bfd) (elf_tdata(bfd) -> local_got_offsets)
+#define elf_dt_needed_name(bfd)        (elf_tdata(bfd) -> dt_needed_name)
+#define elf_bad_symtab(bfd)    (elf_tdata(bfd) -> bad_symtab)
+#define elf_ppc_flags_init(bfd)        (elf_tdata(bfd) -> ppc_flags_init)
+\f
+extern char * bfd_elf_string_from_elf_section PARAMS ((bfd *, unsigned, unsigned));
+extern char * bfd_elf_get_str_section PARAMS ((bfd *, unsigned));
+
+extern void bfd_elf_print_symbol PARAMS ((bfd *, PTR, asymbol *,
+                                         bfd_print_symbol_type));
+#define elf_string_from_elf_strtab(abfd,strindex) \
+     bfd_elf_string_from_elf_section(abfd,elf_elfheader(abfd)->e_shstrndx,strindex)
+
+#define bfd_elf32_print_symbol bfd_elf_print_symbol
+#define bfd_elf64_print_symbol bfd_elf_print_symbol
+#define bfd_elf32_mkobject     bfd_elf_mkobject
+#define bfd_elf64_mkobject     bfd_elf_mkobject
+#define elf_mkobject           bfd_elf_mkobject
+
+extern unsigned long bfd_elf_hash PARAMS ((CONST unsigned char *));
+
+extern bfd_reloc_status_type bfd_elf_generic_reloc PARAMS ((bfd *,
+                                                           arelent *,
+                                                           asymbol *,
+                                                           PTR,
+                                                           asection *,
+                                                           bfd *,
+                                                           char **));
+extern boolean bfd_elf_mkobject PARAMS ((bfd *));
+extern Elf_Internal_Shdr *bfd_elf_find_section PARAMS ((bfd *, char *));
+extern boolean _bfd_elf_make_section_from_shdr
+  PARAMS ((bfd *abfd, Elf_Internal_Shdr *hdr, const char *name));
+extern struct bfd_hash_entry *_bfd_elf_link_hash_newfunc
+  PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
+extern struct bfd_link_hash_table *_bfd_elf_link_hash_table_create
+  PARAMS ((bfd *));
+extern boolean _bfd_elf_link_hash_table_init
+  PARAMS ((struct elf_link_hash_table *, bfd *,
+          struct bfd_hash_entry *(*) (struct bfd_hash_entry *,
+                                      struct bfd_hash_table *,
+                                      const char *)));
+
+extern boolean _bfd_elf_copy_private_symbol_data
+  PARAMS ((bfd *, asymbol *, bfd *, asymbol *));
+extern boolean _bfd_elf_copy_private_section_data
+  PARAMS ((bfd *, asection *, bfd *, asection *));
+extern boolean _bfd_elf_write_object_contents PARAMS ((bfd *));
+extern boolean _bfd_elf_set_section_contents PARAMS ((bfd *, sec_ptr, PTR,
+                                                      file_ptr,
+                                                      bfd_size_type));
+extern long _bfd_elf_get_symtab_upper_bound PARAMS ((bfd *));
+extern long _bfd_elf_get_symtab PARAMS ((bfd *, asymbol **));
+extern long _bfd_elf_get_dynamic_symtab_upper_bound PARAMS ((bfd *));
+extern long _bfd_elf_canonicalize_dynamic_symtab PARAMS ((bfd *, asymbol **));
+extern long _bfd_elf_get_reloc_upper_bound PARAMS ((bfd *, sec_ptr));
+extern long _bfd_elf_canonicalize_reloc PARAMS ((bfd *, sec_ptr,
+                                                 arelent **, asymbol **));
+extern asymbol *_bfd_elf_make_empty_symbol PARAMS ((bfd *));
+extern void _bfd_elf_get_symbol_info PARAMS ((bfd *, asymbol *,
+                                              symbol_info *));
+extern alent *_bfd_elf_get_lineno PARAMS ((bfd *, asymbol *));
+extern boolean _bfd_elf_set_arch_mach PARAMS ((bfd *, enum bfd_architecture,
+                                               unsigned long));
+extern boolean _bfd_elf_find_nearest_line PARAMS ((bfd *, asection *,
+                                                   asymbol **,
+                                                   bfd_vma, CONST char **,
+                                                   CONST char **,
+                                                   unsigned int *));
+#define _bfd_elf_read_minisymbols _bfd_generic_read_minisymbols
+#define _bfd_elf_minisymbol_to_symbol _bfd_generic_minisymbol_to_symbol
+extern int _bfd_elf_sizeof_headers PARAMS ((bfd *, boolean));
+extern boolean _bfd_elf_new_section_hook PARAMS ((bfd *, asection *));
+
+/* If the target doesn't have reloc handling written yet:  */
+extern void _bfd_elf_no_info_to_howto PARAMS ((bfd *, arelent *,
+                                              Elf_Internal_Rela *));
+
+asection *bfd_section_from_elf_index PARAMS ((bfd *, unsigned int));
+boolean _bfd_elf_create_dynamic_sections PARAMS ((bfd *,
+                                                 struct bfd_link_info *));
+struct bfd_strtab_hash *_bfd_elf_stringtab_init PARAMS ((void));
+boolean
+_bfd_elf_link_record_dynamic_symbol PARAMS ((struct bfd_link_info *,
+                                            struct elf_link_hash_entry *));
+boolean
+_bfd_elf_compute_section_file_positions PARAMS ((bfd *,
+                                                struct bfd_link_info *));
+void _bfd_elf_assign_file_positions_for_relocs PARAMS ((bfd *));
+file_ptr _bfd_elf_assign_file_position_for_section PARAMS ((Elf_Internal_Shdr *,
+                                                           file_ptr,
+                                                           boolean));
+
+boolean _bfd_elf_create_dynamic_sections PARAMS ((bfd *,
+                                                 struct bfd_link_info *));
+boolean _bfd_elf_create_got_section PARAMS ((bfd *,
+                                            struct bfd_link_info *));
+
+extern const bfd_target *bfd_elf32_object_p PARAMS ((bfd *));
+extern const bfd_target *bfd_elf32_core_file_p PARAMS ((bfd *));
+extern char *bfd_elf32_core_file_failing_command PARAMS ((bfd *));
+extern int bfd_elf32_core_file_failing_signal PARAMS ((bfd *));
+extern boolean bfd_elf32_core_file_matches_executable_p PARAMS ((bfd *,
+                                                                bfd *));
+
+extern boolean bfd_elf32_bfd_link_add_symbols
+  PARAMS ((bfd *, struct bfd_link_info *));
+extern boolean bfd_elf32_bfd_final_link
+  PARAMS ((bfd *, struct bfd_link_info *));
+
+extern void bfd_elf32_swap_symbol_in
+  PARAMS ((bfd *, Elf32_External_Sym *, Elf_Internal_Sym *));
+extern void bfd_elf32_swap_symbol_out
+  PARAMS ((bfd *, Elf_Internal_Sym *, PTR));
+extern void bfd_elf32_swap_reloc_in
+  PARAMS ((bfd *, Elf32_External_Rel *, Elf_Internal_Rel *));
+extern void bfd_elf32_swap_reloc_out
+  PARAMS ((bfd *, Elf_Internal_Rel *, Elf32_External_Rel *));
+extern void bfd_elf32_swap_reloca_in
+  PARAMS ((bfd *, Elf32_External_Rela *, Elf_Internal_Rela *));
+extern void bfd_elf32_swap_reloca_out
+  PARAMS ((bfd *, Elf_Internal_Rela *, Elf32_External_Rela *));
+extern void bfd_elf32_swap_phdr_in
+  PARAMS ((bfd *, Elf32_External_Phdr *, Elf_Internal_Phdr *));
+extern void bfd_elf32_swap_phdr_out
+  PARAMS ((bfd *, Elf_Internal_Phdr *, Elf32_External_Phdr *));
+extern void bfd_elf32_swap_dyn_in
+  PARAMS ((bfd *, const Elf32_External_Dyn *, Elf_Internal_Dyn *));
+extern void bfd_elf32_swap_dyn_out
+  PARAMS ((bfd *, const Elf_Internal_Dyn *, Elf32_External_Dyn *));
+extern boolean bfd_elf32_add_dynamic_entry
+  PARAMS ((struct bfd_link_info *, bfd_vma, bfd_vma));
+extern boolean bfd_elf32_link_create_dynamic_sections
+  PARAMS ((bfd *, struct bfd_link_info *));
+
+extern const bfd_target *bfd_elf64_object_p PARAMS ((bfd *));
+extern const bfd_target *bfd_elf64_core_file_p PARAMS ((bfd *));
+extern char *bfd_elf64_core_file_failing_command PARAMS ((bfd *));
+extern int bfd_elf64_core_file_failing_signal PARAMS ((bfd *));
+extern boolean bfd_elf64_core_file_matches_executable_p PARAMS ((bfd *,
+                                                                bfd *));
+extern boolean bfd_elf64_bfd_link_add_symbols
+  PARAMS ((bfd *, struct bfd_link_info *));
+extern boolean bfd_elf64_bfd_final_link
+  PARAMS ((bfd *, struct bfd_link_info *));
+
+extern void bfd_elf64_swap_symbol_in
+  PARAMS ((bfd *, Elf64_External_Sym *, Elf_Internal_Sym *));
+extern void bfd_elf64_swap_symbol_out
+  PARAMS ((bfd *, Elf_Internal_Sym *, PTR));
+extern void bfd_elf64_swap_reloc_in
+  PARAMS ((bfd *, Elf64_External_Rel *, Elf_Internal_Rel *));
+extern void bfd_elf64_swap_reloc_out
+  PARAMS ((bfd *, Elf_Internal_Rel *, Elf64_External_Rel *));
+extern void bfd_elf64_swap_reloca_in
+  PARAMS ((bfd *, Elf64_External_Rela *, Elf_Internal_Rela *));
+extern void bfd_elf64_swap_reloca_out
+  PARAMS ((bfd *, Elf_Internal_Rela *, Elf64_External_Rela *));
+extern void bfd_elf64_swap_phdr_in
+  PARAMS ((bfd *, Elf64_External_Phdr *, Elf_Internal_Phdr *));
+extern void bfd_elf64_swap_phdr_out
+  PARAMS ((bfd *, Elf_Internal_Phdr *, Elf64_External_Phdr *));
+extern void bfd_elf64_swap_dyn_in
+  PARAMS ((bfd *, const Elf64_External_Dyn *, Elf_Internal_Dyn *));
+extern void bfd_elf64_swap_dyn_out
+  PARAMS ((bfd *, const Elf_Internal_Dyn *, Elf64_External_Dyn *));
+extern boolean bfd_elf64_add_dynamic_entry
+  PARAMS ((struct bfd_link_info *, bfd_vma, bfd_vma));
+extern boolean bfd_elf64_link_create_dynamic_sections
+  PARAMS ((bfd *, struct bfd_link_info *));
+
+#define bfd_elf32_link_record_dynamic_symbol _bfd_elf_link_record_dynamic_symbol
+#define bfd_elf64_link_record_dynamic_symbol _bfd_elf_link_record_dynamic_symbol
+
+#endif /* _LIBELF_H_ */
index 95245f547322fb36090be26e7197234c42f5ab4b..083f8d2c07b53ae98257a26e095ac21b2784ad07 100644 (file)
--- a/bfd/elf.c
+++ b/bfd/elf.c
@@ -38,11 +38,11 @@ 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 **));
 
@@ -219,6 +219,26 @@ _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_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;
 
@@ -521,7 +541,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 +565,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 +601,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.  */
@@ -867,27 +887,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;
@@ -1220,7 +1230,7 @@ elf_map_symbols (abfd)
   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;
 
@@ -1333,6 +1343,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 +1413,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 +1428,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 +1462,542 @@ _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;
+
+  m = ((struct elf_segment_map *)
+       bfd_zalloc (abfd,
+                  (sizeof (struct elf_segment_map)
+                   + (to - from - 1) * sizeof (asection *))));
+  if (m == NULL)
+    {
+      bfd_set_error (bfd_error_no_memory);
+      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;
+
+  return m;
 }
 
-/* Assign a file position to a section, optionally aligning to the
-   required section alignment.  */
+/* Set up a mapping from BFD sections to program segments.  */
 
-INLINE file_ptr
-_bfd_elf_assign_file_position_for_section (i_shdrp, offset, align)
-     Elf_Internal_Shdr *i_shdrp;
-     file_ptr offset;
-     boolean align;
+static boolean
+map_sections_to_segments (abfd)
+     bfd *abfd;
 {
-  if (align)
+  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;
+  unsigned int phdr_index;
+  bfd_vma maxpagesize;
+  asection **hdrpp;
+
+  if (elf_tdata (abfd)->segment_map != NULL)
+    return true;
+
+  if (bfd_count_sections (abfd) == 0)
+    return true;
+
+  /* Select the allocated sections, and sort them.  */
+
+  sections = (asection **) malloc (bfd_count_sections (abfd)
+                                  * sizeof (asection *));
+  if (sections == NULL)
     {
-      unsigned int al;
+      bfd_set_error (bfd_error_no_memory);
+      goto error_return;
+    }
 
-      al = i_shdrp->sh_addralign;
-      if (al > 1)
-       offset = BFD_ALIGN (offset, al);
+  i = 0;
+  for (s = abfd->sections; s != NULL; s = s->next)
+    {
+      if ((s->flags & SEC_ALLOC) != 0)
+       {
+         sections[i] = s;
+         ++i;
+       }
     }
-  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;
-}
+  BFD_ASSERT (i <= bfd_count_sections (abfd));
+  count = i;
 
-/* Get the size of the program header.
+  qsort (sections, (size_t) count, sizeof (asection *), elf_sort_sections);
 
-   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?)
+  /* Build the mapping.  */
 
-   If SORTED_HDRS is NULL we assume there are two segments: text and data
-   (exclusive of .interp and .dynamic).
+  mfirst = NULL;
+  pm = &mfirst;
 
-   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).
+  /* 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)
+    {
+      m = ((struct elf_segment_map *)
+          bfd_zalloc (abfd, sizeof (struct elf_segment_map)));
+      if (m == NULL)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         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;
 
-   ??? User written scripts must either not use SIZEOF_HEADERS, or assume there
-   will be two segments.  */
+      *pm = m;
+      pm = &m->next;
 
-static bfd_size_type
-get_program_header_size (abfd, sorted_hdrs, count, maxpagesize)
-     bfd *abfd;
-     Elf_Internal_Shdr **sorted_hdrs;
-     unsigned int count;
-     bfd_vma maxpagesize;
-{
-  size_t segs;
-  asection *s;
-  struct elf_backend_data *bed = get_elf_backend_data (abfd);
+      m = ((struct elf_segment_map *)
+          bfd_zalloc (abfd, sizeof (struct elf_segment_map)));
+      if (m == NULL)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         goto error_return;
+       }
+      m->next = NULL;
+      m->p_type = PT_INTERP;
+      m->count = 1;
+      m->sections[0] = s;
 
-  /* 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;
+      *pm = m;
+      pm = &m->next;
+    }
 
-  if (sorted_hdrs != NULL)
+  /* 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++)
     {
-      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;
+      asection *hdr;
 
-         hdr = *hdrpp;
+      hdr = *hdrpp;
 
-         /* 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;
+      /* 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;
+       }
 
-             adjust = hdr->sh_addr - (p_vaddr + p_memsz);
-             p_memsz += hdr->sh_size + adjust;
-             file_offset = next_offset;
-             last_type = hdr->sh_type;
-             continue;
-           }
+      /* 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.  */
 
-         /* The section won't fit, start a new segment.  */
-         ++segs;
+      m = make_mapping (abfd, sections, phdr_index, i);
+      if (m == NULL)
+       goto error_return;
 
-         /* Initialize the segment.  */
-         p_vaddr = hdr->sh_addr;
-         p_memsz = hdr->sh_size;
-         p_offset = file_offset;
-         file_offset = next_offset;
+      *pm = m;
+      pm = &m->next;
 
-         last_type = hdr->sh_type;
-       }
+      last_hdr = hdr;
+      phdr_index = i;
     }
-  else
+
+  /* Create a final PT_LOAD program segment.  */
+  if (last_hdr != NULL)
     {
-      /* Assume we will need exactly two PT_LOAD segments: one for text
-        and one for data.  */
-      segs = 2;
+      m = make_mapping (abfd, sections, phdr_index, i);
+      if (m == NULL)
+       goto error_return;
+
+      *pm = m;
+      pm = &m->next;
     }
 
-  s = bfd_get_section_by_name (abfd, ".interp");
+  /* 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)
     {
-      /* 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)
+       {
+         bfd_set_error (bfd_error_no_memory);
+         goto error_return;
+       }
+      m->next = NULL;
+      m->p_type = PT_DYNAMIC;
+      m->count = 1;
+      m->sections[0] = s;
 
-  if (bfd_get_section_by_name (abfd, ".dynamic") != NULL)
-    {
-      /* We need a PT_DYNAMIC segment.  */
-      ++segs;
+      *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));
+  free (sections);
+  sections = NULL;
 
-  elf_tdata (abfd)->program_header_size = segs * bed->s->sizeof_phdr;
-  return elf_tdata (abfd)->program_header_size;
+  elf_tdata (abfd)->segment_map = mfirst;
+  return true;
+
+ error_return:
+  if (sections != NULL)
+    free (sections);
+  return false;
 }
 
-/* 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.  */
+/* Sort sections by VMA.  */
 
-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;
+static int
+elf_sort_sections (arg1, arg2)
+     const PTR arg1;
+     const PTR arg2;
 {
-  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);
+  const asection *sec1 = *(const asection **) arg1;
+  const asection *sec2 = *(const asection **) arg2;
 
-  BFD_ASSERT ((abfd->flags & (EXEC_P | DYNAMIC)) != 0);
-  BFD_ASSERT (phdr_size / sizeof (Elf_Internal_Phdr)
-             <= sizeof phdrs / sizeof (phdrs[0]));
+  if (sec1->vma < sec2->vma)
+    return -1;
+  else if (sec1->vma > sec2->vma)
+    return 1;
 
-  phdr_count = 0;
-  phdr = phdrs;
+  /* Put !SEC_LOAD sections after SEC_LOAD ones.  */
 
-  if (bed->want_hdr_in_seg)
-    phdr_size_adjust = first->sh_offset - phdr_size;
-  else
-    phdr_size_adjust = 0;
+#define TOEND(x) (((x)->flags & SEC_LOAD) == 0)
 
-  /* 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)
-    {
-      BFD_ASSERT (first != NULL);
+  if (TOEND (sec1))
+    if (TOEND (sec2))
+      return sec1->target_index - sec2->target_index;
+    else 
+      return 1;
 
-      phdr->p_type = PT_PHDR;
+  if (TOEND (sec2))
+    return -1;
 
-      phdr->p_offset = off;
+#undef TOEND
 
-      /* 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);
+  /* Sort by size, to put zero sized sections before others at the
+     same address.  */
 
-      /* 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);
+  if (sec1->_raw_size < sec2->_raw_size)
+    return -1;
+  if (sec1->_raw_size > sec2->_raw_size)
+    return 1;
 
-      phdr->p_paddr = 0;
-      phdr->p_filesz = phdr_size;
-      phdr->p_memsz = phdr_size;
+  return sec1->target_index - sec2->target_index;
+}
 
-      /* FIXME: UnixWare and Solaris set PF_X, Irix 5 does not.  */
-      phdr->p_flags = PF_R | PF_X;
+/* 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.  */
 
-      phdr->p_align = bed->s->file_align;
-      BFD_ASSERT ((phdr->p_vaddr - phdr->p_offset) % bed->s->file_align == 0);
+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;
+  boolean found_load;
+  Elf_Internal_Phdr *p;
+
+  if (elf_tdata (abfd)->segment_map == NULL)
+    {
+      if (! map_sections_to_segments (abfd))
+       return false;
+    }
 
-      /* Include the ELF header in the first loadable segment.  */
-      phdr_size_adjust += off;
+  count = 0;
+  for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
+    ++count;
 
-      ++phdr_count;
-      ++phdr;
+  elf_elfheader (abfd)->e_phoff = bed->s->sizeof_ehdr;
+  elf_elfheader (abfd)->e_phentsize = bed->s->sizeof_phdr;
+  elf_elfheader (abfd)->e_phnum = count;
 
-      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);
+  if (count == 0)
+    return true;
 
-      ++phdr_count;
-      ++phdr;
+  /* Let the backend count up any program headers it might need.  */
+  if (bed->elf_backend_create_program_headers)
+    count = ((*bed->elf_backend_create_program_headers)
+            (abfd, (Elf_Internal_Phdr *) NULL, count));
+
+  /* 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;
     }
 
-  /* 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 (alloc == 0)
+    alloc = count;
+
+  phdrs = ((Elf_Internal_Phdr *)
+          bfd_alloc (abfd, alloc * sizeof (Elf_Internal_Phdr)));
+  if (phdrs == NULL)
     {
-      Elf_Internal_Shdr *hdr;
+      bfd_set_error (bfd_error_no_memory);
+      return false;
+    }
 
-      hdr = *hdrpp;
+  off = bed->s->sizeof_ehdr;
+  off += alloc * bed->s->sizeof_phdr;
 
-      /* Ignore any section which will not be part of the process
-        image.  */
-      if ((hdr->sh_flags & SHF_ALLOC) == 0)
-       continue;
+  found_load = false;
+  for (m = elf_tdata (abfd)->segment_map, p = phdrs;
+       m != NULL;
+       m = m->next, p++)
+    {
+      unsigned int i;
+      asection **secpp;
+      boolean adjusted;
+
+      p->p_type = m->p_type;
+
+      if (m->p_flags_valid)
+       p->p_flags = m->p_flags;
+
+      if (m->count == 0)
+       p->p_vaddr = 0;
+      else
+       p->p_vaddr = m->sections[0]->vma;
 
-      /* 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 (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_filesz = 0;
+      p->p_memsz = 0;
+
+      adjusted = false;
+      if (p->p_type == PT_LOAD)
        {
-         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;
+         p->p_offset = off;
+
+         if (! found_load)
+           {
+             struct elf_segment_map *mi;
+             Elf_Internal_Phdr *pi;
+             Elf_Internal_Phdr *pi_phdr;
+
+             /* This is the first PT_LOAD segment.  If there is a
+                 PT_INTERP segment, adjust the offset of this segment
+                 to include the program headers and the file header.  */
+             pi_phdr = NULL;
+             for (mi = elf_tdata (abfd)->segment_map, pi = phdrs;
+                  mi != NULL;
+                  mi = mi->next, pi++)
+               {
+                 if (mi->p_type == PT_INTERP)
+                   {
+                     p->p_offset = 0;
+                     p->p_filesz = off;
+                     p->p_memsz = off;
+                     p->p_vaddr -= off;
+                     p->p_paddr -= off;
+                     adjusted = true;
+                   }
+                 if (mi->p_type == PT_PHDR)
+                   pi_phdr = pi;
+               }
+
+             /* Set up the PT_PHDR addresses.  */
+             if (pi_phdr != NULL)
+               {
+                 pi_phdr->p_vaddr = p->p_vaddr + bed->s->sizeof_ehdr;
+                 pi_phdr->p_paddr = p->p_paddr + bed->s->sizeof_ehdr;
+               }
+
+             found_load = true;
+           }
        }
 
-      /* 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->p_flags_valid)
+       p->p_flags = PF_R;
+      for (i = 0, secpp = m->sections; i < m->count; i++, secpp++)
        {
-         ++phdr;
-         ++phdr_count;
+         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.  */
+             adjust = (sec->vma - off) % bed->maxpagesize;
+             if (adjust != 0)
+               {
+                 if (i == 0 && ! adjusted)
+                   p->p_offset += adjust;
+                 else
+                   {
+                     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)
+           {
+             if ((flags & SEC_CODE) != 0)
+               p->p_flags |= PF_X;
+             if ((flags & SEC_READONLY) == 0)
+               p->p_flags |= PF_W;
+           }
        }
+    }
 
-      /* 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)))
+  /* 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++)
+    {
+      if (p->p_type != PT_LOAD && m->count > 0)
+       p->p_offset = m->sections[0]->filepos;
+      if (p->p_type == PT_PHDR)
        {
-         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;
+         p->p_offset = bed->s->sizeof_ehdr;
+         p->p_filesz = count * bed->s->sizeof_phdr;
+         p->p_memsz = p->p_filesz;
        }
-
-      last_type = hdr->sh_type;
     }
 
-  if (phdr->p_type != PT_NULL)
+  /* Let the backend set up any program headers it might need.  */
+  if (bed->elf_backend_create_program_headers)
+    count = ((*bed->elf_backend_create_program_headers)
+            (abfd, phdrs, count));
+
+  /* Clear out any program headers we allocated but did not use.  */
+  for (; count < alloc; count++, p++)
     {
-      ++phdr;
-      ++phdr_count;
+      memset (p, 0, sizeof *p);
+      p->p_type = PT_NULL;
     }
 
-  /* 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)
-    {
-      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);
+  elf_tdata (abfd)->phdr = phdrs;
 
-      ++phdr;
-      ++phdr_count;
-    }
+  elf_tdata (abfd)->next_file_pos = off;
 
-  /* 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);
+  /* 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)
+  /* 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)
     {
-      ((*_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;
+      /* 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;
     }
 
-  /* 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;
-
-  /* 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;
-
-  if (bed->s->write_out_phdrs (abfd, phdrs, phdr_count) != 0)
-    return (file_ptr) -1;
+  /* 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));
 
-  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 +2009,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 +2021,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 +2042,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 +2054,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 +2105,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;
@@ -2325,6 +2399,80 @@ _bfd_elf_symbol_from_bfd_symbol (abfd, asym_ptr_ptr)
   return idx;
 }
 
+/* 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;
+
+  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;
@@ -2429,21 +2577,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))
@@ -2713,8 +2896,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;
 }