This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[gold patch 1/3] Support for incremental linking: write incremental inputs, symtab, relocs


Ian,

I'm getting close to completing the incremental linker work, so it's
time to start sending you patches.  I'll start with three patches
that finish the implementation of the full incremental link --
writing the .gnu_incremental_inputs, _symtab, etc., sections.

The first of this series of three patches is based on the last patch
that Rafael sent on 11/30/09, and I've made major changes to that
patch in response to your comments, rewriting everything to use a
class derived from Output_section_data, and extending it to add the
_symtab and _relocs sections as well.  I've made some minor changes
to the data structures, and I'll update the wiki page accordingly.

The second patch adds a _got_plt section to keep track of the
allocation of GOT and PLT entries, so that we know which entries
are already allocated for globals, and which local GOT entries can
be freed for reuse when an object is replaced.

The third patch rewrites the existing incremental test case to
check more of the data recorded in the sections.

Here's the first; the next two will follow in separate messages.

-cary


elfcpp/ChangeLog:

	* elfcpp.h (enum SHT): Add SHT_GNU_INCREMENTAL_SYMTAB,
	SHT_GNU_INCREMENTAL_RELOCS.

gold/ChangeLog:

	* archive.cc: Include incremental.h.
	(Archive::Archive): Initialize incremental_info_.
	(Archive::include_member): Record archive members in incremental info.
	(Add_archive_symbols::run): Record begin and end of an archive in
	incremental info.
	(Lib_group::include_member): Record objects in incremental info.
	* archive.h (Incremental_archive_entry): Forward declaration.
	(Archive::set_incremental_info): New member function.
	(Archive::incremental_info): New member function.
	(Archive::Unused_symbol_iterator): New class.
	(Archive::unused_symbols_begin): New member function.
	(Archive::unused_symbols_end): New member function.
	(Archive::incremental_info_): New data member.
	* incremental-dump.cc (find_input_containing_global): New function.
	(dump_incremental_inputs): Dump new incremental info sections.
	* incremental.cc: Include symtab.h.
	(Output_section_incremental_inputs): New class.
	(Sized_incremental_binary::do_find_incremental_inputs_sections): Support
	new incremental info sections.
	(Sized_incremental_binary::do_check_inputs): Likewise.
	(Incremental_inputs::report_archive): Remove.
	(Incremental_inputs::report_archive_begin): New function.
	(Incremental_inputs::report_archive_end): New function.
	(Incremental_inputs::report_object): New function.
	(Incremental_inputs::finalize_inputs): Remove.
	(Incremental_inputs::report_input_section): New function.
	(Incremental_inputs::report_script): Rewrite.
	(Incremental_inputs::finalize): Do nothing but finalize string table.
	(Incremental_inputs::create_incremental_inputs_section_data): Remove.
	(Incremental_inputs::sized_create_inputs_section_data): Remove.
	(Incremental_inputs::create_data_sections): New function.
	(Incremental_inputs::relocs_entsize): New function.
	(Output_section_incremental_inputs::set_final_data_size): New function.
	(Output_section_incremental_inputs::do_write): New function.
	(Output_section_incremental_inputs::write_header): New function.
	(Output_section_incremental_inputs::write_input_files): New function.
	(Output_section_incremental_inputs::write_info_blocks): New function.
	(Output_section_incremental_inputs::write_symtab): New function.
	* incremental.h (Incremental_script_entry): Forward declaration.
	(Incremental_object_entry): Forward declaration.
	(Incremental_archive_entry): Forward declaration.
	(Incremental_inputs): Forward declaration.
	(Incremental_inputs_header_data): Remove.
	(Incremental_inputs_header): Remove.
	(Incremental_inputs_header_write): Remove.
	(Incremental_inputs_entry_data): Remove.
	(Incremental_inputs_entry): Remove.
	(Incremental_inputs_entry_write): Remove.
	(enum Incremental_input_type): Add INCREMENTAL_INPUT_ARCHIVE_MEMBER.
	(Incremental_binary::find_incremental_inputs_sections): Add parameters.
	(Incremental_binary::do_find_incremental_inputs_sections): Likewise.
	(Sized_ncremental_binary::do_find_incremental_inputs_sections):
	Likewise.
	(Incremental_input_entry): New class.
	(Incremental_script_entry): New class.
	(Incremental_object_entry): New class.
	(Incremental_archive_entry): New class.
	(Incremental_inputs::Incremental_inputs): Initialize new data members.
	(Incremental_inputs::report_inputs): Remove.
	(Incremental_inputs::report_archive): Remove.
	(Incremental_inputs::report_archive_begin): New function.
	(Incremental_inputs::report_archive_end): New function.
	(Incremental_inputs::report_object): Change prototype.
	(Incremental_inputs::report_input_section): New function.
	(Incremental_inputs::report_script): Change prototype.
	(Incremental_inputs::get_reloc_count): New function.
	(Incremental_inputs::set_reloc_count): New function.
	(Incremental_inputs::create_data_sections): New function.
	(Incremental_inputs::create_incremental_inputs_section_data): Remove.
	(Incremental_inputs::inputs_section): New function.
	(Incremental_inputs::symtab_section): New function.
	(Incremental_inputs::relocs_section): New function.
	(Incremental_inputs::get_stringpool): Add const.
	(Incremental_inputs::command_line): Add const.
	(Incremental_inputs::inputs): Remove.
	(Incremental_inputs::command_line_key): New function.
	(Incremental_inputs::input_file_count): New function.
	(Incremental_inputs::input_files): New function.
	(Incremental_inputs::relocs_entsize): New function.
	(Incremental_inputs::sized_create_inputs_section_data): Remove.
	(Incremental_inputs::finalize_inputs): Remove.
	(Incremental_inputs::Input_info): Remove.
	(Incremental_inputs::lock_): Remove.
	(Incremental_inputs::inputs_): Change type.
	(Incremental_inputs::inputs_map_): Remove.
	(Incremental_inputs::current_object_entry_): New data member.
	(Incremental_inputs::inputs_section_): New data member.
	(Incremental_inputs::symtab_section_): New data member.
	(Incremental_inputs::relocs_section_): New data member.
	(Incremental_inputs::reloc_count_): New data member.
	(Incremental_inputs_reader): New class.
	(Incremental_symtab_reader): New class.
	(Incremental_relocs_reader): New class.
	* layout.cc (Layout::finalize): Move finalization of incremental info
	and creation of incremental info sections to follow finalization of
	symbol table.  Set offsets for postprocessing sections.
	(Layout::create_incremental_info_sections): Call
	Incremental_inputs::create_data_sections.  Add incremental symtab
	and relocs sections.  Set sh_entsize and sh_link fields.  Arrange for
	sections to layout after input sections.
	* layout.h (struct Timespec): Forward declaration.
	(Layout::incremental_inputs): Add const.
	(Layout::create_incremental_info_sections): Add parameter.
	* main.cc (main): Remove call to Incremental_inputs::report_inputs.
	* object.cc: Include incremental.h.
	(Relobj::finalize_incremental_relocs): New function.
	(Sized_relobj::do_layout): Record input sections in incremental info.
	* object.h (Object::output_section): New function.
	(Object::output_section_offset): Moved from Relobj.
	(Object::get_incremental_reloc_base): New function.
	(Object::get_incremental_reloc_count): New function.
	(Object::do_output_section): New function.
	(Object::do_output_section_offset): Moved from Relobj.
	(Object::do_get_incremental_reloc_base): New function.
	(Object::do_get_incremental_reloc_count): New function.
	(Object::Object): Initialize new data members.
	(Relobj::output_section): Renamed do_output_section and moved to
	protected.
	(Relobj::output_section_offset): Moved to Object.
	(Relobj::do_get_incremental_reloc_base): New function.
	(Relobj::do_get_incremental_reloc_count): New function.
	(Relobj::allocate_incremental_reloc_counts): New function.
	(Relobj::count_incremental_reloc): New function.
	(Relobj::finalize_incremental_relocs): New function.
	(Relobj::next_incremental_reloc_index): New function.
	(Relobj::reloc_counts_): New data member.
	(Relobj::reloc_bases_): New data member.
	(Sized_relobj::do_relocate_sections): Add parameter.  Change caller.
	(Sized_relobj::relocate_sections): Add parameter.  Change all callers.
	(Sized_relobj::incremental_relocs_scan): New function.
	(Sized_relobj::incremental_relocs_scan_reltype): New function.
	(Sized_relobj::incremental_relocs_write): New function.
	(Sized_relobj::incremental_relocs_write_reltype): New function.
	* plugin.cc (Plugin_manager::add_input_file): Rewrite test for
	incremental link.
	* readsyms.cc (Read_symbols::do_read_symbols): Move reporting of
	archives and object files elsewhere.
	(Add_symbols::run): Report object files here.
	(Finish_group::run): Report end of archive at end of group.
	* reloc.cc: Include layout.h, incremental.h.
	(Sized_relobj::do_read_relocs): Need relocations for incremental link.
	(Sized_relobj::do_scan_relocs): Record relocations for incremental link.
	(Sized_relobj::incremental_relocs_scan): New function.
	(Sized_relobj::incremental_relocs_scan_reltype): New function.
	(Sized_relobj::do_relocate_sections): Write incremental relocations.
	(Sized_relobj::incremental_relocs_write): New function.
	(Sized_relobj::incremental_relocs_write_reltype): New function.
	* script.cc (read_input_script): Rewrite test for incremental link.
	Change call to Incremental_inputs::report_script.
	* symtab.h (Symbol_table::first_global_index): New function.
	(Symbol_table::output_count): New function.
---
 elfcpp/elfcpp.h          |    2 +
 gold/archive.cc          |   21 +-
 gold/archive.h           |   76 ++++
 gold/incremental-dump.cc |  400 +++++++++++++++----
 gold/incremental.cc      |  784 ++++++++++++++++++++++++++++---------
 gold/incremental.h       |  980 ++++++++++++++++++++++++++++++++--------------
 gold/layout.cc           |   82 +++--
 gold/layout.h            |    5 +-
 gold/main.cc             |    5 +-
 gold/object.cc           |   32 ++
 gold/object.h            |  171 +++++++--
 gold/plugin.cc           |    2 +-
 gold/readsyms.cc         |   29 +-
 gold/reloc.cc            |  193 +++++++++-
 gold/script.cc           |   12 +-
 gold/symtab.h            |   10 +
 16 files changed, 2172 insertions(+), 632 deletions(-)

diff --git a/elfcpp/elfcpp.h b/elfcpp/elfcpp.h
index 52b8b25..c46fbf5 100644
--- a/elfcpp/elfcpp.h
+++ b/elfcpp/elfcpp.h
@@ -365,6 +365,8 @@ enum SHT
   // The remaining values are not in the standard.
   // Incremental build data.
   SHT_GNU_INCREMENTAL_INPUTS = 0x6fff4700,
+  SHT_GNU_INCREMENTAL_SYMTAB = 0x6fff4701,
+  SHT_GNU_INCREMENTAL_RELOCS = 0x6fff4702,
   // Object attributes.
   SHT_GNU_ATTRIBUTES = 0x6ffffff5,
   // GNU style dynamic hash table.
diff --git a/gold/archive.cc b/gold/archive.cc
index f1000a1..8c34d55 100644
--- a/gold/archive.cc
+++ b/gold/archive.cc
@@ -39,6 +39,7 @@
 #include "layout.h"
 #include "archive.h"
 #include "plugin.h"
