ld: Write DEBUG_S_FILECHKSMS entries in PDBs
authorMark Harmstone <mark@harmstone.com>
Fri, 9 Dec 2022 01:52:32 +0000 (01:52 +0000)
committerAlan Modra <amodra@gmail.com>
Fri, 23 Dec 2022 09:07:57 +0000 (19:37 +1030)
ld/pdb.c
ld/pdb.h
ld/testsuite/ld-pe/pdb.exp
ld/testsuite/ld-pe/pdb3-c13-info1.d [new file with mode: 0644]
ld/testsuite/ld-pe/pdb3-c13-info2.d [new file with mode: 0644]
ld/testsuite/ld-pe/pdb3-source-info.d [new file with mode: 0644]
ld/testsuite/ld-pe/pdb3a.s [new file with mode: 0644]
ld/testsuite/ld-pe/pdb3b.s [new file with mode: 0644]

index 98663a1f9ae2cd5683c033aa4ca0f512eb07363d..44ba21f2b46d9f0070617e020fa9926b0d8aeb8f 100644 (file)
--- a/ld/pdb.c
+++ b/ld/pdb.c
@@ -46,6 +46,7 @@ struct string
   struct string *next;
   uint32_t hash;
   uint32_t offset;
+  uint32_t source_file_offset;
   size_t len;
   char s[];
 };
@@ -58,6 +59,18 @@ struct string_table
   htab_t hashmap;
 };
 
+struct mod_source_files
+{
+  uint16_t files_count;
+  struct string **files;
+};
+
+struct source_files_info
+{
+  uint16_t mod_count;
+  struct mod_source_files *mods;
+};
+
 /* Add a new stream to the PDB archive, and return its BFD.  */
 static bfd *
 add_stream (bfd *pdb, const char *name, uint16_t *stream_num)
@@ -400,6 +413,135 @@ get_arch_number (bfd *abfd)
   return IMAGE_FILE_MACHINE_I386;
 }
 
+/* Validate the DEBUG_S_FILECHKSMS entry within a module's .debug$S
+   section, and copy it to the module's symbol stream.  */
+static bool
+copy_filechksms (uint8_t *data, uint32_t size, char *string_table,
+                struct string_table *strings, uint8_t *out,
+                struct mod_source_files *mod_source)
+{
+  uint8_t *orig_data = data;
+  uint32_t orig_size = size;
+  uint16_t num_files = 0;
+  struct string **strptr;
+
+  bfd_putl32 (DEBUG_S_FILECHKSMS, out);
+  out += sizeof (uint32_t);
+
+  bfd_putl32 (size, out);
+  out += sizeof (uint32_t);
+
+  /* Calculate the number of files, and check for any overflows.  */
+
+  while (size > 0)
+    {
+      struct file_checksum *fc = (struct file_checksum *) data;
+      uint8_t padding;
+      size_t len;
+
+      if (size < sizeof (struct file_checksum))
+       {
+         bfd_set_error (bfd_error_bad_value);
+         return false;
+       }
+
+      len = sizeof (struct file_checksum) + fc->checksum_length;
+
+      if (size < len)
+       {
+         bfd_set_error (bfd_error_bad_value);
+         return false;
+       }
+
+      data += len;
+      size -= len;
+
+      if (len % sizeof (uint32_t))
+       padding = sizeof (uint32_t) - (len % sizeof (uint32_t));
+      else
+       padding = 0;
+
+      if (size < padding)
+       {
+         bfd_set_error (bfd_error_bad_value);
+         return false;
+       }
+
+      num_files++;
+
+      data += padding;
+      size -= padding;
+    }
+
+  /* Add the files to mod_source, so that they'll appear in the source
+     info substream.  */
+
+  strptr = NULL;
+  if (num_files > 0)
+    {
+      uint16_t new_count = num_files + mod_source->files_count;
+
+      mod_source->files = xrealloc (mod_source->files,
+                                   sizeof (struct string *) * new_count);
+
+      strptr = mod_source->files + mod_source->files_count;
+
+      mod_source->files_count += num_files;
+    }
+
+  /* Actually copy the data.  */
+
+  data = orig_data;
+  size = orig_size;
+
+  while (size > 0)
+    {
+      struct file_checksum *fc = (struct file_checksum *) data;
+      uint32_t string_off;
+      uint8_t padding;
+      size_t len;
+      struct string *str = NULL;
+
+      string_off = bfd_getl32 (&fc->file_id);
+      len = sizeof (struct file_checksum) + fc->checksum_length;
+
+      if (len % sizeof (uint32_t))
+       padding = sizeof (uint32_t) - (len % sizeof (uint32_t));
+      else
+       padding = 0;
+
+      /* Remap the "file ID", i.e. the offset in the module's string table,
+        so it points to the right place in the main string table.  */
+
+      if (string_table)
+       {
+         char *fn = string_table + string_off;
+         size_t fn_len = strlen (fn);
+         uint32_t hash = calc_hash (fn, fn_len);
+         void **slot;
+
+         slot = htab_find_slot_with_hash (strings->hashmap, fn, hash,
+                                          NO_INSERT);
+
+         if (slot)
+           str = (struct string *) *slot;
+       }
+
+      *strptr = str;
+      strptr++;
+
+      bfd_putl32 (str ? str->offset : 0, &fc->file_id);
+
+      memcpy (out, data, len + padding);
+
+      data += len + padding;
+      size -= len + padding;
+      out += len + padding;
+    }
+
+  return true;
+}
+
 /* Add a string to the strings table, if it's not already there.  */
 static void
 add_string (char *str, size_t len, struct string_table *strings)
