summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2006-09-21 22:13:18 +0000
committerIan Lance Taylor <iant@google.com>2006-09-21 22:13:18 +0000
commita2fb1b05e4af3fac54faac6c07a4717f2cb34aae (patch)
treecab19eb8c3abe76aee65d684dcceee884ed59c61
parent5ffff7c1d1d2db595e4c23a8c388d3a51d5bb357 (diff)
downloadbinutils-gdb-a2fb1b05e4af3fac54faac6c07a4717f2cb34aae.tar.gz
New drop, with first cut of section layout code.
-rw-r--r--gold/Makefile.am5
-rw-r--r--gold/Makefile.in15
-rw-r--r--gold/fileread.h16
-rw-r--r--gold/gold.cc17
-rw-r--r--gold/layout.cc432
-rw-r--r--gold/layout.h181
-rw-r--r--gold/object.cc262
-rw-r--r--gold/object.h122
-rw-r--r--gold/options.h2
-rw-r--r--gold/output.cc159
-rw-r--r--gold/output.h214
-rw-r--r--gold/po/POTFILES.in5
-rw-r--r--gold/po/gold.pot67
-rw-r--r--gold/readsyms.cc28
-rw-r--r--gold/readsyms.h14
-rw-r--r--gold/stringpool.cc2
-rw-r--r--gold/symtab.h1
-rw-r--r--gold/targetsize.h56
-rw-r--r--gold/workqueue.h38
19 files changed, 1472 insertions, 164 deletions
diff --git a/gold/Makefile.am b/gold/Makefile.am
index a01aef4aab1..ed26af979f1 100644
--- a/gold/Makefile.am
+++ b/gold/Makefile.am
@@ -22,8 +22,10 @@ CCFILES = \
fileread.cc \
gold.cc \
gold-threads.cc \
+ layout.cc \
object.cc \
options.cc \
+ output.cc \
readsyms.cc \
resolve.cc \
symtab.cc \
@@ -36,13 +38,14 @@ HFILES = \
fileread.h \
gold.h \
gold-threads.h \
+ layout.h \
object.h \
options.h \
+ output.h \
readsyms.h \
stringpool.h \
symtab.h \
target.h \
- targetsize.h \
target-select.h \
workqueue.h
diff --git a/gold/Makefile.in b/gold/Makefile.in
index 8881517e77b..6dba59ce442 100644
--- a/gold/Makefile.in
+++ b/gold/Makefile.in
@@ -66,10 +66,10 @@ CONFIG_HEADER = config.h
CONFIG_CLEAN_FILES = po/Makefile.in
PROGRAMS = $(noinst_PROGRAMS)
am__objects_1 = dirsearch.$(OBJEXT) fileread.$(OBJEXT) gold.$(OBJEXT) \
- gold-threads.$(OBJEXT) object.$(OBJEXT) options.$(OBJEXT) \
- readsyms.$(OBJEXT) resolve.$(OBJEXT) symtab.$(OBJEXT) \
- stringpool.$(OBJEXT) target-select.$(OBJEXT) \
- workqueue.$(OBJEXT)
+ gold-threads.$(OBJEXT) layout.$(OBJEXT) object.$(OBJEXT) \
+ options.$(OBJEXT) output.$(OBJEXT) readsyms.$(OBJEXT) \
+ resolve.$(OBJEXT) symtab.$(OBJEXT) stringpool.$(OBJEXT) \
+ target-select.$(OBJEXT) workqueue.$(OBJEXT)
am__objects_2 =
am__objects_3 = i386.$(OBJEXT)
am_ld_new_OBJECTS = $(am__objects_1) $(am__objects_2) $(am__objects_3)
@@ -233,8 +233,10 @@ CCFILES = \
fileread.cc \
gold.cc \
gold-threads.cc \
+ layout.cc \
object.cc \
options.cc \
+ output.cc \
readsyms.cc \
resolve.cc \
symtab.cc \
@@ -247,13 +249,14 @@ HFILES = \
fileread.h \
gold.h \
gold-threads.h \
+ layout.h \
object.h \
options.h \
+ output.h \
readsyms.h \
stringpool.h \
symtab.h \
target.h \
- targetsize.h \
target-select.h \
workqueue.h
@@ -340,8 +343,10 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gold-threads.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gold.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/i386.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/layout.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/object.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/output.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/readsyms.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolve.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stringpool.Po@am__quote@
diff --git a/gold/fileread.h b/gold/fileread.h
index 2181485a50e..b65a86b0225 100644
--- a/gold/fileread.h
+++ b/gold/fileread.h
@@ -173,22 +173,6 @@ class File_view
const unsigned char* data_;
};
-// An object which locks a file using RAII.
-
-class File_read_lock
-{
- public:
- File_read_lock(File_read& file)
- : file_(file)
- { this->file_.lock(); }
-
- ~File_read_lock()
- { this->file_.unlock(); }
-
- private:
- File_read& file_;
-};
-
// All the information we hold for a single input file. This can be
// an object file, a shared library, or an archive.
diff --git a/gold/gold.cc b/gold/gold.cc
index e419f9cb5ef..02e7366a40e 100644
--- a/gold/gold.cc
+++ b/gold/gold.cc
@@ -12,6 +12,7 @@
#include "dirsearch.h"
#include "readsyms.h"
#include "symtab.h"
+#include "layout.h"
namespace gold
{
@@ -66,7 +67,8 @@ void
queue_initial_tasks(const General_options& options,
const Dirsearch& search_path,
const Command_line::Input_argument_list& inputs,
- Workqueue* workqueue, Symbol_table* symtab)
+ Workqueue* workqueue, Object_list* input_objects,
+ Symbol_table* symtab)
{
if (inputs.empty())
gold_fatal(_("no input files"), false);
@@ -82,12 +84,13 @@ queue_initial_tasks(const General_options& options,
{
Task_token* next_blocker = new Task_token();
next_blocker->add_blocker();
- workqueue->queue(new Read_symbols(options, symtab, search_path,
- *p, this_blocker, next_blocker));
+ workqueue->queue(new Read_symbols(options, input_objects, symtab,
+ search_path, *p, this_blocker,
+ next_blocker));
this_blocker = next_blocker;
}
- // workqueue->queue(new Layout(options, inputs, this_blocker));
+ workqueue->queue(new Layout_task(options, input_objects, this_blocker));
}
} // end anonymous namespace.
@@ -113,6 +116,9 @@ main(int argc, char** argv)
// The work queue.
gold::Workqueue workqueue(command_line.options());
+ // The list of input objects.
+ Object_list input_objects;
+
// The symbol table.
Symbol_table symtab;
@@ -122,7 +128,8 @@ main(int argc, char** argv)
// Queue up the first set of tasks.
queue_initial_tasks(command_line.options(), search_path,
- command_line.inputs(), &workqueue, &symtab);
+ command_line.inputs(), &workqueue, &input_objects,
+ &symtab);
// Run the main task processing loop.
workqueue.process();
diff --git a/gold/layout.cc b/gold/layout.cc
new file mode 100644
index 00000000000..3faec94e797
--- /dev/null
+++ b/gold/layout.cc
@@ -0,0 +1,432 @@
+// layout.cc -- lay out output file sections for gold
+
+#include "gold.h"
+
+#include <cassert>
+#include <cstring>
+#include <iostream>
+#include <utility>
+
+#include "output.h"
+#include "layout.h"
+
+namespace gold
+{
+
+// Layout_task methods.
+
+Layout_task::~Layout_task()
+{
+}
+
+// This task can be run when it is unblocked.
+
+Task::Is_runnable_type
+Layout_task::is_runnable(Workqueue*)
+{
+ if (this->this_blocker_->is_blocked())
+ return IS_BLOCKED;
+ return IS_RUNNABLE;
+}
+
+// We don't need to hold any locks for the duration of this task. In
+// fact this task will be the only one running.
+
+Task_locker*
+Layout_task::locks(Workqueue*)
+{
+ return NULL;
+}
+
+// Lay out the sections. This is called after all the input objects
+// have been read.
+
+void
+Layout_task::run(Workqueue*)
+{
+ Layout layout(this->options_);
+ for (Object_list::const_iterator p = this->input_objects_->begin();
+ p != this->input_objects_->end();
+ ++p)
+ (*p)->layout(&layout);
+}
+
+// Layout methods.
+
+// Hash a key we use to look up an output section mapping.
+
+size_t
+Layout::Hash_key::operator()(const Layout::Key& k) const
+{
+ return reinterpret_cast<size_t>(k.first) + k.second.first + k.second.second;
+}
+
+// Whether to include this section in the link.
+
+template<int size, bool big_endian>
+bool
+Layout::include_section(Object*, const char*,
+ const elfcpp::Shdr<size, big_endian>& shdr)
+{
+ // Some section types are never linked. Some are only linked when
+ // doing a relocateable link.
+ switch (shdr.get_sh_type())
+ {
+ case elfcpp::SHT_NULL:
+ case elfcpp::SHT_SYMTAB:
+ case elfcpp::SHT_DYNSYM:
+ case elfcpp::SHT_STRTAB:
+ case elfcpp::SHT_HASH:
+ case elfcpp::SHT_DYNAMIC:
+ case elfcpp::SHT_SYMTAB_SHNDX:
+ return false;
+
+ case elfcpp::SHT_RELA:
+ case elfcpp::SHT_REL:
+ case elfcpp::SHT_GROUP:
+ return this->options_.is_relocatable();
+
+ default:
+ // FIXME: Handle stripping debug sections here.
+ return true;
+ }
+}
+
+// Return the output section to use for input section NAME, with
+// header HEADER, from object OBJECT. Set *OFF to the offset of this
+// input section without the output section.
+
+template<int size, bool big_endian>
+Output_section*
+Layout::layout(Object* object, const char* name,
+ const elfcpp::Shdr<size, big_endian>& shdr, off_t* off)
+{
+ if (!this->include_section(object, name, shdr))
+ return NULL;
+
+ // Unless we are doing a relocateable link, .gnu.linkonce sections
+ // are laid out as though they were named for the sections are
+ // placed into.
+ if (!this->options_.is_relocatable() && Layout::is_linkonce(name))
+ name = Layout::linkonce_output_name(name);
+
+ // FIXME: Handle SHF_OS_NONCONFORMING here.
+
+ // Canonicalize the section name.
+ name = this->namepool_.add(name);
+
+ // Find the output section. The output section is selected based on
+ // the section name, type, and flags.
+
+ // FIXME: If we want to do relaxation, we need to modify this
+ // algorithm. We also build a list of input sections for each
+ // output section. Then we relax all the input sections. Then we
+ // walk down the list and adjust all the offsets.
+
+ elfcpp::Elf_Word type = shdr.get_sh_type();
+ elfcpp::Elf_Xword flags = shdr.get_sh_flags();
+ const Key key(name, std::make_pair(type, flags));
+ const std::pair<Key, Output_section*> v(key, NULL);
+ std::pair<Section_name_map::iterator, bool> ins(
+ this->section_name_map_.insert(v));
+
+ Output_section* os;
+ if (!ins.second)
+ os = ins.first->second;
+ else
+ {
+ // This is the first time we've seen this name/type/flags
+ // combination.
+ os = this->make_output_section(name, type, flags);
+ ins.first->second = os;
+ }
+
+ // FIXME: Handle SHF_LINK_ORDER somewhere.
+
+ *off = os->add_input_section(object, name, shdr);
+
+ return os;
+}
+
+// Return whether SEG1 should be before SEG2 in the output file. This
+// is based entirely on the segment type and flags. When this is
+// called the segment addresses has normally not yet been set.
+
+bool
+Layout::segment_precedes(const Output_segment* seg1,
+ const Output_segment* seg2)
+{
+ elfcpp::Elf_Word type1 = seg1->type();
+ elfcpp::Elf_Word type2 = seg2->type();
+
+ // The single PT_PHDR segment is required to precede any loadable
+ // segment. We simply make it always first.
+ if (type1 == elfcpp::PT_PHDR)
+ {
+ assert(type2 != elfcpp::PT_PHDR);
+ return true;
+ }
+ if (type2 == elfcpp::PT_PHDR)
+ return false;
+
+ // The single PT_INTERP segment is required to precede any loadable
+ // segment. We simply make it always second.
+ if (type1 == elfcpp::PT_INTERP)
+ {
+ assert(type2 != elfcpp::PT_INTERP);
+ return true;
+ }
+ if (type2 == elfcpp::PT_INTERP)
+ return false;
+
+ // We then put PT_LOAD segments before any other segments.
+ if (type1 == elfcpp::PT_LOAD && type2 != elfcpp::PT_LOAD)
+ return true;
+ if (type2 == elfcpp::PT_LOAD && type1 != elfcpp::PT_LOAD)
+ return false;
+
+ const elfcpp::Elf_Word flags1 = seg1->flags();
+ const elfcpp::Elf_Word flags2 = seg2->flags();
+
+ // The order of non-PT_LOAD segments is unimportant. We simply sort
+ // by the numeric segment type and flags values. There should not
+ // be more than one segment with the same type and flags.
+ if (type1 != elfcpp::PT_LOAD)
+ {
+ if (type1 != type2)
+ return type1 < type2;
+ assert(flags1 != flags2);
+ return flags1 < flags2;
+ }
+
+ // We sort PT_LOAD segments based on the flags. Readonly segments
+ // come before writable segments. Then executable segments come
+ // before non-executable segments. Then the unlikely case of a
+ // non-readable segment comes before the normal case of a readable
+ // segment. If there are multiple segments with the same type and
+ // flags, we require that the address be set, and we sort by
+ // virtual address and then physical address.
+ if ((flags1 & elfcpp::PF_W) != (flags2 & elfcpp::PF_W))
+ return (flags1 & elfcpp::PF_W) == 0;
+ if ((flags1 & elfcpp::PF_X) != (flags2 & elfcpp::PF_X))
+ return (flags1 & elfcpp::PF_X) != 0;
+ if ((flags1 & elfcpp::PF_R) != (flags2 & elfcpp::PF_R))
+ return (flags1 & elfcpp::PF_R) == 0;
+
+ uint64_t vaddr1 = seg1->vaddr();
+ uint64_t vaddr2 = seg2->vaddr();
+ if (vaddr1 != vaddr2)
+ return vaddr1 < vaddr2;
+
+ uint64_t paddr1 = seg1->paddr();
+ uint64_t paddr2 = seg2->paddr();
+ assert(paddr1 != paddr2);
+ return paddr1 < paddr2;
+}
+
+// Map section flags to segment flags.
+
+elfcpp::Elf_Word
+Layout::section_flags_to_segment(elfcpp::Elf_Xword flags)
+{
+ elfcpp::Elf_Word ret = elfcpp::PF_R;
+ if ((flags & elfcpp::SHF_WRITE) != 0)
+ ret |= elfcpp::PF_W;
+ if ((flags & elfcpp::SHF_EXECINSTR) != 0)
+ ret |= elfcpp::PF_X;
+ return ret;
+}
+
+// Make a new Output_section, and attach it to segments as
+// appropriate.
+
+Output_section*
+Layout::make_output_section(const char* name, elfcpp::Elf_Word type,
+ elfcpp::Elf_Xword flags)
+{
+ Output_section* os = new Output_section(name, type, flags);
+
+ if ((flags & elfcpp::SHF_ALLOC) == 0)
+ this->section_list_.push_back(os);
+ else
+ {
+ // This output section goes into a PT_LOAD segment.
+
+ elfcpp::Elf_Word seg_flags = Layout::section_flags_to_segment(flags);
+
+ // The only thing we really care about for PT_LOAD segments is
+ // whether or not they are writable, so that is how we search
+ // for them. People who need segments sorted on some other
+ // basis will have to wait until we implement a mechanism for
+ // them to describe the segments they want.
+
+ Segment_list::const_iterator p;
+ for (p = this->segment_list_.begin();
+ p != this->segment_list_.end();
+ ++p)
+ {
+ if ((*p)->type() == elfcpp::PT_LOAD
+ && ((*p)->flags() & elfcpp::PF_W) == (seg_flags & elfcpp::PF_W))
+ {
+ (*p)->add_output_section(os);
+ if ((*p)->flags() != seg_flags)
+ (*p)->update_flags(seg_flags);
+ break;
+ }
+ }
+
+ if (p == this->segment_list_.end())
+ {
+ Output_segment* oseg = new Output_segment(elfcpp::PT_LOAD,
+ seg_flags);
+ this->segment_list_.push_back(oseg);
+ oseg->add_output_section(os);
+ }
+
+ // If we see a loadable SHT_NOTE section, we create a PT_NOTE
+ // segment.
+ if (type == elfcpp::SHT_NOTE)
+ {
+ // See if we already have an equivalent PT_NOTE segment.
+ for (p = this->segment_list_.begin();
+ p != segment_list_.end();
+ ++p)
+ {
+ if ((*p)->type() == elfcpp::PT_NOTE
+ && (((*p)->flags() & elfcpp::PF_W)
+ == (seg_flags & elfcpp::PF_W)))
+ {
+ (*p)->add_output_section(os);
+ if ((*p)->flags() != seg_flags)
+ (*p)->update_flags(seg_flags);
+ break;
+ }
+ }
+
+ if (p == this->segment_list_.end())
+ {
+ Output_segment* oseg = new Output_segment(elfcpp::PT_NOTE,
+ seg_flags);
+ this->segment_list_.push_back(oseg);
+ oseg->add_output_section(os);
+ }
+ }
+ }
+
+ return os;
+}
+
+// The mapping of .gnu.linkonce section names to real section names.
+
+#define MAPPING_INIT(f, t) { f, sizeof(f) - 1, t }
+const Layout::Linkonce_mapping Layout::linkonce_mapping[] =
+{
+ MAPPING_INIT("d.rel.ro", ".data.rel.ro"), // Must be before "d".
+ MAPPING_INIT("t", ".text"),
+ MAPPING_INIT("r", ".rodata"),
+ MAPPING_INIT("d", ".data"),
+ MAPPING_INIT("b", ".bss"),
+ MAPPING_INIT("s", ".sdata"),
+ MAPPING_INIT("sb", ".sbss"),
+ MAPPING_INIT("s2", ".sdata2"),
+ MAPPING_INIT("sb2", ".sbss2"),
+ MAPPING_INIT("wi", ".debug_info"),
+ MAPPING_INIT("td", ".tdata"),
+ MAPPING_INIT("tb", ".tbss"),
+ MAPPING_INIT("lr", ".lrodata"),
+ MAPPING_INIT("l", ".ldata"),
+ MAPPING_INIT("lb", ".lbss"),
+};
+#undef MAPPING_INIT
+
+const int Layout::linkonce_mapping_count =
+ sizeof(Layout::linkonce_mapping) / sizeof(Layout::linkonce_mapping[0]);
+
+// Return the name of the output section to use for a .gnu.linkonce
+// section. This is based on the default ELF linker script of the old
+// GNU linker. For example, we map a name like ".gnu.linkonce.t.foo"
+// to ".text".
+
+const char*
+Layout::linkonce_output_name(const char* name)
+{
+ const char* s = name + sizeof(".gnu.linkonce") - 1;
+ if (*s != '.')
+ return name;
+ ++s;
+ const Linkonce_mapping* plm = linkonce_mapping;
+ for (int i = 0; i < linkonce_mapping_count; ++i, ++plm)
+ {
+ if (strncmp(s, plm->from, plm->fromlen) == 0 && s[plm->fromlen] == '.')
+ return plm->to;
+ }
+ return name;
+}
+
+// Record the signature of a comdat section, and return whether to
+// include it in the link. If GROUP is true, this is a regular
+// section group. If GROUP is false, this is a group signature
+// derived from the name of a linkonce section. We want linkonce
+// signatures and group signatures to block each other, but we don't
+// want a linkonce signature to block another linkonce signature.
+
+bool
+Layout::add_comdat(const char* signature, bool group)
+{
+ std::string sig(signature);
+ std::pair<Signatures::iterator, bool> ins(
+ this->signatures_.insert(std::make_pair(signature, group)));
+
+ if (ins.second)
+ {
+ // This is the first time we've seen this signature.
+ return true;
+ }
+
+ if (ins.first->second)
+ {
+ // We've already seen a real section group with this signature.
+ return false;
+ }
+ else if (group)
+ {
+ // This is a real section group, and we've already seen a
+ // linkonce section with tihs signature. Record that we've seen
+ // a section group, and don't include this section group.
+ ins.first->second = true;
+ return false;
+ }
+ else
+ {
+ // We've already seen a linkonce section and this is a linkonce
+ // section. These don't block each other--this may be the same
+ // symbol name with different section types.
+ return true;
+ }
+}
+
+// Instantiate the templates we need. We could use the configure
+// script to restrict this to only the ones for implemented targets.
+
+template
+Output_section*
+Layout::layout<32, false>(Object* object, const char* name,
+ const elfcpp::Shdr<32, false>& shdr, off_t*);
+
+template
+Output_section*
+Layout::layout<32, true>(Object* object, const char* name,
+ const elfcpp::Shdr<32, true>& shdr, off_t*);
+
+template
+Output_section*
+Layout::layout<64, false>(Object* object, const char* name,
+ const elfcpp::Shdr<64, false>& shdr, off_t*);
+
+template
+Output_section*
+Layout::layout<64, true>(Object* object, const char* name,
+ const elfcpp::Shdr<64, true>& shdr, off_t*);
+
+
+} // End namespace gold.
diff --git a/gold/layout.h b/gold/layout.h
new file mode 100644
index 00000000000..77fdfc29371
--- /dev/null
+++ b/gold/layout.h
@@ -0,0 +1,181 @@
+// layout.h -- lay out output file sections for gold -*- C++ -*-
+
+#ifndef GOLD_LAYOUT_H
+#define GOLD_LAYOUT_H
+
+#include <list>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "options.h"
+#include "workqueue.h"
+#include "object.h"
+#include "stringpool.h"
+
+namespace gold
+{
+
+class Output_section;
+class Output_segment;
+
+// This Task handles mapping the input sections to output sections and
+// laying them out in memory.
+
+class Layout_task : public Task
+{
+ public:
+ // OPTIONS is the command line options, INPUT_OBJECTS is the list of
+ // input objects, THIS_BLOCKER is a token which blocks this task
+ // from executing until all the input symbols have been read.
+ Layout_task(const General_options& options, const Object_list* input_objects,
+ Task_token* this_blocker)
+ : options_(options), input_objects_(input_objects),
+ this_blocker_(this_blocker)
+ { }
+
+ ~Layout_task();
+
+ // The standard Task methods.
+
+ Is_runnable_type
+ is_runnable(Workqueue*);
+
+ Task_locker*
+ locks(Workqueue*);
+
+ void
+ run(Workqueue*);
+
+ private:
+ Layout_task(const Layout_task&);
+ Layout_task& operator=(const Layout_task&);
+
+ const General_options& options_;
+ const Object_list* input_objects_;
+ Task_token* this_blocker_;
+};
+
+// This class handles the details of laying out input sections.
+
+class Layout
+{
+ public:
+ Layout(const General_options& options)
+ : options_(options), namepool_(), signatures_(),
+ section_name_map_(), segment_list_()
+ { }
+
+ // Given an input section named NAME with data in SHDR from the
+ // object file OBJECT, return the output section where this input
+ // section should go. Set *OFFSET to the offset within the output
+ // section.
+ template<int size, bool big_endian>
+ Output_section*
+ layout(Object *object, const char* name,
+ const elfcpp::Shdr<size, big_endian>& shdr, off_t* offset);
+
+ // Return whether a section is a .gnu.linkonce section, given the
+ // section name.
+ static inline bool
+ is_linkonce(const char* name)
+ { return strncmp(name, ".gnu.linkonce", sizeof(".gnu.linkonce") - 1) == 0; }
+
+ // Record the signature of a comdat section, and return whether to
+ // include it in the link. The GROUP parameter is true for a
+ // section group signature, false for a signature derived from a
+ // .gnu.linkonce section.
+ bool
+ add_comdat(const char*, bool group);
+
+ private:
+ Layout(const Layout&);
+ Layout& operator=(const Layout&);
+
+ // Mapping from .gnu.linkonce section names to output section names.
+ struct Linkonce_mapping
+ {
+ const char* from;
+ int fromlen;
+ const char* to;
+ };
+ static const Linkonce_mapping linkonce_mapping[];
+ static const int linkonce_mapping_count;
+
+ // Return whether to include this section in the link.
+ template<int size, bool big_endian>
+ bool
+ include_section(Object* object, const char* name,
+ const elfcpp::Shdr<size, big_endian>&);
+
+ // Return the output section name to use for a linkonce section
+ // name.
+ static const char*
+ linkonce_output_name(const char* name);
+
+ // Create a new Output_section.
+ Output_section*
+ make_output_section(const char* name, elfcpp::Elf_Word type,
+ elfcpp::Elf_Xword flags);
+
+ // Return whether SEG1 comes before SEG2 in the output file.
+ static bool
+ Layout::segment_precedes(const Output_segment* seg1,
+ const Output_segment* seg2);
+
+ // Map from section flags to segment flags.
+ static elfcpp::Elf_Word
+ section_flags_to_segment(elfcpp::Elf_Xword flags);
+
+ // A mapping used for group signatures.
+ typedef Unordered_map<std::string, bool> Signatures;
+
+ // Mapping from input section name/type/flags to output section. We
+ // use canonicalized strings here.
+
+ typedef std::pair<const char*,
+ std::pair<elfcpp::Elf_Word, elfcpp::Elf_Xword> > Key;
+
+ struct Hash_key
+ {
+ size_t
+ operator()(const Key& k) const;
+ };
+
+ typedef Unordered_map<Key, Output_section*, Hash_key> Section_name_map;
+
+ // A comparison class for segments.
+
+ struct Compare_segments
+ {
+ bool
+ operator()(const Output_segment* seg1, const Output_segment* seg2)
+ { return Layout::segment_precedes(seg1, seg2); }
+ };
+
+ // The list of segments.
+
+ typedef std::list<Output_segment*> Segment_list;
+
+ // The list of sections not attached to a segment.
+
+ typedef std::list<Output_section*> Section_list;
+
+ // A reference to the options on the command line.
+ const General_options& options_;
+ // The output section names.
+ Stringpool namepool_;
+ // The list of group sections and linkonce sections which we have seen.
+ Signatures signatures_;
+ // The mapping from input section name/type/flags to output sections.
+ Section_name_map section_name_map_;
+ // The list of output segments.
+ Segment_list segment_list_;
+ // The list of output sections which are not attached to any output
+ // segment.
+ Section_list section_list_;
+};
+
+} // End namespace gold.
+
+#endif // !defined(GOLD_LAYOUT_H)
diff --git a/gold/object.cc b/gold/object.cc
index bad7f478467..0e00c43130a 100644
--- a/gold/object.cc
+++ b/gold/object.cc
@@ -8,6 +8,7 @@
#include "object.h"
#include "target-select.h"
+#include "layout.h"
namespace gold
{
@@ -42,28 +43,24 @@ Sized_object<size, big_endian>::Sized_object(
off_t offset,
const elfcpp::Ehdr<size, big_endian>& ehdr)
: Object(name, input_file, false, offset),
- osabi_(ehdr.get_e_ident()[elfcpp::EI_OSABI]),
- abiversion_(ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]),
- machine_(ehdr.get_e_machine()),
flags_(ehdr.get_e_flags()),
shoff_(ehdr.get_e_shoff()),
- shnum_(0),
shstrndx_(0),
symtab_shnum_(0),
symbols_(NULL)
{
- if (ehdr.get_e_ehsize() != elfcpp::Elf_sizes<size>::ehdr_size)
+ if (ehdr.get_e_ehsize() != This::ehdr_size)
{
fprintf(stderr, _("%s: %s: bad e_ehsize field (%d != %d)\n"),
program_name, this->name().c_str(), ehdr.get_e_ehsize(),
- elfcpp::Elf_sizes<size>::ehdr_size);
+ This::ehdr_size);
gold_exit(false);
}
- if (ehdr.get_e_shentsize() != elfcpp::Elf_sizes<size>::shdr_size)
+ if (ehdr.get_e_shentsize() != This::shdr_size)
{
fprintf(stderr, _("%s: %s: bad e_shentsize field (%d != %d)\n"),
program_name, this->name().c_str(), ehdr.get_e_shentsize(),
- elfcpp::Elf_sizes<size>::shdr_size);
+ This::shdr_size);
gold_exit(false);
}
}
@@ -81,12 +78,14 @@ void
Sized_object<size, big_endian>::setup(
const elfcpp::Ehdr<size, big_endian>& ehdr)
{
- Target* target = select_target(this->machine_, size, big_endian,
- this->osabi_, this->abiversion_);
+ int machine = ehdr.get_e_machine();
+ Target* target = select_target(machine, size, big_endian,
+ ehdr.get_e_ident()[elfcpp::EI_OSABI],
+ ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]);
if (target == NULL)
{
fprintf(stderr, _("%s: %s: unsupported ELF machine number %d\n"),
- program_name, this->name().c_str(), this->machine_);
+ program_name, this->name().c_str(), machine);
gold_exit(false);
}
this->set_target(target);
@@ -95,25 +94,24 @@ Sized_object<size, big_endian>::setup(
if ((shnum == 0 || shstrndx == elfcpp::SHN_XINDEX)
&& this->shoff_ != 0)
{
- const unsigned char* p = this->get_view
- (this->shoff_, elfcpp::Elf_sizes<size>::shdr_size);
+ const unsigned char* p = this->get_view (this->shoff_, This::shdr_size);
elfcpp::Shdr<size, big_endian> shdr(p);
if (shnum == 0)
shnum = shdr.get_sh_size();
if (shstrndx == elfcpp::SHN_XINDEX)
shstrndx = shdr.get_sh_link();
}
- this->shnum_ = shnum;
+ this->set_shnum(shnum);
this->shstrndx_ = shstrndx;
if (shnum == 0)
return;
// Find the SHT_SYMTAB section.
- const unsigned char* p = this->get_view
- (this->shoff_, shnum * elfcpp::Elf_sizes<size>::shdr_size);
+ const unsigned char* p = this->get_view (this->shoff_,
+ shnum * This::shdr_size);
// Skip the first section, which is always empty.
- p += elfcpp::Elf_sizes<size>::shdr_size;
+ p += This::shdr_size;
for (unsigned int i = 1; i < shnum; ++i)
{
elfcpp::Shdr<size, big_endian> shdr(p);
@@ -122,7 +120,7 @@ Sized_object<size, big_endian>::setup(
this->symtab_shnum_ = i;
break;
}
- p += elfcpp::Elf_sizes<size>::shdr_size;
+ p += This::shdr_size;
}
}
@@ -143,7 +141,7 @@ Sized_object<size, big_endian>::do_read_symbols()
return ret;
}
- int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
+ const int shdr_size = This::shdr_size;
// Read the symbol table section header.
off_t symtabshdroff = this->shoff_ + (this->symtab_shnum_ * shdr_size);
@@ -152,7 +150,7 @@ Sized_object<size, big_endian>::do_read_symbols()
assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
// We only need the external symbols.
- int sym_size = elfcpp::Elf_sizes<size>::sym_size;
+ const int sym_size = This::sym_size;
off_t locsize = symtabshdr.get_sh_info() * sym_size;
off_t extoff = symtabshdr.get_sh_offset() + locsize;
off_t extsize = symtabshdr.get_sh_size() - locsize;
@@ -162,7 +160,7 @@ Sized_object<size, big_endian>::do_read_symbols()
// Read the section header for the symbol names.
unsigned int strtab_shnum = symtabshdr.get_sh_link();
- if (strtab_shnum == 0 || strtab_shnum >= this->shnum_)
+ if (strtab_shnum == 0 || strtab_shnum >= this->shnum())
{
fprintf(stderr, _("%s: %s: invalid symbol table name index: %u\n"),
program_name, this->name().c_str(), strtab_shnum);
@@ -206,7 +204,7 @@ Sized_object<size, big_endian>::do_add_symbols(Symbol_table* symtab,
return;
}
- unsigned int sym_size = elfcpp::Elf_sizes<size>::sym_size;
+ const int sym_size = This::sym_size;
size_t symcount = sd.symbols_size / sym_size;
if (symcount * sym_size != sd.symbols_size)
{
@@ -224,6 +222,226 @@ Sized_object<size, big_endian>::do_add_symbols(Symbol_table* symtab,
reinterpret_cast<const char*>(sd.symbol_names->data());
symtab->add_from_object(this, syms, symcount, sym_names,
sd.symbol_names_size, this->symbols_);
+
+ delete sd.symbols;
+ delete sd.symbol_names;
+}
+
+// Return whether to include a section group in the link. LAYOUT is
+// used to keep track of which section groups we have already seen.
+// INDEX is the index of the section group and SHDR is the section
+// header. If we do not want to include this group, we set bits in
+// OMIT for each section which should be discarded.
+
+template<int size, bool big_endian>
+bool
+Sized_object<size, big_endian>::include_section_group(
+ Layout* layout,
+ unsigned int index,
+ const elfcpp::Shdr<size, big_endian>& shdr,
+ std::vector<bool>* omit)
+{
+ // Read the section contents.
+ const unsigned char* pcon = this->get_view(shdr.get_sh_offset(),
+ shdr.get_sh_size());
+ const elfcpp::Elf_Word* pword =
+ reinterpret_cast<const elfcpp::Elf_Word*>(pcon);
+
+ // The first word contains flags. We only care about COMDAT section
+ // groups. Other section groups are always included in the link
+ // just like ordinary sections.
+ elfcpp::Elf_Word flags = elfcpp::read_elf_word<big_endian>(pword);
+ if ((flags & elfcpp::GRP_COMDAT) == 0)
+ return true;
+
+ // Look up the group signature, which is the name of a symbol. This
+ // is a lot of effort to go to to read a string. Why didn't they
+ // just use the name of the SHT_GROUP section as the group
+ // signature?
+
+ // Get the appropriate symbol table header (this will normally be
+ // the single SHT_SYMTAB section, but in principle it need not be).
+ if (shdr.get_sh_link() >= this->shnum())
+ {
+ fprintf(stderr, _("%s: %s: section group %u link %u out of range\n"),
+ program_name, this->name().c_str(), index, shdr.get_sh_link());
+ gold_exit(false);
+ }
+ off_t off = this->shoff_ + shdr.get_sh_link() * This::shdr_size;
+ const unsigned char* psymshdr = this->get_view(off, This::shdr_size);
+ elfcpp::Shdr<size, big_endian> symshdr(psymshdr);
+
+ // Read the symbol table entry.
+ if (shdr.get_sh_info() >= symshdr.get_sh_size() / This::sym_size)
+ {
+ fprintf(stderr, _("%s: %s: section group %u info %u out of range\n"),
+ program_name, this->name().c_str(), index, shdr.get_sh_info());
+ gold_exit(false);
+ }
+ off_t symoff = symshdr.get_sh_offset() + shdr.get_sh_info() * This::sym_size;
+ const unsigned char* psym = this->get_view(symoff, This::sym_size);
+ elfcpp::Sym<size, big_endian> sym(psym);
+
+ // Read the section header for the symbol table names.
+ if (symshdr.get_sh_link() >= this->shnum())
+ {
+ fprintf(stderr, _("%s; %s: symtab section %u link %u out of range\n"),
+ program_name, this->name().c_str(), shdr.get_sh_link(),
+ symshdr.get_sh_link());
+ gold_exit(false);
+ }
+ off_t symnameoff = this->shoff_ + symshdr.get_sh_link() * This::shdr_size;
+ const unsigned char* psymnamehdr = this->get_view(symnameoff,
+ This::shdr_size);
+ elfcpp::Shdr<size, big_endian> symnamehdr(psymnamehdr);
+
+ // Read the symbol table names.
+ const unsigned char *psymnamesu = this->get_view(symnamehdr.get_sh_offset(),
+ symnamehdr.get_sh_size());
+ const char* psymnames = reinterpret_cast<const char*>(psymnamesu);
+
+ // Get the section group signature.
+ if (sym.get_st_name() >= symnamehdr.get_sh_size())
+ {
+ fprintf(stderr, _("%s: %s: symbol %u name offset %u out of range\n"),
+ program_name, this->name().c_str(), shdr.get_sh_info(),
+ sym.get_st_name());
+ gold_exit(false);
+ }
+
+ const char* signature = psymnames + sym.get_st_name();
+
+ // Record this section group, and see whether we've already seen one
+ // with the same signature.
+ if (layout->add_comdat(signature, true))
+ return true;
+
+ // This is a duplicate. We want to discard the sections in this
+ // group.
+ size_t count = shdr.get_sh_size() / sizeof(elfcpp::Elf_Word);
+ for (size_t i = 1; i < count; ++i)
+ {
+ elfcpp::Elf_Word secnum = elfcpp::read_elf_word<big_endian>(pword + i);
+ if (secnum >= this->shnum())
+ {
+ fprintf(stderr,
+ _("%s: %s: section %u in section group %u out of range"),
+ program_name, this->name().c_str(), secnum,
+ index);
+ gold_exit(false);
+ }
+ (*omit)[secnum] = true;
+ }
+
+ return false;
+}
+
+// Whether to include a linkonce section in the link. NAME is the
+// name of the section and SHDR is the section header.
+
+// Linkonce sections are a GNU extension implemented in the original
+// GNU linker before section groups were defined. The semantics are
+// that we only include one linkonce section with a given name. The
+// name of a linkonce section is normally .gnu.linkonce.T.SYMNAME,
+// where T is the type of section and SYMNAME is the name of a symbol.
+// In an attempt to make linkonce sections interact well with section
+// groups, we try to identify SYMNAME and use it like a section group
+// signature. We want to block section groups with that signature,
+// but not other linkonce sections with that signature. We also use
+// the full name of the linkonce section as a normal section group
+// signature.
+
+template<int size, bool big_endian>
+bool
+Sized_object<size, big_endian>::include_linkonce_section(
+ Layout* layout,
+ const char* name,
+ const elfcpp::Shdr<size, big_endian>&)
+{
+ const char* symname = strrchr(name, '.') + 1;
+ bool omit1 = layout->add_comdat(symname, false);
+ bool omit2 = layout->add_comdat(name, true);
+ return omit1 || omit2;
+}
+
+// Lay out the input sections. We walk through the sections and check
+// whether they should be included in the link. If they should, we
+// pass them to the Layout object, which will return an output section
+// and an offset.
+
+template<int size, bool big_endian>
+void
+Sized_object<size, big_endian>::do_layout(Layout* layout)
+{
+ // This is always called from the main thread. Lock the file to
+ // keep the error checks happy.
+ Task_locker_obj<File_read> frl(this->input_file()->file());
+
+ // Get the section headers.
+ unsigned int shnum = this->shnum();
+ const unsigned char* pshdrs = this->get_view(this->shoff_,
+ shnum * This::shdr_size);
+
+ // Get the section names.
+ const unsigned char* pshdrnames = pshdrs + this->shstrndx_ * This::shdr_size;
+ elfcpp::Shdr<size, big_endian> shdrnames(pshdrnames);
+ typename elfcpp::Elf_types<size>::Elf_WXword names_size =
+ shdrnames.get_sh_size();
+ const unsigned char* pnamesu = this->get_view(shdrnames.get_sh_offset(),
+ shdrnames.get_sh_size());
+ const char* pnames = reinterpret_cast<const char*>(pnamesu);
+
+ std::vector<Map_to_output>& map_sections(this->map_to_output());
+ map_sections.reserve(shnum);
+
+ // Keep track of which sections to omit.
+ std::vector<bool> omit(shnum, false);
+
+ for (unsigned int i = 0; i < shnum; ++i)
+ {
+ elfcpp::Shdr<size, big_endian> shdr(pshdrs);
+
+ if (shdr.get_sh_name() >= names_size)
+ {
+ fprintf(stderr,
+ _("%s: %s: bad section name offset for section %u: %lu\n"),
+ program_name, this->name().c_str(), i,
+ static_cast<unsigned long>(shdr.get_sh_name()));
+ gold_exit(false);
+ }
+
+ const char* name = pnames + shdr.get_sh_name();
+
+ bool discard = omit[i];
+ if (!discard)
+ {
+ if (shdr.get_sh_type() == elfcpp::SHT_GROUP)
+ {
+ if (!this->include_section_group(layout, i, shdr, &omit))
+ discard = true;
+ }
+ else if (Layout::is_linkonce(name))
+ {
+ if (!this->include_linkonce_section(layout, name, shdr))
+ discard = true;
+ }
+ }
+
+ if (discard)
+ {
+ // Do not include this section in the link.
+ map_sections[i].output_section = NULL;
+ continue;
+ }
+
+ off_t offset;
+ Output_section* os = layout->layout(this, name, shdr, &offset);
+
+ map_sections[i].output_section = os;
+ map_sections[i].offset = offset;
+
+ pshdrs += This::shdr_size;
+ }
}
} // End namespace gold.
diff --git a/gold/object.h b/gold/object.h
index b0ee015d754..00265314fb9 100644
--- a/gold/object.h
+++ b/gold/object.h
@@ -4,6 +4,7 @@
#define GOLD_OBJECT_H
#include <cassert>
+#include <vector>
#include "elfcpp.h"
#include "fileread.h"
@@ -13,6 +14,9 @@
namespace gold
{
+class Output_section;
+class Layout;
+
// Data to pass from read_symbols() to add_symbols().
struct Read_symbols_data
@@ -42,7 +46,8 @@ class Object
Object(const std::string& name, Input_file* input_file, bool is_dynamic,
off_t offset = 0)
: name_(name), input_file_(input_file), offset_(offset),
- is_dynamic_(is_dynamic), target_(NULL)
+ shnum_(0), is_dynamic_(is_dynamic), target_(NULL),
+ map_to_output_()
{ }
virtual ~Object()
@@ -58,21 +63,26 @@ class Object
is_dynamic() const
{ return this->is_dynamic_; }
- // Read the symbol and relocation information.
- Read_symbols_data
- read_symbols()
- { return this->do_read_symbols(); }
-
- // Add symbol information to the global symbol table.
- void
- add_symbols(Symbol_table* symtab, Read_symbols_data rd)
- { this->do_add_symbols(symtab, rd); }
-
// Return the target structure associated with this object.
Target*
- target()
+ target() const
{ return this->target_; }
+ // Lock the underlying file.
+ void
+ lock()
+ { this->input_file_->file().lock(); }
+
+ // Unlock the underlying file.
+ void
+ unlock()
+ { this->input_file_->file().unlock(); }
+
+ // Return whether the underlying file is locked.
+ bool
+ is_locked() const
+ { return this->input_file_->file().is_locked(); }
+
// Return the sized target structure associated with this object.
// This is like the target method but it returns a pointer of
// appropriate checked type.
@@ -80,7 +90,35 @@ class Object
Sized_target<size, big_endian>*
sized_target();
+ // Read the symbol and relocation information.
+ Read_symbols_data
+ read_symbols()
+ { return this->do_read_symbols(); }
+
+ // Add symbol information to the global symbol table.
+ void
+ add_symbols(Symbol_table* symtab, Read_symbols_data rd)
+ { this->do_add_symbols(symtab, rd); }
+
+ // Pass sections which should be included in the link to the Layout
+ // object, and record where the sections go in the output file.
+ void
+ layout(Layout* lay)
+ { this->do_layout(lay); }
+
protected:
+ // What we need to know to map an input section to an output
+ // section. We keep an array of these, one for each input section,
+ // indexed by the input section number.
+ struct Map_to_output
+ {
+ // The output section. This is NULL if the input section is to be
+ // discarded.
+ Output_section* output_section;
+ // The offset within the output section.
+ off_t offset;
+ };
+
// Read the symbols--implemented by child class.
virtual Read_symbols_data
do_read_symbols() = 0;
@@ -90,6 +128,10 @@ class Object
virtual void
do_add_symbols(Symbol_table*, Read_symbols_data) = 0;
+ // Lay out sections--implemented by child class.
+ virtual void
+ do_layout(Layout*) = 0;
+
// Get the file.
Input_file*
input_file() const
@@ -104,6 +146,16 @@ class Object
const unsigned char*
get_view(off_t start, off_t size);
+ // Get the number of sections.
+ unsigned int
+ shnum(void) const
+ { return this->shnum_; }
+
+ // Set the number of sections.
+ void
+ set_shnum(int shnum)
+ { this->shnum_ = shnum; }
+
// Set the target.
void
set_target(Target* target)
@@ -117,6 +169,11 @@ class Object
File_view*
get_lasting_view(off_t start, off_t size);
+ // Return the vector mapping input sections to output sections.
+ std::vector<Map_to_output>&
+ map_to_output()
+ { return this->map_to_output_; }
+
private:
// This class may not be copied.
Object(const Object&);
@@ -129,10 +186,14 @@ class Object
// Offset within the file--0 for an object file, non-0 for an
// archive.
off_t offset_;
+ // Number of input sections.
+ unsigned int shnum_;
// Whether this is a dynamic object.
bool is_dynamic_;
// Target functions--may be NULL if the target is not known.
Target* target_;
+ // Mapping from input sections to output section.
+ std::vector<Map_to_output> map_to_output_;
};
// Implement sized_target inline for efficiency. This approach breaks
@@ -158,15 +219,23 @@ class Sized_object : public Object
~Sized_object();
+ // Set up the object file based on the ELF header.
void
setup(const typename elfcpp::Ehdr<size, big_endian>&);
+ // Read the symbols.
Read_symbols_data
do_read_symbols();
+ // Add the symbols to the symbol table.
void
do_add_symbols(Symbol_table*, Read_symbols_data);
+ // Lay out the input sections.
+ void
+ do_layout(Layout*);
+
+ // Return the appropriate Sized_target structure.
Sized_target<size, big_endian>*
sized_target()
{ return this->Object::sized_target<size, big_endian>(); }
@@ -176,18 +245,27 @@ class Sized_object : public Object
Sized_object(const Sized_object&);
Sized_object& operator=(const Sized_object&);
- // ELF file header EI_OSABI field.
- unsigned char osabi_;
- // ELF file header EI_ABIVERSION field.
- unsigned char abiversion_;
- // ELF file header e_machine field.
- elfcpp::Elf_Half machine_;
+ // For convenience.
+ typedef Sized_object<size, big_endian> This;
+ static const int ehdr_size = elfcpp::Elf_sizes<size>::ehdr_size;
+ static const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
+ static const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
+
+ // Whether to include a section group in the link.
+ bool
+ include_section_group(Layout*, unsigned int,
+ const elfcpp::Shdr<size, big_endian>&,
+ std::vector<bool>*);
+
+ // Whether to include a linkonce section in the link.
+ bool
+ include_linkonce_section(Layout*, const char*,
+ const elfcpp::Shdr<size, big_endian>&);
+
// ELF file header e_flags field.
unsigned int flags_;
// File offset of section header table.
off_t shoff_;
- // Number of input sections.
- unsigned int shnum_;
// Offset of SHT_STRTAB section holding section names.
unsigned int shstrndx_;
// Index of SHT_SYMTAB section.
@@ -196,6 +274,10 @@ class Sized_object : public Object
Symbol** symbols_;
};
+// The type of the list of input objects.
+
+typedef std::list<Object*> Object_list;
+
// Return an Object appropriate for the input file. P is BYTES long,
// and holds the ELF header.
diff --git a/gold/options.h b/gold/options.h
index 082927fae3c..6ac1ab9c1e0 100644
--- a/gold/options.h
+++ b/gold/options.h
@@ -14,8 +14,6 @@
#include <list>
-#include "gold.h"
-
namespace gold
{
diff --git a/gold/output.cc b/gold/output.cc
new file mode 100644
index 00000000000..fcba1135a50
--- /dev/null
+++ b/gold/output.cc
@@ -0,0 +1,159 @@
+// output.cc -- manage the output file for gold
+
+#include "gold.h"
+
+#include <cstdlib>
+
+#include "object.h"
+#include "output.h"
+
+namespace gold
+{
+
+// Output_data methods.
+
+Output_data::~Output_data()
+{
+}
+
+// Output_data_const methods.
+
+void
+Output_data_const::write(Output_file* output, off_t off)
+{
+ output->write(off, data_.data(), data_.size());
+}
+
+// Output_section methods.
+
+// Construct an Output_section. NAME will point into a Stringpool.
+
+Output_section::Output_section(const char* name, elfcpp::Elf_Word type,
+ elfcpp::Elf_Xword flags)
+ : name_(name),
+ addr_(0),
+ addralign_(0),
+ entsize_(0),
+ offset_(0),
+ size_(0),
+ link_(0),
+ info_(0),
+ type_(type),
+ flags_(flags)
+{
+}
+
+// Add an input section to an Output_section. We don't keep track of
+// input sections for an Output_section. Instead, each Object keeps
+// track of the Output_section for each of its input sections.
+
+template<int size, bool big_endian>
+off_t
+Output_section::add_input_section(Object* object, const char* secname,
+ const elfcpp::Shdr<size, big_endian>& shdr)
+{
+ elfcpp::Elf_Xword addralign = shdr.get_sh_addralign();
+ if ((addralign & (addralign - 1)) != 0)
+ {
+ fprintf(stderr, _("%s: %s: invalid alignment %lu for section \"%s\"\n"),
+ program_name, object->name().c_str(),
+ static_cast<unsigned long>(addralign), secname);
+ gold_exit(false);
+ }
+ this->size_ = (this->size_ + addralign - 1) &~ (addralign - 1);
+
+ if (addralign > this->addralign_)
+ this->addralign_ = addralign;
+
+ off_t ret = this->size_;
+ this->size_ += shdr.get_sh_size();
+
+ return ret;
+}
+
+// Output segment methods.
+
+Output_segment::Output_segment(elfcpp::Elf_Word type, elfcpp::Elf_Word flags)
+ : output_sections_(),
+ vaddr_(0),
+ paddr_(0),
+ memsz_(0),
+ align_(0),
+ offset_(0),
+ filesz_(0),
+ type_(type),
+ flags_(flags)
+{
+}
+
+// Add an Output_section to an Output_segment.
+
+void
+Output_segment::add_output_section(Output_section* os)
+{
+ // So that PT_NOTE segments will work correctly, we need to ensure
+ // that all SHT_NOTE sections are adjacent. This will normally
+ // happen automatically, because all the SHT_NOTE input sections
+ // will wind up in the same output section. However, it is possible
+ // for multiple SHT_NOTE input sections to have different section
+ // flags, and thus be in different output sections, but for the
+ // different section flags to map into the same segment flags and
+ // thus the same output segment.
+ if (os->type() == elfcpp::SHT_NOTE)
+ {
+ for (Section_list::iterator p = this->output_sections_.begin();
+ p != this->output_sections_.end();
+ ++p)
+ {
+ if ((*p)->type() == elfcpp::SHT_NOTE)
+ {
+ ++p;
+ this->output_sections_.insert(p, os);
+ return;
+ }
+ }
+ }
+
+ this->output_sections_.push_back(os);
+}
+
+// Output_file methods.
+
+void
+Output_file::write(off_t, const void*, off_t)
+{
+ abort();
+}
+
+// Instantiate the templates we need. We could use the configure
+// script to restrict this to only the ones for implemented targets.
+
+template
+off_t
+Output_section::add_input_section<32, false>(
+ Object* object,
+ const char* secname,
+ const elfcpp::Shdr<32, false>& shdr);
+
+template
+off_t
+Output_section::add_input_section<32, true>(
+ Object* object,
+ const char* secname,
+ const elfcpp::Shdr<32, true>& shdr);
+
+template
+off_t
+Output_section::add_input_section<64, false>(
+ Object* object,
+ const char* secname,
+ const elfcpp::Shdr<64, false>& shdr);
+
+template
+off_t
+Output_section::add_input_section<64, true>(
+ Object* object,
+ const char* secname,
+ const elfcpp::Shdr<64, true>& shdr);
+
+} // End namespace gold.
diff --git a/gold/output.h b/gold/output.h
new file mode 100644
index 00000000000..943474b42e0
--- /dev/null
+++ b/gold/output.h
@@ -0,0 +1,214 @@
+// output.h -- manage the output file for gold -*- C++ -*-
+
+#ifndef GOLD_OUTPUT_H
+#define GOLD_OUTPUT_H
+
+#include <list>
+
+#include "elfcpp.h"
+
+namespace gold
+{
+
+class Object;
+class Output_file;
+
+// An abtract class for data which has to go into the output file
+// which is not associated with any input section.
+
+class Output_data
+{
+ public:
+ Output_data(off_t size = 0)
+ : size_(size)
+ { }
+
+ virtual
+ ~Output_data();
+
+ // Return the size of the data.
+ off_t
+ size()
+ { return this->size_; }
+
+ // Write the data to the output file at the specified offset. This
+ // must be implemented by the real class.
+ virtual void
+ write(Output_file*, off_t off) = 0;
+
+ protected:
+ // Set the size of the data.
+ void
+ set_size(off_t size)
+ { this->size_ = size; }
+
+ private:
+ Output_data(const Output_data&);
+ Output_data& operator=(const Output_data&);
+
+ // Size of data in file.
+ off_t size_;
+};
+
+// A simple cass of Output_data in which we have constant data to
+// output.
+
+class Output_data_const : public Output_data
+{
+ public:
+ Output_data_const(const std::string& data)
+ : Output_data(data.size()), data_(data)
+ { }
+
+ Output_data_const(const char* p, off_t len)
+ : Output_data(len), data_(p, len)
+ { }
+
+ void
+ write(Output_file* output, off_t off);
+
+ private:
+ std::string data_;
+};
+
+// An output section. We don't expect to have too many output
+// sections, so we don't bother to do a template on the size.
+
+class Output_section
+{
+ public:
+ // Create an output section, giving the name, type, and flags.
+ Output_section(const char* name, elfcpp::Elf_Word, elfcpp::Elf_Xword);
+ ~Output_section();
+
+ // Add a new input section named NAME with header SHDR from object
+ // OBJECT. Return the offset within the output section.
+ template<int size, bool big_endian>
+ off_t
+ add_input_section(Object* object, const char *name,
+ const elfcpp::Shdr<size, big_endian>& shdr);
+
+ // Return the section name.
+ const char*
+ name() const
+ { return this->name_; }
+
+ // Return the section type.
+ elfcpp::Elf_Word
+ type() const
+ { return this->type_; }
+
+ // Return the section flags.
+ elfcpp::Elf_Xword
+ flags() const
+ { return this->flags_; }
+
+ private:
+ // Most of these fields are only valid after layout.
+
+ // The name of the section. This will point into a Stringpool.
+ const char* name_;
+ // The section address.
+ uint64_t addr_;
+ // The section alignment.
+ uint64_t addralign_;
+ // The section entry size.
+ uint64_t entsize_;
+ // The file offset.
+ off_t offset_;
+ // The section size.
+ off_t size_;
+ // The section link field.
+ unsigned int link_;
+ // The section info field.
+ unsigned int info_;
+ // The section type.
+ elfcpp::Elf_Word type_;
+ // The section flags.
+ elfcpp::Elf_Xword flags_;
+};
+
+// An output segment. PT_LOAD segments are built from collections of
+// output sections. Other segments typically point within PT_LOAD
+// segments, and are built directly as needed.
+
+class Output_segment
+{
+ public:
+ // Create an output segment, specifying the type and flags.
+ Output_segment(elfcpp::Elf_Word, elfcpp::Elf_Word);
+
+ // Return the virtual address.
+ uint64_t
+ vaddr() const
+ { return this->vaddr_; }
+
+ // Return the physical address.
+ uint64_t
+ paddr() const
+ { return this->paddr_; }
+
+ // Return the segment type.
+ elfcpp::Elf_Word
+ type() const
+ { return this->type_; }
+
+ // Return the segment flags.
+ elfcpp::Elf_Word
+ flags() const
+ { return this->flags_; }
+
+ // Add an Output_section to this segment.
+ void
+ add_output_section(Output_section*);
+
+ // Update the segment flags to be compatible with FLAGS.
+ void
+ update_flags(elfcpp::Elf_Word flags)
+ { this->flags_ |= flags & (elfcpp::PF_R | elfcpp::PF_W | elfcpp::PF_X); }
+
+ private:
+ Output_segment(const Output_segment&);
+ Output_segment& operator=(const Output_segment&);
+
+ typedef std::list<Output_section*> Section_list;
+
+ // The list of output sections attached to this segment. This is
+ // cleared after layout.
+ Section_list output_sections_;
+ // The segment virtual address.
+ uint64_t vaddr_;
+ // The segment physical address.
+ uint64_t paddr_;
+ // The size of the segment in memory.
+ uint64_t memsz_;
+ // The segment alignment.
+ uint64_t align_;
+ // The offset of the segment data within the file.
+ off_t offset_;
+ // The size of the segment data in the file.
+ off_t filesz_;
+ // The segment type;
+ elfcpp::Elf_Word type_;
+ // The segment flags.
+ elfcpp::Elf_Word flags_;
+};
+
+// This class represents the output file. The output file is a
+// collection of output segments and a collection of output sections
+// which are not associated with segments.
+
+class Output_file
+{
+ public:
+ Output_file();
+ ~Output_file();
+
+ // Write data to the output file.
+ void
+ write(off_t off, const void* data, off_t len);
+};
+
+} // End namespace gold.
+
+#endif // !defined(GOLD_OUTPUT_H)
diff --git a/gold/po/POTFILES.in b/gold/po/POTFILES.in
index 35c5094a1a3..4bb4aad9ad0 100644
--- a/gold/po/POTFILES.in
+++ b/gold/po/POTFILES.in
@@ -7,10 +7,14 @@ gold.h
gold-threads.cc
gold-threads.h
i386.cc
+layout.cc
+layout.h
object.cc
object.h
options.cc
options.h
+output.cc
+output.h
readsyms.cc
readsyms.h
resolve.cc
@@ -21,6 +25,5 @@ symtab.h
target.h
target-select.cc
target-select.h
-targetsize.h
workqueue.cc
workqueue.h
diff --git a/gold/po/gold.pot b/gold/po/gold.pot
index 5d13088b132..e12e01eb26f 100644
--- a/gold/po/gold.pot
+++ b/gold/po/gold.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2006-09-07 14:17-0700\n"
+"POT-Creation-Date: 2006-09-21 13:30-0700\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -51,7 +51,7 @@ msgstr ""
msgid "%s: cannot open %s: %s"
msgstr ""
-#: gold.cc:72
+#: gold.cc:74
msgid "no input files"
msgstr ""
@@ -99,73 +99,103 @@ msgstr ""
msgid "pthread_cond_signal failed"
msgstr ""
-#: object.cc:57
+#: object.cc:54
#, c-format
msgid "%s: %s: bad e_ehsize field (%d != %d)\n"
msgstr ""
-#: object.cc:64
+#: object.cc:61
#, c-format
msgid "%s: %s: bad e_shentsize field (%d != %d)\n"
msgstr ""
-#: object.cc:88
+#: object.cc:87
#, c-format
msgid "%s: %s: unsupported ELF machine number %d\n"
msgstr ""
-#: object.cc:167
+#: object.cc:165
#, c-format
msgid "%s: %s: invalid symbol table name index: %u\n"
msgstr ""
-#: object.cc:177
+#: object.cc:175
#, c-format
msgid "%s: %s: symbol table name section has wrong type: %u\n"
msgstr ""
-#: object.cc:214
+#: object.cc:212
#, c-format
msgid "%s: %s: size of symbols is not multiple of symbol size\n"
msgstr ""
+#: object.cc:266
+#, c-format
+msgid "%s: %s: section group %u link %u out of range\n"
+msgstr ""
+
+#: object.cc:277
+#, c-format
+msgid "%s: %s: section group %u info %u out of range\n"
+msgstr ""
+
+#: object.cc:288
+#, c-format
+msgid "%s; %s: symtab section %u link %u out of range\n"
+msgstr ""
+
+#: object.cc:306
+#, c-format
+msgid "%s: %s: symbol %u name offset %u out of range\n"
+msgstr ""
+
+#: object.cc:328
+#, c-format
+msgid "%s: %s: section %u in section group %u out of range"
+msgstr ""
+
+#: object.cc:407
+#, c-format
+msgid "%s: %s: bad section name offset for section %u: %lu\n"
+msgstr ""
+
#. elfcpp::ET_DYN
-#: object.cc:262
+#: object.cc:480
#, c-format
msgid "%s: %s: dynamic objects are not yet supported\n"
msgstr ""
-#: object.cc:286 object.cc:339 object.cc:360
+#: object.cc:504 object.cc:557 object.cc:578
#, c-format
msgid "%s: %s: ELF file too short\n"
msgstr ""
-#: object.cc:295
+#: object.cc:513
#, c-format
msgid "%s: %s: invalid ELF version 0\n"
msgstr ""
-#: object.cc:298
+#: object.cc:516
#, c-format
msgid "%s: %s: unsupported ELF version %d\n"
msgstr ""
-#: object.cc:306
+#: object.cc:524
#, c-format
msgid "%s: %s: invalid ELF class 0\n"
msgstr ""
-#: object.cc:313
+#: object.cc:531
#, c-format
msgid "%s: %s: unsupported ELF class %d\n"
msgstr ""
-#: object.cc:321
+#: object.cc:539
#, c-format
msgid "%s: %s: invalid ELF data encoding\n"
msgstr ""
-#: object.cc:328
+#: object.cc:546
#, c-format
msgid "%s: %s: unsupported ELF data encoding %d\n"
msgstr ""
@@ -220,6 +250,11 @@ msgstr ""
msgid "%s: -%c: %s\n"
msgstr ""
+#: output.cc:58
+#, c-format
+msgid "%s: %s: invalid alignment %lu for section \"%s\"\n"
+msgstr ""
+
#: resolve.cc:135
#, c-format
msgid "%s: %s: invalid STB_LOCAL symbol %s in external symbols\n"
diff --git a/gold/readsyms.cc b/gold/readsyms.cc
index 1076e6c17ef..aa32e368e85 100644
--- a/gold/readsyms.cc
+++ b/gold/readsyms.cc
@@ -8,6 +8,7 @@
#include "options.h"
#include "dirsearch.h"
#include "readsyms.h"
+#include "object.h"
namespace gold
{
@@ -49,8 +50,6 @@ Read_symbols::locks(Workqueue*)
void
Read_symbols::run(Workqueue* workqueue)
{
- // We don't keep track of Input_file objects, so this is a memory
- // leak.
Input_file* input_file = new Input_file(this->input_);
input_file->open(this->options_, this->dirpath_);
@@ -71,7 +70,9 @@ Read_symbols::run(Workqueue* workqueue)
// This is an ELF object.
Object* obj = make_elf_object(this->input_.name(), input_file, 0,
p, bytes);
-
+
+ this->input_objects_->push_back(obj);
+
Read_symbols_data sd = obj->read_symbols();
workqueue->queue(new Add_symbols(this->symtab_, obj, sd,
this->this_blocker_,
@@ -99,20 +100,37 @@ Add_symbols::~Add_symbols()
// input file.
}
-// We are blocked by this_blocker_. We block next_blocker_.
+// We are blocked by this_blocker_. We block next_blocker_. We also
+// lock the file.
Task::Is_runnable_type
Add_symbols::is_runnable(Workqueue*)
{
if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
return IS_BLOCKED;
+ if (this->object_->is_locked())
+ return IS_LOCKED;
return IS_RUNNABLE;
}
+class Add_symbols::Add_symbols_locker : public Task_locker
+{
+ public:
+ Add_symbols_locker(Task_token& token, Workqueue* workqueue,
+ Object* object)
+ : blocker_(token, workqueue), objlock_(*object)
+ { }
+
+ private:
+ Task_locker_block blocker_;
+ Task_locker_obj<Object> objlock_;
+};
+
Task_locker*
Add_symbols::locks(Workqueue* workqueue)
{
- return new Task_locker_block(*this->next_blocker_, workqueue);
+ return new Add_symbols_locker(*this->next_blocker_, workqueue,
+ this->object_);
}
void
diff --git a/gold/readsyms.h b/gold/readsyms.h
index f01cf6108e2..73d4efe130d 100644
--- a/gold/readsyms.h
+++ b/gold/readsyms.h
@@ -3,7 +3,6 @@
#ifndef GOLD_READSYMS_H
#define GOLD_READSYMS_H
-#include "targetsize.h"
#include "workqueue.h"
#include "object.h"
@@ -26,11 +25,13 @@ class Read_symbols : public Task
// associated Add_symbols task from running before the previous one
// has completed; it will be NULL for the first task. NEXT_BLOCKER
// is used to block the next input file from adding symbols.
- Read_symbols(const General_options& options, Symbol_table* symtab,
- const Dirsearch& dirpath, const Input_argument& input,
+ Read_symbols(const General_options& options, Object_list* input_objects,
+ Symbol_table* symtab, const Dirsearch& dirpath,
+ const Input_argument& input,
Task_token* this_blocker, Task_token* next_blocker)
- : options_(options), symtab_(symtab), dirpath_(dirpath), input_(input),
- this_blocker_(this_blocker), next_blocker_(next_blocker)
+ : options_(options), input_objects_(input_objects), symtab_(symtab),
+ dirpath_(dirpath), input_(input), this_blocker_(this_blocker),
+ next_blocker_(next_blocker)
{ }
~Read_symbols();
@@ -48,6 +49,7 @@ class Read_symbols : public Task
private:
const General_options& options_;
+ Object_list* input_objects_;
Symbol_table* symtab_;
const Dirsearch& dirpath_;
const Input_argument& input_;
@@ -85,6 +87,8 @@ class Add_symbols : public Task
run(Workqueue*);
private:
+ class Add_symbols_locker;
+
Symbol_table* symtab_;
Object* object_;
Read_symbols_data sd_;
diff --git a/gold/stringpool.cc b/gold/stringpool.cc
index 0368014eb26..9a709e00d89 100644
--- a/gold/stringpool.cc
+++ b/gold/stringpool.cc
@@ -101,7 +101,7 @@ Stringpool::add(const char* s)
{
// FIXME: This will look up the entry twice in the hash table. The
// problem is that we can't insert S before we canonicalize it. I
- // don't think there is a way to handle this correct with
+ // don't think there is a way to handle this correctly with
// unordered_set, so this should be replaced with custom code to do
// what we need, which is to return the empty slot.
diff --git a/gold/symtab.h b/gold/symtab.h
index a90ba5db8ab..a7d3423bdf4 100644
--- a/gold/symtab.h
+++ b/gold/symtab.h
@@ -7,7 +7,6 @@
#include <utility>
#include "elfcpp.h"
-#include "targetsize.h"
#include "stringpool.h"
#ifndef GOLD_SYMTAB_H
diff --git a/gold/targetsize.h b/gold/targetsize.h
deleted file mode 100644
index 61f986d333d..00000000000
--- a/gold/targetsize.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// targetsize.h -- representations which change size based on the target
-
-#ifndef GOLD_TARGETSIZE_H
-#define GOLD_TARGETSIZE_H
-
-// Tell GNU/Linux inttypes.h that we want the formatting macros.
-#define __STDC_FORMAT_MACROS
-
-#include <stdint.h>
-#include <inttypes.h>
-
-namespace gold
-{
-
-// A template we use to get the right type sizes via specialization.
-// We never actually use the default version.
-
-template<int size>
-struct Size_types
-{
- typedef signed char Signed_type;
- typedef unsigned char Unsigned_type;
- static const char* signed_printf_dec_format();
- static const char* unsigned_printf_dec_format();
- static const char* unsigned_printf_hex_format();
-};
-
-template<>
-struct Size_types<32>
-{
- typedef int32_t Signed_type;
- typedef uint32_t Unsigned_type;
- static const char* signed_printf_dec_format()
- { return PRId32; }
- static const char* unsigned_printf_dec_format()
- { return PRIu32; }
- static const char* unsigned_printf_hex_format()
- { return PRIx32; }
-};
-
-template<>
-struct Size_types<64>
-{
- typedef int64_t Signed_type;
- typedef uint64_t Unsigned_type;
- static const char* signed_printf_dec_format()
- { return PRId64; }
- static const char* unsigned_printf_dec_format()
- { return PRIu64; }
- static const char* unsigned_printf_hex_format()
- { return PRIx64; }
-};
-
-} // End namespace gold.
-
-#endif // !defined(GOLD_TARGETSIZE_H)
diff --git a/gold/workqueue.h b/gold/workqueue.h
index 5949cf5f439..a97d86d4c6f 100644
--- a/gold/workqueue.h
+++ b/gold/workqueue.h
@@ -146,6 +146,27 @@ class Task_block_token
Workqueue* workqueue_;
};
+// An object which implements an RAII lock for any object which
+// supports lock and unlock methods.
+
+template<typename Obj>
+class Task_lock_obj
+{
+ public:
+ Task_lock_obj(Obj& obj)
+ : obj_(obj)
+ { this->obj_.lock(); }
+
+ ~Task_lock_obj()
+ { this->obj_.unlock(); }
+
+ private:
+ Task_lock_obj(const Task_lock_obj&);
+ Task_lock_obj& operator=(const Task_lock_obj&);
+
+ Obj& obj_;
+};
+
// An abstract class used to lock Task_tokens using RAII. A typical
// implementation would simply have a set of members of type
// Task_read_token, Task_write_token, and Task_block_token.
@@ -209,21 +230,22 @@ class Task_locker_block : public Task_locker
Task_block_token block_token_;
};
-// A version of Task_locker which may be used to hold a lock on a
-// File_read.
+// A version of Task_locker which may be used to hold a lock on any
+// object which supports lock() and unlock() methods.
-class Task_locker_file : public Task_locker
+template<typename Obj>
+class Task_locker_obj : public Task_locker
{
public:
- Task_locker_file(File_read& file)
- : file_lock_(file)
+ Task_locker_obj(Obj& obj)
+ : obj_lock_(obj)
{ }
private:
- Task_locker_file(const Task_locker_file&);
- Task_locker_file& operator=(const Task_locker_file&);
+ Task_locker_obj(const Task_locker_obj&);
+ Task_locker_obj& operator=(const Task_locker_obj&);
- File_read_lock file_lock_;
+ Task_lock_obj<Obj> obj_lock_;
};
// The superclass for tasks to be placed on the workqueue. Each