summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Harmstone <mark@harmstone.com>2022-11-03 02:46:04 +0000
committerMark Harmstone <mark@harmstone.com>2022-11-10 04:50:43 +0000
commit5967ca921c88a09d7cec9d9864ae23799d88ffbb (patch)
tree18947d4491d038f82431a71968f4e73c9aa02221
parent76a95facf1260326469dc9952adc6ee034bac735 (diff)
downloadbinutils-gdb-5967ca921c88a09d7cec9d9864ae23799d88ffbb.tar.gz
ld: Add module information substream to PDB files
-rw-r--r--ld/pdb.c214
-rw-r--r--ld/pdb.h33
-rw-r--r--ld/testsuite/ld-pe/pdb.exp214
-rw-r--r--ld/testsuite/ld-pe/pdb2a.s5
-rw-r--r--ld/testsuite/ld-pe/pdb2b.s5
5 files changed, 427 insertions, 44 deletions
diff --git a/ld/pdb.c b/ld/pdb.c
index 8e151dad10f..910589b4d09 100644
--- a/ld/pdb.c
+++ b/ld/pdb.c
@@ -383,6 +383,196 @@ get_arch_number (bfd *abfd)
return IMAGE_FILE_MACHINE_I386;
}
+/* 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)
+{
+ uint8_t int_buf[sizeof (uint32_t)];
+
+ *sym_byte_size = sizeof (uint32_t);
+
+ /* Write the signature. */
+
+ bfd_putl32 (CV_SIGNATURE_C13, int_buf);
+
+ if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != sizeof (uint32_t))
+ return false;
+
+ /* Write the global refs size. */
+
+ bfd_putl32 (0, int_buf);
+
+ if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != sizeof (uint32_t))
+ return false;
+
+ return true;
+}
+
+/* Create the module info substream within the DBI. */
+static bool
+create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
+ uint32_t *size)
+{
+ uint8_t *ptr;
+
+ static const char linker_fn[] = "* Linker *";
+
+ *size = 0;
+
+ for (bfd *in = coff_data (abfd)->link_info->input_bfds; in;
+ in = in->link.next)
+ {
+ size_t len = sizeof (struct module_info);
+
+ if (!strcmp (bfd_get_filename (in), "dll stuff"))
+ {
+ len += sizeof (linker_fn); /* Object name. */
+ len++; /* Empty module name. */
+ }
+ else if (in->my_archive)
+ {
+ char *name = lrealpath (bfd_get_filename (in));
+
+ len += strlen (name) + 1; /* Object name. */
+
+ free (name);
+
+ name = lrealpath (bfd_get_filename (in->my_archive));
+
+ len += strlen (name) + 1; /* Archive name. */
+
+ free (name);
+ }
+ else
+ {
+ char *name = lrealpath (bfd_get_filename (in));
+ size_t name_len = strlen (name) + 1;
+
+ len += name_len; /* Object name. */
+ len += name_len; /* And again as the archive name. */
+
+ free (name);
+ }
+
+ if (len % 4)
+ len += 4 - (len % 4);
+
+ *size += len;
+ }
+
+ *data = xmalloc (*size);
+
+ ptr = *data;
+
+ 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;
+ uint8_t *start = ptr;
+
+ stream = add_stream (pdb, NULL, &stream_num);
+
+ if (!stream)
+ {
+ free (*data);
+ return false;
+ }
+
+ if (!populate_module_stream (stream, &sym_byte_size))
+ {
+ free (*data);
+ return false;
+ }
+
+ bfd_putl32 (0, &mod->unused1);
+
+ /* These are dummy values - MSVC copies the first section contribution
+ entry here, but doesn't seem to use it for anything. */
+ bfd_putl16 (0xffff, &mod->sc.section);
+ bfd_putl16 (0, &mod->sc.padding1);
+ bfd_putl32 (0, &mod->sc.offset);
+ bfd_putl32 (0xffffffff, &mod->sc.size);
+ bfd_putl32 (0, &mod->sc.characteristics);
+ bfd_putl16 (0xffff, &mod->sc.module_index);
+ bfd_putl16 (0, &mod->sc.padding2);
+ bfd_putl32 (0, &mod->sc.data_crc);
+ bfd_putl32 (0, &mod->sc.reloc_crc);
+
+ bfd_putl16 (0, &mod->flags);
+ 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_putl16 (0, &mod->source_file_count);
+ bfd_putl16 (0, &mod->padding);
+ bfd_putl32 (0, &mod->unused2);
+ bfd_putl32 (0, &mod->source_file_name_index);
+ bfd_putl32 (0, &mod->pdb_file_path_name_index);
+
+ ptr += sizeof (struct module_info);
+
+ if (!strcmp (bfd_get_filename (in), "dll stuff"))
+ {
+ /* Object name. */
+ memcpy (ptr, linker_fn, sizeof (linker_fn));
+ ptr += sizeof (linker_fn);
+
+ /* Empty module name. */
+ *ptr = 0;
+ ptr++;
+ }
+ else if (in->my_archive)
+ {
+ char *name = lrealpath (bfd_get_filename (in));
+ size_t name_len = strlen (name) + 1;
+
+ /* Object name. */
+ memcpy (ptr, name, name_len);
+ ptr += name_len;
+
+ free (name);
+
+ name = lrealpath (bfd_get_filename (in->my_archive));
+ name_len = strlen (name) + 1;
+
+ /* Archive name. */
+ memcpy (ptr, name, name_len);
+ ptr += name_len;
+
+ free (name);
+ }
+ else
+ {
+ char *name = lrealpath (bfd_get_filename (in));
+ size_t name_len = strlen (name) + 1;
+
+ /* Object name. */
+ memcpy (ptr, name, name_len);
+ ptr += name_len;
+
+ /* Object name again as archive name. */
+ memcpy (ptr, name, name_len);
+ ptr += name_len;
+
+ free (name);
+ }
+
+ /* Pad to next four-byte boundary. */
+
+ if ((ptr - start) % 4)
+ {
+ memset (ptr, 0, 4 - ((ptr - start) % 4));
+ ptr += 4 - ((ptr - start) % 4);
+ }
+ }
+
+ return true;
+}
+
/* Return the index of a given output section. */
static uint16_t
find_section_number (bfd *abfd, asection *sect)
@@ -404,13 +594,18 @@ find_section_number (bfd *abfd, asection *sect)
/* Stream 4 is the debug information (DBI) stream. */
static bool
-populate_dbi_stream (bfd *stream, bfd *abfd,
+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)
{
struct pdb_dbi_stream_header h;
struct optional_dbg_header opt;
+ void *mod_info;
+ uint32_t mod_info_size;
+
+ if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size))
+ return false;
bfd_putl32 (0xffffffff, &h.version_signature);
bfd_putl32 (DBI_STREAM_VERSION_70, &h.version_header);
@@ -421,7 +616,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd,
bfd_putl16 (0, &h.pdb_dll_version);
bfd_putl16 (sym_rec_stream_num, &h.sym_record_stream);
bfd_putl16 (0, &h.pdb_dll_rbld);
- bfd_putl32 (0, &h.mod_info_size);
+ bfd_putl32 (mod_info_size, &h.mod_info_size);
bfd_putl32 (0, &h.section_contribution_size);
bfd_putl32 (0, &h.section_map_size);
bfd_putl32 (0, &h.source_info_size);
@@ -434,7 +629,18 @@ populate_dbi_stream (bfd *stream, bfd *abfd,
bfd_putl32 (0, &h.padding);
if (bfd_bwrite (&h, sizeof (h), stream) != sizeof (h))
- return false;
+ {
+ free (mod_info);
+ return false;
+ }
+
+ if (bfd_bwrite (mod_info, mod_info_size, stream) != mod_info_size)
+ {
+ free (mod_info);
+ return false;
+ }
+
+ free (mod_info);
bfd_putl16 (0xffff, &opt.fpo_stream);
bfd_putl16 (0xffff, &opt.exception_stream);
@@ -888,7 +1094,7 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
goto end;
}
- if (!populate_dbi_stream (dbi_stream, abfd, section_header_stream_num,
+ if (!populate_dbi_stream (dbi_stream, abfd, pdb, section_header_stream_num,
sym_rec_stream_num, publics_stream_num))
{
einfo (_("%P: warning: cannot populate DBI stream "
diff --git a/ld/pdb.h b/ld/pdb.h
index 1a80101d288..a44618578b7 100644
--- a/ld/pdb.h
+++ b/ld/pdb.h
@@ -153,6 +153,39 @@ struct optional_dbg_header
uint16_t orig_section_header_stream;
};
+#define CV_SIGNATURE_C13 4
+
+/* SC in dbicommon.h */
+struct section_contribution
+{
+ uint16_t section;
+ uint16_t padding1;
+ uint32_t offset;
+ uint32_t size;
+ uint32_t characteristics;
+ uint16_t module_index;
+ uint16_t padding2;
+ uint32_t data_crc;
+ uint32_t reloc_crc;
+};
+
+/* MODI_60_Persist in dbi.h */
+struct module_info
+{
+ uint32_t unused1;
+ struct section_contribution sc;
+ uint16_t flags;
+ uint16_t module_sym_stream;
+ uint32_t sym_byte_size;
+ uint32_t c11_byte_size;
+ uint32_t c13_byte_size;
+ uint16_t source_file_count;
+ uint16_t padding;
+ uint32_t unused2;
+ uint32_t source_file_name_index;
+ uint32_t pdb_file_path_name_index;
+};
+
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 ee314c41f9b..ab22506d0f2 100644
--- a/ld/testsuite/ld-pe/pdb.exp
+++ b/ld/testsuite/ld-pe/pdb.exp
@@ -494,55 +494,189 @@ proc check_publics_stream { pdb } {
return 1
}
-if ![ld_assemble $as $srcdir/$subdir/pdb1.s tmpdir/pdb1.o] {
- unsupported "Build pdb1.o"
- return
-}
+proc test1 { } {
+ global as
+ global ld
+ global srcdir
+ global subdir
-if ![ld_link $ld "tmpdir/pdb1.exe" "--pdb=tmpdir/pdb1.pdb --gc-sections -e foo tmpdir/pdb1.o"] {
- fail "Could not create a PE image with a PDB file"
- return
-}
+ if ![ld_assemble $as $srcdir/$subdir/pdb1.s tmpdir/pdb1.o] {
+ unsupported "Build pdb1.o"
+ return
+ }
-if ![string equal [get_pdb_name "tmpdir/pdb1.exe"] "pdb1.pdb"] {
- fail "PDB filename not found in CodeView debug info"
- return
-}
+ if ![ld_link $ld "tmpdir/pdb1.exe" "--pdb=tmpdir/pdb1.pdb --gc-sections -e foo tmpdir/pdb1.o"] {
+ fail "Could not create a PE image with a PDB file"
+ return
+ }
-pass "PDB filename present in CodeView debug info"
+ if ![string equal [get_pdb_name "tmpdir/pdb1.exe"] "pdb1.pdb"] {
+ fail "PDB filename not found in CodeView debug info"
+ return
+ }
-if [check_pdb_info_stream tmpdir/pdb1.pdb [get_pdb_guid "tmpdir/pdb1.exe"]] {
- pass "Valid PDB info stream"
-} else {
- fail "Invalid PDB info stream"
-}
+ pass "PDB filename present in CodeView debug info"
-if [check_type_stream tmpdir/pdb1.pdb "0002"] {
- pass "Valid TPI stream"
-} else {
- fail "Invalid TPI stream"
-}
+ if [check_pdb_info_stream tmpdir/pdb1.pdb [get_pdb_guid "tmpdir/pdb1.exe"]] {
+ pass "Valid PDB info stream"
+ } else {
+ fail "Invalid PDB info stream"
+ }
-if [check_type_stream tmpdir/pdb1.pdb "0004"] {
- pass "Valid IPI stream"
-} else {
- fail "Invalid IPI stream"
-}
+ if [check_type_stream tmpdir/pdb1.pdb "0002"] {
+ pass "Valid TPI stream"
+ } else {
+ fail "Invalid TPI stream"
+ }
+
+ if [check_type_stream tmpdir/pdb1.pdb "0004"] {
+ pass "Valid IPI stream"
+ } else {
+ fail "Invalid IPI stream"
+ }
+
+ if [check_dbi_stream tmpdir/pdb1.pdb] {
+ pass "Valid DBI stream"
+ } else {
+ fail "Invalid DBI stream"
+ }
-if [check_dbi_stream tmpdir/pdb1.pdb] {
- pass "Valid DBI stream"
-} else {
- fail "Invalid DBI stream"
+ if [check_section_stream tmpdir/pdb1.exe tmpdir/pdb1.pdb] {
+ pass "Valid section stream"
+ } else {
+ fail "Invalid section stream"
+ }
+
+ if [check_publics_stream tmpdir/pdb1.pdb] {
+ pass "Valid publics stream"
+ } else {
+ fail "Invalid publics stream"
+ }
}
-if [check_section_stream tmpdir/pdb1.exe tmpdir/pdb1.pdb] {
- pass "Valid section stream"
-} else {
- fail "Invalid section stream"
+proc test_mod_info { mod_info } {
+ # check filenames in mod_info
+
+ 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 [string match "*pdb2a.o" $obj1] {
+ pass "Correct name for first object file"
+ } else {
+ fail "Incorrect name for first object file"
+ }
+
+ if [string equal $obj1 $ar1] {
+ pass "Correct archive name for first object file"
+ } else {
+ fail "Incorrect archive name for first object file"
+ }
+
+ if { [expr $off % 4] != 0 } {
+ set off [expr $off + 4 - ($off % 4)]
+ }
+
+ incr off 64
+
+ set obj2 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]]
+ incr off [expr [string length $obj2] + 1]
+
+ set ar2 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]]
+ incr off [expr [string length $ar2] + 1]
+
+ if [string match "*pdb2b.o" $obj2] {
+ pass "Correct name for second object file"
+ } else {
+ fail "Incorrect name for second object file"
+ }
+
+ if [string match "*pdb2b.a" $ar2] {
+ pass "Correct archive name for second object file"
+ } else {
+ fail "Incorrect archive name for second object file"
+ }
+
+ if { [expr $off % 4] != 0 } {
+ set off [expr $off + 4 - ($off % 4)]
+ }
+
+ incr off 64
+
+ set obj3 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]]
+ incr off [expr [string length $obj3] + 1]
+
+ set ar3 [string range $mod_info $off [expr [string first \000 $mod_info $off] - 1]]
+ incr off [expr [string length $ar3] + 1]
+
+ if [string equal $obj3 "* Linker *"] {
+ pass "Correct name for dummy object file"
+ } else {
+ fail "Incorrect name for dummy object file"
+ }
+
+ if [string equal $ar3 ""] {
+ pass "Correct archive name for dummy object file"
+ } else {
+ fail "Incorrect archive name for dummy object file"
+ }
}
-if [check_publics_stream tmpdir/pdb1.pdb] {
- pass "Valid publics stream"
-} else {
- fail "Invalid publics stream"
+proc test2 { } {
+ global as
+ global ar
+ global ld
+ global srcdir
+ global subdir
+
+ if ![ld_assemble $as $srcdir/$subdir/pdb2a.s tmpdir/pdb2a.o] {
+ unsupported "Build pdb2a.o"
+ return
+ }
+
+ if ![ld_assemble $as $srcdir/$subdir/pdb2b.s tmpdir/pdb2b.o] {
+ unsupported "Build pdb2b.o"
+ return
+ }
+
+ set exec_output [run_host_cmd "$ar" "cr tmpdir/pdb2b.a tmpdir/pdb2b.o"]
+
+ if ![string match "" $exec_output] {
+ unsupported "Create pdb2b.a"
+ return
+ }
+
+ if ![ld_link $ld "tmpdir/pdb2.exe" "--pdb=tmpdir/pdb2.pdb -e foo tmpdir/pdb2a.o tmpdir/pdb2b.a"] {
+ unsupported "Create PE image with PDB file"
+ return
+ }
+
+ set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb2.pdb 0003"]
+
+ if ![string match "" $exec_output] {
+ return 0
+ }
+
+ set fi [open tmpdir/0003]
+ fconfigure $fi -translation binary
+
+ seek $fi 24
+
+ set data [read $fi 4]
+ binary scan $data i mod_info_size
+
+ seek $fi 36 current
+
+ set mod_info [read $fi $mod_info_size]
+
+ close $fi
+
+ test_mod_info $mod_info
}
+
+test1
+test2
diff --git a/ld/testsuite/ld-pe/pdb2a.s b/ld/testsuite/ld-pe/pdb2a.s
new file mode 100644
index 00000000000..414edeb2eec
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb2a.s
@@ -0,0 +1,5 @@
+.text
+
+.global foo
+foo:
+ .secrel32 bar
diff --git a/ld/testsuite/ld-pe/pdb2b.s b/ld/testsuite/ld-pe/pdb2b.s
new file mode 100644
index 00000000000..64010237975
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb2b.s
@@ -0,0 +1,5 @@
+.text
+
+.global bar
+bar:
+ .long 0x12345678