@@ -420,6 +562,7 @@ add_string (char *str, size_t len, struct string_table *strings)
       s->next = NULL;
       s->hash = hash;
       s->offset = strings->strings_len;
+      s->source_file_offset = 0xffffffff;
       s->len = len;
       memcpy (s->s, str, len);
 
@@ -479,10 +622,15 @@ parse_string_table (bfd_byte *data, size_t size,
 
 /* Parse the .debug$S section within an object file.  */
 static bool
-handle_debugs_section (asection *s, bfd *mod, struct string_table *strings)
+handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
+                      uint8_t **dataptr, uint32_t *sizeptr,
+                      struct mod_source_files *mod_source)
 {
   bfd_byte *data = NULL;
   size_t off;
+  uint32_t c13_size = 0;
+  char *string_table = NULL;
+  uint8_t *buf, *bufptr;
 
   if (!bfd_get_full_section_contents (mod, s, &data))
     return false;
@@ -498,6 +646,8 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings)
 
   off = sizeof (uint32_t);
 
+  /* calculate size */
+
   while (off + sizeof (uint32_t) <= s->size)
     {
       uint32_t type, size;
@@ -526,9 +676,63 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings)
 
       switch (type)
        {
+       case DEBUG_S_FILECHKSMS:
+         c13_size += sizeof (uint32_t) + sizeof (uint32_t) + size;
+
+         if (c13_size % sizeof (uint32_t))
+           c13_size += sizeof (uint32_t) - (c13_size % sizeof (uint32_t));
+
+         break;
+
        case DEBUG_S_STRINGTABLE:
          parse_string_table (data + off, size, strings);
 
+         string_table = (char *) data + off;
+
+         break;
+       }
+
+      off += size;
+
+      if (off % sizeof (uint32_t))
+       off += sizeof (uint32_t) - (off % sizeof (uint32_t));
+    }
+
+  if (c13_size == 0)
+    {
+      free (data);
+      return true;
+    }
+
+  /* copy data */
+
+  buf = xmalloc (c13_size);
+  bufptr = buf;
+
+  off = sizeof (uint32_t);
+
+  while (off + sizeof (uint32_t) <= s->size)
+    {
+      uint32_t type, size;
+
+      type = bfd_getl32 (data + off);
+      off += sizeof (uint32_t);
+
+      size = bfd_getl32 (data + off);
+      off += sizeof (uint32_t);
+
+      switch (type)
+       {
+       case DEBUG_S_FILECHKSMS:
+         if (!copy_filechksms (data + off, size, string_table,
+                               strings, bufptr, mod_source))
+           {
+             free (data);
+             return false;
+           }
+
+         bufptr += sizeof (uint32_t) + sizeof (uint32_t) + size;
+
          break;
        }
 
@@ -540,6 +744,23 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings)
 
   free (data);
 
