include/
[external/binutils.git] / gold / incremental.cc
index 9f7c4c2..63c2f18 100644 (file)
 #include "output.h"
 #include "target-select.h"
 
-using elfcpp::Convert;
-
 namespace gold {
 
 // Version information. Will change frequently during the development, later
 // we could think about backward (and forward?) compatibility.
-const int INCREMENTAL_LINK_VERSION = 1;
-
-namespace internal {
-
-// 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;
-};
-
-// 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;
-};
-
-}
-
-// Accessors.
-
-// See internal::Incremental_input_header for fields descriptions.
-template<int size, bool big_endian>
-class Incremental_inputs_header_write
-{
- public:
-  Incremental_inputs_header_write(unsigned char *p)
-    : p_(reinterpret_cast<internal::Incremental_inputs_header_data*>(p))
-  { }
-
-  static const int data_size = sizeof(internal::Incremental_inputs_header_data);
-
-  void
-  put_version(elfcpp::Elf_Word v)
-  { this->p_->version = Convert<32, big_endian>::convert_host(v); }
-
-  void
-  put_input_file_count(elfcpp::Elf_Word v)
-  { this->p_->input_file_count = Convert<32, big_endian>::convert_host(v); }
-
-  void
-  put_command_line_offset(elfcpp::Elf_Word v)
-  { this->p_->command_line_offset = Convert<32, big_endian>::convert_host(v); }
-
-  void
-  put_reserved(elfcpp::Elf_Word v)
-  { this->p_->reserved = Convert<32, big_endian>::convert_host(v); }
-
- private:
-  internal::Incremental_inputs_header_data* p_;
-};
-
-// See internal::Incremental_input_entry for fields descriptions.
-template<int size, bool big_endian>
-class Incremental_inputs_entry_write
-{
- public:
-  Incremental_inputs_entry_write(unsigned char *p)
-    : p_(reinterpret_cast<internal::Incremental_inputs_entry_data*>(p))
-  { }
-
-  static const int data_size = sizeof(internal::Incremental_inputs_entry_data);
-
-  void
-  put_filename_offset(elfcpp::Elf_Word v)
-  { this->p_->filename_offset = Convert<32, big_endian>::convert_host(v); }
-
-  void
-  put_data_offset(elfcpp::Elf_Word v)
-  { this->p_->data_offset = Convert<32, big_endian>::convert_host(v); }
-
-  void
-  put_timestamp_sec(elfcpp::Elf_Xword v)
-  { this->p_->timestamp_sec = Convert<64, big_endian>::convert_host(v); }
-
-  void
-  put_timestamp_nsec(elfcpp::Elf_Word v)
-  { this->p_->timestamp_nsec = Convert<32, big_endian>::convert_host(v); }
-
-  void
-  put_input_type(elfcpp::Elf_Word v)
-  { this->p_->input_type = Convert<32, big_endian>::convert_host(v); }
-
-  void
-  put_reserved(elfcpp::Elf_Word v)
-  { this->p_->reserved = Convert<32, big_endian>::convert_host(v); }
-
- private:
-  internal::Incremental_inputs_entry_data* p_;
-};
+const unsigned int INCREMENTAL_LINK_VERSION = 1;
 
 // 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?
@@ -196,16 +79,77 @@ Incremental_binary::error(const char* format, ...) const
 template<int size, bool big_endian>
 bool
 Sized_incremental_binary<size, big_endian>::do_find_incremental_inputs_section(
-    Location* location)
+    Location* location,
+    unsigned int* strtab_shndx)
 {
   unsigned int shndx = this->elf_file_.find_section_by_type(
       elfcpp::SHT_GNU_INCREMENTAL_INPUTS);
   if (shndx == elfcpp::SHN_UNDEF)  // Not found.
     return false;
+  *strtab_shndx = this->elf_file_.section_link(shndx);
   *location = this->elf_file_.section_contents(shndx);
   return true;
 }
 
+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 strtab_shndx;
+  Location location;
+
+  if (!do_find_incremental_inputs_section(&location, &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 strtab_location(this->elf_file_.section_contents(strtab_shndx));
+  View data_view(view(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;
+    }
+
+  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))
+    {
+      explain_no_incremental(_("invalid incremental build data"));
+      return false;
+    }
+
+  if (incremental_inputs->command_line() != command_line)
+    {
+      explain_no_incremental(_("command line changed"));
+      return false;
+    }
+
+  // TODO: compare incremental_inputs->inputs() with entries in data_view.
+  return true;
+}
+
 namespace
 {
 
@@ -227,6 +171,11 @@ make_sized_incremental_binary(Output_file* file,
       return NULL;
     }
 
+  if (!parameters->target_valid())
+    set_parameters_target(target);
+  else if (target != &parameters->target())
+    gold_error(_("%s: incompatible target"), file->filename());
+
   return new Sized_incremental_binary<size, big_endian>(file, ehdr, target);
 }
 
@@ -251,8 +200,8 @@ open_incremental_binary(Output_file* file)
       return NULL;
     }
 
-  int size;
-  bool big_endian;
+  int size = 0;
+  bool big_endian = false;
   std::string error;
   if (!elfcpp::Elf_recognizer::is_valid_header(p, want, &size, &big_endian,
                                                &error))
@@ -322,14 +271,7 @@ Incremental_checker::can_incrementally_link_output_file()
   Incremental_binary* binary = open_incremental_binary(&output);
   if (binary == NULL)
     return false;
-  Incremental_binary::Location inputs_location;
-  if (!binary->find_incremental_inputs_section(&inputs_location))
-    {
-      explain_no_incremental("no incremental data from previous build");
-      delete binary;
-      return false;
-    }
-  return true;
+  return binary->check_inputs(this->incremental_inputs_);
 }
 
 // Add the command line to the string table, setting
@@ -366,7 +308,10 @@ Incremental_inputs::report_command_line(int argc, const char* const* argv)
         }
       args.append("'");
     }
-  this->strtab_->add(args.c_str(), true, &this->command_line_key_);
+
+  this->command_line_ = args;
+  this->strtab_->add(this->command_line_.c_str(), false,
+                     &this->command_line_key_);
 }
 
 // Record that the input argument INPUT is an achive ARCHIVE.  This is
@@ -548,6 +493,22 @@ Incremental_inputs::sized_create_inputs_section_data()
       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();
+        }
       // TODO: add per input data and timestamp.  Currently we store
       // an out-of-bounds offset for future version of gold to reject
       // such an incremental_inputs section.