https://gcc.gnu.org/g:c5043d89449758ecf6a3f1475e027a73976f58c3
commit r15-3279-gc5043d89449758ecf6a3f1475e027a73976f58c3 Author: Mark Harmstone <m...@harmstone.com> Date: Mon Aug 26 22:40:56 2024 +0100 Record member functions in CodeView struct definitions CodeView has two ways of recording struct member functions. Non-overloaded functions have an LF_ONEMETHOD sub-type in the field list, which records the name and the function type (LF_MFUNCTION). Overloaded functions have an LF_METHOD instead, which points to an LF_METHODLIST, which is an array of links to various LF_MFUNCTION types. gcc/ * dwarf2codeview.cc (enum cv_leaf_type): Add LF_MFUNCTION, LF_METHODLIST, LF_METHOD, and LF_ONEMETHOD. (struct codeview_subtype): Add lf_onemethod and lf_method to union. (struct lf_methodlist_entry): New type. (struct codeview_custom_type): Add lf_mfunc_id, lf_mfunction, and lf_methodlist to union. (struct codeview_method): New type. (struct method_hasher): New type. (get_type_num_subroutine_type): Add forward declaration. (write_lf_fieldlist): Handle LF_ONEMETHOD and LF_METHOD. (write_lf_mfunction): New function. (write_lf_methodlist): New function. (write_custom_types): Handle LF_MFUNCTION and LF_METHODLIST. (add_struct_function): New function. (get_mfunction_type): New function. (is_templated_func): New function. (get_type_num_struct): Handle DW_TAG_subprogram child DIEs. (get_type_num_subroutine_type): Add containing_class_type, this_type, and this_adjustment params, and handle creating LF_MFUNCTION types as well as LF_PROCEDURE. (get_type_num): New params for get_type_num_subroutine_type. (add_function): New params for get_type_num_subroutine_type. * dwarf2codeview.h (CV_METHOD_VANILLA, CV_METHOD_VIRTUAL): Define. (CV_METHOD_STATIC, CV_METHOD_FRIEND, CV_METHOD_INTRO): Likewise. (CV_METHOD_PUREVIRT, CV_METHOD_PUREINTRO): Likewise. Diff: --- gcc/dwarf2codeview.cc | 530 ++++++++++++++++++++++++++++++++++++++++++++++++-- gcc/dwarf2codeview.h | 9 + 2 files changed, 528 insertions(+), 11 deletions(-) diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc index e243e0fba251..00a1795575ab 100644 --- a/gcc/dwarf2codeview.cc +++ b/gcc/dwarf2codeview.cc @@ -95,9 +95,11 @@ enum cv_leaf_type { LF_MODIFIER = 0x1001, LF_POINTER = 0x1002, LF_PROCEDURE = 0x1008, + LF_MFUNCTION = 0x1009, LF_ARGLIST = 0x1201, LF_FIELDLIST = 0x1203, LF_BITFIELD = 0x1205, + LF_METHODLIST = 0x1206, LF_INDEX = 0x1404, LF_ENUMERATE = 0x1502, LF_ARRAY = 0x1503, @@ -107,6 +109,8 @@ enum cv_leaf_type { LF_ENUM = 0x1507, LF_MEMBER = 0x150d, LF_STMEMBER = 0x150e, + LF_METHOD = 0x150f, + LF_ONEMETHOD = 0x1511, LF_FUNC_ID = 0x1601, LF_STRING_ID = 0x1605, LF_CHAR = 0x8000, @@ -1225,9 +1229,27 @@ struct codeview_subtype uint32_t type; char *name; } lf_static_member; + struct + { + uint16_t method_attribute; + uint32_t method_type; + char *name; + } lf_onemethod; + struct + { + uint16_t count; + uint32_t method_list; + char *name; + } lf_method; }; }; +struct lf_methodlist_entry +{ + uint16_t method_attribute; + uint32_t method_type; +}; + struct codeview_custom_type { struct codeview_custom_type *next; @@ -1302,10 +1324,32 @@ struct codeview_custom_type char *name; } lf_func_id; struct + { + uint32_t parent_type; + uint32_t function_type; + char *name; + } lf_mfunc_id; + struct { uint32_t substring; char *string; } lf_string_id; + struct + { + 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; + } lf_mfunction; + struct + { + unsigned int count; + lf_methodlist_entry *entries; + } lf_methodlist; }; }; @@ -1330,6 +1374,31 @@ struct string_id_hasher : nofree_ptr_hash <struct codeview_custom_type> } }; +struct codeview_method +{ + uint16_t attribute; + uint32_t type; + char *name; + unsigned int count; + struct codeview_method *next; + struct codeview_method *last; +}; + +struct method_hasher : nofree_ptr_hash <struct codeview_method> +{ + typedef const char *compare_type; + + static hashval_t hash (const codeview_method *x) + { + return htab_hash_string (x->name); + } + + static bool equal (const codeview_method *x, const char *y) + { + return !strcmp (x->name, y); + } +}; + static unsigned int line_label_num; static unsigned int func_label_num; static unsigned int sym_label_num; @@ -1348,6 +1417,10 @@ static codeview_deferred_type *deferred_types, *last_deferred_type; static hash_table<string_id_hasher> *string_id_htab; static uint32_t get_type_num (dw_die_ref type, bool in_struct, bool no_fwd_ref); +static uint32_t get_type_num_subroutine_type (dw_die_ref type, bool in_struct, + uint32_t containing_class_type, + uint32_t this_type, + int32_t this_adjustment); /* Record new line number against the current function. */ @@ -3704,6 +3777,74 @@ write_lf_fieldlist (codeview_custom_type *t) free (v->lf_static_member.name); break; + case LF_ONEMETHOD: + /* This is lf_onemethod in binutils and lfOneMethod in Microsoft's + cvinfo.h: + + struct lf_onemethod + { + uint16_t kind; + uint16_t method_attribute; + uint32_t method_type; + char name[]; + } ATTRIBUTE_PACKED; + */ + + fputs (integer_asm_op (2, false), asm_out_file); + fprint_whex (asm_out_file, LF_ONEMETHOD); + putc ('\n', asm_out_file); + + fputs (integer_asm_op (2, false), asm_out_file); + fprint_whex (asm_out_file, v->lf_onemethod.method_attribute); + putc ('\n', asm_out_file); + + fputs (integer_asm_op (4, false), asm_out_file); + fprint_whex (asm_out_file, v->lf_onemethod.method_type); + putc ('\n', asm_out_file); + + name_len = strlen (v->lf_onemethod.name) + 1; + ASM_OUTPUT_ASCII (asm_out_file, v->lf_onemethod.name, name_len); + + leaf_len = 8 + name_len; + write_cv_padding (4 - (leaf_len % 4)); + + free (v->lf_onemethod.name); + break; + + case LF_METHOD: + /* This is lf_method in binutils and lfMethod in Microsoft's + cvinfo.h: + + struct lf_method + { + uint16_t kind; + uint16_t count; + uint32_t method_list; + char name[]; + } ATTRIBUTE_PACKED; + */ + + fputs (integer_asm_op (2, false), asm_out_file); + fprint_whex (asm_out_file, LF_METHOD); + putc ('\n', asm_out_file); + + fputs (integer_asm_op (2, false), asm_out_file); + fprint_whex (asm_out_file, v->lf_method.count); + putc ('\n', asm_out_file); + + fputs (integer_asm_op (4, false), asm_out_file); + fprint_whex (asm_out_file, v->lf_method.method_list); + putc ('\n', asm_out_file); + + name_len = strlen (v->lf_method.name) + 1; + ASM_OUTPUT_ASCII (asm_out_file, v->lf_method.name, name_len); + + leaf_len = 8 + name_len; + write_cv_padding (4 - (leaf_len % 4)); + + free (v->lf_method.name); + break; + default: break; } @@ -4196,6 +4337,129 @@ write_lf_string_id (codeview_custom_type *t) asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num); } +/* Write an LF_MFUNCTION type, representing a member function. This is the + struct-scoped equivalent of the LF_PROCEDURE type. */ + +static void +write_lf_mfunction (codeview_custom_type *t) +{ + /* This is lf_mfunction in binutils and lfMFunc in Microsoft's 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; + */ + + 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 (4, false), asm_out_file); + fprint_whex (asm_out_file, t->lf_mfunction.return_type); + putc ('\n', asm_out_file); + + fputs (integer_asm_op (4, false), asm_out_file); + fprint_whex (asm_out_file, t->lf_mfunction.containing_class_type); + putc ('\n', asm_out_file); + + fputs (integer_asm_op (4, false), asm_out_file); + fprint_whex (asm_out_file, t->lf_mfunction.this_type); + putc ('\n', asm_out_file); + + fputs (integer_asm_op (1, false), asm_out_file); + fprint_whex (asm_out_file, t->lf_mfunction.calling_convention); + putc ('\n', asm_out_file); + + fputs (integer_asm_op (1, false), asm_out_file); + fprint_whex (asm_out_file, t->lf_mfunction.attributes); + putc ('\n', asm_out_file); + + fputs (integer_asm_op (2, false), asm_out_file); + fprint_whex (asm_out_file, t->lf_mfunction.num_parameters); + putc ('\n', asm_out_file); + + fputs (integer_asm_op (4, false), asm_out_file); + fprint_whex (asm_out_file, t->lf_mfunction.arglist); + putc ('\n', asm_out_file); + + fputs (integer_asm_op (4, false), asm_out_file); + fprint_whex (asm_out_file, t->lf_mfunction.this_adjustment); + putc ('\n', asm_out_file); + + asm_fprintf (asm_out_file, "%LLcv_type%x_end:\n", t->num); +} + +/* Write an LF_METHODLIST type, which is an array of type numbers for + LF_MFUNCTION types. Overloaded functions are represented by a LF_METHOD + subtype in the field list, which points to a LF_METHODLIST type for the + function's various forms. */ + +static void +write_lf_methodlist (codeview_custom_type *t) +{ + /* This is lf_methodlist in binutils and lMethodList in Microsoft's cvinfo.h: + + struct lf_methodlist_entry + { + uint16_t method_attribute; + uint16_t padding; + uint32_t method_type; + } ATTRIBUTE_PACKED; + + struct lf_methodlist + { + uint16_t size; + uint16_t kind; + struct lf_methodlist_entry entries[]; + } 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); + + for (unsigned int i = 0; i < t->lf_methodlist.count; i++) + { + fputs (integer_asm_op (2, false), asm_out_file); + fprint_whex (asm_out_file, t->lf_methodlist.entries[i].method_attribute); + putc ('\n', asm_out_file); + + fputs (integer_asm_op (2, false), asm_out_file); + fprint_whex (asm_out_file, 0); + putc ('\n', asm_out_file); + + fputs (integer_asm_op (4, false), asm_out_file); + fprint_whex (asm_out_file, t->lf_methodlist.entries[i].method_type); + putc ('\n', asm_out_file); + } + + free (t->lf_methodlist.entries); + + 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. */ @@ -4263,6 +4527,14 @@ write_custom_types (void) write_lf_string_id (custom_types); break; + case LF_MFUNCTION: + write_lf_mfunction (custom_types); + break; + + case LF_METHODLIST: + write_lf_methodlist (custom_types); + break; + default: break; } @@ -5057,6 +5329,123 @@ add_struct_static_member (dw_die_ref c, uint16_t accessibility, *el_len += 4 - (*el_len % 4); } +/* Create a field list subtype for a struct function, returning its pointer in + el and its size in el_len. If the function is not overloaded, create an + LF_ONEMETHOD subtype pointing to the LF_MFUNCTION. Otherwise, add an + LF_METHODLIST type of the function's forms, and create an LF_METHOD subtype + pointing to this. */ + +static void +add_struct_function (dw_die_ref c, hash_table<method_hasher> *method_htab, + codeview_subtype **el, size_t *el_len) +{ + const char *name = get_AT_string (c, DW_AT_name); + codeview_method **slot, *meth; + + slot = method_htab->find_slot_with_hash (name, htab_hash_string (name), + NO_INSERT); + if (!slot) + return; + + meth = *slot; + + *el = (codeview_subtype *) xmalloc (sizeof (**el)); + (*el)->next = NULL; + + if (meth->count == 1) + { + (*el)->kind = LF_ONEMETHOD; + (*el)->lf_onemethod.method_attribute = meth->attribute; + (*el)->lf_onemethod.method_type = meth->type; + (*el)->lf_onemethod.name = xstrdup (name); + + *el_len = 9 + strlen ((*el)->lf_onemethod.name); + } + else + { + codeview_custom_type *ct; + lf_methodlist_entry *ent; + + ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type)); + + ct->next = NULL; + ct->kind = LF_METHODLIST; + ct->lf_methodlist.count = meth->count; + ct->lf_methodlist.entries = (lf_methodlist_entry *) + xmalloc (meth->count * sizeof (lf_methodlist_entry)); + + ent = ct->lf_methodlist.entries; + for (codeview_method *m = meth; m; m = m->next) + { + ent->method_attribute = m->attribute; + ent->method_type = m->type; + ent++; + } + + add_custom_type (ct); + + (*el)->kind = LF_METHOD; + (*el)->lf_method.count = meth->count; + (*el)->lf_method.method_list = ct->num; + (*el)->lf_method.name = xstrdup (name); + + *el_len = 9 + strlen ((*el)->lf_method.name); + } + + if (*el_len % 4) + *el_len += 4 - (*el_len % 4); + + method_htab->remove_elt_with_hash (name, htab_hash_string (name)); + + while (meth) + { + codeview_method *next = meth->next; + + free (meth->name); + free (meth); + meth = next; + } +} + +/* Create a new LF_MFUNCTION type for a struct function, add it to the + types_htab hash table, and return its type number. */ + +static uint32_t +get_mfunction_type (dw_die_ref c) +{ + uint32_t containing_class_type, this_type, mfunction_type; + dw_die_ref obj_pointer; + codeview_type **slot, *t; + + containing_class_type = get_type_num (dw_get_die_parent (c), true, false); + + obj_pointer = get_AT_ref (c, DW_AT_object_pointer); + if (obj_pointer && dw_get_die_tag (obj_pointer) == DW_TAG_formal_parameter) + { + this_type = get_type_num (get_AT_ref (obj_pointer, DW_AT_type), + true, false); + } + else + { + this_type = 0; + } + + mfunction_type = get_type_num_subroutine_type (c, true, containing_class_type, + this_type, 0); + + slot = types_htab->find_slot_with_hash (c, htab_hash_pointer (c), INSERT); + + t = (codeview_type *) xmalloc (sizeof (codeview_type)); + + t->die = c; + t->num = mfunction_type; + t->is_fwd_ref = false; + + *slot = t; + + return mfunction_type; +} + /* Translate a DWARF DW_AT_accessibility constant into its CodeView equivalent. If implicit, follow the C++ rules. */ @@ -5084,6 +5473,30 @@ get_accessibility (dw_die_ref c) } } +/* Returns true if the struct function pointed to by die is an instantiated + template function. These are skipped in CodeView struct definitions, as + otherwise the same type might not be deduplicated across different TUs. */ + +static bool +is_templated_func (dw_die_ref die) +{ + dw_die_ref c = dw_get_die_child (die); + + if (!c) + return false; + + do + { + c = dw_get_die_sib (c); + + if (dw_get_die_tag (c) == DW_TAG_template_type_param) + return true; + } + while (c != dw_get_die_child (die)); + + return false; +} + /* Process a DW_TAG_structure_type, DW_TAG_class_type, or DW_TAG_union_type DIE, add an LF_FIELDLIST and an LF_STRUCTURE / LF_CLASS / LF_UNION type, and return the number of the latter. */ @@ -5122,8 +5535,69 @@ get_type_num_struct (dw_die_ref type, bool in_struct, bool *is_fwd_ref) if (first_child) { + hash_table<method_hasher> *method_htab = NULL; dw_die_ref c; + /* First, loop through and record any non-templated member functions. + This is because overloaded and non-overloaded functions are expressed + differently in CodeView, so we need to have a hash table on the name + to know how to record it later on. */ + + c = first_child; + do + { + c = dw_get_die_sib (c); + + if (dw_get_die_tag (c) == DW_TAG_subprogram) + { + const char *name = get_AT_string (c, DW_AT_name); + codeview_method *meth, **slot; + + if (is_templated_func (c)) + continue; + + if (!method_htab) + method_htab = new hash_table<method_hasher> (10); + + meth = (codeview_method *) xmalloc (sizeof (*meth)); + + slot = method_htab->find_slot_with_hash (name, + htab_hash_string (name), + INSERT); + + meth->attribute = get_accessibility (c); + + if (!get_AT_ref (c, DW_AT_object_pointer)) + meth->attribute |= CV_METHOD_STATIC; + + meth->type = get_mfunction_type (c); + meth->next = NULL; + + if (*slot) + { + if ((*slot)->last) + (*slot)->last->next = meth; + else + (*slot)->next = meth; + + (*slot)->last = meth; + (*slot)->count++; + + meth->name = NULL; + } + else + { + meth->name = xstrdup (name); + meth->last = NULL; + meth->count = 1; + *slot = meth; + } + } + } + while (c != first_child); + + /* Now loop through again and record the actual members. */ + c = first_child; do { @@ -5145,6 +5619,11 @@ get_type_num_struct (dw_die_ref type, bool in_struct, bool *is_fwd_ref) add_struct_static_member (c, accessibility, &el, &el_len); break; + case DW_TAG_subprogram: + if (!is_templated_func (c)) + add_struct_function (c, method_htab, &el, &el_len); + break; + default: break; } @@ -5191,6 +5670,9 @@ get_type_num_struct (dw_die_ref type, bool in_struct, bool *is_fwd_ref) num_members++; } while (c != first_child); + + if (method_htab) + delete method_htab; } while (ct) @@ -5252,10 +5734,13 @@ get_type_num_struct (dw_die_ref type, bool in_struct, bool *is_fwd_ref) } /* Process a DW_TAG_subroutine_type DIE, adding an LF_ARGLIST and an - LF_PROCEDURE type, and returning the number of the latter. */ + LF_PROCEDURE or LF_MFUNCTION type, and returning the number of the + latter. */ static uint32_t -get_type_num_subroutine_type (dw_die_ref type, bool in_struct) +get_type_num_subroutine_type (dw_die_ref type, bool in_struct, + uint32_t containing_class_type, + uint32_t this_type, int32_t this_adjustment) { codeview_custom_type *ct; uint32_t return_type, arglist_type; @@ -5294,6 +5779,10 @@ get_type_num_subroutine_type (dw_die_ref type, bool in_struct) && dw_get_die_tag (c) != DW_TAG_unspecified_parameters) continue; + /* We ignore "this" params here. */ + if (get_AT_flag (c, DW_AT_artificial) != 0) + continue; + num_args++; } while (c != first_child); @@ -5323,6 +5812,9 @@ get_type_num_subroutine_type (dw_die_ref type, bool in_struct) { c = dw_get_die_sib (c); + if (get_AT_flag (c, DW_AT_artificial) != 0) + continue; + switch (dw_get_die_tag (c)) { case DW_TAG_formal_parameter: @@ -5351,17 +5843,33 @@ get_type_num_subroutine_type (dw_die_ref type, bool in_struct) arglist_type = ct->num; - /* Finally, create an LF_PROCEDURE. */ + /* Finally, create an LF_PROCEDURE or LF_MFUNCTION type. */ ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type)); ct->next = NULL; - ct->kind = LF_PROCEDURE; - ct->lf_procedure.return_type = return_type; - ct->lf_procedure.calling_convention = 0; - ct->lf_procedure.attributes = 0; - ct->lf_procedure.num_parameters = num_args; - ct->lf_procedure.arglist = arglist_type; + + if (containing_class_type != 0) + { + ct->kind = LF_MFUNCTION; + ct->lf_mfunction.return_type = return_type; + ct->lf_mfunction.containing_class_type = containing_class_type; + ct->lf_mfunction.this_type = this_type; + ct->lf_mfunction.calling_convention = 0; + ct->lf_mfunction.attributes = 0; + ct->lf_mfunction.num_parameters = num_args; + ct->lf_mfunction.arglist = arglist_type; + ct->lf_mfunction.this_adjustment = this_adjustment; + } + else + { + ct->kind = LF_PROCEDURE; + ct->lf_procedure.return_type = return_type; + ct->lf_procedure.calling_convention = 0; + ct->lf_procedure.attributes = 0; + ct->lf_procedure.num_parameters = num_args; + ct->lf_procedure.arglist = arglist_type; + } add_custom_type (ct); @@ -5562,7 +6070,7 @@ get_type_num (dw_die_ref type, bool in_struct, bool no_fwd_ref) break; case DW_TAG_subroutine_type: - num = get_type_num_subroutine_type (type, in_struct); + num = get_type_num_subroutine_type (type, in_struct, 0, 0, 0); break; default: @@ -5700,7 +6208,7 @@ add_function (dw_die_ref die) /* Add an LF_FUNC_ID type for this function. */ - function_type = get_type_num_subroutine_type (die, false); + function_type = get_type_num_subroutine_type (die, false, 0, 0, 0); scope_type = get_scope_string_id (die); ct = (codeview_custom_type *) xmalloc (sizeof (codeview_custom_type)); diff --git a/gcc/dwarf2codeview.h b/gcc/dwarf2codeview.h index 8ede1b295294..c631349640f8 100644 --- a/gcc/dwarf2codeview.h +++ b/gcc/dwarf2codeview.h @@ -66,6 +66,15 @@ along with GCC; see the file COPYING3. If not see #define CV_ACCESS_PROTECTED 2 #define CV_ACCESS_PUBLIC 3 +/* CV_methodprop_e values in cvinfo.h, shifted by 2 for CV_fldattr_t. */ +#define CV_METHOD_VANILLA 0x00 +#define CV_METHOD_VIRTUAL 0x04 +#define CV_METHOD_STATIC 0x08 +#define CV_METHOD_FRIEND 0x0c +#define CV_METHOD_INTRO 0x10 +#define CV_METHOD_PUREVIRT 0x14 +#define CV_METHOD_PUREINTRO 0x18 + #define CV_PROP_FWDREF 0x80 /* Debug Format Interface. Used in dwarf2out.cc. */