+  if (*dataptr)
+    {
+      /* Append the C13 info to what's already there, if the module has
+        multiple .debug$S sections.  */
+
+      *dataptr = xrealloc (*dataptr, *sizeptr + c13_size);
+      memcpy (*dataptr + *sizeptr, buf, c13_size);
+
+      free (buf);
+    }
+  else
+    {
+      *dataptr = buf;
+    }
+
+  *sizeptr += c13_size;
+
   return true;
 }
 
@@ -547,11 +768,15 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings)
    data for each object file.  */
 static bool
 populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
-                       struct string_table *strings)
+                       struct string_table *strings,
+                       uint32_t *c13_info_size,
+                       struct mod_source_files *mod_source)
 {
   uint8_t int_buf[sizeof (uint32_t)];
+  uint8_t *c13_info = NULL;
 
   *sym_byte_size = sizeof (uint32_t);
+  *c13_info_size = 0;
 
   /* Process .debug$S section(s).  */
 
@@ -559,8 +784,13 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
     {
       if (!strcmp (s->name, ".debug$S") && s->size >= sizeof (uint32_t))
        {
-         if (!handle_debugs_section (s, mod, strings))
+         if (!handle_debugs_section (s, mod, strings, &c13_info,
+                                     c13_info_size, mod_source))
+           {
+             free (c13_info);
+             free (mod_source->files);
              return false;
+           }
        }
     }
 
@@ -569,7 +799,21 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
   bfd_putl32 (CV_SIGNATURE_C13, int_buf);
 
   if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != sizeof (uint32_t))
-    return false;
+    {
+      free (c13_info);
+      return false;
+    }
+
+  if (c13_info)
+    {
+      if (bfd_bwrite (c13_info, *c13_info_size, stream) != *c13_info_size)
+       {
+         free (c13_info);
+         return false;
+       }
+
+      free (c13_info);
+    }
 
   /* Write the global refs size.  */
 
@@ -584,9 +828,11 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
 /* Create the module info substream within the DBI.  */
 static bool
 create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
-                             uint32_t *size, struct string_table *strings)
+                             uint32_t *size, struct string_table *strings,
+                             struct source_files_info *source)
 {
   uint8_t *ptr;
+  unsigned int mod_num;
 
   static const char linker_fn[] = "* Linker *";
 
@@ -631,32 +877,54 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
        len += 4 - (len % 4);
 
       *size += len;
+
+      source->mod_count++;
     }
 
   *data = xmalloc (*size);
 
   ptr = *data;
 
+  source->mods = xmalloc (source->mod_count
+                         * sizeof (struct mod_source_files));
+  memset (source->mods, 0,
+         source->mod_count * sizeof (struct mod_source_files));
+
+  mod_num = 0;
+
   for (bfd *in = coff_data (abfd)->link_info->input_bfds; in;
        in = in->link.next)
     {
       struct module_info *mod = (struct module_info *) ptr;
       uint16_t stream_num;
       bfd *stream;
-      uint32_t sym_byte_size;
+      uint32_t sym_byte_size, c13_info_size;
       uint8_t *start = ptr;
 
       stream = add_stream (pdb, NULL, &stream_num);
 
       if (!stream)
        {
+         for (unsigned int i = 0; i < source->mod_count; i++)
+           {
+             free (source->mods[i].files);
+           }
+
+         free (source->mods);
          free (*data);
          return false;
        }
 
       if (!populate_module_stream (stream, in, &sym_byte_size,
-                                  strings))
+                                  strings, &c13_info_size,
+                                  &source->mods[mod_num]))
        {
+         for (unsigned int i = 0; i < source->mod_count; i++)
+           {
+             free (source->mods[i].files);
+           }
+
+         free (source->mods);
          free (*data);
          return false;
        }
@@ -679,7 +947,7 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
       bfd_putl16 (stream_num, &mod->module_sym_stream);
       bfd_putl32 (sym_byte_size, &mod->sym_byte_size);
       bfd_putl32 (0, &mod->c11_byte_size);
-      bfd_putl32 (0, &mod->c13_byte_size);
+      bfd_putl32 (c13_info_size, &mod->c13_byte_size);
       bfd_putl16 (0, &mod->source_file_count);
       bfd_putl16 (0, &mod->padding);
       bfd_putl32 (0, &mod->unused2);
@@ -741,6 +1009,8 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
          memset (ptr, 0, 4 - ((ptr - start) % 4));
          ptr += 4 - ((ptr - start) % 4);
        }
+
+      mod_num++;
     }
 
   return true;
