From 591cc9fbbfd6d51131c0f1d4a92e7893edcc7a28 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Thu, 7 Apr 2022 08:18:00 +0200 Subject: gas/Dwarf: record functions To help tools like addr2line looking up function names, in particular when dealing with e.g. PE/COFF binaries (linked from ELF objects), where there's no ELF symbol table to fall back to, emit minimalistic information for functions marked as such and having their size specified. Notes regarding the restriction to (pure) ELF: - I realize this is a layering violation; I don't see how to deal with that in a better way. - S_GET_SIZE(), when OBJ_MAYBE_ELF is defined, looks wrong: Unlike S_SET_SIZE() it does not check whether the hook is NULL. - symbol_get_obj(), when OBJ_MAYBE_ELF is defined, looks unusable, as its return type can only ever be one object format's type (and this may then not be ELF's). The new testcases are limited to x86 because I wanted to include the case where function size can't be determined yet at the time Dwarf2 info is generated. As .nops gains support by further targets, they could also be added here then (with, as necessary, expecations suitably relaxed to cover for insn size differences). --- gas/dwarf2dbg.c | 156 ++++++++++++++++++++++++++-- gas/testsuite/gas/elf/dwarf-3-func.d | 48 +++++++++ gas/testsuite/gas/elf/dwarf-3-func.s | 40 +++++++ gas/testsuite/gas/elf/dwarf-5-func-global.d | 40 +++++++ gas/testsuite/gas/elf/dwarf-5-func-local.d | 37 +++++++ gas/testsuite/gas/elf/dwarf-5-func.d | 50 +++++++++ gas/testsuite/gas/elf/elf.exp | 4 + 7 files changed, 368 insertions(+), 7 deletions(-) create mode 100644 gas/testsuite/gas/elf/dwarf-3-func.d create mode 100644 gas/testsuite/gas/elf/dwarf-3-func.s create mode 100644 gas/testsuite/gas/elf/dwarf-5-func-global.d create mode 100644 gas/testsuite/gas/elf/dwarf-5-func-local.d create mode 100644 gas/testsuite/gas/elf/dwarf-5-func.d (limited to 'gas') diff --git a/gas/dwarf2dbg.c b/gas/dwarf2dbg.c index fee56c2..44d7d9e 100644 --- a/gas/dwarf2dbg.c +++ b/gas/dwarf2dbg.c @@ -2674,14 +2674,55 @@ out_debug_aranges (segT aranges_seg, segT info_seg) static void out_debug_abbrev (segT abbrev_seg, segT info_seg ATTRIBUTE_UNUSED, - segT line_seg ATTRIBUTE_UNUSED) + segT line_seg ATTRIBUTE_UNUSED, + unsigned char *func_formP) { int secoff_form; + bool have_efunc = false, have_lfunc = false; + + /* Check the symbol table for function symbols which also have their size + specified. */ + if (symbol_rootP) + { + symbolS *symp; + + for (symp = symbol_rootP; symp; symp = symbol_next (symp)) + { + /* A warning construct is a warning symbol followed by the + symbol warned about. Skip this and the following symbol. */ + if (symbol_get_bfdsym (symp)->flags & BSF_WARNING) + { + symp = symbol_next (symp); + if (!symp) + break; + continue; + } + + if (!S_IS_DEFINED (symp) || !S_IS_FUNCTION (symp)) + continue; + +#if defined (OBJ_ELF) /* || defined (OBJ_MAYBE_ELF) */ + if (S_GET_SIZE (symp) == 0) + { + if (!IS_ELF || symbol_get_obj (symp)->size == NULL) + continue; + } +#else + continue; +#endif + + if (S_IS_EXTERNAL (symp)) + have_efunc = true; + else + have_lfunc = true; + } + } + subseg_set (abbrev_seg, 0); out_uleb128 (1); out_uleb128 (DW_TAG_compile_unit); - out_byte (DW_CHILDREN_no); + out_byte (have_efunc || have_lfunc ? DW_CHILDREN_yes : DW_CHILDREN_no); if (DWARF2_VERSION < 4) { if (DWARF2_FORMAT (line_seg) == dwarf2_format_32bit) @@ -2708,6 +2749,29 @@ out_debug_abbrev (segT abbrev_seg, out_abbrev (DW_AT_language, DW_FORM_data2); out_abbrev (0, 0); + if (have_efunc || have_lfunc) + { + out_uleb128 (2); + out_uleb128 (DW_TAG_subprogram); + out_byte (DW_CHILDREN_no); + out_abbrev (DW_AT_name, DW_FORM_strp); + if (have_efunc) + { + if (have_lfunc || DWARF2_VERSION < 4) + *func_formP = DW_FORM_flag; + else + *func_formP = DW_FORM_flag_present; + out_abbrev (DW_AT_external, *func_formP); + } + else + /* Any non-zero value other than DW_FORM_flag will do. */ + *func_formP = DW_FORM_block; + out_abbrev (DW_AT_low_pc, DW_FORM_addr); + out_abbrev (DW_AT_high_pc, + DWARF2_VERSION < 4 ? DW_FORM_addr : DW_FORM_udata); + out_abbrev (0, 0); + } + /* Terminate the abbreviations for this compilation unit. */ out_byte (0); } @@ -2715,9 +2779,10 @@ out_debug_abbrev (segT abbrev_seg, /* Emit a description of this compilation unit for .debug_info. */ static void -out_debug_info (segT info_seg, segT abbrev_seg, segT line_seg, +out_debug_info (segT info_seg, segT abbrev_seg, segT line_seg, segT str_seg, symbolS *ranges_sym, symbolS *name_sym, - symbolS *comp_dir_sym, symbolS *producer_sym) + symbolS *comp_dir_sym, symbolS *producer_sym, + unsigned char func_form) { expressionS exp; symbolS *info_end; @@ -2799,6 +2864,81 @@ out_debug_info (segT info_seg, segT abbrev_seg, segT line_seg, dwarf2 draft has no standard code for assembler. */ out_two (DW_LANG_Mips_Assembler); + if (func_form) + { + symbolS *symp; + + for (symp = symbol_rootP; symp; symp = symbol_next (symp)) + { + const char *name; + size_t len; + + /* Skip warning constructs (see above). */ + if (symbol_get_bfdsym (symp)->flags & BSF_WARNING) + { + symp = symbol_next (symp); + if (!symp) + break; + continue; + } + + if (!S_IS_DEFINED (symp) || !S_IS_FUNCTION (symp)) + continue; + + subseg_set (str_seg, 0); + name_sym = symbol_temp_new_now_octets (); + name = S_GET_NAME (symp); + len = strlen (name) + 1; + memcpy (frag_more (len), name, len); + + subseg_set (info_seg, 0); + + /* DW_TAG_subprogram DIE abbrev */ + out_uleb128 (2); + + /* DW_AT_name */ + TC_DWARF2_EMIT_OFFSET (name_sym, sizeof_offset); + + /* DW_AT_external. */ + if (func_form == DW_FORM_flag) + out_byte (S_IS_EXTERNAL (symp)); + + /* DW_AT_low_pc */ + exp.X_op = O_symbol; + exp.X_add_symbol = symp; + exp.X_add_number = 0; + emit_expr (&exp, sizeof_address); + + /* DW_AT_high_pc */ + exp.X_op = O_constant; +#if defined (OBJ_ELF) /* || defined (OBJ_MAYBE_ELF) */ + exp.X_add_number = S_GET_SIZE (symp); + if (exp.X_add_number == 0 && IS_ELF + && symbol_get_obj (symp)->size != NULL) + { + exp.X_op = O_add; + exp.X_op_symbol = make_expr_symbol (symbol_get_obj (symp)->size); + } +#else + exp.X_add_number = 0; +#endif + if (DWARF2_VERSION < 4) + { + if (exp.X_op == O_constant) + exp.X_op = O_symbol; + exp.X_add_symbol = symp; + emit_expr (&exp, sizeof_address); + } + else if (exp.X_op == O_constant) + out_uleb128 (exp.X_add_number); + else + emit_leb128_expr (symbol_get_value_expression (exp.X_op_symbol), 0); + } + + /* End of children. */ + out_leb128 (0); + } + symbol_set_value_now (info_end); } @@ -2968,6 +3108,7 @@ dwarf2_finish (void) segT aranges_seg; segT str_seg; symbolS *name_sym, *comp_dir_sym, *producer_sym, *ranges_sym; + unsigned char func_form = 0; gas_assert (all_segs); @@ -3013,10 +3154,11 @@ dwarf2_finish (void) } out_debug_aranges (aranges_seg, info_seg); - out_debug_abbrev (abbrev_seg, info_seg, line_seg); + out_debug_abbrev (abbrev_seg, info_seg, line_seg, &func_form); out_debug_str (str_seg, &name_sym, &comp_dir_sym, &producer_sym); - out_debug_info (info_seg, abbrev_seg, line_seg, ranges_sym, - name_sym, comp_dir_sym, producer_sym); + out_debug_info (info_seg, abbrev_seg, line_seg, str_seg, + ranges_sym, name_sym, comp_dir_sym, producer_sym, + func_form); } } diff --git a/gas/testsuite/gas/elf/dwarf-3-func.d b/gas/testsuite/gas/elf/dwarf-3-func.d new file mode 100644 index 0000000..0196f69 --- /dev/null +++ b/gas/testsuite/gas/elf/dwarf-3-func.d @@ -0,0 +1,48 @@ +#as: --gdwarf-3 +#name: Dwarf3 function debug info +#readelf: -W -wai +#target: i?86-*-* x86_64-*-* + +Contents of the .debug_info section: + + +Compilation Unit @ offset (0x)?0: + +Length: .* + +Version: +3 + +Abbrev Offset: +(0x)?0 + +Pointer Size: .* + <0><[0-9a-f]+>: Abbrev Number: 1 \(DW_TAG_compile_unit\) +#... + <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\) + +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): efunc1 + +<[0-9a-f]+> +DW_AT_external +: \(flag\) 1 + +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?0 + +<[0-9a-f]+> +DW_AT_high_pc +: \(addr\) (0x)?2 + <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\) + +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): lfunc1 + +<[0-9a-f]+> +DW_AT_external +: \(flag\) 0 + +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?2 + +<[0-9a-f]+> +DW_AT_high_pc +: \(addr\) (0x)?13 + <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\) + +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): efunc2 + +<[0-9a-f]+> +DW_AT_external +: \(flag\) 1 + +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?13 + +<[0-9a-f]+> +DW_AT_high_pc +: \(addr\) (0x)?35 + <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\) + +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): lfunc2 + +<[0-9a-f]+> +DW_AT_external +: \(flag\) 0 + +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?35 + +<[0-9a-f]+> +DW_AT_high_pc +: \(addr\) (0x)?38 + <1><[0-9a-f]+>: Abbrev Number: 0 + +Contents of the .debug_abbrev section: + + +Number TAG \(0x0\) + +1 +DW_TAG_compile_unit +\[has children\] +#... + +2 +DW_TAG_subprogram +\[no children\] + +DW_AT_name +DW_FORM_strp + +DW_AT_external +DW_FORM_flag + +DW_AT_low_pc +DW_FORM_addr + +DW_AT_high_pc +DW_FORM_addr + +DW_AT value: 0 +DW_FORM value: 0 +#pass diff --git a/gas/testsuite/gas/elf/dwarf-3-func.s b/gas/testsuite/gas/elf/dwarf-3-func.s new file mode 100644 index 0000000..4610686 --- /dev/null +++ b/gas/testsuite/gas/elf/dwarf-3-func.s @@ -0,0 +1,40 @@ + .text + + .ifndef LOCAL +efunc1: + .nop + .nop + .global efunc1 + .type efunc1, %function + .size efunc1, .-efunc1 + .endif + + .ifndef GLOBAL +lfunc1: + .nops 16 + .nop + .type lfunc1, %function + .size lfunc1, .-lfunc1 + .endif + + .ifndef LOCAL +efunc2: + .nop + .nops 32 + .nop + .global efunc2 + .type efunc2, %function + .size efunc2, .-efunc2 + .endif + + .global efunc3 + .type efunc3, %function + + .ifndef GLOBAL +lfunc2: + .nop + .nop + .nop + .type lfunc2, %function + .size lfunc2, .-lfunc2 + .endif diff --git a/gas/testsuite/gas/elf/dwarf-5-func-global.d b/gas/testsuite/gas/elf/dwarf-5-func-global.d new file mode 100644 index 0000000..09b10fd --- /dev/null +++ b/gas/testsuite/gas/elf/dwarf-5-func-global.d @@ -0,0 +1,40 @@ +#as: --gdwarf-5 --defsym GLOBAL=1 +#name: Dwarf5 global function debug info +#readelf: -W -wai +#source: dwarf-3-func.s +#target: i?86-*-* x86_64-*-* + +Contents of the .debug_info section: + + +Compilation Unit @ offset (0x)?0: + +Length: .* + +Version: +5 + +Unit Type: +DW_UT_compile \(1\) + +Abbrev Offset: +(0x)?0 + +Pointer Size: .* + <0><[0-9a-f]+>: Abbrev Number: 1 \(DW_TAG_compile_unit\) +#... + <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\) + +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): efunc1 + +<[0-9a-f]+> +DW_AT_external +: \(flag_present\) 1 + +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?0 + +<[0-9a-f]+> +DW_AT_high_pc +: \(udata\) 2 + <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\) + +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): efunc2 + +<[0-9a-f]+> +DW_AT_external +: \(flag_present\) 1 + +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?2 + +<[0-9a-f]+> +DW_AT_high_pc +: \(udata\) 34 + <1><[0-9a-f]+>: Abbrev Number: 0 + +Contents of the .debug_abbrev section: + + +Number TAG \(0x0\) + +1 +DW_TAG_compile_unit +\[has children\] +#... + +2 +DW_TAG_subprogram +\[no children\] + +DW_AT_name +DW_FORM_strp + +DW_AT_external +DW_FORM_flag_present + +DW_AT_low_pc +DW_FORM_addr + +DW_AT_high_pc +DW_FORM_udata + +DW_AT value: 0 +DW_FORM value: 0 +#pass diff --git a/gas/testsuite/gas/elf/dwarf-5-func-local.d b/gas/testsuite/gas/elf/dwarf-5-func-local.d new file mode 100644 index 0000000..fd97841 --- /dev/null +++ b/gas/testsuite/gas/elf/dwarf-5-func-local.d @@ -0,0 +1,37 @@ +#as: --gdwarf-5 --defsym LOCAL=1 +#name: Dwarf5 local function debug info +#readelf: -W -wai +#source: dwarf-3-func.s +#target: i?86-*-* x86_64-*-* + +Contents of the .debug_info section: + + +Compilation Unit @ offset (0x)?0: + +Length: .* + +Version: +5 + +Unit Type: +DW_UT_compile \(1\) + +Abbrev Offset: +(0x)?0 + +Pointer Size: .* + <0><[0-9a-f]+>: Abbrev Number: 1 \(DW_TAG_compile_unit\) +#... + <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\) + +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): lfunc1 + +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?0 + +<[0-9a-f]+> +DW_AT_high_pc +: \(udata\) 17 + <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\) + +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): lfunc2 + +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?11 + +<[0-9a-f]+> +DW_AT_high_pc +: \(udata\) 3 + <1><[0-9a-f]+>: Abbrev Number: 0 + +Contents of the .debug_abbrev section: + + +Number TAG \(0x0\) + +1 +DW_TAG_compile_unit +\[has children\] +#... + +2 +DW_TAG_subprogram +\[no children\] + +DW_AT_name +DW_FORM_strp + +DW_AT_low_pc +DW_FORM_addr + +DW_AT_high_pc +DW_FORM_udata + +DW_AT value: 0 +DW_FORM value: 0 +#pass diff --git a/gas/testsuite/gas/elf/dwarf-5-func.d b/gas/testsuite/gas/elf/dwarf-5-func.d new file mode 100644 index 0000000..11a78b2 --- /dev/null +++ b/gas/testsuite/gas/elf/dwarf-5-func.d @@ -0,0 +1,50 @@ +#as: --gdwarf-5 +#name: Dwarf5 function debug info +#readelf: -W -wai +#source: dwarf-3-func.s +#target: i?86-*-* x86_64-*-* + +Contents of the .debug_info section: + + +Compilation Unit @ offset (0x)?0: + +Length: .* + +Version: +5 + +Unit Type: +DW_UT_compile \(1\) + +Abbrev Offset: +(0x)?0 + +Pointer Size: .* + <0><[0-9a-f]+>: Abbrev Number: 1 \(DW_TAG_compile_unit\) +#... + <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\) + +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): efunc1 + +<[0-9a-f]+> +DW_AT_external +: \(flag\) 1 + +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?0 + +<[0-9a-f]+> +DW_AT_high_pc +: \(udata\) 2 + <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\) + +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): lfunc1 + +<[0-9a-f]+> +DW_AT_external +: \(flag\) 0 + +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?2 + +<[0-9a-f]+> +DW_AT_high_pc +: \(udata\) 17 + <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\) + +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): efunc2 + +<[0-9a-f]+> +DW_AT_external +: \(flag\) 1 + +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?13 + +<[0-9a-f]+> +DW_AT_high_pc +: \(udata\) 34 + <1><[0-9a-f]+>: Abbrev Number: 2 \(DW_TAG_subprogram\) + +<[0-9a-f]+> +DW_AT_name +: \(strp\) \(offset: (0x)?[0-9a-f]+\): lfunc2 + +<[0-9a-f]+> +DW_AT_external +: \(flag\) 0 + +<[0-9a-f]+> +DW_AT_low_pc +: \(addr\) (0x)?35 + +<[0-9a-f]+> +DW_AT_high_pc +: \(udata\) 3 + <1><[0-9a-f]+>: Abbrev Number: 0 + +Contents of the .debug_abbrev section: + + +Number TAG \(0x0\) + +1 +DW_TAG_compile_unit +\[has children\] +#... + +2 +DW_TAG_subprogram +\[no children\] + +DW_AT_name +DW_FORM_strp + +DW_AT_external +DW_FORM_flag + +DW_AT_low_pc +DW_FORM_addr + +DW_AT_high_pc +DW_FORM_udata + +DW_AT value: 0 +DW_FORM value: 0 +#pass diff --git a/gas/testsuite/gas/elf/elf.exp b/gas/testsuite/gas/elf/elf.exp index 963730a..abdc288 100644 --- a/gas/testsuite/gas/elf/elf.exp +++ b/gas/testsuite/gas/elf/elf.exp @@ -308,6 +308,10 @@ if { [is_elf_format] } then { run_dump_test "dwarf-5-cu" $dump_opts run_dump_test "dwarf-5-nop-for-line-table" $dump_opts run_dump_test "dwarf-5-irp" $dump_opts + run_dump_test "dwarf-3-func" $dump_opts + run_dump_test "dwarf-5-func" $dump_opts + run_dump_test "dwarf-5-func-global" $dump_opts + run_dump_test "dwarf-5-func-local" $dump_opts run_dump_test "pr25917" run_dump_test "bss" run_dump_test "bad-bss" -- cgit v1.1