diff mbox series

[07/11] Handle structs and classes for CodeView

Message ID 20240618001713.24034-8-mark@harmstone.com
State New
Headers show
Series CodeView variables and type system | expand

Commit Message

Mark Harmstone June 18, 2024, 12:17 a.m. UTC
Translates DW_TAG_structure_type DIEs into LF_STRUCTURE symbols, and
DW_TAG_class_type DIEs into LF_CLASS symbols.

    gcc/
            * dwarf2codeview.cc
            (struct codeview_type): Add is_fwd_ref member.
            (struct codeview_subtype): Add lf_member to union.
            (struct codeview_custom_type): Add lf_structure to union.
            (struct codeview_deferred_type): New structure.
            (deferred_types, last_deferred_type): New variables.
            (get_type_num): Add new args to prototype.
            (write_lf_fieldlist): Handle LF_MEMBER subtypes.
            (write_lf_structure): New function.
            (write_custom_types): Call write_lf_structure.
            (get_type_num_pointer_type): Add in_struct argument.
            (get_type_num_const_type): Likewise.
            (get_type_num_volatile_type): Likewise.
            (add_enum_forward_def): Fix get_type_num call.
            (get_type_num_enumeration_type): Add in-struct argument.
            (add_deferred_type, flush_deferred_types): New functions.
            (add_struct_forward_def, get_type_num_struct): Likewise.
            (get_type_num): Handle self-referential structs.
            (add_variable): Fix get_type_num call.
            (codeview_debug_early_finish): Call flush_deferred_types.
            * dwarf2codeview.h (LF_CLASS, LF_STRUCTURE, LF_MEMBER): Define.
---
 gcc/dwarf2codeview.cc | 513 ++++++++++++++++++++++++++++++++++++++++--
 gcc/dwarf2codeview.h  |   3 +
 2 files changed, 493 insertions(+), 23 deletions(-)

Comments

Jeff Law June 25, 2024, 5:40 a.m. UTC | #1
On 6/17/24 6:17 PM, Mark Harmstone wrote:
> Translates DW_TAG_structure_type DIEs into LF_STRUCTURE symbols, and
> DW_TAG_class_type DIEs into LF_CLASS symbols.
> 
>      gcc/
>              * dwarf2codeview.cc
>              (struct codeview_type): Add is_fwd_ref member.
>              (struct codeview_subtype): Add lf_member to union.
>              (struct codeview_custom_type): Add lf_structure to union.
>              (struct codeview_deferred_type): New structure.
>              (deferred_types, last_deferred_type): New variables.
>              (get_type_num): Add new args to prototype.
>              (write_lf_fieldlist): Handle LF_MEMBER subtypes.
>              (write_lf_structure): New function.
>              (write_custom_types): Call write_lf_structure.
>              (get_type_num_pointer_type): Add in_struct argument.
>              (get_type_num_const_type): Likewise.
>              (get_type_num_volatile_type): Likewise.
>              (add_enum_forward_def): Fix get_type_num call.
>              (get_type_num_enumeration_type): Add in-struct argument.
>              (add_deferred_type, flush_deferred_types): New functions.
>              (add_struct_forward_def, get_type_num_struct): Likewise.
>              (get_type_num): Handle self-referential structs.
>              (add_variable): Fix get_type_num call.
>              (codeview_debug_early_finish): Call flush_deferred_types.
>              * dwarf2codeview.h (LF_CLASS, LF_STRUCTURE, LF_MEMBER): Define.
Thanks.  I've pushed this to the trunk.
jeff
diff mbox series

Patch

diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc
index 475a53573e9..9c6614f6297 100644
--- a/gcc/dwarf2codeview.cc
+++ b/gcc/dwarf2codeview.cc
@@ -158,6 +158,7 @@  struct codeview_type
 {
   dw_die_ref die;
   uint32_t num;
+  bool is_fwd_ref;
 };
 
 struct die_hasher : free_ptr_hash <codeview_type>
@@ -197,6 +198,13 @@  struct codeview_subtype
     {
       uint32_t type_num;
     } lf_index;
+    struct
+    {
+      uint16_t attributes;
+      uint32_t type;
+      codeview_integer offset;
+      char *name;
+    } lf_member;
   };
 };
 
@@ -232,9 +240,25 @@  struct codeview_custom_type
       uint32_t fieldlist;
       char *name;
     } lf_enum;
