diff options
-rw-r--r-- | gold/ChangeLog | 28 | ||||
-rw-r--r-- | gold/archive.cc | 52 | ||||
-rw-r--r-- | gold/archive.h | 6 | ||||
-rw-r--r-- | gold/plugin.cc | 206 | ||||
-rw-r--r-- | gold/plugin.h | 75 | ||||
-rw-r--r-- | gold/readsyms.cc | 27 | ||||
-rw-r--r-- | gold/readsyms.h | 4 | ||||
-rw-r--r-- | gold/symtab.cc | 8 |
8 files changed, 388 insertions, 18 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog index a1207cf..32bdcab 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,31 @@ +2011-01-24 Ian Lance Taylor <iant@google.com> + + * plugin.cc (class Plugin_rescan): Define new class. + (Plugin_manager::claim_file): Set any_claimed_. + (Plugin_manager::save_archive): New function. + (Plugin_manager::save_input_group): New function. + (Plugin_manager::all_symbols_read): Create Plugin_rescan task if + necessary. + (Plugin_manager::new_undefined_symbol): New function. + (Plugin_manager::rescan): New function. + (Plugin_manager::rescannable_defines): New function. + (Plugin_manager::add_input_file): Set any_added_. + * plugin.h (class Plugin_manager): define new fields rescannable_, + undefined_symbols_, any_claimed_, and any_added_. Declare + Plugin_rescan as friend. Declare new functions. + (Plugin_manager::Rescannable): Define type. + (Plugin_manager::Rescannable_list): Define type. + (Plugin_manager::Undefined_symbol_list): Define type. + (Plugin_manager::Plugin_manager): Initialize new fields. + * archive.cc (Archive::defines_symbol): New function. + (Add_archive_symbols::run): Pass archive to plugins if any. + * archive.h (class Archive): Declare defines_symbol. + * readsyms.cc (Input_group::~Input_group): New function. + (Finish_group::run): Pass input_group to plugins if any. + * readsyms.h (class Input_group): Declare destructor. + * symtab.cc (add_from_object): Pass undefined symbol to plugins if + any. + 2011-01-10 Ian Lance Taylor <iant@google.com> * layout.cc (Layout::layout_eh_frame): Mark a writable .eh_frame diff --git a/gold/archive.cc b/gold/archive.cc index 2837ec2..89fc422 100644 --- a/gold/archive.cc +++ b/gold/archive.cc @@ -1,6 +1,6 @@ // archive.cc -- archive support for gold -// Copyright 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. +// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. // Written by Ian Lance Taylor <iant@google.com>. // This file is part of gold. @@ -779,6 +779,42 @@ Archive::add_symbols(Symbol_table* symtab, Layout* layout, return true; } +// Return whether the archive includes a member which defines the +// symbol SYM. + +bool +Archive::defines_symbol(Symbol* sym) const +{ + const char* symname = sym->name(); + size_t symname_len = strlen(symname); + size_t armap_size = this->armap_.size(); + for (size_t i = 0; i < armap_size; ++i) + { + if (this->armap_checked_[i]) + continue; + const char* archive_symname = (this->armap_names_.data() + + this->armap_[i].name_offset); + if (strncmp(archive_symname, symname, symname_len) != 0) + continue; + char c = archive_symname[symname_len]; + if (c == '\0' && sym->version() == NULL) + return true; + if (c == '@') + { + const char* ver = archive_symname + symname_len + 1; + if (*ver == '@') + { + if (sym->version() == NULL) + return true; + ++ver; + } + if (sym->version() != NULL && strcmp(sym->version(), ver) == 0) + return true; + } + } + return false; +} + // Include all the archive members in the link. This is for --whole-archive. bool @@ -1001,8 +1037,18 @@ Add_archive_symbols::run(Workqueue* workqueue) if (incremental_inputs != NULL) incremental_inputs->report_archive_end(this->archive_); - // We no longer need to know about this archive. - delete this->archive_; + if (!parameters->options().has_plugins() + || this->archive_->input_file()->options().whole_archive()) + { + // We no longer need to know about this archive. + delete this->archive_; + } + else + { + // The plugin interface may want to rescan this archive. + parameters->options().plugins()->save_archive(this->archive_); + } + this->archive_ = NULL; } } diff --git a/gold/archive.h b/gold/archive.h index b4db76d..c5ba114 100644 --- a/gold/archive.h +++ b/gold/archive.h @@ -1,6 +1,6 @@ // archive.h -- archive support for gold -*- C++ -*- -// Copyright 2006, 2007, 2008, 2010 Free Software Foundation, Inc. +// Copyright 2006, 2007, 2008, 2010, 2011 Free Software Foundation, Inc. // Written by Ian Lance Taylor <iant@google.com>. // This file is part of gold. @@ -152,6 +152,10 @@ class Archive bool add_symbols(Symbol_table*, Layout*, Input_objects*, Mapfile*); + // Return whether the archive defines the symbol. + bool + defines_symbol(Symbol*) const; + // Dump statistical information to stderr. static void print_stats(); diff --git a/gold/plugin.cc b/gold/plugin.cc index a3569c9..9c444c2 100644 --- a/gold/plugin.cc +++ b/gold/plugin.cc @@ -1,6 +1,6 @@ // plugin.cc -- plugin manager for gold -*- C++ -*- -// Copyright 2008, 2009, 2010 Free Software Foundation, Inc. +// Copyright 2008, 2009, 2010, 2011 Free Software Foundation, Inc. // Written by Cary Coutant <ccoutant@google.com>. // This file is part of gold. @@ -261,6 +261,45 @@ Plugin::cleanup() } } +// This task is used to rescan archives as needed. + +class Plugin_rescan : public Task +{ + public: + Plugin_rescan(Task_token* this_blocker, Task_token* next_blocker) + : this_blocker_(this_blocker), next_blocker_(next_blocker) + { } + + ~Plugin_rescan() + { + delete this->this_blocker_; + } + + Task_token* + is_runnable() + { + if (this->this_blocker_->is_blocked()) + return this->this_blocker_; + return NULL; + } + + void + locks(Task_locker* tl) + { tl->add(this, this->next_blocker_); } + + void + run(Workqueue*) + { parameters->options().plugins()->rescan(this); } + + std::string + get_name() const + { return "Plugin_rescan"; } + + private: + Task_token* this_blocker_; + Task_token* next_blocker_; +}; + // Plugin_manager methods. Plugin_manager::~Plugin_manager() @@ -311,6 +350,8 @@ Plugin_manager::claim_file(Input_file* input_file, off_t offset, { if ((*this->current_)->claim_file(&this->plugin_input_file_)) { + this->any_claimed_ = true; + if (this->objects_.size() > handle) return this->objects_[handle]; @@ -324,6 +365,31 @@ Plugin_manager::claim_file(Input_file* input_file, off_t offset, return NULL; } +// Save an archive. This is used so that a plugin can add a file +// which refers to a symbol which was not previously referenced. In +// that case we want to pretend that the symbol was referenced before, +// and pull in the archive object. + +void +Plugin_manager::save_archive(Archive* archive) +{ + if (this->in_replacement_phase_ || !this->any_claimed_) + delete archive; + else + this->rescannable_.push_back(Rescannable(archive)); +} + +// Save an Input_group. This is like save_archive. + +void +Plugin_manager::save_input_group(Input_group* input_group) +{ + if (this->in_replacement_phase_ || !this->any_claimed_) + delete input_group; + else + this->rescannable_.push_back(Rescannable(input_group)); +} + // Call the all-symbols-read handlers. void @@ -348,9 +414,146 @@ Plugin_manager::all_symbols_read(Workqueue* workqueue, Task* task, ++this->current_) (*this->current_)->all_symbols_read(); + if (this->any_added_) + { + Task_token* next_blocker = new Task_token(true); + next_blocker->add_blocker(); + workqueue->queue(new Plugin_rescan(this->this_blocker_, next_blocker)); + this->this_blocker_ = next_blocker; + } + *last_blocker = this->this_blocker_; } +// This is called when we see a new undefined symbol. If we are in +// the replacement phase, this means that we may need to rescan some +// archives we have previously seen. + +void +Plugin_manager::new_undefined_symbol(Symbol* sym) +{ + if (this->in_replacement_phase_) + this->undefined_symbols_.push_back(sym); +} + +// Rescan archives as needed. This handles the case where a new +// object file added by a plugin has an undefined reference to some +// symbol defined in an archive. + +void +Plugin_manager::rescan(Task* task) +{ + size_t rescan_pos = 0; + size_t rescan_size = this->rescannable_.size(); + while (!this->undefined_symbols_.empty()) + { + if (rescan_pos >= rescan_size) + { + this->undefined_symbols_.clear(); + return; + } + + Undefined_symbol_list undefs; + undefs.reserve(this->undefined_symbols_.size()); + this->undefined_symbols_.swap(undefs); + + size_t min_rescan_pos = rescan_size; + + for (Undefined_symbol_list::const_iterator p = undefs.begin(); + p != undefs.end(); + ++p) + { + if (!(*p)->is_undefined()) + continue; + + this->undefined_symbols_.push_back(*p); + + // Find the first rescan archive which defines this symbol, + // starting at the current rescan position. The rescan position + // exists so that given -la -lb -lc we don't look for undefined + // symbols in -lb back in -la, but instead get the definition + // from -lc. Don't bother to look past the current minimum + // rescan position. + for (size_t i = rescan_pos; i < min_rescan_pos; ++i) + { + if (this->rescannable_defines(i, *p)) + { + min_rescan_pos = i; + break; + } + } + } + + if (min_rescan_pos >= rescan_size) + { + // We didn't find any rescannable archives which define any + // undefined symbols. + return; + } + + const Rescannable& r(this->rescannable_[min_rescan_pos]); + if (r.is_archive) + { + Task_lock_obj<Archive> tl(task, r.u.archive); + r.u.archive->add_symbols(this->symtab_, this->layout_, + this->input_objects_, this->mapfile_); + } + else + { + size_t next_saw_undefined = this->symtab_->saw_undefined(); + size_t saw_undefined; + do + { + saw_undefined = next_saw_undefined; + + for (Input_group::const_iterator p = r.u.input_group->begin(); + p != r.u.input_group->end(); + ++p) + { + Task_lock_obj<Archive> tl(task, *p); + + (*p)->add_symbols(this->symtab_, this->layout_, + this->input_objects_, this->mapfile_); + } + + next_saw_undefined = this->symtab_->saw_undefined(); + } + while (saw_undefined != next_saw_undefined); + } + + for (size_t i = rescan_pos; i < min_rescan_pos + 1; ++i) + { + if (this->rescannable_[i].is_archive) + delete this->rescannable_[i].u.archive; + else + delete this->rescannable_[i].u.input_group; + } + + rescan_pos = min_rescan_pos + 1; + } +} + +// Return whether the rescannable at index I defines SYM. + +bool +Plugin_manager::rescannable_defines(size_t i, Symbol* sym) +{ + const Rescannable& r(this->rescannable_[i]); + if (r.is_archive) + return r.u.archive->defines_symbol(sym); + else + { + for (Input_group::const_iterator p = r.u.input_group->begin(); + p != r.u.input_group->end(); + ++p) + { + if ((*p)->defines_symbol(sym)) + return true; + } + return false; + } +} + // Layout deferred objects. void @@ -473,6 +676,7 @@ Plugin_manager::add_input_file(const char* pathname, bool is_lib) this->this_blocker_, next_blocker)); this->this_blocker_ = next_blocker; + this->any_added_ = true; return LDPS_OK; } diff --git a/gold/plugin.h b/gold/plugin.h index 32f1bb7..c26414d 100644 --- a/gold/plugin.h +++ b/gold/plugin.h @@ -1,6 +1,6 @@ // plugin.h -- plugin manager for gold -*- C++ -*- -// Copyright 2008, 2009, 2010 Free Software Foundation, Inc. +// Copyright 2008, 2009, 2010, 2011 Free Software Foundation, Inc. // Written by Cary Coutant <ccoutant@google.com>. // This file is part of gold. @@ -36,12 +36,17 @@ namespace gold class General_options; class Input_file; class Input_objects; +class Archive; +class Input_group; +class Symbol; class Symbol_table; class Layout; class Dirsearch; class Mapfile; +class Task; class Task_token; class Pluginobj; +class Plugin_rescan; // This class represents a single plugin library. @@ -124,7 +129,8 @@ class Plugin_manager public: Plugin_manager(const General_options& options) : plugins_(), objects_(), deferred_layout_objects_(), input_file_(NULL), - plugin_input_file_(), in_replacement_phase_(false), + plugin_input_file_(), rescannable_(), undefined_symbols_(), + any_claimed_(false), in_replacement_phase_(false), any_added_(false), options_(options), workqueue_(NULL), task_(NULL), input_objects_(NULL), symtab_(NULL), layout_(NULL), dirpath_(NULL), mapfile_(NULL), this_blocker_(NULL), extra_search_path_() @@ -153,6 +159,16 @@ class Plugin_manager Pluginobj* claim_file(Input_file* input_file, off_t offset, off_t filesize); + // Let the plugin manager save an archive for later rescanning. + // This takes ownership of the Archive pointer. + void + save_archive(Archive*); + + // Let the plugin manager save an input group for later rescanning. + // This takes ownership of the Input_group pointer. + void + save_input_group(Input_group*); + // Call the all-symbols-read handlers. void all_symbols_read(Workqueue* workqueue, Task* task, @@ -160,6 +176,11 @@ class Plugin_manager Layout* layout, Dirsearch* dirpath, Mapfile* mapfile, Task_token** last_blocker); + // Tell the plugin manager that we've a new undefined symbol which + // may require rescanning. + void + new_undefined_symbol(Symbol*); + // Run deferred layout. void layout_deferred_objects(); @@ -245,9 +266,42 @@ class Plugin_manager Plugin_manager(const Plugin_manager&); Plugin_manager& operator=(const Plugin_manager&); + // Plugin_rescan is a Task which calls the private rescan method. + friend class Plugin_rescan; + + // An archive or input group which may have to be rescanned if a + // plugin adds a new file. + struct Rescannable + { + bool is_archive; + union + { + Archive* archive; + Input_group* input_group; + } u; + + Rescannable(Archive* archive) + : is_archive(true) + { this->u.archive = archive; } + + Rescannable(Input_group* input_group) + : is_archive(false) + { this->u.input_group = input_group; } + }; + typedef std::list<Plugin*> Plugin_list; typedef std::vector<Pluginobj*> Object_list; typedef std::vector<Relobj*> Deferred_layout_list; + typedef std::vector<Rescannable> Rescannable_list; + typedef std::vector<Symbol*> Undefined_symbol_list; + + // Rescan archives for undefined symbols. + void + rescan(Task*); + + // See whether the rescannable at index I defines SYM. + bool + rescannable_defines(size_t i, Symbol* sym); // The list of plugin libraries. Plugin_list plugins_; @@ -265,11 +319,24 @@ class Plugin_manager Input_file* input_file_; struct ld_plugin_input_file plugin_input_file_; - // TRUE after the all symbols read event; indicates that we are - // processing replacement files whose symbols should replace the + // A list of archives and input groups being saved for possible + // later rescanning. + Rescannable_list rescannable_; + + // A list of undefined symbols found in added files. + Undefined_symbol_list undefined_symbols_; + + // Whether any input files have been claimed by a plugin. + bool any_claimed_; + + // Set to true after the all symbols read event; indicates that we + // are processing replacement files whose symbols should replace the // placeholder symbols from the Pluginobj objects. bool in_replacement_phase_; + // Whether any input files or libraries were added by a plugin. + bool any_added_; + const General_options& options_; Workqueue* workqueue_; Task* task_; diff --git a/gold/readsyms.cc b/gold/readsyms.cc index 5c00026..9f88b01 100644 --- a/gold/readsyms.cc +++ b/gold/readsyms.cc @@ -1,6 +1,6 @@ // readsyms.cc -- read input file symbols for gold -// Copyright 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. +// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. // Written by Ian Lance Taylor <iant@google.com>. // This file is part of gold. @@ -599,6 +599,19 @@ Add_symbols::run(Workqueue*) } } +// Class Input_group. + +// When we delete an Input_group we can delete the archive +// information. + +Input_group::~Input_group() +{ + for (Input_group::const_iterator p = this->begin(); + p != this->end(); + ++p) + delete *p; +} + // Class Start_group. Start_group::~Start_group() @@ -680,8 +693,8 @@ Finish_group::run(Workqueue*) } } - // Now that we're done with the archives, record the incremental layout - // information, then delete them. + // Now that we're done with the archives, record the incremental + // layout information. for (Input_group::const_iterator p = this->input_group_->begin(); p != this->input_group_->end(); ++p) @@ -691,10 +704,12 @@ Finish_group::run(Workqueue*) this->layout_->incremental_inputs(); if (incremental_inputs != NULL) incremental_inputs->report_archive_end(*p); - - delete *p; } - delete this->input_group_; + + if (parameters->options().has_plugins()) + parameters->options().plugins()->save_input_group(this->input_group_); + else + delete this->input_group_; } // Class Read_script diff --git a/gold/readsyms.h b/gold/readsyms.h index bc4f38f..9515ba1 100644 --- a/gold/readsyms.h +++ b/gold/readsyms.h @@ -1,6 +1,6 @@ // readsyms.h -- read input file symbols for gold -*- C++ -*- -// Copyright 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. +// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. // Written by Ian Lance Taylor <iant@google.com>. // This file is part of gold. @@ -191,6 +191,8 @@ class Input_group : archives_() { } + ~Input_group(); + // Add an archive to the group. void add_archive(Archive* arch) diff --git a/gold/symtab.cc b/gold/symtab.cc index 75ed7f6..8531911 100644 --- a/gold/symtab.cc +++ b/gold/symtab.cc @@ -1,6 +1,6 @@ // symtab.cc -- the gold symbol table -// Copyright 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. +// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. // Written by Ian Lance Taylor <iant@google.com>. // This file is part of gold. @@ -1002,7 +1002,11 @@ Symbol_table::add_from_object(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_; + { + ++this->saw_undefined_; + if (parameters->options().has_plugins()) + parameters->options().plugins()->new_undefined_symbol(ret); + } // Keep track of common symbols, to speed up common symbol // allocation. |