[PATCH] gold: create .gnu_incremental_inputs entries for inputs

Mikolaj Zalewski mikolajz@google.com
Mon May 11 22:14:00 GMT 2009


  This patch stores some data for inputs entries. It does not yet
create entries for archive members, doesn't store the extra type
specific information nor save the timestamp, but all this can be fixed
later if the approach in this patch is good. A file added by a plugin
is currently  an error - I will look into plug-ins later.
  Incremental link needs more information about input element that I
have found gold to save, so I'm storing the extra information in a map
in Incremental_inputs. It could be added as extra fields to
Input_argument, but then the structure wouldn't be immutable anymore
and there would be a slight memory overhead for non-incremental
builds.

2009-05-11  Mikolaj Zalewski  <mikolajz@google.com>

	* gold.h (Incremental_argument_list): remove (invalid) forward declaration
	* incremental.cc (Incremental_inputs::report_achive): new method
	(Incremental_inputs::report_object): new method
	(Incremental_inputs::report_script): new method
	(Incremental_inputs::finalize_inputs): new method
	(Incremental_inputs::finalize): call finalize_inputs()
	(Incremental_inputs::sized_create_incremental_inputs_section_data):
create inputs entries
	* incremental.h (Incremental_input_type): new enum
	(Incremental_inputs::Incremental_input): initialize new fields
	(Incremental_inputs::report_inputs): new method
	(Incremental_inputs::report_achive): new method
	(Incremental_inputs::report_object): new method
	(Incremental_inputs::report_script): new method
	(Incremental_inputs::finalize_inputs): new method
	(Incremental_inputs::Input_info): new struct
	(Incremental_inputs::Input_info_map): new typedef
	(Incremental_inputs::lock_): new field
	(Incremental_inputs::Inputs_): new field
	(Incremental_inputs::Inputs_map): new field
	* main.cc (main): call Incremental_input::report_inputs
	* options.h (Input_argument_list): typedef moved from Input_arguments
	(Input_file_group::Files): remove, use ::Input_argument_list
	(Input_file_group::Input_argument_list): remove, use ::Input_argument_list
	* plugin.cc (Plugin_manager::add_input_file): add error in incremental build
	* read_syms.cc (do_read_syms): call Incremental_input::report_* functions
	* script.cc (read_input_script): call Incremental_input::report_script
	* script.h (Script_info): new class
-------------- next part --------------
From b0e242153d4668bb1876c3714beb0d1ad23a3322 Mon Sep 17 00:00:00 2001
From: Mikolaj Zalewski <mikolajz@puchatek.dom>
Date: Mon, 11 May 2009 23:35:19 +0200
Subject: [PATCH] gold: create .gnu_incremental_inputs entries for inputs

---
 gold/gold.h         |    3 +-
 gold/incremental.cc |  145 ++++++++++++++++++++++++++++++++++++++++++++++++---
 gold/incremental.h  |   91 ++++++++++++++++++++++++++++++--
 gold/main.cc        |    5 ++-
 gold/options.h      |    8 ++--
 gold/plugin.cc      |    3 +
 gold/readsyms.cc    |   14 ++++-
 gold/script.cc      |    7 +++
 gold/script.h       |   21 +++++++
 9 files changed, 275 insertions(+), 22 deletions(-)

diff --git a/gold/gold.h b/gold/gold.h
index efebda8..bf014e8 100644
--- a/gold/gold.h
+++ b/gold/gold.h
@@ -148,7 +148,6 @@ namespace gold
 
 class General_options;
 class Command_line;
-class Input_argument_list;
 class Dirsearch;
 class Input_objects;
 class Mapfile;
