2010-03-08 Hui Zhu <teawater@gmail.com>
[binutils-gdb.git] / gold / plugin.cc
index a5f7a06f27e526d1255b4a59a100b953f79f7312..2831c2b19fdfe5a03fcb07d7a391825f55d056ec 100644 (file)
@@ -1,6 +1,6 @@
-// plugin.c -- plugin manager for gold      -*- C++ -*-
+// plugin.cc -- plugin manager for gold      -*- C++ -*-
 
-// Copyright 2008 Free Software Foundation, Inc.
+// Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 // Written by Cary Coutant <ccoutant@google.com>.
 
 // This file is part of gold.
 // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
 // MA 02110-1301, USA.
 
+#include "gold.h"
+
 #include <cstdio>
 #include <cstdarg>
 #include <cstring>
 #include <string>
 #include <vector>
+
+#ifdef ENABLE_PLUGINS
 #include <dlfcn.h>
+#endif
 
-#include "gold.h"
 #include "parameters.h"
 #include "errors.h"
 #include "fileread.h"
@@ -61,6 +65,12 @@ register_cleanup(ld_plugin_cleanup_handler handler);
 static enum ld_plugin_status
 add_symbols(void *handle, int nsyms, const struct ld_plugin_symbol *syms);
 
+static enum ld_plugin_status
+get_input_file(const void *handle, struct ld_plugin_input_file *file);
+
+static enum ld_plugin_status
+release_input_file(const void *handle);
+
 static enum ld_plugin_status
 get_symbols(const void *handle, int nsyms, struct ld_plugin_symbol *syms);
 
@@ -68,14 +78,17 @@ static enum ld_plugin_status
 add_input_file(char *pathname);
 
 static enum ld_plugin_status
-message(int level, char *format, ...);
+add_input_library(char *pathname);
+
+static enum ld_plugin_status
+message(int level, const char *format, ...);
 
 };
 
 #endif // ENABLE_PLUGINS
 
 static Pluginobj* make_sized_plugin_object(Input_file* input_file,
-                                           off_t offset);
+                                           off_t offset, off_t filesize);
 
 // Plugin methods.
 
