https://gcc.gnu.org/g:75fe4e2932136c3814e7ba9a2620d395d8f18133
commit r15-5846-g75fe4e2932136c3814e7ba9a2620d395d8f18133 Author: Mark Harmstone <m...@harmstone.com> Date: Thu Nov 7 03:59:18 2024 +0000 Write binary annotations for CodeView S_INLINESITE symbols Add "binary annotations" at the end of CodeView S_INLINESITE symbols, which are a series of compressed integers that represent how line numbers map to addresses. This requires assembler support; you will need commit b3aa594d ("gas: add .cv_ucomp and .cv_scomp pseudo-directives") in binutils. gcc/ * configure.ac (HAVE_GAS_CV_UCOMP): New check. * configure: Regenerate. * config.in: Regenerate. * dwarf2codeview.cc (enum binary_annotation_opcode): Define. (struct codeview_function): Add htab_next and inline_loc; (struct cv_func_hasher): Define. (cv_func_htab): New global variable. (new_codeview_function): Add new codeview_function to hash table. (codeview_begin_block): Record location of inline block. (codeview_end_block): Add dummy source line at end of inline block. (find_line_function): New function. (write_binary_annotations): New function. (write_s_inlinesite): Call write_binary_annotations. (codeview_debug_finish): Delete cv_func_htab. Diff: --- gcc/config.in | 6 ++ gcc/configure | 32 ++++++++ gcc/configure.ac | 3 + gcc/dwarf2codeview.cc | 203 +++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 240 insertions(+), 4 deletions(-) diff --git a/gcc/config.in b/gcc/config.in index 972c0c2034dc..d8145a1453b4 100644 --- a/gcc/config.in +++ b/gcc/config.in @@ -1459,6 +1459,12 @@ /* Define 0/1 if your assembler supports .cfi_sections. */ #undef HAVE_GAS_CFI_SECTIONS_DIRECTIVE +/* Define if your assembler supports .cv_ucomp. */ +#ifndef USED_FOR_TARGET +#undef HAVE_GAS_CV_UCOMP +#endif + + /* Define if your assembler supports the .loc discriminator sub-directive. */ #ifndef USED_FOR_TARGET #undef HAVE_GAS_DISCRIMINATOR diff --git a/gcc/configure b/gcc/configure index 8bb71cfe3485..bab4181a940f 100755 --- a/gcc/configure +++ b/gcc/configure @@ -25943,6 +25943,38 @@ $as_echo "#define HAVE_GAS_BASE64 1" >>confdefs.h fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for .cv_ucomp" >&5 +$as_echo_n "checking assembler for .cv_ucomp... " >&6; } +if ${gcc_cv_as_cv_ucomp+:} false; then : + $as_echo_n "(cached) " >&6 +else + gcc_cv_as_cv_ucomp=no + if test x$gcc_cv_as != x; then + $as_echo '.cv_ucomp 0' > conftest.s + if { ac_try='$gcc_cv_as $gcc_cv_as_flags -o conftest.o conftest.s >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } + then + gcc_cv_as_cv_ucomp=yes + else + echo "configure: failed program was" >&5 + cat conftest.s >&5 + fi + rm -f conftest.o conftest.s + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_as_cv_ucomp" >&5 +$as_echo "$gcc_cv_as_cv_ucomp" >&6; } +if test $gcc_cv_as_cv_ucomp = yes; then + +$as_echo "#define HAVE_GAS_CV_UCOMP 1" >>confdefs.h + +fi + + # gnu_indirect_function type is an extension proposed at # http://groups.google/com/group/generic-abi/files. It allows dynamic runtime # selection of function implementation diff --git a/gcc/configure.ac b/gcc/configure.ac index 88a1a44fcf75..b1b21cf4d7b2 100644 --- a/gcc/configure.ac +++ b/gcc/configure.ac @@ -3065,6 +3065,9 @@ gcc_GAS_CHECK_FEATURE([.base64], gcc_cv_as_base64,, .base64 "Tm9uIHB1ZG9yIGVzdCBuaWwgc2NpcmUsIHB1ZG9yIG5pbCBkaXNjZXJlIHZlbGxlLgo="],, [AC_DEFINE(HAVE_GAS_BASE64, 1, [Define if your assembler supports .base64.])]) +gcc_GAS_CHECK_FEATURE([.cv_ucomp], gcc_cv_as_cv_ucomp,,[.cv_ucomp 0],, +[AC_DEFINE(HAVE_GAS_CV_UCOMP, 1, [Define if your assembler supports .cv_ucomp.])]) + # gnu_indirect_function type is an extension proposed at # http://groups.google/com/group/generic-abi/files. It allows dynamic runtime # selection of function implementation diff --git a/gcc/dwarf2codeview.cc b/gcc/dwarf2codeview.cc index a0809d893430..19ec58d096ee 100644 --- a/gcc/dwarf2codeview.cc +++ b/gcc/dwarf2codeview.cc @@ -1086,6 +1086,25 @@ enum cv_amd64_register { CV_AMD64_YMM15D3 = 687 }; +/* This is enum BinaryAnnotationOpcode in Microsoft's cvinfo.h. */ + +enum binary_annotation_opcode { + ba_op_invalid, + ba_op_code_offset, + ba_op_change_code_offset_base, + ba_op_change_code_offset, + ba_op_change_code_length, + ba_op_change_file, + ba_op_change_line_offset, + ba_op_change_line_end_delta, + ba_op_change_range_kind, + ba_op_change_column_start, + ba_op_change_column_end_delta, + ba_op_change_code_offset_and_line_offset, + ba_op_change_code_length_and_code_offset, + ba_op_change_column_end +}; + struct codeview_string { codeview_string *next; @@ -1157,11 +1176,13 @@ struct codeview_inlinee_lines struct codeview_function { codeview_function *next; + codeview_function *htab_next; function *func; unsigned int end_label; codeview_line_block *blocks, *last_block; codeview_function *parent; unsigned int inline_block; + location_t inline_loc; }; struct codeview_symbol @@ -1445,6 +1466,16 @@ struct inlinee_lines_hasher : free_ptr_hash <struct codeview_inlinee_lines> } }; +struct cv_func_hasher : nofree_ptr_hash <struct codeview_function> +{ + typedef dw_die_ref compare_type; + + static bool equal (const codeview_function *f, dw_die_ref die) + { + return lookup_decl_die (f->func->decl) == die; + } +}; + static unsigned int line_label_num; static unsigned int func_label_num; static unsigned int sym_label_num; @@ -1462,6 +1493,7 @@ static codeview_custom_type *custom_types, *last_custom_type; static codeview_deferred_type *deferred_types, *last_deferred_type; static hash_table<string_id_hasher> *string_id_htab; static hash_table<inlinee_lines_hasher> *inlinee_lines_htab; +static hash_table<cv_func_hasher> *cv_func_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, @@ -1511,14 +1543,18 @@ get_file_id (const char *filename) static codeview_function * new_codeview_function (void) { + codeview_function **slot; + dw_die_ref die; codeview_function *f = (codeview_function *) xmalloc (sizeof (codeview_function)); f->next = NULL; + f->htab_next = NULL; f->func = cfun; f->end_label = 0; f->blocks = f->last_block = NULL; f->inline_block = 0; + f->inline_loc = 0; if (!funcs) funcs = f; @@ -1527,6 +1563,18 @@ new_codeview_function (void) last_func = f; + if (!cv_func_htab) + cv_func_htab = new hash_table<cv_func_hasher> (10); + + die = lookup_decl_die (cfun->decl); + + slot = cv_func_htab->find_slot_with_hash (die, htab_hash_pointer (die), + INSERT); + if (*slot) + f->htab_next = *slot; + + *slot = f; + return f; } @@ -1605,6 +1653,7 @@ codeview_begin_block (unsigned int line ATTRIBUTE_UNUSED, f->parent = cur_func; f->inline_block = blocknum; + f->inline_loc = locus; cur_func = f; } @@ -1617,7 +1666,13 @@ void codeview_end_block (unsigned int line ATTRIBUTE_UNUSED, unsigned int blocknum) { if (cur_func && cur_func->inline_block == blocknum) - cur_func = cur_func->parent; + { + /* If inlined function, add dummy source line at the end so we know how + long the actual last line is. */ + codeview_source_line (0, ""); + + cur_func = cur_func->parent; + } } /* Adds string to the string table, returning its offset. If already present, @@ -3328,6 +3383,137 @@ write_optimized_static_local_vars (dw_die_ref die) while (c != first_child); } +#ifdef HAVE_GAS_CV_UCOMP + +/* Given a DW_TAG_inlined_subroutine DIE within parent_func, return a pointer + to the corresponding codeview_function, which is used to map addresses + to line numbers. */ + +static codeview_function * +find_line_function (dw_die_ref parent_func, dw_die_ref die) +{ + codeview_function **slot, *f; + + if (!cv_func_htab) + return NULL; + + slot = cv_func_htab->find_slot_with_hash (parent_func, + htab_hash_pointer (parent_func), + NO_INSERT); + + if (!slot || !*slot) + return NULL; + + f = *slot; + + while (f) + { + expanded_location loc; + dwarf_file_data *call_file; + + if (f->inline_block == 0) + { + f = f->htab_next; + continue; + } + + loc = expand_location (f->inline_loc); + + if ((unsigned) loc.line != get_AT_unsigned (die, DW_AT_call_line) + || (unsigned) loc.column != get_AT_unsigned (die, DW_AT_call_column)) + { + f = f->htab_next; + continue; + } + + call_file = get_AT_file (die, DW_AT_call_file); + + if (!call_file || strcmp (call_file->filename, loc.file)) + { + f = f->htab_next; + continue; + } + + return f; + } + + return NULL; +} + +/* Write the "binary annotations" for an S_INLINESITE symbol, which are how + CodeView represents line numbers within inlined functions. This is + completely different to how line numbers are represented normally, and + requires assembler support for the .cv_ucomp and .cv_scomp pseudos. */ + +static void +write_binary_annotations (codeview_function *line_func, uint32_t func_id) +{ + codeview_line_block *b; + codeview_inlinee_lines **slot, *il; + codeview_function *top_parent; + unsigned int line_no, label_num; + + slot = inlinee_lines_htab->find_slot_with_hash (func_id, func_id, NO_INSERT); + if (!slot || !*slot) + return; + + il = *slot; + + line_no = il->starting_line; + + top_parent = line_func; + while (top_parent->parent) + { + top_parent = top_parent->parent; + } + + label_num = top_parent->blocks->lines->label_num; + + b = line_func->blocks; + while (b) + { + codeview_line *l = b->lines; + + while (l) + { + if (!l->next && !b->next) /* last line (end of block) */ + { + asm_fprintf (asm_out_file, "\t.cv_ucomp %u\n", + ba_op_change_code_length); + asm_fprintf (asm_out_file, + "\t.cv_ucomp %L" LINE_LABEL "%u - %L" LINE_LABEL "%u\n", + l->label_num, label_num); + } + else + { + if (l->line_no != line_no) + { + asm_fprintf (asm_out_file, "\t.cv_ucomp %u\n", + ba_op_change_line_offset); + asm_fprintf (asm_out_file, "\t.cv_scomp %i\n", + l->line_no - line_no); + + line_no = l->line_no; + } + + asm_fprintf (asm_out_file, "\t.cv_ucomp %u\n", + ba_op_change_code_offset); + asm_fprintf (asm_out_file, + "\t.cv_ucomp %L" LINE_LABEL "%u - %L" LINE_LABEL "%u\n", + l->label_num, label_num); + } + + label_num = l->label_num; + + l = l->next; + } + + b = b->next; + } +} + +#endif + /* Write an S_INLINESITE symbol, to record that a function has been inlined inside another function. */ @@ -3337,6 +3523,7 @@ write_s_inlinesite (dw_die_ref parent_func, dw_die_ref die) unsigned int label_num = ++sym_label_num; dw_die_ref func; uint32_t func_id; + codeview_function *line_func; func = get_AT_ref (die, DW_AT_abstract_origin); if (!func) @@ -3385,9 +3572,14 @@ write_s_inlinesite (dw_die_ref parent_func, dw_die_ref die) fprint_whex (asm_out_file, func_id); putc ('\n', asm_out_file); - /* FIXME: there now should follow some "binary annotations", recording how - offsets in the function map to line numbers in the inlined function, - but these require support in GAS. */ +#ifdef HAVE_GAS_CV_UCOMP + line_func = find_line_function (parent_func, die); + + if (line_func) + write_binary_annotations (line_func, func_id); +#else + (void) line_func; +#endif targetm.asm_out.internal_label (asm_out_file, SYMBOL_END_LABEL, label_num); @@ -5027,6 +5219,9 @@ codeview_debug_finish (void) if (inlinee_lines_htab) delete inlinee_lines_htab; + + if (cv_func_htab) + delete cv_func_htab; } /* Translate a DWARF base type (DW_TAG_base_type) into its CodeView