public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
From: Mark Harmstone <mark@harmstone.com>
To: binutils@sourceware.org
Cc: Mark Harmstone <mark@harmstone.com>
Subject: [PATCH] ld: Write linker symbols in PDB
Date: Mon,  5 Dec 2022 01:53:47 +0000	[thread overview]
Message-ID: <20221205015347.25781-3-mark@harmstone.com> (raw)
In-Reply-To: <20221205015347.25781-1-mark@harmstone.com>

Adds a few symbols that Microsoft's LINK generates itself, with the
version number etc.

---
 ld/pdb.c                   | 246 ++++++++++++++++++++++++++++++++-----
 ld/pdb.h                   |  42 +++++++
 ld/testsuite/ld-pe/pdb.exp |  77 ++++++++++++
 3 files changed, 331 insertions(+), 34 deletions(-)

diff --git a/ld/pdb.c b/ld/pdb.c
index e16e45c23b1..86c71cfdfbb 100644
--- a/ld/pdb.c
+++ b/ld/pdb.c
@@ -21,6 +21,7 @@
 #include "pdb.h"
 #include "bfdlink.h"
 #include "ld.h"
+#include "ldmain.h"
 #include "ldmisc.h"
 #include "libbfd.h"
 #include "libiberty.h"
@@ -3422,6 +3423,171 @@ handle_debugt_section (asection *s, bfd *mod, struct types *types,
   return true;
 }
 
+/* Return the CodeView constant for the selected architecture.  */
+static uint16_t
+target_processor (bfd *abfd)
+{
+  if (abfd->arch_info->arch != bfd_arch_i386)
+    return 0;
+
+  if (abfd->arch_info->mach & bfd_mach_x86_64)
+    return CV_CFL_X64;
+  else
+    return CV_CFL_80386;
+}
+
+/* Create the symbols that go in "* Linker *", the dummy module created
+   for the linker itself.  */
+static bool
+create_linker_symbols (bfd *abfd, uint8_t **syms, uint32_t *sym_byte_size,
+		       const char *pdb_name)
+{
+  uint8_t *ptr;
+  struct objname *name;
+  struct compile3 *comp;
+  struct envblock *env;
+  size_t padding1, padding2, env_size;
+  char *cwdval, *exeval, *pdbval;
+
+  /* extra NUL for padding */
+  static const char linker_fn[] = "* Linker *\0";
+  static const char linker_name[] = "GNU LD " VERSION;
+
+  static const char cwd[] = "cwd";
+  static const char exe[] = "exe";
+  static const char pdb[] = "pdb";
+
+  cwdval = getcwd (NULL, 0);
+  if (!cwdval)
+    {
+      einfo (_("%P: warning: unable to get working directory\n"));
+      return false;
+    }
+
+  exeval = lrealpath (program_name);
+
+  if (!exeval)
+    {
+      einfo (_("%P: warning: unable to get program name\n"));
+      free (cwdval);
+      return false;
+    }
+
+  pdbval = lrealpath (pdb_name);
+
+  if (!pdbval)
+    {
+      einfo (_("%P: warning: unable to get full path to PDB\n"));
+      free (exeval);
+      free (cwdval);
+      return false;
+    }
+
+  *sym_byte_size += offsetof (struct objname, name) + sizeof (linker_fn);
+  *sym_byte_size += offsetof (struct compile3, compiler) + sizeof (linker_name);
+
+  if (*sym_byte_size % 4)
+    padding1 = 4 - (*sym_byte_size % 4);
+  else
+    padding1 = 0;
+
+  *sym_byte_size += padding1;
+
+  env_size = offsetof (struct envblock, strings);
+  env_size += sizeof (cwd);
+  env_size += strlen (cwdval) + 1;
+  env_size += sizeof (exe);
+  env_size += strlen (exeval) + 1;
+  env_size += sizeof (pdb);
+  env_size += strlen (pdbval) + 1;
+
+  if (env_size % 4)
+    padding2 = 4 - (env_size % 4);
+  else
+    padding2 = 0;
+
+  env_size += padding2;
+
+  *sym_byte_size += env_size;
+
+  *syms = xmalloc (*sym_byte_size);
+  ptr = *syms;
+
+  /* Write S_OBJNAME */
+
+  name = (struct objname *) ptr;
+  bfd_putl16 (offsetof (struct objname, name)
+	      + sizeof (linker_fn) - sizeof (uint16_t),
+	      &name->size);
+  bfd_putl16 (S_OBJNAME, &name->kind);
+  bfd_putl32 (0, &name->signature);
+  memcpy (name->name, linker_fn, sizeof (linker_fn));
+
+  ptr += offsetof (struct objname, name) + sizeof (linker_fn);
+
+  /* Write S_COMPILE3 */
+
+  comp = (struct compile3 *) ptr;
+
+  bfd_putl16 (offsetof (struct compile3, compiler) + sizeof (linker_name)
+	      + padding1 - sizeof (uint16_t),
+	      &comp->size);
+  bfd_putl16 (S_COMPILE3, &comp->kind);
+  bfd_putl32 (CV_CFL_LINK, &comp->flags);
+  bfd_putl16 (target_processor (abfd), &comp->machine);
+  bfd_putl16 (0, &comp->frontend_major);
+  bfd_putl16 (0, &comp->frontend_minor);
+  bfd_putl16 (0, &comp->frontend_build);
+  bfd_putl16 (0, &comp->frontend_qfe);
+  bfd_putl16 (0, &comp->backend_major);
+  bfd_putl16 (0, &comp->backend_minor);
+  bfd_putl16 (0, &comp->backend_build);
+  bfd_putl16 (0, &comp->backend_qfe);
+  memcpy (comp->compiler, linker_name, sizeof (linker_name));
+
+  memset (comp->compiler + sizeof (linker_name), 0, padding1);
+
+  ptr += offsetof (struct compile3, compiler)
+	 + sizeof (linker_name) + padding1;
+
+  /* Write S_ENVBLOCK */
+
+  env = (struct envblock *) ptr;
+
+  bfd_putl16 (env_size - sizeof (uint16_t), &env->size);
+  bfd_putl16 (S_ENVBLOCK, &env->kind);
+  env->flags = 0;
+
+  ptr += offsetof (struct envblock, strings);
+
+  memcpy (ptr, cwd, sizeof (cwd));
+  ptr += sizeof (cwd);
+  memcpy (ptr, cwdval, strlen (cwdval) + 1);
+  ptr += strlen (cwdval) + 1;
+
+  memcpy (ptr, exe, sizeof (exe));
+  ptr += sizeof (exe);
+  memcpy (ptr, exeval, strlen (exeval) + 1);
+  ptr += strlen (exeval) + 1;
+
+  memcpy (ptr, pdb, sizeof (pdb));
+  ptr += sizeof (pdb);
+  memcpy (ptr, pdbval, strlen (pdbval) + 1);
+  ptr += strlen (pdbval) + 1;
+
+  /* Microsoft's LINK also includes "cmd", the command-line options passed
+     to the linker, but unfortunately we don't have access to argc and argv
+     at this stage.  */
+
+  memset (ptr, 0, padding2);
+
+  free (pdbval);
+  free (exeval);
+  free (cwdval);
+
+  return true;
+}
+
 /* Populate the module stream, which consists of the transformed .debug$S
    data for each object file.  */
 static bool