@@ -293,7 +292,7 @@ queue_initial_tasks(const General_options&,
 
 // Queue up the set of tasks to be done before
 // the middle set of tasks.  Only used when garbage
-// collection is to be done. 
+// collection is to be done.
 extern void
 queue_middle_gc_tasks(const General_options&,
                       const Task*,
diff --git a/gold/incremental.cc b/gold/incremental.cc
index 7e99e1e..70c3a50 100644
--- a/gold/incremental.cc
+++ b/gold/incremental.cc
@@ -86,7 +86,7 @@ class Incremental_inputs_header_write
   Incremental_inputs_header_write(unsigned char *p)
     : p_(reinterpret_cast<internal::Incremental_inputs_header_data*>(p))
   { }
-  
+
   static const int data_size = sizeof(internal::Incremental_inputs_header_data);
 
   void
@@ -179,12 +179,114 @@ Incremental_inputs::report_command_line(int argc, const char* const* argv)
   this->strtab_->add(args.c_str(), true, &this->command_line_key_);
 }
 
+// Record that the input argument INPUT is an achive ARCHIVE. This is called
+// by Read_symbols after finding out the type of the file.
+
+void
+Incremental_inputs::report_archive(const Input_argument* input,
+                                   Archive* archive)
+{
+  Hold_lock hl(*this->lock_);
+
+  Input_info info;
+  info.type = INCREMENTAL_INPUT_ARCHIVE;
+  info.archive = archive;
+  inputs_map_.insert(std::make_pair(input, info));
+}
+
+// Record that the input argument INPUT is an object OBJ. This is called
+// by Read_symbols after finding out the type of the file.
+
+void
+Incremental_inputs::report_object(const Input_argument* input,
+                                  Object* obj)
+{
+  Hold_lock hl(*this->lock_);
+
+  Input_info info;
+  info.type = obj->is_dynamic() ? INCREMENTAL_INPUT_SHARED_LIBRARY
+                                : INCREMENTAL_INPUT_OBJECT;
+  info.object = obj;
+  inputs_map_.insert(std::make_pair(input, info));
+}
+
+// Record that the input argument INPUT is an script SCRIPT. This is called
+// by read_script after parsing the script and reading the list of inputs added
+// by this script.
+
+void
+Incremental_inputs::report_script(const Input_argument* input,
+                                  Script_info* script)
+{
+  Hold_lock hl(*this->lock_);
+
+  Input_info info;
+  info.type = INCREMENTAL_INPUT_SCRIPT;
+  info.script = script;
+  inputs_map_.insert(std::make_pair(input, info));
+}
+
+// Compute indexes in the order in which the inputs should appear in 
+// .gnu_incremental_inputs. This needs to be done after all the scripts are
+// parsed. The function is first called for the command line
+// inputs arguments and may call itself recursively for e.g. a list of
+// elements of a group or a list of inputs added by a script.
+// The [BEGIN; END) interval to analyze and *INDEX is the current value of the
+// index (that will be updated).
+
+void
+Incremental_inputs::finalize_inputs(
+    Input_argument_list::const_iterator begin,
+    Input_argument_list::const_iterator end,
+    unsigned int* index)
+{
+  for (Input_argument_list::const_iterator p = begin; p != end; ++p)
+    {
+      if (p->is_group())
+        {
+          finalize_inputs(p->group()->begin(), p->group()->end(), index);
+          continue;
+        }
+
+      Inputs_info_map::iterator it = inputs_map_.find(&(*p));
+      // TODO: turn it into an assert when the code will be more stable.
+      if (it == inputs_map_.end())
+        {
+          gold_error("internal error: %s: incremental build info not provided",
+              (p->is_file() ? p->file().name() : "[group]"));
+          continue;
+        }
+      Input_info* info = &it->second;
+      info->index = *index;
+      (*index)++;
+      this->strtab_->add(p->file().name(), false, &info->filename_key);
+      if (info->type == INCREMENTAL_INPUT_SCRIPT)
+        {
+          finalize_inputs(info->script->inputs()->begin(),
+                          info->script->inputs()->end(),
+                          index);
+        }
+    }
+}
+
+
 // Finalize the incremental link information.  Called from
 // Layout::finalize.
 
 void
 Incremental_inputs::finalize()
 {
+  unsigned int index = 0;
+  finalize_inputs(this->inputs_->begin(), this->inputs_->end(), &index);
+
+  // Sanity check.
+  for (Inputs_info_map::const_iterator p = inputs_map_.begin();
+       p != inputs_map_.end();
+       ++p)
+    {
+      gold_assert(p->second.filename_key != 0);
+    }
+
   this->strtab_->set_string_offsets();
 }
 
@@ -213,7 +315,7 @@ Incremental_inputs::create_incremental_inputs_section_data()
 #endif
     default:
       gold_unreachable();
-    }  
+    }
 }
 
 // Sized creation of .gnu_incremental_inputs section.
