public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
From: Mark Harmstone <mark@harmstone.com>
To: binutils@sourceware.org, nickc@redhat.com
Cc: Mark Harmstone <mark@harmstone.com>
Subject: [PATCH 05/10] ld: Write types into TPI stream of PDB
Date: Fri,  9 Dec 2022 01:52:35 +0000	[thread overview]
Message-ID: <20221209015240.6348-5-mark@harmstone.com> (raw)
In-Reply-To: <20221209015240.6348-1-mark@harmstone.com>

---
 ld/pdb.c                                 | 1290 +++++++++++++++++++++-
 ld/pdb.h                                 |  264 +++++
 ld/testsuite/ld-pe/pdb-types1-hashlist.d |   13 +
 ld/testsuite/ld-pe/pdb-types1-skiplist.d |    5 +
 ld/testsuite/ld-pe/pdb-types1-typelist.d |   60 +
 ld/testsuite/ld-pe/pdb-types1a.s         |   27 +
 ld/testsuite/ld-pe/pdb-types1b.s         |  461 ++++++++
 ld/testsuite/ld-pe/pdb.exp               |  172 +++
 8 files changed, 2270 insertions(+), 22 deletions(-)
 create mode 100644 ld/testsuite/ld-pe/pdb-types1-hashlist.d
 create mode 100644 ld/testsuite/ld-pe/pdb-types1-skiplist.d
 create mode 100644 ld/testsuite/ld-pe/pdb-types1-typelist.d
 create mode 100644 ld/testsuite/ld-pe/pdb-types1a.s
 create mode 100644 ld/testsuite/ld-pe/pdb-types1b.s

diff --git a/ld/pdb.c b/ld/pdb.c
index a2f78c4c348..4bb19420f95 100644
--- a/ld/pdb.c
+++ b/ld/pdb.c
@@ -71,6 +71,69 @@ struct source_files_info
   struct mod_source_files *mods;
 };
 
+struct type_entry
+{
+  struct type_entry *next;
+  uint32_t index;
+  uint32_t cv_hash;
+  uint8_t data[];
+};
+
+struct types
+{
+  htab_t hashmap;
+  uint32_t num_types;
+  struct type_entry *first;
+  struct type_entry *last;
+};
+
+static const uint32_t crc_table[] =
+{
+  0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
+  0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+  0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
+  0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+  0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
+  0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+  0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
+  0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+  0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
+  0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+  0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
+  0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+  0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
+  0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+  0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
+  0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+  0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
+  0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+  0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
+  0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+  0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
+  0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+  0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
+  0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+  0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
+  0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+  0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
+  0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+  0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
+  0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+  0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
+  0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+  0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
+  0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+  0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
+  0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+  0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
+  0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+  0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
+  0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+  0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
+  0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+  0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
 /* Add a new stream to the PDB archive, and return its BFD.  */
 static bfd *
 add_stream (bfd *pdb, const char *name, uint16_t *stream_num)
@@ -365,38 +428,142 @@ end:
   return ret;
 }
 
+/* Calculate the CRC32 used for type hashes.  */
+static uint32_t
+crc32 (const uint8_t *data, size_t len)
+{
+  uint32_t crc = 0;
+
+  while (len > 0)
+    {
+      crc = (crc >> 8) ^ crc_table[(crc & 0xff) ^ *data];
+
+      data++;
+      len--;
+    }
+
+  return crc;
+}
+
 /* Stream 2 is the type information (TPI) stream, and stream 4 is
    the ID information (IPI) stream.  They differ only in which records
    go in which stream. */
 static bool
-create_type_stream (bfd *pdb)
+populate_type_stream (bfd *pdb, bfd *stream, struct types *types)
 {
-  bfd *stream;
   struct pdb_tpi_stream_header h;
+  struct type_entry *e;
+  uint32_t len = 0, index_offset_len, off;
+  struct bfd *hash_stream = NULL;
+  uint16_t hash_stream_index;
 
-  stream = add_stream (pdb, NULL, NULL);
-  if (!stream)
+  static const uint32_t index_skip = 0x2000;
+
+  e = types->first;
+
+  index_offset_len = 0;
+
+  while (e)
+    {
+      uint32_t old_len = len;
+
+      len += sizeof (uint16_t) + bfd_getl16 (e->data);
+
+      if (old_len == 0 || old_len / index_skip != len / index_skip)
+	index_offset_len += sizeof (uint32_t) * 2;
+
+      e = e->next;
+    }
+
+  /* Each type stream also has a stream which holds the hash value for each
+     type, along with a skip list to speed up searching.  */
+
+  hash_stream = add_stream (pdb, "", &hash_stream_index);
+
+  if (!hash_stream)
     return false;
 
   bfd_putl32 (TPI_STREAM_VERSION_80, &h.version);
   bfd_putl32 (sizeof (h), &h.header_size);
   bfd_putl32 (TPI_FIRST_INDEX, &h.type_index_begin);
-  bfd_putl32 (TPI_FIRST_INDEX, &h.type_index_end);
-  bfd_putl32 (0, &h.type_record_bytes);
-  bfd_putl16 (0xffff, &h.hash_stream_index);
+  bfd_putl32 (TPI_FIRST_INDEX + types->num_types, &h.type_index_end);
+  bfd_putl32 (len, &h.type_record_bytes);
+  bfd_putl16 (hash_stream_index, &h.hash_stream_index);
   bfd_putl16 (0xffff, &h.hash_aux_stream_index);
-  bfd_putl32 (4, &h.hash_key_size);
-  bfd_putl32 (0x3ffff, &h.num_hash_buckets);
+  bfd_putl32 (sizeof (uint32_t), &h.hash_key_size);
+  bfd_putl32 (NUM_TPI_HASH_BUCKETS, &h.num_hash_buckets);
   bfd_putl32 (0, &h.hash_value_buffer_offset);
-  bfd_putl32 (0, &h.hash_value_buffer_length);
-  bfd_putl32 (0, &h.index_offset_buffer_offset);
-  bfd_putl32 (0, &h.index_offset_buffer_length);
-  bfd_putl32 (0, &h.hash_adj_buffer_offset);
+  bfd_putl32 (types->num_types * sizeof (uint32_t),
+	      &h.hash_value_buffer_length);
+  bfd_putl32 (types->num_types * sizeof (uint32_t),
+	      &h.index_offset_buffer_offset);
+  bfd_putl32 (index_offset_len, &h.index_offset_buffer_length);
+  bfd_putl32 ((types->num_types * sizeof (uint32_t)) + index_offset_len,
+	      &h.hash_adj_buffer_offset);
   bfd_putl32 (0, &h.hash_adj_buffer_length);
 
   if (bfd_bwrite (&h, sizeof (h), stream) != sizeof (h))
     return false;
 
+  /* Write the type definitions into the main stream, and the hashes
+     into the hash stream.  The hashes have already been calculated
+     in handle_type.  */
+
+  e = types->first;
+
+  while (e)
+    {
+      uint8_t buf[sizeof (uint32_t)];
+      uint16_t size;
+
+      size = bfd_getl16 (e->data);
+
+      if (bfd_bwrite (e->data, size + sizeof (uint16_t), stream)
+	  != size + sizeof (uint16_t))
+	return false;
+
+      bfd_putl32 (e->cv_hash % NUM_TPI_HASH_BUCKETS, buf);
+
+      if (bfd_bwrite (buf, sizeof (uint32_t), hash_stream)
+	  != sizeof (uint32_t))
+	return false;
+
+      e = e->next;
+    }
+
+  /* Write the index offsets, i.e. the skip list, into the hash stream.  We
+     copy MSVC here by writing a new entry for every 8192 bytes.  */
+
+  e = types->first;
+  off = 0;
+
+  while (e)
+    {
+      uint32_t old_off = off;
+      uint16_t size = bfd_getl16 (e->data);
+
+      off += size + sizeof (uint16_t);
+
+      if (old_off == 0 || old_off / index_skip != len / index_skip)
+	{
+	  uint8_t buf[sizeof (uint32_t)];
+
+	  bfd_putl32 (TPI_FIRST_INDEX + e->index, buf);
+
+	  if (bfd_bwrite (buf, sizeof (uint32_t), hash_stream)
+	      != sizeof (uint32_t))
+	    return false;
+
+	  bfd_putl32 (old_off, buf);
+
+	  if (bfd_bwrite (buf, sizeof (uint32_t), hash_stream)
+	      != sizeof (uint32_t))
+	    return false;
+	}
+
+      e = e->next;
+    }
+
   return true;
 }
 
@@ -619,6 +786,32 @@ parse_string_table (bfd_byte *data, size_t size,
     }
 }
 
+/* Return the size of an extended value parameter, as used in
+   LF_ENUMERATE etc.  */
+static unsigned int
+extended_value_len (uint16_t type)
+{
+  switch (type)
+    {
+    case LF_CHAR:
+      return 1;
+
+    case LF_SHORT:
+    case LF_USHORT:
+      return 2;
+
+    case LF_LONG:
+    case LF_ULONG:
+      return 4;
+
+    case LF_QUADWORD:
+    case LF_UQUADWORD:
+      return 8;
+    }
+
+  return 0;
+}
+
 /* Parse the .debug$S section within an object file.  */
 static bool
 handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
@@ -865,6 +1058,984 @@ handle_debugs_section (asection *s, bfd *mod, struct string_table *strings,
   return true;
 }
 