@@ -855,6 +1125,114 @@ create_section_contrib_substream (bfd *abfd, void **data, uint32_t *size)
   return true;
 }
 
+/* The source info substream lives within the DBI stream, and lists the
+   source files for each object file (i.e. it's derived from the
+   DEBUG_S_FILECHKSMS parts of the .debug$S sections).  This is a bit
+   superfluous, as the filenames are also available in the C13 parts of
+   the module streams, but MSVC relies on it to work properly.  */
+static void
+create_source_info_substream (void **data, uint32_t *size,
+                             struct source_files_info *source)
+{
+  uint16_t dedupe_source_files_count = 0;
+  uint16_t source_files_count = 0;
+  uint32_t strings_len = 0;
+  uint8_t *ptr;
+
+  /* Loop through the source files, marking unique filenames.  The pointers
+     here are for entries in the main string table, and so have already
+     been deduplicated.  */
+
+  for (uint16_t i = 0; i < source->mod_count; i++)
+    {
+      for (uint16_t j = 0; j < source->mods[i].files_count; j++)
+       {
+         if (source->mods[i].files[j])
+           {
+             if (source->mods[i].files[j]->source_file_offset == 0xffffffff)
+               {
+                 source->mods[i].files[j]->source_file_offset = strings_len;
+                 strings_len += source->mods[i].files[j]->len + 1;
+                 dedupe_source_files_count++;
+               }
+
+             source_files_count++;
+           }
+       }
+    }
+
+  *size = sizeof (uint16_t) + sizeof (uint16_t);
+  *size += (sizeof (uint16_t) + sizeof (uint16_t)) * source->mod_count;
+  *size += sizeof (uint32_t) * source_files_count;
+  *size += strings_len;
+
+  *data = xmalloc (*size);
+
+  ptr = (uint8_t *) *data;
+
+  /* Write header (module count and source file count).  */
+
+  bfd_putl16 (source->mod_count, ptr);
+  ptr += sizeof (uint16_t);
+
+  bfd_putl16 (dedupe_source_files_count, ptr);
+  ptr += sizeof (uint16_t);
+
+  /* Write "ModIndices".  As the LLVM documentation puts it, "this array is
+     present, but does not appear to be useful".  */
+
+  for (uint16_t i = 0; i < source->mod_count; i++)
+    {
+      bfd_putl16 (i, ptr);
+      ptr += sizeof (uint16_t);
+    }
+
+  /* Write source file count for each module.  */
+
+  for (uint16_t i = 0; i < source->mod_count; i++)
+    {
+      bfd_putl16 (source->mods[i].files_count, ptr);
+      ptr += sizeof (uint16_t);
+    }
+
+  /* For each module, write the offsets within the string table
+     for each source file.  */
+
+  for (uint16_t i = 0; i < source->mod_count; i++)
+    {
+      for (uint16_t j = 0; j < source->mods[i].files_count; j++)
+       {
+         if (source->mods[i].files[j])
+           {
+             bfd_putl32 (source->mods[i].files[j]->source_file_offset, ptr);
+             ptr += sizeof (uint32_t);
+           }
+       }
+    }
+
+  /* Write the string table.  We set source_file_offset to a dummy value for
+     each entry we write, so we don't write duplicate filenames.  */
+
+  for (uint16_t i = 0; i < source->mod_count; i++)
+    {
+      for (uint16_t j = 0; j < source->mods[i].files_count; j++)
+       {
+         if (source->mods[i].files[j]
+             && source->mods[i].files[j]->source_file_offset != 0xffffffff)
+           {
+             memcpy (ptr, source->mods[i].files[j]->s,
+                     source->mods[i].files[j]->len);
+             ptr += source->mods[i].files[j]->len;
+
+             *ptr = 0;
+             ptr++;
+
+             source->mods[i].files[j]->source_file_offset = 0xffffffff;
+           }
+       }
+    }
+}
+
 /* Stream 4 is the debug information (DBI) stream.  */
 static bool
 populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