+#include "incremental.h"
 
 namespace gold
 {
@@ -89,7 +90,8 @@ Archive::Archive(const std::string& name, Input_file* input_file,
   : name_(name), input_file_(input_file), armap_(), armap_names_(),
     extended_names_(), armap_checked_(), seen_offsets_(), members_(),
     is_thin_archive_(is_thin_archive), included_member_(false),
-    nested_archives_(), dirpath_(dirpath), task_(task), num_members_(0)
+    nested_archives_(), dirpath_(dirpath), task_(task), num_members_(0),
+    incremental_info_(NULL)
 {
   this->no_export_ =
     parameters->options().check_excluded_libs(input_file->found_name());
@@ -891,6 +893,8 @@ Archive::include_member(Symbol_table* symtab, Layout* layout,
   else
     {
       {
+	if (layout->incremental_inputs() != NULL)
+	  layout->incremental_inputs()->report_object(obj, this);
 	Read_symbols_data sd;
 	obj->read_symbols(&sd);
 	obj->layout(symtab, layout, &sd);
@@ -952,6 +956,11 @@ Add_archive_symbols::locks(Task_locker* tl)
 void
 Add_archive_symbols::run(Workqueue* workqueue)
 {
+  // For an incremental link, begin recording layout information.
+  Incremental_inputs* incremental_inputs = this->layout_->incremental_inputs();
+  if (incremental_inputs != NULL)
+    incremental_inputs->report_archive_begin(this->archive_);
+
   bool added = this->archive_->add_symbols(this->symtab_, this->layout_,
 					   this->input_objects_,
 					   this->mapfile_);
@@ -978,6 +987,11 @@ Add_archive_symbols::run(Workqueue* workqueue)
     this->input_group_->add_archive(this->archive_);
   else
     {
+      // For an incremental link, finish recording the layout information.
+      Incremental_inputs* incremental_inputs = this->layout_->incremental_inputs();
+      if (incremental_inputs != NULL)
+	incremental_inputs->report_archive_end(this->archive_);
+
       // We no longer need to know about this archive.
       delete this->archive_;
       this->archive_ = NULL;
@@ -1077,6 +1091,9 @@ Lib_group::include_member(Symbol_table* symtab, Layout* layout,
   obj->lock(this->task_);
   if (input_objects->add_object(obj))
     {
+      // FIXME: Record incremental link info for --start-lib/--end-lib.
+      if (layout->incremental_inputs() != NULL)
+	layout->incremental_inputs()->report_object(obj, NULL);
       obj->layout(symtab, layout, sd);
       obj->add_symbols(symtab, sd, layout);
       // Unlock the file for the next task.
@@ -1116,6 +1133,8 @@ void
 Add_lib_group_symbols::run(Workqueue*)
 {
   this->lib_->add_symbols(this->symtab_, this->layout_, this->input_objects_);
+
+  // FIXME: Record incremental link info for --start_lib/--end_lib.
 }
 
 Add_lib_group_symbols::~Add_lib_group_symbols()
diff --git a/gold/archive.h b/gold/archive.h
index bff3457..7b9b5af 100644
--- a/gold/archive.h
+++ b/gold/archive.h
@@ -42,6 +42,7 @@ class Symbol_table;
 class Object;
 class Read_symbols_data;
 class Input_file_lib;
+class Incremental_archive_entry;
 
 // An entry in the archive map of offsets to members.
 struct Archive_member
@@ -164,6 +165,16 @@ class Archive
   no_export()
   { return this->no_export_; }
 
+  // Store a pointer to the incremental link info for the archive.
+  void
+  set_incremental_info(Incremental_archive_entry* info)
+  { this->incremental_info_ = info; }
+
+  // Return the pointer to the incremental link info for the archive.
+  Incremental_archive_entry*
+  incremental_info() const
+  { return this->incremental_info_; }
+
   // When we see a symbol in an archive we might decide to include the member,
   // not include the member or be undecided. This enum represents these
   // possibilities.
@@ -181,6 +192,69 @@ class Archive
                         size_t* tmpbuflen);
 
  private:
+  struct Armap_entry;
+
+ public:
+  // Iterator class for unused global symbols.  This iterator is used
+  // for incremental links.
+
+  class Unused_symbol_iterator
+  {
+   public:
+    Unused_symbol_iterator(Archive* arch,
+                           std::vector<Armap_entry>::const_iterator it)
+      : arch_(arch), it_(it)
+    { this->skip_used_symbols(); }
+
+    const char*
+    operator*() const
+    { return this->arch_->armap_names_.data() + this->it_->name_offset; }
+
+    Unused_symbol_iterator&
+    operator++()
+    {
+      ++this->it_;
+      this->skip_used_symbols();
+      return *this;
+    }
+
+    bool
+    operator==(const Unused_symbol_iterator p) const
+    { return this->it_ == p.it_; }
+
+    bool
+    operator!=(const Unused_symbol_iterator p) const
+    { return this->it_ != p.it_; }
+
+   private:
+    // Skip over symbols defined by members that have been included.
+    void
+    skip_used_symbols()
+    {
+      while (this->it_ != this->arch_->armap_.end()
+	     && (this->arch_->seen_offsets_.find(this->it_->file_offset)
+		 != this->arch_->seen_offsets_.end()))
+	++it_;
+    }
+
+    // The underlying archive.
+    Archive* arch_;
+
+    // The underlying iterator over all entries in the archive map.
+    std::vector<Armap_entry>::const_iterator it_;
+  };
+
+  // Return an iterator referring to the first unused symbol.
+  Unused_symbol_iterator
+  unused_symbols_begin()
+  { return Unused_symbol_iterator(this, this->armap_.begin()); }
+
+  // Return an iterator referring to the end of the unused symbols.
+  Unused_symbol_iterator
+  unused_symbols_end()
+  { return Unused_symbol_iterator(this, this->armap_.end()); }
+
+ private:
   Archive(const Archive&);
   Archive& operator=(const Archive&);
 
@@ -312,6 +386,8 @@ class Archive
   unsigned int num_members_;
   // True if we exclude this library archive from automatic export.
   bool no_export_;
+  // The incremental link information for this archive.
+  Incremental_archive_entry* incremental_info_;
 };
 
 // This class is used to read an archive and pick out the desired
diff --git a/gold/incremental-dump.cc b/gold/incremental-dump.cc
index e174b99..e8ad272 100644
--- a/gold/incremental-dump.cc
+++ b/gold/incremental-dump.cc
@@ -1,4 +1,4 @@
-// inremental.cc -- incremental linking test/deubg tool
+// incremental.cc -- incremental linking test/debug tool
 
 // Copyright 2009 Free Software Foundation, Inc.
 // Written by Rafael Avila de Espindola <rafael.espindola@gmail.com>
@@ -32,6 +32,7 @@
 
 #include <stdio.h>
 #include <errno.h>
+#include <time.h>
 
 #include "incremental.h"
 
@@ -43,15 +44,51 @@ namespace gold
 using namespace gold;
 
 template<int size, bool big_endian>
+static typename Incremental_inputs_reader<size, big_endian>::
+    Incremental_input_entry_reader
+find_input_containing_global(
+    Incremental_inputs_reader<size, big_endian>& incremental_inputs,
+    unsigned int offset,
+    unsigned int* symndx)
+{
+  typedef Incremental_inputs_reader<size, big_endian> Inputs_reader;
+  for (unsigned int i = 0; i < incremental_inputs.input_file_count(); ++i)
+    {
+      typename Inputs_reader::Incremental_input_entry_reader input_file =
+	  incremental_inputs.input_file(i);
+      if (input_file.type() != INCREMENTAL_INPUT_OBJECT
+          && input_file.type() != INCREMENTAL_INPUT_ARCHIVE_MEMBER)
+        continue;
+      unsigned int nsyms = input_file.get_global_symbol_count();
+      if (offset >= input_file.get_symbol_offset(0)
+          && offset < input_file.get_symbol_offset(nsyms))
+	{
+	  *symndx = (offset - input_file.get_symbol_offset(0)) / 16;
+	  return input_file;
+	}
+    }
+  gold_unreachable();
+}
+
+template<int size, bool big_endian>
 static void
-dump_incremental_inputs(const char* argv0,
-                        const char* filename, Incremental_binary* inc)
+dump_incremental_inputs(const char* argv0, const char* filename,
+			Incremental_binary* inc)
 {
   bool t;
-  unsigned int strtab_shndx;
-  Incremental_binary::Location location;
-
-  t = inc->find_incremental_inputs_section(&location, &strtab_shndx);
+  unsigned int inputs_shndx;
+  unsigned int isymtab_shndx;
+  unsigned int irelocs_shndx;
+  unsigned int istrtab_shndx;
+  typedef Incremental_binary::Location Location;
+  typedef Incremental_binary::View View;
+  typedef Incremental_inputs_reader<size, big_endian> Inputs_reader;
+  typedef typename Inputs_reader::Incremental_input_entry_reader Entry_reader;
+
+  // Find the .gnu_incremental_inputs, _symtab, _relocs, and _strtab sections.
+
+  t = inc->find_incremental_inputs_sections(&inputs_shndx, &isymtab_shndx,
+					    &irelocs_shndx, &istrtab_shndx);
   if (!t)
     {
       fprintf(stderr, "%s: %s: no .gnu_incremental_inputs section\n", argv0,
@@ -59,105 +96,306 @@ dump_incremental_inputs(const char* argv0,
       exit (1);
     }
 
-  Incremental_binary::View inputs_view(inc->view(location));
-  const unsigned char* p = inputs_view.data();
+  elfcpp::Elf_file<size, big_endian, Incremental_binary> elf_file(inc);
+
+  // Get a view of the .gnu_incremental_inputs section.
+
+  Location inputs_location(elf_file.section_contents(inputs_shndx));
+  View inputs_view(inc->view(inputs_location));
+
+  // Get the .gnu_incremental_strtab section as a string table.
 
-  Incremental_inputs_header<size, big_endian> incremental_header(p);
+  Location istrtab_location(elf_file.section_contents(istrtab_shndx));
+  View istrtab_view(inc->view(istrtab_location));
+  elfcpp::Elf_strtab istrtab(istrtab_view.data(), istrtab_location.data_size);
 
-  const unsigned char* incremental_inputs_base =
-    (p + sizeof(Incremental_inputs_header_data));
+  // Create a reader object for the .gnu_incremental_inputs section.
 
-  if (incremental_header.get_version() != 1)
+  Incremental_inputs_reader<size, big_endian>
+      incremental_inputs(inputs_view.data(), istrtab);
+
+  if (incremental_inputs.version() != 1)
     {
       fprintf(stderr, "%s: %s: unknown incremental version %d\n", argv0,
-              filename, incremental_header.get_version());
+              filename, incremental_inputs.version());
       exit(1);
     }
 
-  elfcpp::Elf_file<size, big_endian, Incremental_binary> elf_file(inc);
-
-  if (elf_file.section_type(strtab_shndx) != elfcpp::SHT_STRTAB)
+  const char* command_line = incremental_inputs.command_line();
+  if (command_line == NULL)
     {
       fprintf(stderr,
-              "%s: %s: invalid string table section %u (type %d != %d)\n",
-              argv0, filename, strtab_shndx,
-              elf_file.section_type(strtab_shndx), elfcpp::SHT_STRTAB);
+              "%s: %s: failed to get link command line\n",
+              argv0, filename);
       exit(1);
     }
+  printf("Link command line: %s\n", command_line);
 
-  Incremental_binary::Location
-    strtab_location(elf_file.section_contents(strtab_shndx));
+  printf("\nInput files:\n");
+  for (unsigned int i = 0; i < incremental_inputs.input_file_count(); ++i)
+    {
+      typedef Incremental_inputs_reader<size, big_endian> Inputs_reader;
+      typename Inputs_reader::Incremental_input_entry_reader input_file =
+	  incremental_inputs.input_file(i);
+
+      const char* objname = input_file.filename();
+      if (objname == NULL)
+	{
+	  fprintf(stderr,"%s: %s: failed to get file name for object %u\n",
+		  argv0, filename, i);
+	  exit(1);
+	}
+      printf("[%d] %s\n", i, objname);
+
+      Timespec mtime = input_file.get_mtime();
+      printf("    Timestamp: %llu.%09d  %s",
+	     static_cast<unsigned long long>(mtime.seconds),
+	     mtime.nanoseconds,
+	     ctime(&mtime.seconds));
+
+      Incremental_input_type input_type = input_file.type();
+      printf("    Type: ");
+      switch (input_type)
+	{
+	case INCREMENTAL_INPUT_OBJECT:
+	  {
+	    printf("Object\n");
+	    printf("    Input section count: %d\n",
+		   input_file.get_input_section_count());
+	    printf("    Symbol count: %d\n",
+		   input_file.get_global_symbol_count());
+	  }
+	  break;
+	case INCREMENTAL_INPUT_ARCHIVE_MEMBER:
+	  {
+	    printf("Archive member\n");
+	    printf("    Input section count: %d\n",
+		   input_file.get_input_section_count());
+	    printf("    Symbol count: %d\n",
+		   input_file.get_global_symbol_count());
+	  }
+	  break;
+	case INCREMENTAL_INPUT_ARCHIVE:
+	  {
+	    printf("Archive\n");
+	    printf("    Member count: %d\n", input_file.get_member_count());
+	    printf("    Unused symbol count: %d\n",
+		   input_file.get_unused_symbol_count());
+	  }
+	  break;
+	case INCREMENTAL_INPUT_SHARED_LIBRARY:
+	  {
+	    printf("Shared library\n");
+	    printf("    Symbol count: %d\n",
+		   input_file.get_global_symbol_count());
+	  }
+	  break;
+	case INCREMENTAL_INPUT_SCRIPT:
+	  printf("Linker script\n");
+	  break;
+	default:
+	  fprintf(stderr, "%s: invalid file type for object %u: %d\n",
+		  argv0, i, input_type);
+	  exit(1);
+	}
+    }
 
-  Incremental_binary::View strtab_view(inc->view(strtab_location));
-  p = strtab_view.data();
+  printf("\nInput sections:\n");
+  for (unsigned int i = 0; i < incremental_inputs.input_file_count(); ++i)
+    {
+      typedef Incremental_inputs_reader<size, big_endian> Inputs_reader;
+      typedef typename Inputs_reader::Incremental_input_entry_reader
+          Entry_reader;
+
+      Entry_reader input_file(incremental_inputs.input_file(i));
+
+      if (input_file.type() != INCREMENTAL_INPUT_OBJECT
+	  && input_file.type() != INCREMENTAL_INPUT_ARCHIVE_MEMBER)
+	continue;
+
+      const char* objname = input_file.filename();
+      if (objname == NULL)
+	{
+	  fprintf(stderr,"%s: %s: failed to get file name for object %u\n",
+		  argv0, filename, i);
+	  exit(1);
+	}
+
+      printf("[%d] %s\n", i, objname);
+
+      printf("    %3s  %6s  %8s  %8s  %s\n",
+	     "n", "outndx", "offset", "size", "name");
+      unsigned int nsections = input_file.get_input_section_count();
+      for (unsigned int shndx = 0; shndx < nsections; ++shndx)
+	{
+	  typename Entry_reader::Input_section_info info(
+	      input_file.get_input_section(shndx));
+	  printf("    %3d  %6d  %8lld  %8lld  %s\n", shndx,
+		 info.output_shndx,
+		 static_cast<long long>(info.sh_offset),
+		 static_cast<long long>(info.sh_size),
+		 info.name);
+	}
+    }
 
-  elfcpp::Elf_strtab strtab(strtab_view.data(), strtab_location.data_size);
-  const char* command_line;
-  elfcpp::Elf_Word command_line_offset =
-    incremental_header.get_command_line_offset();
-  t = strtab.get_c_string(command_line_offset, &command_line);
+  printf("\nGlobal symbols per input file:\n");
+  for (unsigned int i = 0; i < incremental_inputs.input_file_count(); ++i)
+    {
+      typedef Incremental_inputs_reader<size, big_endian> Inputs_reader;
+      typedef typename Inputs_reader::Incremental_input_entry_reader
+          Entry_reader;
+
+      Entry_reader input_file(incremental_inputs.input_file(i));
+
+      if (input_file.type() != INCREMENTAL_INPUT_OBJECT
+	  && input_file.type() != INCREMENTAL_INPUT_ARCHIVE_MEMBER)
+	continue;
+
+      const char* objname = input_file.filename();
+      if (objname == NULL)
+	{
+	  fprintf(stderr,"%s: %s: failed to get file name for object %u\n",
+		  argv0, filename, i);
+	  exit(1);
+	}
+
+      printf("[%d] %s\n", i, objname);
+
+      unsigned int nsyms = input_file.get_global_symbol_count();
+      if (nsyms > 0)
+	printf("    %6s  %8s  %8s  %8s  %8s\n",
+	       "outndx", "offset", "chain", "#relocs", "rbase");
+      for (unsigned int symndx = 0; symndx < nsyms; ++symndx)
+	{
+	  typename Entry_reader::Global_symbol_info info(
+	      input_file.get_global_symbol_info(symndx));
+	  printf("    %6d  %8d  %8d  %8d  %8d\n",
+		 info.output_symndx,
+		 input_file.get_symbol_offset(symndx),
+		 info.next_offset,
+		 info.reloc_count,
+		 info.reloc_offset);
+	}
+    }
 
-  if (!t)
+  // Get a view of the .symtab section.
+
+  unsigned int symtab_shndx = elf_file.find_section_by_type(elfcpp::SHT_SYMTAB);
+  if (symtab_shndx == elfcpp::SHN_UNDEF)  // Not found.
     {
-      fprintf(stderr,
-              "%s: %s: failed to get link command line: %zu out of range\n",
-              argv0, filename,
-              static_cast<size_t>(command_line_offset));
-      exit(1);
+      fprintf(stderr, "%s: %s: no symbol table section\n", argv0, filename);
+      exit (1);
     }
+  Location symtab_location(elf_file.section_contents(symtab_shndx));
+  View symtab_view(inc->view(symtab_location));
 
-  printf("Link command line: %s\n", command_line);
+  // Get a view of the .strtab section.
 
-  printf("Input files:\n");
-  for (unsigned i = 0; i < incremental_header.get_input_file_count(); ++i)
+  unsigned int strtab_shndx = elf_file.section_link(symtab_shndx);
+  if (strtab_shndx == elfcpp::SHN_UNDEF
+      || strtab_shndx > elf_file.shnum()
+      || elf_file.section_type(strtab_shndx) != elfcpp::SHT_STRTAB)
     {
-      const unsigned char* input_p = incremental_inputs_base +
-        i * sizeof(Incremental_inputs_entry_data);
-      Incremental_inputs_entry<size, big_endian> input(input_p);
-      const char* objname;
+      fprintf(stderr, "%s: %s: no string table section\n", argv0, filename);
+      exit (1);
+    }
+  Location strtab_location(elf_file.section_contents(strtab_shndx));
+  View strtab_view(inc->view(strtab_location));
+  elfcpp::Elf_strtab strtab(strtab_view.data(), strtab_location.data_size);
+
+  // Get a view of the .gnu_incremental_symtab section.
 
-      t = strtab.get_c_string(input.get_filename_offset(), &objname);
-      if (!t)
+  Location isymtab_location(elf_file.section_contents(isymtab_shndx));
+  View isymtab_view(inc->view(isymtab_location));
+
+  // Get a view of the .gnu_incremental_relocs section.
+
+  Location irelocs_location(elf_file.section_contents(irelocs_shndx));
+  View irelocs_view(inc->view(irelocs_location));
+
+  // The .gnu_incremental_symtab section contains entries that parallel
+  // the global symbols of the main symbol table.  The sh_info field
+  // of the main symbol table's section header tells us how many global
+  // symbols there are, but that count does not include any global
+  // symbols that were forced local during the link.  Therefore, we
+  // use the size of the .gnu_incremental_symtab section to deduce
+  // the number of global symbols + forced-local symbols there are
+  // in the symbol table.
+  unsigned int sym_size = elfcpp::Elf_sizes<size>::sym_size;
+  unsigned int nsyms = symtab_location.data_size / sym_size;
+  unsigned int nglobals = isymtab_location.data_size / 4;
+  unsigned int first_global = nsyms - nglobals;
+  unsigned const char* sym_p = symtab_view.data() + first_global * sym_size;
+  unsigned const char* isym_p = isymtab_view.data();
+
+  Incremental_symtab_reader<big_endian> isymtab(isymtab_view.data());
+  Incremental_relocs_reader<size, big_endian> irelocs(irelocs_view.data());
+
+  printf("\nGlobal symbol table:\n");
+  for (unsigned int i = 0; i < nglobals; i++)
+    {
+      elfcpp::Sym<size, big_endian> sym(sym_p);
+      const char* symname;
+      if (!strtab.get_c_string(sym.get_st_name(), &symname))
+	symname = "<unknown>";
+      printf("[%d] %s\n", first_global + i, symname);
+      unsigned int offset = isymtab.get_list_head(i);
+      while (offset > 0)
         {
-          fprintf(stderr,"%s: %s: failed to get file name for object %u:"
-                  " %zu out of range\n", argv0, filename, i,
-                  static_cast<size_t>(input.get_filename_offset()));
-          exit(1);
-        }
-      printf("  %s\n", objname);
-      printf("    Timestamp sec = %llu\n",
-             static_cast<unsigned long long>(input.get_timestamp_sec()));
-      printf("    Timestamp nsec = %d\n", input.get_timestamp_nsec());
-      printf("    Type = ");
-      // TODO: print the data at input->data_offset once we have it.
-      elfcpp::Elf_Word input_type = input.get_input_type();
-      switch (input_type)
-      {
-      case INCREMENTAL_INPUT_OBJECT:
-        printf("Object\n");
-        break;
-      case INCREMENTAL_INPUT_ARCHIVE:
-        printf("Archive\n");
-        break;
-      case INCREMENTAL_INPUT_SHARED_LIBRARY:
-        printf("Shared library\n");
-        break;
-      case INCREMENTAL_INPUT_SCRIPT:
-        printf("Linker script\n");
-        if (input.get_data_offset() != 0)
-          {
-            fprintf(stderr,"%s: %s: %u is a script but offset is not zero",
-                    argv0, filename, i);
-            exit(1);
-          }
-        break;
-      case INCREMENTAL_INPUT_INVALID:
-      default:
-        fprintf(stderr, "%s: invalid file type for object %u: %d\n",
-                argv0, i, input_type);
-        exit(1);
-      }
+	  unsigned int sym_ndx;
+	  Entry_reader input_file =
+	      find_input_containing_global<size, big_endian>(incremental_inputs,
+							     offset, &sym_ndx);
+	  typename Entry_reader::Global_symbol_info sym_info(
+	      input_file.get_global_symbol_info(sym_ndx));
+	  printf("    %s (first reloc: %d, reloc count: %d)",
+		 input_file.filename(), sym_info.reloc_offset,
+		 sym_info.reloc_count);
+	  if (sym_info.output_symndx != first_global + i)
+	    printf(" ** wrong output symndx (%d) **", sym_info.output_symndx);
+	  printf("\n");
+	  // Dump the relocations from this input file for this symbol.
+	  unsigned int r_off = sym_info.reloc_offset;
+	  for (unsigned int j = 0; j < sym_info.reloc_count; j++)
+	    {
+	      printf("      %4d  relocation type %3d  shndx %d"
+		     "  offset %016llx  addend %016llx  %s\n",
+		     r_off,
+		     irelocs.get_r_type(r_off),
+		     irelocs.get_r_shndx(r_off),
+		     static_cast<long long>(irelocs.get_r_offset(r_off)),
+		     static_cast<long long>(irelocs.get_r_addend(r_off)),
+		     symname);
+	      r_off += irelocs.reloc_size;
+	    }
+	  offset = sym_info.next_offset;
+	}
+      sym_p += sym_size;
+      isym_p += 4;
     }
+
+  printf("\nUnused archive symbols:\n");
+  for (unsigned int i = 0; i < incremental_inputs.input_file_count(); ++i)
+    {
+      Entry_reader input_file(incremental_inputs.input_file(i));
+
+      if (input_file.type() != INCREMENTAL_INPUT_ARCHIVE)
+	continue;
+
+      const char* objname = input_file.filename();
+      if (objname == NULL)
+	{
+	  fprintf(stderr,"%s: %s: failed to get file name for object %u\n",
+		  argv0, filename, i);
+	  exit(1);
+	}
+
+      printf("[%d] %s\n", i, objname);
+      unsigned int nsyms = input_file.get_unused_symbol_count();
+      for (unsigned int symndx = 0; symndx < nsyms; ++symndx)
+        printf("    %s\n", input_file.get_unused_symbol(symndx));
+    }
+
 }
 
 int
diff --git a/gold/incremental.cc b/gold/incremental.cc
index 01be470..50b7482 100644
--- a/gold/incremental.cc
+++ b/gold/incremental.cc
@@ -27,6 +27,7 @@
 
 #include "elfcpp.h"
 #include "output.h"
+#include "symtab.h"
 #include "incremental.h"
 #include "archive.h"
 #include "output.h"
@@ -38,6 +39,73 @@ namespace gold {
 // we could think about backward (and forward?) compatibility.
 const unsigned int INCREMENTAL_LINK_VERSION = 1;
 
+// This class manages the .gnu_incremental_inputs section, which holds
+// the header information, a directory of input files, and separate
+// entries for each input file.
+
+template<int size, bool big_endian>
+class Output_section_incremental_inputs : public Output_section_data
+{
+ public:
+  Output_section_incremental_inputs(const Incremental_inputs* inputs,
+				    const Symbol_table* symtab)
+    : Output_section_data(size / 8), inputs_(inputs), symtab_(symtab)
+  { }
+
+ protected:
+  // Set the final data size.
+  void
+  set_final_data_size();
+
+  // Write the data to the file.
+  void
+  do_write(Output_file*);
+
+  // Write to a map file.
+  void
+  do_print_to_mapfile(Mapfile* mapfile) const
+  { mapfile->print_output_data(this, _("** incremental_inputs")); }
+
+ private:
+  // Write the section header.
+  unsigned char*
+  write_header(unsigned char* pov, unsigned int input_file_count,
+	       section_offset_type command_line_offset);
+
+  // Write the input file entries.
+  unsigned char*
+  write_input_files(unsigned char* oview, unsigned char* pov,
+		    Stringpool* strtab);
+
+  // Write the supplemental information blocks.
+  unsigned char*
+  write_info_blocks(unsigned char* oview, unsigned char* pov,
+		    Stringpool* strtab, unsigned int* global_syms,
+		    unsigned int global_sym_count);
+
+  // Write the contents of the .gnu_incremental_symtab section.
+  void
+  write_symtab(unsigned char* pov, unsigned int* global_syms,
+	       unsigned int global_sym_count);
+
+  // Typedefs for writing the data to the output sections.
+  typedef elfcpp::Swap<size, big_endian> Swap;
+  typedef elfcpp::Swap<16, big_endian> Swap16;
+  typedef elfcpp::Swap<32, big_endian> Swap32;
+  typedef elfcpp::Swap<64, big_endian> Swap64;
+
+  // Sizes of various structures.
+  static const int sizeof_addr = size / 8;
+  static const int header_size = 16;
+  static const int input_entry_size = 24;
+
+  // The Incremental_inputs object.
+  const Incremental_inputs* inputs_;
+
+  // The symbol table.
+  const Symbol_table* symtab_;
+};
+
 // Inform the user why we don't do an incremental link.  Not called in
 // the obvious case of missing output file.  TODO: Is this helpful?
 
@@ -77,77 +145,101 @@ Incremental_binary::error(const char* format, ...) const
   va_end(args);
 }
 
+// Find the .gnu_incremental_inputs section and related sections.
+
 template<int size, bool big_endian>
 bool
-Sized_incremental_binary<size, big_endian>::do_find_incremental_inputs_section(
-    Location* location,
-    unsigned int* strtab_shndx)
+Sized_incremental_binary<size, big_endian>::do_find_incremental_inputs_sections(
+    unsigned int* p_inputs_shndx,
+    unsigned int* p_symtab_shndx,
+    unsigned int* p_relocs_shndx,
+    unsigned int* p_strtab_shndx)
 {
-  unsigned int shndx = this->elf_file_.find_section_by_type(
-      elfcpp::SHT_GNU_INCREMENTAL_INPUTS);
-  if (shndx == elfcpp::SHN_UNDEF)  // Not found.
+  unsigned int inputs_shndx =
+      this->elf_file_.find_section_by_type(elfcpp::SHT_GNU_INCREMENTAL_INPUTS);
+  if (inputs_shndx == elfcpp::SHN_UNDEF)  // Not found.
+    return false;
+
+  unsigned int symtab_shndx =
+      this->elf_file_.find_section_by_type(elfcpp::SHT_GNU_INCREMENTAL_SYMTAB);
+  if (symtab_shndx == elfcpp::SHN_UNDEF)  // Not found.
+    return false;
+  if (this->elf_file_.section_link(symtab_shndx) != inputs_shndx)
     return false;
-  *strtab_shndx = this->elf_file_.section_link(shndx);
-  *location = this->elf_file_.section_contents(shndx);
+
+  unsigned int relocs_shndx =
+      this->elf_file_.find_section_by_type(elfcpp::SHT_GNU_INCREMENTAL_RELOCS);
+  if (relocs_shndx == elfcpp::SHN_UNDEF)  // Not found.
+    return false;
+  if (this->elf_file_.section_link(relocs_shndx) != inputs_shndx)
+    return false;
+
+  unsigned int strtab_shndx = this->elf_file_.section_link(inputs_shndx);
+  if (strtab_shndx == elfcpp::SHN_UNDEF
+      || strtab_shndx > this->elf_file_.shnum()
+      || this->elf_file_.section_type(strtab_shndx) != elfcpp::SHT_STRTAB)
+    return false;
+
+  if (p_inputs_shndx != NULL)
+    *p_inputs_shndx = inputs_shndx;
+  if (p_symtab_shndx != NULL)
+    *p_symtab_shndx = symtab_shndx;
+  if (p_relocs_shndx != NULL)
+    *p_relocs_shndx = relocs_shndx;
+  if (p_strtab_shndx != NULL)
+    *p_strtab_shndx = strtab_shndx;
   return true;
 }
 
+// Determine whether an incremental link based on the existing output file
+// can be done.
+
 template<int size, bool big_endian>
 bool
 Sized_incremental_binary<size, big_endian>::do_check_inputs(
     Incremental_inputs* incremental_inputs)
 {
-  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 inputs_shndx;
+  unsigned int symtab_shndx;
+  unsigned int relocs_shndx;
   unsigned int strtab_shndx;
-  Location location;
 
-  if (!do_find_incremental_inputs_section(&location, &strtab_shndx))
+  if (!do_find_incremental_inputs_sections(&inputs_shndx, &symtab_shndx,
+					   &relocs_shndx, &strtab_shndx))
     {
       explain_no_incremental(_("no incremental data from previous build"));
       return false;
     }
-  if (location.data_size < header_size
-      || strtab_shndx >= this->elf_file_.shnum()
-      || this->elf_file_.section_type(strtab_shndx) != elfcpp::SHT_STRTAB)
-    {
-      explain_no_incremental(_("invalid incremental build data"));
-      return false;
-    }
 
+  Location inputs_location(this->elf_file_.section_contents(inputs_shndx));
+  Location symtab_location(this->elf_file_.section_contents(symtab_shndx));
+  Location relocs_location(this->elf_file_.section_contents(relocs_shndx));
   Location strtab_location(this->elf_file_.section_contents(strtab_shndx));
-  View data_view(view(location));
+
+  View inputs_view(view(inputs_location));
+  View symtab_view(view(symtab_location));
+  View relocs_view(view(relocs_location));
   View strtab_view(view(strtab_location));
+
   elfcpp::Elf_strtab strtab(strtab_view.data(), strtab_location.data_size);
-  Incremental_inputs_header<size, big_endian> header(data_view.data());
 
-  if (header.get_version() != INCREMENTAL_LINK_VERSION)
-    {
-      explain_no_incremental(_("different version of incremental build data"));
-      return false;
-    }
+  Incremental_inputs_reader<size, big_endian>
+      incoming_inputs(inputs_view.data(), strtab);
 
-  const char* command_line;
-  // We divide instead of multiplying to make sure there is no integer
-  // overflow.
-  size_t max_input_entries = (location.data_size - header_size) / entry_size;
-  if (header.get_input_file_count() > max_input_entries
-      || !strtab.get_c_string(header.get_command_line_offset(), &command_line))
+  if (incoming_inputs.version() != INCREMENTAL_LINK_VERSION)
     {
-      explain_no_incremental(_("invalid incremental build data"));
+      explain_no_incremental(_("different version of incremental build data"));
       return false;
     }
 
-  if (incremental_inputs->command_line() != command_line)
+  if (incremental_inputs->command_line() != incoming_inputs.command_line())
     {
       explain_no_incremental(_("command line changed"));
       return false;
     }
 
   // TODO: compare incremental_inputs->inputs() with entries in data_view.
+
   return true;
 }
 
@@ -182,8 +274,8 @@ make_sized_incremental_binary(Output_file* file,
 
 }  // End of anonymous namespace.
 
-// Create an Incremental_binary object for FILE. Returns NULL is this is not
-// possible, e.g. FILE is not an ELF file or has an unsupported target. FILE
+// Create an Incremental_binary object for FILE.  Returns NULL is this is not
+// possible, e.g. FILE is not an ELF file or has an unsupported target.  FILE
 // should be opened.
 
 Incremental_binary*
@@ -275,6 +367,8 @@ Incremental_checker::can_incrementally_link_output_file()
   return binary->check_inputs(this->incremental_inputs_);
 }
 
+// Class Incremental_inputs.
+
 // Add the command line to the string table, setting
 // command_line_key_.  In incremental builds, the command line is
 // stored in .gnu_incremental_inputs so that the next linker run can
@@ -289,7 +383,7 @@ Incremental_inputs::report_command_line(int argc, const char* const* argv)
   // Copied from collect_argv in main.cc.
   for (int i = 1; i < argc; ++i)
     {
-      // Adding/removing these options should result in a full relink.
+      // Adding/removing these options should not result in a full relink.
       if (strcmp(argv[i], "--incremental-changed") == 0
 	  || strcmp(argv[i], "--incremental-unchanged") == 0
 	  || strcmp(argv[i], "--incremental-unknown") == 0)
@@ -315,99 +409,104 @@ Incremental_inputs::report_command_line(int argc, const char* const* argv)
                      &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.
+// Record the input archive file ARCHIVE.  This is called by the
+// Add_archive_symbols task before determining which archive members
+// to include.  We create the Incremental_archive_entry here and
+// attach it to the Archive, but we do not add it to the list of
+// input objects until report_archive_end is called.
 
 void
-Incremental_inputs::report_archive(const Input_argument* input,
-                                   Archive* archive)
+Incremental_inputs::report_archive_begin(Archive* arch)
 {
-  Hold_lock hl(*this->lock_);
+  Stringpool::Key filename_key;
+  Timespec mtime = arch->file().get_mtime();
 
-  Input_info info;
-  info.type = INCREMENTAL_INPUT_ARCHIVE;
-  info.archive = archive;
-  info.mtime = archive->file().get_mtime();
-  this->inputs_map_.insert(std::make_pair(input, info));
+  this->strtab_->add(arch->filename().c_str(), false, &filename_key);
+  Incremental_archive_entry* entry =
+      new Incremental_archive_entry(filename_key, arch, mtime);
+  arch->set_incremental_info(entry);
 }
 
-// Record that the input argument INPUT is an object OBJ.  This is
-// called by Read_symbols after finding out the type of the file.
+// Finish recording the input archive file ARCHIVE.  This is called by the
+// Add_archive_symbols task after determining which archive members
+// to include.
 
 void
-Incremental_inputs::report_object(const Input_argument* input,
-                                  Object* obj)
+Incremental_inputs::report_archive_end(Archive* arch)
 {
-  Hold_lock hl(*this->lock_);
-
-  Input_info info;
-  info.type = (obj->is_dynamic()
-	       ? INCREMENTAL_INPUT_SHARED_LIBRARY
-	       : INCREMENTAL_INPUT_OBJECT);
-  info.object = obj;
-  info.mtime = obj->input_file()->file().get_mtime();
-  this->inputs_map_.insert(std::make_pair(input, info));
+  Incremental_archive_entry* entry = arch->incremental_info();
+
+  gold_assert(entry != NULL);
+
+  // Collect unused global symbols.
+  for (Archive::Unused_symbol_iterator p = arch->unused_symbols_begin();
+       p != arch->unused_symbols_end();
+       ++p)
+    {
+      Stringpool::Key symbol_key;
+      this->strtab_->add(*p, true, &symbol_key);
+      entry->add_unused_global_symbol(symbol_key);
+    }
+  this->inputs_.push_back(entry);
 }
 
-// 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.
+// Record the input object file OBJ.  If ARCH is not NULL, attach
+// the object file to the archive.  This is called by the
+// Add_symbols task after finding out the type of the file.
 
 void
-Incremental_inputs::report_script(const Input_argument* input,
-                                  Timespec mtime,
-                                  Script_info* script)
+Incremental_inputs::report_object(Object* obj, Archive* arch)
 {
-  Hold_lock hl(*this->lock_);
+  Stringpool::Key filename_key;
+  Timespec mtime = obj->input_file()->file().get_mtime();
+
+  this->strtab_->add(obj->name().c_str(), false, &filename_key);
+  Incremental_object_entry* obj_entry =
+      new Incremental_object_entry(filename_key, obj, mtime);
+  this->inputs_.push_back(obj_entry);
 
-  Input_info info;
-  info.type = INCREMENTAL_INPUT_SCRIPT;
-  info.script = script;
-  info.mtime = mtime;
-  this->inputs_map_.insert(std::make_pair(input, info));
+  if (arch != NULL)
+    {
+      Incremental_archive_entry* arch_entry = arch->incremental_info();
+      gold_assert(arch_entry != NULL);
+      arch_entry->add_object(obj_entry);
+    }
+
+  this->current_object_ = obj;
+  this->current_object_entry_ = obj_entry;
 }
 
-// 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).
+// Record the input object file OBJ.  If ARCH is not NULL, attach
+// the object file to the archive.  This is called by the
+// Add_symbols task after finding out the type of the file.
 
 void
-Incremental_inputs::finalize_inputs(
-    Input_argument_list::const_iterator begin,
-    Input_argument_list::const_iterator end,
-    unsigned int* index)
+Incremental_inputs::report_input_section(Object* obj, unsigned int shndx,
+					 const char* name, off_t sh_size)
 {
-  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;
-        }
+  Stringpool::Key key = 0;
 
-      Inputs_info_map::iterator it = this->inputs_map_.find(&(*p));
-      // TODO: turn it into an assert when the code will be more stable.
-      if (it == this->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);
-        }
-    }
+  if (name != NULL)
+      this->strtab_->add(name, true, &key);
+
+  gold_assert(obj == this->current_object_);
+  this->current_object_entry_->add_input_section(shndx, key, sh_size);
+}
+
+// Record that the input argument INPUT is a 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 std::string& filename,
+				  Script_info* script, Timespec mtime)
+{
+  Stringpool::Key filename_key;
+
+  this->strtab_->add(filename.c_str(), false, &filename_key);
+  Incremental_script_entry* entry =
+      new Incremental_script_entry(filename_key, script, mtime);
+  this->inputs_.push_back(entry);
 }
 
 // Finalize the incremental link information.  Called from
@@ -416,108 +515,425 @@ Incremental_inputs::finalize_inputs(
 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 = this->inputs_map_.begin();
-       p != this->inputs_map_.end();
-       ++p)
-    {
-      gold_assert(p->second.filename_key != 0);
-    }
-
+  // Finalize the string table.
   this->strtab_->set_string_offsets();
 }
 
-// Create the content of the .gnu_incremental_inputs section.
+// Create the .gnu_incremental_inputs, _symtab, and _relocs input sections.
 
-Output_section_data*
-Incremental_inputs::create_incremental_inputs_section_data()
+void
+Incremental_inputs::create_data_sections(Symbol_table* symtab)
 {
   switch (parameters->size_and_endianness())
     {
 #ifdef HAVE_TARGET_32_LITTLE
     case Parameters::TARGET_32_LITTLE:
-      return this->sized_create_inputs_section_data<32, false>();
+      this->inputs_section_ =
+          new Output_section_incremental_inputs<32, false>(this, symtab);
+      break;
 #endif
 #ifdef HAVE_TARGET_32_BIG
     case Parameters::TARGET_32_BIG:
-      return this->sized_create_inputs_section_data<32, true>();
+      this->inputs_section_ =
+          new Output_section_incremental_inputs<32, true>(this, symtab);
+      break;
 #endif
 #ifdef HAVE_TARGET_64_LITTLE
     case Parameters::TARGET_64_LITTLE:
-      return this->sized_create_inputs_section_data<64, false>();
+      this->inputs_section_ =
+          new Output_section_incremental_inputs<64, false>(this, symtab);
+      break;
 #endif
 #ifdef HAVE_TARGET_64_BIG
     case Parameters::TARGET_64_BIG:
-      return this->sized_create_inputs_section_data<64, true>();
+      this->inputs_section_ =
+          new Output_section_incremental_inputs<64, true>(this, symtab);
+      break;
 #endif
     default:
       gold_unreachable();
     }
+  this->symtab_section_ = new Output_data_space(4, "** incremental_symtab");
+  this->relocs_section_ = new Output_data_space(4, "** incremental_relocs");
 }
 
-// Sized creation of .gnu_incremental_inputs section.
+// Return the sh_entsize value for the .gnu_incremental_relocs section.
+unsigned int
+Incremental_inputs::relocs_entsize() const
+{
+  return 8 + 2 * parameters->target().get_size() / 8;
+}
+
+// Class Output_section_incremental_inputs.
+
+// Finalize the offsets for each input section and supplemental info block,
+// and set the final data size of the incremental output sections.
 
 template<int size, bool big_endian>
-Output_section_data*
-Incremental_inputs::sized_create_inputs_section_data()
+void
+Output_section_incremental_inputs<size, big_endian>::set_final_data_size()
 {
-  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(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)
+  const Incremental_inputs* inputs = this->inputs_;
+  const unsigned int sizeof_addr = size / 8;
+  const unsigned int rel_size = 8 + 2 * sizeof_addr;
+
+  // Offset of each input entry.
+  unsigned int input_offset = this->header_size;
+
+  // Offset of each supplemental info block.
+  unsigned int info_offset = this->header_size;
+  info_offset += this->input_entry_size * inputs->input_file_count();
+
+  // Count each input file and its supplemental information block.
+  for (Incremental_inputs::Input_list::const_iterator p =
+	   inputs->input_files().begin();
+       p != inputs->input_files().end();
+       ++p)
     {
-      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);
-      switch (it->second.type)
-        {
-        case INCREMENTAL_INPUT_SCRIPT:
-          entry.put_data_offset(0);
-          break;
-        case INCREMENTAL_INPUT_ARCHIVE:
-        case INCREMENTAL_INPUT_OBJECT:
-        case INCREMENTAL_INPUT_SHARED_LIBRARY:
-          // TODO: add per input data.  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);
-          break;
-        default:
-          gold_unreachable();
-        }
-      entry.put_timestamp_sec(it->second.mtime.seconds);
-      entry.put_timestamp_nsec(it->second.mtime.nanoseconds);
-      entry.put_input_type(it->second.type);
-      entry.put_reserved(0);
+      // Set the offset of the input file entry.
+      (*p)->set_offset(input_offset);
+      input_offset += this->input_entry_size;
+
+      // Set the offset of the supplemental info block.
+      switch ((*p)->type())
+	{
+	case INCREMENTAL_INPUT_SCRIPT:
+	  // No supplemental info for a script.
+	  (*p)->set_info_offset(0);
+	  break;
+	case INCREMENTAL_INPUT_OBJECT:
+	case INCREMENTAL_INPUT_ARCHIVE_MEMBER:
+	  {
+	    Incremental_object_entry *entry = (*p)->object_entry();
+	    gold_assert(entry != NULL);
+	    (*p)->set_info_offset(info_offset);
+	    // Input section count + global symbol count.
+	    info_offset += 8;
+	    // Each input section.
+	    info_offset += (entry->get_input_section_count()
+			    * (8 + 2 * sizeof_addr));
+	    // Each global symbol.
+	    const Object::Symbols* syms = entry->object()->get_global_symbols();
+	    info_offset += syms->size() * 16;
+	  }
+	  break;
+	case INCREMENTAL_INPUT_SHARED_LIBRARY:
+	  {
+	    Incremental_object_entry *entry = (*p)->object_entry();
+	    gold_assert(entry != NULL);
+	    (*p)->set_info_offset(info_offset);
+	    // Global symbol count.
+	    info_offset += 4;
+	    // Each global symbol.
+	    const Object::Symbols* syms = entry->object()->get_global_symbols();
+	    unsigned int nsyms = syms != NULL ? syms->size() : 0;
+	    info_offset += nsyms * 4;
+	  }
+	  break;
+	case INCREMENTAL_INPUT_ARCHIVE:
+	  {
+	    Incremental_archive_entry *entry = (*p)->archive_entry();
+	    gold_assert(entry != NULL);
+	    (*p)->set_info_offset(info_offset);
+	    // Member count + unused global symbol count.
+	    info_offset += 8;
+	    // Each member.
+	    info_offset += (entry->get_member_count() * 4);
+	    // Each global symbol.
+	    info_offset += (entry->get_unused_global_symbol_count() * 4);
+	  }
+	  break;
+	default:
+	  gold_unreachable();
+	}
     }
 
-  return new Output_data_const_buffer(buffer, sz, 8,
-				      "** incremental link inputs list");
+  this->set_data_size(info_offset);
+
+  // Set the size of the .gnu_incremental_symtab section.
+  inputs->symtab_section()->set_current_data_size(this->symtab_->output_count()
+						  * sizeof(unsigned int));
+
+  // Set the size of the .gnu_incremental_relocs section.
+  inputs->relocs_section()->set_current_data_size(inputs->get_reloc_count()
+						  * rel_size);
+}
+
+// Write the contents of the .gnu_incremental_inputs and
+// .gnu_incremental_symtab sections.
+
+template<int size, bool big_endian>
+void
+Output_section_incremental_inputs<size, big_endian>::do_write(Output_file* of)
+{
+  const Incremental_inputs* inputs = this->inputs_;
+  Stringpool* strtab = inputs->get_stringpool();
+
+  // Get a view into the .gnu_incremental_inputs section.
+  const off_t off = this->offset();
+  const off_t oview_size = this->data_size();
+  unsigned char* const oview = of->get_output_view(off, oview_size);
+  unsigned char* pov = oview;
+
+  // Get a view into the .gnu_incremental_symtab section.
+  const off_t symtab_off = inputs->symtab_section()->offset();
+  const off_t symtab_size = inputs->symtab_section()->data_size();
+  unsigned char* const symtab_view = of->get_output_view(symtab_off,
+							 symtab_size);
+
+  // Allocate an array of linked list heads for the .gnu_incremental_symtab
+  // section.  Each element corresponds to a global symbol in the output
+  // symbol table, and points to the head of the linked list that threads
+  // through the object file input entries.  The value of each element
+  // is the section-relative offset to a global symbol entry in a
+  // supplemental information block.
+  unsigned int global_sym_count = this->symtab_->output_count();
+  unsigned int* global_syms = new unsigned int[global_sym_count];
+  memset(global_syms, 0, global_sym_count * sizeof(unsigned int));
+
+  // Write the section header.
+  Stringpool::Key command_line_key = inputs->command_line_key();
+  pov = this->write_header(pov, inputs->input_file_count(),
+			   strtab->get_offset_from_key(command_line_key));
+
+  // Write the list of input files.
+  pov = this->write_input_files(oview, pov, strtab);
+
+  // Write the supplemental information blocks for each input file.
+  pov = this->write_info_blocks(oview, pov, strtab, global_syms,
+				global_sym_count);
+
+  gold_assert(pov - oview == oview_size);
+
+  // Write the .gnu_incremental_symtab section.
+  gold_assert(global_sym_count * 4 == symtab_size);
+  this->write_symtab(symtab_view, global_syms, global_sym_count);
+
+  delete[] global_syms;
+
+  of->write_output_view(off, oview_size, oview);
+  of->write_output_view(symtab_off, symtab_size, symtab_view);
+}
+
+// Write the section header: version, input file count, offset of command line
+// in the string table, and 4 bytes of padding.
+
+template<int size, bool big_endian>
+unsigned char*
+Output_section_incremental_inputs<size, big_endian>::write_header(
+    unsigned char* pov,
+    unsigned int input_file_count,
+    section_offset_type command_line_offset)
+{
+  Swap32::writeval(pov, INCREMENTAL_LINK_VERSION);
+  Swap32::writeval(pov + 4, input_file_count);
+  Swap32::writeval(pov + 8, command_line_offset);
+  Swap32::writeval(pov + 12, 0);
+  return pov + this->header_size;
+}
+
+// Write the input file entries.
+
+template<int size, bool big_endian>
+unsigned char*
+Output_section_incremental_inputs<size, big_endian>::write_input_files(
+    unsigned char* oview,
+    unsigned char* pov,
+    Stringpool* strtab)
+{
+  const Incremental_inputs* inputs = this->inputs_;
+
+  for (Incremental_inputs::Input_list::const_iterator p =
+	   inputs->input_files().begin();
+       p != inputs->input_files().end();
+       ++p)
+    {
+      gold_assert(pov - oview == (*p)->get_offset());
+      section_offset_type filename_offset =
+          strtab->get_offset_from_key((*p)->get_filename_key());
+      const Timespec& mtime = (*p)->get_mtime();
+      Swap32::writeval(pov, filename_offset);
+      Swap32::writeval(pov + 4, (*p)->get_info_offset());
+      Swap64::writeval(pov + 8, mtime.seconds);
+      Swap32::writeval(pov + 16, mtime.nanoseconds);
+      Swap16::writeval(pov + 20, (*p)->type());
+      Swap16::writeval(pov + 22, 0);
+      pov += this->input_entry_size;
+    }
+  return pov;
+}
+
+// Write the supplemental information blocks.
+
+template<int size, bool big_endian>
+unsigned char*
+Output_section_incremental_inputs<size, big_endian>::write_info_blocks(
+    unsigned char* oview,
+    unsigned char* pov,
+    Stringpool* strtab,
+    unsigned int* global_syms,
+    unsigned int global_sym_count)
+{
+  const Incremental_inputs* inputs = this->inputs_;
+  unsigned int first_global_index = this->symtab_->first_global_index();
+
+  for (Incremental_inputs::Input_list::const_iterator p =
+	   inputs->input_files().begin();
+       p != inputs->input_files().end();
+       ++p)
+    {
+      switch ((*p)->type())
+	{
+	case INCREMENTAL_INPUT_SCRIPT:
+	  // No supplemental info for a script.
+	  break;
+
+	case INCREMENTAL_INPUT_OBJECT:
+	case INCREMENTAL_INPUT_ARCHIVE_MEMBER:
+	  {
+	    gold_assert(pov - oview == (*p)->get_info_offset());
+	    Incremental_object_entry* entry = (*p)->object_entry();
+	    gold_assert(entry != NULL);
+	    const Object* obj = entry->object();
+	    const Object::Symbols* syms = obj->get_global_symbols();
+	    // Write the input section count and global symbol count.
+	    unsigned int nsections = entry->get_input_section_count();
+	    unsigned int nsyms = syms->size();
+	    Swap32::writeval(pov, nsections);
+	    Swap32::writeval(pov + 4, nsyms);
+	    pov += 8;
+
+	    // For each input section, write the name, output section index,
+	    // offset within output section, and input section size.
+	    for (unsigned int i = 0; i < nsections; i++)
+	      {
+		Stringpool::Key key = entry->get_input_section_name_key(i);
+		off_t name_offset = 0;
+		if (key != 0)
+		  name_offset = strtab->get_offset_from_key(key);
+		int out_shndx = 0;
+		off_t out_offset = 0;
+		off_t sh_size = 0;
+		Output_section* os = obj->output_section(i);
+		if (os != NULL)
+		  {
+		    out_shndx = os->out_shndx();
+		    out_offset = obj->output_section_offset(i);
+		    sh_size = entry->get_input_section_size(i);
+		  }
+		Swap32::writeval(pov, name_offset);
+		Swap32::writeval(pov + 4, out_shndx);
+		Swap::writeval(pov + 8, out_offset);
+		Swap::writeval(pov + 8 + sizeof_addr, sh_size);
+		pov += 8 + 2 * sizeof_addr;
+	      }
+
+	    // For each global symbol, write its associated relocations,
+	    // add it to the linked list of globals, then write the
+	    // supplemental information:  global symbol table index,
+	    // linked list chain pointer, relocation count, and offset
+	    // to the relocations.
+	    for (unsigned int i = 0; i < nsyms; i++)
+	      {
+		const Symbol* sym = (*syms)[i];
+		unsigned int symtab_index = sym->symtab_index();
+		unsigned int chain = 0;
+		unsigned int first_reloc = 0;
+		unsigned int nrelocs = obj->get_incremental_reloc_count(i);
+		if (nrelocs > 0)
+		  {
+		    gold_assert(symtab_index != -1U
+				&& (symtab_index - first_global_index
+				    < global_sym_count));
+		    first_reloc = obj->get_incremental_reloc_base(i);
+		    chain = global_syms[symtab_index - first_global_index];
+		    global_syms[symtab_index - first_global_index] =
+			pov - oview;
+		  }
+		Swap32::writeval(pov, symtab_index);
+		Swap32::writeval(pov + 4, chain);
+		Swap32::writeval(pov + 8, nrelocs);
+		Swap32::writeval(pov + 12, first_reloc * 3 * sizeof_addr);
+		pov += 16;
+	      }
+	  }
+	  break;
+
+	case INCREMENTAL_INPUT_SHARED_LIBRARY:
+	  {
+	    gold_assert(pov - oview == (*p)->get_info_offset());
+	    Incremental_object_entry* entry = (*p)->object_entry();
+	    gold_assert(entry != NULL);
+	    const Object* obj = entry->object();
+	    const Object::Symbols* syms = obj->get_global_symbols();
+
+	    // Write the global symbol count.
+	    unsigned int nsyms = syms != NULL ? syms->size() : 0;
+	    Swap32::writeval(pov, nsyms);
+	    pov += 4;
+
+	    // For each global symbol, write the global symbol table index.
+	    for (unsigned int i = 0; i < nsyms; i++)
+	      {
+		const Symbol* sym = (*syms)[i];
+		Swap32::writeval(pov, sym->symtab_index());
+		pov += 4;
+	      }
+	  }
+	  break;
+
+	case INCREMENTAL_INPUT_ARCHIVE:
+	  {
+	    gold_assert(pov - oview == (*p)->get_info_offset());
+	    Incremental_archive_entry* entry = (*p)->archive_entry();
+	    gold_assert(entry != NULL);
+
+	    // Write the member count and unused global symbol count.
+	    unsigned int nmembers = entry->get_member_count();
+	    unsigned int nsyms = entry->get_unused_global_symbol_count();
+	    Swap32::writeval(pov, nmembers);
+	    Swap32::writeval(pov + 4, nsyms);
+	    pov += 8;
+
+	    // For each member, write the offset to its input file entry.
+	    for (unsigned int i = 0; i < nmembers; ++i)
+	      {
+		Incremental_object_entry* member = entry->get_member(i);
+		Swap32::writeval(pov, member->get_offset());
+		pov += 4;
+	      }
+
+	    // For each global symbol, write the name offset.
+	    for (unsigned int i = 0; i < nsyms; ++i)
+	      {
+		Stringpool::Key key = entry->get_unused_global_symbol(i);
+		Swap32::writeval(pov, strtab->get_offset_from_key(key));
+		pov += 4;
+	      }
+	  }
+	  break;
+
+	default:
+	  gold_unreachable();
+	}
+    }
+  return pov;
+}
+
+// Write the contents of the .gnu_incremental_symtab section.
+
+template<int size, bool big_endian>
+void
+Output_section_incremental_inputs<size, big_endian>::write_symtab(
+    unsigned char* pov,
+    unsigned int* global_syms,
+    unsigned int global_sym_count)
+{
+  for (unsigned int i = 0; i < global_sym_count; ++i)
+    {
+      Swap32::writeval(pov, global_syms[i]);
+      pov += 4;
+    }
 }
 
 // Instantiate the templates we need.
diff --git a/gold/incremental.h b/gold/incremental.h
index a94f397..979a94d 100644
--- a/gold/incremental.h
+++ b/gold/incremental.h
@@ -38,221 +38,21 @@ namespace gold
 class Archive;
 class Input_argument;
 class Incremental_inputs_checker;
+class Incremental_script_entry;
+class Incremental_object_entry;
+class Incremental_archive_entry;
+class Incremental_inputs;
 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
-};
-
-// Header of the .gnu_incremental_input section.
-struct Incremental_inputs_header_data
-{
-  // Incremental linker version.
-  elfcpp::Elf_Word version;
-
-  // Numer of input files in the link.
-  elfcpp::Elf_Word input_file_count;
-
-  // Offset of command line options in .gnu_incremental_strtab.
-  elfcpp::Elf_Word command_line_offset;
-
-  // Padding.
-  elfcpp::Elf_Word reserved;
-};
-
-// Reader class for .gnu_incremental_inputs header. See
-// Incremental_inputs_header_data for fields descriptions.
-
-template<int size, bool big_endian>
-class Incremental_inputs_header
-{
- private:
-  typedef Incremental_inputs_header_data Data_type;
-  typedef elfcpp::Convert<32, big_endian> Convert32;
-
- public:
-  Incremental_inputs_header(const unsigned char *p)
-    : p_(reinterpret_cast<const Data_type*>(p))
-  { }
-
-  static const int data_size = sizeof(Data_type);
-
-  elfcpp::Elf_Word
-  get_version() const
-  { return Convert32::convert_host(this->p_->version); }
-
-  elfcpp::Elf_Word
-  get_input_file_count() const
-  { return Convert32::convert_host(this->p_->input_file_count); }
-
-  elfcpp::Elf_Word
-  get_command_line_offset() const
-  { return Convert32::convert_host(this->p_->command_line_offset); }
-
-  elfcpp::Elf_Word
-  get_reserved() const
-  { return Convert32::convert_host(this->p_->reserved); }
-
- private:
-  const Data_type* p_;
-};
-
-// Writer class for .gnu_incremental_inputs header. See
-// Incremental_inputs_header_data for fields descriptions.
-
-template<int size, bool big_endian>
-class Incremental_inputs_header_write
-{
- private:
-  typedef Incremental_inputs_header_data Data_type;
-  typedef elfcpp::Convert<32, big_endian> Convert32;
-
- public:
-  Incremental_inputs_header_write(unsigned char *p)
-    : p_(reinterpret_cast<Data_type*>(p))
-  { }
-
-  static const int data_size = sizeof(Data_type);
-
-  void
-  put_version(elfcpp::Elf_Word v)
-  { this->p_->version = Convert32::convert_host(v); }
-
-  void
-  put_input_file_count(elfcpp::Elf_Word v)
-  { this->p_->input_file_count = Convert32::convert_host(v); }
-
-  void
-  put_command_line_offset(elfcpp::Elf_Word v)
-  { this->p_->command_line_offset = Convert32::convert_host(v); }
-
-  void
-  put_reserved(elfcpp::Elf_Word v)
-  { this->p_->reserved = Convert32::convert_host(v); }
-
- private:
-  Data_type* p_;
-};
-
-// Data stored in .gnu_incremental_input after the header for each of the
-// Incremental_input_header_data::input_file_count input entries.
-struct Incremental_inputs_entry_data
-{
-  // Offset of file name in .gnu_incremental_strtab section.
-  elfcpp::Elf_Word filename_offset;
-
-  // Offset of data in .gnu_incremental_input.
-  elfcpp::Elf_Word data_offset;
-
-  // Timestamp (in seconds).
-  elfcpp::Elf_Xword timestamp_sec;
-
-  // Nano-second part of timestamp (if supported).
-  elfcpp::Elf_Word timestamp_nsec;
-
-  // Type of the input entry.
-  elfcpp::Elf_Half input_type;
-
-  // Padding.
-  elfcpp::Elf_Half reserved;
-};
-
-// Reader class for an .gnu_incremental_inputs entry. See
-// Incremental_inputs_entry_data for fields descriptions.
-template<int size, bool big_endian>
-class Incremental_inputs_entry
-{
- private:
-  typedef Incremental_inputs_entry_data Data_type;
-  typedef elfcpp::Convert<32, big_endian> Convert32;
-  typedef elfcpp::Convert<64, big_endian> Convert64;
-
- public:
-  Incremental_inputs_entry(const unsigned char *p)
-    : p_(reinterpret_cast<const Data_type*>(p))
-  { }
-
-  static const int data_size = sizeof(Data_type);
-
-  elfcpp::Elf_Word
-  get_filename_offset()
-  { return Convert32::convert_host(this->p_->filename_offset); }
-
-  elfcpp::Elf_Word
-  get_data_offset()
-  { return Convert32::convert_host(this->p_->data_offset); }
-
-  elfcpp::Elf_Xword
-  get_timestamp_sec()
-  { return Convert64::convert_host(this->p_->timestamp_sec); }
-
-  elfcpp::Elf_Word
-  get_timestamp_nsec()
-  { return Convert32::convert_host(this->p_->timestamp_nsec); }
-
-  elfcpp::Elf_Word
-  get_input_type()
-  { return Convert32::convert_host(this->p_->input_type); }
-
-  elfcpp::Elf_Word
-  get_reserved()
-  { return Convert32::convert_host(this->p_->reserved); }
-
- private:
-  const Data_type* p_;
-};
-
-// Writer class for an .gnu_incremental_inputs entry. See
-// Incremental_inputs_entry_data for fields descriptions.
-template<int size, bool big_endian>
-class Incremental_inputs_entry_write
-{
- private:
-  typedef Incremental_inputs_entry_data Data_type;
-  typedef elfcpp::Convert<32, big_endian> Convert32;
-  typedef elfcpp::Convert<64, big_endian> Convert64;
-
- public:
-  Incremental_inputs_entry_write(unsigned char *p)
-    : p_(reinterpret_cast<Data_type*>(p))
-  { }
-
-  static const int data_size = sizeof(Data_type);
-
-  void
-  put_filename_offset(elfcpp::Elf_Word v)
-  { this->p_->filename_offset = Convert32::convert_host(v); }
-
-  void
-  put_data_offset(elfcpp::Elf_Word v)
-  { this->p_->data_offset = Convert32::convert_host(v); }
-
-  void
-  put_timestamp_sec(elfcpp::Elf_Xword v)
-  { this->p_->timestamp_sec = Convert64::convert_host(v); }
-
-  void
-  put_timestamp_nsec(elfcpp::Elf_Word v)
-  { this->p_->timestamp_nsec = Convert32::convert_host(v); }
-
-  void
-  put_input_type(elfcpp::Elf_Word v)
-  { this->p_->input_type = Convert32::convert_host(v); }
-
-  void
-  put_reserved(elfcpp::Elf_Word v)
-  { this->p_->reserved = Convert32::convert_host(v); }
-
- private:
-  Data_type* p_;
+  INCREMENTAL_INPUT_ARCHIVE_MEMBER = 2,
+  INCREMENTAL_INPUT_ARCHIVE = 3,
+  INCREMENTAL_INPUT_SHARED_LIBRARY = 4,
+  INCREMENTAL_INPUT_SCRIPT = 5
 };
 
 // An object representing the ELF file we edit during an incremental build.