+/* Remap the type number stored in data from the per-module numbering to
+   that of the deduplicated output list.  */
+static bool
+remap_type (void *data, struct type_entry **map,
+	    uint32_t type_num, uint32_t num_types)
+{
+  uint32_t type = bfd_getl32 (data);
+
+  /* Ignore builtin types (those with IDs below 0x1000).  */
+  if (type < TPI_FIRST_INDEX)
+    return true;
+
+  if (type >= TPI_FIRST_INDEX + type_num)
+    {
+      einfo (_("%P: CodeView type %v references other type %v not yet "
+	       "declared\n"), TPI_FIRST_INDEX + type_num, type);
+      return false;
+    }
+
+  if (type >= TPI_FIRST_INDEX + num_types)
+    {
+      einfo (_("%P: CodeView type %v references out of range type %v\n"),
+	     TPI_FIRST_INDEX + type_num, type);
+      return false;
+    }
+
+  type = TPI_FIRST_INDEX + map[type - TPI_FIRST_INDEX]->index;
+  bfd_putl32 (type, data);
+
+  return true;
+}
+
+/* Determines whether the name of a struct, class, or union counts as
+   "anonymous".  Non-anonymous types have a hash based on just the name,
+   rather than the whole structure.  */
+static bool
+is_name_anonymous (char *name, size_t len)
+{
+  static const char tag1[] = "<unnamed-tag>";
+  static const char tag2[] = "__unnamed";
+  static const char tag3[] = "::<unnamed-tag>";
+  static const char tag4[] = "::__unnamed";
+
+  if (len == sizeof (tag1) - 1 && !memcmp (name, tag1, sizeof (tag1) - 1))
+    return true;
+
+  if (len == sizeof (tag2) - 1 && !memcmp (name, tag2, sizeof (tag2) - 1))
+    return true;
+
+  if (len >= sizeof (tag3) - 1
+      && !memcmp (name + len - sizeof (tag3) + 1, tag3, sizeof (tag3) - 1))
+    return true;
+
+  if (len >= sizeof (tag4) - 1
+      && !memcmp (name + len - sizeof (tag4) + 1, tag4, sizeof (tag4) - 1))
+    return true;
+
+  return false;
+}
+
+/* Parse a type definition in the .debug$T section.  We remap the numbers
+   of any referenced types, and if the type is not a duplicate of one
+   already seen add it to types (for TPI types) or ids (for IPI types).  */
+static bool
+handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
+	     uint32_t num_types, struct types *types)
+{
+  uint16_t size, type;
+  void **slot;
+  hashval_t hash;
+  bool other_hash = false;
+  uint32_t cv_hash;
+
+  size = bfd_getl16 (data) + sizeof (uint16_t);
+  type = bfd_getl16 (data + sizeof (uint16_t));
+
+  switch (type)
+    {
+    case LF_MODIFIER:
+      {
+	struct lf_modifier *mod = (struct lf_modifier *) data;
+
+	if (size < offsetof (struct lf_modifier, modifier))
+	  {
+	    einfo (_("%P: warning: truncated CodeView type record "
+		     "LF_MODIFIER\n"));
+	    return false;
+	  }
+
+	if (!remap_type (&mod->base_type, map, type_num, num_types))
+	  return false;
+
+	break;
+      }
+
+    case LF_POINTER:
+      {
+	struct lf_pointer *ptr = (struct lf_pointer *) data;
+
+	if (size < offsetof (struct lf_pointer, attributes))
+	  {
+	    einfo (_("%P: warning: truncated CodeView type record"
+		     " LF_POINTER\n"));
+	    return false;
+	  }
+
+	if (!remap_type (&ptr->base_type, map, type_num, num_types))
+	  return false;
+
+	break;
+      }
+
+    case LF_PROCEDURE:
+      {
+	struct lf_procedure *proc = (struct lf_procedure *) data;
+
+	if (size < sizeof (struct lf_procedure))
+	  {
+	    einfo (_("%P: warning: truncated CodeView type record"
+		     " LF_PROCEDURE\n"));
+	    return false;
+	  }
+
+	if (!remap_type (&proc->return_type, map, type_num, num_types))
+	  return false;
+
+	if (!remap_type (&proc->arglist, map, type_num, num_types))
+	  return false;
+
+	break;
+      }
+
+    case LF_MFUNCTION:
+      {
+	struct lf_mfunction *func = (struct lf_mfunction *) data;
+
+	if (size < sizeof (struct lf_procedure))
+	  {
+	    einfo (_("%P: warning: truncated CodeView type record"
+		     " LF_MFUNCTION\n"));
+	    return false;
+	  }
+
+	if (!remap_type (&func->return_type, map, type_num, num_types))
+	  return false;
+
+	if (!remap_type (&func->containing_class_type, map, type_num,
+			 num_types))
+	  return false;
+
+	if (!remap_type (&func->this_type, map, type_num, num_types))
+	  return false;
+
+	if (!remap_type (&func->arglist, map, type_num, num_types))
+	  return false;
+
+	break;
+      }
+
+    case LF_ARGLIST:
+      {
+	uint32_t num_entries;
+	struct lf_arglist *al = (struct lf_arglist *) data;
+
+	if (size < offsetof (struct lf_arglist, args))
+	  {
+	    einfo (_("%P: warning: truncated CodeView type record"
+		     " LF_ARGLIST\n"));
+	    return false;
+	  }
+
+	num_entries = bfd_getl32 (&al->num_entries);
+
+	if (size < offsetof (struct lf_arglist, args)
+		   + (num_entries * sizeof (uint32_t)))
+	  {
+	    einfo (_("%P: warning: truncated CodeView type record"
+		     " LF_ARGLIST\n"));
+	    return false;
+	  }
+
+	for (uint32_t i = 0; i < num_entries; i++)
+	  {
+	    if (!remap_type (&al->args[i], map, type_num, num_types))
+	      return false;
+	  }
+
+	break;
+      }
+
+    case LF_FIELDLIST:
+      {
+	uint16_t left = size - sizeof (uint16_t) - sizeof (uint16_t);
+	uint8_t *ptr = data + sizeof (uint16_t) + sizeof (uint16_t);
+
+	while (left > 0)
+	  {
+	    uint16_t subtype;
+
+	    if (left < sizeof (uint16_t))
+	      {
+		einfo (_("%P: warning: truncated CodeView type record"
+			 " LF_FIELDLIST\n"));
+		return false;
+	      }
+
+	    subtype = bfd_getl16 (ptr);
+
+	    switch (subtype)
+	      {
+	      case LF_MEMBER:
+		{
+		  struct lf_member *mem = (struct lf_member *) ptr;
+		  size_t name_len, subtype_len;
+
+		  if (left < offsetof (struct lf_member, name))
+		    {
+		      einfo (_("%P: warning: truncated CodeView type record"
+			       " LF_MEMBER\n"));
+		      return false;
+		    }
+
+		  if (!remap_type (&mem->type, map, type_num, num_types))
+		    return false;
+
+		  name_len =
+		    strnlen (mem->name,
+			     left - offsetof (struct lf_member, name));
+
+		  if (name_len == left - offsetof (struct lf_member, name))
+		    {
+		      einfo (_("%P: warning: name for LF_MEMBER has no"
+			       " terminating zero\n"));
+		      return false;
+		    }
+
+		  name_len++;
+
+		  subtype_len = offsetof (struct lf_member, name) + name_len;
+
+		  if (subtype_len % 4 != 0)
+		    subtype_len += 4 - (subtype_len % 4);
+
+		  if (left < subtype_len)
+		    {
+		      einfo (_("%P: warning: truncated CodeView type record"
+			       " LF_FIELDLIST\n"));
+		      return false;
+		    }
+
+		  ptr += subtype_len;
+		  left -= subtype_len;
+
+		  break;
+		}
+
+	      case LF_ENUMERATE:
+		{
+		  struct lf_enumerate *en = (struct lf_enumerate *) ptr;
+		  size_t name_len, subtype_len;
+		  uint16_t val;
+
+		  if (left < offsetof (struct lf_enumerate, name))
+		    {
+		      einfo (_("%P: warning: truncated CodeView type record"
+			       " LF_ENUMERATE\n"));
+		      return false;
+		    }
+
+		  subtype_len = offsetof (struct lf_enumerate, name);
+
+		  val = bfd_getl16 (&en->value);
+
+		  /* If val >= 0x8000, the actual value immediately follows.  */
+		  if (val >= 0x8000)
+		    {
+		      unsigned int param_len = extended_value_len (val);
+
+		      if (param_len == 0)
+			{
+			  einfo (_("%P: warning: unhandled type %v within"
+				   " LF_ENUMERATE\n"), val);
+			  return false;
+			}
+
+		      if (left < subtype_len + param_len)
+			{
+			  einfo (_("%P: warning: truncated CodeView type"
+				   " record LF_ENUMERATE\n"));
+			  return false;
+			}
+
+		      subtype_len += param_len;
+		    }
+
+		  name_len = strnlen ((char *) ptr + subtype_len,
+				      left - subtype_len);
+
+		  if (name_len == left - offsetof (struct lf_enumerate, name))
+		    {
+		      einfo (_("%P: warning: name for LF_ENUMERATE has no"
+			       " terminating zero\n"));
+		      return false;
+		    }
+
+		  name_len++;
+
+		  subtype_len += name_len;
+
+		  if (subtype_len % 4 != 0)
+		    subtype_len += 4 - (subtype_len % 4);
+
+		  if (left < subtype_len)
+		    {
+		      einfo (_("%P: warning: truncated CodeView type record"
+			       " LF_ENUMERATE\n"));
+		      return false;
+		    }
+
+		  ptr += subtype_len;
+		  left -= subtype_len;
+
+		  break;
+		}
+
+	      case LF_INDEX:
+		{
+		  struct lf_index *ind = (struct lf_index *) ptr;
+
+		  if (left < sizeof (struct lf_index))
+		    {
+		      einfo (_("%P: warning: truncated CodeView type record"
+			       " LF_INDEX\n"));
+		      return false;
+		    }
+
+		  if (!remap_type (&ind->index, map, type_num, num_types))
+		    return false;
+
+		  ptr += sizeof (struct lf_index);
+		  left -= sizeof (struct lf_index);
+
+		  break;
+		}
+
+	      case LF_ONEMETHOD:
+		{
+		  struct lf_onemethod *meth = (struct lf_onemethod *) ptr;
+		  size_t name_len, subtype_len;
+
+		  if (left < offsetof (struct lf_onemethod, name))
+		    {
+		      einfo (_("%P: warning: truncated CodeView type record"
+			       " LF_ONEMETHOD\n"));
+		      return false;
+		    }
+
+		  if (!remap_type (&meth->method_type, map, type_num,
+				   num_types))
+		    return false;
+
+		  name_len =
+		    strnlen (meth->name,
+			     left - offsetof (struct lf_onemethod, name));
+
+		  if (name_len == left - offsetof (struct lf_onemethod, name))
+		    {
+		      einfo (_("%P: warning: name for LF_ONEMETHOD has no"
+			       " terminating zero\n"));
+		      return false;
+		    }
+
+		  name_len++;
+
+		  subtype_len = offsetof (struct lf_onemethod, name)
+				+ name_len;
+
+		  if (subtype_len % 4 != 0)
+		    subtype_len += 4 - (subtype_len % 4);
+
+		  if (left < subtype_len)
+		    {
+		      einfo (_("%P: warning: truncated CodeView type record"
+			       " LF_FIELDLIST\n"));
+		      return false;
+		    }
+
+		  ptr += subtype_len;
+		  left -= subtype_len;
+
+		  break;
+		}
+
+	      case LF_METHOD:
+		{
+		  struct lf_method *meth = (struct lf_method *) ptr;
+		  size_t name_len, subtype_len;
+
+		  if (left < offsetof (struct lf_method, name))
+		    {
+		      einfo (_("%P: warning: truncated CodeView type record"
+			       " LF_METHOD\n"));
+		      return false;
+		    }
+
+		  if (!remap_type (&meth->method_list, map, type_num,
+				   num_types))
+		    return false;
+
+		  name_len =
+		    strnlen (meth->name,
+			     left - offsetof (struct lf_method, name));
+
+		  if (name_len == left - offsetof (struct lf_method, name))
+		    {
+		      einfo (_("%P: warning: name for LF_METHOD has no"
+			       " terminating zero\n"));
+		      return false;
+		    }
+
+		  name_len++;
+
+		  subtype_len = offsetof (struct lf_method, name) + name_len;
+
+		  if (subtype_len % 4 != 0)
+		    subtype_len += 4 - (subtype_len % 4);
+
+		  if (left < subtype_len)
+		    {
+		      einfo (_("%P: warning: truncated CodeView type record"
+			       " LF_FIELDLIST\n"));
+		      return false;
+		    }
+
+		  ptr += subtype_len;
+		  left -= subtype_len;
+
+		  break;
+		}
+
+	      case LF_BCLASS:
+		{
+		  struct lf_bclass *bc = (struct lf_bclass *) ptr;
+
+		  if (left < sizeof (struct lf_bclass))
+		    {
+		      einfo (_("%P: warning: truncated CodeView type record"
+			       " LF_BCLASS\n"));
+		      return false;
+		    }
+
+		  if (!remap_type (&bc->base_class_type, map, type_num,
+				   num_types))
+		    return false;
+
+		  ptr += sizeof (struct lf_bclass);
+		  left -= sizeof (struct lf_bclass);
+
+		  break;
+		}
+
+	      case LF_VFUNCTAB:
+		{
+		  struct lf_vfunctab *vft = (struct lf_vfunctab *) ptr;
+
+		  if (left < sizeof (struct lf_vfunctab))
+		    {
+		      einfo (_("%P: warning: truncated CodeView type record"
+			       " LF_VFUNCTAB\n"));
+		      return false;
+		    }
+
+		  if (!remap_type (&vft->type, map, type_num, num_types))
+		    return false;
+
+		  ptr += sizeof (struct lf_vfunctab);
+		  left -= sizeof (struct lf_vfunctab);
+
+		  break;
+		}
+
+	      case LF_VBCLASS:
+	      case LF_IVBCLASS:
+		{
+		  struct lf_vbclass *vbc = (struct lf_vbclass *) ptr;
+
+		  if (left < sizeof (struct lf_vbclass))
+		    {
+		      einfo (_("%P: warning: truncated CodeView type record"
+			       " LF_VBCLASS/LF_IVBCLASS\n"));
+		      return false;
+		    }
+
+		  if (!remap_type (&vbc->base_class_type, map, type_num,
+				   num_types))
+		    return false;
+
+		  if (!remap_type (&vbc->virtual_base_pointer_type, map,
+				   type_num, num_types))
+		    return false;
+
+		  ptr += sizeof (struct lf_vbclass);
+		  left -= sizeof (struct lf_vbclass);
+
+		  break;
+		}
+
+	      case LF_STMEMBER:
+		{
+		  struct lf_static_member *st =
+		    (struct lf_static_member *) ptr;
+		  size_t name_len, subtype_len;
+
+		  if (left < offsetof (struct lf_static_member, name))
+		    {
+		      einfo (_("%P: warning: truncated CodeView type record"
+			       " LF_STMEMBER\n"));
+		      return false;
+		    }
+
+		  if (!remap_type (&st->type, map, type_num, num_types))
+		    return false;
+
+		  name_len =
+		    strnlen (st->name,
+			     left - offsetof (struct lf_static_member, name));
+
+		  if (name_len == left
+				  - offsetof (struct lf_static_member, name))
+		    {
+		      einfo (_("%P: warning: name for LF_STMEMBER has no"
+			       " terminating zero\n"));
+		      return false;
+		    }
+
+		  name_len++;
+
+		  subtype_len = offsetof (struct lf_static_member, name)
+				+ name_len;
+
+		  if (subtype_len % 4 != 0)
+		    subtype_len += 4 - (subtype_len % 4);
+
+		  if (left < subtype_len)
+		    {
+		      einfo (_("%P: warning: truncated CodeView type record"
+			       " LF_FIELDLIST\n"));
+		      return false;
+		    }
+
+		  ptr += subtype_len;
+		  left -= subtype_len;
+
+		  break;
+		}
+
+	      case LF_NESTTYPE:
+		{
+		  struct lf_nest_type *nest = (struct lf_nest_type *) ptr;
+		  size_t name_len, subtype_len;
+
+		  if (left < offsetof (struct lf_nest_type, name))
+		    {
+		      einfo (_("%P: warning: truncated CodeView type record"
+			       " LF_NESTTYPE\n"));
+		      return false;
+		    }
+
+		  if (!remap_type (&nest->type, map, type_num, num_types))
+		    return false;
+
+		  name_len =
+		    strnlen (nest->name,
+			     left - offsetof (struct lf_nest_type, name));
+
+		  if (name_len == left - offsetof (struct lf_nest_type, name))
+		    {
+		      einfo (_("%P: warning: name for LF_NESTTYPE has no"
+			       " terminating zero\n"));
+		      return false;
+		    }
+
+		  name_len++;
+
+		  subtype_len = offsetof (struct lf_nest_type, name)
+				+ name_len;
+
+		  if (subtype_len % 4 != 0)
+		    subtype_len += 4 - (subtype_len % 4);
+
+		  if (left < subtype_len)
+		    {
+		      einfo (_("%P: warning: truncated CodeView type record"
+			       " LF_FIELDLIST\n"));
+		      return false;
+		    }
+
+		  ptr += subtype_len;
+		  left -= subtype_len;
+
+		  break;
+		}
+
+	      default:
+		einfo (_("%P: warning: unrecognized CodeView subtype %v\n"),
+		       subtype);
+		return false;
+	      }
+	  }
+
+	break;
+      }
+
+    case LF_BITFIELD:
+      {
+	struct lf_bitfield *bf = (struct lf_bitfield *) data;
+
+	if (size < offsetof (struct lf_bitfield, length))
+	  {
+	    einfo (_("%P: warning: truncated CodeView type record"
+		     " LF_BITFIELD\n"));
+	    return false;
+	  }
+
+	if (!remap_type (&bf->base_type, map, type_num, num_types))
+	  return false;
+
+	break;
+      }
+
+    case LF_METHODLIST:
+      {
+	struct lf_methodlist *ml = (struct lf_methodlist *) data;
+	unsigned int num_entries;
+
+	if (size < offsetof (struct lf_methodlist, entries))
+	  {
+	    einfo (_("%P: warning: truncated CodeView type record"
+		     " LF_METHODLIST\n"));
+	    return false;
+	  }
+
+	if ((size - offsetof (struct lf_methodlist, entries))
+	    % sizeof (struct lf_methodlist_entry))
+	  {
+	    einfo (_("%P: warning: malformed CodeView type record"
+		     " LF_METHODLIST\n"));
+	    return false;
+	  }
+
+	num_entries = (size - offsetof (struct lf_methodlist, entries))
+		      / sizeof (struct lf_methodlist_entry);
+
+	for (unsigned int i = 0; i < num_entries; i++)
+	  {
+	    if (!remap_type (&ml->entries[i].method_type, map,
+			     type_num, num_types))
+	      return false;
+	  }
+
+	break;
+      }
+
+    case LF_ARRAY:
+      {
+	struct lf_array *arr = (struct lf_array *) data;
+
+	if (size < offsetof (struct lf_array, length_in_bytes))
+	  {
+	    einfo (_("%P: warning: truncated CodeView type record"
+		     " LF_ARRAY\n"));
+	    return false;
+	  }
+
+	if (!remap_type (&arr->element_type, map, type_num, num_types))
+	  return false;
+
+	if (!remap_type (&arr->index_type, map, type_num, num_types))
+	  return false;
+
+	break;
+      }
+
+    case LF_CLASS:
+    case LF_STRUCTURE:
+      {
+	struct lf_class *cl = (struct lf_class *) data;
+	uint16_t prop;
+	size_t name_len;
+
+	if (size < offsetof (struct lf_class, name))
+	  {
+	    einfo (_("%P: warning: truncated CodeView type record"
+		     " LF_CLASS/LF_STRUCTURE\n"));
+	    return false;
+	  }
+
+	if (!remap_type (&cl->field_list, map, type_num, num_types))
+	  return false;
+
+	if (!remap_type (&cl->derived_from, map, type_num, num_types))
+	  return false;
+
+	if (!remap_type (&cl->vshape, map, type_num, num_types))
+	  return false;
+
+	name_len = strnlen (cl->name, size - offsetof (struct lf_class, name));
+
+	if (name_len == size - offsetof (struct lf_class, name))
+	  {
+	    einfo (_("%P: warning: name for LF_CLASS/LF_STRUCTURE has no"
+		     " terminating zero\n"));
+	    return false;
+	  }
+
+	prop = bfd_getl16 (&cl->properties);
+
+	if (prop & CV_PROP_HAS_UNIQUE_NAME)
+	  {
+	    /* Structure has another name following first one.  */
+
+	    size_t len = offsetof (struct lf_class, name) + name_len + 1;
+	    size_t unique_name_len;
+
+	    unique_name_len = strnlen (cl->name + name_len + 1, size - len);
+
+	    if (unique_name_len == size - len)
+	      {
+		einfo (_("%P: warning: unique name for LF_CLASS/LF_STRUCTURE"
+			 " has no terminating zero\n"));
+		return false;
+	      }
+	  }
+
+	if (!(prop & (CV_PROP_FORWARD_REF | CV_PROP_SCOPED))
+	    && !is_name_anonymous (cl->name, name_len))
+	  {
+	    other_hash = true;
+	    cv_hash = crc32 ((uint8_t *) cl->name, name_len);
+	  }
+
+	break;
+      }
+
+    case LF_UNION:
+      {
+	struct lf_union *un = (struct lf_union *) data;
+	uint16_t prop;
+	size_t name_len;
+
+	if (size < offsetof (struct lf_union, name))
+	  {
+	    einfo (_("%P: warning: truncated CodeView type record"
+		     " LF_UNION\n"));
+	    return false;
+	  }
+
+	if (!remap_type (&un->field_list, map, type_num, num_types))
+	  return false;
+
+	name_len = strnlen (un->name, size - offsetof (struct lf_union, name));
+
+	if (name_len == size - offsetof (struct lf_union, name))
+	  {
+	    einfo (_("%P: warning: name for LF_UNION has no"
+		     " terminating zero\n"));
+	    return false;
+	  }
+
+	prop = bfd_getl16 (&un->properties);
+
+	if (prop & CV_PROP_HAS_UNIQUE_NAME)
+	  {
+	    /* Structure has another name following first one.  */
+
+	    size_t len = offsetof (struct lf_union, name) + name_len + 1;
+	    size_t unique_name_len;
+
+	    unique_name_len = strnlen (un->name + name_len + 1, size - len);
+
+	    if (unique_name_len == size - len)
+	      {
+		einfo (_("%P: warning: unique name for LF_UNION has"
+			 " no terminating zero\n"));
+		return false;
+	      }
+	  }
+
+	if (!(prop & (CV_PROP_FORWARD_REF | CV_PROP_SCOPED))
+	    && !is_name_anonymous (un->name, name_len))
+	  {
+	    other_hash = true;
+	    cv_hash = crc32 ((uint8_t *) un->name, name_len);
+	  }
+
+	break;
+      }
+
+    case LF_ENUM:
+      {
+	struct lf_enum *en = (struct lf_enum *) data;
+	uint16_t prop;
+	size_t name_len;
+
+	if (size < offsetof (struct lf_enum, name))
+	  {
+	    einfo (_("%P: warning: truncated CodeView type record"
+		     " LF_ENUM\n"));
+	    return false;
+	  }
+
+	if (!remap_type (&en->underlying_type, map, type_num, num_types))
+	  return false;
+
+	if (!remap_type (&en->field_list, map, type_num, num_types))
+	  return false;
+
+	name_len = strnlen (en->name, size - offsetof (struct lf_enum, name));
+
+	if (name_len == size - offsetof (struct lf_enum, name))
+	  {
+	    einfo (_("%P: warning: name for LF_ENUM has no"
+		     " terminating zero\n"));
+	    return false;
+	  }
+
+	prop = bfd_getl16 (&en->properties);
+
+	if (prop & CV_PROP_HAS_UNIQUE_NAME)
+	  {
+	    /* Structure has another name following first one.  */
+
+	    size_t len = offsetof (struct lf_enum, name) + name_len + 1;
+	    size_t unique_name_len;
+
+	    unique_name_len = strnlen (en->name + name_len + 1, size - len);
+
+	    if (unique_name_len == size - len)
+	      {
+		einfo (_("%P: warning: unique name for LF_ENUM has"
+			 " no terminating zero\n"));
+		return false;
+	      }
+	  }
+
+	break;
+      }
+
+    case LF_VTSHAPE:
+      /* Does not reference any types, nothing to be done.  */
+      break;
+
+    default:
+      einfo (_("%P: warning: unrecognized CodeView type %v\n"), type);
+      return false;
+    }
+
+  hash = iterative_hash (data, size, 0);
+
+  slot = htab_find_slot_with_hash (types->hashmap, data, hash, INSERT);
+  if (!slot)
+    return false;
+
+  if (!*slot) /* new entry */
+    {
+      struct type_entry *e;
+
+      *slot = xmalloc (offsetof (struct type_entry, data) + size);
+
+      e = (struct type_entry *) *slot;
+
+      e->next = NULL;
+      e->index = types->num_types;
+
+      if (other_hash)
+	e->cv_hash = cv_hash;
+      else
+	e->cv_hash = crc32 (data, size);
+
+      memcpy (e->data, data, size);
+
+      if (types->last)
+	types->last->next = e;
+      else
+	types->first = e;
+
+      types->last = e;
+
+      map[type_num] = e;
+
+      types->num_types++;
+    }
+  else /* duplicate */
+    {
+      map[type_num] = (struct type_entry *) *slot;
+    }
+
+  return true;
+}
+
+/* Parse the .debug$T section of a module, and pass any type definitions
+   found to handle_type.  */
+static bool
+handle_debugt_section (asection *s, bfd *mod, struct types *types)
+{
+  bfd_byte *data = NULL;
+  size_t off;
+  unsigned int num_types = 0;
+  struct type_entry **map;
+  uint32_t type_num;
+
+  if (!bfd_get_full_section_contents (mod, s, &data))
+    return false;
+
+  if (!data)
+    return false;
+
+  if (bfd_getl32 (data) != CV_SIGNATURE_C13)
+    {
+      free (data);
+      return true;
+    }
+
+  off = sizeof (uint32_t);
+
+  while (off + sizeof (uint16_t) <= s->size)
+    {
+      uint16_t size;
+
+      size = bfd_getl16 (data + off);
+      off += sizeof (uint16_t);
+
+      if (size + off > s->size || size <= sizeof (uint16_t))
+	{
+	  free (data);
+	  bfd_set_error (bfd_error_bad_value);
+	  return false;
+	}
+
+      num_types++;
+      off += size;
+    }
+
+  if (num_types == 0)
+    {
+      free (data);
+      return true;
+    }
+
+  map = xcalloc (num_types, sizeof (struct type_entry *));
+
+  off = sizeof (uint32_t);
+  type_num = 0;
+
+  while (off + sizeof (uint16_t) <= s->size)
+    {
+      uint16_t size;
+
+      size = bfd_getl16 (data + off);
+
+      if (!handle_type (data + off, map, type_num, num_types, types))
+	{
+	  free (data);
+	  free (map);
+	  bfd_set_error (bfd_error_bad_value);
+	  return false;
+	}
+
+      off += sizeof (uint16_t) + size;
+      type_num++;
+    }
+
+  free (data);
+  free (map);
+
+  return true;
+}
+
 /* Populate the module stream, which consists of the transformed .debug$S
    data for each object file.  */
 static bool
