* [PATCH 01/10] ld: Generate PDB string table
@ 2022-12-09 1:52 Mark Harmstone
2022-12-09 1:52 ` [PATCH 02/10] ld: Write DEBUG_S_FILECHKSMS entries in PDBs Mark Harmstone
` (9 more replies)
0 siblings, 10 replies; 14+ messages in thread
From: Mark Harmstone @ 2022-12-09 1:52 UTC (permalink / raw)
To: binutils, nickc; +Cc: Mark Harmstone
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
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
--
2.37.4
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 02/10] ld: Write DEBUG_S_FILECHKSMS entries in PDBs
2022-12-09 1:52 [PATCH 01/10] ld: Generate PDB string table Mark Harmstone
@ 2022-12-09 1:52 ` Mark Harmstone
2022-12-09 1:52 ` [PATCH 03/10] ld: Fix segfault in populate_publics_stream Mark Harmstone
` (8 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Mark Harmstone @ 2022-12-09 1:52 UTC (permalink / raw)
To: binutils, nickc; +Cc: Mark Harmstone
---
ld/pdb.c | 430 +++++++++++++++++++++++++-
ld/pdb.h | 9 +
ld/testsuite/ld-pe/pdb.exp | 154 +++++++++
ld/testsuite/ld-pe/pdb3-c13-info1.d | 8 +
ld/testsuite/ld-pe/pdb3-c13-info2.d | 8 +
ld/testsuite/ld-pe/pdb3-source-info.d | 7 +
ld/testsuite/ld-pe/pdb3a.s | 52 ++++
ld/testsuite/ld-pe/pdb3b.s | 52 ++++
8 files changed, 708 insertions(+), 12 deletions(-)
create mode 100644 ld/testsuite/ld-pe/pdb3-c13-info1.d
create mode 100644 ld/testsuite/ld-pe/pdb3-c13-info2.d
create mode 100644 ld/testsuite/ld-pe/pdb3-source-info.d
create mode 100644 ld/testsuite/ld-pe/pdb3a.s
create mode 100644 ld/testsuite/ld-pe/pdb3b.s
diff --git a/ld/pdb.c b/ld/pdb.c
index 98663a1f9ae..b37a5a0ebfd 100644
--- 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,134 @@ 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. */
+
+ 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 +561,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 +621,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 +645,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 +675,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 +743,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 +767,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 +783,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 +798,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 +827,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 +876,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 +946,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 +1008,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 +1124,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 +1242,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 +1285,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 +1296,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 +1304,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 +1314,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);
diff --git a/ld/pdb.h b/ld/pdb.h
index 611f71041c0..e8f673c24a0 100644
--- 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
diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp
index 09e9b4a8809..9dab41110ac 100644
--- a/ld/testsuite/ld-pe/pdb.exp
+++ b/ld/testsuite/ld-pe/pdb.exp
@@ -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
index 00000000000..f92062ba4e5
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb3-c13-info1.d
@@ -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
index 00000000000..1c33ce1e798
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb3-c13-info2.d
@@ -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
index 00000000000..5b7d58cfa0c
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb3-source-info.d
@@ -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
index 00000000000..71795b53a66
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb3a.s
@@ -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
index 00000000000..fffb1150c88
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb3b.s
@@ -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
--
2.37.4
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 03/10] ld: Fix segfault in populate_publics_stream
2022-12-09 1:52 [PATCH 01/10] ld: Generate PDB string table Mark Harmstone
2022-12-09 1:52 ` [PATCH 02/10] ld: Write DEBUG_S_FILECHKSMS entries in PDBs Mark Harmstone
@ 2022-12-09 1:52 ` Mark Harmstone
2022-12-09 1:52 ` [PATCH 04/10] ld: Write DEBUG_S_LINES entries in PDB file Mark Harmstone
` (7 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Mark Harmstone @ 2022-12-09 1:52 UTC (permalink / raw)
To: binutils, nickc; +Cc: Mark Harmstone
---
ld/pdb.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/ld/pdb.c b/ld/pdb.c
index b37a5a0ebfd..3edeab25332 100644
--- a/ld/pdb.c
+++ b/ld/pdb.c
@@ -1410,6 +1410,9 @@ populate_publics_stream (bfd *stream, bfd *abfd, bfd *sym_rec_stream)
for (bfd *in = coff_data (abfd)->link_info->input_bfds; in;
in = in->link.next)
{
+ if (!in->outsymbols)
+ continue;
+
for (unsigned int i = 0; i < in->symcount; i++)
{
struct bfd_symbol *sym = in->outsymbols[i];
--
2.37.4
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 04/10] ld: Write DEBUG_S_LINES entries in PDB file
2022-12-09 1:52 [PATCH 01/10] ld: Generate PDB string table Mark Harmstone
2022-12-09 1:52 ` [PATCH 02/10] ld: Write DEBUG_S_FILECHKSMS entries in PDBs Mark Harmstone
2022-12-09 1:52 ` [PATCH 03/10] ld: Fix segfault in populate_publics_stream Mark Harmstone
@ 2022-12-09 1:52 ` Mark Harmstone
2022-12-09 1:52 ` [PATCH 05/10] ld: Write types into TPI stream of PDB Mark Harmstone
` (6 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Mark Harmstone @ 2022-12-09 1:52 UTC (permalink / raw)
To: binutils, nickc; +Cc: Mark Harmstone
---
ld/pdb.c | 111 +++++++++++++++++++++++++++-
ld/pdb.h | 1 +
ld/testsuite/ld-pe/pdb.exp | 2 +-
ld/testsuite/ld-pe/pdb3-c13-info1.d | 8 +-
ld/testsuite/ld-pe/pdb3a.s | 88 ++++++++++++++++++++++
5 files changed, 204 insertions(+), 6 deletions(-)
diff --git a/ld/pdb.c b/ld/pdb.c
index 3edeab25332..a2f78c4c348 100644
--- a/ld/pdb.c
+++ b/ld/pdb.c
@@ -623,7 +623,8 @@ parse_string_table (bfd_byte *data, size_t size,
static bool
handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
uint8_t **dataptr, uint32_t *sizeptr,
- struct mod_source_files *mod_source)
+ struct mod_source_files *mod_source,
+ bfd *abfd)
{
bfd_byte *data = NULL;
size_t off;
@@ -637,6 +638,59 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
if (!data)
return false;
+ /* Resolve relocations. Addresses are stored within the .debug$S section as
+ a .secidx, .secrel32 pair. */
+
+ if (s->flags & SEC_RELOC)
+ {
+ struct internal_reloc *relocs;
+ struct internal_syment *symbols;
+ asection **sectlist;
+ unsigned int syment_count;
+ int sect_num;
+ struct external_syment *ext;
+
+ syment_count = obj_raw_syment_count (mod);
+
+ relocs =
+ _bfd_coff_read_internal_relocs (mod, s, false, NULL, true, NULL);
+
+ symbols = xmalloc (sizeof (struct internal_syment) * syment_count);
+ sectlist = xmalloc (sizeof (asection *) * syment_count);
+
+ ext = (struct external_syment *) (coff_data (mod)->external_syms);
+
+ for (unsigned int i = 0; i < syment_count; i++)
+ {
+ bfd_coff_swap_sym_in (mod, &ext[i], &symbols[i]);
+ }
+
+ sect_num = 1;
+
+ for (asection *sect = mod->sections; sect; sect = sect->next)
+ {
+ for (unsigned int i = 0; i < syment_count; i++)
+ {
+ if (symbols[i].n_scnum == sect_num)
+ sectlist[i] = sect;
+ }
+
+ sect_num++;
+ }
+
+ if (!bfd_coff_relocate_section (abfd, coff_data (abfd)->link_info, mod,
+ s, data, relocs, symbols, sectlist))
+ {
+ free (sectlist);
+ free (symbols);
+ free (data);
+ return false;
+ }
+
+ free (sectlist);
+ free (symbols);
+ }
+
if (bfd_getl32 (data) != CV_SIGNATURE_C13)
{
free (data);
@@ -689,6 +743,32 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
string_table = (char *) data + off;
break;
+
+ case DEBUG_S_LINES:
+ {
+ uint16_t sect;
+
+ if (size < sizeof (uint32_t) + sizeof (uint16_t))
+ {
+ free (data);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ sect = bfd_getl16 (data + off + sizeof (uint32_t));
+
+ /* Skip GC'd symbols. */
+ if (sect != 0)
+ {
+ 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;
+ }
}
off += size;
@@ -733,6 +813,28 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
bufptr += sizeof (uint32_t) + sizeof (uint32_t) + size;
break;
+
+ case DEBUG_S_LINES:
+ {
+ uint16_t sect;
+
+ sect = bfd_getl16 (data + off + sizeof (uint32_t));
+
+ /* Skip if GC'd. */
+ if (sect != 0)
+ {
+ bfd_putl32 (type, bufptr);
+ bufptr += sizeof (uint32_t);
+
+ bfd_putl32 (size, bufptr);
+ bufptr += sizeof (uint32_t);
+
+ memcpy (bufptr, data + off, size);
+ bufptr += size;
+ }
+
+ break;
+ }
}
off += size;
@@ -769,7 +871,8 @@ static bool
populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
struct string_table *strings,
uint32_t *c13_info_size,
- struct mod_source_files *mod_source)
+ struct mod_source_files *mod_source,
+ bfd *abfd)
{
uint8_t int_buf[sizeof (uint32_t)];
uint8_t *c13_info = NULL;
@@ -784,7 +887,7 @@ 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, &c13_info,
- c13_info_size, mod_source))
+ c13_info_size, mod_source, abfd))
{
free (c13_info);
free (mod_source->files);
@@ -916,7 +1019,7 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
if (!populate_module_stream (stream, in, &sym_byte_size,
strings, &c13_info_size,
- &source->mods[mod_num]))
+ &source->mods[mod_num], abfd))
{
for (unsigned int i = 0; i < source->mod_count; i++)
{
diff --git a/ld/pdb.h b/ld/pdb.h
index e8f673c24a0..bbb106043c4 100644
--- a/ld/pdb.h
+++ b/ld/pdb.h
@@ -155,6 +155,7 @@ struct optional_dbg_header
#define CV_SIGNATURE_C13 4
+#define DEBUG_S_LINES 0xf2
#define DEBUG_S_STRINGTABLE 0xf3
#define DEBUG_S_FILECHKSMS 0xf4
diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp
index 9dab41110ac..2e5f83477aa 100644
--- a/ld/testsuite/ld-pe/pdb.exp
+++ b/ld/testsuite/ld-pe/pdb.exp
@@ -870,7 +870,7 @@ proc test4 { } {
return
}
- if ![ld_link $ld "tmpdir/pdb3.exe" "--pdb=tmpdir/pdb3.pdb tmpdir/pdb3a.o tmpdir/pdb3b.o"] {
+ if ![ld_link $ld "tmpdir/pdb3.exe" "--pdb=tmpdir/pdb3.pdb --gc-sections -e main tmpdir/pdb3a.o tmpdir/pdb3b.o"] {
unsupported "Create PE image with PDB file"
return
}
diff --git a/ld/testsuite/ld-pe/pdb3-c13-info1.d b/ld/testsuite/ld-pe/pdb3-c13-info1.d
index f92062ba4e5..5a4f94861c7 100644
--- a/ld/testsuite/ld-pe/pdb3-c13-info1.d
+++ b/ld/testsuite/ld-pe/pdb3-c13-info1.d
@@ -5,4 +5,10 @@ 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
+ 0030 2301efcd ab890000 f2000000 58000000 #...........X...
+ 0040 00000000 01000000 14000000 00000000 ................
+ 0050 02000000 1c000000 00000000 01000080 ................
+ 0060 04000000 02000080 18000000 02000000 ................
+ 0070 1c000000 08000000 03000080 0c000000 ................
+ 0080 04000080 00000000 01000000 14000000 ................
+ 0090 10000000 05000080 ........
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb3a.s b/ld/testsuite/ld-pe/pdb3a.s
index 71795b53a66..1df84a344f6 100644
--- a/ld/testsuite/ld-pe/pdb3a.s
+++ b/ld/testsuite/ld-pe/pdb3a.s
@@ -1,4 +1,5 @@
.equ CV_SIGNATURE_C13, 4
+.equ DEBUG_S_LINES, 0xf2
.equ DEBUG_S_STRINGTABLE, 0xf3
.equ DEBUG_S_FILECHKSMS, 0xf4
.equ CHKSUM_TYPE_MD5, 1
@@ -50,3 +51,90 @@
.chksms_end:
.balign 4
+
+.long DEBUG_S_LINES
+.long .lines_end - .lines_start
+
+.lines_start:
+
+.secrel32 main
+.secidx main
+.short 0 # flags
+.long .main_end - main # length of region
+
+.lines_block1:
+
+.long 0 # file ID 0 (foo)
+.long 2 # no. lines
+.long .lines_block2 - .lines_block1 # length
+
+.long .line1 - main
+.long 0x80000001 # line 1
+.long .line2 - main
+.long 0x80000002 # line 2
+
+.lines_block2:
+
+.long 0x18 # file ID 18 (bar)
+.long 2 # no. lines
+.long .lines_block3 - .lines_block2 # length
+
+.long .line3 - main
+.long 0x80000003 # line 3
+.long .line4 - main
+.long 0x80000004 # line 4
+
+.lines_block3:
+
+.long 0 # file ID 0 (foo)
+.long 1 # no. lines
+.long .lines_end - .lines_block3 # length
+
+.long .line5 - main
+.long 0x80000005 # line 5
+
+.lines_end:
+
+.long DEBUG_S_LINES
+.long .lines_end2 - .lines_start2
+
+.lines_start2:
+
+.secrel32 gcfunc
+.secidx gcfunc
+.short 0 # flags
+.long .gcfunc_end - gcfunc # length of region
+
+.lines_block4:
+
+.long 0 # file ID 0 (foo)
+.long 1 # no. lines
+.long .lines_end2 - .lines_block4 # length
+
+.long .line6 - gcfunc
+.long 0x80000006 # line 6
+
+.lines_end2:
+
+.text
+
+.global main
+main:
+.line1:
+ .long 0x12345678
+.line2:
+ .long 0x12345678
+.line3:
+ .long 0x12345678
+.line4:
+ .long 0x12345678
+.line5:
+ .long 0x12345678
+.main_end:
+
+.section "gcsect"
+
+gcfunc:
+.line6:
+ .long 0x12345678
+.gcfunc_end:
--
2.37.4
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 05/10] ld: Write types into TPI stream of PDB
2022-12-09 1:52 [PATCH 01/10] ld: Generate PDB string table Mark Harmstone
` (2 preceding siblings ...)
2022-12-09 1:52 ` [PATCH 04/10] ld: Write DEBUG_S_LINES entries in PDB file Mark Harmstone
@ 2022-12-09 1:52 ` Mark Harmstone
2022-12-09 1:52 ` [PATCH 06/10] ld: Write types into IPI " Mark Harmstone
` (5 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Mark Harmstone @ 2022-12-09 1:52 UTC (permalink / raw)
To: binutils, nickc; +Cc: Mark Harmstone
---
ld/pdb.c | 1290 +++++++++++++++++++++-
ld/pdb.h | 264 +++++
ld/testsuite/ld-pe/pdb-types1-hashlist.d | 13 +
ld/testsuite/ld-pe/pdb-types1-skiplist.d | 5 +
ld/testsuite/ld-pe/pdb-types1-typelist.d | 60 +
ld/testsuite/ld-pe/pdb-types1a.s | 27 +
ld/testsuite/ld-pe/pdb-types1b.s | 461 ++++++++
ld/testsuite/ld-pe/pdb.exp | 172 +++
8 files changed, 2270 insertions(+), 22 deletions(-)
create mode 100644 ld/testsuite/ld-pe/pdb-types1-hashlist.d
create mode 100644 ld/testsuite/ld-pe/pdb-types1-skiplist.d
create mode 100644 ld/testsuite/ld-pe/pdb-types1-typelist.d
create mode 100644 ld/testsuite/ld-pe/pdb-types1a.s
create mode 100644 ld/testsuite/ld-pe/pdb-types1b.s
diff --git a/ld/pdb.c b/ld/pdb.c
index a2f78c4c348..4bb19420f95 100644
--- a/ld/pdb.c
+++ b/ld/pdb.c
@@ -71,6 +71,69 @@ struct source_files_info
struct mod_source_files *mods;
};
+struct type_entry
+{
+ struct type_entry *next;
+ uint32_t index;
+ uint32_t cv_hash;
+ uint8_t data[];
+};
+
+struct types
+{
+ htab_t hashmap;
+ uint32_t num_types;
+ struct type_entry *first;
+ struct type_entry *last;
+};
+
+static const uint32_t crc_table[] =
+{
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+ 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+ 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+ 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+ 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+ 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+ 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+ 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+ 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+ 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+ 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+ 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+ 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+ 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+ 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+ 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+ 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+ 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+ 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+ 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+ 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+ 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
/* 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)
@@ -365,38 +428,142 @@ end:
return ret;
}
+/* Calculate the CRC32 used for type hashes. */
+static uint32_t
+crc32 (const uint8_t *data, size_t len)
+{
+ uint32_t crc = 0;
+
+ while (len > 0)
+ {
+ crc = (crc >> 8) ^ crc_table[(crc & 0xff) ^ *data];
+
+ data++;
+ len--;
+ }
+
+ return crc;
+}
+
/* Stream 2 is the type information (TPI) stream, and stream 4 is
the ID information (IPI) stream. They differ only in which records
go in which stream. */
static bool
-create_type_stream (bfd *pdb)
+populate_type_stream (bfd *pdb, bfd *stream, struct types *types)
{
- bfd *stream;
struct pdb_tpi_stream_header h;
+ struct type_entry *e;
+ uint32_t len = 0, index_offset_len, off;
+ struct bfd *hash_stream = NULL;
+ uint16_t hash_stream_index;
- stream = add_stream (pdb, NULL, NULL);
- if (!stream)
+ static const uint32_t index_skip = 0x2000;
+
+ e = types->first;
+
+ index_offset_len = 0;
+
+ while (e)
+ {
+ uint32_t old_len = len;
+
+ len += sizeof (uint16_t) + bfd_getl16 (e->data);
+
+ if (old_len == 0 || old_len / index_skip != len / index_skip)
+ index_offset_len += sizeof (uint32_t) * 2;
+
+ e = e->next;
+ }
+
+ /* Each type stream also has a stream which holds the hash value for each
+ type, along with a skip list to speed up searching. */
+
+ hash_stream = add_stream (pdb, "", &hash_stream_index);
+
+ if (!hash_stream)
return false;
bfd_putl32 (TPI_STREAM_VERSION_80, &h.version);
bfd_putl32 (sizeof (h), &h.header_size);
bfd_putl32 (TPI_FIRST_INDEX, &h.type_index_begin);
- bfd_putl32 (TPI_FIRST_INDEX, &h.type_index_end);
- bfd_putl32 (0, &h.type_record_bytes);
- bfd_putl16 (0xffff, &h.hash_stream_index);
+ bfd_putl32 (TPI_FIRST_INDEX + types->num_types, &h.type_index_end);
+ bfd_putl32 (len, &h.type_record_bytes);
+ bfd_putl16 (hash_stream_index, &h.hash_stream_index);
bfd_putl16 (0xffff, &h.hash_aux_stream_index);
- bfd_putl32 (4, &h.hash_key_size);
- bfd_putl32 (0x3ffff, &h.num_hash_buckets);
+ bfd_putl32 (sizeof (uint32_t), &h.hash_key_size);
+ bfd_putl32 (NUM_TPI_HASH_BUCKETS, &h.num_hash_buckets);
bfd_putl32 (0, &h.hash_value_buffer_offset);
- bfd_putl32 (0, &h.hash_value_buffer_length);
- bfd_putl32 (0, &h.index_offset_buffer_offset);
- bfd_putl32 (0, &h.index_offset_buffer_length);
- bfd_putl32 (0, &h.hash_adj_buffer_offset);
+ bfd_putl32 (types->num_types * sizeof (uint32_t),
+ &h.hash_value_buffer_length);
+ bfd_putl32 (types->num_types * sizeof (uint32_t),
+ &h.index_offset_buffer_offset);
+ bfd_putl32 (index_offset_len, &h.index_offset_buffer_length);
+ bfd_putl32 ((types->num_types * sizeof (uint32_t)) + index_offset_len,
+ &h.hash_adj_buffer_offset);
bfd_putl32 (0, &h.hash_adj_buffer_length);
if (bfd_bwrite (&h, sizeof (h), stream) != sizeof (h))
return false;
+ /* Write the type definitions into the main stream, and the hashes
+ into the hash stream. The hashes have already been calculated
+ in handle_type. */
+
+ e = types->first;
+
+ while (e)
+ {
+ uint8_t buf[sizeof (uint32_t)];
+ uint16_t size;
+
+ size = bfd_getl16 (e->data);
+
+ if (bfd_bwrite (e->data, size + sizeof (uint16_t), stream)
+ != size + sizeof (uint16_t))
+ return false;
+
+ bfd_putl32 (e->cv_hash % NUM_TPI_HASH_BUCKETS, buf);
+
+ if (bfd_bwrite (buf, sizeof (uint32_t), hash_stream)
+ != sizeof (uint32_t))
+ return false;
+
+ e = e->next;
+ }
+
+ /* Write the index offsets, i.e. the skip list, into the hash stream. We
+ copy MSVC here by writing a new entry for every 8192 bytes. */
+
+ e = types->first;
+ off = 0;
+
+ while (e)
+ {
+ uint32_t old_off = off;
+ uint16_t size = bfd_getl16 (e->data);
+
+ off += size + sizeof (uint16_t);
+
+ if (old_off == 0 || old_off / index_skip != len / index_skip)
+ {
+ uint8_t buf[sizeof (uint32_t)];
+
+ bfd_putl32 (TPI_FIRST_INDEX + e->index, buf);
+
+ if (bfd_bwrite (buf, sizeof (uint32_t), hash_stream)
+ != sizeof (uint32_t))
+ return false;
+
+ bfd_putl32 (old_off, buf);
+
+ if (bfd_bwrite (buf, sizeof (uint32_t), hash_stream)
+ != sizeof (uint32_t))
+ return false;
+ }
+
+ e = e->next;
+ }
+
return true;
}
@@ -619,6 +786,32 @@ parse_string_table (bfd_byte *data, size_t size,
}
}
+/* Return the size of an extended value parameter, as used in
+ LF_ENUMERATE etc. */
+static unsigned int
+extended_value_len (uint16_t type)
+{
+ switch (type)
+ {
+ case LF_CHAR:
+ return 1;
+
+ case LF_SHORT:
+ case LF_USHORT:
+ return 2;
+
+ case LF_LONG:
+ case LF_ULONG:
+ return 4;
+
+ case LF_QUADWORD:
+ case LF_UQUADWORD:
+ return 8;
+ }
+
+ return 0;
+}
+
/* Parse the .debug$S section within an object file. */
static bool
handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
@@ -865,6 +1058,984 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
return true;
}
+/* Remap the type number stored in data from the per-module numbering to
+ that of the deduplicated output list. */
+static bool
+remap_type (void *data, struct type_entry **map,
+ uint32_t type_num, uint32_t num_types)
+{
+ uint32_t type = bfd_getl32 (data);
+
+ /* Ignore builtin types (those with IDs below 0x1000). */
+ if (type < TPI_FIRST_INDEX)
+ return true;
+
+ if (type >= TPI_FIRST_INDEX + type_num)
+ {
+ einfo (_("%P: CodeView type %v references other type %v not yet "
+ "declared\n"), TPI_FIRST_INDEX + type_num, type);
+ return false;
+ }
+
+ if (type >= TPI_FIRST_INDEX + num_types)
+ {
+ einfo (_("%P: CodeView type %v references out of range type %v\n"),
+ TPI_FIRST_INDEX + type_num, type);
+ return false;
+ }
+
+ type = TPI_FIRST_INDEX + map[type - TPI_FIRST_INDEX]->index;
+ bfd_putl32 (type, data);
+
+ return true;
+}
+
+/* Determines whether the name of a struct, class, or union counts as
+ "anonymous". Non-anonymous types have a hash based on just the name,
+ rather than the whole structure. */
+static bool
+is_name_anonymous (char *name, size_t len)
+{
+ static const char tag1[] = "<unnamed-tag>";
+ static const char tag2[] = "__unnamed";
+ static const char tag3[] = "::<unnamed-tag>";
+ static const char tag4[] = "::__unnamed";
+
+ if (len == sizeof (tag1) - 1 && !memcmp (name, tag1, sizeof (tag1) - 1))
+ return true;
+
+ if (len == sizeof (tag2) - 1 && !memcmp (name, tag2, sizeof (tag2) - 1))
+ return true;
+
+ if (len >= sizeof (tag3) - 1
+ && !memcmp (name + len - sizeof (tag3) + 1, tag3, sizeof (tag3) - 1))
+ return true;
+
+ if (len >= sizeof (tag4) - 1
+ && !memcmp (name + len - sizeof (tag4) + 1, tag4, sizeof (tag4) - 1))
+ return true;
+
+ return false;
+}
+
+/* Parse a type definition in the .debug$T section. We remap the numbers
+ of any referenced types, and if the type is not a duplicate of one
+ already seen add it to types (for TPI types) or ids (for IPI types). */
+static bool
+handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
+ uint32_t num_types, struct types *types)
+{
+ uint16_t size, type;
+ void **slot;
+ hashval_t hash;
+ bool other_hash = false;
+ uint32_t cv_hash;
+
+ size = bfd_getl16 (data) + sizeof (uint16_t);
+ type = bfd_getl16 (data + sizeof (uint16_t));
+
+ switch (type)
+ {
+ case LF_MODIFIER:
+ {
+ struct lf_modifier *mod = (struct lf_modifier *) data;
+
+ if (size < offsetof (struct lf_modifier, modifier))
+ {
+ einfo (_("%P: warning: truncated CodeView type record "
+ "LF_MODIFIER\n"));
+ return false;
+ }
+
+ if (!remap_type (&mod->base_type, map, type_num, num_types))
+ return false;
+
+ break;
+ }
+
+ case LF_POINTER:
+ {
+ struct lf_pointer *ptr = (struct lf_pointer *) data;
+
+ if (size < offsetof (struct lf_pointer, attributes))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_POINTER\n"));
+ return false;
+ }
+
+ if (!remap_type (&ptr->base_type, map, type_num, num_types))
+ return false;
+
+ break;
+ }
+
+ case LF_PROCEDURE:
+ {
+ struct lf_procedure *proc = (struct lf_procedure *) data;
+
+ if (size < sizeof (struct lf_procedure))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_PROCEDURE\n"));
+ return false;
+ }
+
+ if (!remap_type (&proc->return_type, map, type_num, num_types))
+ return false;
+
+ if (!remap_type (&proc->arglist, map, type_num, num_types))
+ return false;
+
+ break;
+ }
+
+ case LF_MFUNCTION:
+ {
+ struct lf_mfunction *func = (struct lf_mfunction *) data;
+
+ if (size < sizeof (struct lf_procedure))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_MFUNCTION\n"));
+ return false;
+ }
+
+ if (!remap_type (&func->return_type, map, type_num, num_types))
+ return false;
+
+ if (!remap_type (&func->containing_class_type, map, type_num,
+ num_types))
+ return false;
+
+ if (!remap_type (&func->this_type, map, type_num, num_types))
+ return false;
+
+ if (!remap_type (&func->arglist, map, type_num, num_types))
+ return false;
+
+ break;
+ }
+
+ case LF_ARGLIST:
+ {
+ uint32_t num_entries;
+ struct lf_arglist *al = (struct lf_arglist *) data;
+
+ if (size < offsetof (struct lf_arglist, args))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_ARGLIST\n"));
+ return false;
+ }
+
+ num_entries = bfd_getl32 (&al->num_entries);
+
+ if (size < offsetof (struct lf_arglist, args)
+ + (num_entries * sizeof (uint32_t)))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_ARGLIST\n"));
+ return false;
+ }
+
+ for (uint32_t i = 0; i < num_entries; i++)
+ {
+ if (!remap_type (&al->args[i], map, type_num, num_types))
+ return false;
+ }
+
+ break;
+ }
+
+ case LF_FIELDLIST:
+ {
+ uint16_t left = size - sizeof (uint16_t) - sizeof (uint16_t);
+ uint8_t *ptr = data + sizeof (uint16_t) + sizeof (uint16_t);
+
+ while (left > 0)
+ {
+ uint16_t subtype;
+
+ if (left < sizeof (uint16_t))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_FIELDLIST\n"));
+ return false;
+ }
+
+ subtype = bfd_getl16 (ptr);
+
+ switch (subtype)
+ {
+ case LF_MEMBER:
+ {
+ struct lf_member *mem = (struct lf_member *) ptr;
+ size_t name_len, subtype_len;
+
+ if (left < offsetof (struct lf_member, name))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_MEMBER\n"));
+ return false;
+ }
+
+ if (!remap_type (&mem->type, map, type_num, num_types))
+ return false;
+
+ name_len =
+ strnlen (mem->name,
+ left - offsetof (struct lf_member, name));
+
+ if (name_len == left - offsetof (struct lf_member, name))
+ {
+ einfo (_("%P: warning: name for LF_MEMBER has no"
+ " terminating zero\n"));
+ return false;
+ }
+
+ name_len++;
+
+ subtype_len = offsetof (struct lf_member, name) + name_len;
+
+ if (subtype_len % 4 != 0)
+ subtype_len += 4 - (subtype_len % 4);
+
+ if (left < subtype_len)
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_FIELDLIST\n"));
+ return false;
+ }
+
+ ptr += subtype_len;
+ left -= subtype_len;
+
+ break;
+ }
+
+ case LF_ENUMERATE:
+ {
+ struct lf_enumerate *en = (struct lf_enumerate *) ptr;
+ size_t name_len, subtype_len;
+ uint16_t val;
+
+ if (left < offsetof (struct lf_enumerate, name))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_ENUMERATE\n"));
+ return false;
+ }
+
+ subtype_len = offsetof (struct lf_enumerate, name);
+
+ val = bfd_getl16 (&en->value);
+
+ /* If val >= 0x8000, the actual value immediately follows. */
+ if (val >= 0x8000)
+ {
+ unsigned int param_len = extended_value_len (val);
+
+ if (param_len == 0)
+ {
+ einfo (_("%P: warning: unhandled type %v within"
+ " LF_ENUMERATE\n"), val);
+ return false;
+ }
+
+ if (left < subtype_len + param_len)
+ {
+ einfo (_("%P: warning: truncated CodeView type"
+ " record LF_ENUMERATE\n"));
+ return false;
+ }
+
+ subtype_len += param_len;
+ }
+
+ name_len = strnlen ((char *) ptr + subtype_len,
+ left - subtype_len);
+
+ if (name_len == left - offsetof (struct lf_enumerate, name))
+ {
+ einfo (_("%P: warning: name for LF_ENUMERATE has no"
+ " terminating zero\n"));
+ return false;
+ }
+
+ name_len++;
+
+ subtype_len += name_len;
+
+ if (subtype_len % 4 != 0)
+ subtype_len += 4 - (subtype_len % 4);
+
+ if (left < subtype_len)
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_ENUMERATE\n"));
+ return false;
+ }
+
+ ptr += subtype_len;
+ left -= subtype_len;
+
+ break;
+ }
+
+ case LF_INDEX:
+ {
+ struct lf_index *ind = (struct lf_index *) ptr;
+
+ if (left < sizeof (struct lf_index))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_INDEX\n"));
+ return false;
+ }
+
+ if (!remap_type (&ind->index, map, type_num, num_types))
+ return false;
+
+ ptr += sizeof (struct lf_index);
+ left -= sizeof (struct lf_index);
+
+ break;
+ }
+
+ case LF_ONEMETHOD:
+ {
+ struct lf_onemethod *meth = (struct lf_onemethod *) ptr;
+ size_t name_len, subtype_len;
+
+ if (left < offsetof (struct lf_onemethod, name))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_ONEMETHOD\n"));
+ return false;
+ }
+
+ if (!remap_type (&meth->method_type, map, type_num,
+ num_types))
+ return false;
+
+ name_len =
+ strnlen (meth->name,
+ left - offsetof (struct lf_onemethod, name));
+
+ if (name_len == left - offsetof (struct lf_onemethod, name))
+ {
+ einfo (_("%P: warning: name for LF_ONEMETHOD has no"
+ " terminating zero\n"));
+ return false;
+ }
+
+ name_len++;
+
+ subtype_len = offsetof (struct lf_onemethod, name)
+ + name_len;
+
+ if (subtype_len % 4 != 0)
+ subtype_len += 4 - (subtype_len % 4);
+
+ if (left < subtype_len)
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_FIELDLIST\n"));
+ return false;
+ }
+
+ ptr += subtype_len;
+ left -= subtype_len;
+
+ break;
+ }
+
+ case LF_METHOD:
+ {
+ struct lf_method *meth = (struct lf_method *) ptr;
+ size_t name_len, subtype_len;
+
+ if (left < offsetof (struct lf_method, name))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_METHOD\n"));
+ return false;
+ }
+
+ if (!remap_type (&meth->method_list, map, type_num,
+ num_types))
+ return false;
+
+ name_len =
+ strnlen (meth->name,
+ left - offsetof (struct lf_method, name));
+
+ if (name_len == left - offsetof (struct lf_method, name))
+ {
+ einfo (_("%P: warning: name for LF_METHOD has no"
+ " terminating zero\n"));
+ return false;
+ }
+
+ name_len++;
+
+ subtype_len = offsetof (struct lf_method, name) + name_len;
+
+ if (subtype_len % 4 != 0)
+ subtype_len += 4 - (subtype_len % 4);
+
+ if (left < subtype_len)
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_FIELDLIST\n"));
+ return false;
+ }
+
+ ptr += subtype_len;
+ left -= subtype_len;
+
+ break;
+ }
+
+ case LF_BCLASS:
+ {
+ struct lf_bclass *bc = (struct lf_bclass *) ptr;
+
+ if (left < sizeof (struct lf_bclass))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_BCLASS\n"));
+ return false;
+ }
+
+ if (!remap_type (&bc->base_class_type, map, type_num,
+ num_types))
+ return false;
+
+ ptr += sizeof (struct lf_bclass);
+ left -= sizeof (struct lf_bclass);
+
+ break;
+ }
+
+ case LF_VFUNCTAB:
+ {
+ struct lf_vfunctab *vft = (struct lf_vfunctab *) ptr;
+
+ if (left < sizeof (struct lf_vfunctab))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_VFUNCTAB\n"));
+ return false;
+ }
+
+ if (!remap_type (&vft->type, map, type_num, num_types))
+ return false;
+
+ ptr += sizeof (struct lf_vfunctab);
+ left -= sizeof (struct lf_vfunctab);
+
+ break;
+ }
+
+ case LF_VBCLASS:
+ case LF_IVBCLASS:
+ {
+ struct lf_vbclass *vbc = (struct lf_vbclass *) ptr;
+
+ if (left < sizeof (struct lf_vbclass))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_VBCLASS/LF_IVBCLASS\n"));
+ return false;
+ }
+
+ if (!remap_type (&vbc->base_class_type, map, type_num,
+ num_types))
+ return false;
+
+ if (!remap_type (&vbc->virtual_base_pointer_type, map,
+ type_num, num_types))
+ return false;
+
+ ptr += sizeof (struct lf_vbclass);
+ left -= sizeof (struct lf_vbclass);
+
+ break;
+ }
+
+ case LF_STMEMBER:
+ {
+ struct lf_static_member *st =
+ (struct lf_static_member *) ptr;
+ size_t name_len, subtype_len;
+
+ if (left < offsetof (struct lf_static_member, name))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_STMEMBER\n"));
+ return false;
+ }
+
+ if (!remap_type (&st->type, map, type_num, num_types))
+ return false;
+
+ name_len =
+ strnlen (st->name,
+ left - offsetof (struct lf_static_member, name));
+
+ if (name_len == left
+ - offsetof (struct lf_static_member, name))
+ {
+ einfo (_("%P: warning: name for LF_STMEMBER has no"
+ " terminating zero\n"));
+ return false;
+ }
+
+ name_len++;
+
+ subtype_len = offsetof (struct lf_static_member, name)
+ + name_len;
+
+ if (subtype_len % 4 != 0)
+ subtype_len += 4 - (subtype_len % 4);
+
+ if (left < subtype_len)
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_FIELDLIST\n"));
+ return false;
+ }
+
+ ptr += subtype_len;
+ left -= subtype_len;
+
+ break;
+ }
+
+ case LF_NESTTYPE:
+ {
+ struct lf_nest_type *nest = (struct lf_nest_type *) ptr;
+ size_t name_len, subtype_len;
+
+ if (left < offsetof (struct lf_nest_type, name))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_NESTTYPE\n"));
+ return false;
+ }
+
+ if (!remap_type (&nest->type, map, type_num, num_types))
+ return false;
+
+ name_len =
+ strnlen (nest->name,
+ left - offsetof (struct lf_nest_type, name));
+
+ if (name_len == left - offsetof (struct lf_nest_type, name))
+ {
+ einfo (_("%P: warning: name for LF_NESTTYPE has no"
+ " terminating zero\n"));
+ return false;
+ }
+
+ name_len++;
+
+ subtype_len = offsetof (struct lf_nest_type, name)
+ + name_len;
+
+ if (subtype_len % 4 != 0)
+ subtype_len += 4 - (subtype_len % 4);
+
+ if (left < subtype_len)
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_FIELDLIST\n"));
+ return false;
+ }
+
+ ptr += subtype_len;
+ left -= subtype_len;
+
+ break;
+ }
+
+ default:
+ einfo (_("%P: warning: unrecognized CodeView subtype %v\n"),
+ subtype);
+ return false;
+ }
+ }
+
+ break;
+ }
+
+ case LF_BITFIELD:
+ {
+ struct lf_bitfield *bf = (struct lf_bitfield *) data;
+
+ if (size < offsetof (struct lf_bitfield, length))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_BITFIELD\n"));
+ return false;
+ }
+
+ if (!remap_type (&bf->base_type, map, type_num, num_types))
+ return false;
+
+ break;
+ }
+
+ case LF_METHODLIST:
+ {
+ struct lf_methodlist *ml = (struct lf_methodlist *) data;
+ unsigned int num_entries;
+
+ if (size < offsetof (struct lf_methodlist, entries))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_METHODLIST\n"));
+ return false;
+ }
+
+ if ((size - offsetof (struct lf_methodlist, entries))
+ % sizeof (struct lf_methodlist_entry))
+ {
+ einfo (_("%P: warning: malformed CodeView type record"
+ " LF_METHODLIST\n"));
+ return false;
+ }
+
+ num_entries = (size - offsetof (struct lf_methodlist, entries))
+ / sizeof (struct lf_methodlist_entry);
+
+ for (unsigned int i = 0; i < num_entries; i++)
+ {
+ if (!remap_type (&ml->entries[i].method_type, map,
+ type_num, num_types))
+ return false;
+ }
+
+ break;
+ }
+
+ case LF_ARRAY:
+ {
+ struct lf_array *arr = (struct lf_array *) data;
+
+ if (size < offsetof (struct lf_array, length_in_bytes))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_ARRAY\n"));
+ return false;
+ }
+
+ if (!remap_type (&arr->element_type, map, type_num, num_types))
+ return false;
+
+ if (!remap_type (&arr->index_type, map, type_num, num_types))
+ return false;
+
+ break;
+ }
+
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ {
+ struct lf_class *cl = (struct lf_class *) data;
+ uint16_t prop;
+ size_t name_len;
+
+ if (size < offsetof (struct lf_class, name))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_CLASS/LF_STRUCTURE\n"));
+ return false;
+ }
+
+ if (!remap_type (&cl->field_list, map, type_num, num_types))
+ return false;
+
+ if (!remap_type (&cl->derived_from, map, type_num, num_types))
+ return false;
+
+ if (!remap_type (&cl->vshape, map, type_num, num_types))
+ return false;
+
+ name_len = strnlen (cl->name, size - offsetof (struct lf_class, name));
+
+ if (name_len == size - offsetof (struct lf_class, name))
+ {
+ einfo (_("%P: warning: name for LF_CLASS/LF_STRUCTURE has no"
+ " terminating zero\n"));
+ return false;
+ }
+
+ prop = bfd_getl16 (&cl->properties);
+
+ if (prop & CV_PROP_HAS_UNIQUE_NAME)
+ {
+ /* Structure has another name following first one. */
+
+ size_t len = offsetof (struct lf_class, name) + name_len + 1;
+ size_t unique_name_len;
+
+ unique_name_len = strnlen (cl->name + name_len + 1, size - len);
+
+ if (unique_name_len == size - len)
+ {
+ einfo (_("%P: warning: unique name for LF_CLASS/LF_STRUCTURE"
+ " has no terminating zero\n"));
+ return false;
+ }
+ }
+
+ if (!(prop & (CV_PROP_FORWARD_REF | CV_PROP_SCOPED))
+ && !is_name_anonymous (cl->name, name_len))
+ {
+ other_hash = true;
+ cv_hash = crc32 ((uint8_t *) cl->name, name_len);
+ }
+
+ break;
+ }
+
+ case LF_UNION:
+ {
+ struct lf_union *un = (struct lf_union *) data;
+ uint16_t prop;
+ size_t name_len;
+
+ if (size < offsetof (struct lf_union, name))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_UNION\n"));
+ return false;
+ }
+
+ if (!remap_type (&un->field_list, map, type_num, num_types))
+ return false;
+
+ name_len = strnlen (un->name, size - offsetof (struct lf_union, name));
+
+ if (name_len == size - offsetof (struct lf_union, name))
+ {
+ einfo (_("%P: warning: name for LF_UNION has no"
+ " terminating zero\n"));
+ return false;
+ }
+
+ prop = bfd_getl16 (&un->properties);
+
+ if (prop & CV_PROP_HAS_UNIQUE_NAME)
+ {
+ /* Structure has another name following first one. */
+
+ size_t len = offsetof (struct lf_union, name) + name_len + 1;
+ size_t unique_name_len;
+
+ unique_name_len = strnlen (un->name + name_len + 1, size - len);
+
+ if (unique_name_len == size - len)
+ {
+ einfo (_("%P: warning: unique name for LF_UNION has"
+ " no terminating zero\n"));
+ return false;
+ }
+ }
+
+ if (!(prop & (CV_PROP_FORWARD_REF | CV_PROP_SCOPED))
+ && !is_name_anonymous (un->name, name_len))
+ {
+ other_hash = true;
+ cv_hash = crc32 ((uint8_t *) un->name, name_len);
+ }
+
+ break;
+ }
+
+ case LF_ENUM:
+ {
+ struct lf_enum *en = (struct lf_enum *) data;
+ uint16_t prop;
+ size_t name_len;
+
+ if (size < offsetof (struct lf_enum, name))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_ENUM\n"));
+ return false;
+ }
+
+ if (!remap_type (&en->underlying_type, map, type_num, num_types))
+ return false;
+
+ if (!remap_type (&en->field_list, map, type_num, num_types))
+ return false;
+
+ name_len = strnlen (en->name, size - offsetof (struct lf_enum, name));
+
+ if (name_len == size - offsetof (struct lf_enum, name))
+ {
+ einfo (_("%P: warning: name for LF_ENUM has no"
+ " terminating zero\n"));
+ return false;
+ }
+
+ prop = bfd_getl16 (&en->properties);
+
+ if (prop & CV_PROP_HAS_UNIQUE_NAME)
+ {
+ /* Structure has another name following first one. */
+
+ size_t len = offsetof (struct lf_enum, name) + name_len + 1;
+ size_t unique_name_len;
+
+ unique_name_len = strnlen (en->name + name_len + 1, size - len);
+
+ if (unique_name_len == size - len)
+ {
+ einfo (_("%P: warning: unique name for LF_ENUM has"
+ " no terminating zero\n"));
+ return false;
+ }
+ }
+
+ break;
+ }
+
+ case LF_VTSHAPE:
+ /* Does not reference any types, nothing to be done. */
+ break;
+
+ default:
+ einfo (_("%P: warning: unrecognized CodeView type %v\n"), type);
+ return false;
+ }
+
+ hash = iterative_hash (data, size, 0);
+
+ slot = htab_find_slot_with_hash (types->hashmap, data, hash, INSERT);
+ if (!slot)
+ return false;
+
+ if (!*slot) /* new entry */
+ {
+ struct type_entry *e;
+
+ *slot = xmalloc (offsetof (struct type_entry, data) + size);
+
+ e = (struct type_entry *) *slot;
+
+ e->next = NULL;
+ e->index = types->num_types;
+
+ if (other_hash)
+ e->cv_hash = cv_hash;
+ else
+ e->cv_hash = crc32 (data, size);
+
+ memcpy (e->data, data, size);
+
+ if (types->last)
+ types->last->next = e;
+ else
+ types->first = e;
+
+ types->last = e;
+
+ map[type_num] = e;
+
+ types->num_types++;
+ }
+ else /* duplicate */
+ {
+ map[type_num] = (struct type_entry *) *slot;
+ }
+
+ return true;
+}
+
+/* Parse the .debug$T section of a module, and pass any type definitions
+ found to handle_type. */
+static bool
+handle_debugt_section (asection *s, bfd *mod, struct types *types)
+{
+ bfd_byte *data = NULL;
+ size_t off;
+ unsigned int num_types = 0;
+ struct type_entry **map;
+ uint32_t type_num;
+
+ 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 (uint16_t) <= s->size)
+ {
+ uint16_t size;
+
+ size = bfd_getl16 (data + off);
+ off += sizeof (uint16_t);
+
+ if (size + off > s->size || size <= sizeof (uint16_t))
+ {
+ free (data);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ num_types++;
+ off += size;
+ }
+
+ if (num_types == 0)
+ {
+ free (data);
+ return true;
+ }
+
+ map = xcalloc (num_types, sizeof (struct type_entry *));
+
+ off = sizeof (uint32_t);
+ type_num = 0;
+
+ while (off + sizeof (uint16_t) <= s->size)
+ {
+ uint16_t size;
+
+ size = bfd_getl16 (data + off);
+
+ if (!handle_type (data + off, map, type_num, num_types, types))
+ {
+ free (data);
+ free (map);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ off += sizeof (uint16_t) + size;
+ type_num++;
+ }
+
+ free (data);
+ free (map);
+
+ return true;
+}
+
/* Populate the module stream, which consists of the transformed .debug$S
data for each object file. */
static bool
@@ -872,7 +2043,7 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
struct string_table *strings,
uint32_t *c13_info_size,
struct mod_source_files *mod_source,
- bfd *abfd)
+ bfd *abfd, struct types *types)
{
uint8_t int_buf[sizeof (uint32_t)];
uint8_t *c13_info = NULL;
@@ -894,6 +2065,15 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
return false;
}
}
+ else if (!strcmp (s->name, ".debug$T") && s->size >= sizeof (uint32_t))
+ {
+ if (!handle_debugt_section (s, mod, types))
+ {
+ free (c13_info);
+ free (mod_source->files);
+ return false;
+ }
+ }
}
/* Write the signature. */
@@ -931,7 +2111,8 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
static bool
create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
uint32_t *size, struct string_table *strings,
- struct source_files_info *source)
+ struct source_files_info *source,
+ struct types *types)
{
uint8_t *ptr;
unsigned int mod_num;
@@ -1019,7 +2200,8 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
if (!populate_module_stream (stream, in, &sym_byte_size,
strings, &c13_info_size,
- &source->mods[mod_num], abfd))
+ &source->mods[mod_num], abfd,
+ types))
{
for (unsigned int i = 0; i < source->mod_count; i++)
{
@@ -1341,7 +2523,8 @@ 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 string_table *strings)
+ struct string_table *strings,
+ struct types *types)
{
struct pdb_dbi_stream_header h;
struct optional_dbg_header opt;
@@ -1353,7 +2536,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
source.mods = NULL;
if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size,
- strings, &source))
+ strings, &source, types))
return false;
if (!create_section_contrib_substream (abfd, &sc, &sc_size))
@@ -1883,6 +3066,31 @@ populate_names_stream (bfd *stream, struct string_table *strings)
return true;
}
+/* Calculate the hash of a type_entry. */
+static hashval_t
+hash_type_entry (const void *p)
+{
+ const struct type_entry *e = (const struct type_entry *) p;
+ uint16_t size = bfd_getl16 (e->data) + sizeof (uint16_t);
+
+ return iterative_hash (e->data, size, 0);
+}
+
+/* Compare a type_entry with a type. */
+static int
+eq_type_entry (const void *a, const void *b)
+{
+ const struct type_entry *e = (const struct type_entry *) a;
+ uint16_t size_a = bfd_getl16 (e->data);
+ uint16_t size_b = bfd_getl16 (b);
+
+ if (size_a != size_b)
+ return 0;
+
+ return memcmp (e->data + sizeof (uint16_t),
+ (const uint8_t *) b + sizeof (uint16_t), size_a) == 0;
+}
+
/* Create a PDB debugging file for the PE image file abfd with the build ID
guid, stored at pdb_name. */
bool
@@ -1891,9 +3099,10 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
bfd *pdb;
bool ret = false;
bfd *info_stream, *dbi_stream, *names_stream, *sym_rec_stream,
- *publics_stream;
+ *publics_stream, *tpi_stream, *ipi_stream;
uint16_t section_header_stream_num, sym_rec_stream_num, publics_stream_num;
struct string_table strings;
+ struct types types, ids;
pdb = bfd_openw (pdb_name, "pdb");
if (!pdb)
@@ -1927,7 +3136,9 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
goto end;
}
- if (!create_type_stream (pdb))
+ tpi_stream = add_stream (pdb, NULL, NULL);
+
+ if (!tpi_stream)
{
einfo (_("%P: warning: cannot create TPI stream "
"in PDB file: %E\n"));
@@ -1943,7 +3154,9 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
goto end;
}
- if (!create_type_stream (pdb))
+ ipi_stream = add_stream (pdb, NULL, NULL);
+
+ if (!ipi_stream)
{
einfo (_("%P: warning: cannot create IPI stream "
"in PDB file: %E\n"));
@@ -1984,15 +3197,48 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
goto end;
}
+ types.num_types = 0;
+ types.hashmap = htab_create_alloc (0, hash_type_entry, eq_type_entry,
+ free, xcalloc, free);
+ types.first = types.last = NULL;
+
+ ids.num_types = 0;
+ ids.hashmap = htab_create_alloc (0, hash_type_entry, eq_type_entry,
+ free, xcalloc, free);
+ ids.first = ids.last = NULL;
+
if (!populate_dbi_stream (dbi_stream, abfd, pdb, section_header_stream_num,
sym_rec_stream_num, publics_stream_num,
- &strings))
+ &strings, &types))
{
einfo (_("%P: warning: cannot populate DBI stream "
"in PDB file: %E\n"));
+ htab_delete (types.hashmap);
+ htab_delete (ids.hashmap);
+ goto end;
+ }
+
+ if (!populate_type_stream (pdb, tpi_stream, &types))
+ {
+ einfo (_("%P: warning: cannot populate TPI stream "
+ "in PDB file: %E\n"));
+ htab_delete (types.hashmap);
+ htab_delete (ids.hashmap);
goto end;
}
+ htab_delete (types.hashmap);
+
+ if (!populate_type_stream (pdb, ipi_stream, &ids))
+ {
+ einfo (_("%P: warning: cannot populate IPI stream "
+ "in PDB file: %E\n"));
+ htab_delete (ids.hashmap);
+ goto end;
+ }
+
+ htab_delete (ids.hashmap);
+
add_string ("", 0, &strings);
if (!populate_names_stream (names_stream, &strings))
diff --git a/ld/pdb.h b/ld/pdb.h
index bbb106043c4..ecc26c1c87a 100644
--- a/ld/pdb.h
+++ b/ld/pdb.h
@@ -27,6 +27,41 @@
#include "sysdep.h"
#include "bfd.h"
#include <stdbool.h>
+#include <stddef.h>
+
+#define LF_VTSHAPE 0x000a
+#define LF_MODIFIER 0x1001
+#define LF_POINTER 0x1002
+#define LF_PROCEDURE 0x1008
+#define LF_MFUNCTION 0x1009
+#define LF_ARGLIST 0x1201
+#define LF_FIELDLIST 0x1203
+#define LF_BITFIELD 0x1205
+#define LF_METHODLIST 0x1206
+#define LF_BCLASS 0x1400
+#define LF_VBCLASS 0x1401
+#define LF_IVBCLASS 0x1402
+#define LF_INDEX 0x1404
+#define LF_VFUNCTAB 0x1409
+#define LF_ENUMERATE 0x1502
+#define LF_ARRAY 0x1503
+#define LF_CLASS 0x1504
+#define LF_STRUCTURE 0x1505
+#define LF_UNION 0x1506
+#define LF_ENUM 0x1507
+#define LF_MEMBER 0x150d
+#define LF_STMEMBER 0x150e
+#define LF_METHOD 0x150f
+#define LF_NESTTYPE 0x1510
+#define LF_ONEMETHOD 0x1511
+
+#define LF_CHAR 0x8000
+#define LF_SHORT 0x8001
+#define LF_USHORT 0x8002
+#define LF_LONG 0x8003
+#define LF_ULONG 0x8004
+#define LF_QUADWORD 0x8009
+#define LF_UQUADWORD 0x800a
#define S_PUB32 0x110e
@@ -65,6 +100,7 @@ struct pdb_tpi_stream_header
#define TPI_STREAM_VERSION_80 20040203
#define TPI_FIRST_INDEX 0x1000
+#define NUM_TPI_HASH_BUCKETS 0x3ffff
/* NewDBIHdr in dbi.h */
struct pdb_dbi_stream_header
@@ -210,6 +246,234 @@ struct file_checksum
uint8_t checksum_type;
} ATTRIBUTE_PACKED;
+/* lfModifier in cvinfo.h */
+struct lf_modifier
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t base_type;
+ uint16_t modifier;
+ uint16_t padding;
+} ATTRIBUTE_PACKED;
+
+/* lfPointer in cvinfo.h */
+struct lf_pointer
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t base_type;
+ uint32_t attributes;
+} ATTRIBUTE_PACKED;
+
+/* lfArgList in cvinfo.h */
+struct lf_arglist
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t num_entries;
+ uint32_t args[];
+} ATTRIBUTE_PACKED;
+
+/* lfProc in cvinfo.h */
+struct lf_procedure
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t return_type;
+ uint8_t calling_convention;
+ uint8_t attributes;
+ uint16_t num_parameters;
+ uint32_t arglist;
+} ATTRIBUTE_PACKED;
+
+/* lfMFunc in cvinfo.h */
+struct lf_mfunction
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t return_type;
+ uint32_t containing_class_type;
+ uint32_t this_type;
+ uint8_t calling_convention;
+ uint8_t attributes;
+ uint16_t num_parameters;
+ uint32_t arglist;
+ int32_t this_adjustment;
+} ATTRIBUTE_PACKED;
+
+/* lfArray in cvinfo.h */
+struct lf_array
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t element_type;
+ uint32_t index_type;
+ uint16_t length_in_bytes;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* lfBitfield in cvinfo.h */
+struct lf_bitfield
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t base_type;
+ uint8_t length;
+ uint8_t position;
+} ATTRIBUTE_PACKED;
+
+/* lfMember in cvinfo.h */
+struct lf_member
+{
+ uint16_t kind;
+ uint16_t attributes;
+ uint32_t type;
+ uint16_t offset;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* from bitfield structure CV_prop_t in cvinfo.h */
+#define CV_PROP_FORWARD_REF 0x80
+#define CV_PROP_SCOPED 0x100
+#define CV_PROP_HAS_UNIQUE_NAME 0x200
+
+/* lfClass in cvinfo.h */
+struct lf_class
+{
+ uint16_t size;
+ uint16_t kind;
+ uint16_t num_members;
+ uint16_t properties;
+ uint32_t field_list;
+ uint32_t derived_from;
+ uint32_t vshape;
+ uint16_t length;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* lfUnion in cvinfo.h */
+struct lf_union
+{
+ uint16_t size;
+ uint16_t kind;
+ uint16_t num_members;
+ uint16_t properties;
+ uint32_t field_list;
+ uint16_t length;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* lfEnumerate in cvinfo.h */
+struct lf_enumerate
+{
+ uint16_t kind;
+ uint16_t attributes;
+ uint16_t value;
+ /* then actual value if value >= 0x8000 */
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* lfEnum in cvinfo.h */
+struct lf_enum
+{
+ uint16_t size;
+ uint16_t kind;
+ uint16_t num_elements;
+ uint16_t properties;
+ uint32_t underlying_type;
+ uint32_t field_list;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* lfIndex in cvinfo.h */
+struct lf_index
+{
+ uint16_t kind;
+ uint16_t padding;
+ uint32_t index;
+} ATTRIBUTE_PACKED;
+
+/* lfOneMethod in cvinfo.h */
+struct lf_onemethod
+{
+ uint16_t kind;
+ uint16_t method_attribute;
+ uint32_t method_type;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* mlMethod in cvinfo.h */
+struct lf_methodlist_entry
+{
+ uint16_t method_attribute;
+ uint16_t padding;
+ uint32_t method_type;
+} ATTRIBUTE_PACKED;
+
+/* lfMethodList in cvinfo.h */
+struct lf_methodlist
+{
+ uint16_t size;
+ uint16_t kind;
+ struct lf_methodlist_entry entries[];
+} ATTRIBUTE_PACKED;
+
+/* lfMethod in cvinfo.h */
+struct lf_method
+{
+ uint16_t kind;
+ uint16_t count;
+ uint32_t method_list;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* lfBClass in cvinfo.h */
+struct lf_bclass
+{
+ uint16_t kind;
+ uint16_t attributes;
+ uint32_t base_class_type;
+ uint16_t offset;
+ uint16_t padding;
+} ATTRIBUTE_PACKED;
+
+/* lfVFuncTab in cvinfo.h */
+struct lf_vfunctab
+{
+ uint16_t kind;
+ uint16_t padding;
+ uint32_t type;
+} ATTRIBUTE_PACKED;
+
+/* lfVBClass in cvinfo.h */
+struct lf_vbclass
+{
+ uint16_t kind;
+ uint16_t attributes;
+ uint32_t base_class_type;
+ uint32_t virtual_base_pointer_type;
+ uint16_t virtual_base_pointer_offset;
+ uint16_t virtual_base_vbtable_offset;
+} ATTRIBUTE_PACKED;
+
+/* lfSTMember in cvinfo.h */
+struct lf_static_member
+{
+ uint16_t kind;
+ uint16_t attributes;
+ uint32_t type;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* lfNestType in cvinfo.h */
+struct lf_nest_type
+{
+ uint16_t kind;
+ uint16_t padding;
+ uint32_t type;
+ char name[];
+} ATTRIBUTE_PACKED;
+
extern bool create_pdb_file (bfd *, const char *, const unsigned char *);
#endif
diff --git a/ld/testsuite/ld-pe/pdb-types1-hashlist.d b/ld/testsuite/ld-pe/pdb-types1-hashlist.d
new file mode 100644
index 00000000000..b75f08c1de7
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types1-hashlist.d
@@ -0,0 +1,13 @@
+
+*: file format binary
+
+Contents of section .data:
+ 0000 5b4f0200 e29b0300 a99a0300 90160300 *
+ 0010 32970300 d6540100 fad50000 c3b00000 *
+ 0020 6a270200 7b000100 31de0000 2bf50200 *
+ 0030 b59b0300 73cf0300 10b90000 84240300 *
+ 0040 64150100 2e8a0000 88fe0000 c0660000 *
+ 0050 ffd80200 b0260100 7c060200 e3240200 *
+ 0060 63ff0100 fb6b0300 0ad90100 523c0200 *
+ 0070 4d5e0200 8a940200 4b710300 6aa90300 *
+ 0080 0a2c0300 67e10300 4a3d0300 *
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-types1-skiplist.d b/ld/testsuite/ld-pe/pdb-types1-skiplist.d
new file mode 100644
index 00000000000..52c10fa501d
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types1-skiplist.d
@@ -0,0 +1,5 @@
+
+*: file format binary
+
+Contents of section .data:
+ 0000 00100000 00000000 *
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-types1-typelist.d b/ld/testsuite/ld-pe/pdb-types1-typelist.d
new file mode 100644
index 00000000000..ff2d91c311e
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types1-typelist.d
@@ -0,0 +1,60 @@
+
+*: file format binary
+
+Contents of section .data:
+ 0000 0a000110 12000000 02000000 0a000110 ................
+ 0010 12000000 01000000 0a000110 22000000 ............"...
+ 0020 02000000 0a000110 74000000 03000000 ........t.......
+ 0030 0a000210 01100000 0c000100 0a000210 ................
+ 0040 02100000 0a800000 12000112 03000000 ................
+ 0050 01100000 02100000 03100000 0e000810 ................
+ 0060 02100000 00000300 06100000 0e000315 ................
+ 0070 04100000 74000000 180000f1 0a000512 ....t...........
+ 0080 75000000 0100f2f1 0a000512 75000000 u...........u...
+ 0090 1f01f2f1 22000312 0d150300 09100000 ...."...........
+ 00a0 00006e75 6d3100f1 0d150300 0a100000 ..num1..........
+ 00b0 00006e75 6d3200f1 22000515 02000000 ..num2..".......
+ 00c0 0b100000 00000000 00000000 04003c75 ..............<u
+ 00d0 6e6e616d 65642d74 61673e00 1e000515 nnamed-tag>.....
+ 00e0 00008002 00000000 00000000 00000000 ................
+ 00f0 0000666f 6f006261 7200f2f1 0a000210 ..foo.bar.......
+ 0100 0d100000 0c000100 06000112 00000000 ................
+ 0110 1a000910 02100000 0d100000 0e100000 ................
+ 0120 00000000 0f100000 00000000 1a000910 ................
+ 0130 02100000 0d100000 0e100000 00000300 ................
+ 0140 06100000 00000000 12000612 00000000 ................
+ 0150 10100000 00000000 11100000 32000312 ............2...
+ 0160 0d150300 75000000 00006e75 6d00f2f1 ....u.....num...
+ 0170 11150000 10100000 6d657468 6f6400f1 ........method..
+ 0180 0f150200 12100000 6d657468 6f643200 ........method2.
+ 0190 1e000515 02000002 13100000 00000000 ................
+ 01a0 00000000 0400666f 6f006261 7200f2f1 ......foo.bar...
+ 01b0 22000312 0d150300 75000000 00006e75 ".......u.....nu
+ 01c0 6d3100f1 0d150300 10000000 00006e75 m1............nu
+ 01d0 6d3200f1 1a000615 02000000 15100000 m2..............
+ 01e0 04003c75 6e6e616d 65642d74 61673e00 ..<unnamed-tag>.
+ 01f0 16000615 00008002 00000000 00006261 ..............ba
+ 0200 7a007175 7800f2f1 16000615 02000002 z.qux...........
+ 0210 15100000 04006261 7a007175 7800f2f1 ......baz.qux...
+ 0220 52000312 02150300 00007265 6400f2f1 R.........red...
+ 0230 02150300 01006772 65656e00 02150300 ......green.....
+ 0240 0380ffff ffff626c 756500f1 02150300 ......blue......
+ 0250 02800080 79656c6c 6f7700f1 02150300 ....yellow......
+ 0260 0a800000 00000100 00007075 72706c65 ..........purple
+ 0270 00f3f2f1 1e000715 00008002 23000000 ............#...
+ 0280 00000000 636f6c6f 75720063 6f6c6f75 ....colour.colou
+ 0290 723200f1 1e000715 05000002 23000000 r2..........#...
+ 02a0 19100000 636f6c6f 75720063 6f6c6f75 ....colour.colou
+ 02b0 723200f1 0a000312 04140000 19100000 r2..............
+ 02c0 06000a00 010000f1 0a000210 1d100000 ................
+ 02d0 0c000100 0a000312 02150300 00006100 ..............a.
+ 02e0 22000715 01000800 75000000 1f100000 ".......u.......
+ 02f0 71757578 3a3a6e65 73746564 5f656e75 quux::nested_enu
+ 0300 6d00f2f1 52000312 00140000 14100000 m...R...........
+ 0310 0400f2f1 09140000 1e100000 01140000 ................
+ 0320 14100000 1e100000 00000000 0e150000 ................
+ 0330 02100000 73746174 69635f6d 656d6265 ....static_membe
+ 0340 7200f2f1 10150000 20100000 6e657374 r....... ...nest
+ 0350 65645f65 6e756d00 1a000515 01000000 ed_enum.........
+ 0360 21100000 00000000 00000000 04007175 !.............qu
+ 0370 757800f1 ux..
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-types1a.s b/ld/testsuite/ld-pe/pdb-types1a.s
new file mode 100644
index 00000000000..a2ee9a9972f
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types1a.s
@@ -0,0 +1,27 @@
+.equ CV_SIGNATURE_C13, 4
+
+.equ T_LONG, 0x0012
+
+.equ LF_MODIFIER, 0x1001
+
+.section ".debug$T", "rn"
+
+.long CV_SIGNATURE_C13
+
+# Type 1000, volatile long
+.mod1:
+.short .mod2 - .mod1 - 2
+.short LF_MODIFIER
+.long T_LONG
+.short 2 # volatile
+.p2align 2
+
+# Type 1001, const long
+.mod2:
+.short .types_end - .mod2 - 2
+.short LF_MODIFIER
+.long T_LONG
+.short 1 # const
+.p2align 2
+
+.types_end:
diff --git a/ld/testsuite/ld-pe/pdb-types1b.s b/ld/testsuite/ld-pe/pdb-types1b.s
new file mode 100644
index 00000000000..89ee6e3840f
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types1b.s
@@ -0,0 +1,461 @@
+.equ CV_SIGNATURE_C13, 4
+
+.equ T_CHAR, 0x0010
+.equ T_LONG, 0x0012
+.equ T_ULONG, 0x0022
+.equ T_INT4, 0x0074
+.equ T_UINT4, 0x0075
+.equ T_UQUAD, 0x0023
+
+.equ LF_VTSHAPE, 0x000a
+.equ LF_MODIFIER, 0x1001
+.equ LF_POINTER, 0x1002
+.equ LF_PROCEDURE, 0x1008
+.equ LF_MFUNCTION, 0x1009
+.equ LF_ARGLIST, 0x1201
+.equ LF_FIELDLIST, 0x1203
+.equ LF_BITFIELD, 0x1205
+.equ LF_METHODLIST, 0x1206
+.equ LF_BCLASS, 0x1400
+.equ LF_VBCLASS, 0x1401
+.equ LF_INDEX, 0x1404
+.equ LF_VFUNCTAB, 0x1409
+.equ LF_ENUMERATE, 0x1502
+.equ LF_ARRAY, 0x1503
+.equ LF_STRUCTURE, 0x1505
+.equ LF_UNION, 0x1506
+.equ LF_ENUM, 0x1507
+.equ LF_MEMBER, 0x150d
+.equ LF_STMEMBER, 0x150e
+.equ LF_METHOD, 0x150f
+.equ LF_NESTTYPE, 0x1510
+.equ LF_ONEMETHOD, 0x1511
+
+.equ LF_USHORT, 0x8002
+.equ LF_LONG, 0x8003
+.equ LF_UQUADWORD, 0x800a
+
+.equ CV_PTR_NEAR32, 0xa
+.equ CV_PTR_64, 0xc
+
+.section ".debug$T", "rn"
+
+.long CV_SIGNATURE_C13
+
+# Type 1000, const long
+.mod1:
+.short .mod2 - .mod1 - 2
+.short LF_MODIFIER
+.long T_LONG
+.short 1 # const
+.p2align 2
+
+# Type 1001, volatile unsigned long
+.mod2:
+.short .mod3 - .mod2 - 2
+.short LF_MODIFIER
+.long T_ULONG
+.short 2 # volatile
+.p2align 2
+
+# Type 1002, const volatile int
+.mod3:
+.short .ptr1 - .mod3 - 2
+.short LF_MODIFIER
+.long T_INT4
+.short 3 # const volatile
+.p2align 2
+
+# Type 1003, const long * (64-bit pointer)
+.ptr1:
+.short .ptr2 - .ptr1 - 2
+.short LF_POINTER
+.long 0x1000
+.long (8 << 13) | CV_PTR_64
+
+# Type 1004, volatile unsigned long * (32-bit pointer)
+.ptr2:
+.short .arglist1 - .ptr2 - 2
+.short LF_POINTER
+.long 0x1001
+.long (4 << 13) | CV_PTR_NEAR32
+
+# Type 1005, arg list of types 1000, 1001, 1002
+.arglist1:
+.short .proc1 - .arglist1 - 2
+.short LF_ARGLIST
+.long 3 # no. entries
+.long 0x1000
+.long 0x1001
+.long 0x1002
+
+# Type 1006, procedure, return type 1001, arg list 1005
+.proc1:
+.short .arr1 - .proc1 - 2
+.short LF_PROCEDURE
+.long 0x1001
+.byte 0 # calling convention
+.byte 0 # attributes
+.short 3 # no. parameters
+.long 0x1005
+
+# Type 1007, array[3] of const long *
+.arr1:
+.short .bitfield1 - .arr1 - 2
+.short LF_ARRAY
+.long 0x1003 # element type
+.long T_INT4 # index type
+.short 24 # length in bytes
+.byte 0 # name
+.byte 0xf1 # padding
+
+# Type 1008, bitfield of uint32_t, position 0, length 1
+.bitfield1:
+.short .bitfield2 - .bitfield1 - 2
+.short LF_BITFIELD
+.long T_UINT4
+.byte 1
+.byte 0
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1009, bitfield of uint32_t, position 1, length 31
+.bitfield2:
+.short .fieldlist1 - .bitfield2 - 2
+.short LF_BITFIELD
+.long T_UINT4
+.byte 31
+.byte 1
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 100a, field list (1008 as num1, 1009 as num2)
+.fieldlist1:
+.short .struct1 - .fieldlist1 - 2
+.short LF_FIELDLIST
+.short LF_MEMBER
+.short 3 # public
+.long 0x1008
+.short 0 # offset
+.asciz "num1"
+.byte 0xf1 # padding
+.short LF_MEMBER
+.short 3 # public
+.long 0x1009
+.short 0 # offset
+.asciz "num2"
+.byte 0xf1 # padding
+
+# Type 100b, anonymous struct, field list 100a
+.struct1:
+.short .struct2 - .struct1 - 2
+.short LF_STRUCTURE
+.short 2 # no. members
+.short 0 # property
+.long 0x100a # field list
+.long 0 # type derived from
+.long 0 # type of vshape table
+.short 4 # size
+.asciz "<unnamed-tag>"
+
+# Type 100c, forward declaration of struct foo
+.struct2:
+.short .ptr3 - .struct2 - 2
+.short LF_STRUCTURE
+.short 0 # no. members
+.short 0x280 # property (has unique name, forward declaration)
+.long 0 # field list
+.long 0 # type derived from
+.long 0 # type of vshape table
+.short 0 # size
+.asciz "foo" # name
+.asciz "bar" # unique name
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 100d, pointer to 100c
+.ptr3:
+.short .arglist2 - .ptr3 - 2
+.short LF_POINTER
+.long 0x100c
+.long (8 << 13) | CV_PTR_64
+
+# Type 100e, empty arg list
+.arglist2:
+.short .mfunc1 - .arglist2 - 2
+.short LF_ARGLIST
+.long 0 # no. entries
+
+# Type 100f, member function of 100c, return type 1001
+.mfunc1:
+.short .mfunc2 - .mfunc1 - 2
+.short LF_MFUNCTION
+.long 0x1001
+.long 0x100c
+.long 0x100d # type of "this" pointer
+.byte 0 # calling convention
+.byte 0 # attributes
+.short 0 # no. parameters
+.long 0x100e # arg list
+.long 0 # "this" adjustment
+
+# Type 1010, member function of 100c, return type 1001, arg list 1005
+.mfunc2:
+.short .methodlist1 - .mfunc2 - 2
+.short LF_MFUNCTION
+.long 0x1001
+.long 0x100c
+.long 0x100d # type of "this" pointer
+.byte 0 # calling convention
+.byte 0 # attributes
+.short 3 # no. parameters
+.long 0x1005 # arg list
+.long 0 # "this" adjustment
+
+# Type 1011, method list for both member functions 100f and 1010
+.methodlist1:
+.short .fieldlist2 - .methodlist1 - 2
+.short LF_METHODLIST
+.short 0 # attributes
+.short 0 # padding
+.long 0x100f
+.short 0 # attributes
+.short 0 # padding
+.long 0x1010
+
+# Type 1012, field list (uint32_t as num1)
+.fieldlist2:
+.short .struct3 - .fieldlist2 - 2
+.short LF_FIELDLIST
+.short LF_MEMBER
+.short 3 # public
+.long T_UINT4
+.short 0 # offset
+.asciz "num"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+.short LF_ONEMETHOD
+.short 0 # method attribute
+.long 0x100f # method type
+.asciz "method"
+.byte 0xf1
+.short LF_METHOD
+.short 2 # no. overloads
+.long 0x1011 # method list
+.asciz "method2"
+
+# Type 1013, struct foo, field list 1012
+.struct3:
+.short .fieldlist3 - .struct3 - 2
+.short LF_STRUCTURE
+.short 2 # no. members
+.short 0x200 # property (has unique name)
+.long 0x1012 # field list
+.long 0 # type derived from
+.long 0 # type of vshape table
+.short 4 # size
+.asciz "foo" # name
+.asciz "bar" # unique name
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1014, field list (uint32_t as num1, char as num2)
+.fieldlist3:
+.short .union1 - .fieldlist3 - 2
+.short LF_FIELDLIST
+.short LF_MEMBER
+.short 3 # public
+.long T_UINT4
+.short 0 # offset
+.asciz "num1"
+.byte 0xf1 # padding
+.short LF_MEMBER
+.short 3 # public
+.long T_CHAR
+.short 0 # offset
+.asciz "num2"
+.byte 0xf1 # padding
+
+# Type 1015, anonymous union (field list 1014)
+.union1:
+.short .union2 - .union1 - 2
+.short LF_UNION
+.short 2 # no. members
+.short 0 # property
+.long 0x1014
+.short 4 # size
+.asciz "<unnamed-tag>"
+
+# Type 1016, forward declaration of union baz
+.union2:
+.short .union3 - .union2 - 2
+.short LF_UNION
+.short 0 # no. members
+.short 0x280 # property (has unique name, forward declaration)
+.long 0 # field list
+.short 0 # size
+.asciz "baz"
+.asciz "qux"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1017, union baz (field list 1014)
+.union3:
+.short .fieldlist4 - .union3 - 2
+.short LF_UNION
+.short 2 # no. members
+.short 0x200 # property (has unique name, forward declaration)
+.long 0x1014 # field list
+.short 4 # size
+.asciz "baz"
+.asciz "qux"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1018, field list for enum (red = 0, green = 1, blue = -1, yellow = 0x8000, purple = 0x100000000)
+.fieldlist4:
+.short .enum1 - .fieldlist4 - 2
+.short LF_FIELDLIST
+.short LF_ENUMERATE
+.short 3 # public
+.short 0 # value
+.asciz "red"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+.short LF_ENUMERATE
+.short 3 # public
+.short 1 # value
+.asciz "green"
+.short LF_ENUMERATE
+.short 3 # public
+.short LF_LONG
+.long 0xffffffff # value
+.asciz "blue"
+.byte 0xf1 # padding
+.short LF_ENUMERATE
+.short 3 # public
+.short LF_USHORT
+.short 0x8000 # value
+.asciz "yellow"
+.byte 0xf1 # padding
+.short LF_ENUMERATE
+.short 3 # public
+.short LF_UQUADWORD
+.quad 0x100000000 # value
+.asciz "purple"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1019, forward reference to enum
+.enum1:
+.short .enum2 - .enum1 - 2
+.short LF_ENUM
+.short 0 # no. elements
+.short 0x280 # property (has unique name, forward ref)
+.long T_UQUAD # underlying type
+.long 0 # field list
+.asciz "colour"
+.asciz "colour2"
+.byte 0xf1 # padding
+
+# Type 101a, enum (field list 1018)
+.enum2:
+.short .fieldlist5 - .enum2 - 2
+.short LF_ENUM
+.short 5 # no. elements
+.short 0x200 # property (has unique name)
+.long T_UQUAD # underlying type
+.long 0x1018 # field list
+.asciz "colour"
+.asciz "colour2"
+.byte 0xf1 # padding
+
+# Type 101b, field list referencing other field list 1018
+.fieldlist5:
+.short .vtshape1 - .fieldlist5 - 2
+.short LF_FIELDLIST
+.short LF_INDEX
+.short 0 # padding
+.long 0x1018
+
+# Type 101c, virtual function table shape
+.vtshape1:
+.short .ptr4 - .vtshape1 - 2
+.short LF_VTSHAPE
+.short 1 # no. descriptors
+.byte 0 # descriptor (CV_VTS_near)
+.byte 0xf1 # padding
+
+# Type 101d, pointer to 101c
+.ptr4:
+.short .fieldlist6 - .ptr4 - 2
+.short LF_POINTER
+.long 0x101c
+.long (8 << 13) | CV_PTR_64
+
+# Type 101e, fieldlist for enum
+.fieldlist6:
+.short .enum3 - .fieldlist6 - 2
+.short LF_FIELDLIST
+.short LF_ENUMERATE
+.short 3 # public
+.short 0 # value
+.asciz "a"
+
+# Type 101f, nested enum
+.enum3:
+.short .fieldlist7 - .enum3 - 2
+.short LF_ENUM
+.short 1 # no. elements
+.short 0x8 # property (is nested)
+.long T_UINT4 # underlying type
+.long 0x101e # field list
+.asciz "quux::nested_enum"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1020, field list for struct quux
+.fieldlist7:
+.short .struct4 - .fieldlist7 - 2
+.short LF_FIELDLIST
+.short LF_BCLASS
+.short 0 # attributes
+.long 0x1013 # base class
+.short 4 # offset within class
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+.short LF_VFUNCTAB
+.short 0 # padding
+.long 0x101d # pointer to vtshape
+.short LF_VBCLASS
+.short 0 # attribute
+.long 0x1013 # type index of direct virtual base class
+.long 0x101d # type index of virtual base pointer
+.short 0 # virtual base pointer offset
+.short 0 # virtual base offset from vbtable
+.short LF_STMEMBER
+.short 0 # attribute
+.long 0x1001 # volatile unsigned long
+.asciz "static_member"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+.short LF_NESTTYPE
+.short 0 # padding
+.long 0x101f # enum type
+.asciz "nested_enum"
+
+# Type 1021, struct quux, field list 1020
+.struct4:
+.short .types_end - .struct4 - 2
+.short LF_STRUCTURE
+.short 1 # no. members
+.short 0 # property
+.long 0x1020 # field list
+.long 0 # type derived from
+.long 0 # type of vshape table
+.short 4 # size
+.asciz "quux" # name
+.byte 0xf1 # padding
+
+.types_end:
diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp
index 2e5f83477aa..5661438c9e9 100644
--- a/ld/testsuite/ld-pe/pdb.exp
+++ b/ld/testsuite/ld-pe/pdb.exp
@@ -977,7 +977,179 @@ proc test4 { } {
}
}
+proc test5 { } {
+ global as
+ global ar
+ global ld
+ global objdump
+ global srcdir
+ global subdir
+
+ if ![ld_assemble $as $srcdir/$subdir/pdb-types1a.s tmpdir/pdb-types1a.o] {
+ unsupported "Build pdb-types1a.o"
+ return
+ }
+
+ if ![ld_assemble $as $srcdir/$subdir/pdb-types1b.s tmpdir/pdb-types1b.o] {
+ unsupported "Build pdb-types1b.o"
+ return
+ }
+
+ if ![ld_link $ld "tmpdir/pdb-types1.exe" "--pdb=tmpdir/pdb-types1.pdb tmpdir/pdb-types1a.o tmpdir/pdb-types1b.o"] {
+ unsupported "Create PE image with PDB file"
+ return
+ }
+
+ set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types1.pdb 0002"]
+
+ if ![string match "" $exec_output] {
+ fail "Could not extract TPI stream"
+ return
+ } else {
+ pass "Extracted TPI stream"
+ }
+
+ # check values in TPI header, and save anything interesting
+
+ set fi [open tmpdir/0002]
+ fconfigure $fi -translation binary
+
+ seek $fi 8 current
+
+ set data [read $fi 4]
+ binary scan $data i first_type
+
+ if { $first_type != 0x1000 } {
+ fail "Incorrect first type value in TPI stream."
+ } else {
+ pass "Correct first type value in TPI stream."
+ }
+
+ set data [read $fi 4]
+ binary scan $data i end_type
+
+ # end_type is one greater than the last type in the stream
+ if { $end_type != 0x1023 } {
+ fail "Incorrect end type value in TPI stream."
+ } else {
+ pass "Correct end type value in TPI stream."
+ }
+
+ set data [read $fi 4]
+ binary scan $data i type_list_size
+
+ set data [read $fi 2]
+ binary scan $data s hash_stream_index
+
+ seek $fi 2 current
+
+ set data [read $fi 4]
+ binary scan $data i hash_size
+
+ if { $hash_size != 4 } {
+ fail "Incorrect hash size in TPI stream."
+ } else {
+ pass "Correct hash size in TPI stream."
+ }
+
+ set data [read $fi 4]
+ binary scan $data i num_buckets
+
+ if { $num_buckets != 0x3ffff } {
+ fail "Incorrect number of buckets in TPI stream."
+ } else {
+ pass "Correct number of buckets in TPI stream."
+ }
+
+ set data [read $fi 4]
+ binary scan $data i hash_list_offset
+
+ set data [read $fi 4]
+ binary scan $data i hash_list_size
+
+ set data [read $fi 4]
+ binary scan $data i skip_list_offset
+
+ set data [read $fi 4]
+ binary scan $data i skip_list_size
+
+ seek $fi 8 current
+
+ set type_list [read $fi $type_list_size]
+
+ close $fi
+
+ set fi [open tmpdir/pdb-types1-typelist w]
+ fconfigure $fi -translation binary
+ puts -nonewline $fi $type_list
+ close $fi
+
+ # check type list
+
+ set exp [file_contents "$srcdir/$subdir/pdb-types1-typelist.d"]
+ set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types1-typelist"]
+ if ![string match $exp $got] {
+ fail "Incorrect type list in TPI stream."
+ } else {
+ pass "Correct type list in TPI stream."
+ }
+
+ # extract hash list and skip list
+
+ set index_str [format "%04x" $hash_stream_index]
+
+ set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types1.pdb $index_str"]
+
+ if ![string match "" $exec_output] {
+ fail "Could not extract TPI hash stream."
+ } else {
+ pass "Extracted TPI hash stream."
+ }
+
+ set fi [open tmpdir/$index_str]
+ fconfigure $fi -translation binary
+
+ seek $fi $hash_list_offset
+ set hash_list [read $fi $hash_list_size]
+
+ seek $fi $skip_list_offset
+ set skip_list [read $fi $skip_list_size]
+
+ close $fi
+
+ # check hash list
+
+ set fi [open tmpdir/pdb-types1-hashlist w]
+ fconfigure $fi -translation binary
+ puts -nonewline $fi $hash_list
+ close $fi
+
+ set exp [file_contents "$srcdir/$subdir/pdb-types1-hashlist.d"]
+ set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types1-hashlist"]
+ if ![string match $exp $got] {
+ fail "Incorrect hash list in TPI stream."
+ } else {
+ pass "Correct hash list in TPI stream."
+ }
+
+ # check skip list
+
+ set fi [open tmpdir/pdb-types1-skiplist w]
+ fconfigure $fi -translation binary
+ puts -nonewline $fi $skip_list
+ close $fi
+
+ set exp [file_contents "$srcdir/$subdir/pdb-types1-skiplist.d"]
+ set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types1-skiplist"]
+ if ![string match $exp $got] {
+ fail "Incorrect skip list in TPI stream."
+ } else {
+ pass "Correct skip list in TPI stream."
+ }
+}
+
test1
test2
test3
test4
+test5
--
2.37.4
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 06/10] ld: Write types into IPI stream of PDB
2022-12-09 1:52 [PATCH 01/10] ld: Generate PDB string table Mark Harmstone
` (3 preceding siblings ...)
2022-12-09 1:52 ` [PATCH 05/10] ld: Write types into TPI stream of PDB Mark Harmstone
@ 2022-12-09 1:52 ` Mark Harmstone
2022-12-09 1:52 ` [PATCH 07/10] ld: Parse LF_UDT_SRC_LINE records when creating PDB file Mark Harmstone
` (4 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Mark Harmstone @ 2022-12-09 1:52 UTC (permalink / raw)
To: binutils, nickc; +Cc: Mark Harmstone
---
ld/pdb.c | 204 +++++++++++++++++++--
ld/pdb.h | 45 ++++-
ld/testsuite/ld-pe/pdb-types2-hashlist.d | 8 +
ld/testsuite/ld-pe/pdb-types2-skiplist.d | 5 +
ld/testsuite/ld-pe/pdb-types2-typelist.d | 20 ++
ld/testsuite/ld-pe/pdb-types2a.s | 42 +++++
ld/testsuite/ld-pe/pdb-types2b.s | 221 +++++++++++++++++++++++
ld/testsuite/ld-pe/pdb.exp | 172 ++++++++++++++++++
8 files changed, 699 insertions(+), 18 deletions(-)
create mode 100644 ld/testsuite/ld-pe/pdb-types2-hashlist.d
create mode 100644 ld/testsuite/ld-pe/pdb-types2-skiplist.d
create mode 100644 ld/testsuite/ld-pe/pdb-types2-typelist.d
create mode 100644 ld/testsuite/ld-pe/pdb-types2a.s
create mode 100644 ld/testsuite/ld-pe/pdb-types2b.s
diff --git a/ld/pdb.c b/ld/pdb.c
index 4bb19420f95..69fade50814 100644
--- a/ld/pdb.c
+++ b/ld/pdb.c
@@ -1123,13 +1123,16 @@ is_name_anonymous (char *name, size_t len)
already seen add it to types (for TPI types) or ids (for IPI types). */
static bool
handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
- uint32_t num_types, struct types *types)
+ uint32_t num_types, struct types *types,
+ struct types *ids)
{
uint16_t size, type;
void **slot;
hashval_t hash;
bool other_hash = false;
uint32_t cv_hash;
+ struct types *t;
+ bool ipi = false;
size = bfd_getl16 (data) + sizeof (uint16_t);
type = bfd_getl16 (data + sizeof (uint16_t));
@@ -1910,6 +1913,168 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
/* Does not reference any types, nothing to be done. */
break;
+ case LF_STRING_ID:
+ {
+ struct lf_string_id *str = (struct lf_string_id *) data;
+ size_t string_len;
+
+ if (size < offsetof (struct lf_string_id, string))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_STRING_ID\n"));
+ return false;
+ }
+
+ if (!remap_type (&str->substring, map, type_num, num_types))
+ return false;
+
+ string_len = strnlen (str->string,
+ size - offsetof (struct lf_string_id, string));
+
+ if (string_len == size - offsetof (struct lf_string_id, string))
+ {
+ einfo (_("%P: warning: string for LF_STRING_ID has no"
+ " terminating zero\n"));
+ return false;
+ }
+
+ ipi = true;
+
+ break;
+ }
+
+ case LF_SUBSTR_LIST:
+ {
+ uint32_t num_entries;
+ struct lf_arglist *ssl = (struct lf_arglist *) data;
+
+ if (size < offsetof (struct lf_arglist, args))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_SUBSTR_LIST\n"));
+ return false;
+ }
+
+ num_entries = bfd_getl32 (&ssl->num_entries);
+
+ if (size < offsetof (struct lf_arglist, args)
+ + (num_entries * sizeof (uint32_t)))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_SUBSTR_LIST\n"));
+ return false;
+ }
+
+ for (uint32_t i = 0; i < num_entries; i++)
+ {
+ if (!remap_type (&ssl->args[i], map, type_num, num_types))
+ return false;
+ }
+
+ ipi = true;
+
+ break;
+ }
+
+ case LF_BUILDINFO:
+ {
+ uint16_t num_entries;
+ struct lf_build_info *bi = (struct lf_build_info *) data;
+
+ if (size < offsetof (struct lf_build_info, strings))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_BUILDINFO\n"));
+ return false;
+ }
+
+ num_entries = bfd_getl16 (&bi->count);
+
+ if (size < offsetof (struct lf_build_info, strings)
+ + (num_entries * sizeof (uint32_t)))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_BUILDINFO\n"));
+ return false;
+ }
+
+ for (uint32_t i = 0; i < num_entries; i++)
+ {
+ if (!remap_type (&bi->strings[i], map, type_num, num_types))
+ return false;
+ }
+
+ ipi = true;
+
+ break;
+ }
+
+ case LF_FUNC_ID:
+ {
+ struct lf_func_id *func = (struct lf_func_id *) data;
+ size_t name_len;
+
+ if (size < offsetof (struct lf_func_id, name))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_FUNC_ID\n"));
+ return false;
+ }
+
+ if (!remap_type (&func->parent_scope, map, type_num, num_types))
+ return false;
+
+ if (!remap_type (&func->function_type, map, type_num, num_types))
+ return false;
+
+ name_len = strnlen (func->name,
+ size - offsetof (struct lf_func_id, name));
+
+ if (name_len == size - offsetof (struct lf_func_id, name))
+ {
+ einfo (_("%P: warning: string for LF_FUNC_ID has no"
+ " terminating zero\n"));
+ return false;
+ }
+
+ ipi = true;
+
+ break;
+ }
+
+ case LF_MFUNC_ID:
+ {
+ struct lf_mfunc_id *mfunc = (struct lf_mfunc_id *) data;
+ size_t name_len;
+
+ if (size < offsetof (struct lf_mfunc_id, name))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_MFUNC_ID\n"));
+ return false;
+ }
+
+ if (!remap_type (&mfunc->parent_type, map, type_num, num_types))
+ return false;
+
+ if (!remap_type (&mfunc->function_type, map, type_num, num_types))
+ return false;
+
+ name_len = strnlen (mfunc->name,
+ size - offsetof (struct lf_mfunc_id, name));
+
+ if (name_len == size - offsetof (struct lf_mfunc_id, name))
+ {
+ einfo (_("%P: warning: string for LF_MFUNC_ID has no"
+ " terminating zero\n"));
+ return false;
+ }
+
+ ipi = true;
+
+ break;
+ }
+
default:
einfo (_("%P: warning: unrecognized CodeView type %v\n"), type);
return false;
@@ -1917,7 +2082,9 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
hash = iterative_hash (data, size, 0);
- slot = htab_find_slot_with_hash (types->hashmap, data, hash, INSERT);
+ t = ipi ? ids : types;
+
+ slot = htab_find_slot_with_hash (t->hashmap, data, hash, INSERT);
if (!slot)
return false;
@@ -1930,7 +2097,7 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
e = (struct type_entry *) *slot;
e->next = NULL;
- e->index = types->num_types;
+ e->index = t->num_types;
if (other_hash)
e->cv_hash = cv_hash;
@@ -1939,16 +2106,16 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
memcpy (e->data, data, size);
- if (types->last)
- types->last->next = e;
+ if (t->last)
+ t->last->next = e;
else
- types->first = e;
+ t->first = e;
- types->last = e;
+ t->last = e;
map[type_num] = e;
- types->num_types++;
+ t->num_types++;
}
else /* duplicate */
{
@@ -1961,7 +2128,8 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
/* Parse the .debug$T section of a module, and pass any type definitions
found to handle_type. */
static bool
-handle_debugt_section (asection *s, bfd *mod, struct types *types)
+handle_debugt_section (asection *s, bfd *mod, struct types *types,
+ struct types *ids)
{
bfd_byte *data = NULL;
size_t off;
@@ -2018,7 +2186,7 @@ handle_debugt_section (asection *s, bfd *mod, struct types *types)
size = bfd_getl16 (data + off);
- if (!handle_type (data + off, map, type_num, num_types, types))
+ if (!handle_type (data + off, map, type_num, num_types, types, ids))
{
free (data);
free (map);
@@ -2043,7 +2211,8 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
struct string_table *strings,
uint32_t *c13_info_size,
struct mod_source_files *mod_source,
- bfd *abfd, struct types *types)
+ bfd *abfd, struct types *types,
+ struct types *ids)
{
uint8_t int_buf[sizeof (uint32_t)];
uint8_t *c13_info = NULL;
@@ -2067,7 +2236,7 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
}
else if (!strcmp (s->name, ".debug$T") && s->size >= sizeof (uint32_t))
{
- if (!handle_debugt_section (s, mod, types))
+ if (!handle_debugt_section (s, mod, types, ids))
{
free (c13_info);
free (mod_source->files);
@@ -2112,7 +2281,7 @@ static bool
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 *types, struct types *ids)
{
uint8_t *ptr;
unsigned int mod_num;
@@ -2201,7 +2370,7 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
if (!populate_module_stream (stream, in, &sym_byte_size,
strings, &c13_info_size,
&source->mods[mod_num], abfd,
- types))
+ types, ids))
{
for (unsigned int i = 0; i < source->mod_count; i++)
{
@@ -2524,7 +2693,8 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
uint16_t sym_rec_stream_num,
uint16_t publics_stream_num,
struct string_table *strings,
- struct types *types)
+ struct types *types,
+ struct types *ids)
{
struct pdb_dbi_stream_header h;
struct optional_dbg_header opt;
@@ -2536,7 +2706,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
source.mods = NULL;
if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size,
- strings, &source, types))
+ strings, &source, types, ids))
return false;
if (!create_section_contrib_substream (abfd, &sc, &sc_size))
@@ -3209,7 +3379,7 @@ 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))
+ &strings, &types, &ids))
{
einfo (_("%P: warning: cannot populate DBI stream "
"in PDB file: %E\n"));
diff --git a/ld/pdb.h b/ld/pdb.h
index ecc26c1c87a..7d87a3fef07 100644
--- a/ld/pdb.h
+++ b/ld/pdb.h
@@ -54,6 +54,11 @@
#define LF_METHOD 0x150f
#define LF_NESTTYPE 0x1510
#define LF_ONEMETHOD 0x1511
+#define LF_FUNC_ID 0x1601
+#define LF_MFUNC_ID 0x1602
+#define LF_BUILDINFO 0x1603
+#define LF_SUBSTR_LIST 0x1604
+#define LF_STRING_ID 0x1605
#define LF_CHAR 0x8000
#define LF_SHORT 0x8001
@@ -265,7 +270,7 @@ struct lf_pointer
uint32_t attributes;
} ATTRIBUTE_PACKED;
-/* lfArgList in cvinfo.h */
+/* lfArgList in cvinfo.h (used for both LF_ARGLIST and LF_SUBSTR_LIST) */
struct lf_arglist
{
uint16_t size;
@@ -474,6 +479,44 @@ struct lf_nest_type
char name[];
} ATTRIBUTE_PACKED;
+/* lfStringId in cvinfo.h */
+struct lf_string_id
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t substring;
+ char string[];
+} ATTRIBUTE_PACKED;
+
+/* lfBuildInfo in cvinfo.h */
+struct lf_build_info
+{
+ uint16_t size;
+ uint16_t kind;
+ uint16_t count;
+ uint32_t strings[];
+} ATTRIBUTE_PACKED;
+
+/* lfFuncId in cvinfo.h */
+struct lf_func_id
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t parent_scope;
+ uint32_t function_type;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* lfMFuncId in cvinfo.h */
+struct lf_mfunc_id
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t parent_type;
+ uint32_t function_type;
+ char name[];
+} ATTRIBUTE_PACKED;
+
extern bool create_pdb_file (bfd *, const char *, const unsigned char *);
#endif
diff --git a/ld/testsuite/ld-pe/pdb-types2-hashlist.d b/ld/testsuite/ld-pe/pdb-types2-hashlist.d
new file mode 100644
index 00000000000..71d9045fbbd
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types2-hashlist.d
@@ -0,0 +1,8 @@
+
+*: file format binary
+
+Contents of section .data:
+ 0000 75cf0100 8d660300 f2a20300 aea00000 *
+ 0010 ef990300 223d0000 d6b60000 24070100 *
+ 0020 7f220100 f6d10200 16100200 010a0300 *
+ 0030 0b4f0300 12690300 a56d0300 *
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-types2-skiplist.d b/ld/testsuite/ld-pe/pdb-types2-skiplist.d
new file mode 100644
index 00000000000..52c10fa501d
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types2-skiplist.d
@@ -0,0 +1,5 @@
+
+*: file format binary
+
+Contents of section .data:
+ 0000 00100000 00000000 *
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-types2-typelist.d b/ld/testsuite/ld-pe/pdb-types2-typelist.d
new file mode 100644
index 00000000000..d0fd26e0fa4
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types2-typelist.d
@@ -0,0 +1,20 @@
+
+*: file format binary
+
+Contents of section .data:
+ 0000 0e000516 00000000 74657374 00f3f2f1 ........test....
+ 0010 0a000516 00000000 666f6f00 0a000516 ........foo.....
+ 0020 00000000 62617200 0e000416 02000000 ....bar.........
+ 0030 01100000 02100000 0a000516 03100000 ................
+ 0040 62617a00 0e000516 00000000 2f746d70 baz........./tmp
+ 0050 00f3f2f1 0a000516 00000000 67636300 ............gcc.
+ 0060 0e000516 00000000 746d702e 6300f2f1 ........tmp.c...
+ 0070 0e000516 00000000 746d702e 70646200 ........tmp.pdb.
+ 0080 12000516 00000000 2d67636f 64657669 ........-gcodevi
+ 0090 657700f1 1a000316 05000510 00000610 ew..............
+ 00a0 00000710 00000810 00000910 0000f2f1 ................
+ 00b0 12000516 00000000 6e616d65 73706163 ........namespac
+ 00c0 6500f2f1 12000116 00000000 01100000 e...............
+ 00d0 66756e63 3100f2f1 12000116 0b100000 func1...........
+ 00e0 01100000 66756e63 3200f2f1 12000216 ....func2.......
+ 00f0 02100000 04100000 6d657468 6f6400f1 ........method..
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-types2a.s b/ld/testsuite/ld-pe/pdb-types2a.s
new file mode 100644
index 00000000000..e11843ae575
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types2a.s
@@ -0,0 +1,42 @@
+.equ CV_SIGNATURE_C13, 4
+
+.equ T_VOID, 0x0003
+.equ T_INT4, 0x0074
+
+.equ LF_PROCEDURE, 0x1008
+.equ LF_MFUNCTION, 0x1009
+.equ LF_POINTER, 0x1002
+.equ LF_ARGLIST, 0x1201
+.equ LF_FIELDLIST, 0x1203
+.equ LF_CLASS, 0x1504
+.equ LF_ONEMETHOD, 0x1511
+.equ LF_FUNC_ID, 0x1601
+.equ LF_MFUNC_ID, 0x1602
+.equ LF_BUILDINFO, 0x1603
+.equ LF_SUBSTR_LIST, 0x1604
+.equ LF_STRING_ID, 0x1605
+
+.equ CV_PTR_64, 0xc
+
+.section ".debug$T", "rn"
+
+.long CV_SIGNATURE_C13
+
+# Type 1000, string "test"
+.string1:
+.short .string2 - .string1 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "test"
+.byte 0xf3
+.byte 0xf2
+.byte 0xf1
+
+# Type 1001, string "foo"
+.string2:
+.short .types_end - .string2 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "foo"
+
+.types_end:
diff --git a/ld/testsuite/ld-pe/pdb-types2b.s b/ld/testsuite/ld-pe/pdb-types2b.s
new file mode 100644
index 00000000000..33541729f63
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types2b.s
@@ -0,0 +1,221 @@
+.equ CV_SIGNATURE_C13, 4
+
+.equ T_VOID, 0x0003
+.equ T_INT4, 0x0074
+
+.equ LF_PROCEDURE, 0x1008
+.equ LF_MFUNCTION, 0x1009
+.equ LF_POINTER, 0x1002
+.equ LF_ARGLIST, 0x1201
+.equ LF_FIELDLIST, 0x1203
+.equ LF_CLASS, 0x1504
+.equ LF_ONEMETHOD, 0x1511
+.equ LF_FUNC_ID, 0x1601
+.equ LF_MFUNC_ID, 0x1602
+.equ LF_BUILDINFO, 0x1603
+.equ LF_SUBSTR_LIST, 0x1604
+.equ LF_STRING_ID, 0x1605
+
+.equ CV_PTR_64, 0xc
+
+.section ".debug$T", "rn"
+
+.long CV_SIGNATURE_C13
+
+# Type 1000, string "foo"
+.string1:
+.short .string2 - .string1 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "foo"
+
+# Type 1001, string "bar"
+.string2:
+.short .substrlist1 - .string2 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "bar"
+
+# Type 1002, substr list of "foo" and "bar"
+.substrlist1:
+.short .string3 - .substrlist1 - 2
+.short LF_SUBSTR_LIST
+.long 2 # count
+.long 0x1000
+.long 0x1001
+
+# Type 1003, string "baz" referencing substr list 1002
+.string3:
+.short .string4 - .string3 - 2
+.short LF_STRING_ID
+.long 0x1002
+.asciz "baz"
+
+# Type 1004, string "/tmp" (build directory)
+.string4:
+.short .string5 - .string4 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "/tmp"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1005, string "gcc" (compiler)
+.string5:
+.short .string6 - .string5 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "gcc"
+
+# Type 1006, string "tmp.c" (source file)
+.string6:
+.short .string7 - .string6 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "tmp.c"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1007, string "tmp.pdb" (PDB file)
+.string7:
+.short .string8 - .string7 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "tmp.pdb"
+
+# Type 1008, string "-gcodeview" (command arguments)
+.string8:
+.short .buildinfo1 - .string8 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "-gcodeview"
+.byte 0xf1 # padding
+
+# The 1009, build info
+.buildinfo1:
+.short .string9 - .buildinfo1 - 2
+.short LF_BUILDINFO
+.short 5 # count
+.long 0x1004 # build directory
+.long 0x1005 # compiler
+.long 0x1006 # source file
+.long 0x1007 # PDB file
+.long 0x1008 # command arguments
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 100a, string "namespace"
+.string9:
+.short .arglist1 - .string9 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "namespace"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 100b, arg list of type T_INT4
+.arglist1:
+.short .proc1 - .arglist1 - 2
+.short LF_ARGLIST
+.long 1 # no. entries
+.long T_INT4
+
+# Type 100c, procedure, return type T_VOID, arg list 100b
+.proc1:
+.short .func1 - .proc1 - 2
+.short LF_PROCEDURE
+.long T_VOID
+.byte 0 # calling convention
+.byte 0 # attributes
+.short 1 # no. parameters
+.long 0x100b
+
+# Type 100d, function "func1"
+.func1:
+.short .func2 - .func1 - 2
+.short LF_FUNC_ID
+.long 0 # parent scope
+.long 0x100c # type
+.asciz "func1"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 100e, function "func2" within scope "namespace"
+.func2:
+.short .class1 - .func2 - 2
+.short LF_FUNC_ID
+.long 0x100a # parent scope
+.long 0x100c # type
+.asciz "func2"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 100f, forward declaration of class foo
+.class1:
+.short .ptr1 - .class1 - 2
+.short LF_CLASS
+.short 0 # no. members
+.short 0x80 # property (has unique name, forward declaration)
+.long 0 # field list
+.long 0 # type derived from
+.long 0 # type of vshape table
+.short 0 # size
+.asciz "foo" # name
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1010, pointer to 100f
+.ptr1:
+.short .mfunction1 - .ptr1 - 2
+.short LF_POINTER
+.long 0x100f
+.long (8 << 13) | CV_PTR_64
+
+# Type 1011, member function of 100f, return type void, arg list 100b
+.mfunction1:
+.short .fieldlist1 - .mfunction1 - 2
+.short LF_MFUNCTION
+.long T_VOID
+.long 0x100f
+.long 0x1010 # type of "this" pointer
+.byte 0 # calling convention
+.byte 0 # attributes
+.short 1 # no. parameters
+.long 0x100b # arg list
+.long 0 # "this" adjustment
+
+# Type 1012, field list for class foo
+.fieldlist1:
+.short .class2 - .fieldlist1 - 2
+.short LF_FIELDLIST
+.short LF_ONEMETHOD
+.short 0 # method attribute
+.long 0x1010 # method type
+.asciz "method"
+.byte 0xf1
+
+# Type 1013, actual declaration of class foo
+.class2:
+.short .mfunc1 - .class2 - 2
+.short LF_CLASS
+.short 0 # no. members
+.short 0 # property
+.long 0x1012 # field list
+.long 0 # type derived from
+.long 0 # type of vshape table
+.short 0 # size
+.asciz "foo" # name
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1014, function "method" within class "foo"
+.mfunc1:
+.short .types_end - .mfunc1 - 2
+.short LF_MFUNC_ID
+.long 0x100f # parent class
+.long 0x1011 # function type
+.asciz "method"
+.byte 0xf1 # padding
+
+.types_end:
diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp
index 5661438c9e9..9753216f544 100644
--- a/ld/testsuite/ld-pe/pdb.exp
+++ b/ld/testsuite/ld-pe/pdb.exp
@@ -1148,8 +1148,180 @@ proc test5 { } {
}
}
+proc test6 { } {
+ global as
+ global ar
+ global ld
+ global objdump
+ global srcdir
+ global subdir
+
+ if ![ld_assemble $as $srcdir/$subdir/pdb-types2a.s tmpdir/pdb-types2a.o] {
+ unsupported "Build pdb-types2a.o"
+ return
+ }
+
+ if ![ld_assemble $as $srcdir/$subdir/pdb-types2b.s tmpdir/pdb-types2b.o] {
+ unsupported "Build pdb-types2b.o"
+ return
+ }
+
+ if ![ld_link $ld "tmpdir/pdb-types2.exe" "--pdb=tmpdir/pdb-types2.pdb tmpdir/pdb-types2a.o tmpdir/pdb-types2b.o"] {
+ unsupported "Create PE image with PDB file"
+ return
+ }
+
+ set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types2.pdb 0004"]
+
+ if ![string match "" $exec_output] {
+ fail "Could not extract IPI stream"
+ return
+ } else {
+ pass "Extracted IPI stream"
+ }
+
+ # check values in IPI header, and save anything interesting
+
+ set fi [open tmpdir/0004]
+ fconfigure $fi -translation binary
+
+ seek $fi 8 current
+
+ set data [read $fi 4]
+ binary scan $data i first_type
+
+ if { $first_type != 0x1000 } {
+ fail "Incorrect first type value in IPI stream."
+ } else {
+ pass "Correct first type value in IPI stream."
+ }
+
+ set data [read $fi 4]
+ binary scan $data i end_type
+
+ # end_type is one greater than the last type in the stream
+ if { $end_type != 0x100f } {
+ fail "Incorrect end type value in IPI stream."
+ } else {
+ pass "Correct end type value in IPI stream."
+ }
+
+ set data [read $fi 4]
+ binary scan $data i type_list_size
+
+ set data [read $fi 2]
+ binary scan $data s hash_stream_index
+
+ seek $fi 2 current
+
+ set data [read $fi 4]
+ binary scan $data i hash_size
+
+ if { $hash_size != 4 } {
+ fail "Incorrect hash size in IPI stream."
+ } else {
+ pass "Correct hash size in IPI stream."
+ }
+
+ set data [read $fi 4]
+ binary scan $data i num_buckets
+
+ if { $num_buckets != 0x3ffff } {
+ fail "Incorrect number of buckets in IPI stream."
+ } else {
+ pass "Correct number of buckets in IPI stream."
+ }
+
+ set data [read $fi 4]
+ binary scan $data i hash_list_offset
+
+ set data [read $fi 4]
+ binary scan $data i hash_list_size
+
+ set data [read $fi 4]
+ binary scan $data i skip_list_offset
+
+ set data [read $fi 4]
+ binary scan $data i skip_list_size
+
+ seek $fi 8 current
+
+ set type_list [read $fi $type_list_size]
+
+ close $fi
+
+ set fi [open tmpdir/pdb-types2-typelist w]
+ fconfigure $fi -translation binary
+ puts -nonewline $fi $type_list
+ close $fi
+
+ # check type list
+
+ set exp [file_contents "$srcdir/$subdir/pdb-types2-typelist.d"]
+ set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types2-typelist"]
+ if ![string match $exp $got] {
+ fail "Incorrect type list in IPI stream."
+ } else {
+ pass "Correct type list in IPI stream."
+ }
+
+ # extract hash list and skip list
+
+ set index_str [format "%04x" $hash_stream_index]
+
+ set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types2.pdb $index_str"]
+
+ if ![string match "" $exec_output] {
+ fail "Could not extract IPI hash stream."
+ } else {
+ pass "Extracted IPI hash stream."
+ }
+
+ set fi [open tmpdir/$index_str]
+ fconfigure $fi -translation binary
+
+ seek $fi $hash_list_offset
+ set hash_list [read $fi $hash_list_size]
+
+ seek $fi $skip_list_offset
+ set skip_list [read $fi $skip_list_size]
+
+ close $fi
+
+ # check hash list
+
+ set fi [open tmpdir/pdb-types2-hashlist w]
+ fconfigure $fi -translation binary
+ puts -nonewline $fi $hash_list
+ close $fi
+
+ set exp [file_contents "$srcdir/$subdir/pdb-types2-hashlist.d"]
+ set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types2-hashlist"]
+ if ![string match $exp $got] {
+ fail "Incorrect hash list in IPI stream."
+ } else {
+ pass "Correct hash list in IPI stream."
+ }
+
+ # check skip list
+
+ set fi [open tmpdir/pdb-types2-skiplist w]
+ fconfigure $fi -translation binary
+ puts -nonewline $fi $skip_list
+ close $fi
+
+ set exp [file_contents "$srcdir/$subdir/pdb-types2-skiplist.d"]
+ set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types2-skiplist"]
+ if ![string match $exp $got] {
+ fail "Incorrect skip list in IPI stream."
+ } else {
+ pass "Correct skip list in IPI stream."
+ }
+}
+
test1
test2
test3
test4
test5
+test6
--
2.37.4
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 07/10] ld: Parse LF_UDT_SRC_LINE records when creating PDB file
2022-12-09 1:52 [PATCH 01/10] ld: Generate PDB string table Mark Harmstone
` (4 preceding siblings ...)
2022-12-09 1:52 ` [PATCH 06/10] ld: Write types into IPI " Mark Harmstone
@ 2022-12-09 1:52 ` Mark Harmstone
2022-12-09 1:52 ` [PATCH 08/10] ld: Write globals stream in PDB Mark Harmstone
` (3 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Mark Harmstone @ 2022-12-09 1:52 UTC (permalink / raw)
To: binutils, nickc; +Cc: Mark Harmstone
---
ld/pdb.c | 171 +++++++++++++++++++++--
ld/pdb.h | 23 +++
ld/testsuite/ld-pe/pdb-types3-hashlist.d | 5 +
ld/testsuite/ld-pe/pdb-types3-skiplist.d | 5 +
ld/testsuite/ld-pe/pdb-types3-typelist.d | 7 +
ld/testsuite/ld-pe/pdb-types3a.s | 57 ++++++++
ld/testsuite/ld-pe/pdb-types3b.s | 68 +++++++++
ld/testsuite/ld-pe/pdb.exp | 133 ++++++++++++++++++
8 files changed, 459 insertions(+), 10 deletions(-)
create mode 100644 ld/testsuite/ld-pe/pdb-types3-hashlist.d
create mode 100644 ld/testsuite/ld-pe/pdb-types3-skiplist.d
create mode 100644 ld/testsuite/ld-pe/pdb-types3-typelist.d
create mode 100644 ld/testsuite/ld-pe/pdb-types3a.s
create mode 100644 ld/testsuite/ld-pe/pdb-types3b.s
diff --git a/ld/pdb.c b/ld/pdb.c
index 69fade50814..53a83fb8d6b 100644
--- a/ld/pdb.c
+++ b/ld/pdb.c
@@ -76,6 +76,7 @@ struct type_entry
struct type_entry *next;
uint32_t index;
uint32_t cv_hash;
+ bool has_udt_src_line;
uint8_t data[];
};
@@ -708,19 +709,19 @@ copy_filechksms (uint8_t *data, uint32_t size, char *string_table,
return true;
}
-/* Add a string to the strings table, if it's not already there. */
-static void
+/* Add a string to the strings table, if it's not already there. Returns its
+ offset within the string table. */
+static uint32_t
add_string (char *str, size_t len, struct string_table *strings)
{
uint32_t hash = calc_hash (str, len);
+ struct string *s;
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;
@@ -741,6 +742,12 @@ add_string (char *str, size_t len, struct string_table *strings)
strings->strings_len += len + 1;
}
+ else
+ {
+ s = (struct string *) *slot;
+ }
+
+ return s->offset;
}
/* Return the hash of an entry in the string table. */
@@ -1118,13 +1125,149 @@ is_name_anonymous (char *name, size_t len)
return false;
}
+/* Handle LF_UDT_SRC_LINE type entries, which are a special case. These
+ give the source file and line number for each user-defined type that is
+ declared. We parse these and emit instead an LF_UDT_MOD_SRC_LINE entry,
+ which also includes the module number. */
+static bool
+handle_udt_src_line (uint8_t *data, uint16_t size, struct type_entry **map,
+ uint32_t type_num, uint32_t num_types,
+ struct types *ids, uint16_t mod_num,
+ struct string_table *strings)
+{
+ struct lf_udt_src_line *usl = (struct lf_udt_src_line *) data;
+ uint32_t orig_type, source_file_type;
+ void **slot;
+ hashval_t hash;
+ struct type_entry *e, *type_e, *str_e;
+ struct lf_udt_mod_src_line *umsl;
+ struct lf_string_id *str;
+ uint32_t source_file_offset;
+
+ if (size < sizeof (struct lf_udt_src_line))
+ {
+ einfo (_("%P: warning: truncated CodeView type record"
+ " LF_UDT_SRC_LINE\n"));
+ return false;
+ }
+
+ /* Check if LF_UDT_MOD_SRC_LINE already present for type, and return. */
+
+ orig_type = bfd_getl32 (&usl->type);
+
+ if (orig_type < TPI_FIRST_INDEX ||
+ orig_type >= TPI_FIRST_INDEX + num_types ||
+ !map[orig_type - TPI_FIRST_INDEX])
+ {
+ einfo (_("%P: warning: CodeView type record LF_UDT_SRC_LINE"
+ " referred to unknown type %v\n"), orig_type);
+ return false;
+ }
+
+ type_e = map[orig_type - TPI_FIRST_INDEX];
+
+ /* Skip if type already declared in other module. */
+ if (type_e->has_udt_src_line)
+ return true;
+
+ if (!remap_type (&usl->type, map, type_num, num_types))
+ return false;
+
+ /* Extract string from source_file_type. */
+
+ source_file_type = bfd_getl32 (&usl->source_file_type);
+
+ if (source_file_type < TPI_FIRST_INDEX ||
+ source_file_type >= TPI_FIRST_INDEX + num_types ||
+ !map[source_file_type - TPI_FIRST_INDEX])
+ {
+ einfo (_("%P: warning: CodeView type record LF_UDT_SRC_LINE"
+ " referred to unknown string %v\n"), source_file_type);
+ return false;
+ }
+
+ str_e = map[source_file_type - TPI_FIRST_INDEX];
+
+ if (bfd_getl16 (str_e->data + sizeof (uint16_t)) != LF_STRING_ID)
+ {
+ einfo (_("%P: warning: CodeView type record LF_UDT_SRC_LINE"
+ " pointed to unexpected record type\n"));
+ return false;
+ }
+
+ str = (struct lf_string_id *) str_e->data;
+
+ /* Add string to string table. */
+
+ source_file_offset = add_string (str->string, strlen (str->string),
+ strings);
+
+ /* Add LF_UDT_MOD_SRC_LINE entry. */
+
+ size = sizeof (struct lf_udt_mod_src_line);
+
+ e = xmalloc (offsetof (struct type_entry, data) + size);
+
+ e->next = NULL;
+ e->index = ids->num_types;
+ e->has_udt_src_line = false;
+
+ /* LF_UDT_MOD_SRC_LINE use calc_hash on the type number, rather than
+ the crc32 used for type hashes elsewhere. */
+ e->cv_hash = calc_hash ((char *) &usl->type, sizeof (uint32_t));
+
+ type_e->has_udt_src_line = true;
+
+ umsl = (struct lf_udt_mod_src_line *) e->data;
+
+ bfd_putl16 (size - sizeof (uint16_t), &umsl->size);
+ bfd_putl16 (LF_UDT_MOD_SRC_LINE, &umsl->kind);
+ memcpy (&umsl->type, &usl->type, sizeof (uint32_t));
+ bfd_putl32 (source_file_offset, &umsl->source_file_string);
+ memcpy (&umsl->line_no, &usl->line_no, sizeof (uint16_t));
+ bfd_putl16 (mod_num + 1, &umsl->module_no);
+
+ hash = iterative_hash (e->data, size, 0);
+
+ slot = htab_find_slot_with_hash (ids->hashmap, data, hash, INSERT);
+ if (!slot)
+ {
+ free (e);
+ return false;
+ }
+
+ if (*slot)
+ {
+ free (e);
+ einfo (_("%P: warning: duplicate CodeView type record "
+ "LF_UDT_MOD_SRC_LINE\n"));
+ return false;
+ }
+
+ *slot = e;
+
+ if (ids->last)
+ ids->last->next = e;
+ else
+ ids->first = e;
+
+ ids->last = e;
+
+ map[type_num] = e;
+
+ ids->num_types++;
+
+ return true;
+}
+
/* Parse a type definition in the .debug$T section. We remap the numbers
of any referenced types, and if the type is not a duplicate of one
already seen add it to types (for TPI types) or ids (for IPI types). */
static bool
handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
uint32_t num_types, struct types *types,
- struct types *ids)
+ struct types *ids, uint16_t mod_num,
+ struct string_table *strings)
{
uint16_t size, type;
void **slot;
@@ -2075,6 +2218,10 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
break;
}
+ case LF_UDT_SRC_LINE:
+ return handle_udt_src_line (data, size, map, type_num, num_types,
+ ids, mod_num, strings);
+
default:
einfo (_("%P: warning: unrecognized CodeView type %v\n"), type);
return false;
@@ -2104,6 +2251,8 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
else
e->cv_hash = crc32 (data, size);
+ e->has_udt_src_line = false;
+
memcpy (e->data, data, size);
if (t->last)
@@ -2129,7 +2278,8 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
found to handle_type. */
static bool
handle_debugt_section (asection *s, bfd *mod, struct types *types,
- struct types *ids)
+ struct types *ids, uint16_t mod_num,
+ struct string_table *strings)
{
bfd_byte *data = NULL;
size_t off;
@@ -2186,7 +2336,8 @@ handle_debugt_section (asection *s, bfd *mod, struct types *types,
size = bfd_getl16 (data + off);
- if (!handle_type (data + off, map, type_num, num_types, types, ids))
+ if (!handle_type (data + off, map, type_num, num_types, types, ids,
+ mod_num, strings))
{
free (data);
free (map);
@@ -2212,7 +2363,7 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
uint32_t *c13_info_size,
struct mod_source_files *mod_source,
bfd *abfd, struct types *types,
- struct types *ids)
+ struct types *ids, uint16_t mod_num)
{
uint8_t int_buf[sizeof (uint32_t)];
uint8_t *c13_info = NULL;
@@ -2236,7 +2387,7 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
}
else if (!strcmp (s->name, ".debug$T") && s->size >= sizeof (uint32_t))
{
- if (!handle_debugt_section (s, mod, types, ids))
+ if (!handle_debugt_section (s, mod, types, ids, mod_num, strings))
{
free (c13_info);
free (mod_source->files);
@@ -2370,7 +2521,7 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
if (!populate_module_stream (stream, in, &sym_byte_size,
strings, &c13_info_size,
&source->mods[mod_num], abfd,
- types, ids))
+ types, ids, mod_num))
{
for (unsigned int i = 0; i < source->mod_count; i++)
{
diff --git a/ld/pdb.h b/ld/pdb.h
index 7d87a3fef07..668f3e168fd 100644
--- a/ld/pdb.h
+++ b/ld/pdb.h
@@ -59,6 +59,8 @@
#define LF_BUILDINFO 0x1603
#define LF_SUBSTR_LIST 0x1604
#define LF_STRING_ID 0x1605
+#define LF_UDT_SRC_LINE 0x1606
+#define LF_UDT_MOD_SRC_LINE 0x1607
#define LF_CHAR 0x8000
#define LF_SHORT 0x8001
@@ -517,6 +519,27 @@ struct lf_mfunc_id
char name[];
} ATTRIBUTE_PACKED;
+/* lfUdtSrcLine in cvinfo.h */
+struct lf_udt_src_line
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t type;
+ uint32_t source_file_type;
+ uint32_t line_no;
+} ATTRIBUTE_PACKED;
+
+/* 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;
+
extern bool create_pdb_file (bfd *, const char *, const unsigned char *);
#endif
diff --git a/ld/testsuite/ld-pe/pdb-types3-hashlist.d b/ld/testsuite/ld-pe/pdb-types3-hashlist.d
new file mode 100644
index 00000000000..4a3775be42a
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types3-hashlist.d
@@ -0,0 +1,5 @@
+
+*: file format binary
+
+Contents of section .data:
+ 0000 d4d90000 0c1c0000 *
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-types3-skiplist.d b/ld/testsuite/ld-pe/pdb-types3-skiplist.d
new file mode 100644
index 00000000000..52c10fa501d
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types3-skiplist.d
@@ -0,0 +1,5 @@
+
+*: file format binary
+
+Contents of section .data:
+ 0000 00100000 00000000 *
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-types3-typelist.d b/ld/testsuite/ld-pe/pdb-types3-typelist.d
new file mode 100644
index 00000000000..d6ffaadb246
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types3-typelist.d
@@ -0,0 +1,7 @@
+
+*: file format binary
+
+Contents of section .data:
+ 0000 0e000516 00000000 666f6f2e 6800f2f1 ........foo.h...
+ 0010 10000716 01100000 01000000 2a000000 ............*...
+ 0020 0100 ..
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-types3a.s b/ld/testsuite/ld-pe/pdb-types3a.s
new file mode 100644
index 00000000000..def001e52f7
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types3a.s
@@ -0,0 +1,57 @@
+.equ CV_SIGNATURE_C13, 4
+.equ T_INT4, 0x0074
+
+.equ LF_FIELDLIST, 0x1203
+.equ LF_STRUCTURE, 0x1505
+.equ LF_MEMBER, 0x150d
+.equ LF_STRING_ID, 0x1605
+.equ LF_UDT_SRC_LINE, 0x1606
+
+.section ".debug$T", "rn"
+
+.long CV_SIGNATURE_C13
+
+# Type 1000, fieldlist for struct foo
+.fieldlist1:
+.short .struct1 - .fieldlist1 - 2
+.short LF_FIELDLIST
+.short LF_MEMBER
+.short 3 # public
+.long T_INT4
+.short 0 # offset
+.asciz "num"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1001, struct foo
+.struct1:
+.short .string1 - .struct1 - 2
+.short LF_STRUCTURE
+.short 1 # no. members
+.short 0 # property
+.long 0x1000 # field list
+.long 0 # type derived from
+.long 0 # type of vshape table
+.short 4 # size
+.asciz "foo" # name
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1002, string "foo"
+.string1:
+.short .udtsrcline1 - .string1 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "foo.h"
+.byte 0xf2
+.byte 0xf1
+
+# Type 1003, UDT source line for type 1001
+.udtsrcline1:
+.short .types_end - .udtsrcline1 - 2
+.short LF_UDT_SRC_LINE
+.long 0x1001
+.long 0x1002 # source file string
+.long 42 # line no.
+
+.types_end:
diff --git a/ld/testsuite/ld-pe/pdb-types3b.s b/ld/testsuite/ld-pe/pdb-types3b.s
new file mode 100644
index 00000000000..a22234e221f
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types3b.s
@@ -0,0 +1,68 @@
+.equ CV_SIGNATURE_C13, 4
+
+.equ T_LONG, 0x0012
+.equ T_INT4, 0x0074
+
+.equ LF_MODIFIER, 0x1001
+.equ LF_FIELDLIST, 0x1203
+.equ LF_STRUCTURE, 0x1505
+.equ LF_MEMBER, 0x150d
+.equ LF_STRING_ID, 0x1605
+.equ LF_UDT_SRC_LINE, 0x1606
+
+.section ".debug$T", "rn"
+
+.long CV_SIGNATURE_C13
+
+# Type 1000, const long
+.mod1:
+.short .fieldlist1 - .mod1 - 2
+.short LF_MODIFIER
+.long T_LONG
+.short 1 # const
+.short 0 # padding
+
+# Type 1001, fieldlist for struct foo
+.fieldlist1:
+.short .struct1 - .fieldlist1 - 2
+.short LF_FIELDLIST
+.short LF_MEMBER
+.short 3 # public
+.long T_INT4
+.short 0 # offset
+.asciz "num"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1002, struct foo
+.struct1:
+.short .string1 - .struct1 - 2
+.short LF_STRUCTURE
+.short 1 # no. members
+.short 0 # property
+.long 0x1001 # field list
+.long 0 # type derived from
+.long 0 # type of vshape table
+.short 4 # size
+.asciz "foo" # name
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1003, string "foo"
+.string1:
+.short .udtsrcline1 - .string1 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "foo.h"
+.byte 0xf2
+.byte 0xf1
+
+# Type 1004, UDT source line for type 1002
+.udtsrcline1:
+.short .types_end - .udtsrcline1 - 2
+.short LF_UDT_SRC_LINE
+.long 0x1002
+.long 0x1003 # source file string
+.long 42 # line no.
+
+.types_end:
diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp
index 9753216f544..1a5416890d1 100644
--- a/ld/testsuite/ld-pe/pdb.exp
+++ b/ld/testsuite/ld-pe/pdb.exp
@@ -1319,9 +1319,142 @@ proc test6 { } {
}
}
+proc test7 { } {
+ global as
+ global ar
+ global ld
+ global objdump
+ global srcdir
+ global subdir
+
+ if ![ld_assemble $as $srcdir/$subdir/pdb-types3a.s tmpdir/pdb-types3a.o] {
+ unsupported "Build pdb-types3a.o"
+ return
+ }
+
+ if ![ld_assemble $as $srcdir/$subdir/pdb-types3b.s tmpdir/pdb-types3b.o] {
+ unsupported "Build pdb-types3b.o"
+ return
+ }
+
+ if ![ld_link $ld "tmpdir/pdb-types3.exe" "--pdb=tmpdir/pdb-types3.pdb tmpdir/pdb-types3a.o tmpdir/pdb-types3b.o"] {
+ unsupported "Create PE image with PDB file"
+ return
+ }
+
+ set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types3.pdb 0004"]
+
+ if ![string match "" $exec_output] {
+ fail "Could not extract IPI stream"
+ return
+ } else {
+ pass "Extracted IPI stream"
+ }
+
+ set fi [open tmpdir/0004]
+ fconfigure $fi -translation binary
+
+ seek $fi 16 current
+
+ set data [read $fi 4]
+ binary scan $data i type_list_size
+
+ set data [read $fi 2]
+ binary scan $data s hash_stream_index
+
+ seek $fi 10 current
+
+ set data [read $fi 4]
+ binary scan $data i hash_list_offset
+
+ set data [read $fi 4]
+ binary scan $data i hash_list_size
+
+ set data [read $fi 4]
+ binary scan $data i skip_list_offset
+
+ set data [read $fi 4]
+ binary scan $data i skip_list_size
+
+ seek $fi 8 current
+
+ set type_list [read $fi $type_list_size]
+
+ close $fi
+
+ set fi [open tmpdir/pdb-types3-typelist w]
+ fconfigure $fi -translation binary
+ puts -nonewline $fi $type_list
+ close $fi
+
+ # check type list
+
+ set exp [file_contents "$srcdir/$subdir/pdb-types3-typelist.d"]
+ set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types3-typelist"]
+ if ![string match $exp $got] {
+ fail "Incorrect type list in IPI stream."
+ } else {
+ pass "Correct type list in IPI stream."
+ }
+
+ # extract hash list and skip list
+
+ set index_str [format "%04x" $hash_stream_index]
+
+ set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types3.pdb $index_str"]
+
+ if ![string match "" $exec_output] {
+ fail "Could not extract IPI hash stream."
+ } else {
+ pass "Extracted IPI hash stream."
+ }
+
+ set fi [open tmpdir/$index_str]
+ fconfigure $fi -translation binary
+
+ seek $fi $hash_list_offset
+ set hash_list [read $fi $hash_list_size]
+
+ seek $fi $skip_list_offset
+ set skip_list [read $fi $skip_list_size]
+
+ close $fi
+
+ # check hash list
+
+ set fi [open tmpdir/pdb-types3-hashlist w]
+ fconfigure $fi -translation binary
+ puts -nonewline $fi $hash_list
+ close $fi
+
+ set exp [file_contents "$srcdir/$subdir/pdb-types3-hashlist.d"]
+ set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types3-hashlist"]
+ if ![string match $exp $got] {
+ fail "Incorrect hash list in IPI stream."
+ } else {
+ pass "Correct hash list in IPI stream."
+ }
+
+ # check skip list
+
+ set fi [open tmpdir/pdb-types3-skiplist w]
+ fconfigure $fi -translation binary
+ puts -nonewline $fi $skip_list
+ close $fi
+
+ set exp [file_contents "$srcdir/$subdir/pdb-types3-skiplist.d"]
+ set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types3-skiplist"]
+ if ![string match $exp $got] {
+ fail "Incorrect skip list in IPI stream."
+ } else {
+ pass "Correct skip list in IPI stream."
+ }
+}
+
test1
test2
test3
test4
test5
test6
+test7
--
2.37.4
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 08/10] ld: Write globals stream in PDB
2022-12-09 1:52 [PATCH 01/10] ld: Generate PDB string table Mark Harmstone
` (5 preceding siblings ...)
2022-12-09 1:52 ` [PATCH 07/10] ld: Parse LF_UDT_SRC_LINE records when creating PDB file Mark Harmstone
@ 2022-12-09 1:52 ` Mark Harmstone
2022-12-09 1:52 ` [PATCH 09/10] ld: Copy other symbols into PDB file Mark Harmstone
` (2 subsequent siblings)
9 siblings, 0 replies; 14+ messages in thread
From: Mark Harmstone @ 2022-12-09 1:52 UTC (permalink / raw)
To: binutils, nickc; +Cc: Mark Harmstone
---
ld/pdb.c | 1088 +++++++++++++++++++++--
ld/pdb.h | 77 ++
ld/testsuite/ld-pe/pdb-syms1-globals.d | 57 ++
ld/testsuite/ld-pe/pdb-syms1-records.d | 61 ++
ld/testsuite/ld-pe/pdb-syms1-symbols1.d | 8 +
ld/testsuite/ld-pe/pdb-syms1-symbols2.d | 56 ++
ld/testsuite/ld-pe/pdb-syms1a.s | 110 +++
ld/testsuite/ld-pe/pdb-syms1b.s | 737 +++++++++++++++
ld/testsuite/ld-pe/pdb.exp | 164 ++++
9 files changed, 2279 insertions(+), 79 deletions(-)
create mode 100644 ld/testsuite/ld-pe/pdb-syms1-globals.d
create mode 100644 ld/testsuite/ld-pe/pdb-syms1-records.d
create mode 100644 ld/testsuite/ld-pe/pdb-syms1-symbols1.d
create mode 100644 ld/testsuite/ld-pe/pdb-syms1-symbols2.d
create mode 100644 ld/testsuite/ld-pe/pdb-syms1a.s
create mode 100644 ld/testsuite/ld-pe/pdb-syms1b.s
diff --git a/ld/pdb.c b/ld/pdb.c
index 53a83fb8d6b..5257cda2e25 100644
--- a/ld/pdb.c
+++ b/ld/pdb.c
@@ -88,6 +88,24 @@ struct types
struct type_entry *last;
};
+struct global
+{
+ struct global *next;
+ uint32_t offset;
+ uint32_t hash;
+ uint32_t refcount;
+ unsigned int index;
+ uint8_t data[];
+};
+
+struct globals
+{
+ uint32_t num_entries;
+ struct global *first;
+ struct global *last;
+ htab_t hashmap;
+};
+
static const uint32_t crc_table[] =
{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
@@ -767,56 +785,677 @@ eq_string_table_entry (const void *a, const void *b)
const char *s2 = (const char *) b;
size_t s2_len = strlen (s2);
- if (s2_len != s1->len)
- return 0;
+ 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;
+ }
+}
+
+/* Remap a type reference within a CodeView symbol. */
+static bool
+remap_symbol_type (void *data, struct type_entry **map, uint32_t num_types)
+{
+ uint32_t type = bfd_getl32 (data);
+
+ /* Ignore builtin types (those with IDs below 0x1000). */
+ if (type < TPI_FIRST_INDEX)
+ return true;
+
+ if (type >= TPI_FIRST_INDEX + num_types)
+ {
+ einfo (_("%P: CodeView symbol references out of range type %v\n"),
+ type);
+ return false;
+ }
+
+ type = TPI_FIRST_INDEX + map[type - TPI_FIRST_INDEX]->index;
+ bfd_putl32 (type, data);
+
+ return true;
+}
+
+/* Add an entry into the globals stream. If it already exists, increase
+ the refcount. */
+static bool
+add_globals_ref (struct globals *glob, bfd *sym_rec_stream, const char *name,
+ size_t name_len, uint8_t *data, size_t len)
+{
+ void **slot;
+ uint32_t hash;
+ struct global *g;
+
+ slot = htab_find_slot_with_hash (glob->hashmap, data,
+ iterative_hash (data, len, 0), INSERT);
+
+ if (*slot)
+ {
+ g = *slot;
+ g->refcount++;
+ return true;
+ }
+
+ *slot = xmalloc (offsetof (struct global, data) + len);
+
+ hash = crc32 ((const uint8_t *) name, name_len);
+ hash %= NUM_GLOBALS_HASH_BUCKETS;
+
+ g = *slot;
+ g->next = NULL;
+ g->offset = bfd_tell (sym_rec_stream);
+ g->hash = hash;
+ g->refcount = 1;
+ memcpy (g->data, data, len + 1);
+
+ glob->num_entries++;
+
+ if (glob->last)
+ glob->last->next = g;
+ else
+ glob->first = g;
+
+ glob->last = g;
+
+ return bfd_bwrite (data, len, sym_rec_stream) == len;
+}
+
+/* Find the end of the current scope within symbols data. */
+static uint8_t *
+find_end_of_scope (uint8_t *data, uint32_t size)
+{
+ unsigned int scope_level = 1;
+ uint16_t len;
+
+ len = bfd_getl16 (data) + sizeof (uint16_t);
+
+ data += len;
+ size -= len;
+
+ while (true)
+ {
+ uint16_t type;
+
+ if (size < sizeof (uint32_t))
+ return NULL;
+
+ len = bfd_getl16 (data) + sizeof (uint16_t);
+ type = bfd_getl16 (data + sizeof (uint16_t));
+
+ if (size < len)
+ return NULL;
+
+ switch (type)
+ {
+ case S_GPROC32:
+ case S_LPROC32:
+ scope_level++;
+ break;
+
+ case S_END:
+ case S_PROC_ID_END:
+ scope_level--;
+
+ if (scope_level == 0)
+ return data;
+
+ break;
+ }
+
+ data += len;
+ size -= len;
+ }
+}
+
+/* Return the size of an extended value parameter, as used in
+ LF_ENUMERATE etc. */
+static unsigned int
+extended_value_len (uint16_t type)
+{
+ switch (type)
+ {
+ case LF_CHAR:
+ return 1;
+
+ case LF_SHORT:
+ case LF_USHORT:
+ return 2;
+
+ case LF_LONG:
+ case LF_ULONG:
+ return 4;
+
+ case LF_QUADWORD:
+ case LF_UQUADWORD:
+ return 8;
+ }
+
+ return 0;
+}
+
+/* Parse the symbols in a .debug$S section, and copy them to the module's
+ symbol stream. */
+static bool
+parse_symbols (uint8_t *data, uint32_t size, uint8_t **buf,
+ struct type_entry **map, uint32_t num_types,
+ bfd *sym_rec_stream, struct globals *glob, uint16_t mod_num)
+{
+ uint8_t *orig_buf = *buf;
+ unsigned int scope_level = 0;
+
+ while (size >= sizeof (uint16_t))
+ {
+ uint16_t len, type;
+
+ len = bfd_getl16 (data) + sizeof (uint16_t);
+
+ if (len > size)
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ type = bfd_getl16 (data + sizeof (uint16_t));
+
+ switch (type)
+ {
+ case S_LDATA32:
+ case S_GDATA32:
+ case S_LTHREAD32:
+ case S_GTHREAD32:
+ {
+ struct datasym *d = (struct datasym *) data;
+ size_t name_len;
+
+ if (len < offsetof (struct datasym, name))
+ {
+ einfo (_("%P: warning: truncated CodeView record"
+ " S_LDATA32/S_GDATA32/S_LTHREAD32/S_GTHREAD32\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ if (scope_level == 0)
+ {
+ uint16_t section = bfd_getl16 (&d->section);
+
+ if (section == 0) /* GC'd, ignore */
+ break;
+ }
+
+ name_len =
+ strnlen (d->name, len - offsetof (struct datasym, name));
+
+ if (name_len == len - offsetof (struct datasym, name))
+ {
+ einfo (_("%P: warning: name for S_LDATA32/S_GDATA32/"
+ "S_LTHREAD32/S_GTHREAD32 has no terminating"
+ " zero\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ if (!remap_symbol_type (&d->type, map, num_types))
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ /* If S_LDATA32 or S_LTHREAD32, copy into module symbols. */
+
+ if (type == S_LDATA32 || type == S_LTHREAD32)
+ {
+ memcpy (*buf, d, len);
+ *buf += len;
+ }
+
+ /* S_LDATA32 and S_LTHREAD32 only go in globals if
+ not in function scope. */
+ if (type == S_GDATA32 || type == S_GTHREAD32 || scope_level == 0)
+ {
+ if (!add_globals_ref (glob, sym_rec_stream, d->name,
+ name_len, data, len))
+ return false;
+ }
+
+ break;
+ }
+
+ case S_GPROC32:
+ case S_LPROC32:
+ case S_GPROC32_ID:
+ case S_LPROC32_ID:
+ {
+ struct procsym *proc = (struct procsym *) data;
+ size_t name_len;
+ uint16_t section;
+ uint32_t end;
+ uint8_t *endptr;
+ size_t ref_size, padding;
+ struct refsym *ref;
+
+ if (len < offsetof (struct procsym, name))
+ {
+ einfo (_("%P: warning: truncated CodeView record"
+ " S_GPROC32/S_LPROC32\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ section = bfd_getl16 (&proc->section);
+
+ endptr = find_end_of_scope (data, size);
+
+ if (!endptr)
+ {
+ einfo (_("%P: warning: could not find end of"
+ " S_GPROC32/S_LPROC32 record\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ if (section == 0) /* skip if GC'd */
+ {
+ /* Skip to after S_END. */
+
+ size -= endptr - data;
+ data = endptr;
+
+ len = bfd_getl16 (data) + sizeof (uint16_t);
+
+ data += len;
+ size -= len;
+
+ continue;
+ }
+
+ name_len =
+ strnlen (proc->name, len - offsetof (struct procsym, name));
+
+ if (name_len == len - offsetof (struct procsym, name))
+ {
+ einfo (_("%P: warning: name for S_GPROC32/S_LPROC32 has no"
+ " terminating zero\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ if (type == S_GPROC32_ID || type == S_LPROC32_ID)
+ {
+ /* Transform into S_GPROC32 / S_LPROC32. */
+
+ uint32_t t_idx = bfd_getl32 (&proc->type);
+ struct type_entry *t;
+ uint16_t t_type;
+
+ if (t_idx < TPI_FIRST_INDEX
+ || t_idx >= TPI_FIRST_INDEX + num_types)
+ {
+ einfo (_("%P: CodeView symbol references out of range"
+ " type %v\n"), type);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ t = map[t_idx - TPI_FIRST_INDEX];
+
+ t_type = bfd_getl16 (t->data + sizeof (uint16_t));
+
+ switch (t_type)
+ {
+ case LF_FUNC_ID:
+ {
+ struct lf_func_id *t_data =
+ (struct lf_func_id *) t->data;
+
+ /* Replace proc->type with function type. */
+
+ memcpy (&proc->type, &t_data->function_type,
+ sizeof (uint32_t));
+
+ break;
+ }
+
+ case LF_MFUNC_ID:
+ {
+ struct lf_mfunc_id *t_data =
+ (struct lf_mfunc_id *) t->data;
+
+ /* Replace proc->type with function type. */
+
+ memcpy (&proc->type, &t_data->function_type,
+ sizeof (uint32_t));
+
+ break;
+ }
+
+ default:
+ einfo (_("%P: CodeView S_GPROC32_ID/S_LPROC32_ID symbol"
+ " referenced unknown type as ID\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ /* Change record type. */
+
+ if (type == S_GPROC32_ID)
+ bfd_putl32 (S_GPROC32, &proc->kind);
+ else
+ bfd_putl32 (S_LPROC32, &proc->kind);
+ }
+ else
+ {
+ if (!remap_symbol_type (&proc->type, map, num_types))
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+ }
+
+ end = *buf - orig_buf + sizeof (uint32_t) + endptr - data;
+ bfd_putl32 (end, &proc->end);
+
+ /* Add S_PROCREF / S_LPROCREF to globals stream. */
+
+ ref_size = offsetof (struct refsym, name) + name_len + 1;
+
+ if (ref_size % sizeof (uint32_t))
+ padding = sizeof (uint32_t) - (ref_size % sizeof (uint32_t));
+ else
+ padding = 0;
+
+ ref = xmalloc (ref_size + padding);
+
+ bfd_putl16 (ref_size + padding - sizeof (uint16_t), &ref->size);
+ bfd_putl16 (type == S_GPROC32 || type == S_GPROC32_ID ?
+ S_PROCREF : S_LPROCREF, &ref->kind);
+ bfd_putl32 (0, &ref->sum_name);
+ bfd_putl32 (*buf - orig_buf + sizeof (uint32_t),
+ &ref->symbol_offset);
+ bfd_putl16 (mod_num + 1, &ref->mod);
+
+ memcpy (ref->name, proc->name, name_len + 1);
+
+ memset (ref->name + name_len + 1, 0, padding);
+
+ if (!add_globals_ref (glob, sym_rec_stream, proc->name, name_len,
+ (uint8_t *) ref, ref_size + padding))
+ {
+ free (ref);
+ return false;
+ }
+
+ free (ref);
+
+ memcpy (*buf, proc, len);
+ *buf += len;
+
+ scope_level++;
+
+ break;
+ }
+
+ case S_UDT:
+ {
+ struct udtsym *udt = (struct udtsym *) data;
+ size_t name_len;
+
+ if (len < offsetof (struct udtsym, name))
+ {
+ einfo (_("%P: warning: truncated CodeView record"
+ " S_UDT\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ name_len =
+ strnlen (udt->name, len - offsetof (struct udtsym, name));
+
+ if (name_len == len - offsetof (struct udtsym, name))
+ {
+ einfo (_("%P: warning: name for S_UDT has no"
+ " terminating zero\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ if (!remap_symbol_type (&udt->type, map, num_types))
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ /* S_UDT goes in the symbols stream if within a procedure,
+ otherwise it goes in the globals stream. */
+ if (scope_level == 0)
+ {
+ if (!add_globals_ref (glob, sym_rec_stream, udt->name,
+ name_len, data, len))
+ return false;
+ }
+ else
+ {
+ memcpy (*buf, udt, len);
+ *buf += len;
+ }
+
+ break;
+ }
+
+ case S_CONSTANT:
+ {
+ struct constsym *c = (struct constsym *) data;
+ size_t name_len, rec_size;
+ uint16_t val;
+
+ if (len < offsetof (struct constsym, name))
+ {
+ einfo (_("%P: warning: truncated CodeView record"
+ " S_CONSTANT\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ rec_size = offsetof (struct constsym, name);
+
+ val = bfd_getl16 (&c->value);
+
+ /* If val >= 0x8000, actual value follows. */
+ if (val >= 0x8000)
+ {
+ unsigned int param_len = extended_value_len (val);
+
+ if (param_len == 0)
+ {
+ einfo (_("%P: warning: unhandled type %v within"
+ " S_CONSTANT\n"), val);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ rec_size += param_len;
+ }
+
+ name_len =
+ strnlen ((const char *) data + rec_size, len - rec_size);
+
+ if (name_len == len - rec_size)
+ {
+ einfo (_("%P: warning: name for S_CONSTANT has no"
+ " terminating zero\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ if (!remap_symbol_type (&c->type, map, num_types))
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ if (!add_globals_ref (glob, sym_rec_stream,
+ (const char *) data + rec_size, name_len,
+ data, len))
+ return false;
+
+ break;
+ }
+
+ case S_END:
+ case S_PROC_ID_END:
+ memcpy (*buf, data, len);
+
+ if (type == S_PROC_ID_END) /* transform to S_END */
+ bfd_putl16 (S_END, *buf + sizeof (uint16_t));
+
+ *buf += len;
+ scope_level--;
+ break;
+
+ default:
+ einfo (_("%P: warning: unrecognized CodeView record %v\n"), type);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ data += len;
+ size -= len;
+ }
+
+ return true;
+}
+
+/* For a given symbol subsection, work out how much space to allocate in the
+ result module stream. This is different because we don't copy certain
+ symbols, such as S_CONSTANT, and we skip over any procedures or data that
+ have been GC'd out. */
+static bool
+calculate_symbols_size (uint8_t *data, uint32_t size, uint32_t *sym_size)
+{
+ unsigned int scope_level = 0;
+
+ while (size >= sizeof (uint32_t))
+ {
+ uint16_t len = bfd_getl16 (data) + sizeof (uint16_t);
+ uint16_t type = bfd_getl16 (data + sizeof (uint16_t));
+
+ switch (type)
+ {
+ case S_LDATA32:
+ case S_LTHREAD32:
+ {
+ struct datasym *d = (struct datasym *) data;
+ uint16_t section;
+
+ if (len < offsetof (struct datasym, name))
+ {
+ einfo (_("%P: warning: truncated CodeView record"
+ " S_LDATA32/S_LTHREAD32\n"));
+ return false;
+ }
+
+ section = bfd_getl16 (&d->section);
+
+ /* copy if not GC'd or within function */
+ if (scope_level != 0 || section != 0)
+ *sym_size += len;
+ }
+
+ case S_GDATA32:
+ case S_GTHREAD32:
+ case S_CONSTANT:
+ /* Not copied into symbols stream. */
+ break;
+
+ case S_GPROC32:
+ case S_LPROC32:
+ case S_GPROC32_ID:
+ case S_LPROC32_ID:
+ {
+ struct procsym *proc = (struct procsym *) data;
+ uint16_t section;
+
+ if (len < offsetof (struct procsym, name))
+ {
+ einfo (_("%P: warning: truncated CodeView record"
+ " S_GPROC32/S_LPROC32\n"));
+ return false;
+ }
+
+ section = bfd_getl16 (&proc->section);
+
+ if (section != 0)
+ {
+ *sym_size += len;
+ }
+ else
+ {
+ uint8_t *endptr = find_end_of_scope (data, size);
+
+ if (!endptr)
+ {
+ einfo (_("%P: warning: could not find end of"
+ " S_GPROC32/S_LPROC32 record\n"));
+ return false;
+ }
- return memcmp (s1->s, s2, s2_len) == 0;
-}
+ /* Skip to after S_END. */
-/* 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);
+ size -= endptr - data;
+ data = endptr;
- add_string ((char *) data, len, strings);
+ len = bfd_getl16 (data) + sizeof (uint16_t);
- data += len + 1;
+ data += len;
+ size -= len;
- if (size <= len + 1)
- break;
+ continue;
+ }
- size -= len + 1;
- }
-}
+ scope_level++;
-/* Return the size of an extended value parameter, as used in
- LF_ENUMERATE etc. */
-static unsigned int
-extended_value_len (uint16_t type)
-{
- switch (type)
- {
- case LF_CHAR:
- return 1;
+ break;
+ }
- case LF_SHORT:
- case LF_USHORT:
- return 2;
+ case S_UDT:
+ if (scope_level != 0) /* only goes in symbols if local */
+ *sym_size += len;
+ break;
- case LF_LONG:
- case LF_ULONG:
- return 4;
+ case S_END: /* always copied */
+ case S_PROC_ID_END:
+ *sym_size += len;
+ scope_level--;
+ break;
- case LF_QUADWORD:
- case LF_UQUADWORD:
- return 8;
+ default:
+ einfo (_("%P: warning: unrecognized CodeView record %v\n"), type);
+ return false;
+ }
+
+ data += len;
+ size -= len;
}
- return 0;
+ return true;
}
/* Parse the .debug$S section within an object file. */
@@ -824,13 +1463,17 @@ static bool
handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
uint8_t **dataptr, uint32_t *sizeptr,
struct mod_source_files *mod_source,
- bfd *abfd)
+ bfd *abfd, uint8_t **syms, uint32_t *sym_byte_size,
+ struct type_entry **map, uint32_t num_types,
+ bfd *sym_rec_stream, struct globals *glob,
+ uint16_t mod_num)
{
bfd_byte *data = NULL;
size_t off;
uint32_t c13_size = 0;
char *string_table = NULL;
- uint8_t *buf, *bufptr;
+ uint8_t *buf, *bufptr, *symbuf, *symbufptr;
+ uint32_t sym_size = 0;
if (!bfd_get_full_section_contents (mod, s, &data))
return false;
@@ -969,6 +1612,16 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
break;
}
+
+ case DEBUG_S_SYMBOLS:
+ if (!calculate_symbols_size (data + off, size, &sym_size))
+ {
+ free (data);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ break;
}
off += size;
@@ -977,7 +1630,10 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
off += sizeof (uint32_t) - (off % sizeof (uint32_t));
}
- if (c13_size == 0)
+ if (sym_size % sizeof (uint32_t))
+ sym_size += sizeof (uint32_t) - (sym_size % sizeof (uint32_t));
+
+ if (c13_size == 0 && sym_size == 0)
{
free (data);
return true;
@@ -985,8 +1641,25 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
/* copy data */
- buf = xmalloc (c13_size);
- bufptr = buf;
+ if (c13_size != 0)
+ {
+ buf = xmalloc (c13_size);
+ bufptr = buf;
+ }
+ else
+ {
+ buf = NULL;
+ }
+
+ if (sym_size != 0)
+ {
+ symbuf = xmalloc (sym_size);
+ symbufptr = symbuf;
+ }
+ else
+ {
+ symbuf = NULL;
+ }
off = sizeof (uint32_t);
@@ -1007,6 +1680,7 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
strings, bufptr, mod_source))
{
free (data);
+ free (symbuf);
return false;
}
@@ -1035,6 +1709,17 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
break;
}
+
+ case DEBUG_S_SYMBOLS:
+ if (!parse_symbols (data + off, size, &symbufptr, map, num_types,
+ sym_rec_stream, glob, mod_num))
+ {
+ free (data);
+ free (symbuf);
+ return false;
+ }
+
+ break;
}
off += size;
@@ -1045,22 +1730,42 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
free (data);
- if (*dataptr)
+ if (buf)
{
- /* Append the C13 info to what's already there, if the module has
- multiple .debug$S sections. */
+ 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);
+ *dataptr = xrealloc (*dataptr, *sizeptr + c13_size);
+ memcpy (*dataptr + *sizeptr, buf, c13_size);
- free (buf);
+ free (buf);
+ }
+ else
+ {
+ *dataptr = buf;
+ }
+
+ *sizeptr += c13_size;
}
- else
+
+ if (symbuf)
{
- *dataptr = buf;
- }
+ if (*syms)
+ {
+ *syms = xrealloc (*syms, *sym_byte_size + sym_size);
+ memcpy (*syms + *sym_byte_size, symbuf, sym_size);
+
+ free (symbuf);
+ }
+ else
+ {
+ *syms = symbuf;
+ }
- *sizeptr += c13_size;
+ *sym_byte_size += sym_size;
+ }
return true;
}
@@ -2279,12 +2984,11 @@ handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
static bool
handle_debugt_section (asection *s, bfd *mod, struct types *types,
struct types *ids, uint16_t mod_num,
- struct string_table *strings)
+ struct string_table *strings,
+ struct type_entry ***map, uint32_t *num_types)
{
bfd_byte *data = NULL;
size_t off;
- unsigned int num_types = 0;
- struct type_entry **map;
uint32_t type_num;
if (!bfd_get_full_section_contents (mod, s, &data))
@@ -2315,17 +3019,17 @@ handle_debugt_section (asection *s, bfd *mod, struct types *types,
return false;
}
- num_types++;
+ (*num_types)++;
off += size;
}
- if (num_types == 0)
+ if (*num_types == 0)
{
free (data);
return true;
}
- map = xcalloc (num_types, sizeof (struct type_entry *));
+ *map = xcalloc (*num_types, sizeof (struct type_entry *));
off = sizeof (uint32_t);
type_num = 0;
@@ -2336,11 +3040,11 @@ handle_debugt_section (asection *s, bfd *mod, struct types *types,
size = bfd_getl16 (data + off);
- if (!handle_type (data + off, map, type_num, num_types, types, ids,
+ if (!handle_type (data + off, *map, type_num, *num_types, types, ids,
mod_num, strings))
{
free (data);
- free (map);
+ free (*map);
bfd_set_error (bfd_error_bad_value);
return false;
}
@@ -2350,7 +3054,6 @@ handle_debugt_section (asection *s, bfd *mod, struct types *types,
}
free (data);
- free (map);
return true;
}
@@ -2363,39 +3066,57 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
uint32_t *c13_info_size,
struct mod_source_files *mod_source,
bfd *abfd, struct types *types,
- struct types *ids, uint16_t mod_num)
+ struct types *ids, uint16_t mod_num,
+ bfd *sym_rec_stream, struct globals *glob)
{
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 = sizeof (uint32_t);
+ *sym_byte_size = 0;
*c13_info_size = 0;
- /* Process .debug$S section(s). */
+ /* Process .debug$T section. */
for (asection *s = mod->sections; s; s = s->next)
{
- if (!strcmp (s->name, ".debug$S") && s->size >= sizeof (uint32_t))
+ if (!strcmp (s->name, ".debug$T") && s->size >= sizeof (uint32_t))
{
- if (!handle_debugs_section (s, mod, strings, &c13_info,
- c13_info_size, mod_source, abfd))
+ if (!handle_debugt_section (s, mod, types, ids, mod_num, strings,
+ &map, &num_types))
{
- free (c13_info);
free (mod_source->files);
return false;
}
+
+ break;
}
- else if (!strcmp (s->name, ".debug$T") && s->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_debugt_section (s, mod, types, ids, mod_num, strings))
+ 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);
+
/* Write the signature. */
bfd_putl32 (CV_SIGNATURE_C13, int_buf);
@@ -2403,9 +3124,22 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != sizeof (uint32_t))
{
free (c13_info);
+ free (syms);
return false;
}
+ if (syms)
+ {
+ if (bfd_bwrite (syms, *sym_byte_size, stream) != *sym_byte_size)
+ {
+ free (c13_info);
+ free (syms);
+ return false;
+ }
+
+ free (syms);
+ }
+
if (c13_info)
{
if (bfd_bwrite (c13_info, *c13_info_size, stream) != *c13_info_size)
@@ -2432,7 +3166,8 @@ static bool
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)
+ struct types *types, struct types *ids,
+ bfd *sym_rec_stream, struct globals *glob)
{
uint8_t *ptr;
unsigned int mod_num;
@@ -2521,7 +3256,8 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
if (!populate_module_stream (stream, in, &sym_byte_size,
strings, &c13_info_size,
&source->mods[mod_num], abfd,
- types, ids, mod_num))
+ types, ids, mod_num,
+ sym_rec_stream, glob))
{
for (unsigned int i = 0; i < source->mod_count; i++)
{
@@ -2549,7 +3285,7 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
bfd_putl16 (0, &mod->flags);
bfd_putl16 (stream_num, &mod->module_sym_stream);
- bfd_putl32 (sym_byte_size, &mod->sym_byte_size);
+ bfd_putl32 (sizeof (uint32_t) + sym_byte_size, &mod->sym_byte_size);
bfd_putl32 (0, &mod->c11_byte_size);
bfd_putl32 (c13_info_size, &mod->c13_byte_size);
bfd_putl16 (0, &mod->source_file_count);
@@ -2837,6 +3573,170 @@ create_source_info_substream (void **data, uint32_t *size,
}
}
+/* Used as parameter to qsort, to sort globals by hash. */
+static int
+global_compare_hash (const void *s1, const void *s2)
+{
+ const struct global *g1 = *(const struct global **) s1;
+ const struct global *g2 = *(const struct global **) s2;
+
+ if (g1->hash < g2->hash)
+ return -1;
+ if (g1->hash > g2->hash)
+ return 1;
+
+ return 0;
+}
+
+/* Create the globals stream, which contains the unmangled symbol names. */
+static bool
+create_globals_stream (bfd *pdb, struct globals *glob, uint16_t *stream_num)
+{
+ bfd *stream;
+ struct globals_hash_header h;
+ uint32_t buckets_size, filled_buckets = 0;
+ struct global **sorted = NULL;
+ bool ret = false;
+ struct global *buckets[NUM_GLOBALS_HASH_BUCKETS];
+ char int_buf[sizeof (uint32_t)];
+
+ stream = add_stream (pdb, NULL, stream_num);
+ if (!stream)
+ return false;
+
+ memset (buckets, 0, sizeof (buckets));
+
+ if (glob->num_entries > 0)
+ {
+ struct global *g;
+
+ /* Create an array of pointers, sorted by hash value. */
+
+ sorted = xmalloc (sizeof (struct global *) * glob->num_entries);
+
+ g = glob->first;
+ for (unsigned int i = 0; i < glob->num_entries; i++)
+ {
+ sorted[i] = g;
+ g = g->next;
+ }
+
+ qsort (sorted, glob->num_entries, sizeof (struct global *),
+ global_compare_hash);
+
+ /* Populate the buckets. */
+
+ for (unsigned int i = 0; i < glob->num_entries; i++)
+ {
+ if (!buckets[sorted[i]->hash])
+ {
+ buckets[sorted[i]->hash] = sorted[i];
+ filled_buckets++;
+ }
+
+ sorted[i]->index = i;
+ }
+ }
+
+ buckets_size = NUM_GLOBALS_HASH_BUCKETS / 8;
+ buckets_size += sizeof (uint32_t);
+ buckets_size += filled_buckets * sizeof (uint32_t);
+
+ bfd_putl32 (GLOBALS_HASH_SIGNATURE, &h.signature);
+ bfd_putl32 (GLOBALS_HASH_VERSION_70, &h.version);
+ bfd_putl32 (glob->num_entries * sizeof (struct hash_record),
+ &h.entries_size);
+ bfd_putl32 (buckets_size, &h.buckets_size);
+
+ if (bfd_bwrite (&h, sizeof (h), stream) != sizeof (h))
+ return false;
+
+ /* Write hash entries, sorted by hash. */
+
+ for (unsigned int i = 0; i < glob->num_entries; i++)
+ {
+ struct hash_record hr;
+
+ bfd_putl32 (sorted[i]->offset + 1, &hr.offset);
+ bfd_putl32 (sorted[i]->refcount, &hr.reference);
+
+ if (bfd_bwrite (&hr, sizeof (hr), stream) != sizeof (hr))
+ goto end;
+ }
+
+ /* Write the bitmap for filled and unfilled buckets. */
+
+ for (unsigned int i = 0; i < NUM_GLOBALS_HASH_BUCKETS; i += 8)
+ {
+ uint8_t v = 0;
+
+ for (unsigned int j = 0; j < 8; j++)
+ {
+ if (buckets[i + j])
+ v |= 1 << j;
+ }
+
+ if (bfd_bwrite (&v, sizeof (v), stream) != sizeof (v))
+ goto end;
+ }
+
+ /* Add a 4-byte gap. */
+
+ bfd_putl32 (0, int_buf);
+
+ if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) != sizeof (uint32_t))
+ goto end;
+
+ /* Write the bucket offsets. */
+
+ for (unsigned int i = 0; i < NUM_GLOBALS_HASH_BUCKETS; i++)
+ {
+ if (buckets[i])
+ {
+ /* 0xc is size of internal hash_record structure in
+ Microsoft's parser. */
+ bfd_putl32 (buckets[i]->index * 0xc, int_buf);
+
+ if (bfd_bwrite (int_buf, sizeof (uint32_t), stream) !=
+ sizeof (uint32_t))
+ goto end;
+ }
+ }
+
+ ret = true;
+
+end:
+ free (sorted);
+
+ return ret;
+}
+
+/* Hash an entry in the globals list. */
+static hashval_t
+hash_global_entry (const void *p)
+{
+ const struct global *g = (const struct global *) p;
+ uint16_t len = bfd_getl16 (g->data);
+
+ return iterative_hash (g->data, len, 0);
+}
+
+/* Compare an entry in the globals list with a symbol. */
+static int
+eq_global_entry (const void *a, const void *b)
+{
+ const struct global *g = (const struct global *) a;
+ uint16_t len1, len2;
+
+ len1 = bfd_getl16 (g->data) + sizeof (uint16_t);
+ len2 = bfd_getl16 (b) + sizeof (uint16_t);
+
+ if (len1 != len2)
+ return 0;
+
+ return !memcmp (g->data, b, len1);
+}
+
/* Stream 4 is the debug information (DBI) stream. */
static bool
populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
@@ -2845,20 +3745,50 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
uint16_t publics_stream_num,
struct string_table *strings,
struct types *types,
- struct types *ids)
+ struct types *ids,
+ bfd *sym_rec_stream)
{
struct pdb_dbi_stream_header h;
struct optional_dbg_header opt;
void *mod_info, *sc, *source_info;
uint32_t mod_info_size, sc_size, source_info_size;
struct source_files_info source;
+ struct globals glob;
+ uint16_t globals_stream_num;
source.mod_count = 0;
source.mods = NULL;
+ glob.num_entries = 0;
+ glob.first = NULL;
+ glob.last = NULL;
+
+ glob.hashmap = htab_create_alloc (0, hash_global_entry,
+ eq_global_entry, free, xcalloc, free);
+
if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size,
- strings, &source, types, ids))
- return false;
+ strings, &source, types, ids,
+ sym_rec_stream, &glob))
+ {
+ htab_delete (glob.hashmap);
+ return false;
+ }
+
+ if (!create_globals_stream (pdb, &glob, &globals_stream_num))
+ {
+ htab_delete (glob.hashmap);
+
+ for (unsigned int i = 0; i < source.mod_count; i++)
+ {
+ free (source.mods[i].files);
+ }
+ free (source.mods);
+
+ free (mod_info);
+ return false;
+ }
+
+ htab_delete (glob.hashmap);
if (!create_section_contrib_substream (abfd, &sc, &sc_size))
{
@@ -2883,7 +3813,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
bfd_putl32 (0xffffffff, &h.version_signature);
bfd_putl32 (DBI_STREAM_VERSION_70, &h.version_header);
bfd_putl32 (1, &h.age);
- bfd_putl16 (0xffff, &h.global_stream_index);
+ bfd_putl16 (globals_stream_num, &h.global_stream_index);
bfd_putl16 (0x8e1d, &h.build_number); // MSVC 14.29
bfd_putl16 (publics_stream_num, &h.public_stream_index);
bfd_putl16 (0, &h.pdb_dll_version);
@@ -3530,7 +4460,7 @@ 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))
+ &strings, &types, &ids, sym_rec_stream))
{
einfo (_("%P: warning: cannot populate DBI stream "
"in PDB file: %E\n"));
diff --git a/ld/pdb.h b/ld/pdb.h
index 668f3e168fd..9048b5fa37c 100644
--- a/ld/pdb.h
+++ b/ld/pdb.h
@@ -70,7 +70,21 @@
#define LF_QUADWORD 0x8009
#define LF_UQUADWORD 0x800a
+#define S_END 0x0006
+#define S_CONSTANT 0x1107
+#define S_UDT 0x1108
+#define S_LDATA32 0x110c
+#define S_GDATA32 0x110d
#define S_PUB32 0x110e
+#define S_LPROC32 0x110f
+#define S_GPROC32 0x1110
+#define S_LTHREAD32 0x1112
+#define S_GTHREAD32 0x1113
+#define S_PROCREF 0x1125
+#define S_LPROCREF 0x1127
+#define S_LPROC32_ID 0x1146
+#define S_GPROC32_ID 0x1147
+#define S_PROC_ID_END 0x114f
/* PDBStream70 in pdb1.h */
struct pdb_stream_70
@@ -109,6 +123,8 @@ struct pdb_tpi_stream_header
#define TPI_FIRST_INDEX 0x1000
#define NUM_TPI_HASH_BUCKETS 0x3ffff
+#define NUM_GLOBALS_HASH_BUCKETS 4096
+
/* NewDBIHdr in dbi.h */
struct pdb_dbi_stream_header
{
@@ -198,6 +214,7 @@ struct optional_dbg_header
#define CV_SIGNATURE_C13 4
+#define DEBUG_S_SYMBOLS 0xf1
#define DEBUG_S_LINES 0xf2
#define DEBUG_S_STRINGTABLE 0xf3
#define DEBUG_S_FILECHKSMS 0xf4
@@ -540,6 +557,66 @@ struct lf_udt_mod_src_line
uint16_t module_no;
} ATTRIBUTE_PACKED;
+/* DATASYM32 in cvinfo.h */
+struct datasym
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t type;
+ uint32_t offset;
+ uint16_t section;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* PROCSYM32 in cvinfo.h */
+struct procsym
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t parent;
+ uint32_t end;
+ uint32_t next;
+ uint32_t proc_len;
+ uint32_t debug_start;
+ uint32_t debug_end;
+ uint32_t type;
+ uint32_t offset;
+ uint16_t section;
+ uint8_t flags;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* REFSYM2 in cvinfo.h */
+struct refsym
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t sum_name;
+ uint32_t symbol_offset;
+ uint16_t mod;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* UDTSYM in cvinfo.h */
+struct udtsym
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t type;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* CONSTSYM in cvinfo.h */
+struct constsym
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t type;
+ uint16_t value;
+ /* then actual value if value >= 0x8000 */
+ char name[];
+} ATTRIBUTE_PACKED;
+
extern bool create_pdb_file (bfd *, const char *, const unsigned char *);
#endif
diff --git a/ld/testsuite/ld-pe/pdb-syms1-globals.d b/ld/testsuite/ld-pe/pdb-syms1-globals.d
new file mode 100644
index 00000000000..356c5de22a3
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-syms1-globals.d
@@ -0,0 +1,57 @@
+
+*: file format binary
+
+Contents of section .data:
+ 0000 ffffffff 1a092ff1 d0000000 64020000 ....../.....d...
+ 0010 fd000000 01000000 35010000 01000000 ........5.......
+ 0020 15000000 01000000 19010000 01000000 ................
+ 0030 c1000000 01000000 b9010000 01000000 ................
+ 0040 d5000000 01000000 61010000 01000000 ........a.......
+ 0050 e9000000 01000000 11020000 01000000 ................
+ 0060 e9010000 01000000 d5010000 01000000 ................
+ 0070 49010000 01000000 a5010000 01000000 I...............
+ 0080 99010000 01000000 85000000 01000000 ................
+ 0090 01000000 01000000 99000000 01000000 ................
+ 00a0 7d010000 01000000 ad000000 01000000 }...............
+ 00b0 5d000000 01000000 49000000 01000000 ].......I.......
+ 00c0 21000000 02000000 35000000 01000000 !.......5.......
+ 00d0 fd010000 01000000 71000000 01000000 ........q.......
+ 00e0 00000000 00000000 00000000 00000000 ................
+ 00f0 00000000 00000000 00000000 00000000 ................
+ 0100 00000000 00000000 00000000 00000000 ................
+ 0110 00000000 00000000 00000000 00000000 ................
+ 0120 00000000 00000000 00000000 00000000 ................
+ 0130 00000000 00000000 00000000 00000000 ................
+ 0140 00000000 00000000 00000000 00000000 ................
+ 0150 00000002 00000000 00000000 00000000 ................
+ 0160 00000000 00000000 00000000 00000000 ................
+ 0170 00000000 00020000 00000000 00000000 ................
+ 0180 00000000 00000000 00000000 00000000 ................
+ 0190 00000000 00000001 00000000 00000000 ................
+ 01a0 00000000 00000000 00000000 00000000 ................
+ 01b0 00000000 00000000 00000000 00000000 ................
+ 01c0 00000000 00000000 00000000 00000000 ................
+ 01d0 00000000 00000000 00000000 00000000 ................
+ 01e0 08001000 00000000 00000000 00000040 ...............@
+ 01f0 04002000 00000000 00000000 00000000 .. .............
+ 0200 00000000 00000001 00000000 00000000 ................
+ 0210 00000000 00000000 00000000 00000000 ................
+ 0220 00000000 00000000 00000000 00000000 ................
+ 0230 00000000 00000000 00080000 00004000 ..............@.
+ 0240 00000000 00000000 00100010 00000000 ................
+ 0250 40000000 00000000 00000000 00000000 @...............
+ 0260 00000000 00000800 00000000 00000000 ................
+ 0270 00000008 00000000 00000000 00000000 ................
+ 0280 00000000 02004000 00000000 00000000 ......@.........
+ 0290 00000000 00008000 00000000 00000000 ................
+ 02a0 00000000 00000000 10000000 00000000 ................
+ 02b0 00000000 00000000 00000000 00800000 ................
+ 02c0 00000000 00000000 00002000 00010000 .......... .....
+ 02d0 00000000 00000000 00000000 00000040 ...............@
+ 02e0 00000000 00000000 0c000000 18000000 ................
+ 02f0 24000000 30000000 3c000000 48000000 $...0...<...H...
+ 0300 54000000 60000000 6c000000 78000000 T...`...l...x...
+ 0310 84000000 90000000 9c000000 a8000000 ................
+ 0320 b4000000 c0000000 d8000000 e4000000 ................
+ 0330 f0000000 fc000000 08010000 20010000 ............ ...
+ 0340 2c010000 ,...
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-syms1-records.d b/ld/testsuite/ld-pe/pdb-syms1-records.d
new file mode 100644
index 00000000000..bbf6d7f158e
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-syms1-records.d
@@ -0,0 +1,61 @@
+
+*: file format binary
+
+Contents of section .data:
+ 0000 12002511 00000000 04000000 01007072 ..%...........pr
+ 0010 6f633200 0a000811 04100000 62617200 oc2.........bar.
+ 0020 12000c11 00100000 08000000 02006c76 ..............lv
+ 0030 61723100 12000c11 00100000 0c000000 ar1.............
+ 0040 02006c76 61723100 12000c11 00100000 ..lvar1.........
+ 0050 00000000 03006c76 61723200 12000d11 ......lvar2.....
+ 0060 00100000 18000000 02006776 61723100 ..........gvar1.
+ 0070 12000d11 00100000 04000000 03006776 ..............gv
+ 0080 61723200 12002511 00000000 54000000 ar2...%.....T...
+ 0090 02007072 6f633100 12002511 00000000 ..proc1...%.....
+ 00a0 88000000 02007072 6f633200 12002711 ......proc2...'.
+ 00b0 00000000 f0000000 02007072 6f633300 ..........proc3.
+ 00c0 12002711 00000000 24010000 02007072 ..'.....$.....pr
+ 00d0 6f633400 12002511 00000000 58010000 oc4...%.....X...
+ 00e0 02007072 6f633500 12002511 00000000 ..proc5...%.....
+ 00f0 8c010000 02007072 6f633600 1a002511 ......proc6...%.
+ 0100 00000000 c0010000 0200666f 6f3a3a6d ..........foo::m
+ 0110 6574686f 64000000 1a002511 00000000 ethod.....%.....
+ 0120 f8010000 0200666f 6f3a3a6d 6574686f ......foo::metho
+ 0130 64320000 12002711 00000000 30020000 d2....'.....0...
+ 0140 02007072 6f633900 16002511 00000000 ..proc9...%.....
+ 0150 64020000 02007072 6f633130 00000000 d.....proc10....
+ 0160 1a002711 00000000 98020000 0200666f ..'...........fo
+ 0170 6f3a3a6d 6574686f 64330000 1a002711 o::method3....'.
+ 0180 00000000 d0020000 0200666f 6f3a3a6d ..........foo::m
+ 0190 6574686f 64340000 0a000811 0a100000 ethod4..........
+ 01a0 666f6f00 12000711 75000000 2a00616e foo.....u...*.an
+ 01b0 73776572 00f3f2f1 1a000711 23000000 swer........#...
+ 01c0 0a80efcd ab896745 2301616e 73776572 ......gE#.answer
+ 01d0 3200f2f1 12001211 00100000 20000000 2........... ...
+ 01e0 02006c76 61723500 12001211 00100000 ..lvar5.........
+ 01f0 12000000 03006c76 61723600 12001311 ......lvar6.....
+ 0200 00100000 1c000000 02006776 61723300 ..........gvar3.
+ 0210 12001311 00100000 08000000 03006776 ..............gv
+ 0220 61723400 12000e11 00000000 18000000 ar4.............
+ 0230 02006776 61723100 12000e11 00000000 ..gvar1.........
+ 0240 04000000 03006776 61723200 12000e11 ......gvar2.....
+ 0250 00000000 0c000000 03007072 6f633100 ..........proc1.
+ 0260 12000e11 02000000 06000000 01007072 ..............pr
+ 0270 6f633200 12000e11 00000000 0d000000 oc2.............
+ 0280 03007072 6f633300 12000e11 02000000 ..proc3.........
+ 0290 07000000 01007072 6f633400 12000e11 ......proc4.....
+ 02a0 00000000 0e000000 03007072 6f633500 ..........proc5.
+ 02b0 12000e11 02000000 08000000 01007072 ..............pr
+ 02c0 6f633600 12000e11 00000000 0f000000 oc6.............
+ 02d0 03007072 6f633700 12000e11 02000000 ..proc7.........
+ 02e0 09000000 01007072 6f633800 12000e11 ......proc8.....
+ 02f0 00000000 10000000 03007072 6f633900 ..........proc9.
+ 0300 16000e11 02000000 0a000000 01007072 ..............pr
+ 0310 6f633130 00000000 16000e11 00000000 oc10............
+ 0320 11000000 03007072 6f633131 00000000 ......proc11....
+ 0330 16000e11 02000000 0b000000 01007072 ..............pr
+ 0340 6f633132 00000000 12000e11 00000000 oc12............
+ 0350 1c000000 02006776 61723300 12000e11 ......gvar3.....
+ 0360 00000000 08000000 03006776 61723400 ..........gvar4.
+ 0370 12000e11 02000000 00000000 01006d61 ..............ma
+ 0380 696e0000 in..
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-syms1-symbols1.d b/ld/testsuite/ld-pe/pdb-syms1-symbols1.d
new file mode 100644
index 00000000000..4de22acbd08
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-syms1-symbols1.d
@@ -0,0 +1,8 @@
+
+*: file format binary
+
+Contents of section .data:
+ 0000 04000000 2e001011 00000000 34000000 ............4...
+ 0010 00000000 01000000 00000000 00000000 ................
+ 0020 02100000 06000000 01000070 726f6332 ...........proc2
+ 0030 00f3f2f1 02000600 00000000 ............
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-syms1-symbols2.d b/ld/testsuite/ld-pe/pdb-syms1-symbols2.d
new file mode 100644
index 00000000000..f134637a744
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-syms1-symbols2.d
@@ -0,0 +1,56 @@
+
+*: file format binary
+
+Contents of section .data:
+ 0000 04000000 12000c11 00100000 08000000 ................
+ 0010 02006c76 61723100 12000c11 00100000 ..lvar1.........
+ 0020 08000000 02006c76 61723100 12000c11 ......lvar1.....
+ 0030 00100000 0c000000 02006c76 61723100 ..........lvar1.
+ 0040 12000c11 00100000 00000000 03006c76 ..............lv
+ 0050 61723200 2e001011 00000000 84000000 ar2.............
+ 0060 00000000 01000000 00000000 00000000 ................
+ 0070 05100000 0c000000 03000070 726f6331 ...........proc1
+ 0080 00f3f2f1 02000600 2e001011 00000000 ................
+ 0090 ec000000 00000000 01000000 00000000 ................
+ 00a0 00000000 05100000 06000000 01000070 ...............p
+ 00b0 726f6332 00f3f2f1 0a000811 04100000 roc2............
+ 00c0 62617200 12000c11 00100000 10000000 bar.............
+ 00d0 02006c76 61723300 12001211 00100000 ..lvar3.........
+ 00e0 14000000 02006c76 61723400 02000600 ......lvar4.....
+ 00f0 2e000f11 00000000 20010000 00000000 ........ .......
+ 0100 01000000 00000000 00000000 05100000 ................
+ 0110 0d000000 03000070 726f6333 00f3f2f1 .......proc3....
+ 0120 02000600 2e000f11 00000000 54010000 ............T...
+ 0130 00000000 01000000 00000000 00000000 ................
+ 0140 05100000 07000000 01000070 726f6334 ...........proc4
+ 0150 00f3f2f1 02000600 2e001011 00000000 ................
+ 0160 88010000 00000000 01000000 00000000 ................
+ 0170 00000000 05100000 0e000000 03000070 ...............p
+ 0180 726f6335 00f3f2f1 02000600 2e001011 roc5............
+ 0190 00000000 bc010000 00000000 01000000 ................
+ 01a0 00000000 00000000 05100000 08000000 ................
+ 01b0 01000070 726f6336 00f3f2f1 02000600 ...proc6........
+ 01c0 32001011 00000000 f4010000 00000000 2...............
+ 01d0 01000000 00000000 00000000 05100000 ................
+ 01e0 0f000000 03000066 6f6f3a3a 6d657468 .......foo::meth
+ 01f0 6f6400f1 02000600 32001011 00000000 od......2.......
+ 0200 2c020000 00000000 01000000 00000000 ,...............
+ 0210 00000000 05100000 09000000 01000066 ...............f
+ 0220 6f6f3a3a 6d657468 6f643200 02000600 oo::method2.....
+ 0230 2e000f11 00000000 60020000 00000000 ........`.......
+ 0240 01000000 00000000 00000000 05100000 ................
+ 0250 10000000 03000070 726f6339 00f3f2f1 .......proc9....
+ 0260 02000600 2e001011 00000000 94020000 ................
+ 0270 00000000 01000000 00000000 00000000 ................
+ 0280 05100000 0a000000 01000070 726f6331 ...........proc1
+ 0290 3000f2f1 02000600 32000f11 00000000 0.......2.......
+ 02a0 cc020000 00000000 01000000 00000000 ................
+ 02b0 00000000 05100000 11000000 03000066 ...............f
+ 02c0 6f6f3a3a 6d657468 6f643300 02000600 oo::method3.....
+ 02d0 32000f11 00000000 04030000 00000000 2...............
+ 02e0 01000000 00000000 00000000 05100000 ................
+ 02f0 0b000000 01000066 6f6f3a3a 6d657468 .......foo::meth
+ 0300 6f643400 02000600 12001211 00100000 od4.............
+ 0310 20000000 02006c76 61723500 12001211 .....lvar5.....
+ 0320 00100000 12000000 03006c76 61723600 ..........lvar6.
+ 0330 00000000 ....
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-syms1a.s b/ld/testsuite/ld-pe/pdb-syms1a.s
new file mode 100644
index 00000000000..c1929c3ec85
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-syms1a.s
@@ -0,0 +1,110 @@
+.equ CV_SIGNATURE_C13, 4
+.equ DEBUG_S_SYMBOLS, 0xf1
+
+.equ T_UINT4, 0x0075
+
+.equ LF_MODIFIER, 0x1001
+.equ LF_PROCEDURE, 0x1008
+.equ LF_ARGLIST, 0x1201
+.equ LF_FIELDLIST, 0x1203
+.equ LF_STRUCTURE, 0x1505
+.equ LF_MEMBER, 0x150d
+
+.equ S_END, 0x0006
+.equ S_UDT, 0x1108
+.equ S_GPROC32, 0x1110
+
+.section ".debug$S", "rn"
+
+.long CV_SIGNATURE_C13
+
+.long DEBUG_S_SYMBOLS
+.long .syms_end - .syms_start
+
+.syms_start:
+
+.gproc2:
+.short .gproc2_end - .gproc2 - 2
+.short S_GPROC32
+.long 0 # parent
+.long 0 # end
+.long 0 # next symbol
+.long 1 # length
+.long 0 # debug start offset
+.long 0 # debug end offset
+.long 0x1002 # type
+.secrel32 proc2
+.secidx proc2
+.byte 0 # flags
+.asciz "proc2"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.gproc2_end:
+.short .udt1 - .gproc2_end - 2
+.short S_END
+
+.udt1:
+.short .syms_end - .udt1 - 2
+.short S_UDT
+.long 0x1004 # struct bar
+.asciz "bar"
+
+.syms_end:
+
+.section ".debug$T", "rn"
+
+.long CV_SIGNATURE_C13
+
+# Type 1000, const uint32_t
+.mod1:
+.short .arglist1 - .mod1 - 2
+.short LF_MODIFIER
+.long T_UINT4
+.short 1 # const
+.p2align 2
+
+# Type 1001, arglist (uint32_t)
+.arglist1:
+.short .proctype1 - .arglist1 - 2
+.short LF_ARGLIST
+.long 1 # no. entries
+.long T_UINT4
+
+# Type 1002, procedure (return type T_VOID, arglist 1001)
+.proctype1:
+.short .fieldlist1 - .proctype1 - 2
+.short LF_PROCEDURE
+.long T_VOID
+.byte 0 # calling convention
+.byte 0 # attributes
+.short 1 # no. parameters
+.long 0x1001
+
+# Type 1003, field list for struct bar
+.fieldlist1:
+.short .struct1 - .fieldlist1 - 2
+.short LF_FIELDLIST
+.short LF_MEMBER
+.short 3 # public
+.long T_UINT4
+.short 0 # offset
+.asciz "num1"
+.byte 0xf1 # padding
+
+# Type 1004, declaration of struct bar
+.struct1:
+.short .types_end - .struct1 - 2
+.short LF_STRUCTURE
+.short 1 # no. members
+.short 0 # property
+.long 0x1003 # field list
+.long 0 # type derived from
+.long 0 # type of vshape table
+.short 4 # size
+.asciz "bar" # name
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.types_end:
diff --git a/ld/testsuite/ld-pe/pdb-syms1b.s b/ld/testsuite/ld-pe/pdb-syms1b.s
new file mode 100644
index 00000000000..ddc471104b5
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-syms1b.s
@@ -0,0 +1,737 @@
+.equ CV_SIGNATURE_C13, 4
+.equ DEBUG_S_SYMBOLS, 0xf1
+
+.equ T_VOID, 0x0003
+.equ T_UQUAD, 0x0023
+.equ T_UINT4, 0x0075
+
+.equ LF_MODIFIER, 0x1001
+.equ LF_POINTER, 0x1002
+.equ LF_PROCEDURE, 0x1008
+.equ LF_MFUNCTION, 0x1009
+.equ LF_ARGLIST, 0x1201
+.equ LF_FIELDLIST, 0x1203
+.equ LF_CLASS, 0x1504
+.equ LF_STRUCTURE, 0x1505
+.equ LF_MEMBER, 0x150d
+.equ LF_ONEMETHOD, 0x1511
+.equ LF_FUNC_ID, 0x1601
+.equ LF_MFUNC_ID, 0x1602
+
+.equ LF_UQUADWORD, 0x800a
+
+.equ S_END, 0x0006
+.equ S_CONSTANT, 0x1107
+.equ S_UDT, 0x1108
+.equ S_LDATA32, 0x110c
+.equ S_GDATA32, 0x110d
+.equ S_LPROC32, 0x110f
+.equ S_GPROC32, 0x1110
+.equ S_LTHREAD32, 0x1112
+.equ S_GTHREAD32, 0x1113
+.equ S_LPROC32_ID, 0x1146
+.equ S_GPROC32_ID, 0x1147
+.equ S_PROC_ID_END, 0x114f
+
+.equ CV_PTR_64, 0xc
+
+.section ".debug$S", "rn"
+
+.long CV_SIGNATURE_C13
+
+.long DEBUG_S_SYMBOLS
+.long .syms_end - .syms_start
+
+.syms_start:
+
+.ldata1:
+.short .ldata1a - .ldata1 - 2
+.short S_LDATA32
+.long 0x1000 # const uint32_t
+.secrel32 lvar1
+.secidx lvar1
+.asciz "lvar1"
+
+.ldata1a: # duplicate with same address
+.short .ldata1b - .ldata1a - 2
+.short S_LDATA32
+.long 0x1000 # const uint32_t
+.secrel32 lvar1
+.secidx lvar1
+.asciz "lvar1"
+
+.ldata1b: # duplicate with different address
+.short .ldata2 - .ldata1b - 2
+.short S_LDATA32
+.long 0x1000 # const uint32_t
+.secrel32 lvar1a
+.secidx lvar1a
+.asciz "lvar1"
+
+.ldata2:
+.short .gdata1 - .ldata2 - 2
+.short S_LDATA32
+.long 0x1000 # const uint32_t
+.secrel32 lvar2
+.secidx lvar2
+.asciz "lvar2"
+
+.gdata1:
+.short .gdata2 - .gdata1 - 2
+.short S_GDATA32
+.long 0x1000 # const uint32_t
+.secrel32 gvar1
+.secidx gvar1
+.asciz "gvar1"
+
+.gdata2:
+.short .gproc1 - .gdata2 - 2
+.short S_GDATA32
+.long 0x1000 # const uint32_t
+.secrel32 gvar2
+.secidx gvar2
+.asciz "gvar2"
+
+.gproc1:
+.short .gproc1_end - .gproc1 - 2
+.short S_GPROC32
+.long 0 # parent
+.long 0 # end
+.long 0 # next symbol
+.long .proc1_end - proc1 # length
+.long 0 # debug start offset
+.long 0 # debug end offset
+.long 0x1002 # type
+.secrel32 proc1
+.secidx proc1
+.byte 0 # flags
+.asciz "proc1"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.gproc1_end:
+.short .gproc2 - .gproc1_end - 2
+.short S_END
+
+.gproc2:
+.short .udt1 - .gproc2 - 2
+.short S_GPROC32
+.long 0 # parent
+.long 0 # end
+.long 0 # next symbol
+.long .proc2_end - proc2 # length
+.long 0 # debug start offset
+.long 0 # debug end offset
+.long 0x1002 # type
+.secrel32 proc2
+.secidx proc2
+.byte 0 # flags
+.asciz "proc2"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.udt1:
+.short .ldata3 - .udt1 - 2
+.short S_UDT
+.long 0x1011 # struct bar
+.asciz "bar"
+
+.ldata3:
+.short .lthread1 - .ldata3 - 2
+.short S_LDATA32
+.long 0x1000 # const uint32_t
+.secrel32 lvar3
+.secidx lvar3
+.asciz "lvar3"
+
+.lthread1:
+.short .gproc2_end - .lthread1 - 2
+.short S_LTHREAD32
+.long 0x1000 # const uint32_t
+.secrel32 lvar4
+.secidx lvar4
+.asciz "lvar4"
+
+.gproc2_end:
+.short .gproc3 - .gproc2_end - 2
+.short S_END
+
+.gproc3:
+.short .gproc3_end - .gproc3 - 2
+.short S_LPROC32
+.long 0 # parent
+.long 0 # end
+.long 0 # next symbol
+.long .proc3_end - proc3 # length
+.long 0 # debug start offset
+.long 0 # debug end offset
+.long 0x1002 # type
+.secrel32 proc3
+.secidx proc3
+.byte 0 # flags
+.asciz "proc3"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.gproc3_end:
+.short .gproc4 - .gproc3_end - 2
+.short S_END
+
+.gproc4:
+.short .gproc4_end - .gproc4 - 2
+.short S_LPROC32
+.long 0 # parent
+.long 0 # end
+.long 0 # next symbol
+.long .proc4_end - proc4 # length
+.long 0 # debug start offset
+.long 0 # debug end offset
+.long 0x1002 # type
+.secrel32 proc4
+.secidx proc4
+.byte 0 # flags
+.asciz "proc4"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.gproc4_end:
+.short .gproc5 - .gproc4_end - 2
+.short S_END
+
+.gproc5:
+.short .gproc5_end - .gproc5 - 2
+.short S_GPROC32_ID
+.long 0 # parent
+.long 0 # end
+.long 0 # next symbol
+.long .proc5_end - proc5 # length
+.long 0 # debug start offset
+.long 0 # debug end offset
+.long 0x1003 # func ID
+.secrel32 proc5
+.secidx proc5
+.byte 0 # flags
+.asciz "proc5"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.gproc5_end:
+.short .gproc6 - .gproc5_end - 2
+.short S_PROC_ID_END
+
+.gproc6:
+.short .gproc6_end - .gproc6 - 2
+.short S_GPROC32_ID
+.long 0 # parent
+.long 0 # end
+.long 0 # next symbol
+.long .proc6_end - proc6 # length
+.long 0 # debug start offset
+.long 0 # debug end offset
+.long 0x1004 # func ID
+.secrel32 proc6
+.secidx proc6
+.byte 0 # flags
+.asciz "proc6"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.gproc6_end:
+.short .gproc7 - .gproc6_end - 2
+.short S_PROC_ID_END
+
+.gproc7:
+.short .gproc7_end - .gproc7 - 2
+.short S_GPROC32_ID
+.long 0 # parent
+.long 0 # end
+.long 0 # next symbol
+.long .proc7_end - proc7 # length
+.long 0 # debug start offset
+.long 0 # debug end offset
+.long 0x100a # func ID
+.secrel32 proc7
+.secidx proc7
+.byte 0 # flags
+.asciz "foo::method"
+.byte 0xf1 # padding
+
+.gproc7_end:
+.short .gproc8 - .gproc7_end - 2
+.short S_PROC_ID_END
+
+.gproc8:
+.short .gproc8_end - .gproc8 - 2
+.short S_GPROC32_ID
+.long 0 # parent
+.long 0 # end
+.long 0 # next symbol
+.long .proc8_end - proc8 # length
+.long 0 # debug start offset
+.long 0 # debug end offset
+.long 0x100b # func ID
+.secrel32 proc8
+.secidx proc8
+.byte 0 # flags
+.asciz "foo::method2"
+
+.gproc8_end:
+.short .gproc9 - .gproc8_end - 2
+.short S_PROC_ID_END
+
+.gproc9:
+.short .gproc9_end - .gproc9 - 2
+.short S_LPROC32_ID
+.long 0 # parent
+.long 0 # end
+.long 0 # next symbol
+.long .proc9_end - proc9 # length
+.long 0 # debug start offset
+.long 0 # debug end offset
+.long 0x100c # func ID
+.secrel32 proc9
+.secidx proc9
+.byte 0 # flags
+.asciz "proc9"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.gproc9_end:
+.short .gproc10 - .gproc9_end - 2
+.short S_PROC_ID_END
+
+.gproc10:
+.short .gproc10_end - .gproc10 - 2
+.short S_GPROC32_ID
+.long 0 # parent
+.long 0 # end
+.long 0 # next symbol
+.long .proc10_end - proc10 # length
+.long 0 # debug start offset
+.long 0 # debug end offset
+.long 0x100d # func ID
+.secrel32 proc10
+.secidx proc10
+.byte 0 # flags
+.asciz "proc10"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.gproc10_end:
+.short .gproc11 - .gproc10_end - 2
+.short S_PROC_ID_END
+
+.gproc11:
+.short .gproc11_end - .gproc11 - 2
+.short S_LPROC32_ID
+.long 0 # parent
+.long 0 # end
+.long 0 # next symbol
+.long .proc11_end - proc11 # length
+.long 0 # debug start offset
+.long 0 # debug end offset
+.long 0x100e # func ID
+.secrel32 proc11
+.secidx proc11
+.byte 0 # flags
+.asciz "foo::method3"
+
+.gproc11_end:
+.short .gproc12 - .gproc11_end - 2
+.short S_PROC_ID_END
+
+.gproc12:
+.short .gproc12_end - .gproc12 - 2
+.short S_LPROC32_ID
+.long 0 # parent
+.long 0 # end
+.long 0 # next symbol
+.long .proc12_end - proc12 # length
+.long 0 # debug start offset
+.long 0 # debug end offset
+.long 0x100f # func ID
+.secrel32 proc12
+.secidx proc12
+.byte 0 # flags
+.asciz "foo::method4"
+
+.gproc12_end:
+.short .udt2 - .gproc12_end - 2
+.short S_PROC_ID_END
+
+.udt2:
+.short .constant1 - .udt2 - 2
+.short S_UDT
+.long 0x1009 # class foo
+.asciz "foo"
+
+.constant1:
+.short .constant2 - .constant1 - 2
+.short S_CONSTANT
+.long T_UINT4
+.short 42
+.asciz "answer"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.constant2:
+.short .lthread2 - .constant2 - 2
+.short S_CONSTANT
+.long T_UQUAD
+.short LF_UQUADWORD
+.quad 0x0123456789abcdef
+.asciz "answer2"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.lthread2:
+.short .lthread3 - .lthread2 - 2
+.short S_LTHREAD32
+.long 0x1000 # const uint32_t
+.secrel32 lvar5
+.secidx lvar5
+.asciz "lvar5"
+
+.lthread3:
+.short .gthread1 - .lthread3 - 2
+.short S_LTHREAD32
+.long 0x1000 # const uint32_t
+.secrel32 lvar6
+.secidx lvar6
+.asciz "lvar6"
+
+.gthread1:
+.short .gthread2 - .gthread1 - 2
+.short S_GTHREAD32
+.long 0x1000 # const uint32_t
+.secrel32 gvar3
+.secidx gvar3
+.asciz "gvar3"
+
+.gthread2:
+.short .syms_end - .gthread2 - 2
+.short S_GTHREAD32
+.long 0x1000 # const uint32_t
+.secrel32 gvar4
+.secidx gvar4
+.asciz "gvar4"
+
+.p2align 2
+.syms_end:
+
+.section ".debug$T", "rn"
+
+.long CV_SIGNATURE_C13
+
+# Type 1000, const uint32_t
+.mod1:
+.short .arglist1 - .mod1 - 2
+.short LF_MODIFIER
+.long T_UINT4
+.short 1 # const
+.p2align 2
+
+# Type 1001, arglist (uint32_t)
+.arglist1:
+.short .proctype1 - .arglist1 - 2
+.short LF_ARGLIST
+.long 1 # no. entries
+.long T_UINT4
+
+# Type 1002, procedure (return type T_VOID, arglist 1001)
+.proctype1:
+.short .funcid1 - .proctype1 - 2
+.short LF_PROCEDURE
+.long T_VOID
+.byte 0 # calling convention
+.byte 0 # attributes
+.short 1 # no. parameters
+.long 0x1001
+
+# Type 1003, func ID for proc5
+.funcid1:
+.short .funcid2 - .funcid1 - 2
+.short LF_FUNC_ID
+.long 0 # parent scope
+.long 0x1002 # type
+.asciz "proc5"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1004, func ID for proc6
+.funcid2:
+.short .class1 - .funcid2 - 2
+.short LF_FUNC_ID
+.long 0 # parent scope
+.long 0x1002 # type
+.asciz "proc6"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1005, forward declaration of class foo
+.class1:
+.short .ptr1 - .class1 - 2
+.short LF_CLASS
+.short 0 # no. members
+.short 0x80 # property (forward declaration)
+.long 0 # field list
+.long 0 # type derived from
+.long 0 # type of vshape table
+.short 0 # size
+.asciz "foo" # name
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1006, pointer to 1005
+.ptr1:
+.short .mfunction1 - .ptr1 - 2
+.short LF_POINTER
+.long 0x1005
+.long (8 << 13) | CV_PTR_64
+
+# Type 1007, member function of 1005, return type void, arg list 1001
+.mfunction1:
+.short .fieldlist1 - .mfunction1 - 2
+.short LF_MFUNCTION
+.long T_VOID
+.long 0x1005
+.long 0x1006 # type of "this" pointer
+.byte 0 # calling convention
+.byte 0 # attributes
+.short 1 # no. parameters
+.long 0x1001 # arg list
+.long 0 # "this" adjustment
+
+# Type 1008, field list for class foo
+.fieldlist1:
+.short .class2 - .fieldlist1 - 2
+.short LF_FIELDLIST
+.short LF_ONEMETHOD
+.short 0 # method attribute
+.long 0x1007 # method type
+.asciz "method"
+.byte 0xf1 # padding
+.short LF_ONEMETHOD
+.short 0 # method attribute
+.long 0x1007 # method type
+.asciz "method2"
+.short LF_ONEMETHOD
+.short 0 # method attribute
+.long 0x1007 # method type
+.asciz "method3"
+.short LF_ONEMETHOD
+.short 0 # method attribute
+.long 0x1007 # method type
+.asciz "method4"
+
+# Type 1009, actual declaration of class foo
+.class2:
+.short .mfunc1 - .class2 - 2
+.short LF_CLASS
+.short 0 # no. members
+.short 0 # property
+.long 0x1008 # field list
+.long 0 # type derived from
+.long 0 # type of vshape table
+.short 0 # size
+.asciz "foo" # name
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 100a, function "method" within class "foo"
+.mfunc1:
+.short .mfunc2 - .mfunc1 - 2
+.short LF_MFUNC_ID
+.long 0x1009 # parent class
+.long 0x1002 # function type
+.asciz "method"
+.byte 0xf1 # padding
+
+# Type 100b, function "method2" within class "foo"
+.mfunc2:
+.short .funcid3 - .mfunc2 - 2
+.short LF_MFUNC_ID
+.long 0x1009 # parent class
+.long 0x1002 # function type
+.asciz "method2"
+
+# Type 100c, func ID for proc9
+.funcid3:
+.short .funcid4 - .funcid3 - 2
+.short LF_FUNC_ID
+.long 0 # parent scope
+.long 0x1002 # type
+.asciz "proc9"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 100d, func ID for proc10
+.funcid4:
+.short .mfunc3 - .funcid4 - 2
+.short LF_FUNC_ID
+.long 0 # parent scope
+.long 0x1002 # type
+.asciz "proc10"
+.byte 0xf1 # padding
+
+# Type 100e, function "method3" within class "foo"
+.mfunc3:
+.short .mfunc4 - .mfunc3 - 2
+.short LF_MFUNC_ID
+.long 0x1009 # parent class
+.long 0x1002 # function type
+.asciz "method3"
+
+# Type 100f, function "method4" within class "foo"
+.mfunc4:
+.short .fieldlist2 - .mfunc4 - 2
+.short LF_MFUNC_ID
+.long 0x1009 # parent class
+.long 0x1002 # function type
+.asciz "method4"
+
+# Type 1010, field list for struct bar
+.fieldlist2:
+.short .struct1 - .fieldlist2 - 2
+.short LF_FIELDLIST
+.short LF_MEMBER
+.short 3 # public
+.long T_UINT4
+.short 0 # offset
+.asciz "num1"
+.byte 0xf1 # padding
+
+# Type 1011, declaration of struct bar
+.struct1:
+.short .types_end - .struct1 - 2
+.short LF_STRUCTURE
+.short 1 # no. members
+.short 0 # property
+.long 0x1010 # field list
+.long 0 # type derived from
+.long 0 # type of vshape table
+.short 4 # size
+.asciz "bar" # name
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.types_end:
+
+.data
+
+.long 0x12345678
+.long 0x12345678
+
+lvar1:
+.long 42
+
+lvar1a:
+.long 0x12345678
+
+lvar3:
+.long 84
+
+lvar4:
+.long 85
+
+.global gvar1
+gvar1:
+.long 43
+
+.global gvar3
+gvar3:
+.long 41
+
+lvar5:
+.long 86
+
+.text
+
+.global main
+main:
+ jmp main
+ .secrel32 .data
+
+.global proc2
+proc2:
+ nop
+.proc2_end:
+
+.global proc4
+proc4:
+ nop
+.proc4_end:
+
+.global proc6
+proc6:
+ nop
+.proc6_end:
+
+.global proc8
+proc8:
+ nop
+.proc8_end:
+
+.global proc10
+proc10:
+ nop
+.proc10_end:
+
+.global proc12
+proc12:
+ nop
+.proc12_end:
+
+.section "gcsect"
+
+lvar2:
+.long 84
+
+.global gvar2
+gvar2:
+.long 85
+
+.global gvar4
+gvar4:
+.long 86
+
+.global proc1
+proc1:
+ nop
+.proc1_end:
+
+.global proc3
+proc3:
+ nop
+.proc3_end:
+
+.global proc5
+proc5:
+ nop
+.proc5_end:
+
+.global proc7
+proc7:
+ nop
+.proc7_end:
+
+.global proc9
+proc9:
+ nop
+.proc9_end:
+
+.global proc11
+proc11:
+ nop
+.proc11_end:
+
+lvar6:
+.long 86
diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp
index 1a5416890d1..34eafc142a7 100644
--- a/ld/testsuite/ld-pe/pdb.exp
+++ b/ld/testsuite/ld-pe/pdb.exp
@@ -1451,6 +1451,169 @@ proc test7 { } {
}
}
+proc test8 { } {
+ global as
+ global ar
+ global ld
+ global objdump
+ global srcdir
+ global subdir
+
+ if ![ld_assemble $as $srcdir/$subdir/pdb-syms1a.s tmpdir/pdb-syms1a.o] {
+ unsupported "Build pdb-syms1a.o"
+ return
+ }
+
+ if ![ld_assemble $as $srcdir/$subdir/pdb-syms1b.s tmpdir/pdb-syms1b.o] {
+ unsupported "Build pdb-syms1b.o"
+ return
+ }
+
+ if ![ld_link $ld "tmpdir/pdb-syms1.exe" "--pdb=tmpdir/pdb-syms1.pdb tmpdir/pdb-syms1a.o tmpdir/pdb-syms1b.o"] {
+ unsupported "Create PE image with PDB file"
+ return
+ }
+
+ # get index of globals stream and records stream
+
+ set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms1.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 12
+ set data [read $fi 2]
+ binary scan $data s globals_index
+
+ seek $fi 6 current
+ set data [read $fi 2]
+ binary scan $data s records_index
+
+ seek $fi 2 current
+ 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
+
+ # get index of first and second module streams
+
+ binary scan [string range $mod_info 34 35] s mod1_index
+
+ 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 mod2_index
+
+ # check globals stream
+
+ set index_str [format "%04x" $globals_index]
+
+ set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms1.pdb $index_str"]
+
+ if ![string match "" $exec_output] {
+ fail "Could not extract globals stream"
+ return
+ } else {
+ pass "Extracted globals stream"
+ }
+
+ set exp [file_contents "$srcdir/$subdir/pdb-syms1-globals.d"]
+ set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"]
+
+ if [string match $exp $got] {
+ pass "Correct globals stream"
+ } else {
+ fail "Incorrect globals stream"
+ }
+
+ # check records stream
+
+ set index_str [format "%04x" $records_index]
+
+ set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms1.pdb $index_str"]
+
+ if ![string match "" $exec_output] {
+ fail "Could not extract records stream"
+ return
+ } else {
+ pass "Extracted records stream"
+ }
+
+ set exp [file_contents "$srcdir/$subdir/pdb-syms1-records.d"]
+ set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"]
+
+ if [string match $exp $got] {
+ pass "Correct records stream"
+ } else {
+ fail "Incorrect records stream"
+ }
+
+ # check symbols in first module
+
+ set index_str [format "%04x" $mod1_index]
+
+ set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms1.pdb $index_str"]
+
+ if ![string match "" $exec_output] {
+ fail "Could not extract first module's symbols"
+ return
+ } else {
+ pass "Extracted first module's symbols"
+ }
+
+ set exp [file_contents "$srcdir/$subdir/pdb-syms1-symbols1.d"]
+ set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"]
+
+ if [string match $exp $got] {
+ pass "Correct symbols in first module's stream"
+ } else {
+ fail "Incorrect symbols in first module's stream"
+ }
+
+ # check symbols in second module
+
+ set index_str [format "%04x" $mod2_index]
+
+ set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms1.pdb $index_str"]
+
+ if ![string match "" $exec_output] {
+ fail "Could not extract second module's symbols"
+ return
+ } else {
+ pass "Extracted second module's symbols"
+ }
+
+ set exp [file_contents "$srcdir/$subdir/pdb-syms1-symbols2.d"]
+ set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"]
+
+ if [string match $exp $got] {
+ pass "Correct symbols in second module's stream"
+ } else {
+ fail "Incorrect symbols in second module's stream"
+ }
+}
+
test1
test2
test3
@@ -1458,3 +1621,4 @@ test4
test5
test6
test7
+test8
--
2.37.4
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 09/10] ld: Copy other symbols into PDB file
2022-12-09 1:52 [PATCH 01/10] ld: Generate PDB string table Mark Harmstone
` (6 preceding siblings ...)
2022-12-09 1:52 ` [PATCH 08/10] ld: Write globals stream in PDB Mark Harmstone
@ 2022-12-09 1:52 ` Mark Harmstone
2022-12-09 1:52 ` [PATCH 10/10] ld: Write linker symbols in PDB Mark Harmstone
2022-12-22 0:05 ` [PATCH 01/10] ld: Generate PDB string table Alan Modra
9 siblings, 0 replies; 14+ messages in thread
From: Mark Harmstone @ 2022-12-09 1:52 UTC (permalink / raw)
To: binutils, nickc; +Cc: Mark Harmstone
---
ld/pdb.c | 362 ++++++++++++++++++++
ld/pdb.h | 179 ++++++++++
ld/testsuite/ld-pe/pdb-syms2-symbols1.d | 38 +++
ld/testsuite/ld-pe/pdb-syms2.s | 430 ++++++++++++++++++++++++
ld/testsuite/ld-pe/pdb.exp | 67 ++++
5 files changed, 1076 insertions(+)
create mode 100644 ld/testsuite/ld-pe/pdb-syms2-symbols1.d
create mode 100644 ld/testsuite/ld-pe/pdb-syms2.s
diff --git a/ld/pdb.c b/ld/pdb.c
index 5257cda2e25..09377a02e52 100644
--- a/ld/pdb.c
+++ b/ld/pdb.c
@@ -907,11 +907,15 @@ find_end_of_scope (uint8_t *data, uint32_t size)
{
case S_GPROC32:
case S_LPROC32:
+ case S_BLOCK32:
+ case S_INLINESITE:
+ case S_THUNK32:
scope_level++;
break;
case S_END:
case S_PROC_ID_END:
+ case S_INLINESITE_END:
scope_level--;
if (scope_level == 0)
@@ -960,6 +964,7 @@ parse_symbols (uint8_t *data, uint32_t size, uint8_t **buf,
{
uint8_t *orig_buf = *buf;
unsigned int scope_level = 0;
+ uint8_t *scope = NULL;
while (size >= sizeof (uint16_t))
{
@@ -1205,6 +1210,8 @@ parse_symbols (uint8_t *data, uint32_t size, uint8_t **buf,
free (ref);
+ scope = *buf;
+
memcpy (*buf, proc, len);
*buf += len;
@@ -1320,16 +1327,343 @@ parse_symbols (uint8_t *data, uint32_t size, uint8_t **buf,
}
case S_END:
+ case S_INLINESITE_END:
case S_PROC_ID_END:
memcpy (*buf, data, len);
if (type == S_PROC_ID_END) /* transform to S_END */
bfd_putl16 (S_END, *buf + sizeof (uint16_t));
+ /* Reset scope variable back to the address of the previous
+ scope start. */
+ if (scope)
+ {
+ uint32_t parent;
+ uint16_t scope_start_type =
+ bfd_getl16 (scope + sizeof (uint16_t));
+
+ switch (scope_start_type)
+ {
+ case S_GPROC32:
+ case S_LPROC32:
+ parent = bfd_getl32 (scope + offsetof (struct procsym,
+ parent));
+ break;
+
+ case S_BLOCK32:
+ parent = bfd_getl32 (scope + offsetof (struct blocksym,
+ parent));
+ break;
+
+ case S_INLINESITE:
+ parent = bfd_getl32 (scope + offsetof (struct inline_site,
+ parent));
+ break;
+
+ case S_THUNK32:
+ parent = bfd_getl32 (scope + offsetof (struct thunk,
+ parent));
+ break;
+
+ default:
+ einfo (_("%P: warning: unexpected CodeView scope start"
+ " record %v\n"), scope_start_type);
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ if (parent == 0)
+ scope = NULL;
+ else
+ scope = orig_buf + parent - sizeof (uint32_t);
+ }
+
*buf += len;
scope_level--;
break;
+ case S_BUILDINFO:
+ {
+ struct buildinfosym *bi = (struct buildinfosym *) data;
+
+ if (len < sizeof (struct buildinfosym))
+ {
+ einfo (_("%P: warning: truncated CodeView record"
+ " S_BUILDINFO\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ if (!remap_symbol_type (&bi->type, map, num_types))
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ memcpy (*buf, data, len);
+ *buf += len;
+
+ break;
+ }
+
+ case S_BLOCK32:
+ {
+ struct blocksym *bl = (struct blocksym *) data;
+ uint8_t *endptr;
+ uint32_t end;
+
+ if (len < offsetof (struct blocksym, name))
+ {
+ einfo (_("%P: warning: truncated CodeView record"
+ " S_BLOCK32\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ bfd_putl32 (scope - orig_buf + sizeof (uint32_t), &bl->parent);
+
+ endptr = find_end_of_scope (data, size);
+
+ if (!endptr)
+ {
+ einfo (_("%P: warning: could not find end of"
+ " S_BLOCK32 record\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ end = *buf - orig_buf + sizeof (uint32_t) + endptr - data;
+ bfd_putl32 (end, &bl->end);
+
+ scope = *buf;
+
+ memcpy (*buf, data, len);
+ *buf += len;
+
+ scope_level++;
+
+ break;
+ }
+
+ case S_BPREL32:
+ {
+ struct bprelsym *bp = (struct bprelsym *) data;
+
+ if (len < offsetof (struct bprelsym, name))
+ {
+ einfo (_("%P: warning: truncated CodeView record"
+ " S_BPREL32\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ if (!remap_symbol_type (&bp->type, map, num_types))
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ memcpy (*buf, data, len);
+ *buf += len;
+
+ break;
+ }
+
+ case S_REGISTER:
+ {
+ struct regsym *reg = (struct regsym *) data;
+
+ if (len < offsetof (struct regsym, name))
+ {
+ einfo (_("%P: warning: truncated CodeView record"
+ " S_REGISTER\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ if (!remap_symbol_type (®->type, map, num_types))
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ memcpy (*buf, data, len);
+ *buf += len;
+
+ break;
+ }
+
+ case S_REGREL32:
+ {
+ struct regrel *rr = (struct regrel *) data;
+
+ if (len < offsetof (struct regrel, name))
+ {
+ einfo (_("%P: warning: truncated CodeView record"
+ " S_REGREL32\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ if (!remap_symbol_type (&rr->type, map, num_types))
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ memcpy (*buf, data, len);
+ *buf += len;
+
+ break;
+ }
+
+ case S_LOCAL:
+ {
+ struct localsym *l = (struct localsym *) data;
+
+ if (len < offsetof (struct localsym, name))
+ {
+ einfo (_("%P: warning: truncated CodeView record"
+ " S_LOCAL\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ if (!remap_symbol_type (&l->type, map, num_types))
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ memcpy (*buf, data, len);
+ *buf += len;
+
+ break;
+ }
+
+ case S_INLINESITE:
+ {
+ struct inline_site *is = (struct inline_site *) data;
+ uint8_t *endptr;
+ uint32_t end;
+
+ if (len < offsetof (struct inline_site, binary_annotations))
+ {
+ einfo (_("%P: warning: truncated CodeView record"
+ " S_INLINESITE\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ bfd_putl32 (scope - orig_buf + sizeof (uint32_t), &is->parent);
+
+ endptr = find_end_of_scope (data, size);
+
+ if (!endptr)
+ {
+ einfo (_("%P: warning: could not find end of"
+ " S_INLINESITE record\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ end = *buf - orig_buf + sizeof (uint32_t) + endptr - data;
+ bfd_putl32 (end, &is->end);
+
+ if (!remap_symbol_type (&is->inlinee, map, num_types))
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ scope = *buf;
+
+ memcpy (*buf, data, len);
+ *buf += len;
+
+ scope_level++;
+
+ break;
+ }
+
+ case S_THUNK32:
+ {
+ struct thunk *th = (struct thunk *) data;
+ uint8_t *endptr;
+ uint32_t end;
+
+ if (len < offsetof (struct thunk, name))
+ {
+ einfo (_("%P: warning: truncated CodeView record"
+ " S_THUNK32\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ bfd_putl32 (scope - orig_buf + sizeof (uint32_t), &th->parent);
+
+ endptr = find_end_of_scope (data, size);
+
+ if (!endptr)
+ {
+ einfo (_("%P: warning: could not find end of"
+ " S_THUNK32 record\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ end = *buf - orig_buf + sizeof (uint32_t) + endptr - data;
+ bfd_putl32 (end, &th->end);
+
+ scope = *buf;
+
+ memcpy (*buf, data, len);
+ *buf += len;
+
+ scope_level++;
+
+ break;
+ }
+
+ case S_HEAPALLOCSITE:
+ {
+ struct heap_alloc_site *has = (struct heap_alloc_site *) data;
+
+ if (len < sizeof (struct heap_alloc_site))
+ {
+ einfo (_("%P: warning: truncated CodeView record"
+ " S_HEAPALLOCSITE\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ if (!remap_symbol_type (&has->type, map, num_types))
+ {
+ bfd_set_error (bfd_error_bad_value);
+ return false;
+ }
+
+ memcpy (*buf, data, len);
+ *buf += len;
+
+ break;
+ }
+
+ case S_OBJNAME: /* just copy */
+ case S_COMPILE3:
+ case S_UNAMESPACE:
+ case S_FRAMEPROC:
+ case S_FRAMECOOKIE:
+ case S_LABEL32:
+ case S_DEFRANGE_REGISTER_REL:
+ case S_DEFRANGE_FRAMEPOINTER_REL:
+ case S_DEFRANGE_SUBFIELD_REGISTER:
+ case S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE:
+ case S_DEFRANGE_REGISTER:
+ memcpy (*buf, data, len);
+ *buf += len;
+ break;
+
default:
einfo (_("%P: warning: unrecognized CodeView record %v\n"), type);
bfd_set_error (bfd_error_bad_value);
@@ -1440,12 +1774,40 @@ calculate_symbols_size (uint8_t *data, uint32_t size, uint32_t *sym_size)
*sym_size += len;
break;
+ case S_BLOCK32: /* always copied */
+ case S_INLINESITE:
+ case S_THUNK32:
+ *sym_size += len;
+ scope_level++;
+ break;
+
case S_END: /* always copied */
case S_PROC_ID_END:
+ case S_INLINESITE_END:
*sym_size += len;
scope_level--;
break;
+ case S_OBJNAME: /* always copied */
+ case S_COMPILE3:
+ case S_UNAMESPACE:
+ case S_FRAMEPROC:
+ case S_FRAMECOOKIE:
+ case S_LABEL32:
+ case S_BUILDINFO:
+ case S_BPREL32:
+ case S_REGISTER:
+ case S_REGREL32:
+ case S_LOCAL:
+ case S_DEFRANGE_REGISTER_REL:
+ case S_DEFRANGE_FRAMEPOINTER_REL:
+ case S_DEFRANGE_SUBFIELD_REGISTER:
+ case S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE:
+ case S_DEFRANGE_REGISTER:
+ case S_HEAPALLOCSITE:
+ *sym_size += len;
+ break;
+
default:
einfo (_("%P: warning: unrecognized CodeView record %v\n"), type);
return false;
diff --git a/ld/pdb.h b/ld/pdb.h
index 9048b5fa37c..ddb9b86ce85 100644
--- a/ld/pdb.h
+++ b/ld/pdb.h
@@ -71,20 +71,41 @@
#define LF_UQUADWORD 0x800a
#define S_END 0x0006
+#define S_FRAMEPROC 0x1012
+#define S_OBJNAME 0x1101
+#define S_THUNK32 0x1102
+#define S_BLOCK32 0x1103
+#define S_LABEL32 0x1105
+#define S_REGISTER 0x1106
#define S_CONSTANT 0x1107
#define S_UDT 0x1108
+#define S_BPREL32 0x110b
#define S_LDATA32 0x110c
#define S_GDATA32 0x110d
#define S_PUB32 0x110e
#define S_LPROC32 0x110f
#define S_GPROC32 0x1110
+#define S_REGREL32 0x1111
#define S_LTHREAD32 0x1112
#define S_GTHREAD32 0x1113
+#define S_UNAMESPACE 0x1124
#define S_PROCREF 0x1125
#define S_LPROCREF 0x1127
+#define S_FRAMECOOKIE 0x113a
+#define S_COMPILE3 0x113c
+#define S_LOCAL 0x113e
+#define S_DEFRANGE_REGISTER 0x1141
+#define S_DEFRANGE_FRAMEPOINTER_REL 0x1142
+#define S_DEFRANGE_SUBFIELD_REGISTER 0x1143
+#define S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE 0x1144
+#define S_DEFRANGE_REGISTER_REL 0x1145
#define S_LPROC32_ID 0x1146
#define S_GPROC32_ID 0x1147
+#define S_BUILDINFO 0x114c
+#define S_INLINESITE 0x114d
+#define S_INLINESITE_END 0x114e
#define S_PROC_ID_END 0x114f
+#define S_HEAPALLOCSITE 0x115e
/* PDBStream70 in pdb1.h */
struct pdb_stream_70
@@ -617,6 +638,164 @@ struct constsym
char name[];
} ATTRIBUTE_PACKED;
+/* BUILDINFOSYM in cvinfo.h */
+struct buildinfosym
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t type;
+} ATTRIBUTE_PACKED;
+
+/* BLOCKSYM32 in cvinfo.h */
+struct blocksym
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t parent;
+ uint32_t end;
+ uint32_t len;
+ uint32_t offset;
+ uint16_t section;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* BPRELSYM32 in cvinfo.h */
+struct bprelsym
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t bp_offset;
+ uint32_t type;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* REGSYM in cvinfo.h */
+struct regsym
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t type;
+ uint16_t reg;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* REGREL32 in cvinfo.h */
+struct regrel
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t offset;
+ uint32_t type;
+ uint16_t reg;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* LOCALSYM in cvinfo.h */
+struct localsym
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t type;
+ uint16_t flags;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* CV_LVAR_ADDR_RANGE in cvinfo.h */
+struct lvar_addr_range
+{
+ uint32_t offset;
+ uint16_t section;
+ uint16_t length;
+} ATTRIBUTE_PACKED;
+
+/* CV_LVAR_ADDR_GAP in cvinfo.h */
+struct lvar_addr_gap {
+ uint16_t offset;
+ uint16_t length;
+} ATTRIBUTE_PACKED;
+
+/* DEFRANGESYMREGISTERREL in cvinfo.h */
+struct defrange_register_rel
+{
+ uint16_t size;
+ uint16_t kind;
+ uint16_t reg;
+ uint16_t offset_parent;
+ uint32_t offset_register;
+ struct lvar_addr_range range;
+ struct lvar_addr_gap gaps[];
+} ATTRIBUTE_PACKED;
+
+/* DEFRANGESYMFRAMEPOINTERREL in cvinfo.h */
+struct defrange_framepointer_rel
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t offset;
+ struct lvar_addr_range range;
+ struct lvar_addr_gap gaps[];
+} ATTRIBUTE_PACKED;
+
+/* DEFRANGESYMSUBFIELDREGISTER in cvinfo.h */
+struct defrange_subfield_register
+{
+ uint16_t size;
+ uint16_t kind;
+ uint16_t reg;
+ uint16_t attributes;
+ uint32_t offset_parent;
+ struct lvar_addr_range range;
+ struct lvar_addr_gap gaps[];
+} ATTRIBUTE_PACKED;
+
+/* DEFRANGESYMREGISTER in cvinfo.h */
+struct defrange_register
+{
+ uint16_t size;
+ uint16_t kind;
+ uint16_t reg;
+ uint16_t attributes;
+ struct lvar_addr_range range;
+ struct lvar_addr_gap gaps[];
+} ATTRIBUTE_PACKED;
+
+/* INLINESITESYM in cvinfo.h */
+struct inline_site
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t parent;
+ uint32_t end;
+ uint32_t inlinee;
+ uint8_t binary_annotations[];
+} ATTRIBUTE_PACKED;
+
+/* THUNKSYM32 in cvinfo.h */
+struct thunk
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t parent;
+ uint32_t end;
+ uint32_t next;
+ uint32_t offset;
+ uint16_t section;
+ uint16_t length;
+ uint8_t thunk_type;
+ char name[];
+} ATTRIBUTE_PACKED;
+
+/* HEAPALLOCSITE in cvinfo.h */
+struct heap_alloc_site
+{
+ uint16_t size;
+ uint16_t kind;
+ uint32_t offset;
+ uint16_t section;
+ uint16_t length;
+ uint32_t type;
+} ATTRIBUTE_PACKED;
+
extern bool create_pdb_file (bfd *, const char *, const unsigned char *);
#endif
diff --git a/ld/testsuite/ld-pe/pdb-syms2-symbols1.d b/ld/testsuite/ld-pe/pdb-syms2-symbols1.d
new file mode 100644
index 00000000000..34132d1264e
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-syms2-symbols1.d
@@ -0,0 +1,38 @@
+
+*: file format binary
+
+Contents of section .data:
+ 0000 04000000 0e000111 00000000 73796d73 ............syms
+ 0010 332e6f00 22003c11 00000000 d0000000 3.o.".<.........
+ 0020 00000000 00000000 00000000 0000474e ..............GN
+ 0030 55204153 00f3f2f1 06002411 73746400 U AS......$.std.
+ 0040 06004c11 05100000 2e001011 00000000 ..L.............
+ 0050 18020000 00000000 06000000 00000000 ................
+ 0060 00000000 01100000 00000000 01000070 ...............p
+ 0070 726f6331 00f3f2f1 1e001210 00000000 roc1............
+ 0080 00000000 00000000 00000000 00000000 ................
+ 0090 00000000 0000f2f1 0e003a11 08000000 ..........:.....
+ 00a0 48010000 000000f1 0e000b11 04000000 H...............
+ 00b0 02100000 666f6f00 0e000611 02100000 ....foo.........
+ 00c0 48016261 7200f2f1 12001111 04000000 H.bar...........
+ 00d0 02100000 48016261 7a00f2f1 12003e11 ....H.baz.....>.
+ 00e0 02100000 00006c6f 63616c31 00f3f2f1 ......local1....
+ 00f0 16004511 48010000 00000000 01000000 ..E.H...........
+ 0100 01000400 02000100 12003e11 02100000 ..........>.....
+ 0110 00006c6f 63616c32 00f3f2f1 12004211 ..local2......B.
+ 0120 04000000 01000000 01000400 02000100 ................
+ 0130 12003e11 02100000 00006c6f 63616c33 ..>.......local3
+ 0140 00f3f2f1 16004311 48010000 04000000 ......C.H.......
+ 0150 01000000 01000400 02000100 12003e11 ..............>.
+ 0160 02100000 00006c6f 63616c34 00f3f2f1 ......local4....
+ 0170 06004411 04000000 12003e11 02100000 ..D.......>.....
+ 0180 00006c6f 63616c35 00f3f2f1 12004111 ..local5......A.
+ 0190 48010000 01000000 01000400 02000100 H...............
+ 01a0 0e004d11 48000000 b0010000 06100000 ..M.H...........
+ 01b0 02004e11 16000311 48000000 e0010000 ..N.....H.......
+ 01c0 04000000 01000000 010000f1 12000511 ................
+ 01d0 02000000 0100006c 6162656c 00f3f2f1 .......label....
+ 01e0 02000600 1e000211 48000000 04020000 ........H.......
+ 01f0 00000000 06000000 01000100 00746875 .............thu
+ 0200 6e6b00f1 02000600 0e005e11 04000000 nk........^.....
+ 0210 01000100 02100000 02000600 00000000 ................
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-syms2.s b/ld/testsuite/ld-pe/pdb-syms2.s
new file mode 100644
index 00000000000..ec677eaee43
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-syms2.s
@@ -0,0 +1,430 @@
+.equ CV_SIGNATURE_C13, 4
+.equ DEBUG_S_SYMBOLS, 0xf1
+
+.equ T_VOID, 0x0003
+.equ T_UINT4, 0x0075
+
+.equ LF_MODIFIER, 0x1001
+.equ LF_PROCEDURE, 0x1008
+.equ LF_ARGLIST, 0x1201
+.equ LF_FUNC_ID, 0x1601
+.equ LF_BUILDINFO, 0x1603
+.equ LF_STRING_ID, 0x1605
+
+.equ S_END, 0x0006
+.equ S_FRAMEPROC, 0x1012
+.equ S_OBJNAME, 0x1101
+.equ S_THUNK32, 0x1102
+.equ S_BLOCK32, 0x1103
+.equ S_LABEL32, 0x1105
+.equ S_REGISTER, 0x1106
+.equ S_BPREL32, 0x110b
+.equ S_GPROC32, 0x1110
+.equ S_REGREL32, 0x1111
+.equ S_UNAMESPACE, 0x1124
+.equ S_FRAMECOOKIE, 0x113a
+.equ S_COMPILE3, 0x113c
+.equ S_LOCAL, 0x113e
+.equ S_DEFRANGE_REGISTER, 0x1141
+.equ S_DEFRANGE_FRAMEPOINTER_REL, 0x1142
+.equ S_DEFRANGE_SUBFIELD_REGISTER, 0x1143
+.equ S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE, 0x1144
+.equ S_DEFRANGE_REGISTER_REL, 0x1145
+.equ S_BUILDINFO, 0x114c
+.equ S_INLINESITE, 0x114d
+.equ S_INLINESITE_END, 0x114e
+.equ S_HEAPALLOCSITE, 0x115e
+
+.equ CV_AMD64_RAX, 328
+.equ CV_CFL_AMD64, 0xd0
+
+.section ".debug$S", "rn"
+
+.long CV_SIGNATURE_C13
+
+.long DEBUG_S_SYMBOLS
+.long .syms_end - .syms_start
+
+.syms_start:
+
+.objname1:
+.short .compile1 - .objname1 - 2
+.short S_OBJNAME
+.long 0 # signature
+.asciz "syms3.o"
+
+.compile1:
+.short .unamespace1 - .compile1 - 2
+.short S_COMPILE3
+.long 0 # flags
+.short CV_CFL_AMD64 # target processor
+.short 0 # frontend major
+.short 0 # frontend minor
+.short 0 # frontend build
+.short 0 # frontend qfe
+.short 0 # backend major
+.short 0 # backend minor
+.short 0 # backend build
+.short 0 # backend qfe
+.asciz "GNU AS"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.unamespace1:
+.short .sbuildinfo1 - .unamespace1 - 2
+.short S_UNAMESPACE
+.asciz "std"
+
+.sbuildinfo1:
+.short .gproc1 - .sbuildinfo1 - 2
+.short S_BUILDINFO
+.long 0x1007 # type
+
+.gproc1:
+.short .frameproc1 - .gproc1 - 2
+.short S_GPROC32
+.long 0 # parent
+.long 0 # end
+.long 0 # next symbol
+.long .proc1_end - proc1 # length
+.long 0 # debug start offset
+.long 0 # debug end offset
+.long 0x1001 # type
+.secrel32 proc1
+.secidx proc1
+.byte 0 # flags
+.asciz "proc1"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.frameproc1:
+.short .framecookie1 - .frameproc1 - 2
+.short S_FRAMEPROC
+.long 0 # frame size
+.long 0 # frame padding
+.long 0 # padding offset
+.long 0 # size of callee-save registers
+.long 0 # offset of exception handler
+.short 0 # section of exception handler
+.long 0 # flags
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.framecookie1:
+.short .bprel1 - .framecookie1 - 2
+.short S_FRAMECOOKIE
+.long 8 # frame-relative offset
+.short CV_AMD64_RAX # register
+.long 0 # cookie type (CV_COOKIETYPE_COPY)
+.byte 0 # flags
+.byte 0xf1 # padding
+
+.bprel1:
+.short .reg1 - .bprel1 - 2
+.short S_BPREL32
+.long 4 # BP-relative offset
+.long 0x1008 # type
+.asciz "foo"
+
+.reg1:
+.short .regrel1 - .reg1 - 2
+.short S_REGISTER
+.long 0x1008 # type
+.short CV_AMD64_RAX
+.asciz "bar"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.regrel1:
+.short .local1 - .regrel1 - 2
+.short S_REGREL32
+.long 4 # offset
+.long 0x1008 # type
+.short CV_AMD64_RAX
+.asciz "baz"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.local1:
+.short .defrange1 - .local1 - 2
+.short S_LOCAL
+.long 0x1008 # type
+.short 0 # flags
+.asciz "local1"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.defrange1:
+.short .local2 - .defrange1 - 2
+.short S_DEFRANGE_REGISTER_REL
+.short CV_AMD64_RAX
+.short 0 # offset parent
+.long 0 # offset register
+.secrel32 .block1 # offset
+.secidx .block1 # section
+.short .block1_end - .block1 # length
+.short .gap1 - .block1 # gap 1 offset
+.short .gap1_end - .gap1 # gap 1 length
+
+.local2:
+.short .defrange2 - .local2 - 2
+.short S_LOCAL
+.long 0x1008 # type
+.short 0 # flags
+.asciz "local2"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.defrange2:
+.short .local3 - .defrange2 - 2
+.short S_DEFRANGE_FRAMEPOINTER_REL
+.long 4 # frame pointer offset
+.secrel32 .block1 # offset
+.secidx .block1 # section
+.short .block1_end - .block1 # length
+.short .gap1 - .block1 # gap 1 offset
+.short .gap1_end - .gap1 # gap 1 length
+
+.local3:
+.short .defrange3 - .local3 - 2
+.short S_LOCAL
+.long 0x1008 # type
+.short 0 # flags
+.asciz "local3"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.defrange3:
+.short .local4 - .defrange3 - 2
+.short S_DEFRANGE_SUBFIELD_REGISTER
+.short CV_AMD64_RAX
+.short 0 # attributes
+.long 4 # offset in parent variable
+.secrel32 .block1 # offset
+.secidx .block1 # section
+.short .block1_end - .block1 # length
+.short .gap1 - .block1 # gap 1 offset
+.short .gap1_end - .gap1 # gap 1 length
+
+.local4:
+.short .defrange4 - .local4 - 2
+.short S_LOCAL
+.long 0x1008 # type
+.short 0 # flags
+.asciz "local4"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.defrange4:
+.short .local5 - .defrange4 - 2
+.short S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE
+.long 4 # frame pointer offset
+
+.local5:
+.short .defrange5 - .local5 - 2
+.short S_LOCAL
+.long 0x1008 # type
+.short 0 # flags
+.asciz "local5"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.defrange5:
+.short .inlinesite1 - .defrange5 - 2
+.short S_DEFRANGE_REGISTER
+.short CV_AMD64_RAX
+.short 0 # attributes
+.secrel32 .block1 # offset
+.secidx .block1 # section
+.short .block1_end - .block1 # length
+.short .gap1 - .block1 # gap 1 offset
+.short .gap1_end - .gap1 # gap 1 length
+
+.inlinesite1:
+.short .inlinesite1end - .inlinesite1 - 2
+.short S_INLINESITE
+.long 0 # parent
+.long 0 # end
+.long 0x1009 # inlinee (inline_func)
+
+.inlinesite1end:
+.short .sblock1 - .inlinesite1end - 2
+.short S_INLINESITE_END
+
+.sblock1:
+.short .label1 - .sblock1 - 2
+.short S_BLOCK32
+.long 0 # parent (filled in by linker)
+.long 0 # end (filled in by linker)
+.long .block1_end - .block1 # length
+.secrel32 .block1
+.secidx .block1
+.byte 0 # name
+.byte 0xf1 # padding
+
+.label1:
+.short .sblock1_end - .label1 - 2
+.short S_LABEL32
+.secrel32 label
+.secidx label
+.byte 0 # flags
+.asciz "label"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+.sblock1_end:
+.short .thunk1 - .sblock1_end - 2
+.short S_END
+
+.thunk1:
+.short .thunk1_end - .thunk1 - 2
+.short S_THUNK32
+.long 0 # parent
+.long 0 # end
+.long 0 # next
+.secrel32 thunk
+.secidx thunk
+.short .thunk_end - thunk
+.byte 0 # THUNK_ORDINAL value
+.asciz "thunk"
+.byte 0xf1 # padding
+
+.thunk1_end:
+.short .heapallocsite1 - .thunk1_end - 2
+.short S_END
+
+.heapallocsite1:
+.short .gproc1_end - .heapallocsite1 - 2
+.short S_HEAPALLOCSITE
+.secrel32 .gap1_end
+.secidx .gap1_end
+.short .block1_end - .gap1_end
+.long 0x1008 # type
+
+.gproc1_end:
+.short .syms_end - .gproc1_end - 2
+.short S_END
+
+.syms_end:
+
+.section ".debug$T", "rn"
+
+.long CV_SIGNATURE_C13
+
+# Type 1000, arglist (uint32_t)
+.arglist1:
+.short .proctype1 - .arglist1 - 2
+.short LF_ARGLIST
+.long 1 # no. entries
+.long T_UINT4
+
+# Type 1001, procedure (return type T_VOID, arglist 1000)
+.proctype1:
+.short .string1 - .proctype1 - 2
+.short LF_PROCEDURE
+.long T_VOID
+.byte 0 # calling convention
+.byte 0 # attributes
+.short 1 # no. parameters
+.long 0x1000
+
+# Type 1002, string "/tmp" (build directory)
+.string1:
+.short .string2 - .string1 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "/tmp"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1003, string "gcc" (compiler)
+.string2:
+.short .string3 - .string2 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "gcc"
+
+# Type 1004, string "tmp.c" (source file)
+.string3:
+.short .string4 - .string3 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "tmp.c"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1005, string "tmp.pdb" (PDB file)
+.string4:
+.short .string5 - .string4 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "tmp.pdb"
+
+# Type 1006, string "-gcodeview" (command arguments)
+.string5:
+.short .buildinfo1 - .string5 - 2
+.short LF_STRING_ID
+.long 0 # sub-string
+.asciz "-gcodeview"
+.byte 0xf1 # padding
+
+# Type 1007, build info
+.buildinfo1:
+.short .mod1 - .buildinfo1 - 2
+.short LF_BUILDINFO
+.short 5 # count
+.long 0x1002 # build directory
+.long 0x1003 # compiler
+.long 0x1004 # source file
+.long 0x1005 # PDB file
+.long 0x1006 # command arguments
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1008, const uint32_t
+.mod1:
+.short .funcid1 - .mod1 - 2
+.short LF_MODIFIER
+.long T_UINT4
+.short 1 # const
+.p2align 2
+
+# Type 1009, func ID for inline_func
+.funcid1:
+.short .types_end - .funcid1 - 2
+.short LF_FUNC_ID
+.long 0 # parent scope
+.long 0x1001 # type
+.asciz "inline_func"
+
+.types_end:
+
+.text
+
+.global proc1
+proc1:
+ nop
+.block1:
+ nop
+label:
+ nop
+.gap1:
+ nop
+.gap1_end:
+ nop
+.block1_end:
+ nop
+.proc1_end:
+
+thunk:
+ nop
+.thunk_end:
diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp
index 34eafc142a7..5df1583c247 100644
--- a/ld/testsuite/ld-pe/pdb.exp
+++ b/ld/testsuite/ld-pe/pdb.exp
@@ -1614,6 +1614,72 @@ proc test8 { } {
}
}
+proc test9 { } {
+ global as
+ global ar
+ global ld
+ global objdump
+ global srcdir
+ global subdir
+
+ if ![ld_assemble $as $srcdir/$subdir/pdb-syms2.s tmpdir/pdb-syms2.o] {
+ unsupported "Build pdb-syms2.o"
+ return
+ }
+
+ if ![ld_link $ld "tmpdir/pdb-syms2.exe" "--pdb=tmpdir/pdb-syms2.pdb tmpdir/pdb-syms2.o"] {
+ unsupported "Create PE image with PDB file"
+ return
+ }
+
+ # get index of module stream
+
+ set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-syms2.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
+ 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
+
+ binary scan [string range $mod_info 34 35] s module_index
+
+ # check module records
+
+ set index_str [format "%04x" $module_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 module symbols"
+ return
+ } else {
+ pass "Extracted module symbols"
+ }
+
+ set exp [file_contents "$srcdir/$subdir/pdb-syms2-symbols1.d"]
+ set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/$index_str"]
+
+ if [string match $exp $got] {
+ pass "Correct symbols in module stream"
+ } else {
+ fail "Incorrect symbols in module stream"
+ }
+}
+
test1
test2
test3
@@ -1622,3 +1688,4 @@ test5
test6
test7
test8
+test9
--
2.37.4
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 10/10] ld: Write linker symbols in PDB
2022-12-09 1:52 [PATCH 01/10] ld: Generate PDB string table Mark Harmstone
` (7 preceding siblings ...)
2022-12-09 1:52 ` [PATCH 09/10] ld: Copy other symbols into PDB file Mark Harmstone
@ 2022-12-09 1:52 ` Mark Harmstone
2022-12-22 0:05 ` [PATCH 01/10] ld: Generate PDB string table Alan Modra
9 siblings, 0 replies; 14+ messages in thread
From: Mark Harmstone @ 2022-12-09 1:52 UTC (permalink / raw)
To: binutils, nickc; +Cc: Mark Harmstone
---
ld/pdb.c | 242 +++++++++++++++++++++++++++++++------
ld/pdb.h | 42 +++++++
ld/testsuite/ld-pe/pdb.exp | 77 ++++++++++++
3 files changed, 327 insertions(+), 34 deletions(-)
diff --git a/ld/pdb.c b/ld/pdb.c
index 09377a02e52..0346ccb388c 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"
@@ -3420,6 +3421,168 @@ 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
@@ -3429,55 +3592,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. */
@@ -3529,7 +3702,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;
@@ -3619,7 +3793,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++)
{
@@ -4108,7 +4282,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;
@@ -4130,7 +4304,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;
@@ -4822,7 +4996,7 @@ 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 ddb9b86ce85..749a60249df 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
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 01/10] ld: Generate PDB string table
2022-12-09 1:52 [PATCH 01/10] ld: Generate PDB string table Mark Harmstone
` (8 preceding siblings ...)
2022-12-09 1:52 ` [PATCH 10/10] ld: Write linker symbols in PDB Mark Harmstone
@ 2022-12-22 0:05 ` Alan Modra
2022-12-22 1:25 ` Alan Modra
9 siblings, 1 reply; 14+ messages in thread
From: Alan Modra @ 2022-12-22 0:05 UTC (permalink / raw)
To: Mark Harmstone; +Cc: binutils, nickc
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
Australia Development Lab, IBM
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 01/10] ld: Generate PDB string table
2022-12-22 0:05 ` [PATCH 01/10] ld: Generate PDB string table Alan Modra
@ 2022-12-22 1:25 ` Alan Modra
2022-12-22 10:22 ` Alan Modra
0 siblings, 1 reply; 14+ messages in thread
From: Alan Modra @ 2022-12-22 1:25 UTC (permalink / raw)
To: Mark Harmstone; +Cc: binutils, nickc
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
Australia Development Lab, IBM
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 01/10] ld: Generate PDB string table
2022-12-22 1:25 ` Alan Modra
@ 2022-12-22 10:22 ` Alan Modra
2022-12-23 10:54 ` Alan Modra
0 siblings, 1 reply; 14+ messages in thread
From: Alan Modra @ 2022-12-22 10:22 UTC (permalink / raw)
To: Mark Harmstone; +Cc: binutils, nickc
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
Australia Development Lab, IBM
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 01/10] ld: Generate PDB string table
2022-12-22 10:22 ` Alan Modra
@ 2022-12-23 10:54 ` Alan Modra
0 siblings, 0 replies; 14+ messages in thread
From: Alan Modra @ 2022-12-23 10:54 UTC (permalink / raw)
To: Mark Harmstone; +Cc: binutils, nickc
Mark, I've now pushed your patches, with fixes for the compiler
warnings plus the wrong-size memcpy.
--
Alan Modra
Australia Development Lab, IBM
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2022-12-23 10:54 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-12-09 1:52 [PATCH 01/10] ld: Generate PDB string table Mark Harmstone
2022-12-09 1:52 ` [PATCH 02/10] ld: Write DEBUG_S_FILECHKSMS entries in PDBs Mark Harmstone
2022-12-09 1:52 ` [PATCH 03/10] ld: Fix segfault in populate_publics_stream Mark Harmstone
2022-12-09 1:52 ` [PATCH 04/10] ld: Write DEBUG_S_LINES entries in PDB file Mark Harmstone
2022-12-09 1:52 ` [PATCH 05/10] ld: Write types into TPI stream of PDB Mark Harmstone
2022-12-09 1:52 ` [PATCH 06/10] ld: Write types into IPI " Mark Harmstone
2022-12-09 1:52 ` [PATCH 07/10] ld: Parse LF_UDT_SRC_LINE records when creating PDB file Mark Harmstone
2022-12-09 1:52 ` [PATCH 08/10] ld: Write globals stream in PDB Mark Harmstone
2022-12-09 1:52 ` [PATCH 09/10] ld: Copy other symbols into PDB file Mark Harmstone
2022-12-09 1:52 ` [PATCH 10/10] ld: Write linker symbols in PDB Mark Harmstone
2022-12-22 0:05 ` [PATCH 01/10] ld: Generate PDB string table Alan Modra
2022-12-22 1:25 ` Alan Modra
2022-12-22 10:22 ` Alan Modra
2022-12-23 10:54 ` Alan Modra
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).