[01/10] ld: Generate PDB string table

Message ID 20221209015240.6348-1-mark@harmstone.com
State Accepted
Headers
Series [01/10] ld: Generate PDB string table |

Checks

Context Check Description
snail/binutils-gdb-check success Github commit url

Commit Message

Mark Harmstone Dec. 9, 2022, 1:52 a.m. UTC
  Resubmitted patch set as per Nick's request.

---
 ld/pdb.c                          | 296 +++++++++++++++++++++++++++++-
 ld/pdb.h                          |  12 ++
 ld/testsuite/ld-pe/pdb-strings.d  |  10 +
 ld/testsuite/ld-pe/pdb-strings1.s |  19 ++
 ld/testsuite/ld-pe/pdb-strings2.s |  19 ++
 ld/testsuite/ld-pe/pdb.exp        | 122 ++++++++++++
 6 files changed, 472 insertions(+), 6 deletions(-)
 create mode 100644 ld/testsuite/ld-pe/pdb-strings.d
 create mode 100644 ld/testsuite/ld-pe/pdb-strings1.s
 create mode 100644 ld/testsuite/ld-pe/pdb-strings2.s
  

Comments

Alan Modra Dec. 22, 2022, 12:05 a.m. UTC | #1
On Fri, Dec 09, 2022 at 01:52:31AM +0000, Mark Harmstone wrote:
> Resubmitted patch set as per Nick's request.

The following is a show-stopper.  Any target that doesn't have COFF
support enabled fails to build.

alpha-dec-vms ********make
arm-nacl ********make
csky-elf ********make
bfin-elf ********make
bpf-none ********make
am33_2.0-linux-gnu ********make
arm-vxworks ********make
cris-elf ********make
armeb-linux-gnueabi ********make
arm-netbsdelf ********make
arm-elf ********make
cr16-elf ********make
d10v-elf ********make
arm-nto ********make
arm-linux-gnueabi ********make
crx-elf ********make
arc-elf ********make
arc-linux-uclibc ********make
crisv32-linux-gnu ********make
avr-elf ********make
bfin-linux-uclibc ********make
csky-linux-gnu ********make
cris-linux-gnu ********make
aarch64_be-linux-gnu_ilp32 ********make
alpha-linux-gnuecoff check
alpha-unknown-freebsd4.7 OK
alpha-netbsd OK
arm-pe check
arm-wince-pe check
aarch64-elf OK
alpha-linux-gnu check
aarch64-linux-gnu OK
i386-msdos ********make
d30v-elf ********make
h8300-linux-gnu ********make
ip2k-elf ********make
fr30-elf ********make
hppa64-linux-gnu ********make
ft32-elf ********make
hppa-linux-gnu ********make
h8300-elf ********make
iq2000-elf ********make
i386-bsd ********make
i686-vxworks ********make
frv-linux-gnu ********make
hppa64-hp-hpux11.23 ********make
epiphany-elf ********make
dlx-elf ********make
frv-elf ********make
ia64-vms check
ia64-hpux check
hppa-hp-hpux10 OK
ia64-netbsd check
ia64-freebsd5 check
ia64-elf check
i386-go32 check
i686-pe OK
i386-darwin check
i686-pc-beos OK
i686-nto OK
i386-lynxos OK
i686-pc-elf OK
i586-linux-gnu OK
ia64-linux-gnu check
loongarch32-elf ********make
microblaze-linux-gnu ********make
mcore-elf ********make
microblaze-elf ********make
m32r-linux-gnu ********make
m68hc12-elf ********make
mep-elf ********make
msp430-elf ********make
m32r-elf ********make
m68k-linux-gnu ********make
m68hc11-elf ********make
mn10200-elf ********make
m32c-elf ********make
mn10300-elf ********make
moxie-elf ********make
metag-linux-gnu ********make
m68k-elf ********make
lm32-elf ********make
lm32-linux-gnu ********make
mmix ********make
mcore-pe check
mips-vxworks check
loongarch64-linux-gnu OK
mips64el-openbsd check
mips64-openbsd check
mipstx39-elf check
mipsisa32r2el-elf check
mips-sgi-irix6 check
mipsel-linux-gnu OK
mipsisa32el-linux-gnu OK
mips-linux-gnu check
mips64-linux-gnuabi64 check
pdp11-dec-aout ********make
nios2-linux-gnu ********make
ns32k-pc532-mach ********make
s12z-elf ********make
pru-elf ********make
ns32k-openbsd ********make
rx-elf ********make
nds32le-linux-gnu ********make
mt-elf ********make
riscv32-elf ********make
or1k-elf ********make
pj-elf ********make
rl78-elf ********make
nds32be-elf ********make
or1k-linux-gnu ********make
s390-linux-gnu ********make
riscv64-linux-gnu ********make
rs6000-aix5.1 check
rs6000-aix4.3.3 check
powerpc-aix5.1 check
rs6000-aix7.2 check
powerpc-aix7.2 check
powerpc-wrs-vxworks OK
powerpc-nto OK
powerpc-freebsd OK
powerpc-eabisim OK
powerpcle-elf OK
powerpc-eabivle OK
powerpc64-freebsd OK
powerpc-linux-gnu OK
powerpc64-linux-gnu OK
powerpc64le-linux-gnu OK
x86_64-rdos ********make
v850-elf ********make
x86_64-cloudabi ********make
sparc-elf ********make
xgate-elf ********make
visium-elf ********make
tilepro-linux-gnu ********make
xstormy16-elf ********make
spu-elf ********make
s390x-linux-gnu ********make
sparc-vxworks ********make
vax-netbsdelf ********make
sparc-linux-gnu ********make
score-elf ********make
sparc64-linux-gnu ********make
tic6x-elf ********make
sparc-sun-solaris2 ********make
tilegx-linux-gnu ********make
wasm32 OK
sh-pe check
tic30-unknown-coff OK
tic4x-coff OK
sh-coff check
sh-vxworks check
tic54x-coff OK
sh-rtems check
sh-nto check
shle-unknown-netbsdelf check
x86_64-w64-mingw32 check
sh4-linux-gnu check
x86_64-pc-linux-gnux32 OK
x86_64-linux-gnu OK
z80-elf ********make
xtensa-lx106-elf ********make
z80-coff OK
z8k-coff OK