@@ -872,7 +2043,7 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
 			struct string_table *strings,
 			uint32_t *c13_info_size,
 			struct mod_source_files *mod_source,
-			bfd *abfd)
+			bfd *abfd, struct types *types)
 {
   uint8_t int_buf[sizeof (uint32_t)];
   uint8_t *c13_info = NULL;
@@ -894,6 +2065,15 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
 	      return false;
 	    }
 	}
+      else if (!strcmp (s->name, ".debug$T") && s->size >= sizeof (uint32_t))
+	{
+	  if (!handle_debugt_section (s, mod, types))
+	    {
+	      free (c13_info);
+	      free (mod_source->files);
+	      return false;
+	    }
+	}
     }
 
   /* Write the signature.  */
@@ -931,7 +2111,8 @@ populate_module_stream (bfd *stream, bfd *mod, uint32_t *sym_byte_size,
 static bool
 create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
 			      uint32_t *size, struct string_table *strings,
-			      struct source_files_info *source)
+			      struct source_files_info *source,
+			      struct types *types)
 {
   uint8_t *ptr;
   unsigned int mod_num;
@@ -1019,7 +2200,8 @@ create_module_info_substream (bfd *abfd, bfd *pdb, void **data,
 
       if (!populate_module_stream (stream, in, &sym_byte_size,
 				   strings, &c13_info_size,
-				   &source->mods[mod_num], abfd))
+				   &source->mods[mod_num], abfd,
+				   types))
 	{
 	  for (unsigned int i = 0; i < source->mod_count; i++)
 	    {
@@ -1341,7 +2523,8 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
 		     uint16_t section_header_stream_num,
 		     uint16_t sym_rec_stream_num,
 		     uint16_t publics_stream_num,
-		     struct string_table *strings)
+		     struct string_table *strings,
+		     struct types *types)
 {
   struct pdb_dbi_stream_header h;
   struct optional_dbg_header opt;
@@ -1353,7 +2536,7 @@ populate_dbi_stream (bfd *stream, bfd *abfd, bfd *pdb,
   source.mods = NULL;
 
   if (!create_module_info_substream (abfd, pdb, &mod_info, &mod_info_size,
-				     strings, &source))
+				     strings, &source, types))
     return false;
 
   if (!create_section_contrib_substream (abfd, &sc, &sc_size))