+    struct
+    {
+      uint16_t num_members;
+      uint16_t properties;
+      uint32_t field_list;
+      uint32_t derived_from;
+      uint32_t vshape;
+      codeview_integer length;
+      char *name;
+    } lf_structure;
   };
 };
 
+struct codeview_deferred_type
+{
+  struct codeview_deferred_type *next;
+  dw_die_ref type;
+};
+
 static unsigned int line_label_num;
 static unsigned int func_label_num;
 static unsigned int sym_label_num;
@@ -249,8 +273,9 @@  static uint32_t last_file_id;
 static codeview_symbol *sym, *last_sym;
 static hash_table<die_hasher> *types_htab;
 static codeview_custom_type *custom_types, *last_custom_type;
+static codeview_deferred_type *deferred_types, *last_deferred_type;
 
-static uint32_t get_type_num (dw_die_ref type);
+static uint32_t get_type_num (dw_die_ref type, bool in_struct, bool no_fwd_ref);
 
 /* Record new line number against the current function.  */
 
@@ -1217,6 +1242,51 @@  write_lf_fieldlist (codeview_custom_type *t)
 	  free (v->lf_enumerate.name);
 	  break;
 
+	case LF_MEMBER:
+	  /* This is lf_member in binutils and lfMember in Microsoft's
+	     cvinfo.h:
+
+	    struct lf_member
+	    {
+	      uint16_t kind;
+	      uint16_t attributes;
+	      uint32_t type;
+	      uint16_t offset;
+	      char name[];
+	    } ATTRIBUTE_PACKED;
+	  */
+
+	  fputs (integer_asm_op (2, false), asm_out_file);
+	  fprint_whex (asm_out_file, LF_MEMBER);
+	  putc ('\n', asm_out_file);
+
+	  fputs (integer_asm_op (2, false), asm_out_file);
+	  fprint_whex (asm_out_file, v->lf_member.attributes);
+	  putc ('\n', asm_out_file);
+
+	  fputs (integer_asm_op (4, false), asm_out_file);
+	  fprint_whex (asm_out_file, v->lf_member.type);
+	  putc ('\n', asm_out_file);
+
+	  leaf_len = 8 + write_cv_integer (&v->lf_member.offset);
+
+	  if (v->lf_member.name)
+	    {
+	      name_len = strlen (v->lf_member.name) + 1;
+	      ASM_OUTPUT_ASCII (asm_out_file, v->lf_member.name, name_len);
+	    }
+	  else
+	    {
+	      name_len = 1;
+	      ASM_OUTPUT_ASCII (asm_out_file, "", name_len);
+	    }
+
+	  leaf_len += name_len;
+	  write_cv_padding (4 - (leaf_len % 4));
+
+	  free (v->lf_member.name);
+	  break;
+
 	case LF_INDEX:
 	  /* This is lf_index in binutils and lfIndex in Microsoft's cvinfo.h:
 
@@ -1308,6 +1378,82 @@  write_lf_enum (codeview_custom_type *t)
   asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
 }
 
+/* Write an LF_STRUCTURE or LF_CLASS type (the two have the same structure).  */
+
+static void
+write_lf_structure (codeview_custom_type *t)
+{
+  size_t name_len, leaf_len;
+
+  /* This is lf_class in binutils and lfClass in Microsoft's 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;
+  */
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end - %LLcv_type%x_start\n",
+	       t->num, t->num);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_start:\n", t->num);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->kind);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_structure.num_members);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (2, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_structure.properties);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_structure.field_list);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_structure.derived_from);
+  putc ('\n', asm_out_file);
+
+  fputs (integer_asm_op (4, false), asm_out_file);
+  fprint_whex (asm_out_file, t->lf_structure.vshape);
+  putc ('\n', asm_out_file);
+
+  leaf_len = 20 + write_cv_integer (&t->lf_structure.length);
+
+  if (t->lf_structure.name)
+    {
+      name_len = strlen (t->lf_structure.name) + 1;
+      ASM_OUTPUT_ASCII (asm_out_file, t->lf_structure.name, name_len);
+    }
+  else
+    {
+      static const char unnamed_struct[] = "<unnamed-tag>";
+
+      name_len = sizeof (unnamed_struct);
+      ASM_OUTPUT_ASCII (asm_out_file, unnamed_struct, name_len);
+    }
+
+  leaf_len += name_len;
+  write_cv_padding (4 - (leaf_len % 4));
+
+  free (t->lf_structure.name);
+
+  asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num);
+}
+
 /* Write the .debug$T section, which contains all of our custom type
    definitions.  */
 
