diff options
author | Ian Lance Taylor <iant@google.com> | 2006-09-21 22:13:18 +0000 |
---|---|---|
committer | Ian Lance Taylor <iant@google.com> | 2006-09-21 22:13:18 +0000 |
commit | a2fb1b05e4af3fac54faac6c07a4717f2cb34aae (patch) | |
tree | cab19eb8c3abe76aee65d684dcceee884ed59c61 /gold | |
parent | 5ffff7c1d1d2db595e4c23a8c388d3a51d5bb357 (diff) | |
download | gdb-a2fb1b05e4af3fac54faac6c07a4717f2cb34aae.zip gdb-a2fb1b05e4af3fac54faac6c07a4717f2cb34aae.tar.gz gdb-a2fb1b05e4af3fac54faac6c07a4717f2cb34aae.tar.bz2 |
New drop, with first cut of section layout code.
Diffstat (limited to 'gold')
-rw-r--r-- | gold/Makefile.am | 5 | ||||
-rw-r--r-- | gold/Makefile.in | 15 | ||||
-rw-r--r-- | gold/fileread.h | 16 | ||||
-rw-r--r-- | gold/gold.cc | 17 | ||||
-rw-r--r-- | gold/layout.cc | 432 | ||||
-rw-r--r-- | gold/layout.h | 181 | ||||
-rw-r--r-- | gold/object.cc | 262 | ||||
-rw-r--r-- | gold/object.h | 122 | ||||
-rw-r--r-- | gold/options.h | 2 | ||||
-rw-r--r-- | gold/output.cc | 159 | ||||
-rw-r--r-- | gold/output.h | 214 | ||||
-rw-r--r-- | gold/po/POTFILES.in | 5 | ||||
-rw-r--r-- | gold/po/gold.pot | 67 | ||||
-rw-r--r-- | gold/readsyms.cc | 28 | ||||
-rw-r--r-- | gold/readsyms.h | 14 | ||||
-rw-r--r-- | gold/stringpool.cc | 2 | ||||
-rw-r--r-- | gold/symtab.h | 1 | ||||
-rw-r--r-- | gold/targetsize.h | 56 | ||||
-rw-r--r-- | gold/workqueue.h | 38 |
19 files changed, 1472 insertions, 164 deletions
diff --git a/gold/Makefile.am b/gold/Makefile.am index a01aef4..ed26af9 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 8881517..6dba59c 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 2181485..b65a86b 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 e419f9c..02e7366 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 0000000..3faec94 --- /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 0000000..77fdfc2 --- /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 bad7f47..0e00c43 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 b0ee015..0026531 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 082927f..6ac1ab9 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 0000000..fcba113 --- /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 0000000..943474b --- /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 35c5094..4bb4aad 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 5d13088..e12e01e 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 1076e6c..aa32e36 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 f01cf61..73d4efe 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 0368014..9a709e0 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 a90ba5d..a7d3423 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 61f986d..0000000 --- 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 5949cf5..a97d86d 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 |