@@ -1883,6 +3066,31 @@ populate_names_stream (bfd *stream, struct string_table *strings)
   return true;
 }
 
+/* Calculate the hash of a type_entry.  */
+static hashval_t
+hash_type_entry (const void *p)
+{
+  const struct type_entry *e = (const struct type_entry *) p;
+  uint16_t size = bfd_getl16 (e->data) + sizeof (uint16_t);
+
+  return iterative_hash (e->data, size, 0);
+}
+
+/* Compare a type_entry with a type.  */
+static int
+eq_type_entry (const void *a, const void *b)
+{
+  const struct type_entry *e = (const struct type_entry *) a;
+  uint16_t size_a = bfd_getl16 (e->data);
+  uint16_t size_b = bfd_getl16 (b);
+
+  if (size_a != size_b)
+    return 0;
+
+  return memcmp (e->data + sizeof (uint16_t),
+		 (const uint8_t *) b + sizeof (uint16_t), size_a) == 0;
+}
+
 /* Create a PDB debugging file for the PE image file abfd with the build ID
    guid, stored at pdb_name.  */
 bool
@@ -1891,9 +3099,10 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
   bfd *pdb;
   bool ret = false;
   bfd *info_stream, *dbi_stream, *names_stream, *sym_rec_stream,
-    *publics_stream;
+    *publics_stream, *tpi_stream, *ipi_stream;
   uint16_t section_header_stream_num, sym_rec_stream_num, publics_stream_num;
   struct string_table strings;
