elfcpp/:
authorIan Lance Taylor <ian@airs.com>
Fri, 9 Oct 2009 16:40:51 +0000 (16:40 +0000)
committerIan Lance Taylor <ian@airs.com>
Fri, 9 Oct 2009 16:40:51 +0000 (16:40 +0000)
* elfcpp_file.h: Fix header guard.  Include <cstdio>.
(class Elf_recognizer): New class, code from gold/object.cc.
(Elf_file::find_section_by_type): New method.
gold/:
* incremental.cc: Include <cstdarg> and "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 "elfcpp_file.h" and "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.h (Output_file::filesize): New method.

elfcpp/ChangeLog
elfcpp/elfcpp_file.h
gold/ChangeLog
gold/incremental.cc
gold/incremental.h
gold/object.cc
gold/output.h

index 2a668a674442e39ad9c4450ac622a1f3955ef0ec..7569cbdbf7b70724dff958fe16a7200c679d33a8 100644 (file)
@@ -1,3 +1,9 @@
+2009-10-09  Mikolaj Zalewski  <mikolajz@google.com>
+
+       * elfcpp_file.h: Fix header guard.  Include <cstdio>.
+       (class Elf_recognizer): New class, code from gold/object.cc.
+       (Elf_file::find_section_by_type): New method.
+
 2009-07-23  Ulrich Drepper  <drepper@redhat.com>
 
        * elfcpp.h (enum STB): Add STB_GNU_UNIQUE.
index f1f4423a14e88f790b025f732105a3f4d7cfa171..00ab28b36e11f000f9869dca338af2b0335552d9 100644 (file)
 // 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>
+#include <cstdio>
 #include <cstring>
 
 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 this 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 EHDR_BUF/BUFSIZE 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,
+  // *SIZE is set to either 32 or 64, *BIG_ENDIAN is set to indicate
+  // whether the file is big-endian.
+
+  static bool
+  is_valid_header(const unsigned char* ehdr_buf, off_t bufsize,
+                  int* size, 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 +129,11 @@ class Elf_file
   shoff() const
   { return this->shoff_; }
 
+  // Find the first section with an sh_type field equal to TYPE and
+  // return its index.  Returns SHN_UNDEF if there is no such section.
+  unsigned int
+  find_section_by_type(unsigned int type);
+
   // Return the number of sections.
   unsigned int
   shnum()
@@ -193,6 +228,115 @@ class Elf_file
   int large_shndx_offset_;
 };
 
+// Inline function definitions.
+
+// Check for presence of the ELF magic number.
+
+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
+{
+
+// Print a number to a string.
+
+inline std::string
+internal_printf_int(const char* format, int arg)
+{
+  char buf[256];
+  snprintf(buf, sizeof(buf), format, arg);
+  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 bufsize,
+    int* size,
+    bool* big_endian,
+    std::string* error)
+{
+  if (bufsize < 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 (bufsize < elfcpp::Elf_sizes<32>::ehdr_size)
+        {
+          *error = _("ELF file too short");
+          return false;
+        }
+      *size = 32;
+    }
+  else
+    {
+      if (bufsize < elfcpp::Elf_sizes<64>::ehdr_size)
+        {
+          *error = _("ELF file too short");
+          return false;
+        }
+      *size = 64;
+    }
+
+  return true;
+}
+
 // Template function definitions.
 
 // Construct an Elf_file given an ELF file header.
@@ -269,6 +413,25 @@ Elf_file<size, big_endian, File>::initialize_shnum()
     }
 }
 
+// Find section with sh_type equal to 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>
index 4a2e831a2604e652a24fc65c194f04605fb011ee..227ded71401238f9745df278080887723801d565 100644 (file)
@@ -1,3 +1,24 @@
+2009-10-09  Mikolaj Zalewski  <mikolajz@google.com>
+
+       * incremental.cc: Include <cstdarg> and "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 "elfcpp_file.h" and "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.h (Output_file::filesize): New method.
+
 2009-10-07  Viktor Kutuzov  <vkutuzov@accesssoftek.com>
 
        * arm.cc (Arm_relocate_functions::extract_arm_movw_movt_addend):
index 4a3ecb1a69168b60884784ba5cc8186865bb0119..9f7c4c20ec10b5b0cad939302299ef5877edb4e4 100644 (file)
 // MA 02110-1301, USA.
 
 #include "gold.h"
+
+#include <cstdarg>
+
 #include "elfcpp.h"
 #include "output.h"
 #include "incremental.h"
 #include "archive.h"
 #include "output.h"
+#include "target-select.h"
 
 using elfcpp::Convert;
 
@@ -150,6 +154,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 +319,16 @@ Incremental_checker::can_incrementally_link_output_file()
   Output_file output(this->output_name_);
   if (!output.open_for_modification())
     return false;
+  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;
 }
 
@@ -392,4 +562,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.
index 9650993303259ed1f1a1dec01113517f95152114..8909324884d3402340e0e4b7f833d9ae9c1f3f63 100644 (file)
 #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
index 5ac7878e5eedc627c7a67eab6964e2ead456eca0..b8d9eb84f82612414fc589c32a41c028dfd5dd3d 100644 (file)
@@ -2281,7 +2281,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;
 
@@ -2290,15 +2290,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.
@@ -2311,57 +2303,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 size;
+  if (!elfcpp::Elf_recognizer::is_valid_header(p, bytes, &size,
+                                               &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 (size == 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
@@ -2395,13 +2348,8 @@ make_elf_object(const std::string& name, Input_file* input_file, off_t offset,
 #endif
        }
     }
-  else
+  else if (size == 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
@@ -2435,6 +2383,8 @@ make_elf_object(const std::string& name, Input_file* input_file, off_t offset,
 #endif
        }
     }
+  else
+    gold_unreachable();
 }
 
 // Instantiate the templates we need.
index 6de6e6997108a9b0aa404632ec07cd05a8669d97..377864cddfd2428f459ef5046e22d859587ae073 100644 (file)
@@ -3433,6 +3433,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.