@@ -315,20 +115,27 @@ class Incremental_binary
   };
 
   // Get a View given a Location.
-  View view(Location loc)
+  View
+  view(Location loc)
   { return View(this->view(loc.file_offset, loc.data_size)); }
 
   // Report an error.
   void
   error(const char* format, ...) const ATTRIBUTE_PRINTF_2;
 
-  // Find the .gnu_incremental_inputs section.  It selects the first section
-  // of type SHT_GNU_INCREMENTAL_INPUTS.  Returns false if such a section
-  // is not found.
+  // Find the .gnu_incremental_inputs and related sections.  It selects the
+  // first section of type SHT_GNU_INCREMENTAL_INPUTS,
+  // SHT_GNU_INCRMENTAL_SYMTAB, and SHT_GNU_INCREMENTAL_RELOCS.
+  // Returns false if the sections are not found.
   bool
-  find_incremental_inputs_section(Location* location,
-                                  unsigned int* strtab_shndx)
-  { return do_find_incremental_inputs_section(location, strtab_shndx); }
+  find_incremental_inputs_sections(unsigned int* p_inputs_shndx,
+				   unsigned int* p_symtab_shndx,
+				   unsigned int* p_relocs_shndx,
+				   unsigned int* p_strtab_shndx)
+  {
+    return do_find_incremental_inputs_sections(p_inputs_shndx, p_symtab_shndx,
+					       p_relocs_shndx, p_strtab_shndx);
+  }
 
   // Check the .gnu_incremental_inputs section to see whether an incremental
   // build is possible.