@@ -865,19 +1243,37 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
 {
   struct pdb_dbi_stream_header h;
   struct optional_dbg_header opt;
-  void *mod_info, *sc;
-  uint32_t mod_info_size, sc_size;
+  void *mod_info, *sc, *source_info;
+  uint32_t mod_info_size, sc_size, source_info_size;
+  struct source_files_info source;
+
+  source.mod_count = 0;
+  source.mods = NULL;
 
   if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size,
-                                    strings))
+                                    strings, &source))
     return false;
 
   if (!create_section_contrib_substream (abfd, &sc, &sc_size))
     {
+      for (unsigned int i = 0; i < source.mod_count; i++)
+       {
+         free (source.mods[i].files);
+       }
+      free (source.mods);
+
       free (mod_info);
       return false;
     }
 
+  create_source_info_substream (&source_info, &source_info_size, &source);
+
+  for (unsigned int i = 0; i < source.mod_count; i++)
+    {
+      free (source.mods[i].files);
+    }
+  free (source.mods);
+
   bfd_putl32 (0xffffffff, &h.version_signature);
   bfd_putl32 (DBI_STREAM_VERSION_70, &h.version_header);
   bfd_putl32 (1, &h.age);
@@ -890,7 +1286,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
   bfd_putl32 (mod_info_size, &h.mod_info_size);
   bfd_putl32 (sc_size, &h.section_contribution_size);
   bfd_putl32 (0, &h.section_map_size);
-  bfd_putl32 (0, &h.source_info_size);
+  bfd_putl32 (source_info_size, &h.source_info_size);
   bfd_putl32 (0, &h.type_server_map_size);
   bfd_putl32 (0, &h.mfc_type_server_index);
   bfd_putl32 (sizeof (opt), &h.optional_dbg_header_size);
@@ -901,6 +1297,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
 
   if (bfd_bwrite (&h, sizeof (h), stream) != sizeof (h))
     {
+      free (source_info);
       free (sc);
       free (mod_info);
       return false;
@@ -908,6 +1305,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
 
   if (bfd_bwrite (mod_info, mod_info_size, stream) != mod_info_size)
     {
+      free (source_info);
       free (sc);
       free (mod_info);
       return false;
@@ -917,12 +1315,21 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
 
   if (bfd_bwrite (sc, sc_size, stream) != sc_size)
     {
+      free (source_info);
       free (sc);
       return false;
     }
 
   free (sc);
 
+  if (bfd_bwrite (source_info, source_info_size, stream) != source_info_size)
+    {
+      free (source_info);
+      return false;
+    }
+
+  free (source_info);
+
   bfd_putl16 (0xffff, &opt.fpo_stream);
   bfd_putl16 (0xffff, &opt.exception_stream);
   bfd_putl16 (0xffff, &opt.fixup_stream);
index 611f71041c046539123d584100cb58e7442e78d3..e8f673c24a0020783a417d3d68322e611b0e3c72 100644 (file)
--- a/ld/pdb.h
+++ b/ld/pdb.h
@@ -156,6 +156,7 @@ struct optional_dbg_header
 #define CV_SIGNATURE_C13               4
 
 #define DEBUG_S_STRINGTABLE            0xf3
+#define DEBUG_S_FILECHKSMS             0xf4
 
 #define STRING_TABLE_SIGNATURE         0xeffeeffe
 #define STRING_TABLE_VERSION           1
@@ -200,6 +201,14 @@ struct module_info
   uint32_t pdb_file_path_name_index;
 };
 
+/* filedata in dumpsym7.cpp */
+struct file_checksum
+{
+  uint32_t file_id;
+  uint8_t checksum_length;
+  uint8_t checksum_type;
+} ATTRIBUTE_PACKED;
+
 extern bool create_pdb_file (bfd *, const char *, const unsigned char *);
 
 #endif
index 09e9b4a88092323f4ab21ca1e5a1511672162ba0..9dab41110ac3a510f883fb8f221e22c8958989c2 100644 (file)
@@ -824,6 +824,160 @@ proc test3 { } {
     }
 }
 
