ld: Add --pdb option
authorMark Harmstone <mark@harmstone.com>
Sun, 16 Oct 2022 23:24:18 +0000 (00:24 +0100)
committerAlan Modra <amodra@gmail.com>
Thu, 20 Oct 2022 04:52:37 +0000 (15:22 +1030)
Second patch incorporates fixes for endian and UB issues in calc_hash, as per
https://sourceware.org/pipermail/binutils/2022-October/123514.html.

bfd/libpei.h
bfd/peXXigen.c
bfd/peicode.h
ld/emultempl/pe.em
ld/emultempl/pep.em
ld/testsuite/ld-pe/pdb.exp [new file with mode: 0644]
ld/testsuite/ld-pe/pdb1.s [new file with mode: 0644]

index dcf42f930e9c92b76336de5e2ac20e610b1f17de..98e951d883913ee03cac2e468710a9aa11832698 100644 (file)
@@ -423,9 +423,11 @@ void _bfd_XX_get_symbol_info (bfd *, asymbol *, symbol_info *);
 bool _bfd_XXi_final_link_postscript (bfd *, struct coff_final_link_info *);
 void _bfd_XXi_swap_debugdir_in (bfd *, void *, void *);
 unsigned _bfd_XXi_swap_debugdir_out (bfd *, void *, void *);
-unsigned _bfd_XXi_write_codeview_record (bfd *, file_ptr, CODEVIEW_INFO *);
+unsigned _bfd_XXi_write_codeview_record
+  (bfd *, file_ptr, CODEVIEW_INFO *, const char *);
 CODEVIEW_INFO *_bfd_XXi_slurp_codeview_record
-  (bfd * abfd, file_ptr where, unsigned long length, CODEVIEW_INFO *cvinfo);
+  (bfd * abfd, file_ptr where, unsigned long length, CODEVIEW_INFO *cvinfo,
+   char **pdb);
 
 /* The following are needed only for ONE of pe or pei, but don't
    otherwise vary; peicode.h fixes up ifdefs but we provide the
index e74ed3968a2580d42eee2ff348ec5d7c8abe60a7..c5a7f7bf7ac678d80a227bb95d948dc8b501c03f 100644 (file)
@@ -1122,7 +1122,8 @@ _bfd_XXi_swap_debugdir_out (bfd * abfd, void * inp, void * extp)
 }
 
 CODEVIEW_INFO *
-_bfd_XXi_slurp_codeview_record (bfd * abfd, file_ptr where, unsigned long length, CODEVIEW_INFO *cvinfo)
+_bfd_XXi_slurp_codeview_record (bfd * abfd, file_ptr where, unsigned long length, CODEVIEW_INFO *cvinfo,
+                               char **pdb)
 {
   char buffer[256+1];
   bfd_size_type nread;
@@ -1162,6 +1163,9 @@ _bfd_XXi_slurp_codeview_record (bfd * abfd, file_ptr where, unsigned long length
       cvinfo->SignatureLength = CV_INFO_SIGNATURE_LENGTH;
       /* cvinfo->PdbFileName = cvinfo70->PdbFileName;  */
 