@@ -343,8 +150,10 @@ class Incremental_binary
  protected:
   // Find incremental inputs section.
   virtual bool
-  do_find_incremental_inputs_section(Location* location,
-                                     unsigned int* strtab_shndx) = 0;
+  do_find_incremental_inputs_sections(unsigned int* p_inputs_shndx,
+				      unsigned int* p_symtab_shndx,
+				      unsigned int* p_relocs_shndx,
+				      unsigned int* p_strtab_shndx) = 0;
 
   // Check the .gnu_incremental_inputs section to see whether an incremental
   // build is possible.
@@ -370,8 +179,10 @@ class Sized_incremental_binary : public Incremental_binary
 
  protected:
   virtual bool
-  do_find_incremental_inputs_section(Location* location,
-                                     unsigned int* strtab_shndx);
+  do_find_incremental_inputs_sections(unsigned int* p_inputs_shndx,
+				      unsigned int* p_symtab_shndx,
+				      unsigned int* p_relocs_shndx,
+				      unsigned int* p_strtab_shndx);
 
   virtual bool
   do_check_inputs(Incremental_inputs* incremental_inputs);
@@ -383,11 +194,13 @@ class Sized_incremental_binary : public Incremental_binary
 
 // Create an Incremental_binary object for FILE. Returns NULL is this is not
 // possible, e.g. FILE is not an ELF file or has an unsupported target.
