aboutsummaryrefslogtreecommitdiff
path: root/gold/plugin.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gold/plugin.cc')
-rw-r--r--gold/plugin.cc206
1 files changed, 205 insertions, 1 deletions
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;
}