@@ -221,20 +323,47 @@ Incremental_inputs::create_incremental_inputs_section_data()
 template<int size, bool big_endian>
 Output_section_data*
 Incremental_inputs::sized_create_inputs_section_data()
-{  
-  unsigned int sz =
+{
+  const int entry_size =
+      Incremental_inputs_entry_write<size, big_endian>::data_size;
+  const int header_size =
       Incremental_inputs_header_write<size, big_endian>::data_size;
+
+  unsigned int sz = header_size + entry_size * this->inputs_map_.size();
   unsigned char* buffer = new unsigned char[sz];
+  unsigned char* inputs_base = buffer + header_size;
+
   Incremental_inputs_header_write<size, big_endian> header_writer(buffer);
-  
   gold_assert(this->command_line_key_ > 0);
   int cmd_offset = this->strtab_->get_offset_from_key(this->command_line_key_);
-  
+
   header_writer.put_version(INCREMENTAL_LINK_VERSION);
-  header_writer.put_input_file_count(0);   // TODO: store input files data.
+  header_writer.put_input_file_count(this->inputs_map_.size());
   header_writer.put_command_line_offset(cmd_offset);
   header_writer.put_reserved(0);
-  
+
+  for (Inputs_info_map::const_iterator it = this->inputs_map_.begin();
+       it != this->inputs_map_.end();
+       ++it)
+    {
+      gold_assert(it->second.index < this->inputs_map_.size());
+
+      unsigned char* entry_buffer =
+          inputs_base + it->second.index * entry_size;
+      Incremental_inputs_entry_write<size, big_endian> entry(entry_buffer);
+      int filename_offset =
+          this->strtab_->get_offset_from_key(it->second.filename_key);
+      entry.put_filename_offset(filename_offset);
+      // TODO: add per input data and timestamp. Currently we store an
+      // out-of-bounds offset for future version of gold to reject such an
+      // incremental_inputs section.
+      entry.put_data_offset(0xffffffff);
+      entry.put_timestamp_sec(0);
+      entry.put_timestamp_usec(0);
+      entry.put_input_type(it->second.type);
+      entry.put_reserved(0);
+    }
+
   return new Output_data_const_buffer(buffer, sz, 8,
       "** incremental link inputs list");
 }
diff --git a/gold/incremental.h b/gold/incremental.h
index dd9ebc5..618a9b3 100644
--- a/gold/incremental.h
+++ b/gold/incremental.h
@@ -23,6 +23,7 @@
 #ifndef GOLD_INCREMENTAL_H
 #define GOLD_INCREMENTAL_H
 
+#include <map>
 #include <vector>
 
 #include "stringpool.h"
@@ -37,20 +38,49 @@ class Incremental_inputs_checker;
 class Object;
 class Output_section_data;
 
+// Incremental input type as stored in .gnu_incremental_inputs.
+
+enum Incremental_input_type
+{
+  INCREMENTAL_INPUT_INVALID = 0,
+  INCREMENTAL_INPUT_OBJECT = 1,
+  INCREMENTAL_INPUT_ARCHIVE = 2,
+  INCREMENTAL_INPUT_SHARED_LIBRARY = 3,
+  INCREMENTAL_INPUT_SCRIPT = 4
+};
+
 // This class contains the information needed during an incremental
 // build about the inputs necessary to build the .gnu_incremental_inputs.
 class Incremental_inputs
 {
  public:
   Incremental_inputs()
-    : command_line_key_(0), strtab_(new Stringpool())
+    : lock_(new Lock()), inputs_(NULL), command_line_key_(0),
+      strtab_(new Stringpool())
   { }
   ~Incremental_inputs() { delete this->strtab_; }
 
   // Record the command line.
   void
   report_command_line(int argc, const char* const* argv);
-  
+
+  // Record the input arguments obtained from parsing the command line.
+  void
+  report_inputs(const Input_arguments& inputs)
+  { this->inputs_ = &inputs; }
+
+  // Record that the input argument INPUT is an archive ARCHIVE.
+  void
+  report_archive(const Input_argument* input, Archive* archive);
+
+  // Record that the input argument INPUT is to an object OBJ.
+  void
+  report_object(const Input_argument* input, Object* obj);
+
+  // Record that the input argument INPUT is to an script SCRIPT.
+  void
+  report_script(const Input_argument* input, Script_info* script);
+
   // Prepare for layout.  Called from Layout::finalize.
   void
   finalize();
@@ -58,8 +88,8 @@ class Incremental_inputs
   // Create the content of the .gnu_incremental_inputs section.
   Output_section_data*
   create_incremental_inputs_section_data();
-  
-  // Return the .gnu_incremental_strtab stringpool. 
+
+  // Return the .gnu_incremental_strtab stringpool.
   Stringpool*
   get_stringpool()
   { return this->strtab_; }
@@ -68,7 +98,58 @@ class Incremental_inputs
   // Code for each of the four possible variants of create_inputs_section_data.
   template<int size, bool big_endian>
   Output_section_data*
-  sized_create_inputs_section_data();  
+  sized_create_inputs_section_data();
+
+  // Compute indexes in the order in which the inputs should appear in 
+  // .gnu_incremental_inputs and put file names to the stringtable. This needs
+  // to be done after all the scripts are parsed.
+
+  void
+  finalize_inputs(
+      Input_argument_list::const_iterator begin,
+      Input_argument_list::const_iterator end,
+      unsigned int* index);
+
+  // Additional data about an input needed for an incremental link. None
+  // of these pointers if owned by the structure.
+  struct Input_info
+  {
+    Input_info()
+      : type(INCREMENTAL_INPUT_INVALID), archive(NULL), object(NULL),
+        script(NULL), filename_key(0), index(0)
+    { }
+
+    // Type of the file pointed by this argument.
+    Incremental_input_type type;
+
+    // Present if type == INCREMENTAL_INPUT_ARCHIVE.
+    Archive* archive;
+
+    // Present if type == INCREMENTAL_INPUT_OBJECT or
+    // INCREMENTAL_INPUT_SHARED_LIBRARY.
+    Object* object;
+
+    // Present if type == INCREMENTAL_INPUT_SCRIPT.
+    Script_info* script;
+
+    // Key of the filename string in the section stringtable.
+    Stringpool::Key filename_key;
+
+    // Position of the entry information in the output section.
+    unsigned int index;
+  };
+
+  typedef std::map<const Input_argument*, Input_info> Inputs_info_map;
+
+  // A lock guarding access to inputs_ during the first phase of linking, when
+  // report_ function may be called from multiple threads.
+  Lock* lock_;
+  
+  // The list of input arguments obtained from parsing the command line.
+  const Input_arguments* inputs_;
+
+  // A map containing additional information about the input elements.
+  Inputs_info_map inputs_map_;
 
   // The key of the command line string in the string pool.
   Stringpool::Key command_line_key_;
diff --git a/gold/main.cc b/gold/main.cc
index c2d3c30..84b4ae9 100644
--- a/gold/main.cc
+++ b/gold/main.cc
@@ -221,7 +221,10 @@ main(int argc, char** argv)
 		&command_line.script_options());
 
   if (layout.incremental_inputs() != NULL)
-    layout.incremental_inputs()->report_command_line(argc, argv);
+    {
+      layout.incremental_inputs()->report_command_line(argc, argv);
+      layout.incremental_inputs()->report_inputs(command_line.inputs());
+    }
 
   // Get the search path from the -L options.
   Dirsearch search_path;
diff --git a/gold/options.h b/gold/options.h
index 904743e..b811332 100644
--- a/gold/options.h
+++ b/gold/options.h
@@ -1275,14 +1275,15 @@ class Input_argument
   Input_file_group* group_;
 };
 