+
 Incremental_binary*
 open_incremental_binary(Output_file* file);
 
 // Code invoked early during an incremental link that checks what files need
 // to be relinked.
+
 class Incremental_checker
 {
  public:
@@ -415,135 +228,726 @@ class Incremental_checker
   Incremental_inputs* incremental_inputs_;
 };
 
+// Base class for recording each input file.
+
+class Incremental_input_entry
+{
+ public:
+  Incremental_input_entry(Stringpool::Key filename_key, Timespec mtime)
+    : filename_key_(filename_key), offset_(0), info_offset_(0), mtime_(mtime)
+  { }
+
+  // Return the type of input file.
+  Incremental_input_type
+  type() const
+  { return this->do_type(); }
+
+  // Set the section offset of this input file entry.
+  void
+  set_offset(unsigned int offset)
+  { this->offset_ = offset; }
+
+  // Set the section offset of the supplemental information for this entry.
+  void
+  set_info_offset(unsigned int info_offset)
+  { this->info_offset_ = info_offset; }
+
+  // Get the section offset of this input file entry.
+  unsigned int
+  get_offset() const
+  { return this->offset_; }
+
+  // Get the section offset of the supplemental information for this entry.
+  unsigned int
+  get_info_offset() const
+  { return this->info_offset_; }
+
+  // Get the stringpool key for the input filename.
+  Stringpool::Key
+  get_filename_key() const
+  { return this->filename_key_; }
+
+  // Get the modification time of the input file.
+  const Timespec&
+  get_mtime() const
+  { return this->mtime_; }
+
+  // Return a pointer to the derived Incremental_script_entry object.
+  // Return NULL for input entries that are not script files.
+  Incremental_script_entry*
+  script_entry()
+  { return this->do_script_entry(); }
+
+  // Return a pointer to the derived Incremental_object_entry object.
+  // Return NULL for input entries that are not object files.
+  Incremental_object_entry*
+  object_entry()
+  { return this->do_object_entry(); }
+
+  // Return a pointer to the derived Incremental_archive_entry object.
+  // Return NULL for input entries that are not archive files.
+  Incremental_archive_entry*
+  archive_entry()
+  { return this->do_archive_entry(); }
+
+ protected:
+  // Return the type of input file.
+  virtual Incremental_input_type
+  do_type() const = 0;
+
+  // Return a pointer to the derived Incremental_script_entry object.
+  // Return NULL for input entries that are not script files.
+  virtual Incremental_script_entry*
+  do_script_entry()
+  { return NULL; }
+
+  // Return a pointer to the derived Incremental_object_entry object.
+  // Return NULL for input entries that are not object files.
+  virtual Incremental_object_entry*
+  do_object_entry()
+  { return NULL; }
+
+  // Return a pointer to the derived Incremental_archive_entry object.
+  // Return NULL for input entries that are not archive files.
+  virtual Incremental_archive_entry*
+  do_archive_entry()
+  { return NULL; }
+
+ private:
+  // Key of the filename string in the section stringtable.
+  Stringpool::Key filename_key_;
+
+  // Offset of the entry in the output section.
+  unsigned int offset_;
+
+  // Offset of the extra information in the output section.
+  unsigned int info_offset_;
+
+  // Last modification time of the file.
+  Timespec mtime_;
+};
+
+// Class for recording input scripts.
+
+class Incremental_script_entry : public Incremental_input_entry
+{
+ public:
+  Incremental_script_entry(Stringpool::Key filename_key, Script_info* script,
+			   Timespec mtime)
+    : Incremental_input_entry(filename_key, mtime), script_(script)
+  { }
+
+ protected:
+  virtual Incremental_input_type
+  do_type() const
+  { return INCREMENTAL_INPUT_SCRIPT; }
+
+  // Return a pointer to the derived Incremental_script_entry object.
+  virtual Incremental_script_entry*
+  do_script_entry()
+  { return this; }
+
+ private:
+  // Information about the script file.
+  Script_info* script_;
+};
+
+// Class for recording input object files.
+
+class Incremental_object_entry : public Incremental_input_entry
+{
+ public:
+  Incremental_object_entry(Stringpool::Key filename_key, Object* obj,
+			   Timespec mtime)
+    : Incremental_input_entry(filename_key, mtime), obj_(obj),
+      is_member_(false), sections_()
+  {
+    if (!obj_->is_dynamic())
+      this->sections_.reserve(obj->shnum());
+  }
+
+  // Get the object.
+  Object*
+  object() const
+  { return this->obj_; }
+
+  // Record that this object is an archive member.
+  void
+  set_is_member()
+  { this->is_member_ = true; }
+
+  // Return true if this object is an archive member.
+  bool
+  is_member() const
+  { return this->is_member_; }
+
+  // Add an input section.
+  void
+  add_input_section(unsigned int shndx, Stringpool::Key name_key, off_t sh_size)
+  {
+    if (shndx >= this->sections_.size())
+      this->sections_.resize(shndx + 1);
+    this->sections_[shndx].name_key = name_key;
+    this->sections_[shndx].sh_size = sh_size;
+  }
+
+  // Return the number of input sections in this object.
+  unsigned int
+  get_input_section_count() const
+  { return this->sections_.size(); }
+
+  // Return the stringpool key of the Nth input section.
+  Stringpool::Key
+  get_input_section_name_key(unsigned int n) const
+  { return this->sections_[n].name_key; }
+
+  // Return the size of the Nth input section.
+  off_t
+  get_input_section_size(unsigned int n) const
+  { return this->sections_[n].sh_size; }
+
+ protected:
+  virtual Incremental_input_type
+  do_type() const
+  {
+    return (this->is_member_
+	    ? INCREMENTAL_INPUT_ARCHIVE_MEMBER
+	    : (this->obj_->is_dynamic()
+	       ? INCREMENTAL_INPUT_SHARED_LIBRARY
+	       : INCREMENTAL_INPUT_OBJECT));
+  }
+
+  // Return a pointer to the derived Incremental_object_entry object.
+  virtual Incremental_object_entry*
+  do_object_entry()
+  { return this; }
+
+ private:
+  // The object file itself.
+  Object* obj_;
+
+  // Whether this object is an archive member.
+  bool is_member_;
+
+  // Input sections.
+  struct Input_section
+  {
+    Stringpool::Key name_key;
+    off_t sh_size;
+  };
+  std::vector<Input_section> sections_;
+};
+
+// Class for recording archive library input files.
+
+class Incremental_archive_entry : public Incremental_input_entry
+{
+ public:
+  Incremental_archive_entry(Stringpool::Key filename_key, Archive*,
+			    Timespec mtime)
+    : Incremental_input_entry(filename_key, mtime), members_(), unused_syms_()
+  { }
+
+  // Add a member object to the archive.
+  void
+  add_object(Incremental_object_entry* obj_entry)
+  {
+    this->members_.push_back(obj_entry);
+    obj_entry->set_is_member();
+  }
+
+  // Add an unused global symbol to the archive.
+  void
+  add_unused_global_symbol(Stringpool::Key symbol_key)
+  { this->unused_syms_.push_back(symbol_key); }
+
+  // Return the number of member objects included in the link.
+  unsigned int
+  get_member_count()
+  { return this->members_.size(); }
+
+  // Return the Nth member object.
+  Incremental_object_entry*
+  get_member(unsigned int n)
+  { return this->members_[n]; }
+
+  // Return the number of unused global symbols in this archive.
+  unsigned int
+  get_unused_global_symbol_count()
+  { return this->unused_syms_.size(); }
+
+  // Return the Nth unused global symbol.
+  Stringpool::Key
+  get_unused_global_symbol(unsigned int n)
+  { return this->unused_syms_[n]; }
+
+ protected:
+  virtual Incremental_input_type
+  do_type() const
+  { return INCREMENTAL_INPUT_ARCHIVE; }
+
+  // Return a pointer to the derived Incremental_archive_entry object.
+  virtual Incremental_archive_entry*
+  do_archive_entry()
+  { return this; }
+
+ private:
+  // Members of the archive that have been included in the link.
+  std::vector<Incremental_object_entry*> members_;
+
+  // Unused global symbols from this archive.
+  std::vector<Stringpool::Key> unused_syms_;
+};
+
 // This class contains the information needed during an incremental
 // build about the inputs necessary to build the .gnu_incremental_inputs.