+      if (pdb)
+       *pdb = xstrdup (cvinfo70->PdbFileName);
+
       return cvinfo;
     }
   else if ((cvinfo->CVSignature == CVINFO_PDB20_CVSIGNATURE)
@@ -1173,6 +1177,9 @@ _bfd_XXi_slurp_codeview_record (bfd * abfd, file_ptr where, unsigned long length
       cvinfo->SignatureLength = 4;
       /* cvinfo->PdbFileName = cvinfo20->PdbFileName;  */
 
+      if (pdb)
+       *pdb = xstrdup (cvinfo20->PdbFileName);
+
       return cvinfo;
     }
 
@@ -1180,9 +1187,11 @@ _bfd_XXi_slurp_codeview_record (bfd * abfd, file_ptr where, unsigned long length
 }
 
 unsigned int
-_bfd_XXi_write_codeview_record (bfd * abfd, file_ptr where, CODEVIEW_INFO *cvinfo)
+_bfd_XXi_write_codeview_record (bfd * abfd, file_ptr where, CODEVIEW_INFO *cvinfo,
+                               const char *pdb)
 {
-  const bfd_size_type size = sizeof (CV_INFO_PDB70) + 1;
+  size_t pdb_len = pdb ? strlen (pdb) : 0;
+  const bfd_size_type size = sizeof (CV_INFO_PDB70) + pdb_len + 1;
   bfd_size_type written;
   CV_INFO_PDB70 *cvinfo70;
   char * buffer;
@@ -1205,7 +1214,11 @@ _bfd_XXi_write_codeview_record (bfd * abfd, file_ptr where, CODEVIEW_INFO *cvinf
   memcpy (&(cvinfo70->Signature[8]), &(cvinfo->Signature[8]), 8);
 
   H_PUT_32 (abfd, cvinfo->Age, cvinfo70->Age);
-  cvinfo70->PdbFileName[0] = '\0';
+
+  if (pdb == NULL)
+    cvinfo70->PdbFileName[0] = '\0';
+  else
+    memcpy (cvinfo70->PdbFileName, pdb, pdb_len + 1);
 
   written = bfd_bwrite (buffer, size, abfd);
 
@@ -2603,22 +2616,25 @@ pe_print_debugdata (bfd * abfd, void * vfile)
             We need to use a 32-bit aligned buffer
             to safely read in a codeview record.  */
          char buffer[256 + 1] ATTRIBUTE_ALIGNED_ALIGNOF (CODEVIEW_INFO);
+         char *pdb;
 
          CODEVIEW_INFO *cvinfo = (CODEVIEW_INFO *) buffer;
 
          /* The debug entry doesn't have to have to be in a section,
             in which case AddressOfRawData is 0, so always use PointerToRawData.  */
          if (!_bfd_XXi_slurp_codeview_record (abfd, (file_ptr) idd.PointerToRawData,
-                                              idd.SizeOfData, cvinfo))
+                                              idd.SizeOfData, cvinfo, &pdb))
            continue;
 
          for (j = 0; j < cvinfo->SignatureLength; j++)
            sprintf (&signature[j*2], "%02x", cvinfo->Signature[j] & 0xff);
 
          /* xgettext:c-format */
-         fprintf (file, _("(format %c%c%c%c signature %s age %ld)\n"),
+         fprintf (file, _("(format %c%c%c%c signature %s age %ld pdb %s)\n"),
                   buffer[0], buffer[1], buffer[2], buffer[3],
-                  signature, cvinfo->Age);
+                  signature, cvinfo->Age, pdb[0] ? pdb : "(none)");
+
+         free (pdb);
        }
     }
 
index add8d82bfde9604ec1c2cb4e2d557f65495b5d61..f7ba24ae10aa863ec4742349477c398c2c745b47 100644 (file)
@@ -1402,7 +1402,7 @@ pe_bfd_read_buildid (bfd *abfd)
          */
          if (_bfd_XXi_slurp_codeview_record (abfd,
                                              (file_ptr) idd.PointerToRawData,
-                                             idd.SizeOfData, cvinfo))
+                                             idd.SizeOfData, cvinfo, NULL))
            {
              struct bfd_build_id* build_id = bfd_alloc (abfd,
                         sizeof (struct bfd_build_id) + cvinfo->SignatureLength);
index c01829670d60cc676ead52250e8f4685e01da05e..db906793298a195f8613d71a7d22f5fbc729a356 100644 (file)
@@ -146,6 +146,8 @@ static lang_assignment_statement_type *image_base_statement = 0;
 static unsigned short pe_dll_characteristics = DEFAULT_DLL_CHARACTERISTICS;
 static bool insert_timestamp = true;
 static const char *emit_build_id;
+static int pdb;
+static char *pdb_name;
 
 #ifdef DLL_SUPPORT
 static int pe_enable_stdcall_fixup = -1; /* 0=disable 1=enable.  */
@@ -285,7 +287,8 @@ fragment <<EOF
 #define OPTION_INSERT_TIMESTAMP                (OPTION_TERMINAL_SERVER_AWARE + 1)
 #define OPTION_NO_INSERT_TIMESTAMP     (OPTION_INSERT_TIMESTAMP + 1)
 #define OPTION_BUILD_ID                        (OPTION_NO_INSERT_TIMESTAMP + 1)
-#define OPTION_ENABLE_RELOC_SECTION    (OPTION_BUILD_ID + 1)
+#define OPTION_PDB                     (OPTION_BUILD_ID + 1)
+#define OPTION_ENABLE_RELOC_SECTION    (OPTION_PDB + 1)
 #define OPTION_DISABLE_RELOC_SECTION   (OPTION_ENABLE_RELOC_SECTION + 1)
 /* DLL Characteristics flags.  */
 #define OPTION_DISABLE_DYNAMIC_BASE    (OPTION_DISABLE_RELOC_SECTION + 1)
@@ -384,6 +387,7 @@ gld${EMULATION_NAME}_add_options
     {"tsaware", no_argument, NULL, OPTION_TERMINAL_SERVER_AWARE},
     {"disable-tsaware", no_argument, NULL, OPTION_DISABLE_TERMINAL_SERVER_AWARE},
     {"build-id", optional_argument, NULL, OPTION_BUILD_ID},
+    {"pdb", required_argument, NULL, OPTION_PDB},
     {"enable-reloc-section", no_argument, NULL, OPTION_ENABLE_RELOC_SECTION},
     {"disable-reloc-section", no_argument, NULL, OPTION_DISABLE_RELOC_SECTION},
     {NULL, no_argument, NULL, 0}
@@ -533,6 +537,7 @@ gld${EMULATION_NAME}_list_options (FILE *file)
   fprintf (file, _("  --[disable-]wdmdriver              Driver uses the WDM model\n"));
   fprintf (file, _("  --[disable-]tsaware                Image is Terminal Server aware\n"));
   fprintf (file, _("  --build-id[=STYLE]                 Generate build ID\n"));
+  fprintf (file, _("  --pdb=[FILENAME]                   Generate PDB file\n"));
 }
 
 /* A case insensitive comparison, regardless of the host platform, used for
@@ -973,6 +978,11 @@ gld${EMULATION_NAME}_handle_option (int optc)
       if (strcmp (optarg, "none"))
        emit_build_id = xstrdup (optarg);
       break;
+    case OPTION_PDB:
+      pdb = 1;
+      if (optarg && optarg[0])
+       pdb_name = xstrdup (optarg);
+      break;
     }
 
   /*  Set DLLCharacteristics bits  */