+proc extract_c13_info { pdb mod_info } {
+    global ar
+
+    binary scan [string range $mod_info 34 35] s module_sym_stream
+    binary scan [string range $mod_info 36 39] i sym_byte_size
+    binary scan [string range $mod_info 40 43] i c11_byte_size
+    binary scan [string range $mod_info 44 47] i c13_byte_size
+
+    set index_str [format "%04x" $module_sym_stream]
+
+    set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb $index_str"]
+
+    if ![string match "" $exec_output] {
+       return ""
+    }
+
+    set fi [open tmpdir/$index_str]
+    fconfigure $fi -translation binary
+
+    seek $fi [expr $sym_byte_size + $c11_byte_size]
+
+    set data [read $fi $c13_byte_size]
+
+    close $fi
+
+    return $data
+}
+
+proc test4 { } {
+    global as
+    global ar
+    global ld
+    global objdump
+    global srcdir
+    global subdir
+
+    if ![ld_assemble $as $srcdir/$subdir/pdb3a.s tmpdir/pdb3a.o] {
+       unsupported "Build pdb3a.o"
+       return
+    }
+
+    if ![ld_assemble $as $srcdir/$subdir/pdb3b.s tmpdir/pdb3b.o] {
+       unsupported "Build pdb3b.o"
+       return
+    }
+
+    if ![ld_link $ld "tmpdir/pdb3.exe" "--pdb=tmpdir/pdb3.pdb tmpdir/pdb3a.o tmpdir/pdb3b.o"] {
+       unsupported "Create PE image with PDB file"
+       return
+    }
+
+    # read relevant bits from DBI stream
+
+    set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb3.pdb 0003"]
+
+    if ![string match "" $exec_output] {
+       fail "Could not extract DBI stream"
+       return
+    } else {
+       pass "Extracted DBI stream"
+    }
+
+    set fi [open tmpdir/0003]
+    fconfigure $fi -translation binary
+
+    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
+
+    seek $fi 24 current
+
+    set mod_info [read $fi $mod_info_size]
+
+    seek $fi [expr $section_contribution_size + $section_map_size] current
+
+    set source_info [read $fi $source_info_size]
+
+    close $fi
+
+    # check source info substream
+
+    set fi [open tmpdir/pdb3-source-info w]
+    fconfigure $fi -translation binary
+    puts -nonewline $fi $source_info
+    close $fi
+
+    set exp [file_contents "$srcdir/$subdir/pdb3-source-info.d"]
+    set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb3-source-info"]
+
+    if [string match $exp $got] {
+       pass "Correct source info substream"
+    } else {
+       fail "Incorrect source info substream"
+    }
+
+    # check C13 info in first module
+
+    set c13_info [extract_c13_info "tmpdir/pdb3.pdb" [string range $mod_info 0 63]]
+
+    set fi [open tmpdir/pdb3-c13-info1 w]
+    fconfigure $fi -translation binary
+    puts -nonewline $fi $c13_info
+    close $fi
+
+    set exp [file_contents "$srcdir/$subdir/pdb3-c13-info1.d"]
+    set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb3-c13-info1"]
+
+    if [string match $exp $got] {
+       pass "Correct C13 info for first module"
+    } else {
+       fail "Incorrect C13 info for first module"
+    }
+
+    # check C13 info in second module
+
+    set fn1_end [string first \000 $mod_info 64]
+    set fn2_end [string first \000 $mod_info [expr $fn1_end + 1]]
+
+    set off [expr $fn2_end + 1]
+
+    if { [expr $off % 4] != 0 } {
+       set off [expr $off + 4 - ($off % 4)]
+    }
+
+    set c13_info [extract_c13_info "tmpdir/pdb3.pdb" [string range $mod_info $off [expr $off + 63]]]
+
+    set fi [open tmpdir/pdb3-c13-info2 w]
+    fconfigure $fi -translation binary
+    puts -nonewline $fi $c13_info
+    close $fi
+
+    set exp [file_contents "$srcdir/$subdir/pdb3-c13-info2.d"]
+    set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb3-c13-info2"]
+
+    if [string match $exp $got] {
+       pass "Correct C13 info for second module"
+    } else {
+       fail "Incorrect C13 info for second module"
+    }
+}
+
 test1
 test2
 test3