+
 class Incremental_inputs
 {
  public:
+  typedef std::vector<Incremental_input_entry*> Input_list;
+
   Incremental_inputs()
-    : lock_(new Lock()), inputs_(NULL), command_line_key_(0),
-      strtab_(new Stringpool())
+    : inputs_(), command_line_(), command_line_key_(0),
+      strtab_(new Stringpool()), current_object_(NULL),
+      current_object_entry_(NULL), inputs_section_(NULL),
+      symtab_section_(NULL), relocs_section_(NULL),
+      reloc_count_(0)
   { }
+
   ~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.
+  // Record the initial info for archive file ARCHIVE.
+  void
+  report_archive_begin(Archive* arch);
+
+  // Record the final info for archive file ARCHIVE.
+  void
+  report_archive_end(Archive* arch);
+
+  // Record the info for object file OBJ.  If ARCH is not NULL,
+  // attach the object file to the archive.
   void
-  report_inputs(const Input_arguments& inputs)
-  { this->inputs_ = &inputs; }
+  report_object(Object* obj, Archive* arch);
 
-  // Record that the input argument INPUT is an archive ARCHIVE.
+  // Record an input section belonging to object file OBJ.
   void
-  report_archive(const Input_argument* input, Archive* archive);
+  report_input_section(Object* obj, unsigned int shndx, const char* name,
+		       off_t sh_size);
 
-  // Record that the input argument INPUT is to an object OBJ.
+  // Record the info for input script SCRIPT.
   void
-  report_object(const Input_argument* input, Object* obj);
+  report_script(const std::string& filename, Script_info* script,
+		Timespec mtime);
+
+  // Return the running count of incremental relocations.
+  unsigned int
+  get_reloc_count() const
+  { return this->reloc_count_; }
 
-  // Record that the input argument INPUT is to an script SCRIPT.
+  // Update the running count of incremental relocations.
   void
-  report_script(const Input_argument* input, Timespec mtime,
-                Script_info* script);
+  set_reloc_count(unsigned int count)
+  { this->reloc_count_ = count; }
 
   // Prepare for layout.  Called from Layout::finalize.
   void
   finalize();
 
-  // Create the content of the .gnu_incremental_inputs section.
+  // Create the .gnu_incremental_inputs and related sections.
+  void
+  create_data_sections(Symbol_table* symtab);
+
+  // Return the .gnu_incremental_inputs section.
   Output_section_data*
-  create_incremental_inputs_section_data();
+  inputs_section() const
+  { return this->inputs_section_; }
+
+  // Return the .gnu_incremental_symtab section.
+  Output_data_space*
+  symtab_section() const
+  { return this->symtab_section_; }
+
+  // Return the .gnu_incremental_relocs section.
+  Output_data_space*
+  relocs_section() const
+  { return this->relocs_section_; }
 
   // Return the .gnu_incremental_strtab stringpool.
   Stringpool*
-  get_stringpool()
+  get_stringpool() const
   { return this->strtab_; }
 
   // Return the canonical form of the command line, as will be stored in
   // .gnu_incremental_strtab.
   const std::string&
-  command_line()
+  command_line() const
   { return this->command_line_; }
 
-  // Return the input files found in the command line.
-  const Input_arguments*
-  inputs()
+  // Return the stringpool key of the command line.
+  Stringpool::Key
+  command_line_key() const
+  { return this->command_line_key_; }
+
+  // Return the number of input files.
+  int
+  input_file_count() const
+  { return this->inputs_.size(); }
+
+  // Return the input files.
+  const Input_list&
+  input_files() const
   { return this->inputs_; }
 
+  // Return the sh_entsize value for the .gnu_incremental_relocs section.
+  unsigned int
+  relocs_entsize() const;
+
  private:
-  // 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();
+  // The list of input files.
+  Input_list inputs_;
 
-  // 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.
+  // Canonical form of the command line, as will be stored in
+  // .gnu_incremental_strtab.
+  std::string command_line_;
 
-  void
-  finalize_inputs(Input_argument_list::const_iterator begin,
-		  Input_argument_list::const_iterator end,
-		  unsigned int* index);
+  // The key of the command line string in the string pool.
+  Stringpool::Key command_line_key_;
+
+  // The .gnu_incremental_strtab string pool associated with the
+  // .gnu_incremental_inputs.
+  Stringpool* strtab_;
+
+  // Keep track of the object currently being processed.
+  Object* current_object_;
+  Incremental_object_entry* current_object_entry_;
+
+  // The .gnu_incremental_inputs section.
+  Output_section_data* inputs_section_;
 
-  // Additional data about an input needed for an incremental link.
-  // None of these pointers is owned by the structure.
-  struct Input_info
+  // The .gnu_incremental_symtab section.
+  Output_data_space* symtab_section_;
+
+  // The .gnu_incremental_relocs section.
+  Output_data_space* relocs_section_;
+
+  // Total count of incremental relocations.  Updated during Scan_relocs
+  // phase at the completion of each object file.
+  unsigned int reloc_count_;
+};
+
+// Reader class for .gnu_incremental_inputs section.
+
+template<int size, bool big_endian>
+class Incremental_inputs_reader
+{
+ private:
+  typedef elfcpp::Swap<size, big_endian> Swap;
+  typedef elfcpp::Swap<16, big_endian> Swap16;
+  typedef elfcpp::Swap<32, big_endian> Swap32;
+  typedef elfcpp::Swap<64, big_endian> Swap64;
+
+ public:
+  Incremental_inputs_reader(const unsigned char* p, elfcpp::Elf_strtab& strtab)
+    : p_(p), strtab_(strtab)
+  { this->input_file_count_ = Swap32::readval(this->p_ + 4); }
+
+  // Return the version number.
+  unsigned int
+  version() const
+  { return Swap32::readval(this->p_); }
+
+  // Return the count of input file entries.
+  unsigned int
+  input_file_count() const
+  { return this->input_file_count_; }
+
+  // Return the command line.
+  const char*
+  command_line() const
   {
-    Input_info()
-      : type(INCREMENTAL_INPUT_INVALID), archive(NULL), filename_key(0),
-        index(0)
-    { }
+    unsigned int offset = Swap32::readval(this->p_ + 8);
+    return this->get_string(offset);
+  }
 
-    // Type of the file pointed by this argument.
-    Incremental_input_type type;
+  // Reader class for an input file entry and its supplemental info.
+  class Incremental_input_entry_reader
+  {
+   public:
+    Incremental_input_entry_reader(const Incremental_inputs_reader* inputs,
+				   unsigned int offset)
+      : inputs_(inputs), offset_(offset)
+    {
+      this->info_offset_ = Swap32::readval(inputs->p_ + offset + 4);
+      int type = Swap16::readval(this->inputs_->p_ + offset + 20);
+      this->type_ = static_cast<Incremental_input_type>(type);
+    }
+
+    // Return the filename.
+    const char*
+    filename() const
+    {
+      unsigned int offset = Swap32::readval(this->inputs_->p_ + this->offset_);
+      return this->inputs_->get_string(offset);
+    }
 
-    union
+    // Return the timestamp.
+    Timespec
+    get_mtime() const
+    {
+      Timespec t;
+      const unsigned char* p = this->inputs_->p_ + this->offset_ + 8;
+      t.seconds = Swap64::readval(p);
+      t.nanoseconds = Swap32::readval(p+8);
+      return t;
+    }
+
+    // Return the type of input file.
+    Incremental_input_type
+    type() const
+    { return this->type_; }
+
+    // Return the input section count -- for objects only.
+    unsigned int
+    get_input_section_count() const
     {
-      // Present if type == INCREMENTAL_INPUT_ARCHIVE.
-      Archive* archive;
+      gold_assert(this->type_ == INCREMENTAL_INPUT_OBJECT
+		  || this->type_ == INCREMENTAL_INPUT_ARCHIVE_MEMBER);
+      return Swap32::readval(this->inputs_->p_ + this->info_offset_);
+    }
+
+    // Return the offset of the supplemental info for symbol SYMNDX --
+    // for objects only.
+    unsigned int
+    get_symbol_offset(unsigned int symndx) const
+    {
+      gold_assert(this->type_ == INCREMENTAL_INPUT_OBJECT
+		  || this->type_ == INCREMENTAL_INPUT_ARCHIVE_MEMBER);
+
+      unsigned int section_count = this->get_input_section_count();
+      return (this->info_offset_ + 8
+	      + section_count * input_section_entry_size
+	      + symndx * 16);
+    }
+
+    // Return the global symbol count -- for objects & shared libraries only.
+    unsigned int
+    get_global_symbol_count() const
+    {
+      switch (this->type_)
+	{
+	case INCREMENTAL_INPUT_OBJECT:
+	case INCREMENTAL_INPUT_ARCHIVE_MEMBER:
+	  return Swap32::readval(this->inputs_->p_ + this->info_offset_ + 4);
+	case INCREMENTAL_INPUT_SHARED_LIBRARY:
+	  return Swap32::readval(this->inputs_->p_ + this->info_offset_);
+	default:
+	  gold_unreachable();
+	}
+    }
+
+    // Return the member count -- for archives only.
+    unsigned int
+    get_member_count() const
+    {
+      gold_assert(this->type_ == INCREMENTAL_INPUT_ARCHIVE);
+      return Swap32::readval(this->inputs_->p_ + this->info_offset_);
+    }
 
-      // Present if type == INCREMENTAL_INPUT_OBJECT or
-      // INCREMENTAL_INPUT_SHARED_LIBRARY.
-      Object* object;
+    // Return the unused symbol count -- for archives only.
+    unsigned int
+    get_unused_symbol_count() const
+    {
+      gold_assert(this->type_ == INCREMENTAL_INPUT_ARCHIVE);
+      return Swap32::readval(this->inputs_->p_ + this->info_offset_ + 4);
+    }
 
-      // Present if type == INCREMENTAL_INPUT_SCRIPT.
-      Script_info* script;
+    // Return the input file offset for archive member N -- for archives only.
+    unsigned int
+    get_member_offset(unsigned int n) const
+    {
+      gold_assert(this->type_ == INCREMENTAL_INPUT_ARCHIVE);
+      return Swap32::readval(this->inputs_->p_ + this->info_offset_
+			     + 8 + n * 4);
+    }
+
+    // Return the Nth unused global symbol -- for archives only.
+    const char*
+    get_unused_symbol(unsigned int n) const
+    {
+      gold_assert(this->type_ == INCREMENTAL_INPUT_ARCHIVE);
+      unsigned int member_count = this->get_member_count();
+      unsigned int offset = Swap32::readval(this->inputs_->p_
+					    + this->info_offset_ + 8
+					    + member_count * 4
+					    + n * 4);
+      return this->inputs_->get_string(offset);
+    }
+
+    // Information about an input section.
+    struct Input_section_info
+    {
+      const char* name;
+      unsigned int output_shndx;
+      off_t sh_offset;
+      off_t sh_size;
     };
 
-    // Key of the filename string in the section stringtable.
-    Stringpool::Key filename_key;
+    // Return info about the Nth input section -- for objects only.
+    Input_section_info
+    get_input_section(unsigned int n) const
+    {
+      Input_section_info info;
+      const unsigned char* p = (this->inputs_->p_
+				+ this->info_offset_ + 8
+				+ n * input_section_entry_size);
+      unsigned int name_offset = Swap32::readval(p);
+      info.name = this->inputs_->get_string(name_offset);
+      info.output_shndx = Swap32::readval(p + 4);
+      info.sh_offset = Swap::readval(p + 8);
+      info.sh_size = Swap::readval(p + 8 + size / 8);
+      return info;
+    }
+
+    // Information about a global symbol.
+    struct Global_symbol_info
+    {
+      unsigned int output_symndx;
+      unsigned int next_offset;
+      unsigned int reloc_count;
+      unsigned int reloc_offset;
+    };
 
-    // Position of the entry information in the output section.
-    unsigned int index;
+    // Return info about the Nth global symbol -- for objects only.
+    Global_symbol_info
+    get_global_symbol_info(unsigned int n) const
+    {
+      Global_symbol_info info;
+      unsigned int section_count = this->get_input_section_count();
+      const unsigned char* p = (this->inputs_->p_
+				+ this->info_offset_ + 8
+				+ section_count * input_section_entry_size
+				+ n * 16);
+      info.output_symndx = Swap32::readval(p);
+      info.next_offset = Swap32::readval(p + 4);
+      info.reloc_count = Swap::readval(p + 8);
+      info.reloc_offset = Swap::readval(p + 12);
+      return info;
+    }
 
-    // Last modification time of the file.
-    Timespec mtime;
+   private:
+    // Size of an input section entry.
+    static const unsigned int input_section_entry_size = 8 + 2 * size / 8;
+    // The reader instance for the containing section.
+    const Incremental_inputs_reader* inputs_;
+    // The type of input file.
+    Incremental_input_type type_;
+    // Section offset to the input file entry.
+    unsigned int offset_;
+    // Section offset to the supplemental info for the input file.
+    unsigned int info_offset_;
   };
 
-  typedef std::map<const Input_argument*, Input_info> Inputs_info_map;
+  // Return a reader for the Nth input file entry.
+  Incremental_input_entry_reader
+  input_file(unsigned int n) const
+  {
+    gold_assert(n < this->input_file_count_);
+    Incremental_input_entry_reader input(this, 16 + n * 24);
+    return input;
+  }
+
+ private:
+  // Lookup a string in the ELF string table.
+  const char* get_string(unsigned int offset) const
+  {
+    const char* s;
+    if (this->strtab_.get_c_string(offset, &s))
+      return s;
+    return NULL;
+  }
+
+  // Base address of the .gnu_incremental_inputs section.
+  const unsigned char* p_;
+  // The associated ELF string table.
+  elfcpp::Elf_strtab strtab_;
+  // The number of input file entries in this section.
+  unsigned int input_file_count_;
+};
+
+// Reader class for the .gnu_incremental_symtab section.
 
-  // A lock guarding access to inputs_ during the first phase of linking, when
-  // report_ function may be called from multiple threads.
-  Lock* lock_;
+template<bool big_endian>
+class Incremental_symtab_reader
+{
+ public:
+  Incremental_symtab_reader(const unsigned char* p) : p_(p)
+  { }
 
-  // The list of input arguments obtained from parsing the command line.
-  const Input_arguments* inputs_;
+  // Return the list head for symbol table entry N.
+  unsigned int get_list_head(unsigned int n)
+  { return elfcpp::Swap<32, big_endian>::readval(this->p_ + 4 * n); }
 
-  // A map containing additional information about the input elements.
-  Inputs_info_map inputs_map_;
+ private:
+  // Base address of the .gnu_incremental_relocs section.
+  const unsigned char* p_;
+};
 
-  // Canonical form of the command line, as will be stored in
-  // .gnu_incremental_strtab.
-  std::string command_line_;
+// Reader class for the .gnu_incremental_relocs section.
 
-  // The key of the command line string in the string pool.
-  Stringpool::Key command_line_key_;
+template<int size, bool big_endian>
+class Incremental_relocs_reader
+{
+ private:
+  // Size of each field.
+  static const unsigned int field_size = size / 8;
 
-  // The .gnu_incremental_strtab string pool associated with the
-  // .gnu_incremental_inputs.
-  Stringpool* strtab_;
+ public:
+  typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+  typedef typename elfcpp::Elf_types<size>::Elf_Swxword Addend;
+
+  // Size of each entry.
+  static const unsigned int reloc_size = 8 + 2 * field_size;
+
+  Incremental_relocs_reader(const unsigned char* p) : p_(p)
+  { }
+
+  // Return the relocation type for relocation entry at offset OFF.
+  unsigned int
+  get_r_type(unsigned int off)
+  {
+    return elfcpp::Swap<32, big_endian>::readval(this->p_ + off);
+  }
+
+  // Return the output section index for relocation entry at offset OFF.
+  unsigned int
+  get_r_shndx(unsigned int off)
+  {
+    return elfcpp::Swap<32, big_endian>::readval(this->p_ + off + 4);
+  }
+
+  // Return the output section offset for relocation entry at offset OFF.
+  Address
+  get_r_offset(unsigned int off)
+  {
+    return elfcpp::Swap<size, big_endian>::readval(this->p_ + off + 8);
+  }
+
+  // Return the addend for relocation entry at offset OFF.
+  Addend
+  get_r_addend(unsigned int off)
+  {
+    return elfcpp::Swap<size, big_endian>::readval(this->p_ + off + 8
+						   + this->field_size);
+  }
+
+ private:
+  // Base address of the .gnu_incremental_relocs section.
+  const unsigned char* p_;
 };
 
 } // End namespace gold.
