From: Simon Marchi Date: Thu, 24 Apr 2025 17:36:28 +0000 (-0400) Subject: gdb/dwarf: read multiple .debug_info.dwo sections X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=28f15782adab4e8f984c065e0c793c716dd942f2;p=thirdparty%2Fbinutils-gdb.git gdb/dwarf: read multiple .debug_info.dwo sections When building with gcc, with flags -gdwarf-5, -gsplit-dwarf and -fdebug-types-section, the resulting .dwo files contain multiple .debug_info.dwo sections. One for each type unit and one for the compile unit. This is correct, as per DWARF 5, section F.2.3 ("Contents of the Split DWARF Object Files"): The split DWARF object files each contain the following sections: ... .debug_info.dwo (for the compilation unit) .debug_info.dwo (one COMDAT section for each type unit) ... GDB currently assumes that there is a single .debug_info.dwo section, causing unpredictable behavior. For example, sometimes this crash: ==81781==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x508000007a71 at pc 0x58704d32a59c bp 0x7ffc0acc0bb0 sp 0x7ffc0acc0ba0 READ of size 1 at 0x508000007a71 thread T0 #0 0x58704d32a59b in bfd_getl32 /home/smarchi/src/binutils-gdb/bfd/libbfd.c:846 #1 0x58704ae62dce in read_initial_length(bfd*, unsigned char const*, unsigned int*, bool) /home/smarchi/src/binutils-gdb/gdb/dwarf2/leb.c:92 #2 0x58704aaf76bf in read_comp_unit_head(comp_unit_head*, unsigned char const*, dwarf2_section_info*, rcuh_kind) /home/smarchi/src/binutils-gdb/gdb/dwarf2/comp-unit-head.c:47 #3 0x58704aaf8f97 in read_and_check_comp_unit_head(dwarf2_per_objfile*, comp_unit_head*, dwarf2_section_info*, dwarf2_section_info*, unsigned char const*, rcuh_kind) /home/smarchi/src/binutils-gdb/gdb/dwarf2/comp-unit-head.c:193 #4 0x58704b022908 in create_dwo_unit_hash_tables /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:6233 #5 0x58704b0334a5 in open_and_init_dwo_file /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:7588 #6 0x58704b03965a in lookup_dwo_cutu /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:7935 #7 0x58704b03a5b1 in lookup_dwo_comp_unit /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:8009 #8 0x58704aff5b70 in lookup_dwo_unit /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:2802 The first time that locate_dwo_sections gets called for a .debug_info.dwo section, dwo_sections::info gets initialized properly. The second time it gets called for a .debug_info.dwo section, the size field in dwo_sections::info gets overwritten with the size of the second section. But the buffer remains pointing to the contents of the first section, because the section is already "read in". So the size does not match the buffer. And even if it did, we would only keep the information about one .debug_info.dwo, out of the many. First, add an assert in locate_dwo_sections to make sure we don't try to fill in a dwo section info twice. Add the assert to other functions with the same pattern, while at it. Then, change dwo_sections::info to be a vector of sections (just like we do for type sections). Update locate_dwo_sections to append to that vector when seeing a new .debug_info.dwo section. Update open_and_init_dwo_file to read the units from each section. The problem can be observed by running some tests with the dwarf5-fission-debug-types target board. For example, gdb.base/condbreak.exp crashes (with the ASan failure shown above) before the patch and passes after). [1] https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119766 Change-Id: Iedf275768b6057dee4b1542396714f3d89903cf3 Reviewed-By: Tom de Vries --- diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index cedd0681582..e9cc542f8c9 100644 --- a/gdb/dwarf2/read.c +++ b/gdb/dwarf2/read.c @@ -280,7 +280,7 @@ struct dwo_sections struct dwarf2_section_info str; struct dwarf2_section_info str_offsets; /* In the case of a virtual DWO file, these two are unused. */ - struct dwarf2_section_info info; + std::vector infos; std::vector types; }; @@ -7523,7 +7523,7 @@ cutu_reader::locate_dwo_sections (struct objfile *objfile, bfd *abfd, if (names->abbrev_dwo.matches (sectp->name)) dw_sect = &dwo_sections->abbrev; else if (names->info_dwo.matches (sectp->name)) - dw_sect = &dwo_sections->info; + dw_sect = &dwo_sections->infos.emplace_back (dwarf2_section_info {}); else if (names->line_dwo.matches (sectp->name)) dw_sect = &dwo_sections->line; else if (names->loc_dwo.matches (sectp->name)) @@ -7541,16 +7541,14 @@ cutu_reader::locate_dwo_sections (struct objfile *objfile, bfd *abfd, else if (names->str_offsets_dwo.matches (sectp->name)) dw_sect = &dwo_sections->str_offsets; else if (names->types_dwo.matches (sectp->name)) - { - struct dwarf2_section_info type_section; - - memset (&type_section, 0, sizeof (type_section)); - dwo_sections->types.push_back (type_section); - dw_sect = &dwo_sections->types.back (); - } + dw_sect = &dwo_sections->types.emplace_back (dwarf2_section_info {}); if (dw_sect != nullptr) { + /* Make sure we don't overwrite a section info that has been filled in + already. */ + gdb_assert (!dw_sect->readin); + dw_sect->s.section = sectp; dw_sect->size = bfd_section_size (sectp); dw_sect->read (objfile); @@ -7585,8 +7583,13 @@ cutu_reader::open_and_init_dwo_file (dwarf2_cu *cu, const char *dwo_name, this->locate_dwo_sections (per_objfile->objfile, dwo_file->dbfd.get (), sec, &dwo_file->sections); - create_dwo_unit_hash_tables (*dwo_file, *cu, dwo_file->sections.info, - ruh_kind::COMPILE); + /* There is normally just one .debug_info.dwo section in a DWO file. But when + building with -fdebug-types-section, gcc produces multiple .debug_info.dwo + sections. One for each produced type unit and one for the compile unit. + This is not expected, but we can easily enough deal with what gcc + produces. This behavior has been observed with gcc 14.2.1. */ + for (dwarf2_section_info §ion : dwo_file->sections.infos) + create_dwo_unit_hash_tables (*dwo_file, *cu, section, ruh_kind::COMPILE); for (dwarf2_section_info §ion : dwo_file->sections.types) create_dwo_unit_hash_tables (*dwo_file, *cu, section, ruh_kind::TYPE); @@ -7625,6 +7628,10 @@ dwarf2_locate_common_dwp_sections (struct objfile *objfile, bfd *abfd, if (dw_sect != nullptr) { + /* Make sure we don't overwrite a section info that has been filled in + already. */ + gdb_assert (!dw_sect->readin); + dw_sect->s.section = sectp; dw_sect->size = bfd_section_size (sectp); dw_sect->read (objfile); @@ -7670,6 +7677,10 @@ dwarf2_locate_v2_dwp_sections (struct objfile *objfile, bfd *abfd, if (dw_sect != nullptr) { + /* Make sure we don't overwrite a section info that has been filled in + already. */ + gdb_assert (!dw_sect->readin); + dw_sect->s.section = sectp; dw_sect->size = bfd_section_size (sectp); dw_sect->read (objfile); @@ -7713,6 +7724,10 @@ dwarf2_locate_v5_dwp_sections (struct objfile *objfile, bfd *abfd, if (dw_sect != nullptr) { + /* Make sure we don't overwrite a section info that has been filled in + already. */ + gdb_assert (!dw_sect->readin); + dw_sect->s.section = sectp; dw_sect->size = bfd_section_size (sectp); dw_sect->read (objfile);