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]

PATCH COMMITTED: Add --build-id to gold


I committed this patch to add support for the --build-id option to
gold.  It's easy for gold to do a simple checksum over the entire
output file, so that is what I implemented.  It would be harder to do
what GNU ld does: do a checksum over the file header, the program
headers, the section headers, and the section contents.  The
difference is in the padding bytes and in a few sections which BFD
handles specially (e.g., the symbol table).  Those sections will be
included in the gold checksum but not in the GNU ld checksum.  As far
as I know it doesn't matter.

Ian


2008-03-24  Ian Lance Taylor  <iant@google.com>

	* options.h (class General_options): Define build_id option.
	* layout.h (class Layout): Declare write_build_id, create_note,
	create_build_id.  Add build_id_note_ member.
	* layout.cc: Include <cerrno>, <fcntl.h>, <unistd.h>,
	"libiberty.h", "md5.h", "sha1.h".
	(Layout::Layout): Initialize eh_frame_data_,
	eh_frame_hdr_section_, and build_id_note_.
	(Layout::finalize): Call create_build_id.
	(Layout::create_note): New function, broken out of
	Layout::create_gold_note.
	(Layout::create_gold_note): Call create_note.
	(Layout::create_build_id): New function.
	(Layout::write_build_id): New function.
	(Close_task_runner::run): Call write_build_id.


Index: options.h
===================================================================
RCS file: /cvs/src/src/gold/options.h,v
retrieving revision 1.65
diff -p -u -r1.65 options.h
--- options.h	24 Mar 2008 03:48:29 -0000	1.65
+++ options.h	25 Mar 2008 05:06:49 -0000
@@ -460,6 +460,10 @@ class General_options
   DEFINE_bool(Bsymbolic, options::ONE_DASH, '\0', false,
               N_("Bind defined symbols locally"), NULL);
 
+  DEFINE_optional_string(build_id, options::TWO_DASHES, '\0', "sha1",
+			 N_("Generate build ID note"),
+			 N_("[=STYLE]"));
+
 #ifdef HAVE_ZLIB_H
   DEFINE_enum(compress_debug_sections, options::TWO_DASHES, '\0', "none",
               N_("Compress .debug_* sections in the output file"),
Index: layout.h
===================================================================
RCS file: /cvs/src/src/gold/layout.h,v
retrieving revision 1.51
diff -p -u -r1.51 layout.h
--- layout.h	12 Mar 2008 04:38:42 -0000	1.51
+++ layout.h	25 Mar 2008 05:06:49 -0000
@@ -294,6 +294,10 @@ class Layout
   script_options() const
   { return this->script_options_; }
 
+  // Compute and write out the build ID if needed.
+  void
+  write_build_id(Output_file*) const;
+
   // Rewrite output file in binary format.
   void
   write_binary(Output_file* in) const;
@@ -370,6 +374,11 @@ class Layout
   };
   typedef std::vector<Group_signature> Group_signatures;
 
+  // Create a .note section, filling in the header.
+  Output_section*
+  create_note(const char* name, int note_type, size_t descsz,
+	      bool allocate, size_t* trailing_padding);
+
   // Create a .note section for gold.
   void
   create_gold_note();
@@ -378,6 +387,10 @@ class Layout
   void
   create_executable_stack_info(const Target*);
 
+  // Create a build ID note if needed.
+  void
+  create_build_id();
+
   // Find the first read-only PT_LOAD segment, creating one if
   // necessary.
   Output_segment*
@@ -586,6 +599,8 @@ class Layout
   Eh_frame* eh_frame_data_;
   // The exception frame header output section if there is one.
   Output_section* eh_frame_hdr_section_;
+  // The space for the build ID checksum if there is one.
+  Output_section_data* build_id_note_;
   // A list of group sections and their signatures.
   Group_signatures group_signatures_;
   // The size of the output file.
Index: layout.cc
===================================================================
RCS file: /cvs/src/src/gold/layout.cc,v
retrieving revision 1.94
diff -p -u -r1.94 layout.cc
--- layout.cc	19 Mar 2008 21:41:38 -0000	1.94
+++ layout.cc	25 Mar 2008 05:06:49 -0000
@@ -22,10 +22,16 @@
 
 #include "gold.h"
 
+#include <cerrno>
 #include <cstring>
 #include <algorithm>
 #include <iostream>
 #include <utility>
+#include <fcntl.h>
+#include <unistd.h>
+#include "libiberty.h"
+#include "md5.h"
+#include "sha1.h"
 
 #include "parameters.h"
 #include "options.h"