@@ -1341,6 +1487,11 @@  write_custom_types (void)
 	case LF_ENUM:
 	  write_lf_enum (custom_types);
 	  break;
+
+	case LF_STRUCTURE:
+	case LF_CLASS:
+	  write_lf_structure (custom_types);
+	  break;
 	}
 
       free (custom_types);
@@ -1527,7 +1678,7 @@  add_custom_type (codeview_custom_type *ct)
    LF_POINTER type and return its number.  */
 
 static uint32_t
-get_type_num_pointer_type (dw_die_ref type)
+get_type_num_pointer_type (dw_die_ref type, bool in_struct)
 {
   uint32_t base_type_num, byte_size;
   dw_die_ref base_type;
@@ -1543,7 +1694,7 @@  get_type_num_pointer_type (dw_die_ref type)
   if (!base_type)
     return byte_size == 4 ? T_32PVOID : T_64PVOID;
 
-  base_type_num = get_type_num (base_type);
+  base_type_num = get_type_num (base_type, in_struct, false);
   if (base_type_num == 0)
     return 0;
 
@@ -1580,7 +1731,7 @@  get_type_num_pointer_type (dw_die_ref type)
    its number.  */
 
 static uint32_t
-get_type_num_const_type (dw_die_ref type)
+get_type_num_const_type (dw_die_ref type, bool in_struct)
 {
   dw_die_ref base_type;
   uint32_t base_type_num;
@@ -1602,7 +1753,7 @@  get_type_num_const_type (dw_die_ref type)
 	return 0;
     }
 
-  base_type_num = get_type_num (base_type);
+  base_type_num = get_type_num (base_type, in_struct, false);
   if (base_type_num == 0)
     return 0;
 
@@ -1625,12 +1776,13 @@  get_type_num_const_type (dw_die_ref type)
    returning its number.  */
 
 static uint32_t
-get_type_num_volatile_type (dw_die_ref type)
+get_type_num_volatile_type (dw_die_ref type, bool in_struct)
 {
   uint32_t base_type_num;
   codeview_custom_type *ct;
 
-  base_type_num = get_type_num (get_AT_ref (type, DW_AT_type));
+  base_type_num = get_type_num (get_AT_ref (type, DW_AT_type), in_struct,
+				false);
   if (base_type_num == 0)
     return 0;
 
@@ -1660,7 +1812,8 @@  add_enum_forward_def (dw_die_ref type)
 
   ct->lf_enum.count = 0;
   ct->lf_enum.properties = CV_PROP_FWDREF;
-  ct->lf_enum.underlying_type = get_type_num (get_AT_ref (type, DW_AT_type));
+  ct->lf_enum.underlying_type = get_type_num (get_AT_ref (type, DW_AT_type),
+					      false, false);
   ct->lf_enum.fieldlist = 0;
   ct->lf_enum.name = xstrdup (get_AT_string (type, DW_AT_name));
 
@@ -1673,7 +1826,7 @@  add_enum_forward_def (dw_die_ref type)
    type, returning the number of the latter.  */
 
 static uint32_t
-get_type_num_enumeration_type (dw_die_ref type)
+get_type_num_enumeration_type (dw_die_ref type, bool in_struct)
 {
   dw_die_ref first_child;
   codeview_custom_type *ct;
@@ -1819,7 +1972,8 @@  get_type_num_enumeration_type (dw_die_ref type)
   ct->kind = LF_ENUM;
   ct->lf_enum.count = count;
   ct->lf_enum.properties = 0;
-  ct->lf_enum.underlying_type = get_type_num (get_AT_ref (type, DW_AT_type));
+  ct->lf_enum.underlying_type = get_type_num (get_AT_ref (type, DW_AT_type),
+					      in_struct, false);
   ct->lf_enum.fieldlist = last_type;
   ct->lf_enum.name = xstrdup (get_AT_string (type, DW_AT_name));
 
@@ -1828,6 +1982,292 @@  get_type_num_enumeration_type (dw_die_ref type)
   return ct->num;
 }
 
+/* Add a DIE to our deferred_types list.  This happens when we have a struct
+   with a pointer to a type that hasn't been defined yet, but which gets
+   defined later on.  */
+
+static void
+add_deferred_type (dw_die_ref type)
+{
+  codeview_deferred_type *def;
+
+  def = (codeview_deferred_type *) xmalloc (sizeof (codeview_deferred_type));
+
+  def->next = NULL;
+  def->type = type;
+
+  if (!deferred_types)
+    deferred_types = def;
+  else
+    last_deferred_type->next = def;
+
+  last_deferred_type = def;
+}
+
+/* Flush the contents of our deferred_types list.  This happens after everything
+   else has been written.  We call get_type_num to ensure that a type gets
+   added to custom_types, if it hasn't been already.  */
+
+static void
+flush_deferred_types (void)
+{
+  while (deferred_types)
+    {
+      codeview_deferred_type *next;
+
+      next = deferred_types->next;
+
+      get_type_num (deferred_types->type, false, true);
+
+      free (deferred_types);
+      deferred_types = next;
+    }
+
+  last_deferred_type = NULL;
+}
+
+/* Add a forward definition for a struct or class.  */
+
+static uint32_t
+add_struct_forward_def (dw_die_ref type)
+{
+  codeview_custom_type *ct;
+
+  ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
+
+  ct->next = NULL;
+
+  switch (dw_get_die_tag (type))
+    {
+    case DW_TAG_class_type:
+      ct->kind = LF_CLASS;
+      break;
+
+    case DW_TAG_structure_type:
+      ct->kind = LF_STRUCTURE;
+      break;
+
+    default:
+      break;
+    }
+
+  ct->lf_structure.num_members = 0;
+  ct->lf_structure.properties = CV_PROP_FWDREF;
+  ct->lf_structure.field_list = 0;
+  ct->lf_structure.derived_from = 0;
+  ct->lf_structure.vshape = 0;
+  ct->lf_structure.length.neg = false;
+  ct->lf_structure.length.num = 0;
+  ct->lf_structure.name = xstrdup (get_AT_string (type, DW_AT_name));
+
+  add_custom_type (ct);
+
+  if (!get_AT_flag (type, DW_AT_declaration))
+    add_deferred_type (type);
+
+  return ct->num;
+}
+
+/* Process a DW_TAG_structure_type or DW_TAG_class_type DIE, add an
+   LF_FIELDLIST and an LF_STRUCTURE / LF_CLASS type, and return the number of
+   the latter.  */
+
+static uint32_t
+get_type_num_struct (dw_die_ref type, bool in_struct, bool *is_fwd_ref)
+{
+  dw_die_ref first_child;
+  codeview_custom_type *ct;
+  uint16_t num_members = 0;
+  uint32_t last_type;
+  const char *name;
+
+  if ((in_struct && get_AT_string (type, DW_AT_name))
+      || get_AT_flag (type, DW_AT_declaration))
+    {
+      *is_fwd_ref = true;
+      return add_struct_forward_def (type);
+    }
+
+  *is_fwd_ref = false;
+
+  /* First, add an LF_FIELDLIST for the structure's members.  We don't need to
+     worry about deduplication here, as ld will take care of that for us.
+     If there's a lot of entries, add more LF_FIELDLISTs with LF_INDEXes
+     pointing to the overflow lists.  */
+
+  first_child = dw_get_die_child (type);
+
+  ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
+
+  ct->next = NULL;
+  ct->kind = LF_FIELDLIST;
+  ct->lf_fieldlist.length = 0;
+  ct->lf_fieldlist.subtypes = NULL;
+  ct->lf_fieldlist.last_subtype = NULL;
+
+  if (first_child)
+    {
+      dw_die_ref c;
+
+      c = first_child;
+      do
+	{
+	  codeview_subtype *el;
+	  size_t el_len;
+
+	  c = dw_get_die_sib (c);
+
+	  if (dw_get_die_tag (c) != DW_TAG_member)
+	    continue;
+
+	  el = (codeview_subtype *) xmalloc (sizeof (*el));
+	  el->next = NULL;
+	  el->kind = LF_MEMBER;
+
+	  switch (get_AT_unsigned (c, DW_AT_accessibility))
+	    {
+	    case DW_ACCESS_private:
+	      el->lf_member.attributes = CV_ACCESS_PRIVATE;
+	      break;
+
+	    case DW_ACCESS_protected:
+	      el->lf_member.attributes = CV_ACCESS_PROTECTED;
+	      break;
+
+	    case DW_ACCESS_public:
+	      el->lf_member.attributes = CV_ACCESS_PUBLIC;
+	      break;
+
+	    /* Members in a C++ struct or union are public by default, members
+	      in a class are private.  */
+	    default:
+	      if (dw_get_die_tag (type) == DW_TAG_class_type)
+		el->lf_member.attributes = CV_ACCESS_PRIVATE;
+	      else
+		el->lf_member.attributes = CV_ACCESS_PUBLIC;
+	      break;
+	    }
+
+	  el->lf_member.type = get_type_num (get_AT_ref (c, DW_AT_type), true,
+					    false);
+	  el->lf_member.offset.neg = false;
+	  el->lf_member.offset.num = get_AT_unsigned (c,
+						      DW_AT_data_member_location);
+
+	  el_len = 11;
+	  el_len += cv_integer_len (&el->lf_member.offset);
+
+	  if (get_AT_string (c, DW_AT_name))
+	    {
+	      el->lf_member.name = xstrdup (get_AT_string (c, DW_AT_name));
+	      el_len += strlen (el->lf_member.name);
+	    }
+	  else
+	    {
+	      el->lf_member.name = NULL;
+	    }
+
+	  if (el_len % 4)
+	    el_len += 4 - (el_len % 4);
+
+	  /* Add an LF_INDEX subtype if everything's too big for one
+	     LF_FIELDLIST.  */
+
+	  if (ct->lf_fieldlist.length + el_len > MAX_FIELDLIST_SIZE)
+	    {
+	      codeview_subtype *idx;
+	      codeview_custom_type *ct2;
+
+	      idx = (codeview_subtype *) xmalloc (sizeof (*idx));
+	      idx->next = NULL;
+	      idx->kind = LF_INDEX;
+	      idx->lf_index.type_num = 0;
+
+	      ct->lf_fieldlist.last_subtype->next = idx;
+	      ct->lf_fieldlist.last_subtype = idx;
+
+	      ct2 = (codeview_custom_type *)
+		xmalloc (sizeof (codeview_custom_type));
+
+	      ct2->next = ct;
+	      ct2->kind = LF_FIELDLIST;
+	      ct2->lf_fieldlist.length = 0;
+	      ct2->lf_fieldlist.subtypes = NULL;
+	      ct2->lf_fieldlist.last_subtype = NULL;
+
+	      ct = ct2;
+	    }
+
+	  ct->lf_fieldlist.length += el_len;
+
+	  if (ct->lf_fieldlist.last_subtype)
+	    ct->lf_fieldlist.last_subtype->next = el;
+	  else
+	    ct->lf_fieldlist.subtypes = el;
+
+	  ct->lf_fieldlist.last_subtype = el;
+	  num_members++;
+	}
+      while (c != first_child);
+    }
+
+  while (ct)
+    {
+      codeview_custom_type *ct2;
+
+      ct2 = ct->next;
+      ct->next = NULL;
+
+      if (ct->lf_fieldlist.last_subtype->kind == LF_INDEX)
+	ct->lf_fieldlist.last_subtype->lf_index.type_num = last_type;
+
+      add_custom_type (ct);
+      last_type = ct->num;
+
+      ct = ct2;
+    }
+
+  /* Now add an LF_STRUCTURE / LF_CLASS, pointing to the LF_FIELDLIST we just
+     added.  */
+
+  ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type));
+
+  ct->next = NULL;
+
+  switch (dw_get_die_tag (type))
+    {
+    case DW_TAG_class_type:
+      ct->kind = LF_CLASS;
+      break;
+
+    case DW_TAG_structure_type:
+      ct->kind = LF_STRUCTURE;
+      break;
+
+    default:
+      break;
+    }
+
+  ct->lf_structure.num_members = num_members;
+  ct->lf_structure.properties = 0;
+  ct->lf_structure.field_list = last_type;
+  ct->lf_structure.derived_from = 0;
+  ct->lf_structure.vshape = 0;
+  ct->lf_structure.length.neg = false;
+  ct->lf_structure.length.num = get_AT_unsigned (type, DW_AT_byte_size);
+
+  name = get_AT_string (type, DW_AT_name);
+
+  if (name)
+    ct->lf_structure.name = xstrdup (name);
+  else
+    ct->lf_structure.name = NULL;
+
+  add_custom_type (ct);
+
+  return ct->num;
+}
+
 /* Process a DIE representing a type definition, add a CodeView type if
    necessary, and return its number.  If it's something we can't handle, return
    0.  We keep a hash table so that we're not adding the same type multiple
@@ -1835,9 +2275,11 @@  get_type_num_enumeration_type (dw_die_ref type)
    everything for us.  */
 
 static uint32_t
