public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* gold: try to open the output file in incremental build and check if   it has the inputs section
@ 2009-09-11 17:25 Mikolaj Zalewski
  0 siblings, 0 replies; only message in thread
From: Mikolaj Zalewski @ 2009-09-11 17:25 UTC (permalink / raw)
  To: binutils

[-- Attachment #1: Type: text/plain, Size: 1518 bytes --]

  This patch adds an object representing the output file and makes use
of it - gold now checks if the input is an ELF file and if it has a
SHT_GNU_INCREMENTAL_INPUTS section. I have moved some code from
object.cc to elf_file.h to reuse it.
  The new code informs the user why it needs to fall back to full link
(except for the obvious case that the output doesn't exist). However,
I'm not sure if this will be helpful or annoying. Does anyone has an
opinion?

elfcpp:

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

	* elf_file.h: Fix header guard.
	(Elf_recognizer): New class, code from gold/object.cc.
	(Elf_file::find_section_by_type): New method.

gold:

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

	* incremental.cc: Include <stdarg>.
	Include "target-select.h".
	(vexplain_no_incremental): New function.
	(explain_no_incremental): New function.
	(Incremental_binary::error): New method.
	(Sized_incremental_binary::do_find_incremental_inputs_section): New method.
	(make_sized_incremental_binary): New function.
	(open_incremental_binary): New function.
	(can_incrementally_link_file): Add checks if output is ELF and has
inputs section.
	* incremental.h: Include "elf_file.h".
	Include "output.h".
	(Incremental_binary): New class.
	(Sized_incremental_binary): New class.
	(open_incremental_binary): Declare.
	* object.cc (is_elf_object): Use elfcpp::Elf_recognizer::is_elf_file.
	(make_elf_object): Use elfcpp::Elf_recognizer::is_valid_header.
	* output.cc (Output_file::filesize): New method.

[-- Attachment #2: 0001-gold-try-to-open-the-output-file-in-incremental-build-and-check-if-it-has-the-inputs-section.txt --]
[-- Type: text/plain, Size: 19359 bytes --]

From 77927403b7d4081bb58d0374e41142413a6defc2 Mon Sep 17 00:00:00 2001
From: Mikolaj Zalewski <mikolajz@puchatek.dom>
Date: Fri, 11 Sep 2009 18:54:49 +0200
Subject: [PATCH] gold: try to open the output file in incremental build and check if it has the inputs section

---
 elfcpp/elfcpp_file.h |  158 +++++++++++++++++++++++++++++++++++++++++-
 gold/incremental.cc  |  190 ++++++++++++++++++++++++++++++++++++++++++++++++++
 gold/incremental.h   |  113 ++++++++++++++++++++++++++++++
 gold/object.cc       |   74 +++----------------
 gold/output.h        |    5 ++
 5 files changed, 477 insertions(+), 63 deletions(-)

diff --git a/elfcpp/elfcpp_file.h b/elfcpp/elfcpp_file.h
index f1f4423..aff1a23 100644
--- a/elfcpp/elfcpp_file.h
+++ b/elfcpp/elfcpp_file.h
@@ -53,7 +53,7 @@
 // This permits writing
 //    elfcpp::Shdr shdr(file, ef.section_header(n));
 
-#ifndef ELFPCP_FILE_H
+#ifndef ELFCPP_FILE_H
 #define ELFCPP_FILE_H
 
 #include <string>
@@ -62,6 +62,34 @@
 namespace elfcpp
 {
 
+// A simple helper class to recognize if a file has an ELF header.
+
+class Elf_recognizer
+{
+ public:
+  // Maximum header size. The user should try to read that much of the
+  // file when using this class.
+
+  static const int max_header_size = Elf_sizes<64>::ehdr_size;
+
+  // Checks if the file contains the ELF magic. Other header fields are not
+  // checked.
+
+  static bool
+  is_elf_file(const unsigned char* ehdr_buf, int size);
+
+  // Check if this is a valid header of a 32-bit or 64-bit, little-endian or
+  // big-endian ELF file.  Assumes is_elf_file() has been checked to be true.
+  // If the header is not valid, *ERROR contains a human-readable error
+  // message.  If is is, *BITNESS is set to either 32 or 64, *BIG_ENDIAN
+  // informs if the file is big-endian.
+
+  static bool
+  is_valid_header(const unsigned char* ehdr_buf, off_t size,
+                  int* bitness, bool* big_endian,
+                  std::string* error);
+};
+
 // This object is used to read an ELF file.
 //   SIZE: The size of file, 32 or 64.
 //   BIG_ENDIAN: Whether the file is in big-endian format.
@@ -99,6 +127,11 @@ class Elf_file
   shoff() const
   { return this->shoff_; }
 
+  // Find section of type TYPE and return its index. Returns SHN_UNDEF if
+  // not found.
+  unsigned int
+  find_section_by_type(unsigned int type);
+
   // Return the number of sections.
   unsigned int
   shnum()
@@ -193,6 +226,111 @@ class Elf_file
   int large_shndx_offset_;
 };
 
+// Inline function definitions.
+
+// Check for presence of the ELF magic.
+
+inline bool
+Elf_recognizer::is_elf_file(const unsigned char* ehdr_buf, int size)
+{
+  if (size < 4)
+    return false;
+
+  static unsigned char elfmagic[4] =
+    {
+      elfcpp::ELFMAG0, elfcpp::ELFMAG1,
+      elfcpp::ELFMAG2, elfcpp::ELFMAG3
+    };
+  return memcmp(ehdr_buf, elfmagic, 4) == 0;
+}
+
+namespace
+{
+
+inline std::string
+internal_printf_int(const char* format, int arg)
+{
+  char buf[256];
+  snprintf(buf, sizeof(buf), format, arg);
+  buf[sizeof(buf) - 1] = 0;  // Ensure the string is NULL-terminated.
+  return std::string(buf);
+}
+
+}  // End anonymous namespace.
+
+// Check the validity of the ELF header.
+
+inline bool
+Elf_recognizer::is_valid_header(
+    const unsigned char* ehdr_buf, off_t size, int* bitness, bool* big_endian,
+    std::string* error)
+{
+  if (size < elfcpp::EI_NIDENT)
+    {
+      *error = _("ELF file too short");
+      return false;
+    }
+
+  int v = ehdr_buf[elfcpp::EI_VERSION];
+  if (v != elfcpp::EV_CURRENT)
+    {
+      if (v == elfcpp::EV_NONE)
+        *error = _("invalid ELF version 0");
+      else
+        *error = internal_printf_int(_("unsupported ELF version %d"), v);
+      return false;
+    }
+
+  int c = ehdr_buf[elfcpp::EI_CLASS];
+  if (c == elfcpp::ELFCLASSNONE)
+    {
+      *error = _("invalid ELF class 0");
+      return false;
+    }
+  else if (c != elfcpp::ELFCLASS32
+           && c != elfcpp::ELFCLASS64)
+    {
+      *error = internal_printf_int(_("unsupported ELF class %d"), c);
+      return false;
+    }
+
+  int d = ehdr_buf[elfcpp::EI_DATA];
+  if (d == elfcpp::ELFDATANONE)
+    {
+      *error = _("invalid ELF data encoding");
+      return false;
+    }
+  else if (d != elfcpp::ELFDATA2LSB
+           && d != elfcpp::ELFDATA2MSB)
+    {
+      *error = internal_printf_int(_("unsupported ELF data encoding %d"), d);
+      return false;
+    }
+
+  *big_endian = (d == elfcpp::ELFDATA2MSB);
+
+  if (c == elfcpp::ELFCLASS32)
+    {
+      if (size < elfcpp::Elf_sizes<32>::ehdr_size)
+        {
+          *error = _("ELF file too short");
+          return false;
+        }
+      *bitness = 32;
+    }
+  else
+    {
+      if (size < elfcpp::Elf_sizes<64>::ehdr_size)
+        {
+          *error = _("ELF file too short");
+          return false;
+        }
+      *bitness = 64;
+    }
+  return true;
+}
+
+
 // Template function definitions.
 
 // Construct an Elf_file given an ELF file header.
@@ -269,6 +407,24 @@ Elf_file<size, big_endian, File>::initialize_shnum()
     }
 }
 
+// Find section of type TYPE and return its index. Returns SHN_UNDEF if
+// not found.
+template<int size, bool big_endian, typename File>
+unsigned int
+Elf_file<size, big_endian, File>::find_section_by_type(unsigned int type)
+{
+  unsigned int shnum = this->shnum();
+  typename File::View v(
+      this->file_->view(this->shoff_, This::shdr_size * shnum));
+  for (unsigned int i = 0; i < shnum; i++)
+    {
+      Ef_shdr shdr(v.data() + This::shdr_size * i);
+      if (shdr.get_sh_type() == type)
+        return i;
+    }
+  return SHN_UNDEF;
+}
+
 // Return the file offset of the section header of section SHNDX.
 
 template<int size, bool big_endian, typename File>
diff --git a/gold/incremental.cc b/gold/incremental.cc
index 4a3ecb1..e8dce16 100644
--- a/gold/incremental.cc
+++ b/gold/incremental.cc
@@ -20,12 +20,15 @@
 // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
 // MA 02110-1301, USA.
 
+#include <stdarg.h>
+
 #include "gold.h"
 #include "elfcpp.h"
 #include "output.h"
 #include "incremental.h"
 #include "archive.h"
 #include "output.h"
+#include "target-select.h"
 
 using elfcpp::Convert;
 
@@ -150,6 +153,162 @@ class Incremental_inputs_entry_write
   internal::Incremental_inputs_entry_data* p_;
 };
 