@@ -1095,6 +1105,9 @@ gld${EMULATION_NAME}_after_parse (void)
     einfo (_("%P: warning: --export-dynamic is not supported for PE "
       "targets, did you mean --export-all-symbols?\n"));
 
+  if (pdb && emit_build_id == NULL)
+    emit_build_id = xstrdup (DEFAULT_BUILD_ID_STYLE);
+
   set_entry_point ();
 
   after_parse_default ();
@@ -1274,6 +1287,7 @@ write_build_id (bfd *abfd)
   bfd_size_type size;
   bfd_size_type build_id_size;
   unsigned char *build_id;
+  const char *pdb_base_name = NULL;
 
   /* Find the section the .buildid output section has been merged info.  */
   for (asec = abfd->sections; asec != NULL; asec = asec->next)
@@ -1313,6 +1327,9 @@ write_build_id (bfd *abfd)
 
   bfd_vma ib = pe_data (link_info.output_bfd)->pe_opthdr.ImageBase;
 
+  if (pdb_name)
+    pdb_base_name = lbasename (pdb_name);
+
   /* Construct a debug directory entry which points to an immediately following CodeView record.  */
   struct internal_IMAGE_DEBUG_DIRECTORY idd;
   idd.Characteristics = 0;
@@ -1320,7 +1337,7 @@ write_build_id (bfd *abfd)
   idd.MajorVersion = 0;
   idd.MinorVersion = 0;
   idd.Type = PE_IMAGE_DEBUG_TYPE_CODEVIEW;
-  idd.SizeOfData = sizeof (CV_INFO_PDB70) + 1;
+  idd.SizeOfData = sizeof (CV_INFO_PDB70) + (pdb_base_name ? strlen (pdb_base_name) : 0) + 1;
   idd.AddressOfRawData = asec->vma - ib + link_order->offset
     + sizeof (struct external_IMAGE_DEBUG_DIRECTORY);
   idd.PointerToRawData = asec->filepos + link_order->offset
@@ -1349,7 +1366,8 @@ write_build_id (bfd *abfd)
   free (build_id);
 
   /* Write the codeview record.  */
-  if (_bfd_XXi_write_codeview_record (abfd, idd.PointerToRawData, &cvinfo) == 0)
+  if (_bfd_XXi_write_codeview_record (abfd, idd.PointerToRawData, &cvinfo,
+                                     pdb_base_name) == 0)
     return 0;
 
   /* Record the location of the debug directory in the data directory.  */
@@ -1386,11 +1404,14 @@ setup_build_id (bfd *ibfd)
 
       /* Section is a fixed size:
         One IMAGE_DEBUG_DIRECTORY entry, of type IMAGE_DEBUG_TYPE_CODEVIEW,
-        pointing at a CV_INFO_PDB70 record containing the build-id, with a
-        null byte for PdbFileName.  */
+        pointing at a CV_INFO_PDB70 record containing the build-id, followed by
+        PdbFileName if relevant.  */
       s->size = sizeof (struct external_IMAGE_DEBUG_DIRECTORY)
        + sizeof (CV_INFO_PDB70) + 1;
 
+      if (pdb_name)
+       s->size += strlen (pdb_name);
+
       return true;
     }
 