-get_type_num (dw_die_ref type)
+get_type_num (dw_die_ref type, bool in_struct, bool no_fwd_ref)
 {
   codeview_type **slot, *t;
+  uint32_t num;
+  bool is_fwd_ref;
 
   if (!type)
     return 0;
@@ -1846,47 +2288,69 @@  get_type_num (dw_die_ref type)
     types_htab = new hash_table<die_hasher> (10);
 
   slot = types_htab->find_slot_with_hash (type, htab_hash_pointer (type),
-					  INSERT);
+					  NO_INSERT);
 
-  if (*slot)
+  if (slot && *slot && (!no_fwd_ref || !(*slot)->is_fwd_ref))
     return (*slot)->num;
 
-  t = (codeview_type *) xmalloc (sizeof (codeview_type));
-  t->die = type;
+  is_fwd_ref = false;
 
   switch (dw_get_die_tag (type))
     {
     case DW_TAG_base_type:
-      t->num = get_type_num_base_type (type);
+      num = get_type_num_base_type (type);
       break;
 
     case DW_TAG_typedef:
       /* FIXME - signed longs typedef'd as "HRESULT" should get their
 		 own type (T_HRESULT) */
-      t->num = get_type_num (get_AT_ref (type, DW_AT_type));
+      num = get_type_num (get_AT_ref (type, DW_AT_type), in_struct, false);
       break;
 
     case DW_TAG_pointer_type:
-      t->num = get_type_num_pointer_type (type);
+      num = get_type_num_pointer_type (type, in_struct);
       break;
 
     case DW_TAG_const_type:
-      t->num = get_type_num_const_type (type);
+      num = get_type_num_const_type (type, in_struct);
       break;
 
     case DW_TAG_volatile_type:
-      t->num = get_type_num_volatile_type (type);
+      num = get_type_num_volatile_type (type, in_struct);
       break;
 
     case DW_TAG_enumeration_type:
-      t->num = get_type_num_enumeration_type (type);
+      num = get_type_num_enumeration_type (type, in_struct);
+      break;
+
+    case DW_TAG_structure_type:
+    case DW_TAG_class_type:
+      num = get_type_num_struct (type, in_struct, &is_fwd_ref);
       break;
 
     default:
-      t->num = 0;
+      num = 0;
       break;
     }
 