+  struct types types, ids;
 
   pdb = bfd_openw (pdb_name, "pdb");
   if (!pdb)
@@ -1927,7 +3136,9 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
       goto end;
     }
 
-  if (!create_type_stream (pdb))
+  tpi_stream = add_stream (pdb, NULL, NULL);
+
+  if (!tpi_stream)
     {
       einfo (_("%P: warning: cannot create TPI stream "
 	       "in PDB file: %E\n"));
@@ -1943,7 +3154,9 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
       goto end;
     }
 
-  if (!create_type_stream (pdb))
+  ipi_stream = add_stream (pdb, NULL, NULL);
+
+  if (!ipi_stream)
     {
       einfo (_("%P: warning: cannot create IPI stream "
 	       "in PDB file: %E\n"));
@@ -1984,15 +3197,48 @@ create_pdb_file (bfd *abfd, const char *pdb_name, const unsigned char *guid)
       goto end;
     }
 
+  types.num_types = 0;
+  types.hashmap = htab_create_alloc (0, hash_type_entry, eq_type_entry,
+				     free, xcalloc, free);
+  types.first = types.last = NULL;
+
+  ids.num_types = 0;
+  ids.hashmap = htab_create_alloc (0, hash_type_entry, eq_type_entry,
+				   free, xcalloc, free);
+  ids.first = ids.last = NULL;
+
   if (!populate_dbi_stream (dbi_stream, abfd, pdb, section_header_stream_num,
 			    sym_rec_stream_num, publics_stream_num,
-			    &strings))
+			    &strings, &types))
     {
       einfo (_("%P: warning: cannot populate DBI stream "
 	       "in PDB file: %E\n"));
+      htab_delete (types.hashmap);
+      htab_delete (ids.hashmap);
+      goto end;
+    }
+
+  if (!populate_type_stream (pdb, tpi_stream, &types))
+    {
+      einfo (_("%P: warning: cannot populate TPI stream "
+	       "in PDB file: %E\n"));
+      htab_delete (types.hashmap);
+      htab_delete (ids.hashmap);
       goto end;
     }
 
+  htab_delete (types.hashmap);
+
+  if (!populate_type_stream (pdb, ipi_stream, &ids))
+    {
+      einfo (_("%P: warning: cannot populate IPI stream "
+	       "in PDB file: %E\n"));
+      htab_delete (ids.hashmap);
+      goto end;
+    }
+
+  htab_delete (ids.hashmap);
+
   add_string ("", 0, &strings);
 
   if (!populate_names_stream (names_stream, &strings))
diff --git a/ld/pdb.h b/ld/pdb.h
index bbb106043c4..ecc26c1c87a 100644
--- a/ld/pdb.h
+++ b/ld/pdb.h
@@ -27,6 +27,41 @@
 #include "sysdep.h"
 #include "bfd.h"
 #include <stdbool.h>
+#include <stddef.h>
+
+#define LF_VTSHAPE			0x000a
+#define LF_MODIFIER			0x1001
+#define LF_POINTER			0x1002
+#define LF_PROCEDURE			0x1008
+#define LF_MFUNCTION			0x1009
+#define LF_ARGLIST			0x1201
+#define LF_FIELDLIST			0x1203
+#define LF_BITFIELD			0x1205
+#define LF_METHODLIST			0x1206
+#define LF_BCLASS			0x1400
+#define LF_VBCLASS			0x1401
+#define LF_IVBCLASS			0x1402
+#define LF_INDEX			0x1404
+#define LF_VFUNCTAB			0x1409
+#define LF_ENUMERATE			0x1502
+#define LF_ARRAY			0x1503
+#define LF_CLASS			0x1504
+#define LF_STRUCTURE			0x1505
+#define LF_UNION			0x1506
+#define LF_ENUM				0x1507
+#define LF_MEMBER			0x150d
+#define LF_STMEMBER			0x150e
+#define LF_METHOD			0x150f
+#define LF_NESTTYPE			0x1510
+#define LF_ONEMETHOD			0x1511
+
+#define LF_CHAR				0x8000
+#define LF_SHORT			0x8001
+#define LF_USHORT			0x8002
+#define LF_LONG				0x8003
+#define LF_ULONG			0x8004
+#define LF_QUADWORD			0x8009
+#define LF_UQUADWORD			0x800a
 
 #define S_PUB32				0x110e
 
@@ -65,6 +100,7 @@ struct pdb_tpi_stream_header
 #define TPI_STREAM_VERSION_80		20040203
 
 #define TPI_FIRST_INDEX			0x1000
+#define NUM_TPI_HASH_BUCKETS		0x3ffff
 
 /* NewDBIHdr in dbi.h */
 struct pdb_dbi_stream_header
@@ -210,6 +246,234 @@ struct file_checksum
   uint8_t checksum_type;
 } ATTRIBUTE_PACKED;
 
+/* lfModifier in cvinfo.h */
+struct lf_modifier
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t base_type;
+  uint16_t modifier;
+  uint16_t padding;
+} ATTRIBUTE_PACKED;
+
+/* lfPointer in cvinfo.h */
+struct lf_pointer
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t base_type;
+  uint32_t attributes;
+} ATTRIBUTE_PACKED;
+
+/* lfArgList in cvinfo.h */
+struct lf_arglist
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t num_entries;
+  uint32_t args[];
+} ATTRIBUTE_PACKED;
+
+/* lfProc in cvinfo.h */
+struct lf_procedure
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t return_type;
+  uint8_t calling_convention;
+  uint8_t attributes;
+  uint16_t num_parameters;
+  uint32_t arglist;
+} ATTRIBUTE_PACKED;
+
+/* lfMFunc in cvinfo.h */
+struct lf_mfunction
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t return_type;
+  uint32_t containing_class_type;
+  uint32_t this_type;
+  uint8_t calling_convention;
+  uint8_t attributes;
+  uint16_t num_parameters;
+  uint32_t arglist;
+  int32_t this_adjustment;
+} ATTRIBUTE_PACKED;
+
+/* lfArray in cvinfo.h */
+struct lf_array
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t element_type;
+  uint32_t index_type;
+  uint16_t length_in_bytes;
+  char name[];
+} ATTRIBUTE_PACKED;
+
+/* lfBitfield in cvinfo.h */
+struct lf_bitfield
+{
+  uint16_t size;
+  uint16_t kind;
+  uint32_t base_type;
+  uint8_t length;
+  uint8_t position;
+} ATTRIBUTE_PACKED;
+
+/* lfMember in cvinfo.h */
+struct lf_member
+{
+  uint16_t kind;
+  uint16_t attributes;
+  uint32_t type;
+  uint16_t offset;
+  char name[];
+} ATTRIBUTE_PACKED;
+
+/* from bitfield structure CV_prop_t in cvinfo.h */
+#define CV_PROP_FORWARD_REF	0x80
+#define CV_PROP_SCOPED		0x100
+#define CV_PROP_HAS_UNIQUE_NAME	0x200
+
+/* lfClass in cvinfo.h */
+struct lf_class
+{
+  uint16_t size;
+  uint16_t kind;
+  uint16_t num_members;
+  uint16_t properties;
+  uint32_t field_list;
+  uint32_t derived_from;
+  uint32_t vshape;
+  uint16_t length;
+  char name[];
+} ATTRIBUTE_PACKED;
+
+/* lfUnion in cvinfo.h */
+struct lf_union
+{
+  uint16_t size;
+  uint16_t kind;
+  uint16_t num_members;
+  uint16_t properties;
+  uint32_t field_list;
+  uint16_t length;
+  char name[];
+} ATTRIBUTE_PACKED;
+
+/* lfEnumerate in cvinfo.h */
+struct lf_enumerate
+{
+  uint16_t kind;
+  uint16_t attributes;
+  uint16_t value;
+  /* then actual value if value >= 0x8000 */
+  char name[];
+} ATTRIBUTE_PACKED;
+
+/* lfEnum in cvinfo.h */
+struct lf_enum
+{
+  uint16_t size;
+  uint16_t kind;
+  uint16_t num_elements;
+  uint16_t properties;
+  uint32_t underlying_type;
+  uint32_t field_list;
+  char name[];
+} ATTRIBUTE_PACKED;
+
+/* lfIndex in cvinfo.h */
+struct lf_index
+{
+  uint16_t kind;
+  uint16_t padding;
+  uint32_t index;
+} ATTRIBUTE_PACKED;
+
+/* lfOneMethod in cvinfo.h */
+struct lf_onemethod
+{
+  uint16_t kind;
+  uint16_t method_attribute;
+  uint32_t method_type;
+  char name[];
+} ATTRIBUTE_PACKED;
+
+/* mlMethod in cvinfo.h */
+struct lf_methodlist_entry
+{
+  uint16_t method_attribute;
+  uint16_t padding;
+  uint32_t method_type;
+} ATTRIBUTE_PACKED;
+
+/* lfMethodList in cvinfo.h */
+struct lf_methodlist
+{
+  uint16_t size;
+  uint16_t kind;
+  struct lf_methodlist_entry entries[];
+} ATTRIBUTE_PACKED;
+
+/* lfMethod in cvinfo.h */
+struct lf_method
+{
+  uint16_t kind;
+  uint16_t count;
+  uint32_t method_list;
+  char name[];
+} ATTRIBUTE_PACKED;
+
+/* lfBClass in cvinfo.h */
+struct lf_bclass
+{
+  uint16_t kind;
+  uint16_t attributes;
+  uint32_t base_class_type;
+  uint16_t offset;
+  uint16_t padding;
+} ATTRIBUTE_PACKED;
+
+/* lfVFuncTab in cvinfo.h */
+struct lf_vfunctab
+{
+  uint16_t kind;
+  uint16_t padding;
+  uint32_t type;
+} ATTRIBUTE_PACKED;
+
+/* lfVBClass in cvinfo.h */
+struct lf_vbclass
+{
+  uint16_t kind;
+  uint16_t attributes;
+  uint32_t base_class_type;
+  uint32_t virtual_base_pointer_type;
+  uint16_t virtual_base_pointer_offset;
+  uint16_t virtual_base_vbtable_offset;
+} ATTRIBUTE_PACKED;
+
+/* lfSTMember in cvinfo.h */
+struct lf_static_member
+{
+  uint16_t kind;
+  uint16_t attributes;
+  uint32_t type;
+  char name[];
+} ATTRIBUTE_PACKED;
+
+/* lfNestType in cvinfo.h */
+struct lf_nest_type
+{
+  uint16_t kind;
+  uint16_t padding;
+  uint32_t type;
+  char name[];
+} ATTRIBUTE_PACKED;
+
 extern bool create_pdb_file (bfd *, const char *, const unsigned char *);
 
 #endif