The errors are all like this:
/usr/local/bin/ld: pdb.o: in function `handle_debugs_section':
/home/alan/src/binutils-gdb/ld/pdb.c:1863: undefined reference to `_bfd_coff_read_internal_relocs'
collect2: error: ld returned 1 exit status

There are also (false positive I think) compiler warnings that should
be fixed.

In function ‘handle_debugs_section’,
    inlined from ‘populate_module_stream’ at /home/alan/src/binutils-gdb/ld/pdb.c:3638:13,
    inlined from ‘create_module_info_substream’ at /home/alan/src/binutils-gdb/ld/pdb.c:3792:12,
    inlined from ‘populate_dbi_stream’ at /home/alan/src/binutils-gdb/ld/pdb.c:4305:8,
    inlined from ‘create_pdb_file’ at /home/alan/src/binutils-gdb/ld/pdb.c:4997:8:
/home/alan/src/binutils-gdb/ld/pdb.c:2067:24: error: ‘bufptr’ may be used uninitialized [-Werror=maybe-uninitialized]
 2067 |                 bufptr += sizeof (uint32_t);
      |                 ~~~~~~~^~~~~~~~~~~~~~~~~~~~
/home/alan/src/binutils-gdb/ld/pdb.c: In function ‘create_pdb_file’:
/home/alan/src/binutils-gdb/ld/pdb.c:1838:18: note: ‘bufptr’ was declared here
 1838 |   uint8_t *buf, *bufptr, *symbuf, *symbufptr;
      |                  ^~~~~~
In function ‘copy_filechksms’,
    inlined from ‘handle_debugs_section’ at /home/alan/src/binutils-gdb/ld/pdb.c:2042:9,
    inlined from ‘populate_module_stream’ at /home/alan/src/binutils-gdb/ld/pdb.c:3638:13,
    inlined from ‘create_module_info_substream’ at /home/alan/src/binutils-gdb/ld/pdb.c:3792:12,
    inlined from ‘populate_dbi_stream’ at /home/alan/src/binutils-gdb/ld/pdb.c:4305:8,
    inlined from ‘create_pdb_file’ at /home/alan/src/binutils-gdb/ld/pdb.c:4997:8:
/home/alan/src/binutils-gdb/ld/pdb.c:717:13: error: ‘strptr’ may be used uninitialized [-Werror=maybe-uninitialized]
  717 |       strptr++;
      |       ~~~~~~^~
/home/alan/src/binutils-gdb/ld/pdb.c: In function ‘create_pdb_file’:
/home/alan/src/binutils-gdb/ld/pdb.c:613:19: note: ‘strptr’ was declared here
  613 |   struct string **strptr;
      |                   ^~~~~~
  
Alan Modra Dec. 22, 2022, 1:25 a.m. UTC | #2
On Thu, Dec 22, 2022 at 10:35:48AM +1030, Alan Modra wrote:
> On Fri, Dec 09, 2022 at 01:52:31AM +0000, Mark Harmstone wrote:
> > Resubmitted patch set as per Nick's request.
> 
> The following is a show-stopper.  Any target that doesn't have COFF
> support enabled fails to build.

This fixes the builds for me.  However, it doesn't match the pdb
support you have in bfd/config.bfd so I'm guessing you'll want to
change it to only compile ld/pdb.c for x86 and make the calls in
pe*.em to pdb functions conditional on x86.  Or make bfd/pdb.c
compile for all pe targets.

diff --git a/ld/Makefile.am b/ld/Makefile.am
index 65fef4e1690..005c38c9c16 100644
--- a/ld/Makefile.am
+++ b/ld/Makefile.am
@@ -466,6 +466,7 @@ ALL_64_EMULATIONS = $(ALL_64_EMULATION_SOURCES:.c=.@OBJEXT@)
 
 ALL_EMUL_EXTRA_OFILES = \
 	deffilep.@OBJEXT@ \