@@ -1421,6 +1442,25 @@ gld${EMULATION_NAME}_after_open (void)
     }
 #endif
 
+  if (pdb && !pdb_name)
+    {
+      const char *base = lbasename (bfd_get_filename (link_info.output_bfd));
+      size_t len = strlen (base);
+      static const char suffix[] = ".pdb";
+
+      while (len > 0 && base[len] != '.')
+       {
+         len--;
+       }
+
+      if (len == 0)
+       len = strlen (base);
+
+      pdb_name = xmalloc (len + sizeof (suffix));
+      memcpy (pdb_name, base, len);
+      memcpy (pdb_name + len, suffix, sizeof (suffix));
+    }
+
   if (emit_build_id != NULL)
     {
       bfd *abfd;
index 7a390e74b059b340e7e944348b42e92c266e1432..66cc61ec49c34e7bcfeb50099849d4ef8cbf421e 100644 (file)
@@ -165,6 +165,8 @@ static lang_assignment_statement_type *image_base_statement = 0;
 static unsigned short pe_dll_characteristics = DEFAULT_DLL_CHARACTERISTICS;
 static bool insert_timestamp = true;
 static const char *emit_build_id;
+static int pdb;
+static char *pdb_name;
 
 #ifdef DLL_SUPPORT
 static int    pep_enable_stdcall_fixup = 1; /* 0=disable 1=enable (default).  */
@@ -281,6 +283,7 @@ enum options
   OPTION_NO_INSERT_TIMESTAMP,
   OPTION_TERMINAL_SERVER_AWARE,
   OPTION_BUILD_ID,
+  OPTION_PDB,
   OPTION_ENABLE_RELOC_SECTION,
   OPTION_DISABLE_RELOC_SECTION,
   OPTION_DISABLE_HIGH_ENTROPY_VA,
@@ -369,6 +372,7 @@ gld${EMULATION_NAME}_add_options
     {"insert-timestamp", no_argument, NULL, OPTION_INSERT_TIMESTAMP},
     {"no-insert-timestamp", no_argument, NULL, OPTION_NO_INSERT_TIMESTAMP},
     {"build-id", optional_argument, NULL, OPTION_BUILD_ID},
+    {"pdb", required_argument, NULL, OPTION_PDB},
     {"enable-reloc-section", no_argument, NULL, OPTION_ENABLE_RELOC_SECTION},
     {"disable-reloc-section", no_argument, NULL, OPTION_DISABLE_RELOC_SECTION},
     {"disable-high-entropy-va", no_argument, NULL, OPTION_DISABLE_HIGH_ENTROPY_VA},
@@ -516,6 +520,7 @@ gld${EMULATION_NAME}_list_options (FILE *file)
   fprintf (file, _("  --[disable-]wdmdriver              Driver uses the WDM model\n"));
   fprintf (file, _("  --[disable-]tsaware                Image is Terminal Server aware\n"));
   fprintf (file, _("  --build-id[=STYLE]                 Generate build ID\n"));
+  fprintf (file, _("  --pdb=[FILENAME]                   Generate PDB file\n"));
 #endif
 }
 
@@ -924,6 +929,11 @@ gld${EMULATION_NAME}_handle_option (int optc)
       if (strcmp (optarg, "none"))
        emit_build_id = xstrdup (optarg);
       break;
+    case OPTION_PDB:
+      pdb = 1;
+      if (optarg && optarg[0])
+       pdb_name = xstrdup (optarg);
+      break;
     }
 
   /*  Set DLLCharacteristics bits  */
@@ -1046,6 +1056,9 @@ gld${EMULATION_NAME}_after_parse (void)
     einfo (_("%P: warning: --export-dynamic is not supported for PE+ "
       "targets, did you mean --export-all-symbols?\n"));
 
+  if (pdb && emit_build_id == NULL)
+    emit_build_id = xstrdup (DEFAULT_BUILD_ID_STYLE);
+
   set_entry_point ();
 
   after_parse_default ();
@@ -1266,6 +1279,7 @@ write_build_id (bfd *abfd)
   bfd_size_type size;
   bfd_size_type build_id_size;
   unsigned char *build_id;
+  const char *pdb_base_name = NULL;
 
   /* Find the section the .buildid output section has been merged info.  */
   for (asec = abfd->sections; asec != NULL; asec = asec->next)
@@ -1305,6 +1319,9 @@ write_build_id (bfd *abfd)
 
   bfd_vma ib = pe_data (link_info.output_bfd)->pe_opthdr.ImageBase;
 