@@ -85,42 +98,27 @@ void
 Plugin::load()
 {
 #ifdef ENABLE_PLUGINS
-  std::string filename;
-  std::vector<std::string> args;
-
-  // Parse the filename and arguments, each separated by commas.
-  // FIXME:  Temporarily allowing semicolon as an argument separator
-  // so args can be passed through gcc's -Wl,... option, which
-  // breaks arguments at the commas.
-  const char* p = this->args_;
-  int n = strcspn(p, ",;");
-  filename.assign(p, n);
-  p += n;
-  while (*p == ',' || *p == ';')
-    {
-      ++p;
-      n = strcspn(p, ",;");
-      args.push_back(std::string(p, n));
-      p += n;
-    }
-
   // Load the plugin library.
   // FIXME: Look for the library in standard locations.
-  this->handle_ = dlopen(filename.c_str(), RTLD_NOW);
+  this->handle_ = dlopen(this->filename_.c_str(), RTLD_NOW);
   if (this->handle_ == NULL)
     {
-      gold_error(_("%s: could not load plugin library"), filename.c_str());
+      gold_error(_("%s: could not load plugin library"),
+                 this->filename_.c_str());
       return;
     }
 
   // Find the plugin's onload entry point.
-  ld_plugin_onload onload = reinterpret_cast<ld_plugin_onload>
-    (dlsym(this->handle_, "onload"));
-  if (onload == NULL)
+  void* ptr = dlsym(this->handle_, "onload");
+  if (ptr == NULL)
     {
-      gold_error(_("%s: could not find onload entry point"), filename.c_str());
+      gold_error(_("%s: could not find onload entry point"),
+                 this->filename_.c_str());
       return;
     }
+  ld_plugin_onload onload;
+  gold_assert(sizeof(onload) == sizeof(ptr));
+  memcpy(&onload, &ptr, sizeof(ptr));
 
   // Get the linker's version number.
   const char* ver = get_version_string();
@@ -129,11 +127,17 @@ Plugin::load()
   sscanf(ver, "%d.%d", &major, &minor);
 
   // Allocate and populate a transfer vector.
-  const int tv_fixed_size = 11;
-  int tv_size = args.size() + tv_fixed_size;
+  const int tv_fixed_size = 14;
+  int tv_size = this->args_.size() + tv_fixed_size;
   ld_plugin_tv *tv = new ld_plugin_tv[tv_size];
 
+  // Put LDPT_MESSAGE at the front of the list so the plugin can use it
+  // while processing subsequent entries.
   int i = 0;
+  tv[i].tv_tag = LDPT_MESSAGE;
+  tv[i].tv_u.tv_message = message;
+
+  ++i;
   tv[i].tv_tag = LDPT_API_VERSION;
   tv[i].tv_u.tv_val = LD_PLUGIN_API_VERSION;
 
@@ -150,11 +154,11 @@ Plugin::load()
   else
     tv[i].tv_u.tv_val = LDPO_EXEC;
 
-  for (unsigned int j = 0; j < args.size(); ++j)
+  for (unsigned int j = 0; j < this->args_.size(); ++j)
     {
       ++i;
       tv[i].tv_tag = LDPT_OPTION;
-      tv[i].tv_u.tv_string = args[j].c_str();
+      tv[i].tv_u.tv_string = this->args_[j].c_str();
     }
 
   ++i;
@@ -173,6 +177,14 @@ Plugin::load()
   tv[i].tv_tag = LDPT_ADD_SYMBOLS;
   tv[i].tv_u.tv_add_symbols = add_symbols;
 
+  ++i;
+  tv[i].tv_tag = LDPT_GET_INPUT_FILE;
+  tv[i].tv_u.tv_get_input_file = get_input_file;
+
+  ++i;
+  tv[i].tv_tag = LDPT_RELEASE_INPUT_FILE;
+  tv[i].tv_u.tv_release_input_file = release_input_file;
+
   ++i;
   tv[i].tv_tag = LDPT_GET_SYMBOLS;
   tv[i].tv_u.tv_get_symbols = get_symbols;
@@ -182,8 +194,8 @@ Plugin::load()
   tv[i].tv_u.tv_add_input_file = add_input_file;
 
   ++i;
-  tv[i].tv_tag = LDPT_MESSAGE;
-  tv[i].tv_u.tv_message = message;
+  tv[i].tv_tag = LDPT_ADD_INPUT_LIBRARY;
+  tv[i].tv_u.tv_add_input_library = add_input_library;
 
   ++i;
   tv[i].tv_tag = LDPT_NULL;
@@ -194,7 +206,7 @@ Plugin::load()
   // Call the onload entry point.
   (*onload)(tv);
 
-  delete tv;
+  delete[] tv;
 #endif // ENABLE_PLUGINS
 }
 
@@ -228,8 +240,14 @@ Plugin::all_symbols_read()
 inline void
 Plugin::cleanup()
 {
-  if (this->cleanup_handler_ != NULL)
-    (*this->cleanup_handler_)();
+  if (this->cleanup_handler_ != NULL && !this->cleanup_done_)
+    {
+      // Set this flag before calling to prevent a recursive plunge
+      // in the event that a plugin's cleanup handler issues a
+      // fatal error.
+      this->cleanup_done_ = true;
+      (*this->cleanup_handler_)();
+    }
 }
 
 // Plugin_manager methods.
