[3/3] Identify reproducible builds in 'objdump -p' output for PE files

Message ID 20200121155212.12686-4-jon.turney@dronecode.org.uk
State New
Headers show
Series
  • Identify reproducible build PE files in 'objdump -p'
Related show

Commit Message

Jon Turney Jan. 21, 2020, 3:52 p.m.
These are produced by MSVC when the '/Brepro' flag is used.

To quote from the PE specification [1]:

"The presence of an entry of type IMAGE_DEBUG_TYPE_REPRO indicates the
PE file is built in a way to achieve determinism or reproducibility. If
the input does not change, the output PE file is guaranteed to be
bit-for-bit identical no matter when or where the PE is produced.
Various date/time stamp fields in the PE file are filled with part or
all the bits from a calculated hash value that uses PE file content as
input, and therefore no longer represent the actual date and time when a
PE file or related specific data within the PE is produced. The raw data
of this debug entry may be empty, or may contain a calculated hash value
preceded by a four-byte value that represents the hash value length."

[1] https://docs.microsoft.com/en-us/windows/win32/debug/pe-format

bfd/ChangeLog:

2020-01-16  Jon Turney  <jon.turney@dronecode.org.uk>

	* peXXigen.c (pe_is_repro): New function.
	(_bfd_XX_print_private_bfd_data_common): Note timestamp is
	actually a build hash if PE_IMAGE_DEBUG_TYPE_REPRO is present.
---
 bfd/ChangeLog  |  6 ++++
 bfd/peXXigen.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 86 insertions(+), 5 deletions(-)

-- 
2.21.0

Comments

Alan Modra Jan. 28, 2020, 10:18 p.m. | #1
On Tue, Jan 21, 2020 at 03:52:12PM +0000, Jon Turney wrote:
> 	* peXXigen.c (pe_is_repro): New function.

> 	(_bfd_XX_print_private_bfd_data_common): Note timestamp is

> 	actually a build hash if PE_IMAGE_DEBUG_TYPE_REPRO is present.


OK, except

> +  if ((section == NULL) ||

> +      (!(section->flags & SEC_HAS_CONTENTS)) ||

> +      (section->size < size))


Please fix the trailing operators.

  if (section == NULL
      || !(section->flags & SEC_HAS_CONTENTS)
      || section->size < size)

-- 
Alan Modra
Australia Development Lab, IBM
Jon Turney Jan. 30, 2020, 1:07 p.m. | #2
On 28/01/2020 22:18, Alan Modra wrote:
> On Tue, Jan 21, 2020 at 03:52:12PM +0000, Jon Turney wrote:

>> 	* peXXigen.c (pe_is_repro): New function.

>> 	(_bfd_XX_print_private_bfd_data_common): Note timestamp is

>> 	actually a build hash if PE_IMAGE_DEBUG_TYPE_REPRO is present.

> 

> OK, except

> 

>> +  if ((section == NULL) ||

>> +      (!(section->flags & SEC_HAS_CONTENTS)) ||

>> +      (section->size < size))

> 

> Please fix the trailing operators.

> 

>    if (section == NULL

>        || !(section->flags & SEC_HAS_CONTENTS)

>        || section->size < size)

> 


Thanks.

Pushed with that correction.

Patch

diff --git a/bfd/peXXigen.c b/bfd/peXXigen.c
index dc7951f8d9..f742375525 100644
--- a/bfd/peXXigen.c
+++ b/bfd/peXXigen.c
@@ -2746,6 +2746,71 @@  pe_print_debugdata (bfd * abfd, void * vfile)
   return TRUE;
 }
 
+static bfd_boolean
+pe_is_repro (bfd * abfd)
+{
+  pe_data_type *pe = pe_data (abfd);
+  struct internal_extra_pe_aouthdr *extra = &pe->pe_opthdr;
+  asection *section;
+  bfd_byte *data = 0;
+  bfd_size_type dataoff;
+  unsigned int i;
+  bfd_boolean res = FALSE;
+
+  bfd_vma addr = extra->DataDirectory[PE_DEBUG_DATA].VirtualAddress;
+  bfd_size_type size = extra->DataDirectory[PE_DEBUG_DATA].Size;
+
+  if (size == 0)
+    return FALSE;
+
+  addr += extra->ImageBase;
+  for (section = abfd->sections; section != NULL; section = section->next)
+    {
+      if ((addr >= section->vma) && (addr < (section->vma + section->size)))
+	break;
+    }
+
+  if ((section == NULL) ||
+      (!(section->flags & SEC_HAS_CONTENTS)) ||
+      (section->size < size))
+    {
+      return FALSE;
+    }
+
+  dataoff = addr - section->vma;
+
+  if (size > (section->size - dataoff))
+    {
+      return FALSE;
+    }
+
+  if (!bfd_malloc_and_get_section (abfd, section, &data))
+    {
+      if (data != NULL)
+	free (data);
+      return FALSE;
+    }
+
+  for (i = 0; i < size / sizeof (struct external_IMAGE_DEBUG_DIRECTORY); i++)
+    {
+      struct external_IMAGE_DEBUG_DIRECTORY *ext
+	= &((struct external_IMAGE_DEBUG_DIRECTORY *)(data + dataoff))[i];
+      struct internal_IMAGE_DEBUG_DIRECTORY idd;
+
+      _bfd_XXi_swap_debugdir_in (abfd, ext, &idd);
+
+      if (idd.Type == PE_IMAGE_DEBUG_TYPE_REPRO)
+        {
+          res = TRUE;
+          break;
+        }
+    }
+
+  free(data);
+
+  return res;
+}
+
 /* Print out the program headers.  */
 
 bfd_boolean
@@ -2777,11 +2842,21 @@  _bfd_XX_print_private_bfd_data_common (bfd * abfd, void * vfile)
   PF (IMAGE_FILE_BYTES_REVERSED_HI, "big endian");
 #undef PF
 
-  /* ctime implies '\n'.  */
-  {
-    time_t t = pe->coff.timestamp;
-    fprintf (file, "\nTime/Date\t\t%s", ctime (&t));
-  }
+  /*
+    If a PE_IMAGE_DEBUG_TYPE_REPRO entry is present in the debug directory, the
+    timestamp is to be interpreted as the hash of a reproducible build.
+  */
+  if (pe_is_repro (abfd))
+    {
+      fprintf (file, "\nTime/Date\t\t%08lx", pe->coff.timestamp);
+      fprintf (file, "\t(This is a reproducible build file hash, not a timestamp)\n");
+    }
+  else
+    {
+      /* ctime implies '\n'.  */
+      time_t t = pe->coff.timestamp;
+      fprintf (file, "\nTime/Date\t\t%s", ctime (&t));
+    }
 
 #ifndef IMAGE_NT_OPTIONAL_HDR_MAGIC
 # define IMAGE_NT_OPTIONAL_HDR_MAGIC 0x10b