+// 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?
+
+void
+vexplain_no_incremental(const char* format, va_list args)
+{
+  char* buf = NULL;
+  if (vasprintf(&buf, format, args) < 0)
+    gold_nomem();
+  gold_info(_("the link might take longer: "
+              "cannot perform incremental link: %s"), buf);
+  free(buf);
+}
+
+void
+explain_no_incremental(const char* format, ...)
+{
+  va_list args;
+  va_start(args, format);
+  vexplain_no_incremental(format, args);
+  va_end(args);
+}
+
+// Report an error.
+
+void
+Incremental_binary::error(const char* format, ...) const
+{
+  va_list args;
+  va_start(args, format);
+  // Current code only checks if the file can be used for incremental linking,
+  // so errors shouldn't fail the build, but only result in a fallback to a
+  // full build.
+  // TODO: when we implement incremental editing of the file, we may need a
+  // flag that will cause errors to be treated seriously.
+  vexplain_no_incremental(format, args);
+  va_end(args);
+}
+
+template<int size, bool big_endian>
+bool
+Sized_incremental_binary<size, big_endian>::do_find_incremental_inputs_section(
+    Location* location)
+{
+  unsigned int shndx = this->elf_file_.find_section_by_type(
+      elfcpp::SHT_GNU_INCREMENTAL_INPUTS);
+  if (shndx == elfcpp::SHN_UNDEF)  // Not found.
+    return false;
+  *location = this->elf_file_.section_contents(shndx);
+  return true;
+}
+
+namespace
+{
+
+// Create a Sized_incremental_binary object of the specified size and
+// endianness. Fails if the target architecture is not supported.
+
+template<int size, bool big_endian>
+Incremental_binary*
+make_sized_incremental_binary(Output_file* file,
+                              const elfcpp::Ehdr<size, big_endian>& ehdr)
+{
+  Target* target = select_target(ehdr.get_e_machine(), size, big_endian,
+                                 ehdr.get_e_ident()[elfcpp::EI_OSABI],
+                                 ehdr.get_e_ident()[elfcpp::EI_ABIVERSION]);
+  if (target == NULL)
+    {
+      explain_no_incremental(_("unsupported ELF machine number %d"),
+               ehdr.get_e_machine());
+      return NULL;
+    }
+
+  return new Sized_incremental_binary<size, big_endian>(file, ehdr, target);
+}
+
+}  // 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
+// should be opened.
+
+Incremental_binary*
+open_incremental_binary(Output_file* file)
+{
+  off_t filesize = file->filesize();
+  int want = elfcpp::Elf_recognizer::max_header_size;
+  if (filesize < want)
+    want = filesize;
+
+  const unsigned char* p = file->get_input_view(0, want);
+  if (!elfcpp::Elf_recognizer::is_elf_file(p, want))
+    {
+      explain_no_incremental(_("output is not an ELF file."));
+      return NULL;
+    }
+
+  int size;
+  bool big_endian;
+  std::string error;
+  if (!elfcpp::Elf_recognizer::is_valid_header(p, want, &size, &big_endian,
+                                               &error))
+    {
+      explain_no_incremental(error.c_str());
+      return NULL;
+    }
+
+  Incremental_binary* result = NULL;
+  if (size == 32)
+    {
+      if (big_endian)
+        {
+#ifdef HAVE_TARGET_32_BIG
+          result = make_sized_incremental_binary<32, true>(
+              file, elfcpp::Ehdr<32, true>(p));
+#else
+          explain_no_incremental(_("unsupported file: 32-bit, big-endian"));
+#endif
+        }
+      else
+        {
+#ifdef HAVE_TARGET_32_LITTLE
+          result = make_sized_incremental_binary<32, false>(
+              file, elfcpp::Ehdr<32, false>(p));
+#else
+          explain_no_incremental(_("unsupported file: 32-bit, little-endian"));
+#endif
+        }
+    }
+  else if (size == 64)
+    {
+      if (big_endian)
+        {
+#ifdef HAVE_TARGET_64_BIG
+          result = make_sized_incremental_binary<64, true>(
+              file, elfcpp::Ehdr<64, true>(p));
+#else
+          explain_no_incremental(_("unsupported file: 64-bit, big-endian"));
+#endif
+        }
+      else
+        {
+#ifdef HAVE_TARGET_64_LITTLE
+          result = make_sized_incremental_binary<64, false>(
+              file, elfcpp::Ehdr<64, false>(p));
+#else
+          explain_no_incremental(_("unsupported file: 64-bit, little-endian"));
+#endif
+        }
+    }
+  else
+    gold_unreachable();
+
+  return result;
+}
+
 // Analyzes the output file to check if incremental linking is possible and
 // (to be done) what files need to be relinked.
 
