ld: Add section header stream to PDB files
authorMark Harmstone <mark@harmstone.com>
Mon, 31 Oct 2022 00:15:53 +0000 (00:15 +0000)
committerAlan Modra <amodra@gmail.com>
Mon, 31 Oct 2022 02:27:26 +0000 (12:57 +1030)
ld/pdb.c
ld/testsuite/ld-pe/pdb.exp

index 80ed31e257a864f28f282b7feee59b38a013d70e..1190dcf6cdf548951169e70c6c3741f78fb332d1 100644 (file)
--- a/ld/pdb.c
+++ b/ld/pdb.c
@@ -385,7 +385,8 @@ get_arch_number (bfd *abfd)
 
 /* Stream 4 is the debug information (DBI) stream.  */
 static bool
-populate_dbi_stream (bfd *stream, bfd *abfd)
+populate_dbi_stream (bfd *stream, bfd *abfd,
+                    uint16_t section_header_stream_num)
 {
   struct pdb_dbi_stream_header h;
   struct optional_dbg_header opt;
@@ -419,7 +420,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd)
   bfd_putl16 (0xffff, &opt.fixup_stream);
   bfd_putl16 (0xffff, &opt.omap_to_src_stream);
   bfd_putl16 (0xffff, &opt.omap_from_src_stream);
-  bfd_putl16 (0xffff, &opt.section_header_stream);
+  bfd_putl16 (section_header_stream_num, &opt.section_header_stream);
   bfd_putl16 (0xffff, &opt.token_map_stream);
   bfd_putl16 (0xffff, &opt.xdata_stream);
   bfd_putl16 (0xffff, &opt.pdata_stream);
@@ -432,6 +433,60 @@ populate_dbi_stream (bfd *stream, bfd *abfd)
   return true;
 }
 
+/* The section header stream contains a copy of the section headers
+   from the PE file, in the same format.  */
+static bool
+create_section_header_stream (bfd *pdb, bfd *abfd, uint16_t *num)
+{
+  bfd *stream;
+  unsigned int section_count;
+  file_ptr scn_base;
+  size_t len;
+  char *buf;
+
+  stream = add_stream (pdb, NULL, num);
+  if (!stream)
+    return false;
+
+  section_count = abfd->section_count;
+
+  /* Empty sections aren't output.  */
+  for (asection *sect = abfd->sections; sect; sect = sect->next)
+    {
+      if (sect->size == 0)
+       section_count--;
+    }
+
+  if (section_count == 0)
+    return true;
+
+  /* Copy section table from output - it's already been written at this
+     point.  */
+
+  scn_base = bfd_coff_filhsz (abfd) + bfd_coff_aoutsz (abfd);
+
+  bfd_seek (abfd, scn_base, SEEK_SET);
+
+  len = section_count * sizeof (struct external_scnhdr);
+  buf = xmalloc (len);
+
+  if (bfd_bread (buf, len, abfd) != len)
+    {
+      free (buf);
+      return false;
+    }
+
+  if (bfd_bwrite (buf, len, stream) != len)
+    {
+      free (buf);
+      return false;
+    }
+
+  free (buf);
+
+  return true;
+}
+
 /* Create a PDB debugging file for the PE image file abfd with the build ID
    guid, stored at pdb_name.  */
 bool
@@ -440,6 +495,7 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
   bfd *pdb;
   bool ret = false;
   bfd *info_stream, *dbi_stream, *names_stream;
+  uint16_t section_header_stream_num;
 
   pdb = bfd_openw (pdb_name, "pdb");
   if (!pdb)
@@ -498,7 +554,14 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
       goto end;
     }
 
-  if (!populate_dbi_stream (dbi_stream, abfd))
+  if (!create_section_header_stream (pdb, abfd, &section_header_stream_num))
+    {
+      einfo (_("%P: warning: cannot create section header stream "
+              "in PDB file: %E\n"));
+      goto end;
+    }
+
+  if (!populate_dbi_stream (dbi_stream, abfd, section_header_stream_num))
     {
       einfo (_("%P: warning: cannot populate DBI stream "
               "in PDB file: %E\n"));
index b62ce6da6f8d4afc4d80134401bd8e046bfd3858..cee072187de7b906e80d40cb164d5d708767ffd3 100644 (file)
@@ -278,6 +278,123 @@ proc check_dbi_stream { pdb } {
     return 1
 }
 
+proc get_section_stream_index { pdb } {
+    global ar
+
+    set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb 0003"]
+
+    if ![string match "" $exec_output] {
+       return -1
+    }
+
+    set fi [open tmpdir/0003]
+    fconfigure $fi -translation binary
+
+    # skip fields
+    seek $fi 24
+
+    # read substream sizes
+
+    set data [read $fi 4]
+    binary scan $data i mod_info_size
+
+    set data [read $fi 4]
+    binary scan $data i section_contribution_size
+
+    set data [read $fi 4]
+    binary scan $data i section_map_size
+
+    set data [read $fi 4]
+    binary scan $data i source_info_size
+
+    set data [read $fi 4]
+    binary scan $data i type_server_map_size
+
+    # skip type server index
+    seek $fi 4 current
+
+    set data [read $fi 4]
+    binary scan $data i optional_dbg_header_size
+
+    if { $optional_dbg_header_size < 12 } {
+       close $fi
+       return -1
+    }
+
+    # skip data
+    seek $fi [expr 12 + $mod_info_size + $section_contribution_size + $section_map_size + $source_info_size + $type_server_map_size + 10] current
+
+    set data [read $fi 2]
+    binary scan $data s section_stream_index
+
+    close $fi
+
+    return $section_stream_index
+}
+
+proc check_section_stream { img pdb } {
+    global ar
+
+    # read sections stream
+
+    set index [get_section_stream_index $pdb]
+
+    if { $index == -1 } {
+       return 0
+    }
+
+    set index_str [format "%04x" $index]
+
+    set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb $index_str"]
+
+    if ![string match "" $exec_output] {
+       return 0
+    }
+
+    set stream_length [file size tmpdir/$index_str]
+
+    set fi [open tmpdir/$index_str]
+    fconfigure $fi -translation binary
+
+    set stream_data [read $fi $stream_length]
+
+    close $fi
+
+    # read sections from PE file
+
+    set fi [open $img]
+    fconfigure $fi -translation binary
+
+    # read PE offset
+    read $fi 0x3c
+    set data [read $fi 4]
+    binary scan $data i pe_offset
+
+    # read number of sections
+    seek $fi [expr $pe_offset + 6]
+    set data [read $fi 2]
+    binary scan $data s num_sections
+
+    # read size of optional header
+    seek $fi 12 current
+    set data [read $fi 2]
+    binary scan $data s opt_header_size
+
+    # read section headers
+    seek $fi [expr $opt_header_size + 2] current
+    set section_data [read $fi [expr $num_sections * 40]]
+
+    close $fi
+
+    # compare
+
+    if { $stream_data ne $section_data} {
+       return 0
+    }
+
+    return 1
+}
+
 if ![ld_assemble $as $srcdir/$subdir/pdb1.s tmpdir/pdb1.o] {
     unsupported "Build pdb1.o"
     return
@@ -318,3 +435,9 @@ if [check_dbi_stream tmpdir/pdb1.pdb] {
 } else {
     fail "Invalid DBI stream"
 }
+
+if [check_section_stream tmpdir/pdb1.exe tmpdir/pdb1.pdb] {
+    pass "Valid section stream"
+} else {
+    fail "Invalid section stream"
+}