+	pdb.@OBJEXT@ \
 	pe-dll.@OBJEXT@ \
 	ldelf.@OBJEXT@ \
 	ldelfgen.@OBJEXT@
@@ -496,7 +497,7 @@ OFILES = ldgram.@OBJEXT@ ldlex-wrapper.@OBJEXT@ lexsup.@OBJEXT@ ldlang.@OBJEXT@
 	mri.@OBJEXT@ ldctor.@OBJEXT@ ldmain.@OBJEXT@ plugin.@OBJEXT@ \
 	ldwrite.@OBJEXT@ ldexp.@OBJEXT@  ldemul.@OBJEXT@ ldver.@OBJEXT@ ldmisc.@OBJEXT@ \
 	ldfile.@OBJEXT@ ldcref.@OBJEXT@ ${EMULATION_OFILES} ${EMUL_EXTRA_OFILES} \
-	ldbuildid.@OBJEXT@ pdb.@OBJEXT@
+	ldbuildid.@OBJEXT@
 
 STAGESTUFF = *.@OBJEXT@ ldscripts/* e*.c
 
@@ -960,7 +961,7 @@ EXTRA_ld_new_SOURCES += pep-dll.c pe-dll.c ldelf.c ldelfgen.c
 
 ld_new_SOURCES = ldgram.y ldlex-wrapper.c lexsup.c ldlang.c mri.c ldctor.c ldmain.c \
 	ldwrite.c ldexp.c ldemul.c ldver.c ldmisc.c ldfile.c ldcref.c plugin.c \
-	ldbuildid.c pdb.c
+	ldbuildid.c
 ld_new_DEPENDENCIES = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) \
 		      $(BFDLIB) $(LIBCTF) $(LIBIBERTY) $(LIBINTL_DEP) $(JANSSON_LIBS)
 ld_new_LDADD = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) $(BFDLIB) $(LIBCTF) \
diff --git a/ld/Makefile.in b/ld/Makefile.in
index ff4c916c27b..f3d23c996d1 100644
--- a/ld/Makefile.in
+++ b/ld/Makefile.in
@@ -212,7 +212,7 @@ am_ld_new_OBJECTS = ldgram.$(OBJEXT) ldlex-wrapper.$(OBJEXT) \
 	ldctor.$(OBJEXT) ldmain.$(OBJEXT) ldwrite.$(OBJEXT) \
 	ldexp.$(OBJEXT) ldemul.$(OBJEXT) ldver.$(OBJEXT) \
 	ldmisc.$(OBJEXT) ldfile.$(OBJEXT) ldcref.$(OBJEXT) \
-	plugin.$(OBJEXT) ldbuildid.$(OBJEXT) pdb.$(OBJEXT)
+	plugin.$(OBJEXT) ldbuildid.$(OBJEXT)
 ld_new_OBJECTS = $(am_ld_new_OBJECTS)
 am__DEPENDENCIES_1 =
 @ENABLE_LIBCTF_TRUE@am__DEPENDENCIES_2 = ../libctf/libctf.la
@@ -965,6 +965,7 @@ ALL_64_EMULATION_SOURCES = \
 ALL_64_EMULATIONS = $(ALL_64_EMULATION_SOURCES:.c=.@OBJEXT@)
 ALL_EMUL_EXTRA_OFILES = \
 	deffilep.@OBJEXT@ \
+	pdb.@OBJEXT@ \
 	pe-dll.@OBJEXT@ \
 	ldelf.@OBJEXT@ \
 	ldelfgen.@OBJEXT@
@@ -994,7 +995,7 @@ OFILES = ldgram.@OBJEXT@ ldlex-wrapper.@OBJEXT@ lexsup.@OBJEXT@ ldlang.@OBJEXT@
 	mri.@OBJEXT@ ldctor.@OBJEXT@ ldmain.@OBJEXT@ plugin.@OBJEXT@ \
 	ldwrite.@OBJEXT@ ldexp.@OBJEXT@  ldemul.@OBJEXT@ ldver.@OBJEXT@ ldmisc.@OBJEXT@ \
 	ldfile.@OBJEXT@ ldcref.@OBJEXT@ ${EMULATION_OFILES} ${EMUL_EXTRA_OFILES} \
-	ldbuildid.@OBJEXT@ pdb.@OBJEXT@
+	ldbuildid.@OBJEXT@
 
 STAGESTUFF = *.@OBJEXT@ ldscripts/* e*.c
 SRC_POTFILES = $(CFILES) $(HFILES)
@@ -1013,7 +1014,7 @@ EXTRA_ld_new_SOURCES = deffilep.y ldlex.l pep-dll.c pe-dll.c ldelf.c \
 	$(ALL_64_EMULATION_SOURCES)
 ld_new_SOURCES = ldgram.y ldlex-wrapper.c lexsup.c ldlang.c mri.c ldctor.c ldmain.c \
 	ldwrite.c ldexp.c ldemul.c ldver.c ldmisc.c ldfile.c ldcref.c plugin.c \
-	ldbuildid.c pdb.c
+	ldbuildid.c
 
 ld_new_DEPENDENCIES = $(EMULATION_OFILES) $(EMUL_EXTRA_OFILES) \
 		      $(BFDLIB) $(LIBCTF) $(LIBIBERTY) $(LIBINTL_DEP) $(JANSSON_LIBS)
@@ -1579,7 +1580,6 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libldtestplug4_la-testplug4.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libldtestplug_la-testplug.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mri.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pdb.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pe-dll.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pep-dll.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin.Po@am__quote@
diff --git a/ld/configure.tgt b/ld/configure.tgt
index 741b246f67e..2d7c14b6308 100644
--- a/ld/configure.tgt
+++ b/ld/configure.tgt
@@ -120,7 +120,7 @@ aarch64-*-haiku*)	targ_emul=aarch64haiku
 			;;
 aarch64-*-pe*)
 			targ_emul=aarch64pe
-			targ_extra_ofiles="deffilep.o pep-dll-aarch64.o"
+			targ_extra_ofiles="deffilep.o pdb.o pep-dll-aarch64.o"
 			;;
 alpha*-*-freebsd* | alpha*-*-kfreebsd*-gnu)
 			targ_emul=elf64alpha_fbsd
@@ -163,15 +163,15 @@ arc*-*-linux*)		case "${with_cpu}" in
 			targ_extra_emuls="${targ_extra_emuls} arcelf arcv2elf arcv2elfx"
 			;;
 arm*-*-cegcc*)		targ_emul=arm_wince_pe
-			targ_extra_ofiles="deffilep.o pe-dll.o"
+			targ_extra_ofiles="deffilep.o pdb.o pe-dll.o"
 			LIB_PATH='${tooldir}/lib/w32api'
 			;;
 arm-wince-pe | arm-*-wince | arm*-*-mingw32ce*)
 			targ_emul=arm_wince_pe
-			targ_extra_ofiles="deffilep.o pe-dll.o"
+			targ_extra_ofiles="deffilep.o pdb.o pe-dll.o"
 			;;
 arm-*-pe)		targ_emul=armpe
-			targ_extra_ofiles="deffilep.o pe-dll.o"
+			targ_extra_ofiles="deffilep.o pdb.o pe-dll.o"
 			;;
 arm*b-*-freebsd*)	targ_emul=armelfb_fbsd
 			targ_extra_emuls="armelf_fbsd armelf"
@@ -389,7 +389,7 @@ i[3-7]86-*-solaris*)	targ_emul=elf_i386_ldso
 			targ_extra_libpath=$targ_extra_emuls
 			;;
 i[3-7]86-*-netbsdpe*)	targ_emul=i386pe
-			targ_extra_ofiles="deffilep.o pe-dll.o"
+			targ_extra_ofiles="deffilep.o pdb.o pe-dll.o"
 			;;
 i[3-7]86-*-netbsd* | \
 i[3-7]86-*-netbsd*-gnu* | \
@@ -423,17 +423,17 @@ i[3-7]86-*-moss*)	targ_emul=i386moss
 			targ_extra_emuls=i386msdos
 			;;
 i[3-7]86-*-winnt*)	targ_emul=i386pe ;
-			targ_extra_ofiles="deffilep.o pe-dll.o"
+			targ_extra_ofiles="deffilep.o pdb.o pe-dll.o"
 			;;
 i[3-7]86-*-pe)		targ_emul=i386pe ;
-			targ_extra_ofiles="deffilep.o pe-dll.o"
+			targ_extra_ofiles="deffilep.o pdb.o pe-dll.o"
 			;;
 i[3-7]86-*-cygwin*)	targ_emul=i386pe ;
-			targ_extra_ofiles="deffilep.o pe-dll.o" ;
+			targ_extra_ofiles="deffilep.o pdb.o pe-dll.o" ;
 			test "$targ" != "$host" && LIB_PATH='${tooldir}/lib/w32api'
 			;;
 i[3-7]86-*-mingw32*)	targ_emul=i386pe ;
-			targ_extra_ofiles="deffilep.o pe-dll.o"
+			targ_extra_ofiles="deffilep.o pdb.o pe-dll.o"
 			;;
 i[3-7]86-*-interix*)	targ_emul=i386pe_posix;
 			targ_extra_ofiles="deffilep.o pe-dll.o"
@@ -503,7 +503,7 @@ m68*-*-haiku*)		targ_emul=m68kelf
 m68*-*-*)		targ_emul=m68kelf
 			;;
 mcore-*-pe)		targ_emul=mcorepe ;
-			targ_extra_ofiles="deffilep.o pe-dll.o"
+			targ_extra_ofiles="deffilep.o pdb.o pe-dll.o"
 			;;
 mcore-*-elf)		targ_emul=elf32mcore
 			;;
@@ -900,7 +900,7 @@ sh-*-nto*)		targ_emul=shelf_nto
 			targ_extra_emuls=shlelf_nto
 			;;
 sh-*-pe)		targ_emul=shpe ;
-			targ_extra_ofiles="deffilep.o pe-dll.o"
+			targ_extra_ofiles="deffilep.o pdb.o pe-dll.o"
 			;;
 sh-*-*)			targ_emul=sh;
 			targ_extra_emuls=shl
@@ -1047,16 +1047,16 @@ x86_64-*-freebsd* | x86_64-*-kfreebsd*-gnu)
 			;;
 x86_64-*-pe | x86_64-*-pep) targ_emul=i386pep ;
 			targ_extra_emuls=i386pe ;
-			targ_extra_ofiles="deffilep.o pep-dll-x86_64.o pe-dll.o"
+			targ_extra_ofiles="deffilep.o pdb.o pep-dll-x86_64.o pe-dll.o"
 			;;
 x86_64-*-cygwin)	targ_emul=i386pep ;
 			targ_extra_emuls=i386pe
-			targ_extra_ofiles="deffilep.o pep-dll.o pe-dll.o"
+			targ_extra_ofiles="deffilep.o pdb.o pep-dll.o pe-dll.o"
 			test "$targ" != "$host" && LIB_PATH='${tooldir}/lib/w32api'
 			;;
 x86_64-*-mingw*)	targ_emul=i386pep ;
 			targ_extra_emuls=i386pe
-			targ_extra_ofiles="deffilep.o pep-dll.o pe-dll.o"
+			targ_extra_ofiles="deffilep.o pdb.o pep-dll.o pe-dll.o"
 			;;
 xgate-*-*)		targ_emul=xgateelf
 			targ_extra_ofiles=ldelfgen.o
diff --git a/ld/pdb.c b/ld/pdb.c
index 0346ccb388c..2df75e8f842 100644
--- a/ld/pdb.c
+++ b/ld/pdb.c
@@ -663,6 +663,7 @@ copy_filechksms (uint8_t *data, uint32_t size, char *string_table,
   /* 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;
@@ -2004,25 +2005,15 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
 
   /* copy data */
 