@@ -3431,55 +3597,65 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
 			struct mod_source_files *mod_source,
 			bfd *abfd, struct types *types,
 			struct types *ids, uint16_t mod_num,
-			bfd *sym_rec_stream, struct globals *glob)
+			bfd *sym_rec_stream, struct globals *glob,
+			const char *pdb_name)
 {
   uint8_t int_buf[sizeof (uint32_t)];
   uint8_t *c13_info = NULL;
   uint8_t *syms = NULL;
-  struct type_entry **map = NULL;
-  uint32_t num_types = 0;
 
   *sym_byte_size = 0;
   *c13_info_size = 0;
 
-  /* Process .debug$T section.  */
-
-  for (asection *s = mod->sections; s; s = s->next)
+  if (!strcmp (bfd_get_filename (mod), "dll stuff"))
     {
-      if (!strcmp (s->name, ".debug$T") && s->size >= sizeof (uint32_t))
+      if (!create_linker_symbols (mod, &syms, sym_byte_size, pdb_name))
+	return false;
+    }
+  else
+    {
+      struct type_entry **map = NULL;
+      uint32_t num_types = 0;
+
+      /* Process .debug$T section.  */
+
+      for (asection *s = mod->sections; s; s = s->next)
 	{
-	  if (!handle_debugt_section (s, mod, types, ids, mod_num, strings,
-				      &map, &num_types))
+	  if (!strcmp (s->name, ".debug$T") && s->size >= sizeof (uint32_t))
 	    {
-	      free (mod_source->files);
-	      return false;
-	    }
+	      if (!handle_debugt_section (s, mod, types, ids, mod_num, strings,
+					  &map, &num_types))
+		{
+		  free (mod_source->files);
+		  return false;
+		}
 
-	  break;
+	      break;
+	    }
 	}
-    }
 
-  /* Process .debug$S section(s).  */
+      /* Process .debug$S section(s).  */
 