@@ -282,14 +300,13 @@ Plugin_manager::claim_file(Input_file* input_file, off_t offset,
     {
       if ((*this->current_)->claim_file(&this->plugin_input_file_))
         {
-          if (this->objects_.size() <= handle)
-            {
-              gold_error(_("%s: plugin claimed the file "
-                           "but did not provide any symbols"),
-                         this->plugin_input_file_.name);
-              return NULL;
-            }
-          return this->objects_[handle];
+          if (this->objects_.size() > handle)
+            return this->objects_[handle];
+
+          // If the plugin claimed the file but did not call the
+          // add_symbols callback, we need to create the Pluginobj now.
+          Pluginobj* obj = this->make_plugin_object(handle);
+          return obj;
         }
     }
 
@@ -299,7 +316,7 @@ Plugin_manager::claim_file(Input_file* input_file, off_t offset,
 // Call the all-symbols-read handlers.
 
 void
-Plugin_manager::all_symbols_read(Workqueue* workqueue,
+Plugin_manager::all_symbols_read(Workqueue* workqueue, Task* task,
                                  Input_objects* input_objects,
                                 Symbol_table* symtab, Layout* layout,
                                 Dirsearch* dirpath, Mapfile* mapfile,
@@ -307,6 +324,7 @@ Plugin_manager::all_symbols_read(Workqueue* workqueue,
 {
   this->in_replacement_phase_ = true;
   this->workqueue_ = workqueue;
+  this->task_ = task;
   this->input_objects_ = input_objects;
   this->symtab_ = symtab;
   this->layout_ = layout;
@@ -322,6 +340,19 @@ Plugin_manager::all_symbols_read(Workqueue* workqueue,
   *last_blocker = this->this_blocker_;
 }
 
+// Layout deferred objects.
+
+void
+Plugin_manager::layout_deferred_objects()
+{
+  Deferred_layout_list::iterator obj;
+
+  for (obj = this->deferred_layout_objects_.begin();
+       obj != this->deferred_layout_objects_.end();
+       ++obj)
+    (*obj)->layout_deferred_sections(this->layout_);
+}
+
 // Call the cleanup handlers.
 
 void
@@ -344,25 +375,66 @@ Plugin_manager::make_plugin_object(unsigned int handle)
     return NULL;
 
   Pluginobj* obj = make_sized_plugin_object(this->input_file_,
-                                            this->plugin_input_file_.offset);
+                                            this->plugin_input_file_.offset,
+                                            this->plugin_input_file_.filesize);
   this->objects_.push_back(obj);
   return obj;
 }
 
+// Get the input file information with an open (possibly re-opened)
+// file descriptor.
+
+ld_plugin_status
+Plugin_manager::get_input_file(unsigned int handle,
+                               struct ld_plugin_input_file *file)
+{
+  Pluginobj* obj = this->object(handle);
+  if (obj == NULL)
+    return LDPS_BAD_HANDLE;
+
+  obj->lock(this->task_);
+  file->name = obj->filename().c_str();
+  file->fd = obj->descriptor();
+  file->offset = obj->offset();
+  file->filesize = obj->filesize();
+  file->handle = reinterpret_cast<void*>(handle);
+  return LDPS_OK;
+}
+
+// Release the input file.
+
+ld_plugin_status
+Plugin_manager::release_input_file(unsigned int handle)
+{
+  Pluginobj* obj = this->object(handle);
+  if (obj == NULL)
+    return LDPS_BAD_HANDLE;
+
+  obj->unlock(this->task_);
+  return LDPS_OK;
+}
+
 // Add a new input file.
 
 ld_plugin_status
-Plugin_manager::add_input_file(char *pathname)
+Plugin_manager::add_input_file(char *pathname, bool is_lib)
 {
-  Input_file_argument file(pathname, false, "", false, this->options_);
+  Input_file_argument file(pathname,
+                           (is_lib
+                            ? Input_file_argument::INPUT_FILE_TYPE_LIBRARY
+                            : Input_file_argument::INPUT_FILE_TYPE_FILE),
+                           "", false, this->options_);
   Input_argument* input_argument = new Input_argument(file);
   Task_token* next_blocker = new Task_token(true);
   next_blocker->add_blocker();
-  this->workqueue_->queue_soon(new Read_symbols(this->options_,
-                                                this->input_objects_,
+  if (this->layout_->incremental_inputs())
+    gold_error(_("input files added by plug-ins in --incremental mode not "
+                "supported yet"));
+  this->workqueue_->queue_soon(new Read_symbols(this->input_objects_,
                                                 this->symtab_,
                                                 this->layout_,
                                                 this->dirpath_,
+                                               0,
                                                 this->mapfile_,
                                                 input_argument,
                                                 NULL,
@@ -375,10 +447,25 @@ Plugin_manager::add_input_file(char *pathname)
 // Class Pluginobj.
 
 Pluginobj::Pluginobj(const std::string& name, Input_file* input_file,
-                     off_t offset)
+                     off_t offset, off_t filesize)
   : Object(name, input_file, false, offset),
-    nsyms_(0), syms_(NULL), symbols_(), comdat_map_()
+    nsyms_(0), syms_(NULL), symbols_(), filesize_(filesize), comdat_map_()
+{
+}
+
+// Return TRUE if a defined symbol might be reachable from outside the
+// universe of claimed objects.
+
+static inline bool
+is_visible_from_outside(Symbol* lsym)
 {
+  if (lsym->in_real_elf())
+    return true;
+  if (parameters->options().relocatable())
+    return true;
+  if (parameters->options().export_dynamic() || parameters->options().shared())
+    return lsym->is_externally_visible();
+  return false;
 }
 
 // Get symbol resolution info.
@@ -386,7 +473,7 @@ Pluginobj::Pluginobj(const std::string& name, Input_file* input_file,
 ld_plugin_status
 Pluginobj::get_symbol_resolution_info(int nsyms, ld_plugin_symbol* syms) const
 {
-  if (this->nsyms_ == 0)
+  if (nsyms > this->nsyms_)
     return LDPS_NO_SYMS;
   for (int i = 0; i < nsyms; i++)
     {
@@ -417,7 +504,7 @@ Pluginobj::get_symbol_resolution_info(int nsyms, ld_plugin_symbol* syms) const
           if (lsym->source() != Symbol::FROM_OBJECT)
             res = LDPR_PREEMPTED_REG;
           else if (lsym->object() == static_cast<const Object*>(this))
-            res = (lsym->in_real_elf()
+            res = (is_visible_from_outside(lsym)
                    ? LDPR_PREVAILING_DEF
                    : LDPR_PREVAILING_DEF_IRONLY);
           else
@@ -442,7 +529,9 @@ Pluginobj::include_comdat_group(std::string comdat_key, Layout* layout)
   // If this is the first time we've seen this comdat key, ask the
   // layout object whether it should be included.
   if (ins.second)
-    ins.first->second = layout->add_comdat(NULL, 1, comdat_key, true);
+    ins.first->second = layout->find_or_add_kept_section(comdat_key,
+                                                        NULL, 0, true,
+                                                        true, NULL);
 
   return ins.first->second;
 }
@@ -453,8 +542,9 @@ template<int size, bool big_endian>
 Sized_pluginobj<size, big_endian>::Sized_pluginobj(
     const std::string& name,
     Input_file* input_file,
-    off_t offset)
-  : Pluginobj(name, input_file, offset)
+    off_t offset,
+    off_t filesize)
+  : Pluginobj(name, input_file, offset, filesize)
 {
 }
 
@@ -479,17 +569,10 @@ Sized_pluginobj<size, big_endian>::do_layout(Symbol_table*, Layout*,
 
 // Add the symbols to the symbol table.
 
-template<int size, bool big_endian>
-void
-Sized_pluginobj<size, big_endian>::do_add_symbols(Symbol_table*,
-                                                  Read_symbols_data*)
-{
-  gold_unreachable();
-}
-
 template<int size, bool big_endian>
 void
 Sized_pluginobj<size, big_endian>::do_add_symbols(Symbol_table* symtab,
+                                                  Read_symbols_data*,
                                                   Layout* layout)
 {
   const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
@@ -621,6 +704,16 @@ Sized_pluginobj<size, big_endian>::do_section_flags(unsigned int)
   return 0;
 }
 
+// Return section entsize.  Not used for plugin objects.
+
+template<int size, bool big_endian>
+uint64_t
+Sized_pluginobj<size, big_endian>::do_section_entsize(unsigned int)
+{
+  gold_unreachable();
+  return 0;
+}
+
 // Return section address.  Not used for plugin objects.
 
 template<int size, bool big_endian>
@@ -691,55 +784,26 @@ Sized_pluginobj<size, big_endian>::do_get_global_symbol_counts(const Symbol_tabl
   gold_unreachable();
 }
 
-// Class Add_plugin_symbols.
-
-Add_plugin_symbols::~Add_plugin_symbols()
-{
-  if (this->this_blocker_ != NULL)
-    delete this->this_blocker_;
-  // next_blocker_ is deleted by the task associated with the next
-  // input file.
-}
-
-// We are blocked by this_blocker_.  We block next_blocker_.  We also
-// lock the file.
+// Get symbols.  Not used for plugin objects.
 
-Task_token*
-Add_plugin_symbols::is_runnable()
-{
-  if (this->this_blocker_ != NULL && this->this_blocker_->is_blocked())
-    return this->this_blocker_;
-  if (this->obj_->is_locked())
-    return this->obj_->token();
-  return NULL;
-}
-
-void
-Add_plugin_symbols::locks(Task_locker* tl)
-{
-  tl->add(this, this->next_blocker_);
-  tl->add(this, this->obj_->token());
-}
-
-// Add the symbols in the object to the symbol table.
-
-void
-Add_plugin_symbols::run(Workqueue*)
+template<int size, bool big_endian>
+const Object::Symbols*
+Sized_pluginobj<size, big_endian>::do_get_global_symbols() const
 {
-  this->obj_->add_symbols(this->symtab_, this->layout_);
+  gold_unreachable();
 }
 
-// Class Plugin_cleanup.  This task calls the plugin cleanup hooks once all
-// replacement files have been added.
+// Class Plugin_finish.  This task runs after all replacement files have
+// been added.  It calls each plugin's cleanup handler.
 
-class Plugin_cleanup : public Task
+class Plugin_finish : public Task
 {
  public:
-  Plugin_cleanup(Task_token* this_blocker, Task_token* next_blocker)
+  Plugin_finish(Task_token* this_blocker, Task_token* next_blocker)
     : this_blocker_(this_blocker), next_blocker_(next_blocker)
   { }
 
-  ~Plugin_cleanup()
+  ~Plugin_finish()
   {
     if (this->this_blocker_ != NULL)
       delete this->this_blocker_;
@@ -759,11 +823,15 @@ class Plugin_cleanup : public Task
 
   void
   run(Workqueue*)
-  { parameters->options().plugins()->cleanup(); }
+  {
+    Plugin_manager* plugins = parameters->options().plugins();
+    gold_assert(plugins != NULL);
+    plugins->cleanup();
+  }
 
   std::string
   get_name() const
-  { return "Plugin_cleanup"; }
+  { return "Plugin_finish"; }
 
  private:
   Task_token* this_blocker_;
@@ -794,29 +862,22 @@ Plugin_hook::locks(Task_locker*)
 {
 }
 
-// Run a Plugin_hook task.
-
-void
-Plugin_hook::run(Workqueue* workqueue)
-{
-  this->do_plugin_hook(workqueue);
-}
-
 // Run the "all symbols read" plugin hook.
 
 void
-Plugin_hook::do_plugin_hook(Workqueue* workqueue)
+Plugin_hook::run(Workqueue* workqueue)
 {
   gold_assert(this->options_.has_plugins());
   this->options_.plugins()->all_symbols_read(workqueue,
+                                             this,
                                              this->input_objects_,
                                              this->symtab_,
                                              this->layout_,
                                              this->dirpath_,
                                              this->mapfile_,
                                              &this->this_blocker_);
-  workqueue->queue_soon(new Plugin_cleanup(this->this_blocker_,
-                                          this->next_blocker_));
+  workqueue->queue_soon(new Plugin_finish(this->this_blocker_,
+                                         this->next_blocker_));
 }
 
 // The C interface routines called by the plugins.
@@ -867,6 +928,29 @@ add_symbols(void* handle, int nsyms, const ld_plugin_symbol *syms)
   return LDPS_OK;
 }
 
+// Get the input file information with an open (possibly re-opened)
+// file descriptor.
+
+static enum ld_plugin_status
+get_input_file(const void *handle, struct ld_plugin_input_file *file)
+{
+  gold_assert(parameters->options().has_plugins());
+  unsigned int obj_index =
+      static_cast<unsigned int>(reinterpret_cast<intptr_t>(handle));
+  return parameters->options().plugins()->get_input_file(obj_index, file);
+}
+
+// Release the input file.
+
+static enum ld_plugin_status
+release_input_file(const void *handle)
+{
+  gold_assert(parameters->options().has_plugins());
+  unsigned int obj_index =
+      static_cast<unsigned int>(reinterpret_cast<intptr_t>(handle));
+  return parameters->options().plugins()->release_input_file(obj_index);
+}
+
 // Get the symbol resolution info for a plugin-claimed input file.
 
 static enum ld_plugin_status
@@ -886,13 +970,22 @@ static enum ld_plugin_status
 add_input_file(char *pathname)
 {
   gold_assert(parameters->options().has_plugins());
-  return parameters->options().plugins()->add_input_file(pathname);
+  return parameters->options().plugins()->add_input_file(pathname, false);
+}
+
+// Add a new (real) library required by a plugin.
+
+static enum ld_plugin_status
+add_input_library(char *pathname)
+{
+  gold_assert(parameters->options().has_plugins());
+  return parameters->options().plugins()->add_input_file(pathname, true);
 }
 
 // Issue a diagnostic message from a plugin.
 
 static enum ld_plugin_status
-message(int level, char * format, ...)
+message(int level, const char * format, ...)
 {
   va_list args;
   va_start(args, format);
@@ -923,22 +1016,19 @@ message(int level, char * format, ...)
 // Allocate a Pluginobj object of the appropriate size and endianness.
 
 static Pluginobj*
-make_sized_plugin_object(Input_file* input_file, off_t offset)
+make_sized_plugin_object(Input_file* input_file, off_t offset, off_t filesize)
 {
-  Target* target;
   Pluginobj* obj = NULL;
 
-  if (parameters->target_valid())
-    target = const_cast<Target*>(&parameters->target());
-  else
-    target = const_cast<Target*>(&parameters->default_target());
+  parameters_force_valid_target();
+  const Target& target(parameters->target());
 
-  if (target->get_size() == 32)
+  if (target.get_size() == 32)
     {
-      if (target->is_big_endian())
+      if (target.is_big_endian())
 #ifdef HAVE_TARGET_32_BIG
         obj = new Sized_pluginobj<32, true>(input_file->filename(),
-                                            input_file, offset);
+                                            input_file, offset, filesize);
 #else
         gold_error(_("%s: not configured to support "
                     "32-bit big-endian object"),
@@ -947,19 +1037,19 @@ make_sized_plugin_object(Input_file* input_file, off_t offset)
       else
 #ifdef HAVE_TARGET_32_LITTLE
         obj = new Sized_pluginobj<32, false>(input_file->filename(),
-                                             input_file, offset);
+                                             input_file, offset, filesize);
 #else
         gold_error(_("%s: not configured to support "
                     "32-bit little-endian object"),
                   input_file->filename().c_str());
 #endif
     }
-  else if (target->get_size() == 64)
+  else if (target.get_size() == 64)
     {
-      if (target->is_big_endian())
+      if (target.is_big_endian())
 #ifdef HAVE_TARGET_64_BIG
         obj = new Sized_pluginobj<64, true>(input_file->filename(),
-                                            input_file, offset);
+                                            input_file, offset, filesize);
 #else
         gold_error(_("%s: not configured to support "
                     "64-bit big-endian object"),
@@ -968,7 +1058,7 @@ make_sized_plugin_object(Input_file* input_file, off_t offset)
       else
 #ifdef HAVE_TARGET_64_LITTLE
         obj = new Sized_pluginobj<64, false>(input_file->filename(),
-                                             input_file, offset);
+                                             input_file, offset, filesize);
 #else
         gold_error(_("%s: not configured to support "
                     "64-bit little-endian object"),
@@ -977,7 +1067,6 @@ make_sized_plugin_object(Input_file* input_file, off_t offset)
     }
 
   gold_assert(obj != NULL);
-  obj->set_target(target);
   return obj;
 }