+  buf = NULL;
   if (c13_size != 0)
-    {
-      buf = xmalloc (c13_size);
-      bufptr = buf;
-    }
-  else
-    {
-      buf = NULL;
-    }
+    buf = xmalloc (c13_size);
+  bufptr = buf;
 
+  symbuf = NULL;
   if (sym_size != 0)
-    {
-      symbuf = xmalloc (sym_size);
-      symbufptr = symbuf;
-    }
-  else
-    {
-      symbuf = NULL;
-    }
+    symbuf = xmalloc (sym_size);
+  symbufptr = symbuf;
 
   off = sizeof (uint32_t);
  
Alan Modra Dec. 22, 2022, 10:22 a.m. UTC | #3
There is also this:

ld/pdb.h:571
/* lfUdtModSrcLine in cvinfo.h */
struct lf_udt_mod_src_line
{
  uint16_t size;
  uint16_t kind;
  uint32_t type;
  uint32_t source_file_string;
  uint32_t line_no;
  uint16_t module_no;
} ATTRIBUTE_PACKED;

ld/pdb.c:2286
  memcpy (&umsl->line_no, &usl->line_no, sizeof (uint16_t));

which results in uninitialised data.
  
Alan Modra Dec. 23, 2022, 10:54 a.m. UTC | #4
Mark, I've now pushed your patches, with fixes for the compiler
warnings plus the wrong-size memcpy.
  