-  for (asection *s = mod->sections; s; s = s->next)
-    {
-      if (!strcmp (s->name, ".debug$S") && s->size >= sizeof (uint32_t))
+      for (asection *s = mod->sections; s; s = s->next)
 	{
-	  if (!handle_debugs_section (s, mod, strings, &c13_info,
-				      c13_info_size, mod_source, abfd,
-				      &syms, sym_byte_size, map, num_types,
-				      sym_rec_stream, glob, mod_num))
+	  if (!strcmp (s->name, ".debug$S") && s->size >= sizeof (uint32_t))
 	    {
-	      free (c13_info);
-	      free (syms);
-	      free (mod_source->files);
-	      free (map);
-	      return false;
+	      if (!handle_debugs_section (s, mod, strings, &c13_info,
+					  c13_info_size, mod_source, abfd,
+					  &syms, sym_byte_size, map, num_types,
+					  sym_rec_stream, glob, mod_num))
+		{
+		  free (c13_info);
+		  free (syms);
+		  free (mod_source->files);
+		  free (map);
+		  return false;
+		}
 	    }
 	}
-    }
 
-  free (map);
+      free (map);
+    }
 
   /* Write the signature.  */
 
@@ -3533,7 +3709,8 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
 			      uint32_t *size, struct string_table *strings,
 			      struct source_files_info *source,
 			      struct types *types, struct types *ids,
-			      bfd *sym_rec_stream, struct globals *glob)
+			      bfd *sym_rec_stream, struct globals *glob,
+			      const char *pdb_name)
 {
   uint8_t *ptr;
   unsigned int mod_num;
@@ -3623,7 +3800,7 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
 				   strings, &c13_info_size,
 				   &source->mods[mod_num], abfd,
 				   types, ids, mod_num,
-				   sym_rec_stream, glob))
+				   sym_rec_stream, glob, pdb_name))
 	{
 	  for (unsigned int i = 0; i < source->mod_count; i++)
 	    {
@@ -4112,7 +4289,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
 		     struct string_table *strings,
 		     struct types *types,
 		     struct types *ids,
-		     bfd *sym_rec_stream)
+		     bfd *sym_rec_stream, const char *pdb_name)
 {
   struct pdb_dbi_stream_header h;
   struct optional_dbg_header opt;
@@ -4135,7 +4312,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
 
   if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size,
 				     strings, &source, types, ids,
-				     sym_rec_stream, &glob))
+				     sym_rec_stream, &glob, pdb_name))
     {
       htab_delete (glob.hashmap);
       return false;
@@ -4829,7 +5006,8 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
 
   if (!populate_dbi_stream (dbi_stream, abfd, pdb, section_header_stream_num,
 			    sym_rec_stream_num, publics_stream_num,
-			    &strings, &types, &ids, sym_rec_stream))
+			    &strings, &types, &ids, sym_rec_stream,
+			    pdb_name))
     {
       einfo (_("%P: warning: cannot populate DBI stream "
 	       "in PDB file: %E\n"));
diff --git a/ld/pdb.h b/ld/pdb.h
index 2ac4701645e..43daf7ca748 100644
--- a/ld/pdb.h
+++ b/ld/pdb.h
@@ -93,6 +93,7 @@
 #define S_LPROCREF			0x1127
 #define S_FRAMECOOKIE			0x113a
 #define S_COMPILE3			0x113c
+#define S_ENVBLOCK			0x113d
 #define S_LOCAL				0x113e
 #define S_DEFRANGE_REGISTER		0x1141
 #define S_DEFRANGE_FRAMEPOINTER_REL	0x1142
@@ -796,6 +797,47 @@ struct heap_alloc_site
   uint32_t type;
 } ATTRIBUTE_PACKED;
 
+/* OBJNAMESYM in cvinfo.h */
+struct objname
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t signature;
+  char name[];
+} ATTRIBUTE_PACKED;
+
+#define CV_CFL_80386			0x03
+#define CV_CFL_X64			0xD0
+
+#define CV_CFL_LINK			0x07
+
+/* COMPILESYM3 in cvinfo.h */
+struct compile3
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t flags;
+  uint16_t machine;
+  uint16_t frontend_major;
+  uint16_t frontend_minor;
+  uint16_t frontend_build;
+  uint16_t frontend_qfe;
+  uint16_t backend_major;
+  uint16_t backend_minor;
+  uint16_t backend_build;
+  uint16_t backend_qfe;
+  char compiler[];
+} ATTRIBUTE_PACKED;
+
+/* ENVBLOCKSYM in cvinfo.h */
+struct envblock
+{
+  uint16_t size;
+  uint16_t kind;
+  uint8_t flags;
+  char strings[];
+} ATTRIBUTE_PACKED;
+
 extern bool create_pdb_file (bfd *, const char *, const unsigned char *);
 
 #endif
diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp
index 5df1583c247..bd50b2fb076 100644
--- a/ld/testsuite/ld-pe/pdb.exp
+++ b/ld/testsuite/ld-pe/pdb.exp
@@ -1678,6 +1678,83 @@ proc test9 { } {
     } else {
 	fail "Incorrect symbols in module stream"
     }
+
+    # check linker symbols
+
+    set off 64
+
+    set obj1 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]]
+    incr off [expr [string length $obj1] + 1]
+
+    set ar1 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]]
+    incr off [expr [string length $ar1] + 1]
+
+    if { [expr $off % 4] != 0 } {
+	set off [expr $off + 4 - ($off % 4)]
+    }
+
+    incr off 34
+
+    binary scan [string range $mod_info $off [expr $off + 1]] s linker_syms_index
+
+    set index_str [format "%04x" $linker_syms_index]
+
+    set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms2.pdb $index_str"]
+
+    if ![string match "" $exec_output] {
+	fail "Could not extract linker symbols"
+	return
+    } else {
+	pass "Extracted linker symbols"
+    }
+
+    set syms [file_contents "tmpdir/$index_str"]
+
+    # check S_OBJNAME
+
+    set off 4
+    binary scan [string range $syms $off [expr $off + 1]] s sym_len
+    binary scan [string range $syms [expr $off + 2] [expr $off + 3]] s sym_type
+
+    if { $sym_type != 0x1101 } {
+	fail "First linker symbol was not S_OBJNAME"
+    } else {
+	pass "First linker symbol was S_OBJNAME"
+
+	set linker_fn [string range $syms [expr $off + 8] [expr [string first \000 $syms [expr $off + 8]] - 1]]
+
+	if ![string equal $linker_fn "* Linker *"] {
+	    fail "Incorrect linker object name"
+	} else {
+	    pass "Correct linker object name"
+	}
+    }
+
+    incr off [expr $sym_len + 2]
+
+    # check S_COMPILE3
+
+    binary scan [string range $syms $off [expr $off + 1]] s sym_len
+    binary scan [string range $syms [expr $off + 2] [expr $off + 3]] s sym_type
+
+    if { $sym_type != 0x113c } {
+	fail "Second linker symbol was not S_COMPILE3"
+    } else {
+	pass "Second linker symbol was S_COMPILE3"
+    }
+
+    incr off [expr $sym_len + 2]
+
+    # check S_ENVBLOCK
+
+    binary scan [string range $syms $off [expr $off + 1]] s sym_len
+    binary scan [string range $syms [expr $off + 2] [expr $off + 3]] s sym_type
+
+    if { $sym_type != 0x113d } {
+	fail "Third linker symbol was not S_ENVBLOCK"
+    } else {
+	pass "Third linker symbol was S_ENVBLOCK"
+    }
 }
 
 test1
-- 
2.37.4


  parent reply	other threads:[~2022-12-05  1:53 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-25  2:53 [PATCH v2] ld: Generate PDB string table Mark Harmstone
2022-11-25  2:54 ` [PATCH] ld: Write DEBUG_S_FILECHKSMS entries in PDBs Mark Harmstone
2022-11-27  2:38   ` [PATCH] ld: Fix segfault in populate_publics_stream Mark Harmstone
2022-11-27  2:38     ` [PATCH] ld: Write DEBUG_S_LINES entries in PDB file Mark Harmstone
2022-11-29  0:10       ` [PATCH] ld: Write types into TPI stream of PDB Mark Harmstone
2022-11-29  0:10         ` [PATCH] ld: Write types into IPI " Mark Harmstone
2022-11-29  0:10         ` [PATCH] ld: Parse LF_UDT_SRC_LINE records when creating PDB file Mark Harmstone
2022-12-05  1:53           ` [PATCH] ld: Write globals stream in PDB Mark Harmstone
2022-12-05  1:53             ` [PATCH] ld: Copy other symbols into PDB file Mark Harmstone
2022-12-05  1:53             ` Mark Harmstone [this message]
2022-12-06 17:07             ` [PATCH] ld: Write globals stream in PDB Nick Clifton
2022-12-06 17:52               ` Mark Harmstone
2022-12-08 11:00                 ` Nick Clifton
2022-12-09  1:11               ` Mark Harmstone
2022-11-28 14:54     ` [PATCH] ld: Fix segfault in populate_publics_stream Jan Beulich
2022-11-28 17:53       ` Mark Harmstone
2022-11-29  9:00         ` Jan Beulich
2022-11-29 17:47           ` Mark Harmstone
2022-11-30  7:00             ` Jan Beulich

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20221205015347.25781-3-mark@harmstone.com \
    --to=mark@harmstone.com \
    --cc=binutils@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).