+test4
diff --git a/ld/testsuite/ld-pe/pdb3-c13-info1.d b/ld/testsuite/ld-pe/pdb3-c13-info1.d
new file mode 100644 (file)
index 0000000..f92062b
--- /dev/null
@@ -0,0 +1,8 @@
+
+*:     file format binary
+
+Contents of section .data:
+ 0000 f4000000 30000000 02000000 10016745  ....0.........gE
+ 0010 2301efcd ab8998ba dcfe1023 45670000  #..........#Eg..
+ 0020 06000000 100198ba dcfe1023 45676745  ...........#EggE
+ 0030 2301efcd ab890000                    #.......        
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb3-c13-info2.d b/ld/testsuite/ld-pe/pdb3-c13-info2.d
new file mode 100644 (file)
index 0000000..1c33ce1
--- /dev/null
@@ -0,0 +1,8 @@
+
+*:     file format binary
+
+Contents of section .data:
+ 0000 f4000000 30000000 06000000 100198ba  ....0...........
+ 0010 dcfe1023 45676745 2301efcd ab890000  ...#EggE#.......
+ 0020 0a000000 10013b2a 19087f6e 5d4c4c5d  ......;*...n]LL]
+ 0030 6e7f0819 2a3b0000                    n...*;..        
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb3-source-info.d b/ld/testsuite/ld-pe/pdb3-source-info.d
new file mode 100644 (file)
index 0000000..5b7d58c
--- /dev/null
@@ -0,0 +1,7 @@
+
+*:     file format binary
+
+Contents of section .data:
+ 0000 03000300 00000100 02000200 02000000  ................
+ 0010 00000000 04000000 04000000 08000000  ................
+ 0020 666f6f00 62617200 62617a00           foo.bar.baz.    
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb3a.s b/ld/testsuite/ld-pe/pdb3a.s
new file mode 100644 (file)
index 0000000..71795b5
--- /dev/null
@@ -0,0 +1,52 @@
+.equ CV_SIGNATURE_C13, 4
+.equ DEBUG_S_STRINGTABLE, 0xf3
+.equ DEBUG_S_FILECHKSMS, 0xf4
+.equ CHKSUM_TYPE_MD5, 1
+
+.equ NUM_MD5_BYTES, 16
+
+.section ".debug$S", "rn"
+.long CV_SIGNATURE_C13
+.long DEBUG_S_STRINGTABLE
+.long .strings_end - .strings_start
+
+.strings_start:
+
+.asciz ""
+
+.src1:
+.asciz "foo"
+
+.src2:
+.asciz "bar"
+
+.strings_end:
+
+.balign 4
+
+.long DEBUG_S_FILECHKSMS
+.long .chksms_end - .chksms_start
+
+.chksms_start:
+
+.long .src1 - .strings_start
+.byte NUM_MD5_BYTES
+.byte CHKSUM_TYPE_MD5
+.long 0x01234567
+.long 0x89abcdef
+.long 0xfedcba98
+.long 0x67452310
+.short 0 # padding
+
+.long .src2 - .strings_start
+.byte NUM_MD5_BYTES
+.byte CHKSUM_TYPE_MD5
+.long 0xfedcba98
+.long 0x67452310
+.long 0x01234567
+.long 0x89abcdef
+.short 0 # padding
+
+.chksms_end:
+
+.balign 4
diff --git a/ld/testsuite/ld-pe/pdb3b.s b/ld/testsuite/ld-pe/pdb3b.s
new file mode 100644 (file)
index 0000000..fffb115
--- /dev/null
@@ -0,0 +1,52 @@
+.equ CV_SIGNATURE_C13, 4
+.equ DEBUG_S_STRINGTABLE, 0xf3
+.equ DEBUG_S_FILECHKSMS, 0xf4
+.equ CHKSUM_TYPE_MD5, 1
+
+.equ NUM_MD5_BYTES, 16
+
+.section ".debug$S", "rn"
+.long CV_SIGNATURE_C13
+.long DEBUG_S_STRINGTABLE
+.long .strings_end - .strings_start
+
+.strings_start:
+
+.asciz ""
+
+.src1:
+.asciz "bar"
+
+.src2:
+.asciz "baz"
+
+.strings_end:
+
+.balign 4
+
+.long DEBUG_S_FILECHKSMS
+.long .chksms_end - .chksms_start
+
+.chksms_start:
+
+.long .src1 - .strings_start
+.byte NUM_MD5_BYTES
+.byte CHKSUM_TYPE_MD5
+.long 0xfedcba98
+.long 0x67452310
+.long 0x01234567
+.long 0x89abcdef
+.short 0 # padding
+
+.long .src2 - .strings_start
+.byte NUM_MD5_BYTES
+.byte CHKSUM_TYPE_MD5
+.long 0x08192a3b
+.long 0x4c5d6e7f
+.long 0x7f6e5d4c
+.long 0x3b2a1908
+.short 0 # padding
+
+.chksms_end:
+
+.balign 4