@@ -76,7 +82,8 @@ Layout::Layout(const General_options& op
     unattached_section_list_(), special_output_list_(),
     section_headers_(NULL), tls_segment_(NULL), symtab_section_(NULL),
     dynsym_section_(NULL), dynamic_section_(NULL), dynamic_data_(NULL),
-    eh_frame_section_(NULL), group_signatures_(), output_file_size_(-1),
+    eh_frame_section_(NULL), eh_frame_data_(NULL), eh_frame_hdr_section_(NULL),
+    build_id_note_(NULL), group_signatures_(), output_file_size_(-1),
     input_requires_executable_stack_(false),
     input_with_gnu_stack_note_(false),
     input_without_gnu_stack_note_(false),
@@ -1035,6 +1042,7 @@ Layout::finalize(const Input_objects* in
 
   this->create_gold_note();
   this->create_executable_stack_info(target);
+  this->create_build_id();
 
   Output_segment* phdr_seg = NULL;
   if (!parameters->options().relocatable() && !parameters->doing_static_link())
@@ -1181,15 +1189,16 @@ Layout::finalize(const Input_objects* in
   return off;
 }
 
-// Create a .note section for an executable or shared library.  This
-// records the version of gold used to create the binary.
+// Create a note header following the format defined in the ELF ABI.
+// NAME is the name, NOTE_TYPE is the type, DESCSZ is the size of the
+// descriptor.  ALLOCATE is true if the section should be allocated in
+// memory.  This returns the new note section.  It sets
+// *TRAILING_PADDING to the number of trailing zero bytes required.
 
-void
-Layout::create_gold_note()
+Output_section*
+Layout::create_note(const char* name, int note_type, size_t descsz,
+		    bool allocate, size_t* trailing_padding)
 {
-  if (parameters->options().relocatable())
-    return;
-
   // Authorities all agree that the values in a .note field should
   // be aligned on 4-byte boundaries for 32-bit binaries.  However,
   // they differ on what the alignment is for 64-bit binaries.
@@ -1208,19 +1217,14 @@ Layout::create_gold_note()
 #endif
 
   // The contents of the .note section.
-  const char* name = "GNU";
-  std::string desc(std::string("gold ") + gold::get_version_string());
   size_t namesz = strlen(name) + 1;
   size_t aligned_namesz = align_address(namesz, size / 8);
-  size_t descsz = desc.length() + 1;
   size_t aligned_descsz = align_address(descsz, size / 8);
-  const int note_type = 4;
 
-  size_t notesz = 3 * (size / 8) + aligned_namesz + aligned_descsz;
+  size_t notehdrsz = 3 * (size / 8) + aligned_namesz;
 
-  unsigned char buffer[128];
-  gold_assert(sizeof buffer >= notesz);
-  memset(buffer, 0, notesz);
+  unsigned char* buffer = new unsigned char[notehdrsz];
+  memset(buffer, 0, notehdrsz);
 
   bool is_big_endian = parameters->target().is_big_endian();
 
@@ -1258,15 +1262,46 @@ Layout::create_gold_note()
     gold_unreachable();
 
   memcpy(buffer + 3 * (size / 8), name, namesz);
-  memcpy(buffer + 3 * (size / 8) + aligned_namesz, desc.data(), descsz);
 
   const char* note_name = this->namepool_.add(".note", false, NULL);
+  elfcpp::Elf_Xword flags = 0;
+  if (allocate)
+    flags = elfcpp::SHF_ALLOC;
   Output_section* os = this->make_output_section(note_name,
 						 elfcpp::SHT_NOTE,
-						 0);
-  Output_section_data* posd = new Output_data_const(buffer, notesz,
-						    size / 8);
+						 flags);
+  Output_section_data* posd = new Output_data_const_buffer(buffer, notehdrsz,
+							   size / 8);
+  os->add_output_section_data(posd);
+
+  *trailing_padding = aligned_descsz - descsz;
+
+  return os;
+}
+
+// For an executable or shared library, create a note to record the
+// version of gold used to create the binary.
+
+void
+Layout::create_gold_note()
+{
+  if (parameters->options().relocatable())
+    return;
+
+  std::string desc = std::string("gold ") + gold::get_version_string();
+
+  size_t trailing_padding;
+  Output_section *os = this->create_note("GNU", elfcpp::NT_GNU_GOLD_VERSION,
+					 desc.size(), false, &trailing_padding);
+
+  Output_section_data* posd = new Output_data_const(desc, 4);
   os->add_output_section_data(posd);
+
+  if (trailing_padding > 0)
+    {
+      posd = new Output_data_fixed_space(trailing_padding, 0);
+      os->add_output_section_data(posd);
+    }
 }
 
 // Record whether the stack should be executable.  This can be set
@@ -1318,6 +1353,104 @@ Layout::create_executable_stack_info(con
     }
 }
 