Patch

diff --git a/ld/pdb.c b/ld/pdb.c
index 6f69574289d..98663a1f9ae 100644
--- a/ld/pdb.c
+++ b/ld/pdb.c
@@ -41,6 +41,23 @@  struct public
   uint32_t address;
 };
 
+struct string
+{
+  struct string *next;
+  uint32_t hash;
+  uint32_t offset;
+  size_t len;
+  char s[];
+};
+
+struct string_table
+{
+  struct string *strings_head;
+  struct string *strings_tail;
+  uint32_t strings_len;
+  htab_t hashmap;
+};
+
 /* 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)
@@ -383,15 +400,170 @@  get_arch_number (bfd *abfd)
   return IMAGE_FILE_MACHINE_I386;
 }
 
+/* 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)
+{
+  uint32_t hash = calc_hash (str, len);
+  void **slot;
+
+  slot = htab_find_slot_with_hash (strings->hashmap, str, hash, INSERT);
+
+  if (!*slot)
+    {
+      struct string *s;
+
+      *slot = xmalloc (offsetof (struct string, s) + len);
+
+      s = (struct string *) *slot;
+
+      s->next = NULL;
+      s->hash = hash;
+      s->offset = strings->strings_len;
+      s->len = len;
+      memcpy (s->s, str, len);
+
+      if (strings->strings_tail)
+	strings->strings_tail->next = s;
+      else
+	strings->strings_head = s;
+
+      strings->strings_tail = s;
+
+      strings->strings_len += len + 1;
+    }
+}
+
+/* Return the hash of an entry in the string table.  */
+static hashval_t
+hash_string_table_entry (const void *p)
+{
+  const struct string *s = (const struct string *) p;
+
+  return s->hash;
+}
+
+/* Compare an entry in the string table with a string.  */
+static int
+eq_string_table_entry (const void *a, const void *b)
+{
+  const struct string *s1 = (const struct string *) a;
+  const char *s2 = (const char *) b;
+  size_t s2_len = strlen (s2);
+
+  if (s2_len != s1->len)
+    return 0;
+
+  return memcmp (s1->s, s2, s2_len) == 0;
+}
+
+/* Parse the string table within the .debug$S section.  */
+static void
+parse_string_table (bfd_byte *data, size_t size,
+		    struct string_table *strings)
+{
+  while (true)
+    {
+      size_t len = strnlen ((char *) data, size);
+
+      add_string ((char *) data, len, strings);
+
+      data += len + 1;
+
+      if (size <= len + 1)
+	break;
+
+      size -= len + 1;
+    }
+}
+
+/* Parse the .debug$S section within an object file.  */
+static bool
+handle_debugs_section (asection *s, bfd *mod, struct string_table *strings)
+{
+  bfd_byte *data = NULL;
+  size_t off;
+
+  if (!bfd_get_full_section_contents (mod, s, &data))
+    return false;
+
+  if (!data)
+    return false;
+
+  if (bfd_getl32 (data) != CV_SIGNATURE_C13)
+    {
+      free (data);
+      return true;
+    }
+
+  off = sizeof (uint32_t);
+
+  while (off + sizeof (uint32_t) <= s->size)
+    {
+      uint32_t type, size;
+
+      type = bfd_getl32 (data + off);
+
+      off += sizeof (uint32_t);
+
+      if (off + sizeof (uint32_t) > s->size)
+	{
+	  free (data);
+	  bfd_set_error (bfd_error_bad_value);
+	  return false;
+	}
+
+      size = bfd_getl32 (data + off);
+
+      off += sizeof (uint32_t);
+
+      if (off + size > s->size)
+	{
+	  free (data);
+	  bfd_set_error (bfd_error_bad_value);
+	  return false;
+	}
+
+      switch (type)
+	{
+	case DEBUG_S_STRINGTABLE:
+	  parse_string_table (data + off, size, strings);
+
+	  break;
+	}
+
+      off += size;
+
+      if (off % sizeof (uint32_t))
+	off += sizeof (uint32_t) - (off % sizeof (uint32_t));
+    }
+
+  free (data);
+
+  return true;
+}
+
 /* Populate the module stream, which consists of the transformed .debug$S
    data for each object file.  */
 static bool