diff --git a/ld/testsuite/ld-pe/pdb-types1-hashlist.d b/ld/testsuite/ld-pe/pdb-types1-hashlist.d
new file mode 100644
index 00000000000..b75f08c1de7
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types1-hashlist.d
@@ -0,0 +1,13 @@
+
+*:     file format binary
+
+Contents of section .data:
+ 0000 5b4f0200 e29b0300 a99a0300 90160300  *
+ 0010 32970300 d6540100 fad50000 c3b00000  *
+ 0020 6a270200 7b000100 31de0000 2bf50200  *
+ 0030 b59b0300 73cf0300 10b90000 84240300  *
+ 0040 64150100 2e8a0000 88fe0000 c0660000  *
+ 0050 ffd80200 b0260100 7c060200 e3240200  *
+ 0060 63ff0100 fb6b0300 0ad90100 523c0200  *
+ 0070 4d5e0200 8a940200 4b710300 6aa90300  *
+ 0080 0a2c0300 67e10300 4a3d0300           *
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-types1-skiplist.d b/ld/testsuite/ld-pe/pdb-types1-skiplist.d
new file mode 100644
index 00000000000..52c10fa501d
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types1-skiplist.d
@@ -0,0 +1,5 @@
+
+*:     file format binary
+
+Contents of section .data:
+ 0000 00100000 00000000                    *
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-types1-typelist.d b/ld/testsuite/ld-pe/pdb-types1-typelist.d
new file mode 100644
index 00000000000..ff2d91c311e
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types1-typelist.d
@@ -0,0 +1,60 @@
+
+*:     file format binary
+
+Contents of section .data:
+ 0000 0a000110 12000000 02000000 0a000110  ................
+ 0010 12000000 01000000 0a000110 22000000  ............"...
+ 0020 02000000 0a000110 74000000 03000000  ........t.......
+ 0030 0a000210 01100000 0c000100 0a000210  ................
+ 0040 02100000 0a800000 12000112 03000000  ................
+ 0050 01100000 02100000 03100000 0e000810  ................
+ 0060 02100000 00000300 06100000 0e000315  ................
+ 0070 04100000 74000000 180000f1 0a000512  ....t...........
+ 0080 75000000 0100f2f1 0a000512 75000000  u...........u...
+ 0090 1f01f2f1 22000312 0d150300 09100000  ...."...........
+ 00a0 00006e75 6d3100f1 0d150300 0a100000  ..num1..........
+ 00b0 00006e75 6d3200f1 22000515 02000000  ..num2..".......
+ 00c0 0b100000 00000000 00000000 04003c75  ..............<u
+ 00d0 6e6e616d 65642d74 61673e00 1e000515  nnamed-tag>.....
+ 00e0 00008002 00000000 00000000 00000000  ................
+ 00f0 0000666f 6f006261 7200f2f1 0a000210  ..foo.bar.......
+ 0100 0d100000 0c000100 06000112 00000000  ................
+ 0110 1a000910 02100000 0d100000 0e100000  ................
+ 0120 00000000 0f100000 00000000 1a000910  ................
+ 0130 02100000 0d100000 0e100000 00000300  ................
+ 0140 06100000 00000000 12000612 00000000  ................
+ 0150 10100000 00000000 11100000 32000312  ............2...
+ 0160 0d150300 75000000 00006e75 6d00f2f1  ....u.....num...
+ 0170 11150000 10100000 6d657468 6f6400f1  ........method..
+ 0180 0f150200 12100000 6d657468 6f643200  ........method2.
+ 0190 1e000515 02000002 13100000 00000000  ................
+ 01a0 00000000 0400666f 6f006261 7200f2f1  ......foo.bar...
+ 01b0 22000312 0d150300 75000000 00006e75  ".......u.....nu
+ 01c0 6d3100f1 0d150300 10000000 00006e75  m1............nu
+ 01d0 6d3200f1 1a000615 02000000 15100000  m2..............
+ 01e0 04003c75 6e6e616d 65642d74 61673e00  ..<unnamed-tag>.
+ 01f0 16000615 00008002 00000000 00006261  ..............ba
+ 0200 7a007175 7800f2f1 16000615 02000002  z.qux...........
+ 0210 15100000 04006261 7a007175 7800f2f1  ......baz.qux...
+ 0220 52000312 02150300 00007265 6400f2f1  R.........red...
+ 0230 02150300 01006772 65656e00 02150300  ......green.....
+ 0240 0380ffff ffff626c 756500f1 02150300  ......blue......
+ 0250 02800080 79656c6c 6f7700f1 02150300  ....yellow......
+ 0260 0a800000 00000100 00007075 72706c65  ..........purple
+ 0270 00f3f2f1 1e000715 00008002 23000000  ............#...
+ 0280 00000000 636f6c6f 75720063 6f6c6f75  ....colour.colou
+ 0290 723200f1 1e000715 05000002 23000000  r2..........#...
+ 02a0 19100000 636f6c6f 75720063 6f6c6f75  ....colour.colou
+ 02b0 723200f1 0a000312 04140000 19100000  r2..............
+ 02c0 06000a00 010000f1 0a000210 1d100000  ................
+ 02d0 0c000100 0a000312 02150300 00006100  ..............a.
+ 02e0 22000715 01000800 75000000 1f100000  ".......u.......
+ 02f0 71757578 3a3a6e65 73746564 5f656e75  quux::nested_enu
+ 0300 6d00f2f1 52000312 00140000 14100000  m...R...........
+ 0310 0400f2f1 09140000 1e100000 01140000  ................
+ 0320 14100000 1e100000 00000000 0e150000  ................
+ 0330 02100000 73746174 69635f6d 656d6265  ....static_membe
+ 0340 7200f2f1 10150000 20100000 6e657374  r....... ...nest
+ 0350 65645f65 6e756d00 1a000515 01000000  ed_enum.........
+ 0360 21100000 00000000 00000000 04007175  !.............qu
+ 0370 757800f1                             ux..            
\ No newline at end of file
diff --git a/ld/testsuite/ld-pe/pdb-types1a.s b/ld/testsuite/ld-pe/pdb-types1a.s
new file mode 100644
index 00000000000..a2ee9a9972f
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types1a.s
@@ -0,0 +1,27 @@
+.equ CV_SIGNATURE_C13, 4
+
+.equ T_LONG, 0x0012
+
+.equ LF_MODIFIER, 0x1001
+
+.section ".debug$T", "rn"
+
+.long CV_SIGNATURE_C13
+
+# Type 1000, volatile long
+.mod1:
+.short .mod2 - .mod1 - 2
+.short LF_MODIFIER
+.long T_LONG
+.short 2 # volatile
+.p2align 2
+
+# Type 1001, const long
+.mod2:
+.short .types_end - .mod2 - 2
+.short LF_MODIFIER
+.long T_LONG
+.short 1 # const
+.p2align 2
+
+.types_end:
diff --git a/ld/testsuite/ld-pe/pdb-types1b.s b/ld/testsuite/ld-pe/pdb-types1b.s
new file mode 100644
index 00000000000..89ee6e3840f
--- /dev/null
+++ b/ld/testsuite/ld-pe/pdb-types1b.s
@@ -0,0 +1,461 @@
+.equ CV_SIGNATURE_C13, 4
+
+.equ T_CHAR, 0x0010
+.equ T_LONG, 0x0012
+.equ T_ULONG, 0x0022
+.equ T_INT4, 0x0074
+.equ T_UINT4, 0x0075
+.equ T_UQUAD, 0x0023
+
+.equ LF_VTSHAPE, 0x000a
+.equ LF_MODIFIER, 0x1001
+.equ LF_POINTER, 0x1002
+.equ LF_PROCEDURE, 0x1008
+.equ LF_MFUNCTION, 0x1009
+.equ LF_ARGLIST, 0x1201
+.equ LF_FIELDLIST, 0x1203
+.equ LF_BITFIELD, 0x1205
+.equ LF_METHODLIST, 0x1206
+.equ LF_BCLASS, 0x1400
+.equ LF_VBCLASS, 0x1401
+.equ LF_INDEX, 0x1404
+.equ LF_VFUNCTAB, 0x1409
+.equ LF_ENUMERATE, 0x1502
+.equ LF_ARRAY, 0x1503
+.equ LF_STRUCTURE, 0x1505
+.equ LF_UNION, 0x1506
+.equ LF_ENUM, 0x1507
+.equ LF_MEMBER, 0x150d
+.equ LF_STMEMBER, 0x150e
+.equ LF_METHOD, 0x150f
+.equ LF_NESTTYPE, 0x1510
+.equ LF_ONEMETHOD, 0x1511
+
+.equ LF_USHORT, 0x8002
+.equ LF_LONG, 0x8003
+.equ LF_UQUADWORD, 0x800a
+
+.equ CV_PTR_NEAR32, 0xa
+.equ CV_PTR_64, 0xc
+
+.section ".debug$T", "rn"
+
+.long CV_SIGNATURE_C13
+
+# Type 1000, const long
+.mod1:
+.short .mod2 - .mod1 - 2
+.short LF_MODIFIER
+.long T_LONG
+.short 1 # const
+.p2align 2
+
+# Type 1001, volatile unsigned long
+.mod2:
+.short .mod3 - .mod2 - 2
+.short LF_MODIFIER
+.long T_ULONG
+.short 2 # volatile
+.p2align 2
+
+# Type 1002, const volatile int
+.mod3:
+.short .ptr1 - .mod3 - 2
+.short LF_MODIFIER
+.long T_INT4
+.short 3 # const volatile
+.p2align 2
+
+# Type 1003, const long * (64-bit pointer)
+.ptr1:
+.short .ptr2 - .ptr1 - 2
+.short LF_POINTER
+.long 0x1000
+.long (8 << 13) | CV_PTR_64
+
+# Type 1004, volatile unsigned long * (32-bit pointer)
+.ptr2:
+.short .arglist1 - .ptr2 - 2
+.short LF_POINTER
+.long 0x1001
+.long (4 << 13) | CV_PTR_NEAR32
+
+# Type 1005, arg list of types 1000, 1001, 1002
+.arglist1:
+.short .proc1 - .arglist1 - 2
+.short LF_ARGLIST
+.long 3 # no. entries
+.long 0x1000
+.long 0x1001
+.long 0x1002
+
+# Type 1006, procedure, return type 1001, arg list 1005
+.proc1:
+.short .arr1 - .proc1 - 2
+.short LF_PROCEDURE
+.long 0x1001
+.byte 0 # calling convention
+.byte 0 # attributes
+.short 3 # no. parameters
+.long 0x1005
+
+# Type 1007, array[3] of const long *
+.arr1:
+.short .bitfield1 - .arr1 - 2
+.short LF_ARRAY
+.long 0x1003 # element type
+.long T_INT4 # index type
+.short 24 # length in bytes
+.byte 0 # name
+.byte 0xf1 # padding
+
+# Type 1008, bitfield of uint32_t, position 0, length 1
+.bitfield1:
+.short .bitfield2 - .bitfield1 - 2
+.short LF_BITFIELD
+.long T_UINT4
+.byte 1
+.byte 0
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1009, bitfield of uint32_t, position 1, length 31
+.bitfield2:
+.short .fieldlist1 - .bitfield2 - 2
+.short LF_BITFIELD
+.long T_UINT4
+.byte 31
+.byte 1
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 100a, field list (1008 as num1, 1009 as num2)
+.fieldlist1:
+.short .struct1 - .fieldlist1 - 2
+.short LF_FIELDLIST
+.short LF_MEMBER
+.short 3 # public
+.long 0x1008
+.short 0 # offset
+.asciz "num1"
+.byte 0xf1 # padding
+.short LF_MEMBER
+.short 3 # public
+.long 0x1009
+.short 0 # offset
+.asciz "num2"
+.byte 0xf1 # padding
+
+# Type 100b, anonymous struct, field list 100a
+.struct1:
+.short .struct2 - .struct1 - 2
+.short LF_STRUCTURE
+.short 2 # no. members
+.short 0 # property
+.long 0x100a # field list
+.long 0 # type derived from
+.long 0 # type of vshape table
+.short 4 # size
+.asciz "<unnamed-tag>"
+
+# Type 100c, forward declaration of struct foo
+.struct2:
+.short .ptr3 - .struct2 - 2
+.short LF_STRUCTURE
+.short 0 # no. members
+.short 0x280 # property (has unique name, forward declaration)
+.long 0 # field list
+.long 0 # type derived from
+.long 0 # type of vshape table
+.short 0 # size
+.asciz "foo" # name
+.asciz "bar" # unique name
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 100d, pointer to 100c
+.ptr3:
+.short .arglist2 - .ptr3 - 2
+.short LF_POINTER
+.long 0x100c
+.long (8 << 13) | CV_PTR_64
+
+# Type 100e, empty arg list
+.arglist2:
+.short .mfunc1 - .arglist2 - 2
+.short LF_ARGLIST
+.long 0 # no. entries
+
+# Type 100f, member function of 100c, return type 1001
+.mfunc1:
+.short .mfunc2 - .mfunc1 - 2
+.short LF_MFUNCTION
+.long 0x1001
+.long 0x100c
+.long 0x100d # type of "this" pointer
+.byte 0 # calling convention
+.byte 0 # attributes
+.short 0 # no. parameters
+.long 0x100e # arg list
+.long 0 # "this" adjustment
+
+# Type 1010, member function of 100c, return type 1001, arg list 1005
+.mfunc2:
+.short .methodlist1 - .mfunc2 - 2
+.short LF_MFUNCTION
+.long 0x1001
+.long 0x100c
+.long 0x100d # type of "this" pointer
+.byte 0 # calling convention
+.byte 0 # attributes
+.short 3 # no. parameters
+.long 0x1005 # arg list
+.long 0 # "this" adjustment
+
+# Type 1011, method list for both member functions 100f and 1010
+.methodlist1:
+.short .fieldlist2 - .methodlist1 - 2
+.short LF_METHODLIST
+.short 0 # attributes
+.short 0 # padding
+.long 0x100f
+.short 0 # attributes
+.short 0 # padding
+.long 0x1010
+
+# Type 1012, field list (uint32_t as num1)
+.fieldlist2:
+.short .struct3 - .fieldlist2 - 2
+.short LF_FIELDLIST
+.short LF_MEMBER
+.short 3 # public
+.long T_UINT4
+.short 0 # offset
+.asciz "num"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+.short LF_ONEMETHOD
+.short 0 # method attribute
+.long 0x100f # method type
+.asciz "method"
+.byte 0xf1
+.short LF_METHOD
+.short 2 # no. overloads
+.long 0x1011 # method list
+.asciz "method2"
+
+# Type 1013, struct foo, field list 1012
+.struct3:
+.short .fieldlist3 - .struct3 - 2
+.short LF_STRUCTURE
+.short 2 # no. members
+.short 0x200 # property (has unique name)
+.long 0x1012 # field list
+.long 0 # type derived from
+.long 0 # type of vshape table
+.short 4 # size
+.asciz "foo" # name
+.asciz "bar" # unique name
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1014, field list (uint32_t as num1, char as num2)
+.fieldlist3:
+.short .union1 - .fieldlist3 - 2
+.short LF_FIELDLIST
+.short LF_MEMBER
+.short 3 # public
+.long T_UINT4
+.short 0 # offset
+.asciz "num1"
+.byte 0xf1 # padding
+.short LF_MEMBER
+.short 3 # public
+.long T_CHAR
+.short 0 # offset
+.asciz "num2"
+.byte 0xf1 # padding
+
+# Type 1015, anonymous union (field list 1014)
+.union1:
+.short .union2 - .union1 - 2
+.short LF_UNION
+.short 2 # no. members
+.short 0 # property
+.long 0x1014
+.short 4 # size
+.asciz "<unnamed-tag>"
+
+# Type 1016, forward declaration of union baz
+.union2:
+.short .union3 - .union2 - 2
+.short LF_UNION
+.short 0 # no. members
+.short 0x280 # property (has unique name, forward declaration)
+.long 0 # field list
+.short 0 # size
+.asciz "baz"
+.asciz "qux"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1017, union baz (field list 1014)
+.union3:
+.short .fieldlist4 - .union3 - 2
+.short LF_UNION
+.short 2 # no. members
+.short 0x200 # property (has unique name, forward declaration)
+.long 0x1014 # field list
+.short 4 # size
+.asciz "baz"
+.asciz "qux"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1018, field list for enum (red = 0, green = 1, blue = -1, yellow = 0x8000, purple = 0x100000000)
+.fieldlist4:
+.short .enum1 - .fieldlist4 - 2
+.short LF_FIELDLIST
+.short LF_ENUMERATE
+.short 3 # public
+.short 0 # value
+.asciz "red"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+.short LF_ENUMERATE
+.short 3 # public
+.short 1 # value
+.asciz "green"
+.short LF_ENUMERATE
+.short 3 # public
+.short LF_LONG
+.long 0xffffffff # value
+.asciz "blue"
+.byte 0xf1 # padding
+.short LF_ENUMERATE
+.short 3 # public
+.short LF_USHORT
+.short 0x8000 # value
+.asciz "yellow"
+.byte 0xf1 # padding
+.short LF_ENUMERATE
+.short 3 # public
+.short LF_UQUADWORD
+.quad 0x100000000 # value
+.asciz "purple"
+.byte 0xf3 # padding
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1019, forward reference to enum
+.enum1:
+.short .enum2 - .enum1 - 2
+.short LF_ENUM
+.short 0 # no. elements
+.short 0x280 # property (has unique name, forward ref)
+.long T_UQUAD # underlying type
+.long 0 # field list
+.asciz "colour"
+.asciz "colour2"
+.byte 0xf1 # padding
+
+# Type 101a, enum (field list 1018)
+.enum2:
+.short .fieldlist5 - .enum2 - 2
+.short LF_ENUM
+.short 5 # no. elements
+.short 0x200 # property (has unique name)
+.long T_UQUAD # underlying type
+.long 0x1018 # field list
+.asciz "colour"
+.asciz "colour2"
+.byte 0xf1 # padding
+
+# Type 101b, field list referencing other field list 1018
+.fieldlist5:
+.short .vtshape1 - .fieldlist5 - 2
+.short LF_FIELDLIST
+.short LF_INDEX
+.short 0 # padding
+.long 0x1018
+
+# Type 101c, virtual function table shape
+.vtshape1:
+.short .ptr4 - .vtshape1 - 2
+.short LF_VTSHAPE
+.short 1 # no. descriptors
+.byte 0 # descriptor (CV_VTS_near)
+.byte 0xf1 # padding
+
+# Type 101d, pointer to 101c
+.ptr4:
+.short .fieldlist6 - .ptr4 - 2
+.short LF_POINTER
+.long 0x101c
+.long (8 << 13) | CV_PTR_64
+
+# Type 101e, fieldlist for enum
+.fieldlist6:
+.short .enum3 - .fieldlist6 - 2
+.short LF_FIELDLIST
+.short LF_ENUMERATE
+.short 3 # public
+.short 0 # value
+.asciz "a"
+
+# Type 101f, nested enum
+.enum3:
+.short .fieldlist7 - .enum3 - 2
+.short LF_ENUM
+.short 1 # no. elements
+.short 0x8 # property (is nested)
+.long T_UINT4 # underlying type
+.long 0x101e # field list
+.asciz "quux::nested_enum"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+
+# Type 1020, field list for struct quux
+.fieldlist7:
+.short .struct4 - .fieldlist7 - 2
+.short LF_FIELDLIST
+.short LF_BCLASS
+.short 0 # attributes
+.long 0x1013 # base class
+.short 4 # offset within class
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+.short LF_VFUNCTAB
+.short 0 # padding
+.long 0x101d # pointer to vtshape
+.short LF_VBCLASS
+.short 0 # attribute
+.long 0x1013 # type index of direct virtual base class
+.long 0x101d # type index of virtual base pointer
+.short 0 # virtual base pointer offset
+.short 0 # virtual base offset from vbtable
+.short LF_STMEMBER
+.short 0 # attribute
+.long 0x1001 # volatile unsigned long
+.asciz "static_member"
+.byte 0xf2 # padding
+.byte 0xf1 # padding
+.short LF_NESTTYPE
+.short 0 # padding
+.long 0x101f # enum type
+.asciz "nested_enum"
+
+# Type 1021, struct quux, field list 1020
+.struct4:
+.short .types_end - .struct4 - 2
+.short LF_STRUCTURE
+.short 1 # no. members
+.short 0 # property
+.long 0x1020 # field list
+.long 0 # type derived from
+.long 0 # type of vshape table
+.short 4 # size
+.asciz "quux" # name
+.byte 0xf1 # padding
+
+.types_end:
diff --git a/ld/testsuite/ld-pe/pdb.exp b/ld/testsuite/ld-pe/pdb.exp
index 2e5f83477aa..5661438c9e9 100644
--- a/ld/testsuite/ld-pe/pdb.exp
+++ b/ld/testsuite/ld-pe/pdb.exp
@@ -977,7 +977,179 @@ proc test4 { } {
     }
 }
 