+  if (pdb_name)
+    pdb_base_name = lbasename (pdb_name);
+
   /* Construct a debug directory entry which points to an immediately following CodeView record.  */
   struct internal_IMAGE_DEBUG_DIRECTORY idd;
   idd.Characteristics = 0;
@@ -1312,7 +1329,7 @@ write_build_id (bfd *abfd)
   idd.MajorVersion = 0;
   idd.MinorVersion = 0;
   idd.Type = PE_IMAGE_DEBUG_TYPE_CODEVIEW;
-  idd.SizeOfData = sizeof (CV_INFO_PDB70) + 1;
+  idd.SizeOfData = sizeof (CV_INFO_PDB70) + (pdb_base_name ? strlen (pdb_base_name) : 0) + 1;
   idd.AddressOfRawData = asec->vma - ib + link_order->offset
     + sizeof (struct external_IMAGE_DEBUG_DIRECTORY);
   idd.PointerToRawData = asec->filepos + link_order->offset
@@ -1341,7 +1358,8 @@ write_build_id (bfd *abfd)
   free (build_id);
 
   /* Write the codeview record.  */
-  if (_bfd_XXi_write_codeview_record (abfd, idd.PointerToRawData, &cvinfo) == 0)
+  if (_bfd_XXi_write_codeview_record (abfd, idd.PointerToRawData, &cvinfo,
+                                     pdb_base_name) == 0)
     return 0;
 
   /* Record the location of the debug directory in the data directory.  */
@@ -1378,11 +1396,14 @@ setup_build_id (bfd *ibfd)
 
       /* Section is a fixed size:
         One IMAGE_DEBUG_DIRECTORY entry, of type IMAGE_DEBUG_TYPE_CODEVIEW,
-        pointing at a CV_INFO_PDB70 record containing the build-id, with a
-        null byte for PdbFileName.  */
+        pointing at a CV_INFO_PDB70 record containing the build-id, followed by
+        PdbFileName if relevant.  */
       s->size = sizeof (struct external_IMAGE_DEBUG_DIRECTORY)
        + sizeof (CV_INFO_PDB70) + 1;
 
+      if (pdb_name)
+       s->size += strlen (pdb_name);
+
       return true;
     }
 
@@ -1414,6 +1435,25 @@ gld${EMULATION_NAME}_after_open (void)
     }
 #endif
 
+  if (pdb && !pdb_name)
+    {
+      const char *base = lbasename (bfd_get_filename (link_info.output_bfd));
+      size_t len = strlen (base);
+      static const char suffix[] = ".pdb";
+
+      while (len > 0 && base[len] != '.')
+       {
+         len--;
+       }
+
+      if (len == 0)
+       len = strlen (base);
+
+      pdb_name = xmalloc (len + sizeof (suffix));
+      memcpy (pdb_name, base, len);
+      memcpy (pdb_name + len, suffix, sizeof (suffix));
+    }
+
   if (emit_build_id != NULL)
     {
       bfd *abfd;
diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp
new file mode 100644 (file)
index 0000000..1560241
--- /dev/null
@@ -0,0 +1,53 @@
+# Expect script for creating PDB files when linking.
+#   Copyright (C) 2022 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Binutils.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+if {![istarget i*86-*-mingw*]
+  && ![istarget x86_64-*-mingw*]} {
+    return
+}
+
+proc get_pdb_name { pe } {
+    global OBJDUMP
+
+    set exec_output [run_host_cmd "$OBJDUMP" "-p $pe"]
+
+    if ![regexp -line "^\\(format RSDS signature (\[0-9a-fA-F\]{32}) age 1 pdb (.*)\\)$" $exec_output full sig pdb] {
+       return ""
+    }
+
+    return $pdb
+}
+
+if ![ld_assemble $as $srcdir/$subdir/pdb1.s tmpdir/pdb1.o] {
+    unsupported "Build pdb1.o"
+    return
+}
+
+if ![ld_link $ld "tmpdir/pdb1.exe" "--pdb=tmpdir/pdb1.pdb tmpdir/pdb1.o"] {
+    fail "Could not create a PE image with a PDB file"
+    return
+}
+
+if ![string equal [get_pdb_name "tmpdir/pdb1.exe"] "pdb1.pdb"] {
+    fail "PDB filename not found in CodeView debug info"
+    return
+}
+
+pass "PDB filename present in CodeView debug info"
diff --git a/ld/testsuite/ld-pe/pdb1.s b/ld/testsuite/ld-pe/pdb1.s
new file mode 100644 (file)
index 0000000..30a8cfc
--- /dev/null
@@ -0,0 +1,5 @@
+.text
+
+.global foo
+foo:
+       .long 0x12345678