aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Harmstone <mark@harmstone.com>2022-12-09 01:52:35 +0000
committerAlan Modra <amodra@gmail.com>2022-12-23 20:05:48 +1030
commitd5b4c0ddb9afaac5d5f741da4a863a4a71992969 (patch)
treeed1550d1d9251d8f71869eb4d18b94aecabf3e8e
parent598c1ae610f7fd6352ed5445ab38f99a21062f21 (diff)
downloadgdb-d5b4c0ddb9afaac5d5f741da4a863a4a71992969.zip
gdb-d5b4c0ddb9afaac5d5f741da4a863a4a71992969.tar.gz
gdb-d5b4c0ddb9afaac5d5f741da4a863a4a71992969.tar.bz2
ld: Write types into TPI stream of PDB
-rw-r--r--ld/pdb.c1290
-rw-r--r--ld/pdb.h264
-rw-r--r--ld/testsuite/ld-pe/pdb-types1-hashlist.d13
-rw-r--r--ld/testsuite/ld-pe/pdb-types1-skiplist.d5
-rw-r--r--ld/testsuite/ld-pe/pdb-types1-typelist.d60
-rw-r--r--ld/testsuite/ld-pe/pdb-types1a.s27
-rw-r--r--ld/testsuite/ld-pe/pdb-types1b.s461
-rw-r--r--ld/testsuite/ld-pe/pdb.exp172
8 files changed, 2270 insertions, 22 deletions
diff --git a/ld/pdb.c b/ld/pdb.c
index 1a8a126..535ea1d 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;
}
@@ -620,6 +787,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,
@@ -866,6 +1059,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
@@ -873,7 +2044,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;
@@ -895,6 +2066,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. */
@@ -932,7 +2112,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;
@@ -1020,7 +2201,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++)
{
@@ -1342,7 +2524,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;
@@ -1354,7 +2537,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))
@@ -1884,6 +3067,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
@@ -1892,9 +3100,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)
@@ -1928,7 +3137,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"));
@@ -1944,7 +3155,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"));
@@ -1985,15 +3198,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 bbb1060..ecc26c1 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 0000000..b75f08c
--- /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 0000000..52c10fa
--- /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 0000000..ff2d91c
--- /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 0000000..a2ee9a9
--- /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 0000000..89ee6e3
--- /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 2e5f834..5661438 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