+typedef std::vector<Input_argument> Input_argument_list;
+
 // 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<Input_argument> Files;
-  typedef Files::const_iterator const_iterator;
+  typedef Input_argument_list::const_iterator const_iterator;
 
   Input_file_group()
     : files_()
@@ -1304,7 +1305,7 @@ class Input_file_group
   { return this->files_.end(); }
 
  private:
-  Files files_;
+  Input_argument_list files_;
 };
 
 // A list of files from the command line or a script.
@@ -1312,7 +1313,6 @@ class Input_file_group
 class Input_arguments
 {
  public:
-  typedef std::vector<Input_argument> Input_argument_list;
   typedef Input_argument_list::const_iterator const_iterator;
 
   Input_arguments()
diff --git a/gold/plugin.cc b/gold/plugin.cc
index 3c4d4ae..acd0137 100644
--- a/gold/plugin.cc
+++ b/gold/plugin.cc
@@ -407,6 +407,9 @@ Plugin_manager::add_input_file(char *pathname)
   Input_argument* input_argument = new Input_argument(file);
   Task_token* next_blocker = new Task_token(true);
   next_blocker->add_blocker();
+  if (this->layout_->incremental_inputs())
+    gold_error(_("Input files added by plug-ins in --incremental mode not "
+        "supported yet.\n"));
   this->workqueue_->queue_soon(new Read_symbols(this->input_objects_,
                                                 this->symtab_,
                                                 this->layout_,
diff --git a/gold/readsyms.cc b/gold/readsyms.cc
index b6da88d..25e1e7f 100644
--- a/gold/readsyms.cc
+++ b/gold/readsyms.cc
@@ -33,6 +33,8 @@
 #include "script.h"
 #include "readsyms.h"
 #include "plugin.h"
+#include "layout.h"
+#include "incremental.h"
 
 namespace gold
 {
@@ -199,7 +201,7 @@ Read_symbols::do_read_symbols(Workqueue* workqueue)
     {
       bool is_thin_archive
           = memcmp(ehdr, Archive::armagt, Archive::sarmag) == 0;
-      if (is_thin_archive 
+      if (is_thin_archive
           || memcmp(ehdr, Archive::armag, Archive::sarmag) == 0)
 	{
 	  // This is an archive.
@@ -207,7 +209,11 @@ Read_symbols::do_read_symbols(Workqueue* workqueue)
 				      input_file, is_thin_archive,
 				      this->dirpath_, this);
 	  arch->setup();
-	  
+
+	  if (this->layout_->incremental_inputs())
+	    this->layout_->incremental_inputs()->report_archive(
+	        this->input_argument_, arch);
+
 	  // Unlock the archive so it can be used in the next task.
 	  arch->unlock(this);
 
@@ -280,6 +286,10 @@ Read_symbols::do_read_symbols(Workqueue* workqueue)
       Read_symbols_data* sd = new Read_symbols_data;
       obj->read_symbols(sd);
 
+      if (this->layout_->incremental_inputs())
+        this->layout_->incremental_inputs()->report_object(
+            this->input_argument_, obj);
+
       // Opening the file locked it, so now we need to unlock it.  We
       // need to unlock it before queuing the Add_symbols task,
       // because the workqueue doesn't know about our lock on the
diff --git a/gold/script.cc b/gold/script.cc
index 30b4e3c..7f45bc4 100644
--- a/gold/script.cc
+++ b/gold/script.cc
@@ -43,6 +43,7 @@
 #include "target-select.h"
 #include "script.h"
 #include "script-c.h"
+#include "incremental.h"
 
 namespace gold
 {
@@ -1414,6 +1415,12 @@ read_input_script(Workqueue* workqueue, Symbol_table* symtab, Layout* layout,
       this_blocker = nb;
     }
 
+  if (layout->incremental_inputs()) {
+    // Like new Read_symbols(...) above, we rely on close.inputs() getting
+    // leaked by closure.
+    Script_info* info = new Script_info(closure.inputs());
+    layout->incremental_inputs()->report_script(input_argument, info);
+  }
   *used_next_blocker = true;
 
   return true;
diff --git a/gold/script.h b/gold/script.h
index e4554d0..781d24d 100644
--- a/gold/script.h
+++ b/gold/script.h
@@ -45,6 +45,7 @@ class Symbol_table;
 class Layout;
 class Mapfile;
 class Input_argument;
+class Input_arguments;
 class Input_objects;
 class Input_group;
 class Input_file;
@@ -382,6 +383,26 @@ class Script_options
   Script_sections script_sections_;
 };
 
+// Information about a script input that will persist during the whole linker
+// run. Needed only during an incremental build to retrieve the input files
+// added by this script.
+
+class Script_info
+{
+ public:
+  Script_info(Input_arguments* inputs)
+    : inputs_(inputs)
+  { }
+
+  // Returns the input files included because of this script.
+  Input_arguments*
+  inputs()
+  { return inputs_; }
+
+ private:
+  Input_arguments* inputs_;
+};
+
 // FILE was found as an argument on the command line, but was not
 // recognized as an ELF file.  Try to read it as a script.  Return
 // true if the file was handled.  This has to handle /usr/lib/libc.so
-- 
1.4.4.2



More information about the Binutils mailing list