public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
From: Mark Harmstone <mark@harmstone.com>
To: binutils@sourceware.org
Cc: Mark Harmstone <mark@harmstone.com>
Subject: [PATCH] ld: Write types into TPI stream of PDB
Date: Tue, 29 Nov 2022 00:10:13 +0000	[thread overview]
Message-ID: <20221129001015.21775-1-mark@harmstone.com> (raw)
In-Reply-To: <20221127023840.32080-2-mark@harmstone.com>

This parses the .debug$T section of the object files, and uses them to
populate the type information stream. This and the following patches
ought to cover all the CodeView types that Clang will emit, and all the
types that I've seen MSVC emit.

Sorry, I know this is massive, but there didn't seem much sense in
splitting it up - most of it is just parsing arbitrary structs. The
actual meaning of the records doesn't matter that much here, merely how
they reference other types and how their hashes are calculated.

As with the other patches, Microsoft's cvdump.exe can be used to
generate a textual version of what's output here.

---
 ld/pdb.c                                 | 1284 +++++++++++++++++++++-
 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, 2264 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 cfc76004227..979ea126aa5 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;
 }
 
@@ -866,6 +1033,1003 @@ 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 immediate follows.  */
+		  if (val >= 0x8000)
+		    {
+		      size_t param_len;
+
+		      switch (val) {
+			case LF_CHAR:
+			  param_len = 1;
+			  break;
+
+			case LF_SHORT:
+			case LF_USHORT:
+			  param_len = 2;
+			  break;
+
+			case LF_LONG:
+			case LF_ULONG:
+			  param_len = 4;
+			  break;
+
+			case LF_QUADWORD:
+			case LF_UQUADWORD:
+			  param_len = 8;
+			  break;
+
+			default:
+			  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 +2037,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 +2059,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.  */
@@ -933,7 +2106,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;
@@ -1021,7 +2195,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++)
 	    {
@@ -1343,7 +2518,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;
@@ -1355,7 +2531,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))
@@ -1886,6 +3062,32 @@ 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
@@ -1894,9 +3096,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)
@@ -1930,7 +3133,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"));
@@ -1946,7 +3151,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"));
@@ -1987,15 +3194,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


  reply	other threads:[~2022-11-29  0:10 UTC|newest]

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

Reply instructions:

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

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

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

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

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

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

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