+proc test5 { } {
+    global as
+    global ar
+    global ld
+    global objdump
+    global srcdir
+    global subdir
+
+    if ![ld_assemble $as $srcdir/$subdir/pdb-types1a.s tmpdir/pdb-types1a.o] {
+	unsupported "Build pdb-types1a.o"
+	return
+    }
+
+    if ![ld_assemble $as $srcdir/$subdir/pdb-types1b.s tmpdir/pdb-types1b.o] {
+	unsupported "Build pdb-types1b.o"
+	return
+    }
+
+    if ![ld_link $ld "tmpdir/pdb-types1.exe" "--pdb=tmpdir/pdb-types1.pdb tmpdir/pdb-types1a.o tmpdir/pdb-types1b.o"] {
+	unsupported "Create PE image with PDB file"
+	return
+    }
+
+    set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types1.pdb 0002"]
+
+    if ![string match "" $exec_output] {
+	fail "Could not extract TPI stream"
+	return
+    } else {
+	pass "Extracted TPI stream"
+    }
+
+    # check values in TPI header, and save anything interesting
+
+    set fi [open tmpdir/0002]
+    fconfigure $fi -translation binary
+
+    seek $fi 8 current
+
+    set data [read $fi 4]
+    binary scan $data i first_type
+
+    if { $first_type != 0x1000 } {
+	fail "Incorrect first type value in TPI stream."
+    } else {
+	pass "Correct first type value in TPI stream."
+    }
+
+    set data [read $fi 4]
+    binary scan $data i end_type
+
+    # end_type is one greater than the last type in the stream
+    if { $end_type != 0x1023 } {
+	fail "Incorrect end type value in TPI stream."
+    } else {
+	pass "Correct end type value in TPI stream."
+    }
+
+    set data [read $fi 4]
+    binary scan $data i type_list_size
+
+    set data [read $fi 2]
+    binary scan $data s hash_stream_index
+
+    seek $fi 2 current
+
+    set data [read $fi 4]
+    binary scan $data i hash_size
+
+    if { $hash_size != 4 } {
+	fail "Incorrect hash size in TPI stream."
+    } else {
+	pass "Correct hash size in TPI stream."
+    }
+
+    set data [read $fi 4]
+    binary scan $data i num_buckets
+
+    if { $num_buckets != 0x3ffff } {
+	fail "Incorrect number of buckets in TPI stream."
+    } else {
+	pass "Correct number of buckets in TPI stream."
+    }
+
+    set data [read $fi 4]
+    binary scan $data i hash_list_offset
+
+    set data [read $fi 4]
+    binary scan $data i hash_list_size
+
+    set data [read $fi 4]
+    binary scan $data i skip_list_offset
+
+    set data [read $fi 4]
+    binary scan $data i skip_list_size
+
+    seek $fi 8 current
+
+    set type_list [read $fi $type_list_size]
+
+    close $fi
+
+    set fi [open tmpdir/pdb-types1-typelist w]
+    fconfigure $fi -translation binary
+    puts -nonewline $fi $type_list
+    close $fi
+
+    # check type list
+
+    set exp [file_contents "$srcdir/$subdir/pdb-types1-typelist.d"]
+    set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types1-typelist"]
+    if ![string match $exp $got] {
+	fail "Incorrect type list in TPI stream."
+    } else {
+	pass "Correct type list in TPI stream."
+    }
+
+    # extract hash list and skip list
+
+    set index_str [format "%04x" $hash_stream_index]
+
+    set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types1.pdb $index_str"]
+
+    if ![string match "" $exec_output] {
+	fail "Could not extract TPI hash stream."
+    } else {
+	pass "Extracted TPI hash stream."
+    }
+
+    set fi [open tmpdir/$index_str]
+    fconfigure $fi -translation binary
+
+    seek $fi $hash_list_offset
+    set hash_list [read $fi $hash_list_size]
+
+    seek $fi $skip_list_offset
+    set skip_list [read $fi $skip_list_size]
+
+    close $fi
+
+    # check hash list
+
+    set fi [open tmpdir/pdb-types1-hashlist w]
+    fconfigure $fi -translation binary
+    puts -nonewline $fi $hash_list
+    close $fi
+
+    set exp [file_contents "$srcdir/$subdir/pdb-types1-hashlist.d"]
+    set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types1-hashlist"]
+    if ![string match $exp $got] {
+	fail "Incorrect hash list in TPI stream."
+    } else {
+	pass "Correct hash list in TPI stream."
+    }
+
+    # check skip list
+
+    set fi [open tmpdir/pdb-types1-skiplist w]
+    fconfigure $fi -translation binary
+    puts -nonewline $fi $skip_list
+    close $fi
+
+    set exp [file_contents "$srcdir/$subdir/pdb-types1-skiplist.d"]
+    set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types1-skiplist"]
+    if ![string match $exp $got] {
+	fail "Incorrect skip list in TPI stream."
+    } else {
+	pass "Correct skip list in TPI stream."
+    }
+}
+
 test1
 test2
 test3
 test4