diff --git a/gold/layout.cc b/gold/layout.cc
index 3a01e92..eb1322a 100644
--- a/gold/layout.cc
+++ b/gold/layout.cc
@@ -974,7 +974,7 @@ Layout::make_output_section(const char* name, elfcpp::Elf_Word type,
       if (this->debug_abbrev_)
         this->debug_info_->set_abbreviations(this->debug_abbrev_);
     }
- else
+  else
     {
       // FIXME: const_cast is ugly.
       Target* target = const_cast<Target*>(&parameters->target());
@@ -1886,12 +1886,6 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab,
       this->set_dynamic_symbol_size(symtab);
     }
   
-  if (this->incremental_inputs_)
-    {
-      this->incremental_inputs_->finalize();
-      this->create_incremental_info_sections();
-    }
-
   // Create segment headers.
   Output_segment_headers* segment_headers =
     (parameters->options().relocatable()
@@ -1951,6 +1945,13 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab,
   // be called after the symbol table has been finalized.
   this->script_options_->finalize_symbols(symtab, this);
 
+  // Create the incremental inputs sections.
+  if (this->incremental_inputs_)
+    {
+      this->incremental_inputs_->finalize();
+      this->create_incremental_info_sections(symtab);
+    }
+
   // Create the .shstrtab section.
   Output_section* shstrtab_section = this->create_shstrtab();
 
@@ -1968,8 +1969,13 @@ Layout::finalize(const Input_objects* input_objects, Symbol_table* symtab,
   // If there are no sections which require postprocessing, we can
   // handle the section names now, and avoid a resize later.
   if (!this->any_postprocessing_sections_)
-    off = this->set_section_offsets(off,
+    {
+      off = this->set_section_offsets(off,
+				      POSTPROCESSING_SECTIONS_PASS);
+      off =
+	  this->set_section_offsets(off,
 				    STRTAB_AFTER_POSTPROCESSING_SECTIONS_PASS);
+    }
 
   file_header->set_section_info(this->section_headers_, shstrtab_section);
 
@@ -2294,37 +2300,65 @@ Layout::link_stabs_sections()
     }
 }
 
-// Create .gnu_incremental_inputs and .gnu_incremental_strtab sections needed
+// Create .gnu_incremental_inputs and related sections needed
 // for the next run of incremental linking to check what has changed.
 
 void
-Layout::create_incremental_info_sections()
+Layout::create_incremental_info_sections(Symbol_table* symtab)
 {
-  gold_assert(this->incremental_inputs_ != NULL);
+  Incremental_inputs* incr = this->incremental_inputs_;
+
+  gold_assert(incr != NULL);
+
+  // Create the .gnu_incremental_inputs, _symtab, and _relocs input sections.
+  incr->create_data_sections(symtab);
 
   // Add the .gnu_incremental_inputs section.
   const char *incremental_inputs_name =
     this->namepool_.add(".gnu_incremental_inputs", false, NULL);
-  Output_section* inputs_os =
+  Output_section* incremental_inputs_os =
     this->make_output_section(incremental_inputs_name,
 			      elfcpp::SHT_GNU_INCREMENTAL_INPUTS, 0,
 			      ORDER_INVALID, false);
-  Output_section_data* posd =
-      this->incremental_inputs_->create_incremental_inputs_section_data();
-  inputs_os->add_output_section_data(posd);
-  
+  incremental_inputs_os->add_output_section_data(incr->inputs_section());
+
+  // Add the .gnu_incremental_symtab section.
+  const char *incremental_symtab_name =
+    this->namepool_.add(".gnu_incremental_symtab", false, NULL);
+  Output_section* incremental_symtab_os =
+    this->make_output_section(incremental_symtab_name,
+			      elfcpp::SHT_GNU_INCREMENTAL_SYMTAB, 0,
+			      ORDER_INVALID, false);
+  incremental_symtab_os->add_output_section_data(incr->symtab_section());
+  incremental_symtab_os->set_entsize(4);
+
+  // Add the .gnu_incremental_relocs section.
+  const char *incremental_relocs_name =
+    this->namepool_.add(".gnu_incremental_relocs", false, NULL);
+  Output_section* incremental_relocs_os =
+    this->make_output_section(incremental_relocs_name,
+			      elfcpp::SHT_GNU_INCREMENTAL_RELOCS, 0,
+			      ORDER_INVALID, false);
+  incremental_relocs_os->add_output_section_data(incr->relocs_section());
+  incremental_relocs_os->set_entsize(incr->relocs_entsize());
+
   // Add the .gnu_incremental_strtab section.
   const char *incremental_strtab_name =
     this->namepool_.add(".gnu_incremental_strtab", false, NULL);
-  Output_section* strtab_os = this->make_output_section(incremental_strtab_name,
-                                                        elfcpp::SHT_STRTAB,
-                                                        0, ORDER_INVALID,
-							false);
+  Output_section* incremental_strtab_os = this->make_output_section(incremental_strtab_name,
+                                                        elfcpp::SHT_STRTAB, 0,
+                                                        ORDER_INVALID, false);
   Output_data_strtab* strtab_data =
-    new Output_data_strtab(this->incremental_inputs_->get_stringpool());
-  strtab_os->add_output_section_data(strtab_data);
-  
-  inputs_os->set_link_section(strtab_data);
+      new Output_data_strtab(incr->get_stringpool());
+  incremental_strtab_os->add_output_section_data(strtab_data);
+
+  incremental_inputs_os->set_after_input_sections();
+  incremental_symtab_os->set_after_input_sections();
+  incremental_relocs_os->set_after_input_sections();
+
+  incremental_inputs_os->set_link_section(incremental_strtab_os);
+  incremental_symtab_os->set_link_section(incremental_inputs_os);
+  incremental_relocs_os->set_link_section(incremental_inputs_os);
 }
 
 // Return whether SEG1 should be before SEG2 in the output file.  This
diff --git a/gold/layout.h b/gold/layout.h
index b5b266c..88133eb 100644
--- a/gold/layout.h
+++ b/gold/layout.h
@@ -58,6 +58,7 @@ class Output_reduced_debug_abbrev_section;
 class Output_reduced_debug_info_section;
 class Eh_frame;
 class Target;
+struct Timespec;
 
 // Return TRUE if SECNAME is the name of a compressed debug section.
 extern bool
@@ -662,7 +663,7 @@ class Layout
   // Return the object managing inputs in incremental build. NULL in
   // non-incremental builds.
   Incremental_inputs*
-  incremental_inputs()
+  incremental_inputs() const
   { return this->incremental_inputs_; }
 
   // For the target-specific code to add dynamic tags which are common
@@ -800,7 +801,7 @@ class Layout
   // Create .gnu_incremental_inputs and .gnu_incremental_strtab sections needed
   // for the next run of incremental linking to check what has changed.
   void
-  create_incremental_info_sections();
+  create_incremental_info_sections(Symbol_table*);
 
   // Find the first read-only PT_LOAD segment, creating one if
   // necessary.
diff --git a/gold/main.cc b/gold/main.cc
index 631f1ac..91d5193 100644
--- a/gold/main.cc
+++ b/gold/main.cc
@@ -229,10 +229,7 @@ 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_inputs(command_line.inputs());
-    }
+    layout.incremental_inputs()->report_command_line(argc, argv);
 
   if (parameters->options().section_ordering_file())
     layout.read_layout_from_file();
diff --git a/gold/object.cc b/gold/object.cc
index 1bf7367..b3d9870 100644
--- a/gold/object.cc
+++ b/gold/object.cc
@@ -40,6 +40,7 @@
 #include "dynobj.h"
 #include "plugin.h"
 #include "compressed_output.h"
+#include "incremental.h"
 
 namespace gold
 {
@@ -346,6 +347,30 @@ Relobj::is_section_name_included(const char* name)
   return false;
 }
 
+// Finalize the incremental relocation information.  Allocates a block
+// of relocation entries for each symbol, and sets the reloc_bases_
+// array to point to the first entry in each block.  Returns the next
+// available reloation index.
+
+void
+Relobj::finalize_incremental_relocs(Layout* layout)
+{
+  unsigned int nsyms = this->get_global_symbols()->size();
+  this->reloc_bases_ = new unsigned int[nsyms];
+
+  gold_assert(this->reloc_bases_ != NULL);
+  gold_assert(layout->incremental_inputs() != NULL);
+
+  unsigned int rindex = layout->incremental_inputs()->get_reloc_count();
+  for (unsigned int i = 0; i < nsyms; ++i)
+    {
+      this->reloc_bases_[i] = rindex;
+      rindex += this->reloc_counts_[i];
+      this->reloc_counts_[i] = 0;
+    }
+  layout->incremental_inputs()->set_reloc_count(rindex);
+}
+
 // Class Sized_relobj.
 
 template<int size, bool big_endian>
@@ -1237,6 +1262,13 @@ Sized_relobj<size, big_endian>::do_layout(Symbol_table* symtab,
 	        }
 	    }
 
+	  // Add the section to the incremental inputs layout.
+	  Incremental_inputs* incremental_inputs = layout->incremental_inputs();
+	  if (incremental_inputs != NULL)
+	    incremental_inputs->report_input_section(this, i,
+						     discard ? NULL : name,
+						     shdr.get_sh_size());
+
           if (discard)
             {
 	      // Do not include this section in the link.
diff --git a/gold/object.h b/gold/object.h
index 59b723f..99ceabf 100644
--- a/gold/object.h
+++ b/gold/object.h
@@ -387,6 +387,18 @@ class Object
   section_addralign(unsigned int shndx)
   { return this->do_section_addralign(shndx); }
 
+  // Return the output section given a section index.
+  Output_section*
+  output_section(unsigned int shndx) const
+  { return this->do_output_section(shndx); }
+
+  // Given a section index, return the offset in the Output_section.
+  // The return value will be -1U if the section is specially mapped,
+  // such as a merge section.
+  uint64_t
+  output_section_offset(unsigned int shndx) const
+  { return this->do_output_section_offset(shndx); }
+
   // Read the symbol information.
   void
   read_symbols(Read_symbols_data* sd)
@@ -525,6 +537,16 @@ class Object
 			section_size_type* uncompressed_size) const
   { return this->do_section_is_compressed(shndx, uncompressed_size); }
 
+  // Return the index of the first incremental relocation for symbol SYMNDX.
+  unsigned int
+  get_incremental_reloc_base(unsigned int symndx) const
+  { return this->do_get_incremental_reloc_base(symndx); }
+
+  // Return the number of incremental relocations for symbol SYMNDX.
+  unsigned int
+  get_incremental_reloc_count(unsigned int symndx) const
+  { return this->do_get_incremental_reloc_count(symndx); }
+
  protected:
   // Returns NULL for Objects that are not plugin objects.  This method
   // is overridden in the Pluginobj class.
@@ -590,6 +612,17 @@ class Object
   virtual uint64_t
   do_section_addralign(unsigned int shndx) = 0;
 
+  // Return the output section given a section index--implemented
+  // by child class.
+  virtual Output_section*
+  do_output_section(unsigned int) const
+  { gold_unreachable(); }
+
+  // Get the offset of a section--implemented by child class.
+  virtual uint64_t
+  do_output_section_offset(unsigned int) const
+  { gold_unreachable(); }
+
   // Return the Xindex structure to use.
   virtual Xindex*
   do_initialize_xindex() = 0;
@@ -641,6 +674,18 @@ class Object
   do_section_is_compressed(unsigned int, section_size_type*) const
   { return false; }
 
+  // Return the index of the first incremental relocation for symbol SYMNDX--
+  // implemented by child class.
+  virtual unsigned int
+  do_get_incremental_reloc_base(unsigned int) const
+  { gold_unreachable(); }
+
+  // Return the number of incremental relocations for symbol SYMNDX--
+  // implemented by child class.
+  virtual unsigned int
+  do_get_incremental_reloc_count(unsigned int) const
+  { gold_unreachable(); }
+
  private:
   // This class may not be copied.
   Object(const Object&);
@@ -685,7 +730,9 @@ class Relobj : public Object
       map_to_relocatable_relocs_(NULL),
       object_merge_map_(NULL),
       relocs_must_follow_section_writes_(false),
-      sd_(NULL)
+      sd_(NULL),
+      reloc_counts_(NULL),
+      reloc_bases_(NULL)
   { }
 
   // During garbage collection, the Read_symbols_data pass for 
@@ -781,16 +828,6 @@ class Relobj : public Object
     return this->output_sections_[shndx] != NULL;
   }
 
-  // Given a section index, return the corresponding Output_section.
-  // The return value will be NULL if the section is not included in
-  // the link.
-  Output_section*
-  output_section(unsigned int shndx) const
-  {
-    gold_assert(shndx < this->output_sections_.size());
-    return this->output_sections_[shndx];
-  }
-
   // The the output section of the input section with index SHNDX.
   // This is only used currently to remove a section from the link in
   // relaxation.
@@ -801,13 +838,6 @@ class Relobj : public Object
     this->output_sections_[shndx] = os;
   }
   
-  // Given a section index, return the offset in the Output_section.
-  // The return value will be -1U if the section is specially mapped,
-  // such as a merge section.
-  uint64_t
-  output_section_offset(unsigned int shndx) const
-  { return this->do_output_section_offset(shndx); }
-
   // Set the offset of an input section within its output section.
   void
   set_section_offset(unsigned int shndx, uint64_t off)
@@ -856,6 +886,16 @@ class Relobj : public Object
   layout_deferred_sections(Layout* layout)
   { this->do_layout_deferred_sections(layout); }
 
+  // Return the index of the first incremental relocation for symbol SYMNDX.
+  virtual unsigned int
+  do_get_incremental_reloc_base(unsigned int symndx) const
+  { return this->reloc_bases_[symndx]; }
+
+  // Return the number of incremental relocations for symbol SYMNDX.
+  virtual unsigned int
+  do_get_incremental_reloc_count(unsigned int symndx) const
+  { return this->reloc_counts_[symndx]; }
+
  protected:
   // The output section to be used for each input section, indexed by
   // the input section number.  The output section is NULL if the
@@ -902,10 +942,6 @@ class Relobj : public Object
   virtual void
   do_relocate(const Symbol_table* symtab, const Layout*, Output_file* of) = 0;
 
-  // Get the offset of a section--implemented by child class.
-  virtual uint64_t
-  do_output_section_offset(unsigned int shndx) const = 0;
-
   // Set the offset of a section--implemented by child class.
   virtual void
   do_set_section_offset(unsigned int shndx, uint64_t off) = 0;
@@ -915,6 +951,16 @@ class Relobj : public Object
   virtual void
   do_layout_deferred_sections(Layout*) = 0;
 
+  // Given a section index, return the corresponding Output_section.
+  // The return value will be NULL if the section is not included in
+  // the link.
+  Output_section*
+  do_output_section(unsigned int shndx) const
+  {
+    gold_assert(shndx < this->output_sections_.size());
+    return this->output_sections_[shndx];
+  }
+
   // Return the vector mapping input sections to output sections.
   Output_sections&
   output_sections()
@@ -938,6 +984,46 @@ class Relobj : public Object
   set_relocs_must_follow_section_writes()
   { this->relocs_must_follow_section_writes_ = true; }
 
+  // Allocate the array for counting incremental relocations.
+  void
+  allocate_incremental_reloc_counts()
+  {
+    unsigned int nsyms = this->do_get_global_symbols()->size();
+    this->reloc_counts_ = new unsigned int[nsyms];
+    gold_assert(this->reloc_counts_ != NULL);
+    memset(this->reloc_counts_, 0, nsyms * sizeof(unsigned int));
+  }
+
+  // Record a relocation in this object referencing global symbol SYMNDX.
+  // Used for tracking incremental link information.
+  void
+  count_incremental_reloc(unsigned int symndx)
+  {
+    unsigned int nsyms = this->do_get_global_symbols()->size();
+    gold_assert(symndx < nsyms);
+    gold_assert(this->reloc_counts_ != NULL);
+    ++this->reloc_counts_[symndx];
+  }
+
+  // Finalize the incremental relocation information.
+  void
+  finalize_incremental_relocs(Layout* layout);
+
+  // Return the index of the next relocation to be written for global symbol
+  // SYMNDX.  Only valid after finalize_incremental_relocs() has been called.
+  unsigned int
+  next_incremental_reloc_index(unsigned int symndx)
+  {
+    unsigned int nsyms = this->do_get_global_symbols()->size();
+
+    gold_assert(this->reloc_counts_ != NULL);
+    gold_assert(this->reloc_bases_ != NULL);
+    gold_assert(symndx < nsyms);
+
+    unsigned int counter = this->reloc_counts_[symndx]++;
+    return this->reloc_bases_[symndx] + counter;
+  }
+
  private:
   // Mapping from input sections to output section.
   Output_sections output_sections_;
@@ -957,6 +1043,10 @@ class Relobj : public Object
   // Again used during garbage collection when laying out referenced
   // sections.
   gold::Symbols_data *sd_;
+  // Per-symbol counts of relocations, for incremental links.
+  unsigned int* reloc_counts_;
+  // Per-symbol base indexes of relocations, for incremental links.
+  unsigned int* reloc_bases_;
 };
 
 // This class is used to handle relocations against a section symbol
