struct type_entry *next;
   uint32_t index;
   uint32_t cv_hash;
+  bool has_udt_src_line;
   uint8_t data[];
 };
 
   return true;
 }
 
-/* Add a string to the strings table, if it's not already there.  */
-static void
+/* Add a string to the strings table, if it's not already there.  Returns its
+   offset within the string table.  */
+static uint32_t
 add_string (char *str, size_t len, struct string_table *strings)
 {
   uint32_t hash = calc_hash (str, len);
+  struct string *s;
   void **slot;
 
   slot = htab_find_slot_with_hash (strings->hashmap, str, hash, INSERT);
 
   if (!*slot)
     {
-      struct string *s;
-
       *slot = xmalloc (offsetof (struct string, s) + len);
 
       s = (struct string *) *slot;
 
       strings->strings_len += len + 1;
     }
+  else
+    {
+      s = (struct string *) *slot;
+    }
+
+  return s->offset;
 }
 
 /* Return the hash of an entry in the string table.  */
   return false;
 }
 
+/* Handle LF_UDT_SRC_LINE type entries, which are a special case.  These
+   give the source file and line number for each user-defined type that is
+   declared.  We parse these and emit instead an LF_UDT_MOD_SRC_LINE entry,
+   which also includes the module number.  */
+static bool
+handle_udt_src_line (uint8_t *data, uint16_t size, struct type_entry **map,
+                    uint32_t type_num, uint32_t num_types,
+                    struct types *ids, uint16_t mod_num,
+                    struct string_table *strings)
+{
+  struct lf_udt_src_line *usl = (struct lf_udt_src_line *) data;
+  uint32_t orig_type, source_file_type;
+  void **slot;
+  hashval_t hash;
+  struct type_entry *e, *type_e, *str_e;
+  struct lf_udt_mod_src_line *umsl;
+  struct lf_string_id *str;
+  uint32_t source_file_offset;
+
+  if (size < sizeof (struct lf_udt_src_line))
+    {
+      einfo (_("%P: warning: truncated CodeView type record"
+              " LF_UDT_SRC_LINE\n"));
+      return false;
+    }
+
+  /* Check if LF_UDT_MOD_SRC_LINE already present for type, and return.  */
+
+  orig_type = bfd_getl32 (&usl->type);
+
+  if (orig_type < TPI_FIRST_INDEX ||
+      orig_type >= TPI_FIRST_INDEX + num_types ||
+      !map[orig_type - TPI_FIRST_INDEX])
+    {
+      einfo (_("%P: warning: CodeView type record LF_UDT_SRC_LINE"
+              " referred to unknown type %v\n"), orig_type);
+      return false;
+    }
+
+  type_e = map[orig_type - TPI_FIRST_INDEX];
+
+  /* Skip if type already declared in other module.  */
+  if (type_e->has_udt_src_line)
+    return true;
+
+  if (!remap_type (&usl->type, map, type_num, num_types))
+    return false;
+
+  /* Extract string from source_file_type.  */
+
+  source_file_type = bfd_getl32 (&usl->source_file_type);
+
+  if (source_file_type < TPI_FIRST_INDEX ||
+      source_file_type >= TPI_FIRST_INDEX + num_types ||
+      !map[source_file_type - TPI_FIRST_INDEX])
+    {
+      einfo (_("%P: warning: CodeView type record LF_UDT_SRC_LINE"
+              " referred to unknown string %v\n"), source_file_type);
+      return false;
+    }
+
+  str_e = map[source_file_type - TPI_FIRST_INDEX];
+
+  if (bfd_getl16 (str_e->data + sizeof (uint16_t)) != LF_STRING_ID)
+    {
+      einfo (_("%P: warning: CodeView type record LF_UDT_SRC_LINE"
+              " pointed to unexpected record type\n"));
+      return false;
+    }
+
+  str = (struct lf_string_id *) str_e->data;
+
+  /* Add string to string table.  */
+
+  source_file_offset = add_string (str->string, strlen (str->string),
+                                  strings);
+
+  /* Add LF_UDT_MOD_SRC_LINE entry.  */
+
+  size = sizeof (struct lf_udt_mod_src_line);
+
+  e = xmalloc (offsetof (struct type_entry, data) + size);
+
+  e->next = NULL;
+  e->index = ids->num_types;
+  e->has_udt_src_line = false;
+
+  /* LF_UDT_MOD_SRC_LINE use calc_hash on the type number, rather than
+     the crc32 used for type hashes elsewhere.  */
+  e->cv_hash = calc_hash ((char *) &usl->type, sizeof (uint32_t));
+
+  type_e->has_udt_src_line = true;
+
+  umsl = (struct lf_udt_mod_src_line *) e->data;
+
+  bfd_putl16 (size - sizeof (uint16_t), &umsl->size);
+  bfd_putl16 (LF_UDT_MOD_SRC_LINE, &umsl->kind);
+  memcpy (&umsl->type, &usl->type, sizeof (uint32_t));
+  bfd_putl32 (source_file_offset, &umsl->source_file_string);
+  memcpy (&umsl->line_no, &usl->line_no, sizeof (uint32_t));
+  bfd_putl16 (mod_num + 1, &umsl->module_no);
+
+  hash = iterative_hash (e->data, size, 0);
+
+  slot = htab_find_slot_with_hash (ids->hashmap, data, hash, INSERT);
+  if (!slot)
+    {
+      free (e);
+      return false;
+    }
+
+  if (*slot)
+    {
+      free (e);
+      einfo (_("%P: warning: duplicate CodeView type record "
+              "LF_UDT_MOD_SRC_LINE\n"));
+      return false;
+    }
+
+  *slot = e;
+
+  if (ids->last)
+    ids->last->next = e;
+  else
+    ids->first = e;
+
+  ids->last = e;
+
+  map[type_num] = e;
+
+  ids->num_types++;
+
+  return true;
+}
+
 /* Parse a type definition in the .debug$T section.  We remap the numbers
    of any referenced types, and if the type is not a duplicate of one
    already seen add it to types (for TPI types) or ids (for IPI types).  */
 static bool
 handle_type (uint8_t *data, struct type_entry **map, uint32_t type_num,
             uint32_t num_types, struct types *types,
-            struct types *ids)
+            struct types *ids, uint16_t mod_num,
+            struct string_table *strings)
 {
   uint16_t size, type;
   void **slot;
        break;
       }
 