+test5
-- 
2.37.4


  parent reply	other threads:[~2022-12-09  1:52 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-12-09  1:52 [PATCH 01/10] ld: Generate PDB string table Mark Harmstone
2022-12-09  1:52 ` [PATCH 02/10] ld: Write DEBUG_S_FILECHKSMS entries in PDBs Mark Harmstone
2022-12-09  1:52 ` [PATCH 03/10] ld: Fix segfault in populate_publics_stream Mark Harmstone
2022-12-09  1:52 ` [PATCH 04/10] ld: Write DEBUG_S_LINES entries in PDB file Mark Harmstone
2022-12-09  1:52 ` Mark Harmstone [this message]
2022-12-09  1:52 ` [PATCH 06/10] ld: Write types into IPI stream of PDB Mark Harmstone
2022-12-09  1:52 ` [PATCH 07/10] ld: Parse LF_UDT_SRC_LINE records when creating PDB file Mark Harmstone
2022-12-09  1:52 ` [PATCH 08/10] ld: Write globals stream in PDB Mark Harmstone
2022-12-09  1:52 ` [PATCH 09/10] ld: Copy other symbols into PDB file Mark Harmstone
2022-12-09  1:52 ` [PATCH 10/10] ld: Write linker symbols in PDB Mark Harmstone
2022-12-22  0:05 ` [PATCH 01/10] ld: Generate PDB string table Alan Modra
2022-12-22  1:25   ` Alan Modra
2022-12-22 10:22     ` Alan Modra
2022-12-23 10:54       ` Alan Modra

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=20221209015240.6348-5-mark@harmstone.com \
    --to=mark@harmstone.com \
    --cc=binutils@sourceware.org \
    --cc=nickc@redhat.com \
    /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).