@@ -1792,7 +1882,8 @@ class Sized_relobj : public Relobj
   // This may be overriden by a child class.
   virtual void
   do_relocate_sections(const Symbol_table* symtab, const Layout* layout,
-		       const unsigned char* pshdrs, Views* pviews);
+		       const unsigned char* pshdrs, Output_file* of,
+		       Views* pviews);
 
   // Allow a child to set output local symbol count.
   void
@@ -1880,8 +1971,9 @@ class Sized_relobj : public Relobj
   // Relocate the sections in the output file.
   void
   relocate_sections(const Symbol_table* symtab, const Layout* layout,
-		    const unsigned char* pshdrs, Views* pviews)
-  { this->do_relocate_sections(symtab, layout, pshdrs, pviews); }
+		    const unsigned char* pshdrs, Output_file* of,
+		    Views* pviews)
+  { this->do_relocate_sections(symtab, layout, pshdrs, of, pviews); }
 
   // Scan the input relocations for --emit-relocs.
   void
@@ -1918,6 +2010,35 @@ class Sized_relobj : public Relobj
 		      unsigned char* reloc_view,
 		      section_size_type reloc_view_size);
 
+  // Scan the input relocations for --incremental.
+  void
+  incremental_relocs_scan(const Read_relocs_data::Relocs_list::iterator&);
+
+  // Scan the input relocations for --incremental, templatized on the
+  // type of the relocation section.
+  template<int sh_type>
+  void
+  incremental_relocs_scan_reltype(
+      const Read_relocs_data::Relocs_list::iterator&);
+
+  void
+  incremental_relocs_write(const Relocate_info<size, big_endian>*,
+			   unsigned int sh_type,
+			   const unsigned char* prelocs,
+			   size_t reloc_count,
+			   Output_section*,
+			   Address output_offset,
+			   Output_file*);
+
+  template<int sh_type>
+  void
+  incremental_relocs_write_reltype(const Relocate_info<size, big_endian>*,
+				   const unsigned char* prelocs,
+				   size_t reloc_count,
+				   Output_section*,
+				   Address output_offset,
+				   Output_file*);
+
   // A type shared by split_stack_adjust_reltype and find_functions.
   typedef std::map<section_offset_type, section_size_type> Function_offsets;
 
diff --git a/gold/plugin.cc b/gold/plugin.cc
index 1c86c03..4680067 100644
--- a/gold/plugin.cc
+++ b/gold/plugin.cc
@@ -451,7 +451,7 @@ Plugin_manager::add_input_file(const char *pathname, bool is_lib)
   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())
+  if (parameters->options().incremental())
     gold_error(_("input files added by plug-ins in --incremental mode not "
 		 "supported yet"));
   this->workqueue_->queue_soon(new Read_symbols(this->input_objects_,
diff --git a/gold/readsyms.cc b/gold/readsyms.cc
index d8af99b..1cec3b3 100644
--- a/gold/readsyms.cc
+++ b/gold/readsyms.cc
@@ -302,12 +302,6 @@ Read_symbols::do_read_symbols(Workqueue* workqueue)
 				      this->dirpath_, this);
 	  arch->setup();
 
-	  if (this->layout_->incremental_inputs())
-	    {
-	      const Input_argument* ia = this->input_argument_;
-	      this->layout_->incremental_inputs()->report_archive(ia, arch);
-	    }
-
 	  // Unlock the archive so it can be used in the next task.
 	  arch->unlock(this);
 
@@ -389,12 +383,6 @@ Read_symbols::do_read_symbols(Workqueue* workqueue)
       Read_symbols_data* sd = new Read_symbols_data;
       obj->read_symbols(sd);
 
-      if (this->layout_->incremental_inputs())
-	{
-	  const Input_argument* ia = this->input_argument_;
-	  this->layout_->incremental_inputs()->report_object(ia, 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
@@ -599,6 +587,10 @@ Add_symbols::run(Workqueue*)
     }
   else
     {
+      Incremental_inputs* incremental_inputs =
+          this->layout_->incremental_inputs();
+      if (incremental_inputs != NULL)
+	incremental_inputs->report_object(this->object_, NULL);
       this->object_->layout(this->symtab_, this->layout_, this->sd_);
       this->object_->add_symbols(this->symtab_, this->sd_, this->layout_);
       delete this->sd_;
@@ -688,11 +680,20 @@ Finish_group::run(Workqueue*)
 	}
     }
 
-  // Delete all the archives now that we no longer need them.
+  // Now that we're done with the archives, record the incremental layout
+  // information, then delete them.
   for (Input_group::const_iterator p = this->input_group_->begin();
        p != this->input_group_->end();
        ++p)
-    delete *p;
+    {
+      // For an incremental link, finish recording the layout information.
+      Incremental_inputs* incremental_inputs =
+          this->layout_->incremental_inputs();
+      if (incremental_inputs != NULL)
+	incremental_inputs->report_archive_end(*p);
+
+      delete *p;
+    }
   delete this->input_group_;
 }
 
diff --git a/gold/reloc.cc b/gold/reloc.cc
index 9f7355e..8938988 100644
--- a/gold/reloc.cc
+++ b/gold/reloc.cc
@@ -25,6 +25,7 @@
 #include <algorithm>
 
 #include "workqueue.h"
+#include "layout.h"
 #include "symtab.h"
 #include "output.h"
 #include "merge.h"
@@ -33,6 +34,7 @@
 #include "reloc.h"
 #include "icf.h"
 #include "compressed_output.h"
+#include "incremental.h"
 
 namespace gold
 {
@@ -300,7 +302,8 @@ Sized_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd)
 				   != 0);
       if (!is_section_allocated
 	  && !parameters->options().relocatable()
-	  && !parameters->options().emit_relocs())
+	  && !parameters->options().emit_relocs()
+	  && !parameters->options().incremental())
 	continue;
 
       if (this->adjust_shndx(shdr.get_sh_link()) != this->symtab_shndx_)
@@ -424,6 +427,10 @@ Sized_relobj<size, big_endian>::do_scan_relocs(Symbol_table* symtab,
   else
     local_symbols = rd->local_symbols->data();
 
+  // For incremental links, allocate the counters for incremental relocations.
+  if (layout->incremental_inputs() != NULL)
+    this->allocate_incremental_reloc_counts();
+
   for (Read_relocs_data::Relocs_list::iterator p = rd->relocs.begin();
        p != rd->relocs.end();
        ++p)
@@ -451,6 +458,8 @@ Sized_relobj<size, big_endian>::do_scan_relocs(Symbol_table* symtab,
 				local_symbols);
 	  if (parameters->options().emit_relocs())
 	    this->emit_relocs_scan(symtab, layout, local_symbols, p);
+	  if (layout->incremental_inputs() != NULL)
+	    this->incremental_relocs_scan(p);
 	}
       else
 	{
@@ -472,6 +481,10 @@ Sized_relobj<size, big_endian>::do_scan_relocs(Symbol_table* symtab,
       p->contents = NULL;
     }
 
+  // For incremental links, finalize the allocation of relocations.
+  if (layout->incremental_inputs() != NULL)
+    this->finalize_incremental_relocs(layout);
+
   if (rd->local_symbols != NULL)
     {
       delete rd->local_symbols;
@@ -567,6 +580,54 @@ Sized_relobj<size, big_endian>::emit_relocs_scan_reltype(
     rr);
 }
 
+// Scan the input relocations for --incremental.
+
+template<int size, bool big_endian>
+void
+Sized_relobj<size, big_endian>::incremental_relocs_scan(
+    const Read_relocs_data::Relocs_list::iterator& p)
+{
+  if (p->sh_type == elfcpp::SHT_REL)
+    this->incremental_relocs_scan_reltype<elfcpp::SHT_REL>(p);
+  else
+    {
+      gold_assert(p->sh_type == elfcpp::SHT_RELA);
+      this->incremental_relocs_scan_reltype<elfcpp::SHT_RELA>(p);
+    }
+}
+
+// Scan the input relocation for --emit-relocs, templatized on the
+// type of the relocation section.
+
+template<int size, bool big_endian>
+template<int sh_type>
+void
+Sized_relobj<size, big_endian>::incremental_relocs_scan_reltype(
+    const Read_relocs_data::Relocs_list::iterator& p)
+{
+  typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reltype;
+  const int reloc_size = Reloc_types<sh_type, size, big_endian>::reloc_size;
+  const unsigned char* prelocs = p->contents->data();
+  size_t reloc_count = p->reloc_count;
+
+  for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
+    {
+      Reltype reloc(prelocs);
+
+      if (p->needs_special_offset_handling
+	  && !p->output_section->is_input_address_mapped(this, p->data_shndx,
+						         reloc.get_r_offset()))
+	continue;
+
+      typename elfcpp::Elf_types<size>::Elf_WXword r_info =
+	  reloc.get_r_info();
+      const unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
+
+      if (r_sym >= this->local_symbol_count_)
+	this->count_incremental_reloc(r_sym - this->local_symbol_count_);
+    }
+}
+
 // Relocate the input sections and write out the local symbols.
 
 template<int size, bool big_endian>
@@ -597,7 +658,7 @@ Sized_relobj<size, big_endian>::do_relocate(const Symbol_table* symtab,
 
   // Apply relocations.
 
-  this->relocate_sections(symtab, layout, pshdrs, &views);
+  this->relocate_sections(symtab, layout, pshdrs, of, &views);
 
   // After we've done the relocations, we release the hash tables,
   // since we no longer need them.
@@ -827,6 +888,7 @@ Sized_relobj<size, big_endian>::do_relocate_sections(
     const Symbol_table* symtab,
     const Layout* layout,
     const unsigned char* pshdrs,
+    Output_file* of,
     Views* pviews)
 {
   unsigned int shnum = this->shnum();
@@ -938,6 +1000,9 @@ Sized_relobj<size, big_endian>::do_relocate_sections(
 	    this->emit_relocs(&relinfo, i, sh_type, prelocs, reloc_count,
 			      os, output_offset, view, address, view_size,
 			      (*pviews)[i].view, (*pviews)[i].view_size);
+	  if (parameters->options().incremental())
+	    this->incremental_relocs_write(&relinfo, sh_type, prelocs,
+					   reloc_count, os, output_offset, of);
 	}
       else
 	{
@@ -1020,6 +1085,126 @@ Sized_relobj<size, big_endian>::emit_relocs_reltype(
     reloc_view_size);
 }
 
+// Write the incremental relocs.
+
+template<int size, bool big_endian>
+void
+Sized_relobj<size, big_endian>::incremental_relocs_write(
+    const Relocate_info<size, big_endian>* relinfo,
+    unsigned int sh_type,
+    const unsigned char* prelocs,
+    size_t reloc_count,
+    Output_section* output_section,
+    Address output_offset,
+    Output_file* of)
+{
+  if (sh_type == elfcpp::SHT_REL)
+    this->incremental_relocs_write_reltype<elfcpp::SHT_REL>(
+	relinfo,
+	prelocs,
+	reloc_count,
+	output_section,
+	output_offset,
+	of);
+  else
+    {
+      gold_assert(sh_type == elfcpp::SHT_RELA);
+      this->incremental_relocs_write_reltype<elfcpp::SHT_RELA>(
+	  relinfo,
+	  prelocs,
+	  reloc_count,
+	  output_section,
+	  output_offset,
+	  of);
+    }
+}
+
+// Write the incremental relocs, templatized on the type of the
+// relocation section.
+
+template<int size, bool big_endian>
+template<int sh_type>
+void
+Sized_relobj<size, big_endian>::incremental_relocs_write_reltype(
+    const Relocate_info<size, big_endian>* relinfo,
+    const unsigned char* prelocs,
+    size_t reloc_count,
+    Output_section* output_section,
+    Address output_offset,
+    Output_file* of)
+{
+  typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reloc;
+  const unsigned int reloc_size =
+      Reloc_types<sh_type, size, big_endian>::reloc_size;
+  const unsigned int sizeof_addr = size / 8;
+  const unsigned int incr_reloc_size = 8 + 2 * sizeof_addr;
+
+  unsigned int out_shndx = output_section->out_shndx();
+
+  // Get a view for the .gnu_incremental_relocs section.
+
+  Incremental_inputs* inputs = relinfo->layout->incremental_inputs();
+  gold_assert(inputs != NULL);
+  const off_t relocs_off = inputs->relocs_section()->offset();
+  const off_t relocs_size = inputs->relocs_section()->data_size();
+  unsigned char* const view = of->get_output_view(relocs_off, relocs_size);
+
+  for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size)
+    {
+      Reloc reloc(prelocs);
+
+      typename elfcpp::Elf_types<size>::Elf_WXword r_info = reloc.get_r_info();
+      const unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
+      const unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
+
+      if (r_sym < this->local_symbol_count_)
+        continue;
+
+      // Get the new offset--the location in the output section where
+      // this relocation should be applied.
+
+      Address offset = reloc.get_r_offset();
+      if (output_offset != invalid_address)
+	offset += output_offset;
+      else
+	{
+          section_offset_type sot_offset =
+              convert_types<section_offset_type, Address>(offset);
+	  section_offset_type new_sot_offset =
+	      output_section->output_offset(relinfo->object,
+					    relinfo->data_shndx,
+					    sot_offset);
+	  gold_assert(new_sot_offset != -1);
+	  offset += new_sot_offset;
+	}
+
+      // Get the addend.
+      typename elfcpp::Elf_types<size>::Elf_Swxword addend;
+      if (sh_type == elfcpp::SHT_RELA)
+	addend =
+	    Reloc_types<sh_type, size, big_endian>::get_reloc_addend(&reloc);
+      else
+        {
+          // FIXME: Get the addend for SHT_REL.
+          addend = 0;
+        }
+
+      // Get the index of the output relocation.
+
+      unsigned int reloc_index =
+          this->next_incremental_reloc_index(r_sym - this->local_symbol_count_);
+
+      // Write the relocation.
+
+      unsigned char* pov = view + reloc_index * incr_reloc_size;
+      elfcpp::Swap<32, big_endian>::writeval(pov, r_type);
+      elfcpp::Swap<32, big_endian>::writeval(pov + 4, out_shndx);
+      elfcpp::Swap<size, big_endian>::writeval(pov + 8, offset);
+      elfcpp::Swap<size, big_endian>::writeval(pov + 8 + sizeof_addr, addend);
+      of->write_output_view(pov - view, incr_reloc_size, view);
+    }
+}
+
 // Create merge hash tables for the local symbols.  These are used to
 // speed up relocations.
 
@@ -1556,6 +1741,7 @@ Sized_relobj<32, false>::do_relocate_sections(
     const Symbol_table* symtab,
     const Layout* layout,
     const unsigned char* pshdrs,
+    Output_file* of,
     Views* pviews);
 #endif
 
@@ -1566,6 +1752,7 @@ Sized_relobj<32, true>::do_relocate_sections(
     const Symbol_table* symtab,
     const Layout* layout,
     const unsigned char* pshdrs,
+    Output_file* of,
     Views* pviews);
 #endif
 
@@ -1576,6 +1763,7 @@ Sized_relobj<64, false>::do_relocate_sections(
     const Symbol_table* symtab,
     const Layout* layout,
     const unsigned char* pshdrs,
+    Output_file* of,
     Views* pviews);
 #endif
 
@@ -1586,6 +1774,7 @@ Sized_relobj<64, true>::do_relocate_sections(
     const Symbol_table* symtab,
     const Layout* layout,
     const unsigned char* pshdrs,
+    Output_file* of,
     Views* pviews);
 #endif
 
diff --git a/gold/script.cc b/gold/script.cc
index e0b9359..d09781a 100644
--- a/gold/script.cc
+++ b/gold/script.cc
@@ -1462,16 +1462,16 @@ read_input_script(Workqueue* workqueue, Symbol_table* symtab, Layout* layout,
       this_blocker = nb;
     }
 
-  if (layout->incremental_inputs())
+  if (layout->incremental_inputs() != NULL)
     {
-      // Like new Read_symbols(...) above, we rely on close.inputs()
+      // Like new Read_symbols(...) above, we rely on closure.inputs()
       // getting leaked by closure.
+      const std::string& filename = input_file->filename();
       Script_info* info = new Script_info(closure.inputs());
-      layout->incremental_inputs()->report_script(
-          input_argument,
-          input_file->file().get_mtime(),
-          info);
+      Timespec mtime = input_file->file().get_mtime();
+      layout->incremental_inputs()->report_script(filename, info, mtime);
     }
+
   *used_next_blocker = true;
 
   return true;
diff --git a/gold/symtab.h b/gold/symtab.h
index 8178e2c..4e5b7b0 100644
--- a/gold/symtab.h
+++ b/gold/symtab.h
@@ -1468,6 +1468,16 @@ class Symbol_table
   compute_final_value(const Sized_symbol<size>* sym,
 		      Compute_final_value_status* pstatus) const;
 
+  // Return the index of the first global symbol.
+  unsigned int
+  first_global_index() const
+  { return this->first_global_index_; }
+
+  // Return the total number of symbols in the symbol table.
+  unsigned int
+  output_count() const
+  { return this->output_count_; }
+
   // Write out the global symbols.
   void
   write_globals(const Stringpool*, const Stringpool*,

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]