aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gold/ChangeLog28
-rw-r--r--gold/archive.cc52
-rw-r--r--gold/archive.h6
-rw-r--r--gold/plugin.cc206
-rw-r--r--gold/plugin.h75
-rw-r--r--gold/readsyms.cc27
-rw-r--r--gold/readsyms.h4
-rw-r--r--gold/symtab.cc8
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.