+// If --build-id was used, set up the build ID note.
+
+void
+Layout::create_build_id()
+{
+  if (!parameters->options().user_set_build_id())
+    return;
+
+  const char* style = parameters->options().build_id();
+  if (strcmp(style, "none") == 0)
+    return;
+
+  // Set DESCSZ to the size of the note descriptor.  When possible,
+  // set DESC to the note descriptor contents.
+  size_t descsz;
+  std::string desc;
+  if (strcmp(style, "md5") == 0)
+    descsz = 128 / 8;
+  else if (strcmp(style, "sha1") == 0)
+    descsz = 160 / 8;
+  else if (strcmp(style, "uuid") == 0)
+    {
+      const size_t uuidsz = 128 / 8;
+
+      char buffer[uuidsz];
+      memset(buffer, 0, uuidsz);
+
+      int descriptor = ::open("/dev/urandom", O_RDONLY);
+      if (descriptor < 0)
+	gold_error(_("--build-id=uuid failed: could not open /dev/urandom: %s"),
+		   strerror(errno));
+      else
+	{
+	  ssize_t got = ::read(descriptor, buffer, uuidsz);
+	  ::close(descriptor);
+	  if (got < 0)
+	    gold_error(_("/dev/urandom: read failed: %s"), strerror(errno));
+	  else if (static_cast<size_t>(got) != uuidsz)
+	    gold_error(_("/dev/urandom: expected %zu bytes, got %zd bytes"),
+		       uuidsz, got);
+	}
+
+      desc.assign(buffer, uuidsz);
+      descsz = uuidsz;
+    }
+  else if (strncmp(style, "0x", 2) == 0)
+    {
+      hex_init();
+      const char* p = style + 2;
+      while (*p != '\0')
+	{
+	  if (hex_p(p[0]) && hex_p(p[1]))
+	    {
+	      char c = (hex_value(p[0]) << 4) | hex_value(p[1]);
+	      desc += c;
+	      p += 2;
+	    }
+	  else if (*p == '-' || *p == ':')
+	    ++p;
+	  else
+	    gold_fatal(_("--build-id argument '%s' not a valid hex number"),
+		       style);
+	}
+      descsz = desc.size();
+    }
+  else
+    gold_fatal(_("unrecognized --build-id argument '%s'"), style);
+
+  // Create the note.
+  size_t trailing_padding;
+  Output_section* os = this->create_note("GNU", elfcpp::NT_GNU_BUILD_ID,
+					 descsz, true, &trailing_padding);
+
+  if (!desc.empty())
+    {
+      // We know the value already, so we fill it in now.
+      gold_assert(desc.size() == descsz);
+
+      Output_section_data* posd = new Output_data_const(desc, 4);
+      os->add_output_section_data(posd);
+
+      if (trailing_padding != 0)
+	{
+	  posd = new Output_data_fixed_space(trailing_padding, 0);
+	  os->add_output_section_data(posd);
+	}
+    }
+  else
+    {
+      // We need to compute a checksum after we have completed the
+      // link.
+      gold_assert(trailing_padding == 0);
+      this->build_id_note_ = new Output_data_fixed_space(descsz, 4);
+      os->add_output_section_data(this->build_id_note_);
+      os->set_after_input_sections();
+    }
+}
+
 // Return whether SEG1 should be before SEG2 in the output file.  This
 // is based entirely on the segment type and flags.  When this is
 // called the segment addresses has normally not yet been set.
@@ -2676,6 +2809,46 @@ Layout::write_sections_after_input_secti
   this->section_headers_->write(of);
 }
 
+// If the build ID requires computing a checksum, do so here, and
+// write it out.  We compute a checksum over the entire file because
+// that is simplest.
+
+void
+Layout::write_build_id(Output_file* of) const
+{
+  if (this->build_id_note_ == NULL)
+    return;
+
+  const unsigned char* iv = of->get_input_view(0, this->output_file_size_);
+
+  unsigned char* ov = of->get_output_view(this->build_id_note_->offset(),
+					  this->build_id_note_->data_size());
+
+  const char* style = parameters->options().build_id();
+  if (strcmp(style, "sha1") == 0)
+    {
+      sha1_ctx ctx;
+      sha1_init_ctx(&ctx);
+      sha1_process_bytes(iv, this->output_file_size_, &ctx);
+      sha1_finish_ctx(&ctx, ov);
+    }
+  else if (strcmp(style, "md5") == 0)
+    {
+      md5_ctx ctx;
+      md5_init_ctx(&ctx);
+      md5_process_bytes(iv, this->output_file_size_, &ctx);
+      md5_finish_ctx(&ctx, ov);
+    }
+  else
+    gold_unreachable();
+
+  of->write_output_view(this->build_id_note_->offset(),
+			this->build_id_note_->data_size(),
+			ov);
+
+  of->free_input_view(0, this->output_file_size_, iv);
+}
+
 // Write out a binary file.  This is called after the link is
 // complete.  IN is the temporary output file we used to generate the
 // ELF code.  We simply walk through the segments, read them from
@@ -2856,6 +3029,9 @@ Write_after_input_sections_task::run(Wor
 void
 Close_task_runner::run(Workqueue*, const Task*)
 {
+  // If we need to compute a checksum for the BUILD if, we do so here.
+  this->layout_->write_build_id(this->of_);
+
   // If we've been asked to create a binary file, we do so here.
   if (this->options_->oformat_enum() != General_options::OBJECT_FORMAT_ELF)
     this->layout_->write_binary(this->of_);

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