-populate_module_stream (bfd *stream, uint32_t *sym_byte_size)
+populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
+			struct string_table *strings)
 {
   uint8_t int_buf[sizeof (uint32_t)];
 
   *sym_byte_size = sizeof (uint32_t);
 
+  /* 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))
+	{
+	  if (!handle_debugs_section (s, mod, strings))
+	      return false;
+	}
+    }
+
   /* Write the signature.  */
 
   bfd_putl32 (CV_SIGNATURE_C13, int_buf);
@@ -412,7 +584,7 @@  populate_module_stream (bfd *stream, 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)
+			      uint32_t *size, struct string_table *strings)
 {
   uint8_t *ptr;
 
@@ -482,7 +654,8 @@  create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
 	  return false;
 	}
 
-      if (!populate_module_stream (stream, &sym_byte_size))
+      if (!populate_module_stream (stream, in, &sym_byte_size,
+				   strings))
 	{
 	  free (*data);
 	  return false;
@@ -687,14 +860,16 @@  static bool
 populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
 		     uint16_t section_header_stream_num,
 		     uint16_t sym_rec_stream_num,
-		     uint16_t publics_stream_num)
+		     uint16_t publics_stream_num,
+		     struct string_table *strings)
 {
   struct pdb_dbi_stream_header h;
   struct optional_dbg_header opt;
   void *mod_info, *sc;
   uint32_t mod_info_size, sc_size;
 
-  if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size))
+  if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size,
+				     strings))
     return false;
 
   if (!create_section_contrib_substream (abfd, &sc, &sc_size))
@@ -1107,6 +1282,95 @@  create_section_header_stream (bfd *pdb, bfd *abfd, uint16_t *num)
   return true;
 }
 