+  /* Check hash table again, and account for the fact that self-referential
+     structs will have created a forward reference to themselves.  */
+
+  slot = types_htab->find_slot_with_hash (type, htab_hash_pointer (type),
+					  INSERT);
+
+  if (*slot && (*slot)->is_fwd_ref && !is_fwd_ref)
+    {
+      (*slot)->num = num;
+      (*slot)->is_fwd_ref = false;
+      return num;
+    }
+
+  t = (codeview_type *) xmalloc (sizeof (codeview_type));
+  t->die = type;
+  t->num = num;
+  t->is_fwd_ref = is_fwd_ref;
+
   *slot = t;
 
   return t->num;
@@ -1909,7 +2373,8 @@  add_variable (dw_die_ref die)
 
   s->next = NULL;
   s->kind = get_AT (die, DW_AT_external) ? S_GDATA32 : S_LDATA32;
-  s->data_symbol.type = get_type_num (get_AT_ref (die, DW_AT_type));
+  s->data_symbol.type = get_type_num (get_AT_ref (die, DW_AT_type), false,
+				      false);
   s->data_symbol.name = xstrdup (name);
   s->data_symbol.die = die;
 
@@ -1944,6 +2409,8 @@  codeview_debug_early_finish (dw_die_ref die)
       c = dw_get_die_sib (c);
     }
   while (c != first_child);
+
+  flush_deferred_types ();
 }
 
 #endif
diff --git a/gcc/dwarf2codeview.h b/gcc/dwarf2codeview.h
index 3f3695625c4..d4740247d93 100644
--- a/gcc/dwarf2codeview.h
+++ b/gcc/dwarf2codeview.h
@@ -66,7 +66,10 @@  along with GCC; see the file COPYING3.  If not see
 #define LF_FIELDLIST		0x1203
 #define LF_INDEX		0x1404
 #define LF_ENUMERATE		0x1502
+#define LF_CLASS		0x1504
+#define LF_STRUCTURE		0x1505
 #define LF_ENUM			0x1507
+#define LF_MEMBER		0x150d
 #define LF_CHAR			0x8000
 #define LF_SHORT		0x8001
 #define LF_USHORT		0x8002