@@ -83,6 +83,7 @@ static struct pdb_type *pointer_types = NULL;
static struct pdb_type *proc_types = NULL;
static struct pdb_type *modifier_types = NULL;
static struct pdb_type *fieldlist_types = NULL;
+static struct pdb_type *struct_types = NULL, *last_struct_type = NULL;
static struct pdb_type *enum_types = NULL;
static struct pdb_type *array_types = NULL;
static struct pdb_alias *aliases = NULL;
@@ -105,6 +106,7 @@ static struct pdb_type *complex16_type, *complex32_type, *complex48_type,
static struct pdb_type *void_type, *nullptr_type;
static bool builtins_initialized = false;
static hash_table <alias_hasher> alias_hash_table (31);
+static hash_table <struct_hasher> struct_hash_table (31);
const struct gcc_debug_hooks pdb_debug_hooks = {
pdbout_init,
@@ -825,6 +827,17 @@ free_type (struct pdb_type *t)
break;
}
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ {
+ struct pdb_struct *str = (struct pdb_struct *) t->data;
+
+ if (str->name)
+ free (str->name);
+
+ break;
+ }
+
case LF_ENUM:
{
struct pdb_enum *en = (struct pdb_enum *) t->data;
@@ -839,7 +852,8 @@ free_type (struct pdb_type *t)
free (t);
}
-/* Output a lfFieldlist structure, which describes the values of an enum. */
+/* Output a lfFieldlist structure, which describes the fields of a struct,
+ * class, or union, or the values of an enum. */
static void
write_fieldlist (struct pdb_fieldlist *fl)
{
@@ -851,6 +865,10 @@ write_fieldlist (struct pdb_fieldlist *fl)
switch (fl->entries[i].cv_type)
{
+ case LF_MEMBER:
+ len += 9 + (fl->entries[i].name ? strlen (fl->entries[i].name) : 0);
+ break;
+
case LF_ENUMERATE:
len += 5;
@@ -904,6 +922,43 @@ write_fieldlist (struct pdb_fieldlist *fl)
switch (fl->entries[i].cv_type)
{
+ case LF_MEMBER:
+ {
+ size_t name_len =
+ fl->entries[i].name ? strlen (fl->entries[i].name) : 0;
+ unsigned int align;
+
+ fprintf (asm_out_file, "\t.short\t0x%x\n",
+ fl->entries[i].fld_attr);
+ fprintf (asm_out_file, "\t.short\t0x%x\n",
+ fl->entries[i].type ? fl->entries[i].type->id : 0);
+ fprintf (asm_out_file, "\t.short\t0\n"); // padding
+ fprintf (asm_out_file, "\t.short\t0x%x\n", fl->entries[i].offset);
+
+ if (fl->entries[i].name)
+ ASM_OUTPUT_ASCII (asm_out_file, fl->entries[i].name,
+ name_len + 1);
+ else
+ fprintf (asm_out_file, "\t.byte\t0\n");
+
+ // handle alignment padding
+
+ align = 4 - ((3 + name_len) % 4);
+
+ if (align != 4)
+ {
+ if (align == 3)
+ fprintf (asm_out_file, "\t.byte\t0xf3\n");
+
+ if (align >= 2)
+ fprintf (asm_out_file, "\t.byte\t0xf2\n");
+
+ fprintf (asm_out_file, "\t.byte\t0xf1\n");
+ }
+
+ break;
+ }
+
case LF_ENUMERATE:
{
size_t name_len =
@@ -1000,6 +1055,48 @@ write_fieldlist (struct pdb_fieldlist *fl)
}
}
+/* Output a lfClass / lfStructure struct. */
+static void
+write_struct (uint16_t type, struct pdb_struct *str)
+{
+ size_t name_len = str->name ? strlen (str->name) : (sizeof (unnamed) - 1);
+ unsigned int len = 23 + name_len, align;
+
+ if (len % 4 != 0)
+ len += 4 - (len % 4);
+
+ fprintf (asm_out_file, "\t.short\t0x%x\n", len - 2);
+ fprintf (asm_out_file, "\t.short\t0x%x\n", type);
+ fprintf (asm_out_file, "\t.short\t0x%x\n", str->count);
+ fprintf (asm_out_file, "\t.short\t0x%x\n", str->property.value);
+ fprintf (asm_out_file, "\t.short\t0x%x\n",
+ str->field_type ? str->field_type->id : 0);
+ fprintf (asm_out_file, "\t.short\t0\n"); // derived
+ fprintf (asm_out_file, "\t.short\t0\n"); // vshape
+ fprintf (asm_out_file, "\t.short\t0\n");
+ fprintf (asm_out_file, "\t.short\t0\n");
+ fprintf (asm_out_file, "\t.short\t0\n");
+ fprintf (asm_out_file, "\t.short\t0x%x\n", str->size);
+
+ if (str->name)
+ ASM_OUTPUT_ASCII (asm_out_file, str->name, name_len + 1);
+ else
+ ASM_OUTPUT_ASCII (asm_out_file, unnamed, sizeof (unnamed));
+
+ align = 4 - ((3 + name_len) % 4);
+
+ if (align != 4)
+ {
+ if (align == 3)
+ fprintf (asm_out_file, "\t.byte\t0xf3\n");
+
+ if (align >= 2)
+ fprintf (asm_out_file, "\t.byte\t0xf2\n");
+
+ fprintf (asm_out_file, "\t.byte\t0xf1\n");
+ }
+}
+
/* Output a lfEnum structure. */
static void
write_enum (struct pdb_enum *en)
@@ -1186,6 +1283,11 @@ write_type (struct pdb_type *t)
write_fieldlist ((struct pdb_fieldlist *) t->data);
break;
+ case LF_CLASS:
+ case LF_STRUCTURE:
+ write_struct (t->cv_type, (struct pdb_struct *) t->data);
+ break;
+
case LF_ENUM:
write_enum ((struct pdb_enum *) t->data);
break;
@@ -1326,6 +1428,13 @@ get_tree_name (tree t)
if (TREE_CODE (t) == FUNCTION_DECL)
name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (t)));
+ else if (TYPE_NAME (t) && TREE_CODE (TYPE_NAME (t)) == IDENTIFIER_NODE)
+ name = xstrdup (IDENTIFIER_POINTER (TYPE_NAME (t)));
+ else if (TYPE_NAME (t) && TREE_CODE (TYPE_NAME (t)) == TYPE_DECL
+ && IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (t)))[0] != '.')
+ name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (t))));
+ else if (DECL_NAME (t) && TREE_CODE (DECL_NAME (t)) == IDENTIFIER_NODE)
+ name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (t)));
else
return NULL;
@@ -1396,7 +1505,73 @@ pdbout_late_global_decl (tree var)
global_vars = v;
}
-/* Add a fieldlist, which is the basis of enums. */
+/* Allocate a pdb_type for a forward declaration for a struct. The debugger
+ * will resolve this automatically, by searching for a substantive
+ * struct definition with the same name. */
+static struct pdb_type *
+add_struct_forward_declaration (tree t, const char *name)
+{
+ struct pdb_type *strtype;
+ struct pdb_struct *str;
+
+ if (name)
+ {
+ strtype =
+ struct_hash_table.find_with_hash (name, struct_hasher::hash (name));
+
+ if (strtype)
+ return strtype;
+ }
+
+ strtype =
+ (struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) +
+ sizeof (struct pdb_struct));
+
+ if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t))
+ strtype->cv_type = LF_CLASS;
+ else
+ strtype->cv_type = LF_STRUCTURE;
+
+ strtype->tree = NULL;
+ strtype->next = strtype->next2 = NULL;
+ strtype->id = 0;
+
+ str = (struct pdb_struct *) strtype->data;
+ str->count = 0;
+ str->field_type = NULL;
+ str->size = 0;
+ str->property.value = 0;
+ str->property.s.fwdref = 1;
+ str->name = name ? xstrdup (name) : NULL;
+
+ if (last_struct_type)
+ last_struct_type->next2 = strtype;
+ else
+ struct_types = strtype;
+
+ last_struct_type = strtype;
+
+ if (last_type)
+ last_type->next = strtype;
+ else
+ types = strtype;
+
+ last_type = strtype;
+
+ if (name)
+ {
+ struct pdb_type **slot =
+ struct_hash_table.find_slot_with_hash (name,
+ struct_hasher::hash (name),
+ INSERT);
+ *slot = strtype;
+ }
+
+ return strtype;
+}
+
+/* Add a fieldlist - this type does double duty as the basis
+ * of both enums and structs. */
static struct pdb_type *
add_type_fieldlist (struct pdb_type *t)
{
@@ -1425,7 +1600,20 @@ add_type_fieldlist (struct pdb_type *t)
break;
}
- if (pfe1->cv_type == LF_ENUMERATE)
+ if (pfe1->cv_type == LF_MEMBER)
+ {
+ if (pfe1->type != pfe2->type ||
+ pfe1->offset != pfe2->offset ||
+ pfe1->fld_attr != pfe2->fld_attr ||
+ ((pfe1->name || pfe2->name) &&
+ (!pfe1->name || !pfe2->name ||
+ strcmp (pfe1->name, pfe2->name))))
+ {
+ same = false;
+ break;
+ }
+ }
+ else if (pfe1->cv_type == LF_ENUMERATE)
{
if (pfe1->value != pfe2->value ||
((pfe1->name || pfe2->name) &&
@@ -1477,6 +1665,278 @@ add_type_fieldlist (struct pdb_type *t)
return t;
}
+inline hashval_t
+struct_hasher::hash (struct_hasher::compare_type name)
+{
+ return htab_hash_string (name);
+}
+
+inline bool
+struct_hasher::equal (const value_type type, compare_type name)
+{
+ struct pdb_struct *str = (struct pdb_struct *) type->data;
+
+ return !strcmp (str->name, name);
+}
+
+/* For a given struct or class, allocate a new pdb_type and
+ * add it to the type list. */
+static struct pdb_type *
+find_type_struct (tree t)
+{
+ tree f;
+ struct pdb_type *fltype = NULL, *strtype, *fwddef = NULL,
+ *last_entry = NULL;
+ struct pdb_fieldlist *fieldlist;
+ struct pdb_fieldlist_entry *ent;
+ struct pdb_struct *str;
+ unsigned int num_entries = 0;
+ bool new_fwddef = false;
+ char *name = get_tree_name (t);
+ uint16_t size =
+ TYPE_SIZE (t) ? (TREE_INT_CST_ELT (TYPE_SIZE (t), 0) / 8) : 0;
+ union pdb_property prop;
+ struct pdb_type **slot;
+
+ if (name)
+ {
+ strtype =
+ struct_hash_table.find_with_hash (name, struct_hasher::hash (name));
+
+ /* Use type found in hash map, unless this is a substantive definition
+ * replacing a forward declaration. */
+
+ if (strtype)
+ {
+ str = (struct pdb_struct *) strtype->data;
+
+ if (TYPE_SIZE (t) == 0 || str->property.s.fwdref != 1)
+ return strtype;
+ }
+ }
+
+ f = TYPE_FIELDS (t);
+
+ while (f)
+ {
+ if (TREE_CODE (f) == FIELD_DECL && DECL_FIELD_OFFSET (f))
+ {
+ if (DECL_NAME (f) && IDENTIFIER_POINTER (DECL_NAME (f)))
+ num_entries++;
+ else
+ { // anonymous field
+ struct pdb_type *type = find_type (TREE_TYPE (f));
+
+ if (type
+ && (type->cv_type == LF_CLASS
+ || type->cv_type == LF_STRUCTURE))
+ {
+ struct pdb_struct *str2 = (struct pdb_struct *) type->data;
+
+ if (str2->field_type)
+ {
+ struct pdb_fieldlist *fl =
+ (struct pdb_fieldlist *) str2->field_type->data;
+
+ // count fields of anonymous struct or union as our own
+
+ num_entries += fl->count;
+ }
+ }
+ }
+ }
+
+ f = TREE_CHAIN (f);
+ }
+
+ if (TYPE_SIZE (t) != 0) // not forward declaration
+ {
+ fwddef = add_struct_forward_declaration (t, name);
+
+ if (!fwddef->tree)
+ {
+ new_fwddef = true;
+ fwddef->tree = t;
+
+ slot =
+ tree_hash_table.find_slot_with_hash (t,
+ pdb_type_tree_hasher::
+ hash (t), INSERT);
+ *slot = fwddef;
+ }
+ }
+
+ if (num_entries > 0)
+ {
+ // add fieldlist type
+
+ fltype =
+ (struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) +
+ offsetof (struct pdb_fieldlist,
+ entries) +
+ (num_entries *
+ sizeof (struct pdb_fieldlist_entry)));
+ fltype->cv_type = LF_FIELDLIST;
+ fltype->tree = NULL;
+
+ fieldlist = (struct pdb_fieldlist *) fltype->data;
+ fieldlist->count = num_entries;
+
+ ent = fieldlist->entries;
+ f = TYPE_FIELDS (t);
+
+ while (f)
+ {
+ if (TREE_CODE (f) == FIELD_DECL && DECL_FIELD_OFFSET (f))
+ {
+ unsigned int bit_offset =
+ (TREE_INT_CST_ELT (DECL_FIELD_OFFSET (f), 0) * 8) +
+ TREE_INT_CST_ELT (DECL_FIELD_BIT_OFFSET (f), 0);
+
+ if (DECL_NAME (f) && IDENTIFIER_POINTER (DECL_NAME (f)))
+ {
+
+ ent->cv_type = LF_MEMBER;
+ ent->fld_attr = CV_FLDATTR_PUBLIC;
+ ent->name = xstrdup (IDENTIFIER_POINTER (DECL_NAME (f)));
+
+ ent->type = find_type (TREE_TYPE (f));
+ ent->offset = bit_offset / 8;
+
+ ent++;
+ }
+ else // anonymous field
+ {
+ struct pdb_type *type = find_type (TREE_TYPE (f));
+
+ if (type
+ && (type->cv_type == LF_CLASS
+ || type->cv_type == LF_STRUCTURE))
+ {
+ struct pdb_struct *str2 =
+ (struct pdb_struct *) type->data;
+
+ if (str2->field_type)
+ {
+ struct pdb_fieldlist *fl =
+ (struct pdb_fieldlist *) str2->field_type->data;
+
+ // treat fields of anonymous struct or union
+ // as our own
+
+ for (unsigned int i = 0; i < fl->count; i++)
+ {
+ ent->cv_type = fl->entries[i].cv_type;
+ ent->type = fl->entries[i].type;
+ ent->offset =
+ (bit_offset / 8) + fl->entries[i].offset;
+ ent->fld_attr = fl->entries[i].fld_attr;
+ ent->name =
+ fl->entries[i].name ?
+ xstrdup (fl->entries[i].name) : NULL;
+
+ ent++;
+ }
+ }
+ }
+ }
+ }
+
+ f = TREE_CHAIN (f);
+ }
+
+ fltype = add_type_fieldlist (fltype);
+ }
+
+ // add type for struct
+
+ prop.value = 0;
+
+ if (!TYPE_SIZE (t)) // forward declaration
+ prop.s.fwdref = 1;
+
+ if (!name)
+ {
+ strtype = struct_types;
+ while (strtype)
+ {
+ str = (struct pdb_struct *) strtype->data;
+
+ if (str->count == num_entries &&
+ str->field_type == fltype &&
+ str->size == size &&
+ str->property.value == prop.value &&
+ !str->name)
+ return strtype;
+
+ last_entry = strtype;
+ strtype = strtype->next2;
+ }
+ }
+ else
+ last_entry = last_struct_type;
+
+ strtype =
+ (struct pdb_type *) xmalloc (offsetof (struct pdb_type, data) +
+ sizeof (struct pdb_struct));
+
+ if (TYPE_LANG_SPECIFIC (t) && CLASSTYPE_DECLARED_CLASS (t))
+ strtype->cv_type = LF_CLASS;
+ else
+ strtype->cv_type = LF_STRUCTURE;
+
+ if (TYPE_SIZE (t) != 0) // not forward declaration
+ strtype->tree = t;
+ else
+ strtype->tree = NULL;
+
+ strtype->next = strtype->next2 = NULL;
+ strtype->id = 0;
+
+ str = (struct pdb_struct *) strtype->data;
+ str->count = num_entries;
+ str->field_type = fltype;
+ str->size = size;
+ str->property.value = prop.value;
+ str->name = name;
+
+ if (last_entry)
+ last_entry->next2 = strtype;
+ else
+ struct_types = strtype;
+
+ last_struct_type = strtype;
+
+ if (last_type)
+ last_type->next = strtype;
+ else
+ types = strtype;
+
+ last_type = strtype;
+
+ if (new_fwddef)
+ fwddef->tree = NULL;
+
+ if (strtype->tree)
+ {
+ slot =
+ tree_hash_table.find_slot_with_hash (t, htab_hash_pointer (t),
+ INSERT);
+ *slot = strtype;
+ }
+
+ if (name)
+ {
+ slot =
+ struct_hash_table.find_slot_with_hash (name,
+ struct_hasher::hash (name),
+ INSERT);
+ *slot = strtype;
+ }
+
+ return strtype;
+}
+
/* Given an array type t, allocate a new pdb_type and add it to the
* type list. */
static struct pdb_type *
@@ -2283,6 +2743,9 @@ find_type (tree t)
case ARRAY_TYPE:
return find_type_array (t);
+ case RECORD_TYPE:
+ return find_type_struct (t);
+
case ENUMERAL_TYPE:
return find_type_enum (t);
@@ -39,7 +39,10 @@
#define LF_FIELDLIST 0x1203
#define LF_ENUMERATE 0x1502
#define LF_ARRAY 0x1503
+#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
@@ -169,6 +172,37 @@ struct pdb_fieldlist
struct pdb_fieldlist_entry entries[1];
};
+union pdb_property
+{ // CV_prop_t in cvdump
+ struct
+ {
+ uint16_t packed:1;
+ uint16_t ctor:1;
+ uint16_t ovlops:1;
+ uint16_t isnested:1;
+ uint16_t cnested:1;
+ uint16_t opassign:1;
+ uint16_t opcast:1;
+ uint16_t fwdref:1;
+ uint16_t scoped:1;
+ uint16_t hasuniquename:1;
+ uint16_t sealed:1;
+ uint16_t hfa:2;
+ uint16_t intrinsic:1;
+ uint16_t mocom:2;
+ } s;
+ uint16_t value;
+};
+
+struct pdb_struct
+{
+ unsigned int count;
+ struct pdb_type *field_type;
+ uint16_t size;
+ union pdb_property property;
+ char *name;
+};
+
struct pdb_enum
{
unsigned int count;
@@ -1287,4 +1321,21 @@ struct alias_hasher : nofree_ptr_hash <struct pdb_alias>
static inline bool equal (const value_type, compare_type);
};
+struct struct_hasher : nofree_ptr_hash <struct pdb_type>
+{
+ typedef struct pdb_type *value_type;
+ typedef const char *compare_type;
+
+ static inline hashval_t hash (compare_type);
+
+ static inline hashval_t hash (const value_type t)
+ {
+ struct pdb_struct *str = (struct pdb_struct *) t->data;
+
+ return hash (str->name);
+ }
+
+ static inline bool equal (const value_type, compare_type);
+};
+
#endif