From: Keith Seitz Date: Fri, 19 Sep 2025 16:50:46 +0000 (-0700) Subject: Correct bounds check when working around GAS DWARF 5 directory table bug X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=31cb4bb676dde0db3f24e03380a02ae92633c98f;p=thirdparty%2Fbinutils-gdb.git Correct bounds check when working around GAS DWARF 5 directory table bug Recent Go toolchains are causing GDB to crash on a relatively recent workaround for a GAS bug: commit a833790a626d9620319d0ca6aee23daa584d445c Date: Wed Nov 1 00:33:12 2023 +0100 [gdb/symtab] Work around gas PR28629 In the original GAS bug, the first directory table entry did not contain the current directory of the compilation. So the above commit added a workaround fix to prepend the second directory table entry. However recent Go toolchain compilations (specifically on aarch64) only output a single directory table entry. Looking at the workaround: if (lh->version == 5 && lh->is_valid_file_index (1)) { std::string dir = lh->include_dir_at (1); fnd.set_comp_dir (std::move (dir)); } `lh->is_valid_file_index (1)' is true, but since the directory table only has one entry, `include_dir_at (1)' returns nullptr. Consequently the std::string ctor will segfault. Since there are no guarantees that the file and directory tables are the same size, a better bounds check is to simply rely on `include_dir_at' to ensure a valid directory table entry. I have updated the workaround commit's test, gdb.dwarf2/dw2-gas-workaround.exp and tested on x86_64 and aarch64 RHEL 9 and Fedora 41. Approved-By: Andrew Burgess --- diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 7cf89d8f80e..bc8b0883ed2 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -6124,7 +6124,7 @@ read_file_scope (struct die_info *die, struct dwarf2_cu *cu) sect_offset line_offset = (sect_offset) attr->as_unsigned (); line_header_up lh = dwarf_decode_line_header (line_offset, cu, fnd.get_comp_dir ()); - if (lh->version == 5 && lh->is_valid_file_index (1)) + if (lh->version == 5 && lh->include_dir_at (1) != nullptr) { std::string dir = lh->include_dir_at (1); fnd.set_comp_dir (std::move (dir)); diff --git a/gdb/testsuite/gdb.dwarf2/dw2-gas-workaround.exp b/gdb/testsuite/gdb.dwarf2/dw2-gas-workaround.exp index 2c8213e0ec3..6611bf31828 100644 --- a/gdb/testsuite/gdb.dwarf2/dw2-gas-workaround.exp +++ b/gdb/testsuite/gdb.dwarf2/dw2-gas-workaround.exp @@ -20,7 +20,7 @@ load_lib dwarf.exp # This test can only be run on targets which support DWARF-2 and use gas. require dwarf2_support -standard_testfile dw2-lines.c -dw2.S +standard_testfile dw2-lines.c -dw2.S -dw2-one-diridx.S with_shared_gdb { set func_info_vars [get_func_info bar] @@ -33,49 +33,61 @@ proc line_for { l } { return [expr $line + 1] } -set asm_file [standard_output_file $srcfile2] -Dwarf::assemble $asm_file { - declare_labels Llines - global srcdir subdir srcfile objdir - global func_info_vars - foreach var $func_info_vars { - global $var - } +# A helper proc to create the DWARF assembly for the test. +# If ONE_DIRIDX is true, then the directory table will be limited +# to one entry. +proc create_dwarf_assembly {source_file one_diridx} { + set asm_file [standard_output_file $source_file] + Dwarf::assemble $asm_file { + declare_labels Llines + global srcdir subdir srcfile objdir + global func_info_vars + upvar one_diridx one_diridx + foreach var $func_info_vars { + global $var + } - cu { version 5 } { - compile_unit { - DW_AT_language @DW_LANG_Mips_Assembler - DW_AT_name $srcfile - DW_AT_comp_dir $objdir - DW_AT_stmt_list $Llines DW_FORM_sec_offset - DW_AT_producer "GNU AS 2.35.2" - } { - subprogram { - DW_AT_external 1 flag - DW_AT_name bar - DW_AT_low_pc $bar_start addr - DW_AT_high_pc "$bar_start + $bar_len" addr + cu { version 5 } { + compile_unit { + DW_AT_language @DW_LANG_Mips_Assembler + DW_AT_name $srcfile + DW_AT_comp_dir $objdir + DW_AT_stmt_list $Llines DW_FORM_sec_offset + DW_AT_producer "GNU AS 2.35.2" + } { + subprogram { + DW_AT_external 1 flag + DW_AT_name bar + DW_AT_low_pc $bar_start addr + DW_AT_high_pc "$bar_start + $bar_len" addr + } } } - } - lines [list version 5] Llines { - set diridx1 [include_dir "${srcdir}/${subdir}"] - set diridx2 [include_dir "${srcdir}/${subdir}"] - file_name "$srcfile" $diridx1 - file_name "$srcfile" $diridx2 - - program { - DW_LNE_set_address bar_label - line [line_for bar_label] - DW_LNS_copy + lines [list version 5] Llines { + set diridx1 [include_dir "${srcdir}/${subdir}"] + file_name "$srcfile" $diridx1 + if {!$one_diridx} { + set diridx2 [include_dir "${srcdir}/${subdir}"] + file_name "$srcfile" $diridx2 + } else { + file_name "$srcfile" $diridx1 + } + program { + DW_LNE_set_address bar_label + line [line_for bar_label] + DW_LNS_copy - DW_LNE_set_address $bar_end - DW_LNE_end_sequence + DW_LNE_set_address $bar_end + DW_LNE_end_sequence + } } } + + return $asm_file } +set asm_file [create_dwarf_assembly $srcfile2 false] if { [prepare_for_testing "failed to prepare" ${testfile} \ [list $srcfile $asm_file] {nodebug}] } { return -1 @@ -90,3 +102,13 @@ gdb_test_multiple "ptype bar" "" { pass $gdb_test_name } } + +# Test whether gdb crashes in the case where the number of +# directory indexes is only one. +set asm_file [create_dwarf_assembly $srcfile3 true] +if {[prepare_for_testing "failed to prepare" ${testfile}-one-diridx \ + [list $srcfile $asm_file] {nodebug}] } { + return -1 +} + +gdb_test "ptype bar" ".*" "do not crash with only one directory table entry"