+/* Populate the "/names" named stream, which contains the string table.  */
+static bool
+populate_names_stream (bfd *stream, struct string_table *strings)
+{
+  char int_buf[sizeof (uint32_t)];
+  struct string_table_header h;
+  uint32_t num_strings = 0, num_buckets;
+  struct string **buckets;
+
+  bfd_putl32 (STRING_TABLE_SIGNATURE, &h.signature);
+  bfd_putl32 (STRING_TABLE_VERSION, &h.version);
+
+  if (bfd_bwrite (&h, sizeof (h), stream) != sizeof (h))
+    return false;
+
+  bfd_putl32 (strings->strings_len, int_buf);
+
+  if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != sizeof (uint32_t))
+    return false;
+
+  int_buf[0] = 0;
+
+  if (bfd_bwrite (int_buf, 1, stream) != 1)
+    return false;
+
+  for (struct string *s = strings->strings_head; s; s = s->next)
+    {
+      if (bfd_bwrite (s->s, s->len, stream) != s->len)
+	return false;
+
+      if (bfd_bwrite (int_buf, 1, stream) != 1)
+	return false;
+
+      num_strings++;
+    }
+
+  num_buckets = num_strings * 2;
+
+  buckets = xmalloc (sizeof (struct string *) * num_buckets);
+  memset (buckets, 0, sizeof (struct string *) * num_buckets);
+
+  for (struct string *s = strings->strings_head; s; s = s->next)
+    {
+      uint32_t bucket_num = s->hash % num_buckets;
+
+      while (buckets[bucket_num])
+	{
+	  bucket_num++;
+
+	  if (bucket_num == num_buckets)
+	    bucket_num = 0;
+	}
+
+      buckets[bucket_num] = s;
+    }
+
+  bfd_putl32 (num_buckets, int_buf);
+
+  if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != sizeof (uint32_t))
+    {
+      free (buckets);
+      return false;
+    }
+
+  for (unsigned int i = 0; i < num_buckets; i++)
+    {
+      if (buckets[i])
+	bfd_putl32 (buckets[i]->offset, int_buf);
+      else
+	bfd_putl32 (0, int_buf);
+
+      if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) !=
+	  sizeof (uint32_t))
+	{
+	  free (buckets);
+	  return false;
+	}
+    }
+
+  free (buckets);
+
+  bfd_putl32 (num_strings, int_buf);
+
+  if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != sizeof (uint32_t))
+    return false;
+
+  return true;
+}
+
 /* Create a PDB debugging file for the PE image file abfd with the build ID
    guid, stored at pdb_name.  */
 bool
@@ -1117,6 +1381,7 @@  create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
   bfd *info_stream, *dbi_stream, *names_stream, *sym_rec_stream,
     *publics_stream;
   uint16_t section_header_stream_num, sym_rec_stream_num, publics_stream_num;
+  struct string_table strings;
 
   pdb = bfd_openw (pdb_name, "pdb");
   if (!pdb)
@@ -1125,6 +1390,13 @@  create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
       return false;
     }
 
+  strings.strings_head = NULL;
+  strings.strings_tail = NULL;
+  strings.strings_len = 1;
+  strings.hashmap = htab_create_alloc (0, hash_string_table_entry,
+				       eq_string_table_entry, free,
+				       xcalloc, free);
+
   bfd_set_format (pdb, bfd_archive);
 
   if (!create_old_directory_stream (pdb))
@@ -1201,13 +1473,23 @@  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))
+			    sym_rec_stream_num, publics_stream_num,
+			    &strings))
     {
       einfo (_("%P: warning: cannot populate DBI stream "
 	       "in PDB file: %E\n"));
       goto end;
     }
 