+    case LF_UDT_SRC_LINE:
+      return handle_udt_src_line (data, size, map, type_num, num_types,
+                                 ids, mod_num, strings);
+
     default:
       einfo (_("%P: warning: unrecognized CodeView type %v\n"), type);
       return false;
       else
        e->cv_hash = crc32 (data, size);
 
+      e->has_udt_src_line = false;
+
       memcpy (e->data, data, size);
 
       if (t->last)
    found to handle_type.  */
 static bool
 handle_debugt_section (asection *s, bfd *mod, struct types *types,
-                      struct types *ids)
+                      struct types *ids, uint16_t mod_num,
+                      struct string_table *strings)
 {
   bfd_byte *data = NULL;
   size_t off;
 
       size = bfd_getl16 (data + off);
 
-      if (!handle_type (data + off, map, type_num, num_types, types, ids))
+      if (!handle_type (data + off, map, type_num, num_types, types, ids,
+                       mod_num, strings))
        {
          free (data);
          free (map);
                        uint32_t *c13_info_size,
                        struct mod_source_files *mod_source,
                        bfd *abfd, struct types *types,
-                       struct types *ids)
+                       struct types *ids, uint16_t mod_num)
 {
   uint8_t int_buf[sizeof (uint32_t)];
   uint8_t *c13_info = NULL;
        }
       else if (!strcmp (s->name, ".debug$T") && s->size >= sizeof (uint32_t))
        {
-         if (!handle_debugt_section (s, mod, types, ids))
+         if (!handle_debugt_section (s, mod, types, ids, mod_num, strings))
            {
              free (c13_info);
              free (mod_source->files);
       if (!populate_module_stream (stream, in, &sym_byte_size,
                                   strings, &c13_info_size,
                                   &source->mods[mod_num], abfd,
-                                  types, ids))
+                                  types, ids, mod_num))
        {
          for (unsigned int i = 0; i < source->mod_count; i++)
            {
 
     }
 }
 