@@ -159,6 +318,15 @@ Incremental_checker::can_incrementally_link_output_file()
   Output_file output(this->output_name_);
   if (!output.open_for_modification())
     return false;
+  std::auto_ptr<Incremental_binary> binary(open_incremental_binary(&output));
+  if (binary.get() == 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");
+      return false;
+    }
   return true;
 }
 
@@ -392,4 +560,26 @@ Incremental_inputs::sized_create_inputs_section_data()
 				      "** incremental link inputs list");
 }
 
+// Instantiate the templates we need.
+
+#ifdef HAVE_TARGET_32_LITTLE
+template
+class Sized_incremental_binary<32, false>;
+#endif
+
+#ifdef HAVE_TARGET_32_BIG
+template
+class Sized_incremental_binary<32, true>;
+#endif
+
+#ifdef HAVE_TARGET_64_LITTLE
+template
+class Sized_incremental_binary<64, false>;
+#endif
+
+#ifdef HAVE_TARGET_64_BIG
+template
+class Sized_incremental_binary<64, true>;
+#endif
+
 } // End namespace gold.
diff --git a/gold/incremental.h b/gold/incremental.h
index 9650993..8909324 100644
--- a/gold/incremental.h
+++ b/gold/incremental.h
@@ -26,9 +26,11 @@
 #include <map>
 #include <vector>
 