+  add_string ("", 0, &strings);
+
+  if (!populate_names_stream (names_stream, &strings))
+    {
+      einfo (_("%P: warning: cannot populate names stream "
+	       "in PDB file: %E\n"));
+      goto end;
+    }
+
   if (!populate_publics_stream (publics_stream, abfd, sym_rec_stream))
     {
       einfo (_("%P: warning: cannot populate publics stream "
@@ -1227,5 +1509,7 @@  create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
 end:
   bfd_close (pdb);
 
+  htab_delete (strings.hashmap);
+
   return ret;
 }
diff --git a/ld/pdb.h b/ld/pdb.h
index e22dea18eca..611f71041c0 100644
--- a/ld/pdb.h
+++ b/ld/pdb.h
@@ -155,6 +155,18 @@  struct optional_dbg_header
 
 #define CV_SIGNATURE_C13		4
 
+#define DEBUG_S_STRINGTABLE		0xf3
+
+#define STRING_TABLE_SIGNATURE		0xeffeeffe
+#define STRING_TABLE_VERSION		1
+
+/* VHdr in nmt.h */
+struct string_table_header
+{
+  uint32_t signature;
+  uint32_t version;
+};
+
 #define SECTION_CONTRIB_VERSION_60	0xf12eba2d
 
 /* SC in dbicommon.h */
diff --git a/ld/testsuite/ld-pe/pdb-strings.d b/ld/testsuite/ld-pe/pdb-strings.d
new file mode 100644
index 00000000000..8be853efb72
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-strings.d
@@ -0,0 +1,10 @@ 
+
+*:     file format binary
+
+Contents of section .data:
+ 0000 feeffeef 01000000 17000000 0000666f  ..............fo
+ 0010 6f006261 72006261 7a007175 78007175  o.bar.baz.qux.qu
+ 0020 7578000c 00000001 0000000a 00000000  ux..............
+ 0030 00000000 00000000 00000012 00000000  ................
+ 0040 00000000 00000002 00000006 00000000  ................
+ 0050 0000000e 00000006 000000             ...........     
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-strings1.s b/ld/testsuite/ld-pe/pdb-strings1.s
new file mode 100644
index 00000000000..09eedd93fb3
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-strings1.s
@@ -0,0 +1,19 @@ 
+.equ CV_SIGNATURE_C13, 4
+.equ DEBUG_S_STRINGTABLE, 0xf3
+
+.section ".debug$S", "rn"
+.long CV_SIGNATURE_C13
+.long DEBUG_S_STRINGTABLE
+.long .strings_end - .strings_start
+
+.strings_start:
+
+.asciz ""
+.asciz "foo"
+.asciz "bar"
+.asciz "baz"
+.asciz "qux"
+
+.strings_end:
+
+.balign 4
diff --git a/ld/testsuite/ld-pe/pdb-strings2.s b/ld/testsuite/ld-pe/pdb-strings2.s
new file mode 100644
index 00000000000..33d9215e4c8
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-strings2.s
@@ -0,0 +1,19 @@ 
+.equ CV_SIGNATURE_C13, 4
+.equ DEBUG_S_STRINGTABLE, 0xf3
+
+.section ".debug$S", "rn"
+.long CV_SIGNATURE_C13
+.long DEBUG_S_STRINGTABLE
+.long .strings_end - .strings_start
+
+.strings_start:
+
+.asciz ""
+.asciz "bar"
+.asciz "baz"
+.asciz "qux"
+.asciz "quux"
+
+.strings_end:
+
+.balign 4
diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp
index 0be65e22fb6..09e9b4a8809 100644
--- a/ld/testsuite/ld-pe/pdb.exp
+++ b/ld/testsuite/ld-pe/pdb.exp
@@ -703,5 +703,127 @@  proc test2 { } {
     test_section_contrib $section_contrib
 }
 
+proc find_named_stream { pdb name } {
+    global ar
+
+    set exec_output [run_host_cmd "$ar" "x --output tmpdir $pdb 0001"]
+
+    if ![string match "" $exec_output] {
+	return 0
+    }
+
+    set fi [open tmpdir/0001]
+    fconfigure $fi -translation binary
+
+    seek $fi 0x1c
+
+    set data [read $fi 4]
+    binary scan $data i string_len
+
+    set strings [read $fi $string_len]
+
+    set string_off 0
+
+    while {[string first \000 $strings $string_off] != -1 } {
+	set str [string range $strings $string_off [expr [string first \000 $strings $string_off] - 1]]
+
+	if { $str eq $name } {
+	    break
+	}
+
+	incr string_off [expr [string length $str] + 1]
+    }
+
+    if { [string length $strings] == $string_off } { # string not found
+	close $fi
+	return 0
+    }
+
+    set data [read $fi 4]
+    binary scan $data i num_entries
+
+    seek $fi 4 current
+
+    set data [read $fi 4]
+    binary scan $data i present_bitmap_len
+
+    seek $fi [expr $present_bitmap_len * 4] current
+
+    set data [read $fi 4]
+    binary scan $data i deleted_bitmap_len
+
+    seek $fi [expr $deleted_bitmap_len * 4] current
+
+    for {set i 0} {$i < $num_entries} {incr i} {
+	set data [read $fi 4]
+	binary scan $data i offset
+
+	if { $offset == $string_off } {
+	    set data [read $fi 4]
+	    binary scan $data i value
+	    close $fi
+
+	    return $value
+	}
+
+	seek $fi 4 current
+    }
+
+    close $fi
+
+    return 0
+}
+
+proc test3 { } {
+    global as
+    global ar
+    global ld
+    global objdump
+    global srcdir
+    global subdir
+
+    if ![ld_assemble $as $srcdir/$subdir/pdb-strings1.s tmpdir/pdb-strings1.o] {
+	unsupported "Build pdb-strings1.o"
+	return
+    }
+
+    if ![ld_assemble $as $srcdir/$subdir/pdb-strings2.s tmpdir/pdb-strings2.o] {
+	unsupported "Build pdb-strings2.o"
+	return
+    }
+
+    if ![ld_link $ld "tmpdir/pdb-strings.exe" "--pdb=tmpdir/pdb-strings.pdb tmpdir/pdb-strings1.o tmpdir/pdb-strings2.o"] {
+	unsupported "Create PE image with PDB file"
+	return
+    }
+
+    set index [find_named_stream "tmpdir/pdb-strings.pdb" "/names"]
+
+    if { $index == 0 } {
+	fail "Could not find /names stream"
+	return
+    } else {
+	pass "Found /names stream"
+    }
+
+    set index_str [format "%04x" $index]
+
+    set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-strings.pdb $index_str"]
+
+    if ![string match "" $exec_output] {
+	return 0
+    }
+
+    set exp [file_contents "$srcdir/$subdir/pdb-strings.d"]
+    set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"]
+
+    if ![string match $exp $got] {
+	fail "Strings table was not as expected"
+    } else {
+	pass "Strings table was as expected"
+    }
+}
+
 test1
 test2
+test3