+proc test7 { } {
+    global as
+    global ar
+    global ld
+    global objdump
+    global srcdir
+    global subdir
+
+    if ![ld_assemble $as $srcdir/$subdir/pdb-types3a.s tmpdir/pdb-types3a.o] {
+       unsupported "Build pdb-types3a.o"
+       return
+    }
+
+    if ![ld_assemble $as $srcdir/$subdir/pdb-types3b.s tmpdir/pdb-types3b.o] {
+       unsupported "Build pdb-types3b.o"
+       return
+    }
+
+    if ![ld_link $ld "tmpdir/pdb-types3.exe" "--pdb=tmpdir/pdb-types3.pdb tmpdir/pdb-types3a.o tmpdir/pdb-types3b.o"] {
+       unsupported "Create PE image with PDB file"
+       return
+    }
+
+    set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types3.pdb 0004"]
+
+    if ![string match "" $exec_output] {
+       fail "Could not extract IPI stream"
+       return
+    } else {
+       pass "Extracted IPI stream"
+    }
+
+    set fi [open tmpdir/0004]
+    fconfigure $fi -translation binary
+
+    seek $fi 16 current
+
+    set data [read $fi 4]
+    binary scan $data i type_list_size
+
+    set data [read $fi 2]
+    binary scan $data s hash_stream_index
+
+    seek $fi 10 current
+
+    set data [read $fi 4]
+    binary scan $data i hash_list_offset
+
+    set data [read $fi 4]
+    binary scan $data i hash_list_size
+
+    set data [read $fi 4]
+    binary scan $data i skip_list_offset
+
+    set data [read $fi 4]
+    binary scan $data i skip_list_size
+
+    seek $fi 8 current
+
+    set type_list [read $fi $type_list_size]
+
+    close $fi
+
+    set fi [open tmpdir/pdb-types3-typelist w]
+    fconfigure $fi -translation binary
+    puts -nonewline $fi $type_list
+    close $fi
+
+    # check type list
+
+    set exp [file_contents "$srcdir/$subdir/pdb-types3-typelist.d"]
+    set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types3-typelist"]
+    if ![string match $exp $got] {
+       fail "Incorrect type list in IPI stream."
+    } else {
+       pass "Correct type list in IPI stream."
+    }
+
+    # extract hash list and skip list
+
+    set index_str [format "%04x" $hash_stream_index]
+
+    set exec_output [run_host_cmd "$ar" "x --output tmpdir tmpdir/pdb-types3.pdb $index_str"]
+
+    if ![string match "" $exec_output] {
+       fail "Could not extract IPI hash stream."
+    } else {
+       pass "Extracted IPI hash stream."
+    }
+
+    set fi [open tmpdir/$index_str]
+    fconfigure $fi -translation binary
+
+    seek $fi $hash_list_offset
+    set hash_list [read $fi $hash_list_size]
+
+    seek $fi $skip_list_offset
+    set skip_list [read $fi $skip_list_size]
+
+    close $fi
+
+    # check hash list
+
+    set fi [open tmpdir/pdb-types3-hashlist w]
+    fconfigure $fi -translation binary
+    puts -nonewline $fi $hash_list
+    close $fi
+
+    set exp [file_contents "$srcdir/$subdir/pdb-types3-hashlist.d"]
+    set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types3-hashlist"]
+    if ![string match $exp $got] {
+       fail "Incorrect hash list in IPI stream."
+    } else {
+       pass "Correct hash list in IPI stream."
+    }
+
+    # check skip list
+
+    set fi [open tmpdir/pdb-types3-skiplist w]
+    fconfigure $fi -translation binary
+    puts -nonewline $fi $skip_list
+    close $fi
+
+    set exp [file_contents "$srcdir/$subdir/pdb-types3-skiplist.d"]
+    set got [run_host_cmd "$objdump" "-s --target=binary tmpdir/pdb-types3-skiplist"]
+    if ![string match $exp $got] {
+       fail "Incorrect skip list in IPI stream."
+    } else {
+       pass "Correct skip list in IPI stream."
+    }
+}
+
 test1
 test2
 test3
 test4
 test5
 test6
+test7