diff options
Diffstat (limited to 'gold')
-rw-r--r-- | gold/Makefile.am | 15 | ||||
-rw-r--r-- | gold/Makefile.in | 27 | ||||
-rw-r--r-- | gold/gold.cc | 10 | ||||
-rw-r--r-- | gold/i386.cc | 47 | ||||
-rw-r--r-- | gold/object.cc | 65 | ||||
-rw-r--r-- | gold/object.h | 74 | ||||
-rw-r--r-- | gold/po/POTFILES.in | 7 | ||||
-rw-r--r-- | gold/po/gold.pot | 59 | ||||
-rw-r--r-- | gold/readsyms.cc | 5 | ||||
-rw-r--r-- | gold/readsyms.h | 16 | ||||
-rw-r--r-- | gold/resolve.cc | 349 | ||||
-rw-r--r-- | gold/stringpool.cc | 130 | ||||
-rw-r--r-- | gold/stringpool.h | 70 | ||||
-rw-r--r-- | gold/strtab.h | 73 | ||||
-rw-r--r-- | gold/symtab.cc | 358 | ||||
-rw-r--r-- | gold/symtab.h | 214 | ||||
-rw-r--r-- | gold/target-select.cc | 52 | ||||
-rw-r--r-- | gold/target-select.h | 69 | ||||
-rw-r--r-- | gold/target.h | 93 |
19 files changed, 1625 insertions, 108 deletions
diff --git a/gold/Makefile.am b/gold/Makefile.am index acff0a4..a01aef4 100644 --- a/gold/Makefile.am +++ b/gold/Makefile.am @@ -17,7 +17,7 @@ INCLUDES = -D_GNU_SOURCE \ noinst_PROGRAMS = ld-new -CFILES = \ +CCFILES = \ dirsearch.cc \ fileread.cc \ gold.cc \ @@ -25,6 +25,10 @@ CFILES = \ object.cc \ options.cc \ readsyms.cc \ + resolve.cc \ + symtab.cc \ + stringpool.cc \ + target-select.cc \ workqueue.cc HFILES = \ @@ -35,20 +39,25 @@ HFILES = \ object.h \ options.h \ readsyms.h \ + stringpool.h \ symtab.h \ target.h \ targetsize.h \ + target-select.h \ workqueue.h +TARGETFILES = \ + i386.cc + OFILES = gold.o options.o -POTFILES= $(CFILES) $(HFILES) +POTFILES= $(CCFILES) $(HFILES) $(TARGETFILES) po/POTFILES.in: @MAINT@ Makefile for f in $(POTFILES); do echo $$f; done | LC_COLLATE= sort > tmp \ && mv tmp $(srcdir)/po/POTFILES.in -ld_new_SOURCES = $(CFILES) $(HFILES) +ld_new_SOURCES = $(CCFILES) $(HFILES) $(TARGETFILES) ld_new_DEPENDENCIES = $(LIBINTL_DEP) ld_new_LDADD = $(LIBINTL) diff --git a/gold/Makefile.in b/gold/Makefile.in index 31a01c4..8881517 100644 --- a/gold/Makefile.in +++ b/gold/Makefile.in @@ -67,9 +67,12 @@ 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) workqueue.$(OBJEXT) + readsyms.$(OBJEXT) resolve.$(OBJEXT) symtab.$(OBJEXT) \ + stringpool.$(OBJEXT) target-select.$(OBJEXT) \ + workqueue.$(OBJEXT) am__objects_2 = -am_ld_new_OBJECTS = $(am__objects_1) $(am__objects_2) +am__objects_3 = i386.$(OBJEXT) +am_ld_new_OBJECTS = $(am__objects_1) $(am__objects_2) $(am__objects_3) ld_new_OBJECTS = $(am_ld_new_OBJECTS) am__DEPENDENCIES_1 = DEFAULT_INCLUDES = -I. -I$(srcdir) -I. @@ -225,7 +228,7 @@ INCLUDES = -D_GNU_SOURCE \ -DLOCALEDIR="\"$(datadir)/locale\"" \ @INCINTL@ -CFILES = \ +CCFILES = \ dirsearch.cc \ fileread.cc \ gold.cc \ @@ -233,6 +236,10 @@ CFILES = \ object.cc \ options.cc \ readsyms.cc \ + resolve.cc \ + symtab.cc \ + stringpool.cc \ + target-select.cc \ workqueue.cc HFILES = \ @@ -243,14 +250,19 @@ HFILES = \ object.h \ options.h \ readsyms.h \ + stringpool.h \ symtab.h \ target.h \ targetsize.h \ + target-select.h \ workqueue.h +TARGETFILES = \ + i386.cc + OFILES = gold.o options.o -POTFILES = $(CFILES) $(HFILES) -ld_new_SOURCES = $(CFILES) $(HFILES) +POTFILES = $(CCFILES) $(HFILES) $(TARGETFILES) +ld_new_SOURCES = $(CCFILES) $(HFILES) $(TARGETFILES) ld_new_DEPENDENCIES = $(LIBINTL_DEP) ld_new_LDADD = $(LIBINTL) all: config.h @@ -327,9 +339,14 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileread.Po@am__quote@ @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)/object.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/options.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@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symtab.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/target-select.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/workqueue.Po@am__quote@ .cc.o: diff --git a/gold/gold.cc b/gold/gold.cc index fd349dd..e419f9c 100644 --- a/gold/gold.cc +++ b/gold/gold.cc @@ -11,6 +11,7 @@ #include "workqueue.h" #include "dirsearch.h" #include "readsyms.h" +#include "symtab.h" namespace gold { @@ -65,7 +66,7 @@ void queue_initial_tasks(const General_options& options, const Dirsearch& search_path, const Command_line::Input_argument_list& inputs, - Workqueue* workqueue) + Workqueue* workqueue, Symbol_table* symtab) { if (inputs.empty()) gold_fatal(_("no input files"), false); @@ -81,8 +82,8 @@ queue_initial_tasks(const General_options& options, { Task_token* next_blocker = new Task_token(); next_blocker->add_blocker(); - workqueue->queue(new Read_symbols(options, search_path, *p, this_blocker, - next_blocker)); + workqueue->queue(new Read_symbols(options, symtab, search_path, + *p, this_blocker, next_blocker)); this_blocker = next_blocker; } @@ -113,6 +114,7 @@ main(int argc, char** argv) gold::Workqueue workqueue(command_line.options()); // The symbol table. + Symbol_table symtab; // Get the search path from the -L options. Dirsearch search_path; @@ -120,7 +122,7 @@ main(int argc, char** argv) // Queue up the first set of tasks. queue_initial_tasks(command_line.options(), search_path, - command_line.inputs(), &workqueue); + command_line.inputs(), &workqueue, &symtab); // Run the main task processing loop. workqueue.process(); diff --git a/gold/i386.cc b/gold/i386.cc new file mode 100644 index 0000000..5ecd2e3 --- /dev/null +++ b/gold/i386.cc @@ -0,0 +1,47 @@ +// i386.cc -- i386 target support for gold. + +#include "gold.h" +#include "elfcpp.h" +#include "target.h" +#include "target-select.h" + +namespace +{ + +using namespace gold; + +// The i386 target class. + +class Target_i386 : public Sized_target<32, false> +{ + public: + Target_i386() + : Sized_target<32, false>(false, false) + { } +}; + +// The selector for i386 object files. + +class Target_selector_i386 : public Target_selector +{ +public: + Target_selector_i386() + : Target_selector(elfcpp::EM_386, 32, false) + { } + + Target* + recognize(int machine, int osabi, int abiversion) const; +}; + +// Recognize an i386 object file when we already know that the machine +// number is EM_386. + +Target* +Target_selector_i386::recognize(int, int, int) const +{ + return new Target_i386(); +} + +Target_selector_i386 target_selector_i386; + +} // End anonymous namespace. diff --git a/gold/object.cc b/gold/object.cc index 8835915..bad7f47 100644 --- a/gold/object.cc +++ b/gold/object.cc @@ -7,6 +7,7 @@ #include <cassert> #include "object.h" +#include "target-select.h" namespace gold { @@ -40,16 +41,16 @@ Sized_object<size, big_endian>::Sized_object( Input_file* input_file, off_t offset, const elfcpp::Ehdr<size, big_endian>& ehdr) - : Object(name, input_file, offset), + : 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()), - target_(NULL), shoff_(ehdr.get_e_shoff()), shnum_(0), shstrndx_(0), - symtab_shnum_(0) + symtab_shnum_(0), + symbols_(NULL) { if (ehdr.get_e_ehsize() != elfcpp::Elf_sizes<size>::ehdr_size) { @@ -80,8 +81,15 @@ void Sized_object<size, big_endian>::setup( const elfcpp::Ehdr<size, big_endian>& ehdr) { - // this->target_ = select_target(this->machine_, size, big_endian, - // this->osabi_, this->abiversion_); + Target* target = select_target(this->machine_, size, big_endian, + this->osabi_, this->abiversion_); + if (target == NULL) + { + fprintf(stderr, _("%s: %s: unsupported ELF machine number %d\n"), + program_name, this->name().c_str(), this->machine_); + gold_exit(false); + } + this->set_target(target); unsigned int shnum = ehdr.get_e_shnum(); unsigned int shstrndx = ehdr.get_e_shstrndx(); if ((shnum == 0 || shstrndx == elfcpp::SHN_XINDEX) @@ -143,9 +151,14 @@ Sized_object<size, big_endian>::do_read_symbols() elfcpp::Shdr<size, big_endian> symtabshdr(psymtabshdr); assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB); + // We only need the external symbols. + int sym_size = elfcpp::Elf_sizes<size>::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; + // Read the symbol table. - File_view* fvsymtab = this->get_lasting_view(symtabshdr.get_sh_offset(), - symtabshdr.get_sh_size()); + File_view* fvsymtab = this->get_lasting_view(extoff, extsize); // Read the section header for the symbol names. unsigned int strtab_shnum = symtabshdr.get_sh_link(); @@ -173,7 +186,7 @@ Sized_object<size, big_endian>::do_read_symbols() Read_symbols_data ret; ret.symbols = fvsymtab; - ret.symbols_size = symtabshdr.get_sh_size(); + ret.symbols_size = extsize; ret.symbol_names = fvstrtab; ret.symbol_names_size = strtabshdr.get_sh_size(); @@ -184,7 +197,8 @@ Sized_object<size, big_endian>::do_read_symbols() template<int size, bool big_endian> void -Sized_object<size, big_endian>::do_add_symbols(Read_symbols_data sd) +Sized_object<size, big_endian>::do_add_symbols(Symbol_table* symtab, + Read_symbols_data sd) { if (sd.symbols == NULL) { @@ -192,25 +206,24 @@ Sized_object<size, big_endian>::do_add_symbols(Read_symbols_data sd) return; } - int sym_size = elfcpp::Elf_sizes<size>::sym_size; - const unsigned char* symstart = sd.symbols->data(); - const unsigned char* symend = symstart + sd.symbols_size; - for (const unsigned char* p = symstart; p < symend; p += sym_size) + unsigned int sym_size = elfcpp::Elf_sizes<size>::sym_size; + size_t symcount = sd.symbols_size / sym_size; + if (symcount * sym_size != sd.symbols_size) { - elfcpp::Sym<size, big_endian> sym(p); - - unsigned int nameoff = sym.get_st_name(); - if (nameoff >= sd.symbol_names_size) - { - fprintf(stderr, - _("%s: %s: invalid symbol name offset %u for symbol %d\n"), - program_name, this->name().c_str(), nameoff, - (p - symstart) / sym_size); - gold_exit(false); - } - const unsigned char* name = sd.symbol_names->data() + nameoff; - printf("%s\n", name); + fprintf(stderr, + _("%s: %s: size of symbols is not multiple of symbol size\n"), + program_name, this->name().c_str()); + gold_exit(false); } + + this->symbols_ = new Symbol*[symcount]; + + const elfcpp::Sym<size, big_endian>* syms = + reinterpret_cast<const elfcpp::Sym<size, big_endian>*>(sd.symbols->data()); + const char* sym_names = + reinterpret_cast<const char*>(sd.symbol_names->data()); + symtab->add_from_object(this, syms, symcount, sym_names, + sd.symbol_names_size, this->symbols_); } } // End namespace gold. diff --git a/gold/object.h b/gold/object.h index 9d63b11..b0ee015 100644 --- a/gold/object.h +++ b/gold/object.h @@ -3,10 +3,12 @@ #ifndef GOLD_OBJECT_H #define GOLD_OBJECT_H +#include <cassert> + #include "elfcpp.h" -#include "targetsize.h" -#include "target.h" #include "fileread.h" +#include "target.h" +#include "symtab.h" namespace gold { @@ -26,8 +28,9 @@ struct Read_symbols_data }; // Object is an interface which represents either a 32-bit or a 64-bit -// object file. The actual instantiations are Sized_object<32> and -// Sized_object<64> +// input object. This can be a regular object file (ET_REL) or a +// shared object (ET_DYN). The actual instantiations are +// Sized_object<32> and Sized_object<64> class Object { @@ -36,17 +39,25 @@ class Object // (e.g., libfoo.a(bar.o) if this is in an archive. INPUT_FILE is // used to read the file. OFFSET is the offset within the input // file--0 for a .o or .so file, something else for a .a file. - Object(const std::string& name, Input_file* input_file, off_t offset = 0) - : name_(name), input_file_(input_file), offset_(offset) + 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) { } virtual ~Object() { } + // Return the name of the object as we would report it to the tuser. const std::string& name() const { return this->name_; } + // Return whether this is a dynamic object. + bool + is_dynamic() const + { return this->is_dynamic_; } + // Read the symbol and relocation information. Read_symbols_data read_symbols() @@ -54,8 +65,20 @@ class Object // Add symbol information to the global symbol table. void - add_symbols(Read_symbols_data rd) - { this->do_add_symbols(rd); } + 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() + { return this->target_; } + + // Return the sized target structure associated with this object. + // This is like the target method but it returns a pointer of + // appropriate checked type. + template<int size, bool big_endian> + Sized_target<size, big_endian>* + sized_target(); protected: // Read the symbols--implemented by child class. @@ -65,7 +88,7 @@ class Object // Add symbol information to the global symbol table--implemented by // child class. virtual void - do_add_symbols(Read_symbols_data) = 0; + do_add_symbols(Symbol_table*, Read_symbols_data) = 0; // Get the file. Input_file* @@ -81,6 +104,11 @@ class Object const unsigned char* get_view(off_t start, off_t size); + // Set the target. + void + set_target(Target* target) + { this->target_ = target; } + // Read data from the underlying file. void read(off_t start, off_t size, void* p); @@ -101,9 +129,25 @@ class Object // Offset within the file--0 for an object file, non-0 for an // archive. off_t offset_; + // Whether this is a dynamic object. + bool is_dynamic_; + // Target functions--may be NULL if the target is not known. + Target* target_; }; -// The functions of Object which are size specific. +// Implement sized_target inline for efficiency. This approach breaks +// static type checking, but is made safe using asserts. + +template<int size, bool big_endian> +inline Sized_target<size, big_endian>* +Object::sized_target() +{ + assert(this->target_->get_size() == size); + assert(this->target_->is_big_endian() ? big_endian : !big_endian); + return static_cast<Sized_target<size, big_endian>*>(this->target_); +} + +// A regular object file. This is size and endian specific. template<int size, bool big_endian> class Sized_object : public Object @@ -121,7 +165,11 @@ class Sized_object : public Object do_read_symbols(); void - do_add_symbols(Read_symbols_data); + do_add_symbols(Symbol_table*, Read_symbols_data); + + Sized_target<size, big_endian>* + sized_target() + { return this->Object::sized_target<size, big_endian>(); } private: // This object may not be copied. @@ -136,8 +184,6 @@ class Sized_object : public Object elfcpp::Elf_Half machine_; // ELF file header e_flags field. unsigned int flags_; - // Target functions--may be NULL. - Target* target_; // File offset of section header table. off_t shoff_; // Number of input sections. @@ -146,6 +192,8 @@ class Sized_object : public Object unsigned int shstrndx_; // Index of SHT_SYMTAB section. unsigned int symtab_shnum_; + // The entries in the symbol table for the external symbols. + Symbol** symbols_; }; // Return an Object appropriate for the input file. P is BYTES long, diff --git a/gold/po/POTFILES.in b/gold/po/POTFILES.in index e0cca00..35c5094 100644 --- a/gold/po/POTFILES.in +++ b/gold/po/POTFILES.in @@ -6,14 +6,21 @@ gold.cc gold.h gold-threads.cc gold-threads.h +i386.cc object.cc object.h options.cc options.h readsyms.cc readsyms.h +resolve.cc +stringpool.cc +stringpool.h +symtab.cc 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 604b371..35860a5 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-08-04 14:23-0700\n" +"POT-Creation-Date: 2006-08-18 15:26-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" @@ -16,7 +16,7 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: dirsearch.cc:50 +#: dirsearch.cc:51 #, c-format msgid "can not read directory %s" msgstr "" @@ -51,7 +51,7 @@ msgstr "" msgid "%s: cannot open %s: %s" msgstr "" -#: gold.cc:71 +#: gold.cc:72 msgid "no input files" msgstr "" @@ -99,68 +99,73 @@ msgstr "" msgid "pthread_cond_signal failed" msgstr "" -#: object.cc:56 +#: object.cc:57 #, c-format msgid "%s: %s: bad e_ehsize field (%d != %d)\n" msgstr "" -#: object.cc:63 +#: object.cc:64 #, c-format msgid "%s: %s: bad e_shentsize field (%d != %d)\n" msgstr "" -#: object.cc:154 +#: object.cc:88 +#, c-format +msgid "%s: %s: unsupported ELF machine number %d\n" +msgstr "" + +#: object.cc:167 #, c-format msgid "%s: %s: invalid symbol table name index: %u\n" msgstr "" -#: object.cc:164 +#: object.cc:177 #, c-format msgid "%s: %s: symbol table name section has wrong type: %u\n" msgstr "" -#: object.cc:206 +#: object.cc:214 #, c-format -msgid "%s: %s: invalid symbol name offset %u for symbol %d\n" +msgid "%s: %s: size of symbols is not multiple of symbol size\n" msgstr "" #. elfcpp::ET_DYN -#: object.cc:249 +#: object.cc:262 #, c-format msgid "%s: %s: dynamic objects are not yet supported\n" msgstr "" -#: object.cc:273 object.cc:326 object.cc:347 +#: object.cc:286 object.cc:339 object.cc:360 #, c-format msgid "%s: %s: ELF file too short\n" msgstr "" -#: object.cc:282 +#: object.cc:295 #, c-format msgid "%s: %s: invalid ELF version 0\n" msgstr "" -#: object.cc:285 +#: object.cc:298 #, c-format msgid "%s: %s: unsupported ELF version %d\n" msgstr "" -#: object.cc:293 +#: object.cc:306 #, c-format msgid "%s: %s: invalid ELF class 0\n" msgstr "" -#: object.cc:300 +#: object.cc:313 #, c-format msgid "%s: %s: unsupported ELF class %d\n" msgstr "" -#: object.cc:308 +#: object.cc:321 #, c-format msgid "%s: %s: invalid ELF data encoding\n" msgstr "" -#: object.cc:315 +#: object.cc:328 #, c-format msgid "%s: %s: unsupported ELF data encoding %d\n" msgstr "" @@ -214,3 +219,23 @@ msgstr "" #, c-format msgid "%s: -%c: %s\n" msgstr "" + +#: resolve.cc:103 +#, c-format +msgid "%s: %s: invalid STB_LOCAL symbol %s in external symbols\n" +msgstr "" + +#: resolve.cc:109 +#, c-format +msgid "%s: %s: unsupported symbol binding %d for symbol %s\n" +msgstr "" + +#: symtab.cc:262 +#, c-format +msgid "%s: %s: mixing 32-bit and 64-bit ELF objects\n" +msgstr "" + +#: symtab.cc:275 +#, c-format +msgid "%s: %s: bad symbol name offset %u at %lu\n" +msgstr "" diff --git a/gold/readsyms.cc b/gold/readsyms.cc index 30b341c..1076e6c 100644 --- a/gold/readsyms.cc +++ b/gold/readsyms.cc @@ -73,7 +73,8 @@ Read_symbols::run(Workqueue* workqueue) p, bytes); Read_symbols_data sd = obj->read_symbols(); - workqueue->queue(new Add_symbols(obj, sd, this->this_blocker_, + workqueue->queue(new Add_symbols(this->symtab_, obj, sd, + this->this_blocker_, this->next_blocker_)); // Opening the file locked it, so now we need to unlock it. @@ -117,7 +118,7 @@ Add_symbols::locks(Workqueue* workqueue) void Add_symbols::run(Workqueue*) { - this->object_->add_symbols(this->sd_); + this->object_->add_symbols(this->symtab_, this->sd_); } } // End namespace gold. diff --git a/gold/readsyms.h b/gold/readsyms.h index bad96c1..f01cf61 100644 --- a/gold/readsyms.h +++ b/gold/readsyms.h @@ -26,10 +26,10 @@ 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, const Dirsearch& dirpath, - const Input_argument& input, Task_token* this_blocker, - Task_token* next_blocker) - : options_(options), dirpath_(dirpath), input_(input), + Read_symbols(const General_options& options, 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) { } @@ -48,6 +48,7 @@ class Read_symbols : public Task private: const General_options& options_; + Symbol_table* symtab_; const Dirsearch& dirpath_; const Input_argument& input_; Task_token* this_blocker_; @@ -64,9 +65,9 @@ class Add_symbols : public Task // THIS_BLOCKER is used to prevent this task from running before the // one for the previous input file. NEXT_BLOCKER is used to prevent // the next task from running. - Add_symbols(Object* object, Read_symbols_data sd, Task_token* this_blocker, - Task_token* next_blocker) - : object_(object), sd_(sd), this_blocker_(this_blocker), + Add_symbols(Symbol_table* symtab, Object* object, Read_symbols_data sd, + Task_token* this_blocker, Task_token* next_blocker) + : symtab_(symtab), object_(object), sd_(sd), this_blocker_(this_blocker), next_blocker_(next_blocker) { } @@ -84,6 +85,7 @@ class Add_symbols : public Task run(Workqueue*); private: + Symbol_table* symtab_; Object* object_; Read_symbols_data sd_; Task_token* this_blocker_; diff --git a/gold/resolve.cc b/gold/resolve.cc new file mode 100644 index 0000000..8c7d828 --- /dev/null +++ b/gold/resolve.cc @@ -0,0 +1,349 @@ +// resolve.cc -- symbol resolution for gold + +#include "gold.h" + +#include "elfcpp.h" +#include "target.h" +#include "object.h" +#include "symtab.h" + +namespace gold +{ + +// Resolve a symbol. This is called the second and subsequent times +// we see a symbol. TO is the pre-existing symbol. SYM is the new +// symbol, seen in OBJECT. + +template<int size, bool big_endian> +void +Symbol_table::resolve(Symbol* to, + const elfcpp::Sym<size, big_endian>& sym, + Object* object) +{ + if (object->target()->has_resolve()) + { + object->sized_target<size, big_endian>()->resolve(to, sym, object); + return; + } + + // Build a little code for each symbol. + // Bit 0: 0 for global, 1 for weak. + // Bit 1: 0 for regular object, 1 for shared object + // Bits 2-3: 0 for normal, 1 for undefined, 2 for common + // This gives us values from 0 to 11: + + enum + { + DEF = 0, + WEAK_DEF = 1, + DYN_DEF = 2, + DYN_WEAK_DEF = 3, + UNDEF = 4, + WEAK_UNDEF = 5, + DYN_UNDEF = 6, + DYN_WEAK_UNDEF = 7, + COMMON = 8, + WEAK_COMMON = 9, + DYN_COMMON = 10, + DYN_WEAK_COMMON = 11 + }; + + int tobits; + switch (to->binding()) + { + case elfcpp::STB_GLOBAL: + tobits = 0; + break; + + case elfcpp::STB_WEAK: + tobits = 1; + break; + + case elfcpp::STB_LOCAL: + // We should only see externally visible symbols in the symbol + // table. + abort(); + + default: + // Any target which wants to handle STB_LOOS, etc., needs to + // define a resolve method. + abort(); + } + + if (to->object() != NULL && to->object()->is_dynamic()) + tobits |= (1 << 1); + + switch (to->shnum()) + { + case elfcpp::SHN_UNDEF: + tobits |= (1 << 2); + break; + + case elfcpp::SHN_COMMON: + tobits |= (2 << 2); + break; + + default: + break; + } + + int frombits; + switch (sym.get_st_bind()) + { + case elfcpp::STB_GLOBAL: + frombits = 0; + break; + + case elfcpp::STB_WEAK: + frombits = 1; + break; + + case elfcpp::STB_LOCAL: + fprintf(stderr, + _("%s: %s: invalid STB_LOCAL symbol %s in external symbols\n"), + program_name, object->name().c_str(), to->name()); + gold_exit(false); + + default: + fprintf(stderr, + _("%s: %s: unsupported symbol binding %d for symbol %s\n"), + program_name, object->name().c_str(), + static_cast<int>(sym.get_st_bind()), to->name()); + gold_exit(false); + } + + if (object->is_dynamic()) + frombits |= (1 << 1); + + switch (sym.get_st_shndx()) + { + case elfcpp::SHN_UNDEF: + frombits |= (1 << 2); + break; + + case elfcpp::SHN_COMMON: + frombits |= (2 << 2); + break; + + default: + break; + } + + // We use a giant switch table for symbol resolution. This code is + // unwieldy, but: 1) it is efficient; 2) we definitely handle all + // cases; 3) it is easy to change the handling of a particular case. + // The alternative would be a series of conditionals, but it is easy + // to get the ordering wrong. This could also be done as a table, + // but that is no easier to understand than this large switch + // statement. + + switch (tobits * 16 + frombits) + { + case DEF * 16 + DEF: + // Two definitions of the same symbol. + fprintf(stderr, "%s: %s: multiple definition of %s\n", + program_name, object->name().c_str(), to->name()); + // FIXME: Report locations. Record that we have seen an error. + return; + + case WEAK_DEF * 16 + DEF: + // In the original SVR4 linker, a weak definition followed by a + // regular definition was treated as a multiple definition + // error. In the Solaris linker and the GNU linker, a weak + // definition followed by a regular definition causes the + // regular definition to be ignored. We are currently + // compatible with the GNU linker. In the future we should add + // a target specific option to change this. FIXME. + return; + + case DYN_DEF * 16 + DEF: + case DYN_WEAK_DEF * 16 + DEF: + case UNDEF * 16 + DEF: + case WEAK_UNDEF * 16 + DEF: + case DYN_UNDEF * 16 + DEF: + case DYN_WEAK_UNDEF * 16 + DEF: + case COMMON * 16 + DEF: + case WEAK_COMMON * 16 + DEF: + case DYN_COMMON * 16 + DEF: + case DYN_WEAK_COMMON * 16 + DEF: + + case DEF * 16 + WEAK_DEF: + case WEAK_DEF * 16 + WEAK_DEF: + case DYN_DEF * 16 + WEAK_DEF: + case DYN_WEAK_DEF * 16 + WEAK_DEF: + case UNDEF * 16 + WEAK_DEF: + case WEAK_UNDEF * 16 + WEAK_DEF: + case DYN_UNDEF * 16 + WEAK_DEF: + case DYN_WEAK_UNDEF * 16 + WEAK_DEF: + case COMMON * 16 + WEAK_DEF: + case WEAK_COMMON * 16 + WEAK_DEF: + case DYN_COMMON * 16 + WEAK_DEF: + case DYN_WEAK_COMMON * 16 + WEAK_DEF: + + case DEF * 16 + DYN_DEF: + case WEAK_DEF * 16 + DYN_DEF: + case DYN_DEF * 16 + DYN_DEF: + case DYN_WEAK_DEF * 16 + DYN_DEF: + case UNDEF * 16 + DYN_DEF: + case WEAK_UNDEF * 16 + DYN_DEF: + case DYN_UNDEF * 16 + DYN_DEF: + case DYN_WEAK_UNDEF * 16 + DYN_DEF: + case COMMON * 16 + DYN_DEF: + case WEAK_COMMON * 16 + DYN_DEF: + case DYN_COMMON * 16 + DYN_DEF: + case DYN_WEAK_COMMON * 16 + DYN_DEF: + + case DEF * 16 + DYN_WEAK_DEF: + case WEAK_DEF * 16 + DYN_WEAK_DEF: + case DYN_DEF * 16 + DYN_WEAK_DEF: + case DYN_WEAK_DEF * 16 + DYN_WEAK_DEF: + case UNDEF * 16 + DYN_WEAK_DEF: + case WEAK_UNDEF * 16 + DYN_WEAK_DEF: + case DYN_UNDEF * 16 + DYN_WEAK_DEF: + case DYN_WEAK_UNDEF * 16 + DYN_WEAK_DEF: + case COMMON * 16 + DYN_WEAK_DEF: + case WEAK_COMMON * 16 + DYN_WEAK_DEF: + case DYN_COMMON * 16 + DYN_WEAK_DEF: + case DYN_WEAK_COMMON * 16 + DYN_WEAK_DEF: + + case DEF * 16 + UNDEF: + case WEAK_DEF * 16 + UNDEF: + case DYN_DEF * 16 + UNDEF: + case DYN_WEAK_DEF * 16 + UNDEF: + case UNDEF * 16 + UNDEF: + case WEAK_UNDEF * 16 + UNDEF: + case DYN_UNDEF * 16 + UNDEF: + case DYN_WEAK_UNDEF * 16 + UNDEF: + case COMMON * 16 + UNDEF: + case WEAK_COMMON * 16 + UNDEF: + case DYN_COMMON * 16 + UNDEF: + case DYN_WEAK_COMMON * 16 + UNDEF: + + case DEF * 16 + WEAK_UNDEF: + case WEAK_DEF * 16 + WEAK_UNDEF: + case DYN_DEF * 16 + WEAK_UNDEF: + case DYN_WEAK_DEF * 16 + WEAK_UNDEF: + case UNDEF * 16 + WEAK_UNDEF: + case WEAK_UNDEF * 16 + WEAK_UNDEF: + case DYN_UNDEF * 16 + WEAK_UNDEF: + case DYN_WEAK_UNDEF * 16 + WEAK_UNDEF: + case COMMON * 16 + WEAK_UNDEF: + case WEAK_COMMON * 16 + WEAK_UNDEF: + case DYN_COMMON * 16 + WEAK_UNDEF: + case DYN_WEAK_COMMON * 16 + WEAK_UNDEF: + + case DEF * 16 + DYN_UNDEF: + case WEAK_DEF * 16 + DYN_UNDEF: + case DYN_DEF * 16 + DYN_UNDEF: + case DYN_WEAK_DEF * 16 + DYN_UNDEF: + case UNDEF * 16 + DYN_UNDEF: + case WEAK_UNDEF * 16 + DYN_UNDEF: + case DYN_UNDEF * 16 + DYN_UNDEF: + case DYN_WEAK_UNDEF * 16 + DYN_UNDEF: + case COMMON * 16 + DYN_UNDEF: + case WEAK_COMMON * 16 + DYN_UNDEF: + case DYN_COMMON * 16 + DYN_UNDEF: + case DYN_WEAK_COMMON * 16 + DYN_UNDEF: + + case DEF * 16 + DYN_WEAK_UNDEF: + case WEAK_DEF * 16 + DYN_WEAK_UNDEF: + case DYN_DEF * 16 + DYN_WEAK_UNDEF: + case DYN_WEAK_DEF * 16 + DYN_WEAK_UNDEF: + case UNDEF * 16 + DYN_WEAK_UNDEF: + case WEAK_UNDEF * 16 + DYN_WEAK_UNDEF: + case DYN_UNDEF * 16 + DYN_WEAK_UNDEF: + case DYN_WEAK_UNDEF * 16 + DYN_WEAK_UNDEF: + case COMMON * 16 + DYN_WEAK_UNDEF: + case WEAK_COMMON * 16 + DYN_WEAK_UNDEF: + case DYN_COMMON * 16 + DYN_WEAK_UNDEF: + case DYN_WEAK_COMMON * 16 + DYN_WEAK_UNDEF: + + case DEF * 16 + COMMON: + case WEAK_DEF * 16 + COMMON: + case DYN_DEF * 16 + COMMON: + case DYN_WEAK_DEF * 16 + COMMON: + case UNDEF * 16 + COMMON: + case WEAK_UNDEF * 16 + COMMON: + case DYN_UNDEF * 16 + COMMON: + case DYN_WEAK_UNDEF * 16 + COMMON: + case COMMON * 16 + COMMON: + case WEAK_COMMON * 16 + COMMON: + case DYN_COMMON * 16 + COMMON: + case DYN_WEAK_COMMON * 16 + COMMON: + + case DEF * 16 + WEAK_COMMON: + case WEAK_DEF * 16 + WEAK_COMMON: + case DYN_DEF * 16 + WEAK_COMMON: + case DYN_WEAK_DEF * 16 + WEAK_COMMON: + case UNDEF * 16 + WEAK_COMMON: + case WEAK_UNDEF * 16 + WEAK_COMMON: + case DYN_UNDEF * 16 + WEAK_COMMON: + case DYN_WEAK_UNDEF * 16 + WEAK_COMMON: + case COMMON * 16 + WEAK_COMMON: + case WEAK_COMMON * 16 + WEAK_COMMON: + case DYN_COMMON * 16 + WEAK_COMMON: + case DYN_WEAK_COMMON * 16 + WEAK_COMMON: + + case DEF * 16 + DYN_COMMON: + case WEAK_DEF * 16 + DYN_COMMON: + case DYN_DEF * 16 + DYN_COMMON: + case DYN_WEAK_DEF * 16 + DYN_COMMON: + case UNDEF * 16 + DYN_COMMON: + case WEAK_UNDEF * 16 + DYN_COMMON: + case DYN_UNDEF * 16 + DYN_COMMON: + case DYN_WEAK_UNDEF * 16 + DYN_COMMON: + case COMMON * 16 + DYN_COMMON: + case WEAK_COMMON * 16 + DYN_COMMON: + case DYN_COMMON * 16 + DYN_COMMON: + case DYN_WEAK_COMMON * 16 + DYN_COMMON: + + case DEF * 16 + DYN_WEAK_COMMON: + case WEAK_DEF * 16 + DYN_WEAK_COMMON: + case DYN_DEF * 16 + DYN_WEAK_COMMON: + case DYN_WEAK_DEF * 16 + DYN_WEAK_COMMON: + case UNDEF * 16 + DYN_WEAK_COMMON: + case WEAK_UNDEF * 16 + DYN_WEAK_COMMON: + case DYN_UNDEF * 16 + DYN_WEAK_COMMON: + case DYN_WEAK_UNDEF * 16 + DYN_WEAK_COMMON: + case COMMON * 16 + DYN_WEAK_COMMON: + case WEAK_COMMON * 16 + DYN_WEAK_COMMON: + case DYN_COMMON * 16 + DYN_WEAK_COMMON: + case DYN_WEAK_COMMON * 16 + DYN_WEAK_COMMON: + + break; + } +} + +// Instantiate the templates we need. We could use the configure +// script to restrict this to only the ones needed for implemented +// targets. + +template +void +Symbol_table::resolve<32, true>( + Symbol* to, + const elfcpp::Sym<32, true>& sym, + Object* object); + +template +void +Symbol_table::resolve<32, false>( + Symbol* to, + const elfcpp::Sym<32, false>& sym, + Object* object); + +template +void +Symbol_table::resolve<64, true>( + Symbol* to, + const elfcpp::Sym<64, true>& sym, + Object* object); + +template +void +Symbol_table::resolve<64, false>( + Symbol* to, + const elfcpp::Sym<64, false>& sym, + Object* object); + +} // End namespace gold. diff --git a/gold/stringpool.cc b/gold/stringpool.cc new file mode 100644 index 0000000..0368014 --- /dev/null +++ b/gold/stringpool.cc @@ -0,0 +1,130 @@ +// stringpool.cc -- a string pool for gold + +#include "gold.h" + +#include <cassert> +#include <cstring> + +#include "stringpool.h" + +namespace gold +{ + +Stringpool::Stringpool() + : string_set_(), strings_() +{ +} + +Stringpool::~Stringpool() +{ + for (std::list<stringdata*>::iterator p = this->strings_.begin(); + p != this->strings_.end(); + ++p) + delete[] reinterpret_cast<char*>(*p); +} + +// Hash function. + +size_t +Stringpool::Stringpool_hash::operator()(const char* s) const +{ + // Fowler/Noll/Vo (FNV) hash (type FNV-1a). + if (sizeof(size_t) == 8) + { + size_t result = 14695981039346656037ULL; + while (*s != '\0') + { + result &= (size_t) *s++; + result *= 1099511628211ULL; + } + return result; + } + else + { + size_t result = 2166136261UL; + while (*s != '\0') + { + result ^= (size_t) *s++; + result *= 16777619UL; + } + return result; + } +} + +// Add a string to the list of canonical strings. Return a pointer to +// the canonical string. + +const char* +Stringpool::add_string(const char* s) +{ + const size_t buffer_size = 1000; + size_t len = strlen(s); + + size_t alc; + bool front = true; + if (len >= buffer_size) + { + alc = sizeof(stringdata) + len; + front = false; + } + else if (this->strings_.empty()) + alc = sizeof(stringdata) + buffer_size; + else + { + stringdata *psd = this->strings_.front(); + if (len >= psd->alc - psd->len) + alc = sizeof(stringdata) + buffer_size; + else + { + char* ret = psd->data + psd->len; + memcpy(ret, s, len + 1); + psd->len += len + 1; + return ret; + } + } + + stringdata *psd = reinterpret_cast<stringdata*>(new char[alc]); + psd->alc = alc; + memcpy(psd->data, s, len + 1); + psd->len = len + 1; + if (front) + this->strings_.push_front(psd); + else + this->strings_.push_back(psd); + return psd->data; +} + +// Add a string to a string pool. + +const char* +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 + // unordered_set, so this should be replaced with custom code to do + // what we need, which is to return the empty slot. + + String_set_type::const_iterator p = this->string_set_.find(s); + if (p != this->string_set_.end()) + return *p; + + const char* ret = this->add_string(s); + std::pair<String_set_type::iterator, bool> ins = + this->string_set_.insert(ret); + assert(ins.second); + return ret; +} + +// Add a prefix of a string to a string pool. + +const char* +Stringpool::add(const char* s, size_t len) +{ + // FIXME: This implementation should be rewritten when we rewrite + // the hash table to avoid copying. + std::string st(s, len); + return this->add(st); +} + +} // End namespace gold. diff --git a/gold/stringpool.h b/gold/stringpool.h new file mode 100644 index 0000000..873c26a --- /dev/null +++ b/gold/stringpool.h @@ -0,0 +1,70 @@ +// stringpool.h -- a string pool for gold -*- C++ -*- + +#include <string> +#include <list> + +// Stringpool +// Manage a pool of unique strings. + +#ifndef GOLD_STRINGPOOL_H +#define GOLD_STRINGPOOL_H + +namespace gold +{ + +class Stringpool +{ + public: + Stringpool(); + + ~Stringpool(); + + // Add a string to the pool. This returns a canonical permanent + // pointer to the string. + const char* add(const char*); + + const char* add(const std::string& s) + { return this->add(s.c_str()); } + + // Add the prefix of a string to the pool. + const char* add(const char *, size_t); + + private: + Stringpool(const Stringpool&); + Stringpool& operator=(const Stringpool&); + + struct stringdata + { + // Length of data in buffer. + size_t len; + // Allocated size of buffer. + size_t alc; + // Buffer. + char data[1]; + }; + + const char* add_string(const char*); + + struct Stringpool_hash + { + size_t + operator()(const char*) const; + }; + + struct Stringpool_eq + { + bool + operator()(const char* p1, const char* p2) const + { return strcmp(p1, p2) == 0; } + }; + + typedef Unordered_set<const char*, Stringpool_hash, Stringpool_eq, + std::allocator<const char*>, + true> String_set_type; + String_set_type string_set_; + std::list<stringdata*> strings_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_STRINGPOOL_H) diff --git a/gold/strtab.h b/gold/strtab.h new file mode 100644 index 0000000..ab22b8f --- /dev/null +++ b/gold/strtab.h @@ -0,0 +1,73 @@ +// strtab.h -- manage an ELF string table for gold -*- C++ -*- + +#ifndef GOLD_STRTAB_H +#define GOLD_STRTAB_H + +#include <cstring> +#include <string> + +namespace gold +{ + +// This class holds an ELF string table. We keep a reference count +// for each string, which we use to determine which strings are +// actually required at the end. When all operations are done, the +// string table is finalized, which sets the offsets to use for each +// string. + +class Strtab +{ + public: + Strtab(); + + ~Strtab(); + + Strtab_ref* add(const char*); + + Strtab_ref* add(const std::string& s) + { return this->add(s.c_str()); } + + private: + Strtab(const Strtab&); + Strtab& operator=(const Strtab&); + + struct strtab_hash + { + std::size_t + operator()(const char*p); + }; + + struct strtab_eq + { + bool + operator()(const char* p1, const char* p2) + { return strcmp(p1, p2) == 0; } + }; + + Unordered_map<const char*, Strtab_ref*, strtab_hash, strtab_eq, + std::allocator<std::pair<const char* const, Strtab_ref*> >, + true> strings_; +}; + +// Users of Strtab work with pointers to Strtab_ref structures. These +// are allocated via new and should be deleted if the string is no +// longer needed. + +class Strtab_ref +{ + public: + ~Strtab_ref(); + + const char* + str() const; + + private: + Strtab_ref(const Strtab_ref&); + Strtab_ref& operator=(const Strtab_ref&); + + int refs_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_STRTAB_H) diff --git a/gold/symtab.cc b/gold/symtab.cc new file mode 100644 index 0000000..a410db3 --- /dev/null +++ b/gold/symtab.cc @@ -0,0 +1,358 @@ +// symtab.cc -- the gold symbol table + +#include "gold.h" + +#include <cassert> +#include <stdint.h> +#include <string> +#include <utility> + +#include "object.h" +#include "symtab.h" + +namespace gold +{ + +// Class Symbol. + +Symbol::~Symbol() +{ +} + +// Initialize the fields in the base class Symbol. + +template<int size, bool big_endian> +void +Symbol::init_base(const char* name, const char* version, Object* object, + const elfcpp::Sym<size, big_endian>& sym) +{ + this->name_ = name; + this->version_ = version; + this->object_ = object; + this->shnum_ = sym.get_st_shndx(); // FIXME: Handle SHN_XINDEX. + this->type_ = sym.get_st_type(); + this->binding_ = sym.get_st_bind(); + this->visibility_ = sym.get_st_visibility(); + this->other_ = sym.get_st_nonvis(); + this->special_ = false; + this->def_ = false; + this->forwarder_ = false; +} + +// Initialize the fields in Sized_symbol. + +template<int size> +template<bool big_endian> +void +Sized_symbol<size>::init(const char* name, const char* version, Object* object, + const elfcpp::Sym<size, big_endian>& sym) +{ + this->init_base(name, version, object, sym); + this->value_ = sym.get_st_value(); + this->size_ = sym.get_st_size(); +} + +// Class Symbol_table. + +Symbol_table::Symbol_table() + : size_(0), table_(), namepool_(), forwarders_() +{ +} + +Symbol_table::~Symbol_table() +{ +} + +// The hash function. The key is always canonicalized, so we use a +// simple combination of the pointers. + +size_t +Symbol_table::Symbol_table_hash::operator()(const Symbol_table_key& key) const +{ + return (reinterpret_cast<size_t>(key.first) + ^ reinterpret_cast<size_t>(key.second)); +} + +// The symbol table key equality function. This is only called with +// canonicalized name and version strings, so we can use pointer +// comparison. + +bool +Symbol_table::Symbol_table_eq::operator()(const Symbol_table_key& k1, + const Symbol_table_key& k2) const +{ + return k1.first == k2.first && k1.second == k2.second; +} + +// Make TO a symbol which forwards to FROM. + +void +Symbol_table::make_forwarder(Symbol* from, Symbol* to) +{ + assert(!from->is_forwarder() && !to->is_forwarder()); + this->forwarders_[from] = to; + from->set_forwarder(); +} + +Symbol* +Symbol_table::resolve_forwards(Symbol* from) const +{ + assert(from->is_forwarder()); + Unordered_map<Symbol*, Symbol*>::const_iterator p = + this->forwarders_.find(from); + assert(p != this->forwarders_.end()); + return p->second; +} + +// Resolve a Symbol with another Symbol. This is only used in the +// unusual case where there are references to both an unversioned +// symbol and a symbol with a version, and we then discover that that +// version is the default version. + +void +Symbol_table::resolve(Symbol*, const Symbol*) +{ +} + +// Add one symbol from OBJECT to the symbol table. NAME is symbol +// name and VERSION is the version; both are canonicalized. DEF is +// whether this is the default version. + +// If DEF is true, then this is the definition of a default version of +// a symbol. That means that any lookup of NAME/NULL and any lookup +// of NAME/VERSION should always return the same symbol. This is +// obvious for references, but in particular we want to do this for +// definitions: overriding NAME/NULL should also override +// NAME/VERSION. If we don't do that, it would be very hard to +// override functions in a shared library which uses versioning. + +// We implement this by simply making both entries in the hash table +// point to the same Symbol structure. That is easy enough if this is +// the first time we see NAME/NULL or NAME/VERSION, but it is possible +// that we have seen both already, in which case they will both have +// independent entries in the symbol table. We can't simply change +// the symbol table entry, because we have pointers to the entries +// attached to the object files. So we mark the entry attached to the +// object file as a forwarder, and record it in the forwarders_ map. +// Note that entries in the hash table will never be marked as +// forwarders. + +template<int size, bool big_endian> +Symbol* +Symbol_table::add_from_object(Sized_object<size, big_endian>* object, + const char *name, + const char *version, bool def, + const elfcpp::Sym<size, big_endian>& sym) +{ + Symbol* const snull = NULL; + std::pair<typename Symbol_table_type::iterator, bool> ins = + this->table_.insert(std::make_pair(std::make_pair(name, version), snull)); + + std::pair<typename Symbol_table_type::iterator, bool> insdef = + std::make_pair(this->table_.end(), false); + if (def) + { + const char* const vnull = NULL; + insdef = this->table_.insert(std::make_pair(std::make_pair(name, vnull), + snull)); + } + + // ins.first: an iterator, which is a pointer to a pair. + // ins.first->first: the key (a pair of name and version). + // ins.first->second: the value (Symbol*). + // ins.second: true if new entry was inserted, false if not. + + Symbol* ret; + if (!ins.second) + { + // We already have an entry for NAME/VERSION. + ret = ins.first->second; + assert(ret != NULL); + Symbol_table::resolve(ret, sym, object); + + if (def) + { + if (insdef.second) + { + // This is the first time we have seen NAME/NULL. Make + // NAME/NULL point to NAME/VERSION. + insdef.first->second = ret; + } + else + { + // This is the unfortunate case where we already have + // entries for both NAME/VERSION and NAME/NULL. + Symbol_table::resolve(ret, insdef.first->second); + this->make_forwarder(insdef.first->second, ret); + insdef.first->second = ret; + } + } + } + else + { + // This is the first time we have seen NAME/VERSION. + assert(ins.first->second == NULL); + if (def && !insdef.second) + { + // We already have an entry for NAME/NULL. Make + // NAME/VERSION point to it. + ret = insdef.first->second; + Symbol_table::resolve(ret, sym, object); + ins.first->second = ret; + } + else + { + Sized_symbol<size>* rs; + Sized_target<size, big_endian>* target = object->sized_target(); + if (target->has_make_symbol()) + { + rs = target->make_symbol(); + if (rs == NULL) + { + // This means that we don't want a symbol table + // entry after all. + if (!def) + this->table_.erase(ins.first); + else + { + this->table_.erase(insdef.first); + // Inserting insdef invalidated ins. + this->table_.erase(std::make_pair(name, version)); + } + return NULL; + } + } + else + rs = new Sized_symbol<size>(); + rs->init(name, version, object, sym); + + ret = rs; + ins.first->second = ret; + if (def) + { + // This is the first time we have seen NAME/NULL. Point + // it at the new entry for NAME/VERSION. + assert(insdef.second); + insdef.first->second = ret; + } + } + } + + return ret; +} + +// Add all the symbols in an object to the hash table. + +template<int size, bool big_endian> +void +Symbol_table::add_from_object( + Sized_object<size, big_endian>* object, + const elfcpp::Sym<size, big_endian>* syms, + size_t count, + const char* sym_names, + size_t sym_name_size, + Symbol** sympointers) +{ + // We take the size from the first object we see. + if (this->get_size() == 0) + this->set_size(size); + + if (size != this->get_size() || size != object->target()->get_size()) + { + fprintf(stderr, _("%s: %s: mixing 32-bit and 64-bit ELF objects\n"), + program_name, object->name().c_str()); + gold_exit(false); + } + + const unsigned char* p = reinterpret_cast<const unsigned char*>(syms); + for (size_t i = 0; i < count; ++i) + { + elfcpp::Sym<size, big_endian> sym(p); + + unsigned int st_name = sym.get_st_name(); + if (st_name >= sym_name_size) + { + fprintf(stderr, _("%s: %s: bad symbol name offset %u at %lu\n"), + program_name, object->name().c_str(), st_name, + static_cast<unsigned long>(i)); + gold_exit(false); + } + + const char* name = sym_names + st_name; + + // In an object file, an '@' in the name separates the symbol + // name from the version name. If there are two '@' characters, + // this is the default version. + const char* ver = strchr(name, '@'); + + Symbol* res; + if (ver == NULL) + { + name = this->namepool_.add(name); + res = this->add_from_object(object, name, NULL, false, sym); + } + else + { + name = this->namepool_.add(name, ver - name); + bool def = false; + ++ver; + if (*ver == '@') + { + def = true; + ++ver; + } + ver = this->namepool_.add(ver); + res = this->add_from_object(object, name, ver, def, sym); + } + + *sympointers++ = res; + + p += elfcpp::Elf_sizes<size>::sym_size; + } +} + +// Instantiate the templates we need. We could use the configure +// script to restrict this to only the ones needed for implemented +// targets. + +template +void +Symbol_table::add_from_object<32, true>( + Sized_object<32, true>* object, + const elfcpp::Sym<32, true>* syms, + size_t count, + const char* sym_names, + size_t sym_name_size, + Symbol** sympointers); + +template +void +Symbol_table::add_from_object<32, false>( + Sized_object<32, false>* object, + const elfcpp::Sym<32, false>* syms, + size_t count, + const char* sym_names, + size_t sym_name_size, + Symbol** sympointers); + +template +void +Symbol_table::add_from_object<64, true>( + Sized_object<64, true>* object, + const elfcpp::Sym<64, true>* syms, + size_t count, + const char* sym_names, + size_t sym_name_size, + Symbol** sympointers); + +template +void +Symbol_table::add_from_object<64, false>( + Sized_object<64, false>* object, + const elfcpp::Sym<64, false>* syms, + size_t count, + const char* sym_names, + size_t sym_name_size, + Symbol** sympointers); + +} // End namespace gold. diff --git a/gold/symtab.h b/gold/symtab.h index 1495fb1..c085dd9 100644 --- a/gold/symtab.h +++ b/gold/symtab.h @@ -3,15 +3,12 @@ // Symbol_table // The symbol table. -#include "gold.h" - #include <string> #include <utility> #include "elfcpp.h" #include "targetsize.h" -#include "object.h" -#include "workqueue.h" +#include "stringpool.h" #ifndef GOLD_SYMTAB_H #define GOLD_SYMTAB_H @@ -19,60 +16,221 @@ namespace gold { -// An entry in the symbol table. The symbol table can have a lot of -// entries, so we don't want this class to get too big. +class Object; + +template<int size, bool big_endian> +class Sized_object; + +template<int size, bool big_endian> +class Sized_target; + +// The base class of an entry in the symbol table. The symbol table +// can have a lot of entries, so we don't want this class to big. +// Size dependent fields can be found in the template class +// Sized_symbol. Targets may support their own derived classes. -template<int size> class Symbol { public: - typedef typename elfcpp::Elf_types<size>::Elf_Addr Value; - typedef typename Size_types<size>::Unsigned_type Size; + virtual ~Symbol(); + + // Return the symbol name. + const char* + name() const + { return this->name_; } + + // Return the symbol version. This will return NULL for an + // unversioned symbol. + const char* + version() const + { return this->version_; } + + // Return whether this symbol is a forwarder. This will never be + // true of a symbol found in the hash table, but may be true of + // symbol pointers attached to object files. + bool + is_forwarder() const + { return this->forwarder_; } + + // Mark this symbol as a forwarder. + void + set_forwarder() + { this->forwarder_ = true; } + + // Return the object with which this symbol is associated. + Object* + object() const + { return this->object_; } + + // Return the symbol binding. + elfcpp::STB + binding() const + { return this->binding_; } + + // Return the section index. + unsigned int + shnum() const + { return this->shnum_; } + + protected: + // Instances of this class should always be created at a specific + // size. + Symbol() + { } + + // Initialize fields from an ELF symbol in OBJECT. + template<int size, bool big_endian> + void + init_base(const char *name, const char* version, Object* object, + const elfcpp::Sym<size, big_endian>&); private: - // Every symbol has a unique name, more or less, so we use - // std::string for the name. There are only a few versions in a - // given link, so for them we point into a pool. - std::string name_; + Symbol(const Symbol&); + Symbol& operator=(const Symbol&); + + // Symbol name (expected to point into a Stringpool). + const char* name_; + // Symbol version (expected to point into a Stringpool). This may + // be NULL. const char* version_; + // Object in which symbol is defined, or in which it was first seen. Object* object_; + // Section number in object_ in which symbol is defined. unsigned int shnum_; - Value value_; - Size size_; + // Symbol type. elfcpp::STT type_ : 4; + // Symbol binding. elfcpp::STB binding_ : 4; - elfcpp:STV visibility_ : 2; + // Symbol visibility. + elfcpp::STV visibility_ : 2; + // Rest of symbol st_other field. unsigned int other_ : 6; + // True if this symbol always requires special target-specific + // handling. + bool special_ : 1; + // True if this is the default version of the symbol. + bool def_ : 1; + // True if this symbol really forwards to another symbol. This is + // used when we discover after the fact that two different entries + // in the hash table really refer to the same symbol. This will + // never be set for a symbol found in the hash table, but may be set + // for a symbol found in the list of symbols attached to an Object. + // It forwards to the symbol found in the forwarders_ map of + // Symbol_table. + bool forwarder_ : 1; }; -// The main linker symbol table. +// The parts of a symbol which are size specific. Using a template +// derived class like this helps us use less space on a 32-bit system. template<int size> +class Sized_symbol : public Symbol +{ + public: + Sized_symbol() + { } + + // Initialize fields from an ELF symbol in OBJECT. + template<bool big_endian> + void + init(const char *name, const char* version, Object* object, + const elfcpp::Sym<size, big_endian>&); + + private: + Sized_symbol(const Sized_symbol&); + Sized_symbol& operator=(const Sized_symbol&); + + // Symbol value. + typename elfcpp::Elf_types<size>::Elf_Addr value_; + // Symbol size. + typename elfcpp::Elf_types<size>::Elf_WXword size_; +}; + +// The main linker symbol table. + class Symbol_table { public: Symbol_table(); - // Return a pointer to a symbol specified by name. - Symbol* - lookup(const std::string& name) const; + virtual ~Symbol_table(); - // Return a pointer to a symbol specified by name plus version. + // Add COUNT external symbols from OBJECT to the symbol table. SYMS + // is the symbols, SYM_NAMES is their names, SYM_NAME_SIZE is the + // size of SYM_NAMES. This sets SYMPOINTERS to point to the symbols + // in the symbol table. + template<int size, bool big_endian> + void + add_from_object(Sized_object<size, big_endian>* object, + const elfcpp::Sym<size, big_endian>* syms, + size_t count, const char* sym_names, size_t sym_name_size, + Symbol** sympointers); + + // Return the real symbol associated with the forwarder symbol FROM. Symbol* - lookup(const std::string& name, const char* version) const; + resolve_forwards(Symbol* from) const; - Task_token& - token() const - { return this->token_; } + // Return the size of the symbols in the table. + int + get_size() const + { return this->size_; } private: Symbol_table(const Symbol_table&); Symbol_table& operator=(const Symbol_table&); - typedef std::pair<std::string, std::string> Symbol_table_key; + // Set the size of the symbols in the table. + void + set_size(int size) + { this->size_ = size; } + + // Make FROM a forwarder symbol to TO. + void + make_forwarder(Symbol* from, Symbol* to); + + // Add a symbol. + template<int size, bool big_endian> + Symbol* + add_from_object(Sized_object<size, big_endian>*, const char *name, + const char *version, bool def, + const elfcpp::Sym<size, big_endian>& sym); + + // Resolve symbols. + template<int size, bool big_endian> + static void + resolve(Symbol* to, const elfcpp::Sym<size, big_endian>& sym, Object*); + + static void + resolve(Symbol* to, const Symbol* from); + + typedef std::pair<const char*, const char*> Symbol_table_key; + + struct Symbol_table_hash + { + size_t + operator()(const Symbol_table_key&) const; + }; + + struct Symbol_table_eq + { + bool + operator()(const Symbol_table_key&, const Symbol_table_key&) const; + }; + + typedef Unordered_map<Symbol_table_key, Symbol*, Symbol_table_hash, + Symbol_table_eq> Symbol_table_type; + + // The size of the symbols in the symbol table (32 or 64). + int size_; + + // The symbol table itself. + Symbol_table_type table_; + + // A pool of symbol names. + Stringpool namepool_; - Unordered_map<Symbol_table_key, Symbol<size>*> table_; - Task_token token_; + // Forwarding symbols. + Unordered_map<Symbol*, Symbol*> forwarders_; }; } // End namespace gold. diff --git a/gold/target-select.cc b/gold/target-select.cc new file mode 100644 index 0000000..28b7527 --- /dev/null +++ b/gold/target-select.cc @@ -0,0 +1,52 @@ +// target-select.cc -- select a target for an object file + +#include "gold.h" + +#include "elfcpp.h" +#include "target-select.h" + +namespace +{ + +// The start of the list of target selectors. + +gold::Target_selector* target_selectors; + +} // End anonymous namespace. + +namespace gold +{ + +// Construct a Target_selector, which means adding it to the linked +// list. This runs at global constructor time, so we want it to be +// fast. + +Target_selector::Target_selector(int machine, int size, bool big_endian) + : machine_(machine), size_(size), big_endian_(big_endian) +{ + this->next_ = target_selectors; + target_selectors = this; +} + +// Find the target for an ELF file. + +extern Target* +select_target(int machine, int size, bool big_endian, int osabi, + int abiversion) +{ + for (const Target_selector* p = target_selectors; p != NULL; p = p->next()) + { + int pmach = p->machine(); + if ((pmach == machine || pmach == elfcpp::EM_NONE) + && p->size() == size + && p->big_endian() ? big_endian : !big_endian) + { + Target* ret = p->recognize(machine, osabi, abiversion); + if (ret != NULL) + return ret; + } + } + return NULL; +} + +} // End namespace gold. diff --git a/gold/target-select.h b/gold/target-select.h new file mode 100644 index 0000000..0762d58 --- /dev/null +++ b/gold/target-select.h @@ -0,0 +1,69 @@ +// target-select.h -- select a target for an object file -*- C++ -*- + +#ifndef GOLD_TARGET_SELECT_H +#define GOLD_TARGET_SELECT_H + +namespace gold +{ + +class Target; + +// We want to avoid a master list of targets, which implies using a +// global constructor. And we also want the program to start up as +// quickly as possible, which implies avoiding global constructors. +// We compromise on a very simple global constructor. We use a target +// selector, which specifies an ELF machine number and a recognition +// function. We use global constructors to build a linked list of +// target selectors--a simple pointer list, not a std::list. + +class Target_selector +{ + public: + // Create a target selector for a specific machine number, size (32 + // or 64), and endianness. The machine number can be EM_NONE to + // test for any machine number. + Target_selector(int machine, int size, bool big_endian); + + virtual ~Target_selector() + { } + + // If we can handle this target, return a pointer to a target + // structure. The size and endianness are known. + virtual Target* recognize(int machine, int osabi, int abiversion) const = 0; + + // Return the next Target_selector in the linked list. + Target_selector* + next() const + { return this->next_; } + + // Return the machine number this selector is looking for, which can + // be EM_NONE to match any machine number. + int + machine() const + { return this->machine_; } + + // Return the size this is looking for (32 or 64). + int + size() const + { return this->size_; } + + // Return the endianness this is looking for. + bool + big_endian() const + { return this->big_endian_; } + + private: + int machine_; + int size_; + bool big_endian_; + Target_selector* next_; +}; + +// Select the target for an ELF file. + +extern Target* select_target(int machine, int size, bool big_endian, + int osabi, int abiversion); + +} // End namespace gold. + +#endif // !defined(GOLD_TARGET_SELECT_H) diff --git a/gold/target.h b/gold/target.h index 1f1c575..236185b 100644 --- a/gold/target.h +++ b/gold/target.h @@ -1,4 +1,4 @@ -// target.h -- target support for gold +// target.h -- target support for gold -*- C++ -*- // The abstract class Target is the interface for target specific // support. It defines abstract methods which each target must @@ -13,16 +13,103 @@ #ifndef GOLD_TARGET_H #define GOLD_TARGET_H +#include "symtab.h" +#include "elfcpp.h" + namespace gold { +class Object; + +// The abstract class for target specific handling. + class Target { public: + virtual ~Target() + { } + + // Return the bit size that this target implements. This should + // return 32 or 64. + int + get_size() const + { return this->size_; } + + // Return whether this target is big-endian. + bool + is_big_endian() const + { return this->is_big_endian_; } + + // Whether this target has a specific make_symbol function. + bool + has_make_symbol() const + { return this->has_make_symbol_; } + + // Whether this target has a specific resolve function. + bool + has_resolve() const + { return this->has_resolve_; } + + // Resolve a symbol. This is called when we see a symbol with a + // target specific binding (STB_LOOS through STB_HIOS or STB_LOPROC + // through STB_HIPROC). TO is a pre-existing symbol. SYM is the + // new symbol, seen in OBJECT. This returns true on success, false + // if the symbol can not be resolved. + template<int size, bool big_endian> + bool + resolve(Sized_symbol<size>* to, const elfcpp::Sym<size, big_endian>& sym, + Object* object); + + protected: + Target(int size, bool is_big_endian, bool has_make_symbol, bool has_resolve) + : size_(size), + is_big_endian_(is_big_endian), + has_make_symbol_(has_make_symbol), + has_resolve_(has_resolve) + { } + + private: + Target(const Target&); + Target& operator=(const Target&); + + // The target size. + int size_; + // Whether this target is big endian. + bool is_big_endian_; + // Whether this target has a special make_symbol function. + bool has_make_symbol_; + // Whether this target has a special resolve function. + bool has_resolve_; }; -extern Target* select_target(int machine, int size, bool big_endian, - int osabi, int abiversion); +// The abstract class for a specific size and endianness of target. +// Each actual target implementation class should derive from an +// instantiation of Sized_target. + +template<int size, bool big_endian> +class Sized_target : public Target +{ + public: + // Make a new symbol table entry for the target. This should be + // overridden by a target which needs additional information in the + // symbol table. This will only be called if has_make_symbol() + // returns true. + virtual Sized_symbol<size>* + make_symbol() + { abort(); } + + // Resolve a symbol for the target. This should be overridden by a + // target which needs to take special action. TO is the + // pre-existing symbol. SYM is the new symbol, seen in OBJECT. + virtual void + resolve(Symbol*, const elfcpp::Sym<size, big_endian>&, Object*) + { abort(); } + + protected: + Sized_target(bool has_make_symbol, bool has_resolve) + : Target(size, big_endian, has_make_symbol, has_resolve) + { } +}; } // End namespace gold. |