From: Ian Lance Taylor Date: Fri, 3 Nov 2006 18:26:11 +0000 (+0000) Subject: Can now do a full static link of hello, world in C or C++ X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=ead1e4244a55707685d105c662a9a1faf5d122fe;p=binutils-gdb.git Can now do a full static link of hello, world in C or C++ --- diff --git a/gold/Makefile.am b/gold/Makefile.am index 8545a77ee74..13aae2678e4 100644 --- a/gold/Makefile.am +++ b/gold/Makefile.am @@ -19,6 +19,8 @@ noinst_PROGRAMS = ld-new CCFILES = \ archive.cc \ + common.cc \ + defstd.cc \ dirsearch.cc \ fileread.cc \ gold.cc \ @@ -37,6 +39,8 @@ CCFILES = \ HFILES = \ archive.h \ + common.h \ + defstd.h \ dirsearch.h \ fileread.h \ gold.h \ diff --git a/gold/Makefile.in b/gold/Makefile.in index a9d60739bc1..d4c0c67c5ac 100644 --- a/gold/Makefile.in +++ b/gold/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.9.5 from Makefile.am. +# Makefile.in generated by automake 1.9.6 from Makefile.am. # @configure_input@ # Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, @@ -16,8 +16,6 @@ # Process this file with automake to generate Makefile.in -SOURCES = $(ld_new_SOURCES) - srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ @@ -52,10 +50,9 @@ subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/../config/depstand.m4 \ $(top_srcdir)/../config/lead-dot.m4 \ - $(top_srcdir)/../bfd/../config/progtest.m4 \ - $(top_srcdir)/../bfd/../config/po.m4 \ - $(top_srcdir)/../bfd/../config/nls.m4 \ - $(top_srcdir)/../bfd/../config/gettext-sister.m4 \ + $(top_srcdir)/../config/progtest.m4 \ + $(top_srcdir)/../config/po.m4 $(top_srcdir)/../config/nls.m4 \ + $(top_srcdir)/../config/gettext-sister.m4 \ $(top_srcdir)/../bfd/warning.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) @@ -65,12 +62,13 @@ mkinstalldirs = $(SHELL) $(top_srcdir)/../mkinstalldirs CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = po/Makefile.in PROGRAMS = $(noinst_PROGRAMS) -am__objects_1 = archive.$(OBJEXT) dirsearch.$(OBJEXT) \ - fileread.$(OBJEXT) gold.$(OBJEXT) gold-threads.$(OBJEXT) \ - layout.$(OBJEXT) object.$(OBJEXT) options.$(OBJEXT) \ - output.$(OBJEXT) readsyms.$(OBJEXT) reloc.$(OBJEXT) \ - resolve.$(OBJEXT) symtab.$(OBJEXT) stringpool.$(OBJEXT) \ - target-select.$(OBJEXT) workqueue.$(OBJEXT) +am__objects_1 = archive.$(OBJEXT) common.$(OBJEXT) defstd.$(OBJEXT) \ + dirsearch.$(OBJEXT) fileread.$(OBJEXT) gold.$(OBJEXT) \ + gold-threads.$(OBJEXT) layout.$(OBJEXT) object.$(OBJEXT) \ + options.$(OBJEXT) output.$(OBJEXT) readsyms.$(OBJEXT) \ + reloc.$(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,6 +231,8 @@ INCLUDES = -D_GNU_SOURCE \ CCFILES = \ archive.cc \ + common.cc \ + defstd.cc \ dirsearch.cc \ fileread.cc \ gold.cc \ @@ -251,6 +251,8 @@ CCFILES = \ HFILES = \ archive.h \ + common.h \ + defstd.h \ dirsearch.h \ fileread.h \ gold.h \ @@ -347,6 +349,8 @@ distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/archive.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/defstd.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dirsearch.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fileread.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gold-threads.Po@am__quote@ @@ -514,7 +518,7 @@ distclean-tags: distdir: $(DISTFILES) $(am__remove_distdir) mkdir $(distdir) - $(mkdir_p) $(distdir)/.. $(distdir)/../bfd $(distdir)/../bfd/../config $(distdir)/../config $(distdir)/po + $(mkdir_p) $(distdir)/.. $(distdir)/../bfd $(distdir)/../config $(distdir)/po @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ list='$(DISTFILES)'; for file in $$list; do \ diff --git a/gold/aclocal.m4 b/gold/aclocal.m4 index 1385aed620a..4438a34308e 100644 --- a/gold/aclocal.m4 +++ b/gold/aclocal.m4 @@ -1,4 +1,4 @@ -# generated automatically by aclocal 1.9.5 -*- Autoconf -*- +# generated automatically by aclocal 1.9.6 -*- Autoconf -*- # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, # 2005 Free Software Foundation, Inc. @@ -28,7 +28,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"]) # Call AM_AUTOMAKE_VERSION so it can be traced. # This function is AC_REQUIREd by AC_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], - [AM_AUTOMAKE_VERSION([1.9.5])]) + [AM_AUTOMAKE_VERSION([1.9.6])]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- @@ -870,8 +870,8 @@ AC_SUBST([am__untar]) m4_include([../config/depstand.m4]) m4_include([../config/lead-dot.m4]) -m4_include([../bfd/../config/progtest.m4]) -m4_include([../bfd/../config/po.m4]) -m4_include([../bfd/../config/nls.m4]) -m4_include([../bfd/../config/gettext-sister.m4]) +m4_include([../config/progtest.m4]) +m4_include([../config/po.m4]) +m4_include([../config/nls.m4]) +m4_include([../config/gettext-sister.m4]) m4_include([../bfd/warning.m4]) diff --git a/gold/archive.cc b/gold/archive.cc index a64109aef12..031ead09a40 100644 --- a/gold/archive.cc +++ b/gold/archive.cc @@ -9,6 +9,7 @@ #include "elfcpp.h" #include "fileread.h" +#include "readsyms.h" #include "symtab.h" #include "object.h" #include "archive.h" @@ -47,14 +48,6 @@ const char Archive::armag[sarmag] = const char Archive::arfmag[2] = { '`', '\n' }; -// Get a view into the underlying file. - -const unsigned char* -Archive::get_view(off_t start, off_t size) -{ - return this->input_file_->file().get_view(start, size); -} - // Set up the archive: read the symbol map and the extended name // table. @@ -113,6 +106,10 @@ Archive::setup() this->extended_names_.assign(px, extended_size); } + // This array keeps track of which symbols are for archive elements + // which we have already included in the link. + this->seen_.resize(nsyms); + // Opening the file locked it. Unlock it now. this->input_file_->file().unlock(); } @@ -221,10 +218,7 @@ void Archive::add_symbols(Symbol_table* symtab, Layout* layout, Input_objects* input_objects) { - size_t armap_size = this->armap_.size(); - std::vector seen; - seen.resize(this->armap_.size()); - seen.clear(); + const size_t armap_size = this->armap_.size(); bool added_new_object; do @@ -233,20 +227,20 @@ Archive::add_symbols(Symbol_table* symtab, Layout* layout, off_t last = -1; for (size_t i = 0; i < armap_size; ++i) { - if (seen[i]) + if (this->seen_[i]) continue; if (this->armap_[i].offset == last) { - seen[i] = true; + this->seen_[i] = true; continue; } Symbol* sym = symtab->lookup(this->armap_[i].name); if (sym == NULL) continue; - else if (sym->shnum() != elfcpp::SHN_UNDEF) + else if (!sym->is_undefined()) { - seen[i] = true; + this->seen_[i] = true; continue; } else if (sym->binding() == elfcpp::STB_WEAK) @@ -255,6 +249,7 @@ Archive::add_symbols(Symbol_table* symtab, Layout* layout, // We want to include this object in the link. last = this->armap_[i].offset; this->include_member(symtab, layout, input_objects, last); + this->seen_[i] = true; added_new_object = true; } } @@ -337,13 +332,13 @@ class Add_archive_symbols::Add_archive_symbols_locker : public Task_locker { public: Add_archive_symbols_locker(Task_token& token, Workqueue* workqueue, - Archive* archive) - : blocker_(token, workqueue), archlock_(*archive) + File_read& file) + : blocker_(token, workqueue), filelock_(file) { } private: Task_locker_block blocker_; - Task_locker_obj archlock_; + Task_locker_obj filelock_; }; Task_locker* @@ -351,7 +346,7 @@ Add_archive_symbols::locks(Workqueue* workqueue) { return new Add_archive_symbols_locker(*this->next_blocker_, workqueue, - this->archive_); + this->archive_->file()); } void @@ -359,6 +354,14 @@ Add_archive_symbols::run(Workqueue*) { this->archive_->add_symbols(this->symtab_, this->layout_, this->input_objects_); + + if (this->input_group_ != NULL) + this->input_group_->add_archive(this->archive_); + else + { + // We no longer need to know about this archive. + delete this->archive_; + } } } // End namespace gold. diff --git a/gold/archive.h b/gold/archive.h index b2d4c46550f..f0edfcb7ffd 100644 --- a/gold/archive.h +++ b/gold/archive.h @@ -13,6 +13,7 @@ namespace gold class Input_file; class Input_objects; +class Input_group; class Layout; class Symbol_table; @@ -44,6 +45,11 @@ class Archive void setup(); + // Get a reference to the underlying file. + File_read& + file() + { return this->input_file_->file(); } + // Lock the underlying file. void lock() @@ -73,7 +79,8 @@ class Archive // Get a view into the underlying file. const unsigned char* - get_view(off_t start, off_t size); + get_view(off_t start, off_t size) + { return this->input_file_->file().get_view(start, size); } // Read an archive member header at OFF. Return the size of the // member, and set *PNAME to the name. @@ -101,6 +108,9 @@ class Archive std::vector armap_; // The extended name table. std::string extended_names_; + // Track which symbols in the archive map are for elements which + // have already been included in the link. + std::vector seen_; }; // This class is used to read an archive and pick out the desired @@ -111,11 +121,12 @@ class Add_archive_symbols : public Task public: Add_archive_symbols(Symbol_table* symtab, Layout* layout, Input_objects* input_objects, - Archive* archive, Task_token* this_blocker, + Archive* archive, Input_group* input_group, + Task_token* this_blocker, Task_token* next_blocker) : symtab_(symtab), layout_(layout), input_objects_(input_objects), - archive_(archive), this_blocker_(this_blocker), - next_blocker_(next_blocker) + archive_(archive), input_group_(input_group), + this_blocker_(this_blocker), next_blocker_(next_blocker) { } ~Add_archive_symbols(); @@ -138,6 +149,7 @@ class Add_archive_symbols : public Task Layout* layout_; Input_objects* input_objects_; Archive* archive_; + Input_group* input_group_; Task_token* this_blocker_; Task_token* next_blocker_; }; diff --git a/gold/common.cc b/gold/common.cc new file mode 100644 index 00000000000..9738f98e740 --- /dev/null +++ b/gold/common.cc @@ -0,0 +1,208 @@ +// common.cc -- handle common symbols for gold + +#include "gold.h" + +#include + +#include "workqueue.h" +#include "layout.h" +#include "output.h" +#include "common.h" + +namespace gold +{ + +// Allocate_commons_task methods. + +// This task allocates the common symbols. We need a lock on the +// symbol table. + +Task::Is_runnable_type +Allocate_commons_task::is_runnable(Workqueue*) +{ + if (!this->symtab_lock_->is_writable()) + return IS_LOCKED; + return IS_RUNNABLE; +} + +// Return the locks we hold: one on the symbol table, and one blocker. + +class Allocate_commons_task::Allocate_commons_locker : public Task_locker +{ + public: + Allocate_commons_locker(Task_token& symtab_lock, Task* task, + Task_token& blocker, Workqueue* workqueue) + : symtab_locker_(symtab_lock, task), + blocker_(blocker, workqueue) + { } + + private: + Task_locker_write symtab_locker_; + Task_locker_block blocker_; +}; + +Task_locker* +Allocate_commons_task::locks(Workqueue* workqueue) +{ + return new Allocate_commons_locker(*this->symtab_lock_, this, + *this->blocker_, workqueue); +} + +// Allocate the common symbols. + +void +Allocate_commons_task::run(Workqueue*) +{ + this->symtab_->allocate_commons(this->options_, this->layout_); +} + +// This class is used to sort the common symbol by size. We put the +// larger common symbols first. + +template +class Sort_commons +{ + public: + Sort_commons(const Symbol_table* symtab) + : symtab_(symtab) + { } + + bool operator()(const Symbol* a, const Symbol* b) const; + + private: + const Symbol_table* symtab_; +}; + +template +bool +Sort_commons::operator()(const Symbol* pa, const Symbol* pb) const +{ + if (pa == NULL) + return false; + if (pb == NULL) + return true; + + const Symbol_table* symtab = this->symtab_; + const Sized_symbol* psa; + psa = symtab->get_sized_symbol SELECT_SIZE_NAME (pa + SELECT_SIZE(size)); + const Sized_symbol* psb; + psb = symtab->get_sized_symbol SELECT_SIZE_NAME (pb + SELECT_SIZE(size)); + + typename Sized_symbol::Size_type sa = psa->symsize(); + typename Sized_symbol::Size_type sb = psb->symsize(); + if (sa < sb) + return false; + else if (sb > sa) + return true; + + // When the symbols are the same size, we sort them by alignment. + typename Sized_symbol::Value_type va = psa->value(); + typename Sized_symbol::Value_type vb = psb->value(); + if (va < vb) + return false; + else if (vb > va) + return true; + + // Otherwise we stabilize the sort by sorting by name. + return strcmp(psa->name(), psb->name()) < 0; +} + +// Allocate the common symbols. + +void +Symbol_table::allocate_commons(const General_options& options, Layout* layout) +{ + if (this->get_size() == 32) + this->do_allocate_commons<32>(options, layout); + else if (this->get_size() == 64) + this->do_allocate_commons<64>(options, layout); + else + abort(); +} + +// Allocated the common symbols, sized version. + +template +void +Symbol_table::do_allocate_commons(const General_options&, + Layout* layout) +{ + typedef typename Sized_symbol::Value_type Value_type; + typedef typename Sized_symbol::Size_type Size_type; + + // We've kept a list of all the common symbols. But the symbol may + // have been resolved to a defined symbol by now. And it may be a + // forwarder. First remove all non-common symbols. + bool any = false; + uint64_t addralign = 0; + for (Commons_type::iterator p = this->commons_.begin(); + p != this->commons_.end(); + ++p) + { + Symbol* sym = *p; + if (sym->is_forwarder()) + { + sym = this->resolve_forwards(sym); + *p = sym; + } + if (!sym->is_common()) + *p = NULL; + else + { + any = true; + Sized_symbol* ssym; + ssym = this->get_sized_symbol SELECT_SIZE_NAME (sym + SELECT_SIZE(size)); + if (ssym->value() > addralign) + addralign = ssym->value(); + } + } + if (!any) + return; + + // Sort the common symbols by size, so that they pack better into + // memory. + std::sort(this->commons_.begin(), this->commons_.end(), + Sort_commons(this)); + + // Place them in a newly allocated .bss section. + + Output_section_common *poc = new Output_section_common(addralign); + + layout->add_output_section_data(".bss", elfcpp::SHT_NOBITS, + elfcpp::SHF_WRITE | elfcpp::SHF_ALLOC, + poc); + + // Allocate them all. + + off_t off = 0; + for (Commons_type::iterator p = this->commons_.begin(); + p != this->commons_.end(); + ++p) + { + Symbol* sym = *p; + if (sym == NULL) + break; + + Sized_symbol* ssym; + ssym = this->get_sized_symbol SELECT_SIZE_NAME (sym + SELECT_SIZE(size)); + + off = align_address(off, ssym->value()); + + Size_type symsize = ssym->symsize(); + ssym->init(ssym->name(), poc, off, symsize, ssym->type(), + ssym->binding(), ssym->visibility(), ssym->nonvis(), + false); + + off += symsize; + } + + poc->set_common_size(off); + + this->commons_.clear(); +} + +} // End namespace gold. diff --git a/gold/common.h b/gold/common.h new file mode 100644 index 00000000000..75237a6a5c5 --- /dev/null +++ b/gold/common.h @@ -0,0 +1,49 @@ +// common.h -- handle common symbols for gold -*- C++ -*- + +#ifndef GOLD_COMMON_H +#define GOLD_COMMON_H + +#include "workqueue.h" + +namespace gold +{ + +class General_options; +class Symbol_table; + +// This task is used to allocate the common symbols. + +class Allocate_commons_task : public Task +{ + public: + Allocate_commons_task(const General_options& options, Symbol_table* symtab, + Layout* layout, Task_token* symtab_lock, + Task_token* blocker) + : options_(options), symtab_(symtab), layout_(layout), + symtab_lock_(symtab_lock), blocker_(blocker) + { } + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + class Allocate_commons_locker; + + const General_options& options_; + Symbol_table* symtab_; + Layout* layout_; + Task_token* symtab_lock_; + Task_token* blocker_; +}; + +} // End namespace gold. + +#endif // !defined(GOLD_COMMON_H) diff --git a/gold/defstd.cc b/gold/defstd.cc new file mode 100644 index 00000000000..29fd2cd8cf9 --- /dev/null +++ b/gold/defstd.cc @@ -0,0 +1,127 @@ +// defstd.cc -- define standard symbols for gold. + +#include "gold.h" + +#include "symtab.h" +#include "defstd.h" + +// This is a simple file which defines the standard symbols like +// "_end". + +namespace +{ + +using namespace gold; + +const Define_symbol_in_section in_section[] = +{ + { + "__preinit_array_start", // name + ".preinit_array", // output_section + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_HIDDEN, // visibility + 0, // nonvis + false, // offset_is_from_end + true // only_if_ref + }, + { + "__preinit_array_end", // name + ".preinit_array", // output_section + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_HIDDEN, // visibility + 0, // nonvis + true, // offset_is_from_end + true // only_if_ref + }, + { + "__init_array_start", // name + ".init_array", // output_section + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_HIDDEN, // visibility + 0, // nonvis + false, // offset_is_from_end + true // only_if_ref + }, + { + "__init_array_end", // name + ".init_array", // output_section + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_HIDDEN, // visibility + 0, // nonvis + true, // offset_is_from_end + true // only_if_ref + }, + { + "__fini_array_start", // name + ".fini_array", // output_section + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_HIDDEN, // visibility + 0, // nonvis + false, // offset_is_from_end + true // only_if_ref + }, + { + "__fini_array_end", // name + ".fini_array", // output_section + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_HIDDEN, // visibility + 0, // nonvis + true, // offset_is_from_end + true // only_if_ref + } +}; + +const int in_section_count = sizeof in_section / sizeof in_section[0]; + +const Define_symbol_in_segment in_segment[] = +{ + { + "_end", // name + elfcpp::PT_LOAD, // segment_type + elfcpp::PF_W, // segment_flags_set + elfcpp::PF(0), // segment_flags_clear + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_DEFAULT, // visibility + 0, // nonvis + Symbol::SEGMENT_START, // offset_from_bas + false // only_if_ref + } +}; + +const int in_segment_count = sizeof in_segment / sizeof in_segment[0]; + +} // End anonymous namespace. + +namespace gold +{ + +void +define_standard_symbols(Symbol_table* symtab, const Layout* layout, + Target* target) +{ + symtab->define_symbols(layout, target, in_section_count, in_section); + symtab->define_symbols(layout, target, in_segment_count, in_segment); +} + +} // End namespace gold. diff --git a/gold/defstd.h b/gold/defstd.h new file mode 100644 index 00000000000..f578b49d76f --- /dev/null +++ b/gold/defstd.h @@ -0,0 +1,16 @@ +// defstd.h -- define standard symbols for gold -*- C++ -*- + +#ifndef GOLD_DEFSTD_H +#define GOLD_DEFSTD_H + +#include "symtab.h" + +namespace gold +{ + +extern void +define_standard_symbols(Symbol_table*, const Layout*, Target*); + +} // End namespace gold. + +#endif // !defined(GOLD_DEFSTD_H) diff --git a/gold/dirsearch.h b/gold/dirsearch.h index 3beb1436c5a..8b6ba59bf58 100644 --- a/gold/dirsearch.h +++ b/gold/dirsearch.h @@ -6,12 +6,13 @@ #include #include -#include "options.h" #include "workqueue.h" namespace gold { +class General_options; + // A simple interface to manage directories to be searched for // libraries. diff --git a/gold/fileread.cc b/gold/fileread.cc index 987408ec244..00971a76ca5 100644 --- a/gold/fileread.cc +++ b/gold/fileread.cc @@ -102,18 +102,16 @@ File_read::is_locked() // See if we have a view which covers the file starting at START for // SIZE bytes. Return a pointer to the View if found, NULL if not. -File_read::View* +inline File_read::View* File_read::find_view(off_t start, off_t size) { - for (std::list::iterator p = this->view_list_.begin(); - p != this->view_list_.end(); - ++p) - { - if ((*p)->start() <= start - && (*p)->start() + (*p)->size() >= start + size) - return *p; - } - return NULL; + off_t page = File_read::page_offset(start); + Views::iterator p = this->views_.find(page); + if (p == this->views_.end()) + return NULL; + if (p->second->size() - (start - page) < size) + return NULL; + return p->second; } // Read data from the file. Return the number of bytes read. If @@ -184,15 +182,59 @@ File_read::find_or_make_view(off_t start, off_t size, off_t* pbytes) { assert(this->lock_count_ > 0); - File_read::View* pv = this->find_view(start, size); - if (pv != NULL) - return pv; + off_t poff = File_read::page_offset(start); + + File_read::View* const vnull = NULL; + std::pair ins = + this->views_.insert(std::make_pair(poff, vnull)); + + if (!ins.second) + { + // There was an existing view at this offset. + File_read::View* v = ins.first->second; + if (v->size() - (start - v->start()) >= size) + { + if (pbytes != NULL) + *pbytes = size; + return v; + } + + // This view is not large enough. + this->saved_views_.push_back(v); + } + + // We need to read data from the file. + + off_t psize = File_read::pages(size + (start - poff)); + unsigned char* p = new unsigned char[psize]; - unsigned char* p = new unsigned char[size]; - off_t bytes = this->do_read(start, size, p, pbytes); - pv = new File_read::View(start, bytes, p); - this->view_list_.push_back(pv); - return pv; + off_t got_bytes; + off_t bytes = this->do_read(poff, psize, p, &got_bytes); + + File_read::View* v = new File_read::View(poff, bytes, p); + + ins.first->second = v; + + if (bytes - (start - poff) >= size) + { + if (pbytes != NULL) + *pbytes = size; + return v; + } + + if (pbytes != NULL) + { + *pbytes = bytes - (start - poff); + return v; + } + + fprintf(stderr, + _("%s: %s: file too short: read only %lld of %lld bytes at %lld\n"), + program_name, this->filename().c_str(), + static_cast(bytes - (start - poff)), + static_cast(size), + static_cast(start)); + gold_exit(false); } // This implementation of get_view just reads into a memory buffer, @@ -221,18 +263,32 @@ File_read::get_lasting_view(off_t start, off_t size, off_t* pbytes) void File_read::clear_views(bool destroying) { - std::list::iterator p = this->view_list_.begin(); - while (p != this->view_list_.end()) + for (Views::iterator p = this->views_.begin(); + p != this->views_.end(); + ++p) { - if ((*p)->is_locked()) + if (!p->second->is_locked()) + delete p->second; + else { assert(!destroying); - ++p; + this->saved_views_.push_back(p->second); } - else + } + this->views_.clear(); + + Saved_views::iterator p = this->saved_views_.begin(); + while (p != this->saved_views_.end()) + { + if (!(*p)->is_locked()) { delete *p; - p = this->view_list_.erase(p); + p = this->saved_views_.erase(p); + } + else + { + assert(!destroying); + ++p; } } } diff --git a/gold/fileread.h b/gold/fileread.h index b65a86b0225..6e49324ab86 100644 --- a/gold/fileread.h +++ b/gold/fileread.h @@ -5,8 +5,9 @@ #ifndef GOLD_FILEREAD_H #define GOLD_FILEREAD_H -#include #include +#include +#include #include "options.h" @@ -14,7 +15,6 @@ namespace gold { class Dirsearch; - class File_view; // File_read manages a file descriptor for a file we are reading. We @@ -123,22 +123,52 @@ class File_read friend class File_view; + // Find a view into the file. View* find_view(off_t start, off_t size); + // Read data from the file into a buffer. off_t do_read(off_t start, off_t size, void* p, off_t* pbytes); + // Find or make a view into the file. View* find_or_make_view(off_t start, off_t size, off_t* pbytes); + // Clear the file views. void clear_views(bool); + // The size of a file page for buffering data. + static const off_t page_size = 8192; + + // Given a file offset, return the page offset. + static off_t + page_offset(off_t file_offset) + { return file_offset & ~ (page_size - 1); } + + // Given a file size, return the size to read integral pages. + static off_t + pages(off_t file_size) + { return (file_size + (page_size - 1)) & ~ (page_size - 1); } + + // The type of a mapping from page start to views. + typedef std::map Views; + + // A simple list of Views. + typedef std::list Saved_views; + + // File name. std::string name_; + // File descriptor. int descriptor_; + // Number of locks on the file. int lock_count_; - std::list view_list_; + // Buffered views into the file. + Views views_; + // List of views which were locked but had to be removed from views_ + // because they were not large enough. + Saved_views saved_views_; }; // A view of file data that persists even when the file is unlocked. @@ -179,7 +209,7 @@ class File_view class Input_file { public: - Input_file(const Input_argument& input_argument) + Input_file(const Input_file_argument& input_argument) : input_argument_(input_argument) { } @@ -201,7 +231,10 @@ class Input_file { return this->file_; } private: - const Input_argument& input_argument_; + Input_file(const Input_file&); + Input_file& operator=(const Input_file&); + + const Input_file_argument& input_argument_; File_read file_; }; diff --git a/gold/gold.cc b/gold/gold.cc index c39f9995858..31598dc9e6a 100644 --- a/gold/gold.cc +++ b/gold/gold.cc @@ -12,9 +12,11 @@ #include "dirsearch.h" #include "readsyms.h" #include "symtab.h" +#include "common.h" #include "object.h" #include "layout.h" #include "reloc.h" +#include "defstd.h" namespace gold { @@ -92,11 +94,11 @@ Middle_runner::run(Workqueue* workqueue) void queue_initial_tasks(const General_options& options, const Dirsearch& search_path, - const Input_argument_list& inputs, + const Command_line& cmdline, Workqueue* workqueue, Input_objects* input_objects, Symbol_table* symtab, Layout* layout) { - if (inputs.empty()) + if (cmdline.begin() == cmdline.end()) gold_fatal(_("no input files"), false); // Read the input files. We have to add the symbols to the symbol @@ -104,14 +106,14 @@ queue_initial_tasks(const General_options& options, // each input file. We associate the blocker with the following // input file, to give us a convenient place to delete it. Task_token* this_blocker = NULL; - for (Input_argument_list::const_iterator p = inputs.begin(); - p != inputs.end(); + for (Command_line::const_iterator p = cmdline.begin(); + p != cmdline.end(); ++p) { Task_token* next_blocker = new Task_token(); next_blocker->add_blocker(); workqueue->queue(new Read_symbols(options, input_objects, symtab, layout, - search_path, *p, this_blocker, + search_path, *p, NULL, this_blocker, next_blocker)); this_blocker = next_blocker; } @@ -134,6 +136,10 @@ queue_middle_tasks(const General_options& options, Layout* layout, Workqueue* workqueue) { + // Predefine standard symbols. This should be fast, so we don't + // bother to create a task for it. + define_standard_symbols(symtab, layout, input_objects->target()); + // Read the relocations of the input files. We do this to find // which symbols are used by relocations which require a GOT and/or // a PLT entry, or a COPY reloc. When we implement garbage @@ -157,15 +163,15 @@ queue_middle_tasks(const General_options& options, // relocations. That task will in turn queue a task to wait // until it can write to the symbol table. blocker->add_blocker(); - workqueue->queue(new Read_relocs(options, symtab, *p, symtab_lock, - blocker)); + workqueue->queue(new Read_relocs(options, symtab, layout, *p, + symtab_lock, blocker)); } // Allocate common symbols. This requires write access to the // symbol table, but is independent of the relocation processing. - // blocker->add_blocker(); - // workqueue->queue(new Allocate_commons_task(options, symtab, layout, - // symtab_lock, blocker)); + blocker->add_blocker(); + workqueue->queue(new Allocate_commons_task(options, symtab, layout, + symtab_lock, blocker)); // When all those tasks are complete, we can start laying out the // output file. @@ -257,7 +263,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, &input_objects, + command_line, &workqueue, &input_objects, &symtab, &layout); // Run the main task processing loop. diff --git a/gold/i386.cc b/gold/i386.cc index 468cb90efe7..53af5eeae6f 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -1,10 +1,14 @@ // i386.cc -- i386 target support for gold. #include "gold.h" + +#include + #include "elfcpp.h" #include "reloc.h" #include "i386.h" #include "object.h" +#include "symtab.h" #include "layout.h" #include "output.h" #include "target.h" @@ -22,13 +26,15 @@ class Target_i386 : public Sized_target<32, false> { public: Target_i386() - : Sized_target<32, false>(&i386_info) + : Sized_target<32, false>(&i386_info), + got_(NULL) { } // Scan the relocations to look for symbol adjustments. void scan_relocs(const General_options& options, Symbol_table* symtab, + Layout* layout, Sized_object<32, false>* object, unsigned int sh_type, const unsigned char* prelocs, @@ -52,12 +58,16 @@ class Target_i386 : public Sized_target<32, false> struct Scan { inline void - local(const General_options& options, Sized_object<32, false>* object, + local(const General_options& options, Symbol_table* symtab, + Layout* layout, Target_i386* target, + Sized_object<32, false>* object, const elfcpp::Rel<32, false>& reloc, unsigned int r_type, const elfcpp::Sym<32, false>& lsym); inline void - global(const General_options& options, Sized_object<32, false>* object, + global(const General_options& options, Symbol_table* symtab, + Layout* layout, Target_i386* target, + Sized_object<32, false>* object, const elfcpp::Rel<32, false>& reloc, unsigned int r_type, Symbol* gsym); }; @@ -66,9 +76,25 @@ class Target_i386 : public Sized_target<32, false> class Relocate { public: - // Do a relocation. - static inline void - relocate(const Relocate_info<32, false>*, size_t relnum, + Relocate() + : skip_call_tls_get_addr_(false) + { } + + ~Relocate() + { + if (this->skip_call_tls_get_addr_) + { + // FIXME: This needs to specify the location somehow. + fprintf(stderr, _("%s: missing expected TLS relocation\n"), + program_name); + gold_exit(false); + } + } + + // Do a relocation. Return false if the caller should not issue + // any warnings about this relocation. + inline bool + relocate(const Relocate_info<32, false>*, Target_i386*, size_t relnum, const elfcpp::Rel<32, false>&, unsigned int r_type, Sized_symbol<32>*, elfcpp::Elf_types<32>::Elf_Addr, @@ -77,7 +103,7 @@ class Target_i386 : public Sized_target<32, false> private: // Do a TLS relocation. - static inline void + inline void relocate_tls(const Relocate_info<32, false>*, size_t relnum, const elfcpp::Rel<32, false>&, unsigned int r_type, Sized_symbol<32>*, @@ -93,6 +119,15 @@ class Target_i386 : public Sized_target<32, false> unsigned char* view, off_t view_size); + // Do a TLS Global-Dynamic to Local-Exec transition. + inline void + tls_gd_to_le(const Relocate_info<32, false>*, size_t relnum, + Output_segment* tls_segment, + const elfcpp::Rel<32, false>&, unsigned int r_type, + elfcpp::Elf_types<32>::Elf_Addr value, + unsigned char* view, + off_t view_size); + // Check the range for a TLS relocation. static inline void check_range(const Relocate_info<32, false>*, size_t relnum, @@ -102,6 +137,10 @@ class Target_i386 : public Sized_target<32, false> static inline void check_tls(const Relocate_info<32, false>*, size_t relnum, const elfcpp::Rel<32, false>&, bool); + + // This is set if we should skip the next reloc, which should be a + // PLT32 reloc against ___tls_get_addr. + bool skip_call_tls_get_addr_; }; // Adjust TLS relocation type based on the options and whether this @@ -109,9 +148,16 @@ class Target_i386 : public Sized_target<32, false> static unsigned int optimize_tls_reloc(const General_options*, bool is_local, int r_type); + // Get the GOT section, creating it if necessary. + Output_section_got<32, false>* + got_section(Symbol_table*, Layout*); + // Information about this specific target which we pass to the // general Target structure. static const Target::Target_info i386_info; + + // The GOT section. + Output_section_got<32, false>* got_; }; const Target::Target_info Target_i386::i386_info = @@ -126,6 +172,34 @@ const Target::Target_info Target_i386::i386_info = 0x1000 // common_pagesize }; +// Get the GOT section, creating it if necessary. + +Output_section_got<32, false>* +Target_i386::got_section(Symbol_table* symtab, Layout* layout) +{ + if (this->got_ == NULL) + { + this->got_ = new Output_section_got<32, false>(); + + assert(symtab != NULL && layout != NULL); + layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + elfcpp::SHF_ALLOC, this->got_); + + // The first three entries are reserved. + this->got_->add_constant(0); + this->got_->add_constant(0); + this->got_->add_constant(0); + + // Define _GLOBAL_OFFSET_TABLE_ at the start of the section. + symtab->define_in_output_data(this, "_GLOBAL_OFFSET_TABLE_", this->got_, + 0, 0, elfcpp::STT_OBJECT, + elfcpp::STB_GLOBAL, + elfcpp::STV_HIDDEN, 0, + false, false); + } + return this->got_; +} + // Optimize the TLS relocation type based on what we know about the // symbol. IS_LOCAL is true if this symbol can be resolved entirely // locally--i.e., does not have to be in the dynamic symbol table. @@ -188,6 +262,9 @@ Target_i386::optimize_tls_reloc(const General_options* options, bool is_local, inline void Target_i386::Scan::local(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Target_i386* target, Sized_object<32, false>* object, const elfcpp::Rel<32, false>&, unsigned int r_type, const elfcpp::Sym<32, false>&) @@ -211,6 +288,12 @@ Target_i386::Scan::local(const General_options& options, case elfcpp::R_386_PC8: break; + case elfcpp::R_386_GOTOFF: + case elfcpp::R_386_GOTPC: + // We need a GOT section. + target->got_section(symtab, layout); + break; + case elfcpp::R_386_COPY: case elfcpp::R_386_GLOB_DAT: case elfcpp::R_386_JUMP_SLOT: @@ -261,8 +344,6 @@ Target_i386::Scan::local(const General_options& options, case elfcpp::R_386_GOT32: case elfcpp::R_386_PLT32: - case elfcpp::R_386_GOTOFF: - case elfcpp::R_386_GOTPC: case elfcpp::R_386_32PLT: case elfcpp::R_386_TLS_GD_32: case elfcpp::R_386_TLS_GD_PUSH: @@ -284,6 +365,9 @@ Target_i386::Scan::local(const General_options& options, inline void Target_i386::Scan::global(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Target_i386* target, Sized_object<32, false>* object, const elfcpp::Rel<32, false>&, unsigned int r_type, Symbol* gsym) @@ -307,6 +391,37 @@ Target_i386::Scan::global(const General_options& options, // relocation in order to avoid a COPY relocation. break; + case elfcpp::R_386_GOT32: + // The symbol requires a GOT entry. + if (!gsym->has_got_offset()) + { + Output_section_got<32, false>* got = target->got_section(symtab, + layout); + const unsigned int got_offset = got->add_global(gsym); + gsym->set_got_offset(got_offset); + + // If this symbol is not resolved locally, we need to add a + // dynamic relocation for it. + if (!gsym->is_resolved_locally()) + abort(); + } + break; + + case elfcpp::R_386_PLT32: + // If the symbol is resolved locally, this is just a PC32 reloc. + if (gsym->is_resolved_locally()) + break; + fprintf(stderr, + _("%s: %s: unsupported reloc %u against global symbol %s\n"), + program_name, object->name().c_str(), r_type, gsym->name()); + break; + + case elfcpp::R_386_GOTOFF: + case elfcpp::R_386_GOTPC: + // We need a GOT section. + target->got_section(symtab, layout); + break; + case elfcpp::R_386_COPY: case elfcpp::R_386_GLOB_DAT: case elfcpp::R_386_JUMP_SLOT: @@ -332,7 +447,7 @@ Target_i386::Scan::global(const General_options& options, case elfcpp::R_386_TLS_GOTDESC: case elfcpp::R_386_TLS_DESC_CALL: r_type = Target_i386::optimize_tls_reloc(&options, - !gsym->in_dynsym(), + gsym->is_resolved_locally(), r_type); switch (r_type) { @@ -357,10 +472,6 @@ Target_i386::Scan::global(const General_options& options, } break; - case elfcpp::R_386_GOT32: - case elfcpp::R_386_PLT32: - case elfcpp::R_386_GOTOFF: - case elfcpp::R_386_GOTPC: case elfcpp::R_386_32PLT: case elfcpp::R_386_TLS_GD_32: case elfcpp::R_386_TLS_GD_PUSH: @@ -384,6 +495,7 @@ Target_i386::Scan::global(const General_options& options, void Target_i386::scan_relocs(const General_options& options, Symbol_table* symtab, + Layout* layout, Sized_object<32, false>* object, unsigned int sh_type, const unsigned char* prelocs, @@ -399,9 +511,12 @@ Target_i386::scan_relocs(const General_options& options, gold_exit(false); } - gold::scan_relocs<32, false, elfcpp::SHT_REL, Target_i386::Scan>( + gold::scan_relocs<32, false, Target_i386, elfcpp::SHT_REL, + Target_i386::Scan>( options, symtab, + layout, + this, object, prelocs, reloc_count, @@ -412,8 +527,9 @@ Target_i386::scan_relocs(const General_options& options, // Perform a relocation. -inline void +inline bool Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, + Target_i386* target, size_t relnum, const elfcpp::Rel<32, false>& rel, unsigned int r_type, @@ -423,6 +539,23 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, elfcpp::Elf_types<32>::Elf_Addr address, off_t view_size) { + if (this->skip_call_tls_get_addr_) + { + if (r_type != elfcpp::R_386_PLT32 + || gsym == NULL + || strcmp(gsym->name(), "___tls_get_addr") != 0) + { + fprintf(stderr, _("%s: %s: missing expected TLS relocation\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str()); + gold_exit(false); + } + + this->skip_call_tls_get_addr_ = false; + + return false; + } + switch (r_type) { case elfcpp::R_386_NONE: @@ -454,6 +587,34 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, Relocate_functions<32, false>::pcrel8(view, value, address); break; + case elfcpp::R_386_PLT32: + if (gsym->is_resolved_locally()) + Relocate_functions<32, false>::pcrel32(view, value, address); + else + fprintf(stderr, _("%s: %s: unsupported reloc %u\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str(), + r_type); + break; + + case elfcpp::R_386_GOT32: + // Local GOT offsets not yet supported. + assert(gsym); + assert(gsym->has_got_offset()); + value = gsym->got_offset(); + Relocate_functions<32, false>::rel32(view, value); + break; + + case elfcpp::R_386_GOTOFF: + value -= target->got_section(NULL, NULL)->address(); + Relocate_functions<32, false>::rel32(view, value); + break; + + case elfcpp::R_386_GOTPC: + value = target->got_section(NULL, NULL)->address(); + Relocate_functions<32, false>::pcrel32(view, value, address); + break; + case elfcpp::R_386_COPY: case elfcpp::R_386_GLOB_DAT: case elfcpp::R_386_JUMP_SLOT: @@ -480,15 +641,10 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, case elfcpp::R_386_TLS_LE_32: case elfcpp::R_386_TLS_GOTDESC: case elfcpp::R_386_TLS_DESC_CALL: - Target_i386::Relocate::relocate_tls(relinfo, relnum, rel, r_type, - gsym, value, view, address, - view_size); + this->relocate_tls(relinfo, relnum, rel, r_type, gsym, value, view, + address, view_size); break; - case elfcpp::R_386_GOT32: - case elfcpp::R_386_PLT32: - case elfcpp::R_386_GOTOFF: - case elfcpp::R_386_GOTPC: case elfcpp::R_386_32PLT: case elfcpp::R_386_TLS_GD_32: case elfcpp::R_386_TLS_GD_PUSH: @@ -507,6 +663,8 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, // gold_exit(false); break; } + + return true; } // Perform a TLS relocation. @@ -531,7 +689,7 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, gold_exit(false); } - const bool is_local = gsym == NULL || !gsym->in_dynsym(); + const bool is_local = gsym == NULL || gsym->is_resolved_locally(); const unsigned int opt_r_type = Target_i386::optimize_tls_reloc(relinfo->options, is_local, r_type); switch (r_type) @@ -564,6 +722,20 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, break; case elfcpp::R_386_TLS_GD: + if (opt_r_type == elfcpp::R_386_TLS_LE_32) + { + this->tls_gd_to_le(relinfo, relnum, tls_segment, + rel, r_type, value, view, + view_size); + break; + } + fprintf(stderr, _("%s: %s: unsupported reloc %u\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str(), + r_type); + // gold_exit(false); + break; + case elfcpp::R_386_TLS_LDM: case elfcpp::R_386_TLS_LDO_32: case elfcpp::R_386_TLS_GOTDESC: @@ -667,14 +839,79 @@ Target_i386::Relocate::tls_ie_to_le(const Relocate_info<32, false>* relinfo, Target_i386::Relocate::check_tls(relinfo, relnum, rel, 0); } - if (r_type == elfcpp::R_386_TLS_IE_32) - value = tls_segment->vaddr() + tls_segment->memsz() - value; - else // elfcpp::R_386_TLS_IE, elfcpp::R_386_TLS_GOTIE - value = value - (tls_segment->vaddr() + tls_segment->memsz()); + value = tls_segment->vaddr() + tls_segment->memsz() - value; + if (r_type == elfcpp::R_386_TLS_IE || r_type == elfcpp::R_386_TLS_GOTIE) + value = - value; Relocate_functions<32, false>::rel32(view, value); } +// Do a relocation in which we convert a TLS Global-Dynamic to a +// Local-Exec. + +inline void +Target_i386::Relocate::tls_gd_to_le(const Relocate_info<32, false>* relinfo, + size_t relnum, + Output_segment* tls_segment, + const elfcpp::Rel<32, false>& rel, + unsigned int, + elfcpp::Elf_types<32>::Elf_Addr value, + unsigned char* view, + off_t view_size) +{ + // leal foo(,%reg,1),%eax; call ___tls_get_addr + // ==> movl %gs,0,%eax; subl $foo@tpoff,%eax + // leal foo(%reg),%eax; call ___tls_get_addr + // ==> movl %gs:0,%eax; subl $foo@tpoff,%eax + + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -2); + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, 9); + + unsigned char op1 = view[-1]; + unsigned char op2 = view[-2]; + + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + op2 == 0x8d || op2 == 0x04); + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + view[4] == 0xe8); + + int roff = 5; + + if (op2 == 0x04) + { + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -3); + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + view[-3] == 0x8d); + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + ((op1 & 0xc7) == 0x05 + && op1 != (4 << 3))); + memcpy(view - 3, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12); + } + else + { + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + (op1 & 0xf8) == 0x80 && (op1 & 7) != 4); + if (rel.get_r_offset() + 9 < view_size && view[9] == 0x90) + { + // There is a trailing nop. Use the size byte subl. + memcpy(view - 2, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12); + roff = 6; + } + else + { + // Use the five byte subl. + memcpy(view - 2, "\x65\xa1\0\0\0\0\x2d\0\0\0", 11); + } + } + + value = tls_segment->vaddr() + tls_segment->memsz() - value; + Relocate_functions<32, false>::rel32(view + roff, value); + + // The next reloc should be a PLT32 reloc against __tls_get_addr. + // We can skip it. + this->skip_call_tls_get_addr_ = true; +} + // Check the range for a TLS relocation. inline void @@ -724,8 +961,10 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo, { assert(sh_type == elfcpp::SHT_REL); - gold::relocate_section<32, false, elfcpp::SHT_REL, Target_i386::Relocate>( + gold::relocate_section<32, false, Target_i386, elfcpp::SHT_REL, + Target_i386::Relocate>( relinfo, + this, prelocs, reloc_count, view, @@ -733,10 +972,6 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo, view_size); } -// The i386 target. - -Target_i386 target_i386; - // The selector for i386 object files. class Target_selector_i386 : public Target_selector @@ -747,16 +982,21 @@ public: { } Target* - recognize(int machine, int osabi, int abiversion) const; + recognize(int machine, int osabi, int abiversion); + + private: + Target_i386* target_; }; // 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 +Target_selector_i386::recognize(int, int, int) { - return &target_i386; + if (this->target_ == NULL) + this->target_ = new Target_i386(); + return this->target_; } Target_selector_i386 target_selector_i386; diff --git a/gold/layout.cc b/gold/layout.cc index a81fd4393f5..9e85f192a4c 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -38,7 +38,7 @@ Layout_task_runner::run(Workqueue* workqueue) // Layout methods. Layout::Layout(const General_options& options) - : options_(options), last_shndx_(0), namepool_(), sympool_(), signatures_(), + : options_(options), namepool_(), sympool_(), signatures_(), section_name_map_(), segment_list_(), section_list_(), special_output_list_(), tls_segment_(NULL) { @@ -86,66 +86,114 @@ Layout::include_section(Object*, const char*, } } -// 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. +// Return an output section named NAME, or NULL if there is none. -template Output_section* -Layout::layout(Object* object, const char* name, - const elfcpp::Shdr& shdr, off_t* off) +Layout::find_output_section(const char* name) const { - // We discard empty input sections. - if (shdr.get_sh_size() == 0) - return NULL; - - 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); + for (Section_name_map::const_iterator p = this->section_name_map_.begin(); + p != this->section_name_map_.end(); + ++p) + if (strcmp(p->first.first, name) == 0) + return p->second; + return NULL; +} - // FIXME: Handle SHF_OS_NONCONFORMING here. +// Return an output segment of type TYPE, with segment flags SET set +// and segment flags CLEAR clear. Return NULL if there is none. - // Canonicalize the section name. - name = this->namepool_.add(name); +Output_segment* +Layout::find_output_segment(elfcpp::PT type, elfcpp::Elf_Word set, + elfcpp::Elf_Word clear) const +{ + for (Segment_list::const_iterator p = this->segment_list_.begin(); + p != this->segment_list_.end(); + ++p) + if (static_cast((*p)->type()) == type + && ((*p)->flags() & set) == set + && ((*p)->flags() & clear) == 0) + return *p; + return NULL; +} - // Find the output section. The output section is selected based on - // the section name, type, and flags. +// Return the output section to use for section NAME with type TYPE +// and section flags 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. +Output_section* +Layout::get_output_section(const char* name, elfcpp::Elf_Word type, + elfcpp::Elf_Xword flags) +{ + // We should ignore some flags. + flags &= ~ (elfcpp::SHF_INFO_LINK + | elfcpp::SHF_LINK_ORDER + | elfcpp::SHF_GROUP); - 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 v(key, NULL); std::pair ins( this->section_name_map_.insert(v)); - Output_section* os; if (!ins.second) - os = ins.first->second; + return 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); + Output_section* os = this->make_output_section(name, type, flags); ins.first->second = os; + return os; } +} + +// Return the output section to use for input section SHNDX, with name +// NAME, with header HEADER, from object OBJECT. Set *OFF to the +// offset of this input section without the output section. + +template +Output_section* +Layout::layout(Object* object, unsigned int shndx, const char* name, + const elfcpp::Shdr& shdr, off_t* off) +{ + if (!this->include_section(object, name, shdr)) + return NULL; + + // If we are not doing a relocateable link, choose the name to use + // for the output section. + size_t len = strlen(name); + if (!this->options_.is_relocatable()) + name = Layout::output_section_name(name, &len); + + // FIXME: Handle SHF_OS_NONCONFORMING here. + + // Canonicalize the section name. + name = this->namepool_.add(name, len); + + // Find the output section. The output section is selected based on + // the section name, type, and flags. + Output_section* os = this->get_output_section(name, shdr.get_sh_type(), + shdr.get_sh_flags()); // FIXME: Handle SHF_LINK_ORDER somewhere. - *off = os->add_input_section(object, name, shdr); + *off = os->add_input_section(object, shndx, name, shdr); return os; } +// Add POSD to an output section using NAME, TYPE, and FLAGS. + +void +Layout::add_output_section_data(const char* name, elfcpp::Elf_Word type, + elfcpp::Elf_Xword flags, + Output_section_data* posd) +{ + // Canonicalize the name. + name = this->namepool_.add(name); + + Output_section* os = this->get_output_section(name, type, flags); + os->add_output_section_data(posd); +} + // Map section flags to segment flags. elfcpp::Elf_Word @@ -166,9 +214,7 @@ Output_section* Layout::make_output_section(const char* name, elfcpp::Elf_Word type, elfcpp::Elf_Xword flags) { - ++this->last_shndx_; - Output_section* os = new Output_section(name, type, flags, - this->last_shndx_); + Output_section* os = new Output_section(name, type, flags, true); if ((flags & elfcpp::SHF_ALLOC) == 0) this->section_list_.push_back(os); @@ -339,8 +385,14 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab) load_seg->add_initial_output_data(file_header); this->special_output_list_.push_back(file_header); - // Set the file offsets of all the segments. - off_t off = this->set_segment_offsets(input_objects->target(), load_seg); + // We set the output section indexes in set_segment_offsets and + // set_section_offsets. + unsigned int shndx = 1; + + // Set the file offsets of all the segments, and all the sections + // they contain. + off_t off = this->set_segment_offsets(input_objects->target(), load_seg, + &shndx); // Create the symbol table sections. // FIXME: We don't need to do this if we are stripping symbols. @@ -354,7 +406,10 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab) // Set the file offsets of all the sections not associated with // segments. - off = this->set_section_offsets(off); + off = this->set_section_offsets(off, &shndx); + + // Now the section index of OSTRTAB is set. + osymtab->set_link(ostrtab->out_shndx()); // Create the section table header. Output_section_headers* oshdrs = this->create_shdrs(size, big_endian, &off); @@ -450,12 +505,13 @@ Layout::segment_precedes(const Output_segment* seg1, return paddr1 < paddr2; } -// Set the file offsets of all the segments. They have all been -// created. LOAD_SEG must be be laid out first. Return the offset of -// the data to follow. +// Set the file offsets of all the segments, and all the sections they +// contain. They have all been created. LOAD_SEG must be be laid out +// first. Return the offset of the data to follow. off_t -Layout::set_segment_offsets(const Target* target, Output_segment* load_seg) +Layout::set_segment_offsets(const Target* target, Output_segment* load_seg, + unsigned int *pshndx) { // Sort them into the final order. std::sort(this->segment_list_.begin(), this->segment_list_.end(), @@ -489,16 +545,17 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg) uint64_t abi_pagesize = target->abi_pagesize(); if (was_readonly && ((*p)->flags() & elfcpp::PF_W) != 0) { - uint64_t align = (*p)->max_data_align(); + uint64_t align = (*p)->addralign(); - addr = (addr + align - 1) & ~ (align - 1); + addr = align_address(addr, align); aligned_addr = addr; if ((addr & (abi_pagesize - 1)) != 0) addr = addr + abi_pagesize; } + unsigned int shndx_hold = *pshndx; off = orig_off + ((addr - orig_addr) & (abi_pagesize - 1)); - uint64_t new_addr = (*p)->set_section_addresses(addr, &off); + uint64_t new_addr = (*p)->set_section_addresses(addr, &off, pshndx); // Now that we know the size of this segment, we may be able // to save a page in memory, at the cost of wasting some @@ -519,10 +576,10 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg) != (new_addr & ~ (common_pagesize - 1))) && first_off + last_off <= common_pagesize) { - addr = ((aligned_addr + common_pagesize - 1) - & ~ (common_pagesize - 1)); + *pshndx = shndx_hold; + addr = align_address(aligned_addr, common_pagesize); off = orig_off + ((addr - orig_addr) & (abi_pagesize - 1)); - new_addr = (*p)->set_section_addresses(addr, &off); + new_addr = (*p)->set_section_addresses(addr, &off, pshndx); } } @@ -550,17 +607,17 @@ Layout::set_segment_offsets(const Target* target, Output_segment* load_seg) // segment. off_t -Layout::set_section_offsets(off_t off) +Layout::set_section_offsets(off_t off, unsigned int* pshndx) { for (Layout::Section_list::iterator p = this->section_list_.begin(); p != this->section_list_.end(); ++p) { + (*p)->set_out_shndx(*pshndx); + ++*pshndx; if ((*p)->offset() != -1) continue; - uint64_t addralign = (*p)->addralign(); - if (addralign != 0) - off = (off + addralign - 1) & ~ (addralign - 1); + off = align_address(off, (*p)->addralign()); (*p)->set_address(0, off); off += (*p)->data_size(); } @@ -592,7 +649,7 @@ Layout::create_symtab_sections(int size, const Input_objects* input_objects, abort(); off_t off = *poff; - off = (off + align - 1) & ~ (align - 1); + off = align_address(off, align); off_t startoff = off; // Save space for the dummy symbol at the start of the section. We @@ -614,23 +671,18 @@ Layout::create_symtab_sections(int size, const Input_objects* input_objects, this->sympool_.set_string_offsets(); - ++this->last_shndx_; const char* symtab_name = this->namepool_.add(".symtab"); Output_section* osymtab = new Output_section_symtab(symtab_name, - off - startoff, - this->last_shndx_); + off - startoff); this->section_list_.push_back(osymtab); - ++this->last_shndx_; const char* strtab_name = this->namepool_.add(".strtab"); Output_section *ostrtab = new Output_section_strtab(strtab_name, - &this->sympool_, - this->last_shndx_); + &this->sympool_); this->section_list_.push_back(ostrtab); this->special_output_list_.push_back(ostrtab); osymtab->set_address(0, startoff); - osymtab->set_link(ostrtab->shndx()); osymtab->set_info(local_symcount); osymtab->set_entsize(symsize); osymtab->set_addralign(align); @@ -654,10 +706,7 @@ Layout::create_shstrtab() this->namepool_.set_string_offsets(); - ++this->last_shndx_; - Output_section* os = new Output_section_strtab(name, - &this->namepool_, - this->last_shndx_); + Output_section* os = new Output_section_strtab(name, &this->namepool_); this->section_list_.push_back(os); this->special_output_list_.push_back(os); @@ -675,8 +724,7 @@ Layout::create_shdrs(int size, bool big_endian, off_t* poff) oshdrs = new Output_section_headers(size, big_endian, this->segment_list_, this->section_list_, &this->namepool_); - uint64_t addralign = oshdrs->addralign(); - off_t off = (*poff + addralign - 1) & ~ (addralign - 1); + off_t off = align_address(*poff, oshdrs->addralign()); oshdrs->set_address(0, off); off += oshdrs->data_size(); *poff = off; @@ -686,7 +734,7 @@ Layout::create_shdrs(int size, bool big_endian, off_t* poff) // The mapping of .gnu.linkonce section names to real section names. -#define MAPPING_INIT(f, t) { f, sizeof(f) - 1, t } +#define MAPPING_INIT(f, t) { f, sizeof(f) - 1, t, sizeof(t) - 1 } const Layout::Linkonce_mapping Layout::linkonce_mapping[] = { MAPPING_INIT("d.rel.ro", ".data.rel.ro"), // Must be before "d". @@ -713,10 +761,11 @@ const int Layout::linkonce_mapping_count = // 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". +// to ".text". Set *PLEN to the length of the name. *PLEN is +// initialized to the length of NAME. const char* -Layout::linkonce_output_name(const char* name) +Layout::linkonce_output_name(const char* name, size_t *plen) { const char* s = name + sizeof(".gnu.linkonce") - 1; if (*s != '.') @@ -726,11 +775,67 @@ Layout::linkonce_output_name(const char* name) for (int i = 0; i < linkonce_mapping_count; ++i, ++plm) { if (strncmp(s, plm->from, plm->fromlen) == 0 && s[plm->fromlen] == '.') - return plm->to; + { + *plen = plm->tolen; + return plm->to; + } } return name; } +// Choose the output section name to use given an input section name. +// Set *PLEN to the length of the name. *PLEN is initialized to the +// length of NAME. + +const char* +Layout::output_section_name(const char* name, size_t* plen) +{ + if (Layout::is_linkonce(name)) + { + // .gnu.linkonce sections are laid out as though they were named + // for the sections are placed into. + return Layout::linkonce_output_name(name, plen); + } + + // If the section name has no '.', or only an initial '.', we use + // the name unchanged (i.e., ".text" is unchanged). + + // Otherwise, if the section name does not include ".rel", we drop + // the last '.' and everything that follows (i.e., ".text.XXX" + // becomes ".text"). + + // Otherwise, if the section name has zero or one '.' after the + // ".rel", we use the name unchanged (i.e., ".rel.text" is + // unchanged). + + // Otherwise, we drop the last '.' and everything that follows + // (i.e., ".rel.text.XXX" becomes ".rel.text"). + + const char* s = name; + if (*s == '.') + ++s; + const char* sdot = strchr(s, '.'); + if (sdot == NULL) + return name; + + const char* srel = strstr(s, ".rel"); + if (srel == NULL) + { + *plen = sdot - name; + return name; + } + + sdot = strchr(srel + 1, '.'); + if (sdot == NULL) + return name; + sdot = strchr(sdot + 1, '.'); + if (sdot == NULL) + return name; + + *plen = sdot - name; + 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 @@ -743,7 +848,7 @@ Layout::add_comdat(const char* signature, bool group) { std::string sig(signature); std::pair ins( - this->signatures_.insert(std::make_pair(signature, group))); + this->signatures_.insert(std::make_pair(sig, group))); if (ins.second) { @@ -851,22 +956,22 @@ Close_task_runner::run(Workqueue*) template Output_section* -Layout::layout<32, false>(Object* object, const char* name, +Layout::layout<32, false>(Object* object, unsigned int shndx, const char* name, const elfcpp::Shdr<32, false>& shdr, off_t*); template Output_section* -Layout::layout<32, true>(Object* object, const char* name, +Layout::layout<32, true>(Object* object, unsigned int shndx, const char* name, const elfcpp::Shdr<32, true>& shdr, off_t*); template Output_section* -Layout::layout<64, false>(Object* object, const char* name, +Layout::layout<64, false>(Object* object, unsigned int shndx, const char* name, const elfcpp::Shdr<64, false>& shdr, off_t*); template Output_section* -Layout::layout<64, true>(Object* object, const char* name, +Layout::layout<64, true>(Object* object, unsigned int shndx, const char* name, const elfcpp::Shdr<64, true>& shdr, off_t*); diff --git a/gold/layout.h b/gold/layout.h index 4ec2a4ad5aa..c96d47da10a 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -8,7 +8,6 @@ #include #include -#include "options.h" #include "workqueue.h" #include "object.h" #include "stringpool.h" @@ -16,8 +15,10 @@ namespace gold { +class General_options; class Input_objects; class Symbol_table; +class Output_section_data; class Output_section; class Output_section_symtab; class Output_section_headers; @@ -63,15 +64,22 @@ class Layout public: Layout(const General_options& options); - // 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. + // Given an input section SHNDX, 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 Output_section* - layout(Object *object, const char* name, + layout(Object *object, unsigned int shndx, const char* name, const elfcpp::Shdr& shdr, off_t* offset); + // Add an Output_section_data to the layout. This is used for + // special sections like the GOT section. + void + add_output_section_data(const char* name, elfcpp::Elf_Word type, + elfcpp::Elf_Xword flags, + Output_section_data*); + // Return the Stringpool used for symbol names. const Stringpool* sympool() const @@ -104,6 +112,16 @@ class Layout void write_data(Output_file*) const; + // Return an output section named NAME, or NULL if there is none. + Output_section* + find_output_section(const char* name) const; + + // Return an output segment of type TYPE, with segment flags SET set + // and segment flags CLEAR clear. Return NULL if there is none. + Output_segment* + find_output_segment(elfcpp::PT type, elfcpp::Elf_Word set, + elfcpp::Elf_Word clear) const; + // The list of segments. typedef std::vector Segment_list; @@ -126,6 +144,7 @@ class Layout const char* from; int fromlen; const char* to; + int tolen; }; static const Linkonce_mapping linkonce_mapping[]; static const int linkonce_mapping_count; @@ -137,12 +156,12 @@ class Layout // Set the final file offsets of all the segments. off_t - set_segment_offsets(const Target*, Output_segment*); + set_segment_offsets(const Target*, Output_segment*, unsigned int* pshndx); - // Set the final file offsets of all the sections not associated - // with a segment. + // Set the final file offsets and section indices of all the + // sections not associated with a segment. off_t - set_section_offsets(off_t); + set_section_offsets(off_t, unsigned int *pshndx); // Create the output sections for the symbol table. void @@ -164,10 +183,21 @@ class Layout include_section(Object* object, const char* name, const elfcpp::Shdr&); + // Return the output section name to use given an input section + // name. Set *PLEN to the length of the name. *PLEN must be + // initialized to the length of NAME. + static const char* + output_section_name(const char* name, size_t* plen); + // Return the output section name to use for a linkonce section - // name. + // name. PLEN is as for output_section_name. static const char* - linkonce_output_name(const char* name); + linkonce_output_name(const char* name, size_t* plen); + + // Return the output section for NAME, TYPE and FLAGS. + Output_section* + get_output_section(const char* name, elfcpp::Elf_Word type, + elfcpp::Elf_Xword flags); // Create a new Output_section. Output_section* @@ -210,8 +240,6 @@ class Layout // A reference to the options on the command line. const General_options& options_; - // The index of the last output section. - unsigned int last_shndx_; // The output section names. Stringpool namepool_; // The output symbol names. @@ -308,6 +336,16 @@ class Close_task_runner : public Task_function_runner Output_file* of_; }; +// A small helper function to align an address. + +inline uint64_t +align_address(uint64_t address, uint64_t addralign) +{ + if (addralign != 0) + address = (address + addralign - 1) &~ (addralign - 1); + return address; +} + } // End namespace gold. #endif // !defined(GOLD_LAYOUT_H) diff --git a/gold/object.cc b/gold/object.cc index 5efb3cbd7fd..4e7f04c1ddd 100644 --- a/gold/object.cc +++ b/gold/object.cc @@ -16,25 +16,6 @@ namespace gold // Class Object. -const unsigned char* -Object::get_view(off_t start, off_t size) -{ - return this->input_file_->file().get_view(start + this->offset_, size); -} - -void -Object::read(off_t start, off_t size, void* p) -{ - this->input_file_->file().read(start + this->offset_, size, p); -} - -File_view* -Object::get_lasting_view(off_t start, off_t size) -{ - return this->input_file_->file().get_lasting_view(start + this->offset_, - size); -} - // Class Sized_object. template @@ -79,7 +60,7 @@ Sized_object::~Sized_object() // Read the section header for section SHNUM. template -const unsigned char* +inline const unsigned char* Sized_object::section_header(unsigned int shnum) { assert(shnum < this->shnum()); @@ -328,6 +309,32 @@ Sized_object::include_section_group( const char* signature = psymnames + sym.get_st_name(); + // It seems that some versions of gas will create a section group + // associated with a section symbol, and then fail to give a name to + // the section symbol. In such a case, use the name of the section. + // FIXME. + if (signature[0] == '\0' + && sym.get_st_type() == elfcpp::STT_SECTION + && sym.get_st_shndx() < this->shnum()) + { + typename This::Shdr shdrnames(this->section_header(this->shstrndx_)); + const unsigned char* pnamesu = this->get_view(shdrnames.get_sh_offset(), + shdrnames.get_sh_size()); + const char* pnames = reinterpret_cast(pnamesu); + + typename This::Shdr sechdr(this->section_header(sym.get_st_shndx())); + if (sechdr.get_sh_name() >= shdrnames.get_sh_size()) + { + fprintf(stderr, + _("%s: %s: bad section name offset for section %u: %lu\n"), + program_name, this->name().c_str(), sym.get_st_shndx(), + static_cast(sechdr.get_sh_name())); + gold_exit(false); + } + + signature = pnames + sechdr.get_sh_name(); + } + // Record this section group, and see whether we've already seen one // with the same signature. if (layout->add_comdat(signature, true)) @@ -446,7 +453,7 @@ Sized_object::do_layout(Layout* layout, } off_t offset; - Output_section* os = layout->layout(this, name, shdr, &offset); + Output_section* os = layout->layout(this, i, name, shdr, &offset); map_sections[i].output_section = os; map_sections[i].offset = offset; @@ -514,7 +521,7 @@ Sized_object::do_finalize_local_symbols(off_t off, return off; } - off = (off + (size >> 3) - 1) & ~ ((off_t) (size >> 3) - 1); + off = align_address(off, size >> 3); this->local_symbol_offset_ = off; @@ -587,6 +594,7 @@ Sized_object::do_finalize_local_symbols(off_t off, } this->values_[i] = (mo[shndx].output_section->address() + + mo[shndx].offset + sym.get_st_value()); } @@ -655,7 +663,7 @@ Sized_object::write_local_symbols(Output_file* of, assert(st_shndx < mo.size()); if (mo[st_shndx].output_section == NULL) continue; - st_shndx = mo[st_shndx].output_section->shndx(); + st_shndx = mo[st_shndx].output_section->out_shndx(); } osym.put_st_name(sympool->get_offset(pnames + isym.get_st_name())); diff --git a/gold/object.h b/gold/object.h index 49a1ce9b86b..5e570399b18 100644 --- a/gold/object.h +++ b/gold/object.h @@ -154,8 +154,8 @@ class Object // Scan the relocs and adjust the symbol table. void scan_relocs(const General_options& options, Symbol_table* symtab, - Read_relocs_data* rd) - { return this->do_scan_relocs(options, symtab, rd); } + Layout* layout, Read_relocs_data* rd) + { return this->do_scan_relocs(options, symtab, layout, rd); } // Initial local symbol processing: set the offset where local // symbol information will be stored; add local symbol names to @@ -184,6 +184,14 @@ class Object inline Output_section* output_section(unsigned int shnum, off_t* poff); + // Set the offset of an input section within its output section. + void + set_section_offset(unsigned int shndx, off_t off) + { + assert(shndx < this->map_to_output_.size()); + this->map_to_output_[shndx].offset = off; + } + // Return the name of a section given a section index. This is only // used for error messages. std::string @@ -218,7 +226,8 @@ class Object // Scan the relocs--implemented by child class. virtual void - do_scan_relocs(const General_options&, Symbol_table*, Read_relocs_data*) = 0; + do_scan_relocs(const General_options&, Symbol_table*, Layout*, + Read_relocs_data*) = 0; // Lay out sections--implemented by child class. virtual void @@ -250,7 +259,8 @@ class Object // Get a view into the underlying file. const unsigned char* - get_view(off_t start, off_t size); + get_view(off_t start, off_t size) + { return this->input_file_->file().get_view(start + this->offset_, size); } // Get the number of sections. unsigned int @@ -269,11 +279,16 @@ class Object // Read data from the underlying file. void - read(off_t start, off_t size, void* p); + read(off_t start, off_t size, void* p) + { this->input_file_->file().read(start + this->offset_, size, p); } // Get a lasting view into the underlying file. File_view* - get_lasting_view(off_t start, off_t size); + get_lasting_view(off_t start, off_t size) + { + return this->input_file_->file().get_lasting_view(start + this->offset_, + size); + } // Return the vector mapping input sections to output sections. std::vector& @@ -353,7 +368,8 @@ class Sized_object : public Object // Scan the relocs and adjust the symbol table. void - do_scan_relocs(const General_options&, Symbol_table*, Read_relocs_data*); + do_scan_relocs(const General_options&, Symbol_table*, Layout*, + Read_relocs_data*); // Lay out the input sections. void diff --git a/gold/options.cc b/gold/options.cc index 8e0465f2af9..397259e25c7 100644 --- a/gold/options.cc +++ b/gold/options.cc @@ -89,6 +89,24 @@ library(int argc, char** argv, char* arg, gold::Command_line* cmdline) return cmdline->process_l_option(argc, argv, arg); } +// Handle the special --start-group option. + +int +start_group(int, char**, char* arg, gold::Command_line* cmdline) +{ + cmdline->start_group(arg); + return 1; +} + +// Handle the special --end-group option. + +int +end_group(int, char**, char* arg, gold::Command_line* cmdline) +{ + cmdline->end_group(arg); + return 1; +} + // Report usage information for ld --help, and exit. int @@ -209,6 +227,10 @@ options::Command_line_options::options[] = SPECIAL('l', "library", N_("Search for library LIBNAME"), N_("-lLIBNAME --library LIBNAME"), TWO_DASHES, &library), + SPECIAL('(', "start-group", N_("Start a library search group"), NULL, + TWO_DASHES, &start_group), + SPECIAL(')', "end-group", N_("End a library search group"), NULL, + TWO_DASHES, &end_group), GENERAL_ARG('L', "library-path", N_("Add directory to search path"), N_("-L DIR, --library-path DIR"), TWO_DASHES, &General_options::add_to_search_path), @@ -246,9 +268,10 @@ Position_dependent_options::Position_dependent_options() { } -// Construct a Command_line. +// Command_line options. Command_line::Command_line() + : options_(), position_options_(), inputs_(), in_group_(false) { } @@ -266,8 +289,7 @@ Command_line::process(int argc, char** argv) { if (argv[i][0] != '-' || no_more_options) { - this->inputs_.push_back(Input_argument(argv[i], false, - this->position_options_)); + this->add_file(argv[i], false); ++i; continue; } @@ -385,6 +407,12 @@ Command_line::process(int argc, char** argv) } } + if (this->in_group_) + { + fprintf(stderr, _("%s: missing group end"), program_name); + this->usage(); + } + // FIXME: We should only do this when configured in native mode. this->options_.add_to_search_path("/lib"); this->options_.add_to_search_path("/usr/lib"); @@ -416,6 +444,22 @@ Command_line::apply_option(const options::One_option& opt, } } +// Add an input file or library. + +void +Command_line::add_file(const char* name, bool is_lib) +{ + Input_file_argument file(name, is_lib, this->position_options_); + if (!this->in_group_) + this->inputs_.push_back(Input_argument(file)); + else + { + assert(!this->inputs_.empty()); + assert(this->inputs_.back().is_group()); + this->inputs_.back().group()->add_file(file); + } +} + // Handle the -l option, which requires special treatment. int @@ -436,12 +480,36 @@ Command_line::process_l_option(int argc, char** argv, char* arg) else this->usage(_("missing argument"), arg); - this->inputs_.push_back(Input_argument(libname, true, - this->position_options_)); + this->add_file(libname, true); return ret; } +// Handle the --start-group option. + +void +Command_line::start_group(const char* arg) +{ + if (this->in_group_) + this->usage(_("may not nest groups"), arg); + + // This object is leaked. + Input_file_group* group = new Input_file_group(); + this->inputs_.push_back(Input_argument(group)); + + this->in_group_ = true; +} + +// Handle the --end-group option. + +void +Command_line::end_group(const char* arg) +{ + if (!this->in_group_) + this->usage(_("group end without group start"), arg); + this->in_group_ = false; +} + // Report a usage error. */ void diff --git a/gold/options.h b/gold/options.h index 7e890faa43d..3b54c497d14 100644 --- a/gold/options.h +++ b/gold/options.h @@ -15,11 +15,13 @@ #include #include #include +#include namespace gold { class Command_line; +class Input_file_group; namespace options { @@ -128,11 +130,15 @@ class Position_dependent_options // A single file or library argument from the command line. -class Input_argument +class Input_file_argument { public: - Input_argument(const char* name, bool is_lib, - const Position_dependent_options& options) + Input_file_argument() + : name_(NULL), is_lib_(false), options_() + { } + + Input_file_argument(const char* name, bool is_lib, + const Position_dependent_options& options) : name_(name), is_lib_(is_lib), options_(options) { } @@ -154,9 +160,90 @@ class Input_argument Position_dependent_options options_; }; -// A list of input files. -class Input_argument_list : public std::vector +// A file or library, or a group, from the command line. + +class Input_argument +{ + public: + // Create a file or library argument. + explicit Input_argument(Input_file_argument file) + : is_file_(true), file_(file), group_(NULL) + { } + + // Create a group argument. + explicit Input_argument(Input_file_group* group) + : is_file_(false), group_(group) + { } + + // Return whether this is a file. + bool + is_file() const + { return this->is_file_; } + + // Return whether this is a group. + bool + is_group() const + { return !this->is_file_; } + + // Return the information about the file. + const Input_file_argument& + file() const + { + assert(this->is_file_); + return this->file_; + } + + // Return the information about the group. + const Input_file_group* + group() const + { + assert(!this->is_file_); + return this->group_; + } + + Input_file_group* + group() + { + assert(!this->is_file_); + return this->group_; + } + + private: + bool is_file_; + Input_file_argument file_; + Input_file_group* group_; +}; + +// A group from the command line. This is a set of arguments within +// --start-group ... --end-group. + +class Input_file_group { + public: + typedef std::vector Files; + typedef Files::const_iterator const_iterator; + + Input_file_group() + : files_() + { } + + // Add a file to the end of the group. + void + add_file(const Input_file_argument& arg) + { this->files_.push_back(Input_argument(arg)); } + + // Iterators to iterate over the group contents. + + const_iterator + begin() const + { return this->files_.begin(); } + + const_iterator + end() const + { return this->files_.end(); } + + private: + Files files_; }; // All the information read from the command line. @@ -164,6 +251,9 @@ class Input_argument_list : public std::vector class Command_line { public: + typedef std::vector Input_arguments; + typedef Input_arguments::const_iterator const_iterator; + Command_line(); // Process the command line options. This will exit with an @@ -175,25 +265,53 @@ class Command_line int process_l_option(int, char**, char*); + // Handle a --start-group option. + void + start_group(const char* arg); + + // Handle a --end-group option. + void + end_group(const char* arg); + // Get the general options. const General_options& options() const { return this->options_; } - // Get the list of input files. - const Input_argument_list& - inputs() const - { return this->inputs_; } + // Iterators to iterate over the list of input files. + + const_iterator + begin() const + { return this->inputs_.begin(); } + + const_iterator + end() const + { return this->inputs_.end(); } private: - void usage() ATTRIBUTE_NORETURN; - void usage(const char* msg, const char* opt) ATTRIBUTE_NORETURN; - void usage(const char* msg, char opt) ATTRIBUTE_NORETURN; - void apply_option(const gold::options::One_option&, const char*); + Command_line(const Command_line&); + Command_line& operator=(const Command_line&); + + // Report usage error. + void + usage() ATTRIBUTE_NORETURN; + void + usage(const char* msg, const char* opt) ATTRIBUTE_NORETURN; + void + usage(const char* msg, char opt) ATTRIBUTE_NORETURN; + + // Apply a command line option. + void + apply_option(const gold::options::One_option&, const char*); + + // Add a file. + void + add_file(const char* name, bool is_lib); General_options options_; Position_dependent_options position_options_; - Input_argument_list inputs_; + Input_arguments inputs_; + bool in_group_; }; } // End namespace gold. diff --git a/gold/output.cc b/gold/output.cc index 82e2ca5d421..703a560a024 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -10,6 +10,8 @@ #include #include "object.h" +#include "symtab.h" +#include "reloc.h" #include "output.h" namespace gold @@ -74,7 +76,8 @@ Output_section_headers::Output_section_headers( for (Layout::Segment_list::const_iterator p = segment_list.begin(); p != segment_list.end(); ++p) - count += (*p)->output_section_count(); + if ((*p)->type() == elfcpp::PT_LOAD) + count += (*p)->output_section_count(); count += section_list.size(); int shdr_size; @@ -137,18 +140,22 @@ Output_section_headers::do_sized_write(Output_file* of) v += shdr_size; + unsigned shndx = 1; for (Layout::Segment_list::const_iterator p = this->segment_list_.begin(); p != this->segment_list_.end(); ++p) v = (*p)->write_section_headers SELECT_SIZE_ENDIAN_NAME ( - this->secnamepool_, v SELECT_SIZE_ENDIAN(size, big_endian)); + this->secnamepool_, v, &shndx + SELECT_SIZE_ENDIAN(size, big_endian)); for (Layout::Section_list::const_iterator p = this->section_list_.begin(); p != this->section_list_.end(); ++p) { + assert(shndx == (*p)->out_shndx()); elfcpp::Shdr_write oshdr(v); (*p)->write_header(this->secnamepool_, &oshdr); v += shdr_size; + ++shndx; } of->write_output_view(this->offset(), all_shdrs_size, view); @@ -318,6 +325,7 @@ Output_file_header::do_sized_write(Output_file* of) oehdr.put_e_machine(this->target_->machine_code()); oehdr.put_e_version(elfcpp::EV_CURRENT); + // FIXME: Need to support -e, and target specific entry symbol. Symbol* sym = this->symtab_->lookup("_start"); typename Sized_symbol::Value_type v; if (sym == NULL) @@ -344,17 +352,137 @@ Output_file_header::do_sized_write(Output_file* of) oehdr.put_e_shentsize(elfcpp::Elf_sizes::shdr_size); oehdr.put_e_shnum(this->section_header_->data_size() / elfcpp::Elf_sizes::shdr_size); - oehdr.put_e_shstrndx(this->shstrtab_->shndx()); + oehdr.put_e_shstrndx(this->shstrtab_->out_shndx()); of->write_output_view(0, ehdr_size, view); } +// Output_section_got::Got_entry methods. + +// Write out the entry. + +template +void +Output_section_got::Got_entry::write(unsigned char* pov) + const +{ + Valtype val = 0; + + switch (this->local_sym_index_) + { + case GSYM_CODE: + { + Symbol* gsym = this->u_.gsym; + + // If the symbol is resolved locally, we need to write out its + // value. Otherwise we just write zero. The target code is + // responsible for creating a relocation entry to fill in the + // value at runtime. + if (gsym->is_resolved_locally()) + { + Sized_symbol* sgsym; + // This cast is a bit ugly. We don't want to put a + // virtual method in Symbol, because we want Symbol to be + // as small as possible. + sgsym = static_cast*>(gsym); + val = sgsym->value(); + } + } + break; + + case CONSTANT_CODE: + val = this->u_.constant; + break; + + default: + abort(); + } + + Valtype* povv = reinterpret_cast(pov); + Swap::writeval(povv, val); +} + +// Output_section_data methods. + +unsigned int +Output_section_data::do_out_shndx() const +{ + assert(this->output_section_ != NULL); + return this->output_section_->out_shndx(); +} + +// Output_section_got methods. + +// Write out the GOT. + +template +void +Output_section_got::do_write(Output_file* of) +{ + const int add = size / 8; + + const off_t off = this->offset(); + const off_t oview_size = this->entries_.size() * add; + unsigned char* const oview = of->get_output_view(off, oview_size); + + unsigned char* pov = oview; + for (typename Got_entries::const_iterator p = this->entries_.begin(); + p != this->entries_.end(); + ++p) + { + p->write(pov); + pov += add; + } + + of->write_output_view(off, oview_size, oview); + + // We no longer need the GOT entries. + this->entries_.clear(); +} + +// Output_section::Input_section methods. + +// Return the data size. For an input section we store the size here. +// For an Output_section_data, we have to ask it for the size. + +off_t +Output_section::Input_section::data_size() const +{ + if (this->is_input_section()) + return this->data_size_; + else + return this->u_.posd->data_size(); +} + +// Set the address and file offset. + +void +Output_section::Input_section::set_address(uint64_t addr, off_t off, + off_t secoff) +{ + if (this->is_input_section()) + this->u_.object->set_section_offset(this->shndx_, off - secoff); + else + this->u_.posd->set_address(addr, off); +} + +// Write out the data. We don't have to do anything for an input +// section--they are handled via Object::relocate--but this is where +// we write out the data for an Output_section_data. + +void +Output_section::Input_section::write(Output_file* of) +{ + if (!this->is_input_section()) + this->u_.posd->write(of); +} + // 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, unsigned int shndx) + elfcpp::Elf_Xword flags, bool may_add_data) : name_(name), addralign_(0), entsize_(0), @@ -362,7 +490,10 @@ Output_section::Output_section(const char* name, elfcpp::Elf_Word type, info_(0), type_(type), flags_(flags), - shndx_(shndx) + out_shndx_(0), + input_sections_(), + first_input_offset_(0), + may_add_data_(may_add_data) { } @@ -370,15 +501,20 @@ Output_section::~Output_section() { } -// Add an input section to an Output_section. We don't keep track of +// Add the input section SHNDX, with header SHDR, named SECNAME, in +// OBJECT, to the Output_section. Return the offset of the input +// section within the output section. We don't always 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 off_t -Output_section::add_input_section(Object* object, const char* secname, +Output_section::add_input_section(Object* object, unsigned int shndx, + const char* secname, const elfcpp::Shdr& shdr) { + assert(this->may_add_data_); + elfcpp::Elf_Xword addralign = shdr.get_sh_addralign(); if ((addralign & (addralign - 1)) != 0) { @@ -392,19 +528,56 @@ Output_section::add_input_section(Object* object, const char* secname, this->addralign_ = addralign; off_t ssize = this->data_size(); - ssize = (ssize + addralign - 1) &~ (addralign - 1); + ssize = align_address(ssize, addralign); + this->set_data_size(ssize + shdr.get_sh_size()); - // SHF_TLS/SHT_NOBITS sections are handled specially: they are - // treated as having no size and taking up no space. We only use - // the real size when setting the pt_memsz field of the PT_TLS - // segment. - if ((this->flags_ & elfcpp::SHF_TLS) == 0 - || this->type_ != elfcpp::SHT_NOBITS) - this->set_data_size(ssize + shdr.get_sh_size()); + // We need to keep track of this section if we are already keeping + // track of sections, or if we are relaxing. FIXME: Add test for + // relaxing. + if (! this->input_sections_.empty()) + this->input_sections_.push_back(Input_section(object, shndx, + shdr.get_sh_size(), + addralign)); return ssize; } +// Add arbitrary data to an output section. + +void +Output_section::add_output_section_data(Output_section_data* posd) +{ + if (this->input_sections_.empty()) + this->first_input_offset_ = this->data_size(); + this->input_sections_.push_back(Input_section(posd)); + uint64_t addralign = posd->addralign(); + if (addralign > this->addralign_) + this->addralign_ = addralign; + posd->set_output_section(this); +} + +// Set the address of an Output_section. This is where we handle +// setting the addresses of any Output_section_data objects. + +void +Output_section::do_set_address(uint64_t address, off_t startoff) +{ + if (this->input_sections_.empty()) + return; + + off_t off = startoff + this->first_input_offset_; + for (Input_section_list::iterator p = this->input_sections_.begin(); + p != this->input_sections_.end(); + ++p) + { + off = align_address(off, p->addralign()); + p->set_address(address + (off - startoff), off, startoff); + off += p->data_size(); + } + + this->set_data_size(off - startoff); +} + // Write the section header to *OSHDR. template @@ -424,11 +597,23 @@ Output_section::write_header(const Stringpool* secnamepool, oshdr->put_sh_entsize(this->entsize_); } +// Write out the data. For input sections the data is written out by +// Object::relocate, but we have to handle Output_section_data objects +// here. + +void +Output_section::do_write(Output_file* of) +{ + for (Input_section_list::iterator p = this->input_sections_.begin(); + p != this->input_sections_.end(); + ++p) + p->write(of); +} + // Output_section_symtab methods. -Output_section_symtab::Output_section_symtab(const char* name, off_t size, - unsigned int shndx) - : Output_section(name, elfcpp::SHT_SYMTAB, 0, shndx) +Output_section_symtab::Output_section_symtab(const char* name, off_t size) + : Output_section(name, elfcpp::SHT_SYMTAB, 0, false) { this->set_data_size(size); } @@ -436,9 +621,8 @@ Output_section_symtab::Output_section_symtab(const char* name, off_t size, // Output_section_strtab methods. Output_section_strtab::Output_section_strtab(const char* name, - Stringpool* contents, - unsigned int shndx) - : Output_section(name, elfcpp::SHT_STRTAB, 0, shndx), + Stringpool* contents) + : Output_section(name, elfcpp::SHT_STRTAB, 0, false), contents_(contents) { this->set_data_size(contents->get_strtab_size()); @@ -462,7 +646,8 @@ Output_segment::Output_segment(elfcpp::Elf_Word type, elfcpp::Elf_Word flags) offset_(0), filesz_(0), type_(type), - flags_(flags) + flags_(flags), + is_align_known_(false) { } @@ -473,12 +658,10 @@ Output_segment::add_output_section(Output_section* os, elfcpp::Elf_Word seg_flags) { assert((os->flags() & elfcpp::SHF_ALLOC) != 0); + assert(!this->is_align_known_); - // Update the segment flags and alignment. + // Update the segment flags. this->flags_ |= seg_flags; - uint64_t addralign = os->addralign(); - if (addralign > this->align_) - this->align_ = addralign; Output_segment::Output_data_list* pdl; if (os->type() == elfcpp::SHT_NOBITS) @@ -524,12 +707,28 @@ Output_segment::add_output_section(Output_section* os, { pdl = &this->output_data_; bool nobits = os->type() == elfcpp::SHT_NOBITS; + bool sawtls = false; Layout::Data_list::iterator p = pdl->end(); do { --p; - if ((*p)->is_section_flag_set(elfcpp::SHF_TLS) - && (nobits || !(*p)->is_section_type(elfcpp::SHT_NOBITS))) + bool insert; + if ((*p)->is_section_flag_set(elfcpp::SHF_TLS)) + { + sawtls = true; + // Put a NOBITS section after the first TLS section. + // But a PROGBITS section after the first TLS/PROGBITS + // section. + insert = nobits || !(*p)->is_section_type(elfcpp::SHT_NOBITS); + } + else + { + // If we've gone past the TLS sections, but we've seen a + // TLS section, then we need to insert this section now. + insert = sawtls; + } + + if (insert) { ++p; pdl->insert(p, os); @@ -537,6 +736,9 @@ Output_segment::add_output_section(Output_section* os, } } while (p != pdl->begin()); + + // There are no TLS sections yet; put this one at the end of the + // section list. } pdl->push_back(os); @@ -548,28 +750,59 @@ Output_segment::add_output_section(Output_section* os, void Output_segment::add_initial_output_data(Output_data* od) { - uint64_t addralign = od->addralign(); - if (addralign > this->align_) - this->align_ = addralign; - + assert(!this->is_align_known_); this->output_data_.push_front(od); } // Return the maximum alignment of the Output_data in Output_segment. -// We keep this up to date as we add Output_sections and Output_data. +// Once we compute this, we prohibit new sections from being added. uint64_t -Output_segment::max_data_align() const +Output_segment::addralign() { + if (!this->is_align_known_) + { + uint64_t addralign; + + addralign = Output_segment::maximum_alignment(&this->output_data_); + if (addralign > this->align_) + this->align_ = addralign; + + addralign = Output_segment::maximum_alignment(&this->output_bss_); + if (addralign > this->align_) + this->align_ = addralign; + + this->is_align_known_ = true; + } + return this->align_; } +// Return the maximum alignment of a list of Output_data. + +uint64_t +Output_segment::maximum_alignment(const Output_data_list* pdl) +{ + uint64_t ret = 0; + for (Output_data_list::const_iterator p = pdl->begin(); + p != pdl->end(); + ++p) + { + uint64_t addralign = (*p)->addralign(); + if (addralign > ret) + ret = addralign; + } + return ret; +} + // Set the section addresses for an Output_segment. ADDR is the -// address and *POFF is the file offset. Return the address of the -// immediately following segment. Update *POFF. +// address and *POFF is the file offset. Set the section indexes +// starting with *PSHNDX. Return the address of the immediately +// following segment. Update *POFF and *PSHNDX. uint64_t -Output_segment::set_section_addresses(uint64_t addr, off_t* poff) +Output_segment::set_section_addresses(uint64_t addr, off_t* poff, + unsigned int* pshndx) { assert(this->type_ == elfcpp::PT_LOAD); @@ -579,13 +812,16 @@ Output_segment::set_section_addresses(uint64_t addr, off_t* poff) off_t orig_off = *poff; this->offset_ = orig_off; - addr = this->set_section_list_addresses(&this->output_data_, addr, poff); + *poff = align_address(*poff, this->addralign()); + + addr = this->set_section_list_addresses(&this->output_data_, addr, poff, + pshndx); this->filesz_ = *poff - orig_off; off_t off = *poff; uint64_t ret = this->set_section_list_addresses(&this->output_bss_, addr, - poff); + poff, pshndx); this->memsz_ = *poff - orig_off; // Ignore the file offset adjustments made by the BSS Output_data @@ -599,26 +835,36 @@ Output_segment::set_section_addresses(uint64_t addr, off_t* poff) uint64_t Output_segment::set_section_list_addresses(Output_data_list* pdl, - uint64_t addr, off_t* poff) + uint64_t addr, off_t* poff, + unsigned int* pshndx) { - off_t off = *poff; + off_t startoff = *poff; + off_t off = startoff; for (Output_data_list::iterator p = pdl->begin(); p != pdl->end(); ++p) { - uint64_t addralign = (*p)->addralign(); - addr = (addr + addralign - 1) & ~ (addralign - 1); - off = (off + addralign - 1) & ~ (addralign - 1); - (*p)->set_address(addr, off); + off = align_address(off, (*p)->addralign()); + (*p)->set_address(addr + (off - startoff), off); + + // Unless this is a PT_TLS segment, we want to ignore the size + // of a SHF_TLS/SHT_NOBITS section. Such a section does not + // affect the size of a PT_LOAD segment. + if (this->type_ == elfcpp::PT_TLS + || !(*p)->is_section_flag_set(elfcpp::SHF_TLS) + || !(*p)->is_section_type(elfcpp::SHT_NOBITS)) + off += (*p)->data_size(); - uint64_t size = (*p)->data_size(); - addr += size; - off += size; + if ((*p)->is_section()) + { + (*p)->set_out_shndx(*pshndx); + ++*pshndx; + } } *poff = off; - return addr; + return addr + (off - startoff); } // For a non-PT_LOAD segment, set the offset from the sections, if @@ -667,8 +913,6 @@ Output_segment::set_offset() this->memsz_ = (last->address() + last->data_size() - this->vaddr_); - - // this->align_ was set as we added items. } // Return the number of Output_sections in an Output_segment. @@ -700,7 +944,7 @@ Output_segment::output_section_count_list(const Output_data_list* pdl) const template void -Output_segment::write_header(elfcpp::Phdr_write* ophdr) const +Output_segment::write_header(elfcpp::Phdr_write* ophdr) { ophdr->put_p_type(this->type_); ophdr->put_p_offset(this->offset_); @@ -709,7 +953,7 @@ Output_segment::write_header(elfcpp::Phdr_write* ophdr) const ophdr->put_p_filesz(this->filesz_); ophdr->put_p_memsz(this->memsz_); ophdr->put_p_flags(this->flags_); - ophdr->put_p_align(this->align_); + ophdr->put_p_align(this->addralign()); } // Write the section headers into V. @@ -717,13 +961,22 @@ Output_segment::write_header(elfcpp::Phdr_write* ophdr) const template unsigned char* Output_segment::write_section_headers(const Stringpool* secnamepool, - unsigned char* v + unsigned char* v, + unsigned int *pshndx ACCEPT_SIZE_ENDIAN) const { + // Every section that is attached to a segment must be attached to a + // PT_LOAD segment, so we only write out section headers for PT_LOAD + // segments. + if (this->type_ != elfcpp::PT_LOAD) + return v; + v = this->write_section_headers_list SELECT_SIZE_ENDIAN_NAME ( - secnamepool, &this->output_data_, v SELECT_SIZE_ENDIAN(size, big_endian)); + secnamepool, &this->output_data_, v, pshndx + SELECT_SIZE_ENDIAN(size, big_endian)); v = this->write_section_headers_list SELECT_SIZE_ENDIAN_NAME ( - secnamepool, &this->output_bss_, v SELECT_SIZE_ENDIAN(size, big_endian)); + secnamepool, &this->output_bss_, v, pshndx + SELECT_SIZE_ENDIAN(size, big_endian)); return v; } @@ -731,7 +984,8 @@ template unsigned char* Output_segment::write_section_headers_list(const Stringpool* secnamepool, const Output_data_list* pdl, - unsigned char* v + unsigned char* v, + unsigned int* pshndx ACCEPT_SIZE_ENDIAN) const { const int shdr_size = elfcpp::Elf_sizes::shdr_size; @@ -742,9 +996,11 @@ Output_segment::write_section_headers_list(const Stringpool* secnamepool, if ((*p)->is_section()) { const Output_section* ps = static_cast(*p); + assert(*pshndx == ps->out_shndx()); elfcpp::Shdr_write oshdr(v); ps->write_header(secnamepool, &oshdr); v += shdr_size; + ++*pshndx; } } return v; @@ -834,6 +1090,7 @@ template off_t Output_section::add_input_section<32, false>( Object* object, + unsigned int shndx, const char* secname, const elfcpp::Shdr<32, false>& shdr); @@ -841,6 +1098,7 @@ template off_t Output_section::add_input_section<32, true>( Object* object, + unsigned int shndx, const char* secname, const elfcpp::Shdr<32, true>& shdr); @@ -848,6 +1106,7 @@ template off_t Output_section::add_input_section<64, false>( Object* object, + unsigned int shndx, const char* secname, const elfcpp::Shdr<64, false>& shdr); @@ -855,7 +1114,24 @@ template off_t Output_section::add_input_section<64, true>( Object* object, + unsigned int shndx, const char* secname, const elfcpp::Shdr<64, true>& shdr); +template +void +Output_section_got<32, false>::do_write(Output_file* of); + +template +void +Output_section_got<32, true>::do_write(Output_file* of); + +template +void +Output_section_got<64, false>::do_write(Output_file* of); + +template +void +Output_section_got<64, true>::do_write(Output_file* of); + } // End namespace gold. diff --git a/gold/output.h b/gold/output.h index e036b98bc1e..5c72c02608d 100644 --- a/gold/output.h +++ b/gold/output.h @@ -5,6 +5,7 @@ #include #include +#include #include "elfcpp.h" #include "layout.h" @@ -31,17 +32,21 @@ class Output_data virtual ~Output_data(); - // Return the address. + // Return the address. This is only valid after Layout::finalize is + // finished. uint64_t address() const { return this->address_; } - // Return the size of the data. + // Return the size of the data. This must be valid after + // Layout::finalize calls set_address, but need not be valid before + // then. off_t data_size() const { return this->data_size_; } - // Return the file offset. + // Return the file offset. This is only valid after + // Layout::finalize is finished. off_t offset() const { return this->offset_; } @@ -67,11 +72,23 @@ class Output_data is_section_flag_set(elfcpp::Elf_Xword shf) const { return this->do_is_section_flag_set(shf); } - // Set the address and file offset of this data. + // Return the output section index, if there is an output section. + unsigned int + out_shndx() const + { return this->do_out_shndx(); } + + // Set the output section index, if this is an output section. + void + set_out_shndx(unsigned int shndx) + { this->do_set_out_shndx(shndx); } + + // Set the address and file offset of this data. This is called + // during Layout::finalize. void set_address(uint64_t addr, off_t off); - // Write the data to the output file. + // Write the data to the output file. This is called after + // Layout::finalize is complete. void write(Output_file* file) { this->do_write(file); } @@ -104,6 +121,16 @@ class Output_data do_is_section_flag_set(elfcpp::Elf_Xword) const { return false; } + // Return the output section index, if there is an output section. + virtual unsigned int + do_out_shndx() const + { abort(); } + + // Set the output section index, if this is an output section. + virtual void + do_set_out_shndx(unsigned int) + { abort(); } + // Set the address and file offset of the data. This only needs to // be implemented if the child needs to know. virtual void @@ -270,6 +297,198 @@ class Output_file_header : public Output_data const Output_section* shstrtab_; }; +// Output sections are mainly comprised of input sections. However, +// there are cases where we have data to write out which is not in an +// input section. Output_section_data is used in such cases. This is +// an abstract base class. + +class Output_section_data : public Output_data +{ + public: + Output_section_data(off_t data_size, uint64_t addralign) + : Output_data(data_size), output_section_(NULL), addralign_(addralign) + { } + + Output_section_data(uint64_t addralign) + : Output_data(0), output_section_(NULL), addralign_(addralign) + { } + + // Record the output section. + void + set_output_section(Output_section* os) + { + assert(this->output_section_ == NULL); + this->output_section_ = os; + } + + protected: + // The child class must implement do_write. + + // Return the required alignment. + uint64_t + do_addralign() const + { return this->addralign_; } + + // Return the section index of the output section. + unsigned int + do_out_shndx() const; + + private: + // The output section for this section. + const Output_section* output_section_; + // The required alignment. + uint64_t addralign_; +}; + +// Output_section_common is used to handle the common symbols. This +// is quite simple. + +class Output_section_common : public Output_section_data +{ + public: + Output_section_common(uint64_t addralign) + : Output_section_data(addralign) + { } + + // Set the size. + void + set_common_size(off_t common_size) + { this->set_data_size(common_size); } + + // Write out the data--there is nothing to do, as common symbols are + // always zero and are stored in the BSS. + void + do_write(Output_file*) + { } +}; + +// Output_section_got is used to manage a GOT. Each entry in the GOT +// is for one symbol--either a global symbol or a local symbol in an +// object. The target specific code adds entries to the GOT as +// needed. The GOT code is then responsible for writing out the data +// and for generating relocs as required. + +template +class Output_section_got : public Output_section_data +{ + public: + typedef typename elfcpp::Elf_types::Elf_Addr Valtype; + + Output_section_got() + : Output_section_data(Output_data::default_alignment(size)), + entries_() + { } + + // Add an entry for a global symbol to the GOT. This returns the + // offset of the new entry from the start of the GOT. + unsigned int + add_global(Symbol* gsym) + { + this->entries_.push_back(Got_entry(gsym)); + this->set_got_size(); + return this->last_got_offset(); + } + + // Add an entry for a local symbol to the GOT. This returns the + // offset of the new entry from the start of the GOT. + unsigned int + add_local(Object* object, unsigned int sym_index) + { + this->entries_.push_back(Got_entry(object, sym_index)); + this->set_got_size(); + return this->last_got_offset(); + } + + // Add a constant to the GOT. This returns the offset of the new + // entry from the start of the GOT. + unsigned int + add_constant(Valtype constant) + { + this->entries_.push_back(Got_entry(constant)); + this->set_got_size(); + return this->last_got_offset(); + } + + // Write out the GOT table. + void + do_write(Output_file*); + + private: + // This POD class holds a single GOT entry. + class Got_entry + { + public: + // Create a zero entry. + Got_entry() + : local_sym_index_(CONSTANT_CODE) + { this->u_.constant = 0; } + + // Create a global symbol entry. + Got_entry(Symbol* gsym) + : local_sym_index_(GSYM_CODE) + { this->u_.gsym = gsym; } + + // Create a local symbol entry. + Got_entry(Object* object, unsigned int local_sym_index) + : local_sym_index_(local_sym_index) + { + assert(local_sym_index != GSYM_CODE + && local_sym_index != CONSTANT_CODE); + this->u_.object = object; + } + + // Create a constant entry. The constant is a host value--it will + // be swapped, if necessary, when it is written out. + Got_entry(Valtype constant) + : local_sym_index_(CONSTANT_CODE) + { this->u_.constant = constant; } + + // Write the GOT entry to an output view. + void + write(unsigned char* pov) const; + + private: + enum + { + GSYM_CODE = -1U, + CONSTANT_CODE = -2U + }; + + union + { + // For a local symbol, the object. + Object* object; + // For a global symbol, the symbol. + Symbol* gsym; + // For a constant, the constant. + Valtype constant; + } u_; + // For a local symbol, the local symbol index. This is -1U for a + // global symbol, or -2U for a constant. + unsigned int local_sym_index_; + }; + + typedef std::vector Got_entries; + + // Return the offset into the GOT of GOT entry I. + unsigned int + got_offset(unsigned int i) const + { return i * (size / 8); } + + // Return the offset into the GOT of the last entry added. + unsigned int + last_got_offset() const + { return this->got_offset(this->entries_.size() - 1); } + + // Set the size of the section. + void + set_got_size() + { this->set_data_size(this->got_offset(this->entries_.size())); } + + // The list of GOT entries. + Got_entries entries_; +}; + // 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. @@ -278,16 +497,20 @@ class Output_section : public Output_data public: // Create an output section, giving the name, type, and flags. Output_section(const char* name, elfcpp::Elf_Word, elfcpp::Elf_Xword, - unsigned int shndx); + bool may_add_data); virtual ~Output_section(); - // Add a new input section named NAME with header SHDR from object - // OBJECT. Return the offset within the output section. + // Add a new input section SHNDX, named NAME, with header SHDR, from + // object OBJECT. Return the offset within the output section. template off_t - add_input_section(Object* object, const char *name, + add_input_section(Object* object, unsigned int shndx, const char *name, const elfcpp::Shdr& shdr); + // Add generated data ODATA to this output section. + virtual void + add_output_section_data(Output_section_data* posd); + // Return the section name. const char* name() const @@ -303,15 +526,15 @@ class Output_section : public Output_data flags() const { return this->flags_; } - // Return the address alignment. - uint64_t - addralign() const - { return this->addralign_; } - - // Return the section index. + // Return the section index in the output file. unsigned int - shndx() const - { return this->shndx_; } + do_out_shndx() const + { return this->out_shndx_; } + + // Set the output section index. + void + do_set_out_shndx(unsigned int shndx) + { this->out_shndx_ = shndx; } // Set the entsize field. void @@ -333,12 +556,19 @@ class Output_section : public Output_data set_addralign(uint64_t v) { this->addralign_ = v; } + // Set the address of the Output_section. For a typical + // Output_section, there is nothing to do, but if there are any + // Output_section_data objects we need to set the final addresses + // here. + void + do_set_address(uint64_t, off_t); + // Write the data to the file. For a typical Output_section, this - // does nothing. We write out the data by looping over all the - // input sections. + // does nothing: the data is written out by calling Object::Relocate + // on each input object. But if there are any Output_section_data + // objects we do need to write them out here. virtual void - do_write(Output_file*) - { } + do_write(Output_file*); // Return the address alignment--function required by parent class. uint64_t @@ -366,6 +596,83 @@ class Output_section : public Output_data write_header(const Stringpool*, elfcpp::Shdr_write*) const; private: + // In some cases we need to keep a list of the input sections + // associated with this output section. We only need the list if we + // might have to change the offsets of the input section within the + // output section after we add the input section. The ordinary + // input sections will be written out when we process the object + // file, and as such we don't need to track them here. We do need + // to track Output_section_data objects here. We store instances of + // this structure in a std::vector, so it must be a POD. There can + // be many instances of this structure, so we use a union to save + // some space. + class Input_section + { + public: + Input_section() + : shndx_(0), p2align_(0), data_size_(0) + { this->u_.object = NULL; } + + Input_section(Object* object, unsigned int shndx, off_t data_size, + uint64_t addralign) + : shndx_(shndx), + p2align_(ffsll(static_cast(addralign))), + data_size_(data_size) + { + assert(shndx != -1U); + this->u_.object = object; + } + + Input_section(Output_section_data* posd) + : shndx_(-1U), + p2align_(ffsll(static_cast(posd->addralign()))), + data_size_(0) + { this->u_.posd = posd; } + + // The required alignment. + uint64_t + addralign() const + { return static_cast(1) << this->p2align_; } + + // Return the required size. + off_t + data_size() const; + + // Set the address and file offset. This is called during + // Layout::finalize. SECOFF is the file offset of the enclosing + // section. + void + set_address(uint64_t addr, off_t off, off_t secoff); + + // Write out the data. This does nothing for an input section. + void + write(Output_file*); + + private: + // Whether this is an input section. + bool + is_input_section() const + { return this->shndx_ != -1U; } + + // For an ordinary input section, this is the section index in + // the input file. For an Output_section_data, this is -1U. + unsigned int shndx_; + // The required alignment, stored as a power of 2. + unsigned int p2align_; + // For an ordinary input section, the section size. + off_t data_size_; + union + { + // If shndx_ != -1U, this points to the object which holds the + // input section. + Object* object; + // If shndx_ == -1U, this is the data to write out. + Output_section_data* posd; + } u_; + }; + + typedef std::vector Input_section_list; + // Most of these fields are only valid after layout. // The name of the section. This will point into a Stringpool. @@ -385,16 +692,35 @@ class Output_section : public Output_data // The section flags. elfcpp::Elf_Xword flags_; // The section index. - unsigned int shndx_; + unsigned int out_shndx_; + // The input sections. This will be empty in cases where we don't + // need to keep track of them. + Input_section_list input_sections_; + // The offset of the first entry in input_sections_. + off_t first_input_offset_; + // Whether we permit adding data. + bool may_add_data_; }; // A special Output_section which represents the symbol table -// (SHT_SYMTAB). +// (SHT_SYMTAB). The actual data is written out by +// Symbol_table::write_globals. class Output_section_symtab : public Output_section { public: - Output_section_symtab(const char* name, off_t size, unsigned int shndx); + Output_section_symtab(const char* name, off_t size); + + // The data is written out by Symbol_table::write_globals. We don't + // do anything here. + void + do_write(Output_file*) + { } + + // We don't expect to see any input sections or data here. + void + add_output_section_data(Output_section_data*) + { abort(); } }; // A special Output_section which holds a string table. @@ -402,13 +728,17 @@ class Output_section_symtab : public Output_section class Output_section_strtab : public Output_section { public: - Output_section_strtab(const char* name, Stringpool* contents, - unsigned int shndx); + Output_section_strtab(const char* name, Stringpool* contents); // Write out the data. void do_write(Output_file*); + // We don't expect to see any input sections or data here. + void + add_output_section_data(Output_section_data*) + { abort(); } + private: Stringpool* contents_; }; @@ -448,9 +778,14 @@ class Output_segment memsz() const { return this->memsz_; } + // Return the file size. + off_t + filesz() const + { return this->filesz_; } + // Return the maximum alignment of the Output_data. uint64_t - max_data_align() const; + addralign(); // Add an Output_section to this segment. void @@ -463,11 +798,12 @@ class Output_segment // Set the address of the segment to ADDR and the offset to *POFF // (aligned if necessary), and set the addresses and offsets of all - // contained output sections accordingly. Return the address of the - // immediately following segment. Update *POFF. This should only - // be called for a PT_LOAD segment. + // contained output sections accordingly. Set the section indexes + // of all contained output sections starting with *PSHNDX. Return + // the address of the immediately following segment. Update *POFF + // and *PSHNDX. This should only be called for a PT_LOAD segment. uint64_t - set_section_addresses(uint64_t addr, off_t* poff); + set_section_addresses(uint64_t addr, off_t* poff, unsigned int* pshndx); // Set the offset of this segment based on the section. This should // only be called for a non-PT_LOAD segment. @@ -481,13 +817,14 @@ class Output_segment // Write the segment header into *OPHDR. template void - write_header(elfcpp::Phdr_write*) const; + write_header(elfcpp::Phdr_write*); // Write the section headers of associated sections into V. template unsigned char* write_section_headers(const Stringpool*, - unsigned char* v ACCEPT_SIZE_ENDIAN) const; + unsigned char* v, + unsigned int* pshndx ACCEPT_SIZE_ENDIAN) const; private: Output_segment(const Output_segment&); @@ -495,9 +832,14 @@ class Output_segment typedef std::list Output_data_list; + // Find the maximum alignment in an Output_data_list. + static uint64_t + maximum_alignment(const Output_data_list*); + // Set the section addresses in an Output_data_list. uint64_t - set_section_list_addresses(Output_data_list*, uint64_t addr, off_t* poff); + set_section_list_addresses(Output_data_list*, uint64_t addr, off_t* poff, + unsigned int* pshndx); // Return the number of Output_sections in an Output_data_list. unsigned int @@ -507,7 +849,8 @@ class Output_segment template unsigned char* write_section_headers_list(const Stringpool*, const Output_data_list*, - unsigned char* v ACCEPT_SIZE_ENDIAN) const; + unsigned char* v, + unsigned int* pshdx ACCEPT_SIZE_ENDIAN) const; // The list of output data with contents attached to this segment. Output_data_list output_data_; @@ -529,6 +872,8 @@ class Output_segment elfcpp::Elf_Word type_; // The segment flags. elfcpp::Elf_Word flags_; + // Whether we have set align_. + bool is_align_known_; }; // This class represents the output file. diff --git a/gold/po/POTFILES.in b/gold/po/POTFILES.in index 76d60f9a1f0..5d4a4a092ba 100644 --- a/gold/po/POTFILES.in +++ b/gold/po/POTFILES.in @@ -1,5 +1,9 @@ archive.cc archive.h +common.cc +common.h +defstd.cc +defstd.h dirsearch.cc dirsearch.h fileread.cc diff --git a/gold/po/gold.pot b/gold/po/gold.pot index 66f9572026b..a62d946aa15 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-10-20 13:39-0700\n" +"POT-Creation-Date: 2006-11-03 10:04-0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -16,42 +16,42 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: archive.cc:69 +#: archive.cc:62 #, c-format msgid "%s: %s: no archive symbol table (run ranlib)\n" msgstr "" -#: archive.cc:98 +#: archive.cc:91 #, c-format msgid "%s: %s: bad archive symbol table names\n" msgstr "" -#: archive.cc:132 +#: archive.cc:129 #, c-format msgid "%s; %s: malformed archive header at %ld\n" msgstr "" -#: archive.cc:153 +#: archive.cc:150 #, c-format msgid "%s: %s: malformed archive header size at %ld\n" msgstr "" -#: archive.cc:165 +#: archive.cc:162 #, c-format msgid "%s: %s: malformed archive header name at %ld\n" msgstr "" -#: archive.cc:191 +#: archive.cc:188 #, c-format msgid "%s: %s: bad extended name index at %ld\n" msgstr "" -#: archive.cc:202 +#: archive.cc:199 #, c-format msgid "%s: %s: bad extended name entry at header %ld\n" msgstr "" -#: archive.cc:284 archive.cc:297 +#: archive.cc:279 archive.cc:292 #, c-format msgid "%s: %s: member at %ld is not an ELF object" msgstr "" @@ -66,32 +66,32 @@ msgstr "" msgid "%s: warning: close(%s) failed: %s" msgstr "" -#: fileread.cc:131 +#: fileread.cc:129 #, c-format msgid "%s: %s: lseek to %lld failed: %s" msgstr "" -#: fileread.cc:141 +#: fileread.cc:139 #, c-format msgid "%s: %s: read failed: %s\n" msgstr "" -#: fileread.cc:151 +#: fileread.cc:149 fileread.cc:232 #, c-format msgid "%s: %s: file too short: read only %lld of %lld bytes at %lld\n" msgstr "" -#: fileread.cc:267 +#: fileread.cc:323 #, c-format msgid "%s: cannot find %s\n" msgstr "" -#: fileread.cc:275 +#: fileread.cc:331 #, c-format msgid "%s: cannot open %s: %s\n" msgstr "" -#: gold.cc:100 +#: gold.cc:102 msgid "no input files" msgstr "" @@ -139,319 +139,361 @@ msgstr "" msgid "pthread_cond_signal failed" msgstr "" -#: i386.cc:223 i386.cc:319 i386.cc:466 +#. FIXME: This needs to specify the location somehow. +#: i386.cc:88 +#, c-format +msgid "%s: missing expected TLS relocation\n" +msgstr "" + +#: i386.cc:306 i386.cc:434 i386.cc:627 #, c-format msgid "%s: %s: unexpected reloc %u in object file\n" msgstr "" -#: i386.cc:256 i386.cc:277 +#: i386.cc:339 i386.cc:358 #, c-format msgid "%s: %s: unsupported reloc %u against local symbol\n" msgstr "" -#: i386.cc:354 i386.cc:376 +#: i386.cc:415 i386.cc:469 i386.cc:487 #, c-format msgid "%s: %s: unsupported reloc %u against global symbol %s\n" msgstr "" -#: i386.cc:397 +#: i386.cc:509 #, c-format msgid "%s: %s: unsupported RELA reloc section\n" msgstr "" -#: i386.cc:503 i386.cc:571 +#: i386.cc:548 +#, c-format +msgid "%s: %s: missing expected TLS relocation\n" +msgstr "" + +#: i386.cc:594 i386.cc:659 i386.cc:732 i386.cc:743 #, c-format msgid "%s: %s: unsupported reloc %u\n" msgstr "" -#: i386.cc:528 +#: i386.cc:686 #, c-format msgid "%s: %s: TLS reloc but no TLS segment\n" msgstr "" -#: i386.cc:559 +#: i386.cc:717 #, c-format msgid "%s: %s: unsupported reloc type %u\n" msgstr "" -#: i386.cc:689 +#: i386.cc:926 #, c-format msgid "%s: %s: TLS relocation out of range\n" msgstr "" -#: i386.cc:707 +#: i386.cc:944 #, c-format msgid "%s: %s: TLS relocation against invalid instruction\n" msgstr "" -#: object.cc:60 +#: object.cc:41 #, c-format msgid "%s: %s: bad e_ehsize field (%d != %d)\n" msgstr "" -#: object.cc:67 +#: object.cc:48 #, c-format msgid "%s: %s: bad e_shentsize field (%d != %d)\n" msgstr "" -#: object.cc:108 object.cc:418 +#: object.cc:89 object.cc:329 object.cc:425 #, c-format msgid "%s: %s: bad section name offset for section %u: %lu\n" msgstr "" -#: object.cc:131 +#: object.cc:112 #, c-format msgid "%s: %s: unsupported ELF machine number %d\n" msgstr "" -#: object.cc:226 +#: object.cc:207 #, c-format msgid "%s: %s: invalid symbol table name index: %u\n" msgstr "" -#: object.cc:234 +#: object.cc:215 #, c-format msgid "%s: %s: symbol table name section has wrong type: %u\n" msgstr "" -#: object.cc:286 +#: object.cc:267 #, c-format msgid "%s: %s: section group %u link %u out of range\n" msgstr "" -#: object.cc:296 +#: object.cc:277 #, c-format msgid "%s: %s: section group %u info %u out of range\n" msgstr "" -#: object.cc:307 +#: object.cc:288 #, c-format msgid "%s; %s: symtab section %u link %u out of range\n" msgstr "" -#: object.cc:323 +#: object.cc:304 #, c-format msgid "%s: %s: symbol %u name offset %u out of range\n" msgstr "" -#: object.cc:345 +#: object.cc:352 #, c-format msgid "%s: %s: section %u in section group %u out of range" msgstr "" -#: object.cc:479 +#: object.cc:486 #, c-format msgid "%s: %s: size of symbols is not multiple of symbol size\n" msgstr "" -#: object.cc:566 +#: object.cc:573 #, c-format msgid "%s: %s: unknown section index %u for local symbol %u\n" msgstr "" -#: object.cc:577 +#: object.cc:584 #, c-format msgid "%s: %s: local symbol %u section index %u out of range\n" msgstr "" #. elfcpp::ET_DYN -#: object.cc:755 +#: object.cc:763 #, c-format msgid "%s: %s: dynamic objects are not yet supported\n" msgstr "" -#: object.cc:779 object.cc:832 object.cc:853 +#: object.cc:787 object.cc:840 object.cc:861 #, c-format msgid "%s: %s: ELF file too short\n" msgstr "" -#: object.cc:788 +#: object.cc:796 #, c-format msgid "%s: %s: invalid ELF version 0\n" msgstr "" -#: object.cc:791 +#: object.cc:799 #, c-format msgid "%s: %s: unsupported ELF version %d\n" msgstr "" -#: object.cc:799 +#: object.cc:807 #, c-format msgid "%s: %s: invalid ELF class 0\n" msgstr "" -#: object.cc:806 +#: object.cc:814 #, c-format msgid "%s: %s: unsupported ELF class %d\n" msgstr "" -#: object.cc:814 +#: object.cc:822 #, c-format msgid "%s: %s: invalid ELF data encoding\n" msgstr "" -#: object.cc:821 +#: object.cc:829 #, c-format msgid "%s: %s: unsupported ELF data encoding %d\n" msgstr "" -#: options.cc:97 +#: options.cc:115 #, c-format msgid "" "Usage: %s [options] file...\n" "Options:\n" msgstr "" -#: options.cc:209 +#: options.cc:227 msgid "Search for library LIBNAME" msgstr "" -#: options.cc:210 +#: options.cc:228 msgid "-lLIBNAME --library LIBNAME" msgstr "" -#: options.cc:212 +#: options.cc:230 +msgid "Start a library search group" +msgstr "" + +#: options.cc:232 +msgid "End a library search group" +msgstr "" + +#: options.cc:234 msgid "Add directory to search path" msgstr "" -#: options.cc:213 +#: options.cc:235 msgid "-L DIR, --library-path DIR" msgstr "" -#: options.cc:215 +#: options.cc:237 msgid "Set output file name" msgstr "" -#: options.cc:216 +#: options.cc:238 msgid "-o FILE, --output FILE" msgstr "" -#: options.cc:218 +#: options.cc:240 msgid "Generate relocatable output" msgstr "" -#: options.cc:220 +#: options.cc:242 msgid "Generate shared library" msgstr "" -#: options.cc:222 +#: options.cc:244 msgid "Do not link against shared libraries" msgstr "" -#: options.cc:224 +#: options.cc:246 msgid "Report usage information" msgstr "" -#: options.cc:322 options.cc:373 options.cc:437 +#: options.cc:344 options.cc:395 options.cc:481 msgid "missing argument" msgstr "" -#: options.cc:335 options.cc:382 +#: options.cc:357 options.cc:404 msgid "unknown option" msgstr "" -#: options.cc:451 +#: options.cc:412 +#, c-format +msgid "%s: missing group end" +msgstr "" + +#: options.cc:494 +msgid "may not nest groups" +msgstr "" + +#: options.cc:509 +msgid "group end without group start" +msgstr "" + +#: options.cc:519 #, c-format msgid "%s: use the --help option for usage information\n" msgstr "" -#: options.cc:460 +#: options.cc:528 #, c-format msgid "%s: %s: %s\n" msgstr "" -#: options.cc:469 +#: options.cc:537 #, c-format msgid "%s: -%c: %s\n" msgstr "" -#: output.cc:385 +#: output.cc:521 #, c-format msgid "%s: %s: invalid alignment %lu for section \"%s\"\n" msgstr "" -#: output.cc:775 +#: output.cc:1031 #, c-format msgid "%s: %s: open: %s\n" msgstr "" -#: output.cc:784 +#: output.cc:1040 #, c-format msgid "%s: %s: lseek: %s\n" msgstr "" -#: output.cc:791 +#: output.cc:1047 #, c-format msgid "%s: %s: write: %s\n" msgstr "" -#: output.cc:801 +#: output.cc:1057 #, c-format msgid "%s: %s: mmap: %s\n" msgstr "" -#: output.cc:815 +#: output.cc:1071 #, c-format msgid "%s: %s: munmap: %s\n" msgstr "" -#: output.cc:823 +#: output.cc:1079 #, c-format msgid "%s: %s: close: %s\n" msgstr "" +#: readsyms.cc:84 +#, c-format +msgid "%s: %s: ordinary object found in input group\n" +msgstr "" + #. Here we have to handle any other input file types we need. -#: readsyms.cc:109 +#: readsyms.cc:126 #, c-format msgid "%s: %s: not an object or archive\n" msgstr "" -#: reloc.cc:165 reloc.cc:392 +#: reloc.cc:168 reloc.cc:408 #, c-format msgid "%s: %s: relocation section %u has bad info %u\n" msgstr "" -#: reloc.cc:176 reloc.cc:409 +#: reloc.cc:187 reloc.cc:425 #, c-format msgid "%s: %s: relocation section %u uses unexpected symbol table %u\n" msgstr "" -#: reloc.cc:192 reloc.cc:428 +#: reloc.cc:203 reloc.cc:444 #, c-format msgid "%s: %s: unexpected entsize for reloc section %u: %lu != %u" msgstr "" -#: reloc.cc:203 reloc.cc:439 +#: reloc.cc:214 reloc.cc:455 #, c-format msgid "%s: %s: reloc section %u size %lu uneven" msgstr "" -#: resolve.cc:138 +#: resolve.cc:140 #, c-format msgid "%s: %s: invalid STB_LOCAL symbol %s in external symbols\n" msgstr "" -#: resolve.cc:144 +#: resolve.cc:146 #, c-format msgid "%s: %s: unsupported symbol binding %d for symbol %s\n" msgstr "" -#: symtab.cc:303 +#: symtab.cc:428 #, c-format msgid "%s: %s: mixing 32-bit and 64-bit ELF objects\n" msgstr "" -#: symtab.cc:320 +#: symtab.cc:445 #, c-format msgid "%s: %s: bad global symbol name offset %u at %lu\n" msgstr "" -#: target-reloc.h:145 +#: symtab.cc:840 symtab.cc:975 +#, c-format +msgid "%s: %s: unsupported symbol section 0x%x\n" +msgstr "" + +#: target-reloc.h:181 #, c-format msgid "%s: %s: reloc has bad offset %zu\n" msgstr "" -#: target-reloc.h:176 +#: target-reloc.h:191 #, c-format msgid "%s: %s: undefined reference to '%s'\n" msgstr "" diff --git a/gold/readsyms.cc b/gold/readsyms.cc index 3a5650ac801..9ff7ab7d2b1 100644 --- a/gold/readsyms.cc +++ b/gold/readsyms.cc @@ -22,15 +22,16 @@ Read_symbols::~Read_symbols() // Add_symbols task. } -// Return whether a Read_symbols task is runnable. We need write -// access to the symbol table. We can read an ordinary input file -// immediately. For an archive specified using -l, we have to wait -// until the search path is complete. +// Return whether a Read_symbols task is runnable. We can read an +// ordinary input file immediately. For an archive specified using +// -l, we have to wait until the search path is complete. Task::Is_runnable_type Read_symbols::is_runnable(Workqueue*) { - if (this->input_.is_lib() && this->dirpath_.token().is_blocked()) + if (this->input_.is_file() + && this->input_.file().is_lib() + && this->dirpath_.token().is_blocked()) return IS_BLOCKED; return IS_RUNNABLE; @@ -51,7 +52,14 @@ Read_symbols::locks(Workqueue*) void Read_symbols::run(Workqueue* workqueue) { - Input_file* input_file = new Input_file(this->input_); + if (this->input_.is_group()) + { + assert(this->input_group_ == NULL); + this->do_group(workqueue); + return; + } + + Input_file* input_file = new Input_file(this->input_.file()); input_file->open(this->options_, this->dirpath_); // Read enough of the file to pick up the entire ELF header. @@ -69,14 +77,22 @@ Read_symbols::run(Workqueue* workqueue) if (memcmp(p, elfmagic, 4) == 0) { // This is an ELF object. - Object* obj = make_elf_object(this->input_.name(), input_file, 0, - p, bytes); - this->input_objects_->add_object(obj); + if (this->input_group_ != NULL) + { + fprintf(stderr, + _("%s: %s: ordinary object found in input group\n"), + program_name, input_file->name()); + gold_exit(false); + } + + Object* obj = make_elf_object(this->input_.file().name(), + input_file, 0, p, bytes); Read_symbols_data* sd = new Read_symbols_data; obj->read_symbols(sd); - workqueue->queue_front(new Add_symbols(this->symtab_, this->layout_, + workqueue->queue_front(new Add_symbols(this->input_objects_, + this->symtab_, this->layout_, obj, sd, this->this_blocker_, this->next_blocker_)); @@ -93,12 +109,13 @@ Read_symbols::run(Workqueue* workqueue) if (memcmp(p, Archive::armag, Archive::sarmag) == 0) { // This is an archive. - Archive* arch = new Archive(this->input_.name(), input_file); + Archive* arch = new Archive(this->input_.file().name(), input_file); arch->setup(); workqueue->queue(new Add_archive_symbols(this->symtab_, this->layout_, this->input_objects_, arch, + this->input_group_, this->this_blocker_, this->next_blocker_)); return; @@ -111,6 +128,46 @@ Read_symbols::run(Workqueue* workqueue) gold_exit(false); } +// Handle a group. We need to walk through the arguments over and +// over until we don't see any new undefined symbols. We do this by +// setting off Read_symbols Tasks as usual, but recording the archive +// entries instead of deleting them. We also start a Finish_group +// Task which runs after we've read all the symbols. In that task we +// process the archives in a loop until we are done. + +void +Read_symbols::do_group(Workqueue* workqueue) +{ + Input_group* input_group = new Input_group(); + + const Input_file_group* group = this->input_.group(); + Task_token* this_blocker = this->this_blocker_; + for (Input_file_group::const_iterator p = group->begin(); + p != group->end(); + ++p) + { + const Input_argument& arg(*p); + assert(arg.is_file()); + + Task_token* next_blocker = new Task_token(); + next_blocker->add_blocker(); + workqueue->queue(new Read_symbols(this->options_, this->input_objects_, + this->symtab_, this->layout_, + this->dirpath_, arg, input_group, + this_blocker, next_blocker)); + this_blocker = next_blocker; + } + + const int saw_undefined = this->symtab_->saw_undefined(); + workqueue->queue(new Finish_group(this->input_objects_, + this->symtab_, + this->layout_, + input_group, + saw_undefined, + this_blocker, + this->next_blocker_)); +} + // Class Add_symbols. Add_symbols::~Add_symbols() @@ -154,13 +211,71 @@ Add_symbols::locks(Workqueue* workqueue) this->object_); } +// Add the symbols in the object to the symbol table. + void Add_symbols::run(Workqueue*) { + this->input_objects_->add_object(this->object_); this->object_->layout(this->layout_, this->sd_); this->object_->add_symbols(this->symtab_, this->sd_); delete this->sd_; this->sd_ = NULL; } +// Class Finish_group. + +Finish_group::~Finish_group() +{ + if (this->this_blocker_ != NULL) + delete this->this_blocker_; + // next_blocker_ is deleted by the task associated with the next + // input file following the group. +} + +// We need to wait for THIS_BLOCKER_ and unblock NEXT_BLOCKER_. + +Task::Is_runnable_type +Finish_group::is_runnable(Workqueue*) +{ + if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked()) + return IS_BLOCKED; + return IS_RUNNABLE; +} + +Task_locker* +Finish_group::locks(Workqueue* workqueue) +{ + return new Task_locker_block(*this->next_blocker_, workqueue); +} + +// Loop over the archives until there are no new undefined symbols. + +void +Finish_group::run(Workqueue*) +{ + int saw_undefined = this->saw_undefined_; + while (saw_undefined != this->symtab_->saw_undefined()) + { + saw_undefined = this->symtab_->saw_undefined(); + + for (Input_group::const_iterator p = this->input_group_->begin(); + p != this->input_group_->end(); + ++p) + { + Task_lock_obj tl(**p); + + (*p)->add_symbols(this->symtab_, this->layout_, + this->input_objects_); + } + } + + // Delete all the archives now that we no longer need them. + for (Input_group::const_iterator p = this->input_group_->begin(); + p != this->input_group_->end(); + ++p) + delete *p; + delete this->input_group_; +} + } // End namespace gold. diff --git a/gold/readsyms.h b/gold/readsyms.h index 2077d473fe0..2348c3237c6 100644 --- a/gold/readsyms.h +++ b/gold/readsyms.h @@ -3,6 +3,8 @@ #ifndef GOLD_READSYMS_H #define GOLD_READSYMS_H +#include + #include "workqueue.h" #include "object.h" @@ -11,6 +13,8 @@ namespace gold class Input_objects; class Symbol_table; +class Input_group; +class Archive; // This Task is responsible for reading the symbols from an input // file. This also includes reading the relocations so that we can @@ -24,17 +28,20 @@ class Read_symbols : public Task { public: // DIRPATH is the list of directories to search for libraries. - // INPUT is the file to read. THIS_BLOCKER is used to prevent the - // 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. + // INPUT is the file to read. INPUT_GROUP is not NULL if we are in + // the middle of an input group. THIS_BLOCKER is used to prevent + // the 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, Input_objects* input_objects, Symbol_table* symtab, Layout* layout, const Dirsearch& dirpath, - const Input_argument& input, + const Input_argument& input, Input_group* input_group, Task_token* this_blocker, Task_token* next_blocker) : options_(options), input_objects_(input_objects), symtab_(symtab), layout_(layout), dirpath_(dirpath), input_(input), - this_blocker_(this_blocker), next_blocker_(next_blocker) + input_group_(input_group), this_blocker_(this_blocker), + next_blocker_(next_blocker) { } ~Read_symbols(); @@ -51,12 +58,17 @@ class Read_symbols : public Task run(Workqueue*); private: + // Handle an archive group. + void + do_group(Workqueue*); + const General_options& options_; Input_objects* input_objects_; Symbol_table* symtab_; Layout* layout_; const Dirsearch& dirpath_; const Input_argument& input_; + Input_group* input_group_; Task_token* this_blocker_; Task_token* next_blocker_; }; @@ -71,11 +83,12 @@ 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(Symbol_table* symtab, Layout* layout, Object* object, - Read_symbols_data* sd, Task_token* this_blocker, - Task_token* next_blocker) - : symtab_(symtab), layout_(layout), object_(object), sd_(sd), - this_blocker_(this_blocker), next_blocker_(next_blocker) + Add_symbols(Input_objects* input_objects, Symbol_table* symtab, + Layout* layout, Object* object, Read_symbols_data* sd, + Task_token* this_blocker, Task_token* next_blocker) + : input_objects_(input_objects), symtab_(symtab), layout_(layout), + object_(object), sd_(sd), this_blocker_(this_blocker), + next_blocker_(next_blocker) { } ~Add_symbols(); @@ -94,6 +107,7 @@ class Add_symbols : public Task private: class Add_symbols_locker; + Input_objects* input_objects_; Symbol_table* symtab_; Layout* layout_; Object* object_; @@ -102,6 +116,75 @@ private: Task_token* next_blocker_; }; +// This class is used to track the archives in a group. + +class Input_group +{ + public: + typedef std::vector Archives; + typedef Archives::const_iterator const_iterator; + + Input_group() + : archives_() + { } + + // Add an archive to the group. + void + add_archive(Archive* arch) + { this->archives_.push_back(arch); } + + // Loop over the archives in the group. + + const_iterator + begin() const + { return this->archives_.begin(); } + + const_iterator + end() const + { return this->archives_.end(); } + + private: + Archives archives_; +}; + +// This class is used to finish up handling a group. It is just a +// closure. + +class Finish_group : public Task +{ + public: + Finish_group(Input_objects* input_objects, Symbol_table* symtab, + Layout* layout, Input_group* input_group, + int saw_undefined, Task_token* this_blocker, + Task_token* next_blocker) + : input_objects_(input_objects), symtab_(symtab), layout_(layout), + input_group_(input_group), saw_undefined_(saw_undefined), + this_blocker_(this_blocker), next_blocker_(next_blocker) + { } + + ~Finish_group(); + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + Input_objects* input_objects_; + Symbol_table* symtab_; + Layout* layout_; + Input_group* input_group_; + int saw_undefined_; + Task_token* this_blocker_; + Task_token* next_blocker_; +}; + } // end namespace gold #endif // !defined(GOLD_READSYMS_H) diff --git a/gold/reloc.cc b/gold/reloc.cc index bb672e4e100..ca5e380dd9e 100644 --- a/gold/reloc.cc +++ b/gold/reloc.cc @@ -38,8 +38,8 @@ Read_relocs::run(Workqueue* workqueue) Read_relocs_data *rd = new Read_relocs_data; this->object_->read_relocs(rd); workqueue->queue_front(new Scan_relocs(this->options_, this->symtab_, - this->object_, rd, this->symtab_lock_, - this->blocker_)); + this->layout_, this->object_, rd, + this->symtab_lock_, this->blocker_)); } // Scan_relocs methods. @@ -52,7 +52,9 @@ Read_relocs::run(Workqueue* workqueue) Task::Is_runnable_type Scan_relocs::is_runnable(Workqueue*) { - return this->symtab_lock_->is_writable() ? IS_RUNNABLE : IS_LOCKED; + if (!this->symtab_lock_->is_writable() || this->object_->is_locked()) + return IS_LOCKED; + return IS_RUNNABLE; } // Return the locks we hold: one on the file, one on the symbol table @@ -85,7 +87,8 @@ Scan_relocs::locks(Workqueue* workqueue) void Scan_relocs::run(Workqueue*) { - this->object_->scan_relocs(this->options_, this->symtab_, this->rd_); + this->object_->scan_relocs(this->options_, this->symtab_, this->layout_, + this->rd_); delete this->rd_; this->rd_ = NULL; } @@ -170,6 +173,14 @@ Sized_object::do_read_relocs(Read_relocs_data* rd) if (!this->is_section_included(shndx)) continue; + // We are scanning relocations in order to fill out the GOT and + // PLT sections. Relocations for sections which are not + // allocated (typically debugging sections) should not add new + // GOT and PLT entries. So we skip them. + typename This::Shdr secshdr(pshdrs + shndx * This::shdr_size); + if ((secshdr.get_sh_flags() & elfcpp::SHF_ALLOC) == 0) + continue; + if (shdr.get_sh_link() != this->symtab_shnum_) { fprintf(stderr, @@ -239,6 +250,7 @@ template void Sized_object::do_scan_relocs(const General_options& options, Symbol_table* symtab, + Layout* layout, Read_relocs_data* rd) { Sized_target* target = this->sized_target(); @@ -253,7 +265,7 @@ Sized_object::do_scan_relocs(const General_options& options, p != rd->relocs.end(); ++p) { - target->scan_relocs(options, symtab, this, p->sh_type, + target->scan_relocs(options, symtab, layout, this, p->sh_type, p->contents->data(), p->reloc_count, this->local_symbol_count_, local_symbols, @@ -338,11 +350,15 @@ Sized_object::write_sections(const unsigned char* pshdrs, if (shdr.get_sh_type() == elfcpp::SHT_NOBITS) continue; - assert(map_sections[i].offset >= 0 - && map_sections[i].offset < os->data_size()); off_t start = os->offset() + map_sections[i].offset; off_t sh_size = shdr.get_sh_size(); + if (sh_size == 0) + continue; + + assert(map_sections[i].offset >= 0 + && map_sections[i].offset + sh_size <= os->data_size()); + unsigned char* view = of->get_output_view(start, sh_size); this->read(shdr.get_sh_offset(), sh_size, view); @@ -477,24 +493,28 @@ template void Sized_object<32, false>::do_scan_relocs(const General_options& options, Symbol_table* symtab, + Layout* layout, Read_relocs_data* rd); template void Sized_object<32, true>::do_scan_relocs(const General_options& options, Symbol_table* symtab, + Layout* layout, Read_relocs_data* rd); template void Sized_object<64, false>::do_scan_relocs(const General_options& options, Symbol_table* symtab, + Layout* layout, Read_relocs_data* rd); template void Sized_object<64, true>::do_scan_relocs(const General_options& options, Symbol_table* symtab, + Layout* layout, Read_relocs_data* rd); template diff --git a/gold/reloc.h b/gold/reloc.h index a2e9d54729a..f8f07b8b19e 100644 --- a/gold/reloc.h +++ b/gold/reloc.h @@ -13,6 +13,7 @@ namespace gold class Object; class Read_relocs_data; class Stringpool; +class Layout; // A class to read the relocations for an object file, and then queue // up a task to see if they require any GOT/PLT/COPY relocations in @@ -24,9 +25,9 @@ class Read_relocs : public Task // SYMTAB_LOCK is used to lock the symbol table. BLOCKER should be // unblocked when the Scan_relocs task completes. Read_relocs(const General_options& options, Symbol_table* symtab, - Object* object, Task_token* symtab_lock, + Layout* layout, Object* object, Task_token* symtab_lock, Task_token* blocker) - : options_(options), symtab_(symtab), object_(object), + : options_(options), symtab_(symtab), layout_(layout), object_(object), symtab_lock_(symtab_lock), blocker_(blocker) { } @@ -44,6 +45,7 @@ class Read_relocs : public Task private: const General_options& options_; Symbol_table* symtab_; + Layout* layout_; Object* object_; Task_token* symtab_lock_; Task_token* blocker_; @@ -58,10 +60,10 @@ class Scan_relocs : public Task // SYMTAB_LOCK is used to lock the symbol table. BLOCKER should be // unblocked when the task completes. Scan_relocs(const General_options& options, Symbol_table* symtab, - Object* object, Read_relocs_data* rd, Task_token* symtab_lock, - Task_token* blocker) - : options_(options), symtab_(symtab), object_(object), rd_(rd), - symtab_lock_(symtab_lock), blocker_(blocker) + Layout* layout, Object* object, Read_relocs_data* rd, + Task_token* symtab_lock, Task_token* blocker) + : options_(options), symtab_(symtab), layout_(layout), object_(object), + rd_(rd), symtab_lock_(symtab_lock), blocker_(blocker) { } // The standard Task methods. @@ -80,6 +82,7 @@ class Scan_relocs : public Task const General_options& options_; Symbol_table* symtab_; + Layout* layout_; Object* object_; Read_relocs_data* rd_; Task_token* symtab_lock_; diff --git a/gold/resolve.cc b/gold/resolve.cc index 6b8199cd8f2..83e616cec6f 100644 --- a/gold/resolve.cc +++ b/gold/resolve.cc @@ -19,12 +19,14 @@ void Symbol::override_base(const elfcpp::Sym& sym, Object* object) { - this->object_ = object; - this->shnum_ = sym.get_st_shndx(); // FIXME: Handle SHN_XINDEX. + assert(this->source_ == FROM_OBJECT); + this->u_.from_object.object = object; + // FIXME: Handle SHN_XINDEX. + this->u_.from_object.shnum = sym.get_st_shndx(); 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->nonvis_ = sym.get_st_nonvis(); } // Override the fields in Sized_symbol. @@ -37,7 +39,7 @@ Sized_symbol::override(const elfcpp::Sym& sym, { this->override_base(sym, object); this->value_ = sym.get_st_value(); - this->size_ = sym.get_st_size(); + this->symsize_ = sym.get_st_size(); } // Resolve a symbol. This is called the second and subsequent times @@ -315,9 +317,16 @@ Symbol_table::resolve(Sized_symbol* to, case DYN_DEF * 16 + UNDEF: case DYN_WEAK_DEF * 16 + UNDEF: case UNDEF * 16 + UNDEF: + // A new undefined reference tells us nothing. + return; + case WEAK_UNDEF * 16 + UNDEF: case DYN_UNDEF * 16 + UNDEF: case DYN_WEAK_UNDEF * 16 + UNDEF: + // A strong undef overrides a dynamic or weak undef. + to->override(sym, object); + return; + case COMMON * 16 + UNDEF: case WEAK_COMMON * 16 + UNDEF: case DYN_COMMON * 16 + UNDEF: @@ -391,50 +400,100 @@ Symbol_table::resolve(Sized_symbol* to, return; case COMMON * 16 + COMMON: + // Set the size to the maximum. + if (sym.get_st_size() > to->symsize()) + to->set_symsize(sym.get_st_size()); + return; + case WEAK_COMMON * 16 + COMMON: + // I'm not sure just what a weak common symbol means, but + // presumably it can be overridden by a regular common symbol. + to->override(sym, object); + return; + case DYN_COMMON * 16 + COMMON: case DYN_WEAK_COMMON * 16 + COMMON: + { + // Use the real common symbol, but adjust the size if necessary. + typename Sized_symbol::Size_type symsize = to->symsize(); + to->override(sym, object); + if (to->symsize() < symsize) + to->set_symsize(symsize); + } + return; case DEF * 16 + WEAK_COMMON: case WEAK_DEF * 16 + WEAK_COMMON: case DYN_DEF * 16 + WEAK_COMMON: case DYN_WEAK_DEF * 16 + WEAK_COMMON: + // Whatever a weak common symbol is, it won't override a + // definition. + return; + case UNDEF * 16 + WEAK_COMMON: case WEAK_UNDEF * 16 + WEAK_COMMON: case DYN_UNDEF * 16 + WEAK_COMMON: case DYN_WEAK_UNDEF * 16 + WEAK_COMMON: + // A weak common symbol is better than an undefined symbol. + to->override(sym, object); + return; + case COMMON * 16 + WEAK_COMMON: case WEAK_COMMON * 16 + WEAK_COMMON: case DYN_COMMON * 16 + WEAK_COMMON: case DYN_WEAK_COMMON * 16 + WEAK_COMMON: + // Ignore a weak common symbol in the presence of a real common + // symbol. + return; case DEF * 16 + DYN_COMMON: case WEAK_DEF * 16 + DYN_COMMON: case DYN_DEF * 16 + DYN_COMMON: case DYN_WEAK_DEF * 16 + DYN_COMMON: + // Ignore a dynamic common symbol in the presence of a + // definition. + return; + case UNDEF * 16 + DYN_COMMON: case WEAK_UNDEF * 16 + DYN_COMMON: case DYN_UNDEF * 16 + DYN_COMMON: case DYN_WEAK_UNDEF * 16 + DYN_COMMON: + // A dynamic common symbol is a definition of sorts. + to->override(sym, object); + return; + case COMMON * 16 + DYN_COMMON: case WEAK_COMMON * 16 + DYN_COMMON: case DYN_COMMON * 16 + DYN_COMMON: case DYN_WEAK_COMMON * 16 + DYN_COMMON: + // Set the size to the maximum. + if (sym.get_st_size() > to->symsize()) + to->set_symsize(sym.get_st_size()); + return; 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: + // A common symbol is ignored in the face of a definition. + return; + 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: + // I guess a weak common symbol is better than a definition. + to->override(sym, object); + return; + 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: - abort(); - break; + // Set the size to the maximum. + if (sym.get_st_size() > to->symsize()) + to->set_symsize(sym.get_st_size()); + return; default: abort(); diff --git a/gold/symtab.cc b/gold/symtab.cc index 748218d843f..0f43faa360e 100644 --- a/gold/symtab.cc +++ b/gold/symtab.cc @@ -17,28 +17,85 @@ namespace gold // Class Symbol. -// Initialize the fields in the base class Symbol. +// Initialize fields in Symbol. This initializes everything except u_ +// and source_. -template void -Symbol::init_base(const char* name, const char* version, Object* object, - const elfcpp::Sym& sym) +Symbol::init_fields(const char* name, const char* version, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis) { 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->is_special_ = false; + this->got_offset_ = 0; + this->type_ = type; + this->binding_ = binding; + this->visibility_ = visibility; + this->nonvis_ = nonvis; + this->is_target_special_ = false; this->is_def_ = false; this->is_forwarder_ = false; + this->in_dyn_ = false; + this->has_got_offset_ = false; +} + +// Initialize the fields in the base class Symbol for SYM in OBJECT. + +template +void +Symbol::init_base(const char* name, const char* version, Object* object, + const elfcpp::Sym& sym) +{ + this->init_fields(name, version, sym.get_st_type(), sym.get_st_bind(), + sym.get_st_visibility(), sym.get_st_nonvis()); + this->u_.from_object.object = object; + // FIXME: Handle SHN_XINDEX. + this->u_.from_object.shnum = sym.get_st_shndx(); + this->source_ = FROM_OBJECT; this->in_dyn_ = object->is_dynamic(); } -// Initialize the fields in Sized_symbol. +// Initialize the fields in the base class Symbol for a symbol defined +// in an Output_data. + +void +Symbol::init_base(const char* name, Output_data* od, elfcpp::STT type, + elfcpp::STB binding, elfcpp::STV visibility, + unsigned char nonvis, bool offset_is_from_end) +{ + this->init_fields(name, NULL, type, binding, visibility, nonvis); + this->u_.in_output_data.output_data = od; + this->u_.in_output_data.offset_is_from_end = offset_is_from_end; + this->source_ = IN_OUTPUT_DATA; +} + +// Initialize the fields in the base class Symbol for a symbol defined +// in an Output_segment. + +void +Symbol::init_base(const char* name, Output_segment* os, elfcpp::STT type, + elfcpp::STB binding, elfcpp::STV visibility, + unsigned char nonvis, Segment_offset_base offset_base) +{ + this->init_fields(name, NULL, type, binding, visibility, nonvis); + this->u_.in_output_segment.output_segment = os; + this->u_.in_output_segment.offset_base = offset_base; + this->source_ = IN_OUTPUT_SEGMENT; +} + +// Initialize the fields in the base class Symbol for a symbol defined +// as a constant. + +void +Symbol::init_base(const char* name, elfcpp::STT type, + elfcpp::STB binding, elfcpp::STV visibility, + unsigned char nonvis) +{ + this->init_fields(name, NULL, type, binding, visibility, nonvis); + this->source_ = CONSTANT; +} + +// Initialize the fields in Sized_symbol for SYM in OBJECT. template template @@ -48,13 +105,61 @@ Sized_symbol::init(const char* name, const char* version, Object* object, { this->init_base(name, version, object, sym); this->value_ = sym.get_st_value(); - this->size_ = sym.get_st_size(); + this->symsize_ = sym.get_st_size(); +} + +// Initialize the fields in Sized_symbol for a symbol defined in an +// Output_data. + +template +void +Sized_symbol::init(const char* name, Output_data* od, + Value_type value, Size_type symsize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis, + bool offset_is_from_end) +{ + this->init_base(name, od, type, binding, visibility, nonvis, + offset_is_from_end); + this->value_ = value; + this->symsize_ = symsize; +} + +// Initialize the fields in Sized_symbol for a symbol defined in an +// Output_segment. + +template +void +Sized_symbol::init(const char* name, Output_segment* os, + Value_type value, Size_type symsize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis, + Segment_offset_base offset_base) +{ + this->init_base(name, os, type, binding, visibility, nonvis, offset_base); + this->value_ = value; + this->symsize_ = symsize; +} + +// Initialize the fields in Sized_symbol for a symbol defined as a +// constant. + +template +void +Sized_symbol::init(const char* name, Value_type value, Size_type symsize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis) +{ + this->init_base(name, type, binding, visibility, nonvis); + this->value_ = value; + this->symsize_ = symsize; } // Class Symbol_table. Symbol_table::Symbol_table() - : size_(0), offset_(0), table_(), namepool_(), forwarders_() + : size_(0), saw_undefined_(0), offset_(0), table_(), namepool_(), + forwarders_(), commons_() { } @@ -144,7 +249,7 @@ Symbol_table::resolve(Sized_symbol* to, const Sized_symbol* from esym.put_st_value(from->value()); esym.put_st_size(from->symsize()); esym.put_st_info(from->binding(), from->type()); - esym.put_st_other(from->visibility(), from->other()); + esym.put_st_other(from->visibility(), from->nonvis()); esym.put_st_shndx(from->shnum()); Symbol_table::resolve(to, esym.sym(), from->object()); } @@ -198,12 +303,18 @@ Symbol_table::add_from_object(Sized_object* object, // ins.second: true if new entry was inserted, false if not. Sized_symbol* ret; + bool was_undefined; + bool was_common; if (!ins.second) { // We already have an entry for NAME/VERSION. ret = this->get_sized_symbol SELECT_SIZE_NAME (ins.first->second SELECT_SIZE(size)); assert(ret != NULL); + + was_undefined = ret->is_undefined(); + was_common = ret->is_common(); + Symbol_table::resolve(ret, sym, object); if (def) @@ -233,6 +344,10 @@ Symbol_table::add_from_object(Sized_object* object, { // This is the first time we have seen NAME/VERSION. assert(ins.first->second == NULL); + + was_undefined = false; + was_common = false; + if (def && !insdef.second) { // We already have an entry for NAME/NULL. Make @@ -279,6 +394,16 @@ Symbol_table::add_from_object(Sized_object* object, } } + // Record every time we see a new undefined symbol, to speed up + // archive groups. + if (!was_undefined && ret->is_undefined()) + ++this->saw_undefined_; + + // Keep track of common symbols, to speed up common symbol + // allocation. + if (!was_common && ret->is_common()) + this->commons_.push_back(ret); + return ret; } @@ -369,6 +494,302 @@ Symbol_table::add_from_object( } } +// Create and return a specially defined symbol. If ONLY_IF_REF is +// true, then only create the symbol if there is a reference to it. + +template +Sized_symbol* +Symbol_table::define_special_symbol(Target* target, const char* name, + bool only_if_ref) +{ + assert(this->size_ == size); + + Symbol* oldsym; + Sized_symbol* sym; + + if (only_if_ref) + { + oldsym = this->lookup(name, NULL); + if (oldsym == NULL) + return NULL; + sym = NULL; + + // Canonicalize NAME. + name = oldsym->name(); + } + else + { + // Canonicalize NAME. + name = this->namepool_.add(name); + + Symbol* const snull = NULL; + const char* const vnull = NULL; + std::pair ins = + this->table_.insert(std::make_pair(std::make_pair(name, vnull), + snull)); + + if (!ins.second) + { + // We already have a symbol table entry for NAME. + oldsym = ins.first->second; + assert(oldsym != NULL); + sym = NULL; + } + else + { + // We haven't seen this symbol before. + assert(ins.first->second == NULL); + + if (!target->has_make_symbol()) + sym = new Sized_symbol(); + else + { + assert(target->get_size() == size); + assert(target->is_big_endian() ? big_endian : !big_endian); + typedef Sized_target My_target; + My_target* sized_target = static_cast(target); + sym = sized_target->make_symbol(); + if (sym == NULL) + return NULL; + } + + ins.first->second = sym; + oldsym = NULL; + } + } + + if (oldsym != NULL) + { + assert(sym == NULL); + + sym = this->get_sized_symbol SELECT_SIZE_NAME (oldsym + SELECT_SIZE(size)); + assert(sym->source() == Symbol::FROM_OBJECT); + const int old_shnum = sym->shnum(); + if (old_shnum != elfcpp::SHN_UNDEF + && old_shnum != elfcpp::SHN_COMMON + && !sym->object()->is_dynamic()) + { + fprintf(stderr, "%s: linker defined: multiple definition of %s\n", + program_name, name); + // FIXME: Report old location. Record that we have seen an + // error. + return NULL; + } + + // Our new definition is going to override the old reference. + } + + return sym; +} + +// Define a symbol based on an Output_data. + +void +Symbol_table::define_in_output_data(Target* target, const char* name, + Output_data* od, + uint64_t value, uint64_t symsize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, + unsigned char nonvis, + bool offset_is_from_end, + bool only_if_ref) +{ + assert(target->get_size() == this->size_); + if (this->size_ == 32) + this->do_define_in_output_data<32>(target, name, od, value, symsize, + type, binding, visibility, nonvis, + offset_is_from_end, only_if_ref); + else if (this->size_ == 64) + this->do_define_in_output_data<64>(target, name, od, value, symsize, + type, binding, visibility, nonvis, + offset_is_from_end, only_if_ref); + else + abort(); +} + +// Define a symbol in an Output_data, sized version. + +template +void +Symbol_table::do_define_in_output_data( + Target* target, + const char* name, + Output_data* od, + typename elfcpp::Elf_types::Elf_Addr value, + typename elfcpp::Elf_types::Elf_WXword symsize, + elfcpp::STT type, + elfcpp::STB binding, + elfcpp::STV visibility, + unsigned char nonvis, + bool offset_is_from_end, + bool only_if_ref) +{ + Sized_symbol* sym; + + if (target->is_big_endian()) + sym = this->define_special_symbol(target, name, only_if_ref); + else + sym = this->define_special_symbol(target, name, only_if_ref); + + if (sym == NULL) + return; + + sym->init(name, od, value, symsize, type, binding, visibility, nonvis, + offset_is_from_end); +} + +// Define a symbol based on an Output_segment. + +void +Symbol_table::define_in_output_segment(Target* target, const char* name, + Output_segment* os, + uint64_t value, uint64_t symsize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, + unsigned char nonvis, + Symbol::Segment_offset_base offset_base, + bool only_if_ref) +{ + assert(target->get_size() == this->size_); + if (this->size_ == 32) + this->do_define_in_output_segment<32>(target, name, os, value, symsize, + type, binding, visibility, nonvis, + offset_base, only_if_ref); + else if (this->size_ == 64) + this->do_define_in_output_segment<64>(target, name, os, value, symsize, + type, binding, visibility, nonvis, + offset_base, only_if_ref); + else + abort(); +} + +// Define a symbol in an Output_segment, sized version. + +template +void +Symbol_table::do_define_in_output_segment( + Target* target, + const char* name, + Output_segment* os, + typename elfcpp::Elf_types::Elf_Addr value, + typename elfcpp::Elf_types::Elf_WXword symsize, + elfcpp::STT type, + elfcpp::STB binding, + elfcpp::STV visibility, + unsigned char nonvis, + Symbol::Segment_offset_base offset_base, + bool only_if_ref) +{ + Sized_symbol* sym; + + if (target->is_big_endian()) + sym = this->define_special_symbol(target, name, only_if_ref); + else + sym = this->define_special_symbol(target, name, only_if_ref); + + if (sym == NULL) + return; + + sym->init(name, os, value, symsize, type, binding, visibility, nonvis, + offset_base); +} + +// Define a special symbol with a constant value. It is a multiple +// definition error if this symbol is already defined. + +void +Symbol_table::define_as_constant(Target* target, const char* name, + uint64_t value, uint64_t symsize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis, + bool only_if_ref) +{ + assert(target->get_size() == this->size_); + if (this->size_ == 32) + this->do_define_as_constant<32>(target, name, value, symsize, + type, binding, visibility, nonvis, + only_if_ref); + else if (this->size_ == 64) + this->do_define_as_constant<64>(target, name, value, symsize, + type, binding, visibility, nonvis, + only_if_ref); + else + abort(); +} + +// Define a symbol as a constant, sized version. + +template +void +Symbol_table::do_define_as_constant( + Target* target, + const char* name, + typename elfcpp::Elf_types::Elf_Addr value, + typename elfcpp::Elf_types::Elf_WXword symsize, + elfcpp::STT type, + elfcpp::STB binding, + elfcpp::STV visibility, + unsigned char nonvis, + bool only_if_ref) +{ + Sized_symbol* sym; + + if (target->is_big_endian()) + sym = this->define_special_symbol(target, name, only_if_ref); + else + sym = this->define_special_symbol(target, name, only_if_ref); + + if (sym == NULL) + return; + + sym->init(name, value, symsize, type, binding, visibility, nonvis); +} + +// Define a set of symbols in output sections. + +void +Symbol_table::define_symbols(const Layout* layout, Target* target, int count, + const Define_symbol_in_section* p) +{ + for (int i = 0; i < count; ++i, ++p) + { + Output_section* os = layout->find_output_section(p->output_section); + if (os != NULL) + this->define_in_output_data(target, p->name, os, p->value, p->size, + p->type, p->binding, p->visibility, + p->nonvis, p->offset_is_from_end, + p->only_if_ref); + else + this->define_as_constant(target, p->name, 0, p->size, p->type, + p->binding, p->visibility, p->nonvis, + p->only_if_ref); + } +} + +// Define a set of symbols in output segments. + +void +Symbol_table::define_symbols(const Layout* layout, Target* target, int count, + const Define_symbol_in_segment* p) +{ + for (int i = 0; i < count; ++i, ++p) + { + Output_segment* os = layout->find_output_segment(p->segment_type, + p->segment_flags_set, + p->segment_flags_clear); + if (os != NULL) + this->define_in_output_segment(target, p->name, os, p->value, p->size, + p->type, p->binding, p->visibility, + p->nonvis, p->offset_base, + p->only_if_ref); + else + this->define_as_constant(target, p->name, 0, p->size, p->type, + p->binding, p->visibility, p->nonvis, + p->only_if_ref); + } +} + // Set the final values for all the symbols. Record the file offset // OFF. Add their names to POOL. Return the new file offset. @@ -383,13 +804,15 @@ Symbol_table::finalize(off_t off, Stringpool* pool) abort(); } -// Set the final value for all the symbols. +// Set the final value for all the symbols. This is called after +// Layout::finalize, so all the output sections have their final +// address. template off_t Symbol_table::sized_finalize(off_t off, Stringpool* pool) { - off = (off + (size >> 3) - 1) & ~ ((size >> 3) - 1); + off = align_address(off, size >> 3); this->offset_ = off; const int sym_size = elfcpp::Elf_sizes::sym_size; @@ -402,34 +825,91 @@ Symbol_table::sized_finalize(off_t off, Stringpool* pool) // FIXME: Here we need to decide which symbols should go into // the output file. - // FIXME: This is wrong. - if (sym->shnum() >= elfcpp::SHN_LORESERVE) - { - ++p; - continue; - } - - off_t secoff; - Output_section* os = sym->object()->output_section(sym->shnum(), - &secoff); + typename Sized_symbol::Value_type value; - if (os == NULL) - { - // We should be able to erase this symbol from the symbol - // table, but at least with gcc 4.0.2 - // std::unordered_map::erase doesn't appear to return the - // new iterator. - // p = this->table_.erase(p); - ++p; - } - else + switch (sym->source()) { - sym->set_value(sym->value() + os->address() + secoff); - pool->add(sym->name()); - ++p; - ++count; - off += sym_size; + case Symbol::FROM_OBJECT: + { + unsigned int shnum = sym->shnum(); + + // FIXME: We need some target specific support here. + if (shnum >= elfcpp::SHN_LORESERVE + && shnum != elfcpp::SHN_ABS) + { + fprintf(stderr, _("%s: %s: unsupported symbol section 0x%x\n"), + program_name, sym->name(), shnum); + gold_exit(false); + } + + if (shnum == elfcpp::SHN_UNDEF) + value = 0; + else if (shnum == elfcpp::SHN_ABS) + value = sym->value(); + else + { + off_t secoff; + Output_section* os = sym->object()->output_section(shnum, + &secoff); + + if (os == NULL) + { + // We should be able to erase this symbol from the + // symbol table, but at least with gcc 4.0.2 + // std::unordered_map::erase doesn't appear to return + // the new iterator. + // p = this->table_.erase(p); + ++p; + continue; + } + + value = sym->value() + os->address() + secoff; + } + } + break; + + case Symbol::IN_OUTPUT_DATA: + { + Output_data* od = sym->output_data(); + value = sym->value() + od->address(); + if (sym->offset_is_from_end()) + value += od->data_size(); + } + break; + + case Symbol::IN_OUTPUT_SEGMENT: + { + Output_segment* os = sym->output_segment(); + value = sym->value() + os->vaddr(); + switch (sym->offset_base()) + { + case Symbol::SEGMENT_START: + break; + case Symbol::SEGMENT_END: + value += os->memsz(); + break; + case Symbol::SEGMENT_BSS: + value += os->filesz(); + break; + default: + abort(); + } + } + break; + + case Symbol::CONSTANT: + value = sym->value(); + break; + + default: + abort(); } + + sym->set_value(value); + pool->add(sym->name()); + ++count; + off += sym_size; + ++p; } this->output_count_ = count; @@ -481,23 +961,61 @@ Symbol_table::sized_write_globals(const Target*, // FIXME: This repeats sized_finalize(). - // FIXME: This is wrong. - if (sym->shnum() >= elfcpp::SHN_LORESERVE) - continue; - - off_t secoff; - Output_section* os = sym->object()->output_section(sym->shnum(), - &secoff); - if (os == NULL) - continue; + unsigned int shndx; + switch (sym->source()) + { + case Symbol::FROM_OBJECT: + { + unsigned int shnum = sym->shnum(); + + // FIXME: We need some target specific support here. + if (shnum >= elfcpp::SHN_LORESERVE + && shnum != elfcpp::SHN_ABS) + { + fprintf(stderr, _("%s: %s: unsupported symbol section 0x%x\n"), + program_name, sym->name(), sym->shnum()); + gold_exit(false); + } + + if (shnum == elfcpp::SHN_UNDEF || shnum == elfcpp::SHN_ABS) + shndx = shnum; + else + { + off_t secoff; + Output_section* os = sym->object()->output_section(shnum, + &secoff); + if (os == NULL) + continue; + + shndx = os->out_shndx(); + } + } + break; + + case Symbol::IN_OUTPUT_DATA: + shndx = sym->output_data()->out_shndx(); + break; + + case Symbol::IN_OUTPUT_SEGMENT: + shndx = elfcpp::SHN_ABS; + break; + + case Symbol::CONSTANT: + shndx = elfcpp::SHN_ABS; + break; + + default: + abort(); + } elfcpp::Sym_write osym(ps); osym.put_st_name(sympool->get_offset(sym->name())); osym.put_st_value(sym->value()); osym.put_st_size(sym->symsize()); osym.put_st_info(elfcpp::elf_st_info(sym->binding(), sym->type())); - osym.put_st_other(elfcpp::elf_st_other(sym->visibility(), sym->other())); - osym.put_st_shndx(os->shndx()); + osym.put_st_other(elfcpp::elf_st_other(sym->visibility(), + sym->nonvis())); + osym.put_st_shndx(shndx); ps += sym_size; } diff --git a/gold/symtab.h b/gold/symtab.h index 2c5fbc93d35..40a9e577061 100644 --- a/gold/symtab.h +++ b/gold/symtab.h @@ -5,6 +5,7 @@ #include #include +#include #include #include "elfcpp.h" @@ -17,6 +18,8 @@ namespace gold { class Object; +class Output_data; +class Output_segment; class Output_file; class Target; @@ -31,6 +34,37 @@ class Sized_object; class Symbol { public: + // Because we want the class to be small, we don't use any virtual + // functions. But because symbols can be defined in different + // places, we need to classify them. This enum is the different + // sources of symbols we support. + enum Source + { + // Symbol defined in an input file--this is the most common case. + FROM_OBJECT, + // Symbol defined in an Output_data, a special section created by + // the target. + IN_OUTPUT_DATA, + // Symbol defined in an Output_segment, with no associated + // section. + IN_OUTPUT_SEGMENT, + // Symbol value is constant. + CONSTANT + }; + + // When the source is IN_OUTPUT_SEGMENT, we need to describe what + // the offset means. + enum Segment_offset_base + { + // From the start of the segment. + SEGMENT_START, + // From the end of the segment. + SEGMENT_END, + // From the filesz of the segment--i.e., after the loaded bytes + // but before the bytes which are allocated but zeroed. + SEGMENT_BSS + }; + // Return the symbol name. const char* name() const @@ -42,10 +76,64 @@ class Symbol version() const { return this->version_; } + // Return the symbol source. + Source + source() const + { return this->source_; } + // Return the object with which this symbol is associated. Object* object() const - { return this->object_; } + { + assert(this->source_ == FROM_OBJECT); + return this->u_.from_object.object; + } + + // Return the index of the section in the input object file. + unsigned int + shnum() const + { + assert(this->source_ == FROM_OBJECT); + return this->u_.from_object.shnum; + } + + // Return the output data section with which this symbol is + // associated, if the symbol was specially defined with respect to + // an output data section. + Output_data* + output_data() const + { + assert(this->source_ == IN_OUTPUT_DATA); + return this->u_.in_output_data.output_data; + } + + // If this symbol was defined with respect to an output data + // section, return whether the value is an offset from end. + bool + offset_is_from_end() const + { + assert(this->source_ == IN_OUTPUT_DATA); + return this->u_.in_output_data.offset_is_from_end; + } + + // Return the output segment with which this symbol is associated, + // if the symbol was specially defined with respect to an output + // segment. + Output_segment* + output_segment() const + { + assert(this->source_ == IN_OUTPUT_SEGMENT); + return this->u_.in_output_segment.output_segment; + } + + // If this symbol was defined with respect to an output segment, + // return the offset base. + Segment_offset_base + offset_base() const + { + assert(this->source_ == IN_OUTPUT_SEGMENT); + return this->u_.in_output_segment.offset_base; + } // Return the symbol binding. elfcpp::STB @@ -64,13 +152,8 @@ class Symbol // Return the non-visibility part of the st_other field. unsigned char - other() const - { return this->other_; } - - // Return the section index. - unsigned int - shnum() const - { return this->shnum_; } + nonvis() const + { return this->nonvis_; } // 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 @@ -94,11 +177,49 @@ class Symbol set_in_dyn() { this->in_dyn_ = true; } - // Return whether this symbol needs an entry in the dynamic symbol - // table. FIXME: Needs to be fleshed out. + // Return whether this symbol has an entry in the GOT section. bool - in_dynsym() const - { return this->in_dyn_; } + has_got_offset() const + { return this->has_got_offset_; } + + // Return the offset into the GOT section of this symbol. + unsigned int + got_offset() const + { + assert(this->has_got_offset()); + return this->got_offset_; + } + + // Set the GOT offset of this symbol. + void + set_got_offset(unsigned int got_offset) + { + this->has_got_offset_ = true; + this->got_offset_ = got_offset; + } + + // Return whether this symbol is resolved locally. This is always + // true when linking statically. It is true for a symbol defined in + // this object when using -Bsymbolic. It is true for a symbol + // marked local in a version file. FIXME: This needs to be + // completed. + bool + is_resolved_locally() const + { return !this->in_dyn_; } + + // Return whether this is an undefined symbol. + bool + is_undefined() const + { + return this->source_ == FROM_OBJECT && this->shnum() == elfcpp::SHN_UNDEF; + } + + // Return whether this is a common symbol. + bool + is_common() const + { + return this->source_ == FROM_OBJECT && this->shnum() == elfcpp::SHN_COMMON; + } protected: // Instances of this class should always be created at a specific @@ -106,12 +227,34 @@ class Symbol Symbol() { } + // Initialize the general fields. + void + init_fields(const char* name, const char* version, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis); + // Initialize fields from an ELF symbol in OBJECT. template void init_base(const char *name, const char* version, Object* object, const elfcpp::Sym&); + // Initialize fields for an Output_data. + void + init_base(const char* name, Output_data*, elfcpp::STT, elfcpp::STB, + elfcpp::STV, unsigned char nonvis, bool offset_is_from_end); + + // Initialize fields for an Output_segment. + void + init_base(const char* name, Output_segment* os, elfcpp::STT type, + elfcpp::STB binding, elfcpp::STV visibility, + unsigned char nonvis, Segment_offset_base offset_base); + + // Initialize fields for a constant. + void + init_base(const char* name, elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis); + // Override existing symbol. template void @@ -126,10 +269,45 @@ class Symbol // 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_; + + union + { + // This struct is used if SOURCE_ == FROM_OBJECT. + struct + { + // 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; + } from_object; + + // This struct is used if SOURCE_ == IN_OUTPUT_DATA. + struct + { + // Output_data in which symbol is defined. Before + // Layout::finalize the symbol's value is an offset within the + // Output_data. + Output_data* output_data; + // True if the offset is from the end, false if the offset is + // from the beginning. + bool offset_is_from_end; + } in_output_data; + + // This struct is used if SOURCE_ == IN_OUTPUT_SEGMENT. + struct + { + // Output_segment in which the symbol is defined. Before + // Layout::finalize the symbol's value is an offset. + Output_segment* output_segment; + // The base to use for the offset before Layout::finalize. + Segment_offset_base offset_base; + } in_output_segment; + } u_; + + // If this symbol has an entry in the GOT section (has_got_offset_ + // is true), this is the offset. + unsigned int got_offset_; // Symbol type. elfcpp::STT type_ : 4; // Symbol binding. @@ -137,10 +315,12 @@ class Symbol // Symbol visibility. elfcpp::STV visibility_ : 2; // Rest of symbol st_other field. - unsigned int other_ : 6; + unsigned int nonvis_ : 6; + // The type of symbol. + Source source_ : 2; // True if this symbol always requires special target-specific // handling. - bool is_special_ : 1; + bool is_target_special_ : 1; // True if this is the default version of the symbol. bool is_def_ : 1; // True if this symbol really forwards to another symbol. This is @@ -153,6 +333,8 @@ class Symbol bool is_forwarder_ : 1; // True if we've seen this symbol in a dynamic object. bool in_dyn_ : 1; + // True if the symbol has an entry in the GOT section. + bool has_got_offset_ : 1; }; // The parts of a symbol which are size specific. Using a template @@ -174,6 +356,23 @@ class Sized_symbol : public Symbol init(const char *name, const char* version, Object* object, const elfcpp::Sym&); + // Initialize fields for an Output_data. + void + init(const char* name, Output_data*, Value_type value, Size_type symsize, + elfcpp::STT, elfcpp::STB, elfcpp::STV, unsigned char nonvis, + bool offset_is_from_end); + + // Initialize fields for an Output_segment. + void + init(const char* name, Output_segment*, Value_type value, Size_type symsize, + elfcpp::STT, elfcpp::STB, elfcpp::STV, unsigned char nonvis, + Segment_offset_base offset_base); + + // Initialize fields for a constant. + void + init(const char* name, Value_type value, Size_type symsize, + elfcpp::STT, elfcpp::STB, elfcpp::STV, unsigned char nonvis); + // Override existing symbol. template void @@ -188,7 +387,12 @@ class Sized_symbol : public Symbol // is a template parameter). Size_type symsize() const - { return this->size_; } + { return this->symsize_; } + + // Set the symbol size. This is used when resolving common symbols. + void + set_symsize(Size_type symsize) + { this->symsize_ = symsize; } // Set the symbol value. This is called when we store the final // values of the symbols into the symbol table. @@ -200,10 +404,84 @@ class Sized_symbol : public Symbol Sized_symbol(const Sized_symbol&); Sized_symbol& operator=(const Sized_symbol&); - // Symbol value. + // Symbol value. Before Layout::finalize this is the offset in the + // input section. This is set to the final value during + // Layout::finalize. Value_type value_; // Symbol size. - Size_type size_; + Size_type symsize_; +}; + +// A struct describing a symbol defined by the linker, where the value +// of the symbol is defined based on an output section. This is used +// for symbols defined by the linker, like "_init_array_start". + +struct Define_symbol_in_section +{ + // The symbol name. + const char* name; + // The name of the output section with which this symbol should be + // associated. If there is no output section with that name, the + // symbol will be defined as zero. + const char* output_section; + // The offset of the symbol within the output section. This is an + // offset from the start of the output section, unless start_at_end + // is true, in which case this is an offset from the end of the + // output section. + uint64_t value; + // The size of the symbol. + uint64_t size; + // The symbol type. + elfcpp::STT type; + // The symbol binding. + elfcpp::STB binding; + // The symbol visibility. + elfcpp::STV visibility; + // The rest of the st_other field. + unsigned char nonvis; + // If true, the value field is an offset from the end of the output + // section. + bool offset_is_from_end; + // If true, this symbol is defined only if we see a reference to it. + bool only_if_ref; +}; + +// A struct describing a symbol defined by the linker, where the value +// of the symbol is defined based on a segment. This is used for +// symbols defined by the linker, like "_end". We describe the +// segment with which the symbol should be associated by its +// characteristics. If no segment meets these characteristics, the +// symbol will be defined as zero. If there is more than one segment +// which meets these characteristics, we will use the first one. + +struct Define_symbol_in_segment +{ + // The symbol name. + const char* name; + // The segment type where the symbol should be defined, typically + // PT_LOAD. + elfcpp::PT segment_type; + // Bitmask of segment flags which must be set. + elfcpp::PF segment_flags_set; + // Bitmask of segment flags which must be clear. + elfcpp::PF segment_flags_clear; + // The offset of the symbol within the segment. The offset is + // calculated from the position set by offset_base. + uint64_t value; + // The size of the symbol. + uint64_t size; + // The symbol type. + elfcpp::STT type; + // The symbol binding. + elfcpp::STB binding; + // The symbol visibility. + elfcpp::STV visibility; + // The rest of the st_other field. + unsigned char nonvis; + // The base from which we compute the offset. + Symbol::Segment_offset_base offset_base; + // If true, this symbol is defined only if we see a reference to it. + bool only_if_ref; }; // The main linker symbol table. @@ -226,6 +504,47 @@ class Symbol_table size_t count, const char* sym_names, size_t sym_name_size, Symbol** sympointers); + // Define a special symbol. + template + Sized_symbol* + define_special_symbol(Target* target, const char* name, bool only_if_ref); + + // Define a special symbol based on an Output_data. It is a + // multiple definition error if this symbol is already defined. + void + define_in_output_data(Target*, const char* name, Output_data*, + uint64_t value, uint64_t symsize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis, + bool offset_is_from_end, bool only_if_ref); + + // Define a special symbol based on an Output_segment. It is a + // multiple definition error if this symbol is already defined. + void + define_in_output_segment(Target*, const char* name, Output_segment*, + uint64_t value, uint64_t symsize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis, + Symbol::Segment_offset_base, bool only_if_ref); + + // Define a special symbol with a constant value. It is a multiple + // definition error if this symbol is already defined. + void + define_as_constant(Target*, const char* name, uint64_t value, + uint64_t symsize, elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis, + bool only_if_ref); + + // Define a set of symbols in output sections. + void + define_symbols(const Layout*, Target*, int count, + const Define_symbol_in_section*); + + // Define a set of symbols in output segments. + void + define_symbols(const Layout*, Target*, int count, + const Define_symbol_in_segment*); + // Look up a symbol. Symbol* lookup(const char*, const char* version = NULL) const; @@ -248,6 +567,15 @@ class Symbol_table const Sized_symbol* get_sized_symbol(const Symbol* ACCEPT_SIZE) const; + // Return the count of undefined symbols seen. + int + saw_undefined() const + { return this->saw_undefined_; } + + // Allocate the common symbols + void + allocate_commons(const General_options&, Layout*); + // Finalize the symbol table after we have set the final addresses // of all the input sections. This sets the final symbol values and // adds the names to *POOL. It records the file offset OFF, and @@ -291,6 +619,43 @@ class Symbol_table resolve(Sized_symbol* to, const Sized_symbol* from ACCEPT_SIZE_ENDIAN); + // Define a symbol in an Output_data, sized version. + template + void + do_define_in_output_data(Target*, const char* name, Output_data*, + typename elfcpp::Elf_types::Elf_Addr value, + typename elfcpp::Elf_types::Elf_WXword ssize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis, + bool offset_is_from_end, bool only_if_ref); + + // Define a symbol in an Output_segment, sized version. + template + void + do_define_in_output_segment( + Target*, const char* name, Output_segment* os, + typename elfcpp::Elf_types::Elf_Addr value, + typename elfcpp::Elf_types::Elf_WXword ssize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis, + Symbol::Segment_offset_base offset_base, bool only_if_ref); + + // Define a symbol as a constant, sized version. + template + void + do_define_as_constant( + Target*, const char* name, + typename elfcpp::Elf_types::Elf_Addr value, + typename elfcpp::Elf_types::Elf_WXword ssize, + elfcpp::STT type, elfcpp::STB binding, + elfcpp::STV visibility, unsigned char nonvis, + bool only_if_ref); + + // Allocate the common symbols, sized version. + template + void + do_allocate_commons(const General_options&, Layout*); + // Finalize symbols specialized for size. template off_t @@ -320,9 +685,17 @@ class Symbol_table typedef Unordered_map Symbol_table_type; + // The type of the list of common symbols. + + typedef std::vector Commons_type; + // The size of the symbols in the symbol table (32 or 64). int size_; + // We increment this every time we see a new undefined symbol, for + // use in archive groups. + int saw_undefined_; + // The file offset within the output symtab section where we should // write the table. off_t offset_; @@ -339,6 +712,13 @@ class Symbol_table // Forwarding symbols. Unordered_map forwarders_; + + // We don't expect there to be very many common symbols, so we keep + // a list of them. When we find a common symbol we add it to this + // list. It is possible that by the time we process the list the + // symbol is no longer a common symbol. It may also have become a + // forwarder. + Commons_type commons_; }; // We inline get_sized_symbol for efficiency. diff --git a/gold/target-reloc.h b/gold/target-reloc.h index 2d1fe306fb4..ebaa8276cf0 100644 --- a/gold/target-reloc.h +++ b/gold/target-reloc.h @@ -36,11 +36,14 @@ struct Reloc_types // way to avoidmaking a function call for each relocation, and to // avoid repeating the generic code for each target. -template +template inline void scan_relocs( const General_options& options, Symbol_table* symtab, + Layout* layout, + Target_type* target, Sized_object* object, const unsigned char* prelocs, size_t reloc_count, @@ -68,6 +71,7 @@ scan_relocs( + r_sym * sym_size); const unsigned int shndx = lsym.get_st_shndx(); if (shndx < elfcpp::SHN_LORESERVE + && shndx != elfcpp::SHN_UNDEF && !object->is_section_included(lsym.get_st_shndx())) { // RELOC is a relocation against a local symbol in a @@ -88,7 +92,8 @@ scan_relocs( continue; } - scan.local(options, object, reloc, r_type, lsym); + scan.local(options, symtab, layout, target, object, reloc, r_type, + lsym); } else { @@ -97,7 +102,8 @@ scan_relocs( if (gsym->is_forwarder()) gsym = symtab->resolve_forwards(gsym); - scan.global(options, object, reloc, r_type, gsym); + scan.global(options, symtab, layout, target, object, reloc, r_type, + gsym); } } } @@ -117,10 +123,12 @@ scan_relocs( // of relocs. VIEW is the section data, VIEW_ADDRESS is its memory // address, and VIEW_SIZE is the size. -template +template inline void relocate_section( const Relocate_info* relinfo, + Target_type* target, const unsigned char* prelocs, size_t reloc_count, unsigned char* view, @@ -140,13 +148,6 @@ relocate_section( Reltype reloc(prelocs); off_t offset = reloc.get_r_offset(); - if (offset < 0 || offset >= view_size) - { - fprintf(stderr, _("%s: %s: reloc has bad offset %zu\n"), - program_name, relinfo->location(i, offset).c_str(), - static_cast(offset)); - gold_exit(false); - } typename elfcpp::Elf_types::Elf_WXword r_info = reloc.get_r_info(); unsigned int r_sym = elfcpp::elf_r_sym(r_info); @@ -169,19 +170,29 @@ relocate_section( sym = static_cast*>(gsym); value = sym->value(); + } - if (sym->shnum() == elfcpp::SHN_UNDEF - && sym->binding() != elfcpp::STB_WEAK) - { - fprintf(stderr, _("%s: %s: undefined reference to '%s'\n"), - program_name, relinfo->location(i, offset).c_str(), - sym->name()); - // gold_exit(false); - } + if (!relocate.relocate(relinfo, target, i, reloc, r_type, sym, value, + view + offset, view_address + offset, view_size)) + continue; + + if (offset < 0 || offset >= view_size) + { + fprintf(stderr, _("%s: %s: reloc has bad offset %zu\n"), + program_name, relinfo->location(i, offset).c_str(), + static_cast(offset)); + gold_exit(false); } - relocate.relocate(relinfo, i, reloc, r_type, sym, value, view + offset, - view_address + offset, view_size); + if (sym != NULL + && sym->is_undefined() + && sym->binding() != elfcpp::STB_WEAK) + { + fprintf(stderr, _("%s: %s: undefined reference to '%s'\n"), + program_name, relinfo->location(i, offset).c_str(), + sym->name()); + // gold_exit(false); + } } } diff --git a/gold/target-select.cc b/gold/target-select.cc index 28b75277809..d15d0e346d1 100644 --- a/gold/target-select.cc +++ b/gold/target-select.cc @@ -34,7 +34,7 @@ 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()) + for (Target_selector* p = target_selectors; p != NULL; p = p->next()) { int pmach = p->machine(); if ((pmach == machine || pmach == elfcpp::EM_NONE) diff --git a/gold/target-select.h b/gold/target-select.h index 0762d58f734..8ccd75fac75 100644 --- a/gold/target-select.h +++ b/gold/target-select.h @@ -29,7 +29,7 @@ class 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; + virtual Target* recognize(int machine, int osabi, int abiversion) = 0; // Return the next Target_selector in the linked list. Target_selector* diff --git a/gold/target.h b/gold/target.h index 75f149e0707..b72998da359 100644 --- a/gold/target.h +++ b/gold/target.h @@ -149,6 +149,7 @@ class Sized_target : public Target virtual void scan_relocs(const General_options& options, Symbol_table* symtab, + Layout* layout, Sized_object* object, unsigned int sh_type, const unsigned char* prelocs, diff --git a/gold/workqueue.h b/gold/workqueue.h index 5cce2d56cca..ed7a5b00d7d 100644 --- a/gold/workqueue.h +++ b/gold/workqueue.h @@ -17,12 +17,12 @@ #define GOLD_WORKQUEUE_H #include "gold-threads.h" -#include "options.h" #include "fileread.h" namespace gold { +class General_options; class Task; class Workqueue; @@ -286,6 +286,10 @@ class Task // Run the task. virtual void run(Workqueue*) = 0; + + private: + Task(const Task&); + Task& operator=(const Task&); }; // A simple task which waits for a blocker and then runs a function.