+#include "elfcpp_file.h"
 #include "stringpool.h"
 #include "workqueue.h"
 #include "fileread.h"
+#include "output.h"
 
 namespace gold
 {
@@ -50,6 +52,117 @@ enum Incremental_input_type
   INCREMENTAL_INPUT_SCRIPT = 4
 };
 
+// An object representing the ELF file we edit during an incremental build.
+// Similar to Object or Dynobj, but operates on Output_file and contains
+// method specific to file edition (TBD). This is the abstract parent class
+// implemented in Sized_incremental_binary<size, big_endian> for a specific
+// endianness and size.
+
+class Incremental_binary
+{
+ public:
+  Incremental_binary(Output_file* output, Target* target)
+    : output_(output), target_(target)
+  { }
+
+  virtual
+  ~Incremental_binary()
+  { }
+
+  // Functions and types for the elfcpp::Elf_file interface.  This
+  // permit us to use Incremental_binary as the File template parameter for
+  // elfcpp::Elf_file.
+
+  // The View class is returned by view.  It must support a single
+  // method, data().  This is trivial, because Output_file::get_output_view
+  // does what we need.
+  class View
+  {
+   public:
+    View(const unsigned char* p)
+      : p_(p)
+    { }
+
+    const unsigned char*
+    data() const
+    { return this->p_; }
+
+   private:
+    const unsigned char* p_;
+  };
+
+  // Return a View.
+  View
+  view(off_t file_offset, section_size_type data_size)
+  { return View(this->output_->get_input_view(file_offset, data_size)); }
+
+  // A location in the file.
+  struct Location
+  {
+    off_t file_offset;
+    off_t data_size;
+
+    Location(off_t fo, section_size_type ds)
+      : file_offset(fo), data_size(ds)
+    { }
+
+    Location()
+      : file_offset(0), data_size(0)
+    { }
+  };
+
+  // Get a View given a Location.
+  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.
+  bool
+  find_incremental_inputs_section(Location* location)
+  { return do_find_incremental_inputs_section(location); }
+
+ protected:
+  // Find incremental inputs section.
+  virtual bool
+  do_find_incremental_inputs_section(Location* location) = 0;
+
+ private:
+  // Edited output file object.
+  Output_file* output_;
+  // Target of the output file.
+  Target* target_;
+};
+
+template<int size, bool big_endian>
+class Sized_incremental_binary : public Incremental_binary
+{
+ public:
+  Sized_incremental_binary(Output_file* output,
+                           const elfcpp::Ehdr<size, big_endian>& ehdr,
+                           Target* target)
+    : Incremental_binary(output, target), elf_file_(this, ehdr)
+  { }
+
+ protected:
+  virtual bool
+  do_find_incremental_inputs_section(Location* location);
+
+ private:
+  // Output as an ELF file.
+  elfcpp::Elf_file<size, big_endian, Incremental_binary> elf_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.
+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
diff --git a/gold/object.cc b/gold/object.cc
index be6294c..141fa23 100644
--- a/gold/object.cc
+++ b/gold/object.cc
@@ -2243,7 +2243,7 @@ is_elf_object(Input_file* input_file, off_t offset,
 	      const unsigned char** start, int *read_size)
 {
   off_t filesize = input_file->file().filesize();
-  int want = elfcpp::Elf_sizes<64>::ehdr_size;
+  int want = elfcpp::Elf_recognizer::max_header_size;
   if (filesize - offset < want)
     want = filesize - offset;
 
@@ -2252,15 +2252,7 @@ is_elf_object(Input_file* input_file, off_t offset,
   *start = p;
   *read_size = want;
 
-  if (want < 4)
-    return false;
-
-  static unsigned char elfmagic[4] =
-    {
-      elfcpp::ELFMAG0, elfcpp::ELFMAG1,
-      elfcpp::ELFMAG2, elfcpp::ELFMAG3
-    };
-  return memcmp(p, elfmagic, 4) == 0;
+  return elfcpp::Elf_recognizer::is_elf_file(p, want);
 }
 
 // Read an ELF file and return the appropriate instance of Object.
@@ -2273,57 +2265,18 @@ make_elf_object(const std::string& name, Input_file* input_file, off_t offset,
   if (punconfigured != NULL)
     *punconfigured = false;
 
-  if (bytes < elfcpp::EI_NIDENT)
-    {
-      gold_error(_("%s: ELF file too short"), name.c_str());
-      return NULL;
-    }
-
-  int v = p[elfcpp::EI_VERSION];
-  if (v != elfcpp::EV_CURRENT)
-    {
-      if (v == elfcpp::EV_NONE)
-	gold_error(_("%s: invalid ELF version 0"), name.c_str());
-      else
-	gold_error(_("%s: unsupported ELF version %d"), name.c_str(), v);
-      return NULL;
-    }
-
-  int c = p[elfcpp::EI_CLASS];
-  if (c == elfcpp::ELFCLASSNONE)
+  std::string error;
+  bool big_endian;
+  int bitness;
+  if (!elfcpp::Elf_recognizer::is_valid_header(p, bytes, &bitness,
+                                               &big_endian, &error))
     {
-      gold_error(_("%s: invalid ELF class 0"), name.c_str());
-      return NULL;
-    }
-  else if (c != elfcpp::ELFCLASS32
-	   && c != elfcpp::ELFCLASS64)
-    {
-      gold_error(_("%s: unsupported ELF class %d"), name.c_str(), c);
+      gold_error(_("%s: %s"), name.c_str(), error.c_str());
       return NULL;
     }
 
-  int d = p[elfcpp::EI_DATA];
-  if (d == elfcpp::ELFDATANONE)
+  if (bitness == 32)
     {
-      gold_error(_("%s: invalid ELF data encoding"), name.c_str());
-      return NULL;
-    }
-  else if (d != elfcpp::ELFDATA2LSB
-	   && d != elfcpp::ELFDATA2MSB)
-    {
-      gold_error(_("%s: unsupported ELF data encoding %d"), name.c_str(), d);
-      return NULL;
-    }
-
-  bool big_endian = d == elfcpp::ELFDATA2MSB;
-
-  if (c == elfcpp::ELFCLASS32)
-    {
-      if (bytes < elfcpp::Elf_sizes<32>::ehdr_size)
-	{
-	  gold_error(_("%s: ELF file too short"), name.c_str());
-	  return NULL;
-	}
       if (big_endian)
 	{
 #ifdef HAVE_TARGET_32_BIG
@@ -2357,13 +2310,8 @@ make_elf_object(const std::string& name, Input_file* input_file, off_t offset,
 #endif
 	}
     }
-  else
+  else if (bitness == 64)
     {
-      if (bytes < elfcpp::Elf_sizes<64>::ehdr_size)
-	{
-	  gold_error(_("%s: ELF file too short"), name.c_str());
-	  return NULL;
-	}
       if (big_endian)
 	{
 #ifdef HAVE_TARGET_64_BIG
@@ -2397,6 +2345,8 @@ make_elf_object(const std::string& name, Input_file* input_file, off_t offset,
 #endif
 	}
     }
+  else
+    gold_unreachable();
 }
 
 // Instantiate the templates we need.
diff --git a/gold/output.h b/gold/output.h
index 7bd0cf3..10429df 100644
--- a/gold/output.h
+++ b/gold/output.h
@@ -3114,6 +3114,11 @@ class Output_file
   void
   close();
 
+  // Return the size of this file.
+  off_t
+  filesize()
+  { return this->file_size_; }
+
   // We currently always use mmap which makes the view handling quite
   // simple.  In the future we may support other approaches.
 
-- 
1.4.4.2


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2009-09-11 17:25 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-09-11 17:25 gold: try to open the output file in incremental build and check if it has the inputs section Mikolaj Zalewski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).