diff --git a/gdb/NEWS b/gdb/NEWS index 32458156591..6ef60ac3399 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -43,6 +43,11 @@ current executable if the current executable has a 'target:' prefix, or if the current executable is within the sysroot. +* GDB now adds all type symbols to the .gdb_index section. The index + version number has not increased as a consequence of this change. + This fixes an issue where GDB could fail to find a type when relying + on the index. Any existing indexes should be regenerated. + * New targets GNU/Linux/MicroBlaze (gdbserver) microblazeel-*linux* diff --git a/gdb/dwarf2/index-write.c b/gdb/dwarf2/index-write.c index 3899463c115..dd77be5310e 100644 --- a/gdb/dwarf2/index-write.c +++ b/gdb/dwarf2/index-write.c @@ -55,7 +55,7 @@ #define DW2_GDB_INDEX_SYMBOL_KIND_SET_VALUE(cu_index, value) \ do { \ gdb_assert ((value) >= GDB_INDEX_SYMBOL_KIND_TYPE \ - && (value) <= GDB_INDEX_SYMBOL_KIND_UNUSED5); \ + && (value) < GDB_INDEX_SYMBOL_KIND_UNUSED5); \ GDB_INDEX_SYMBOL_KIND_SET_VALUE((cu_index), (value)); \ } while (0) @@ -184,9 +184,8 @@ struct symtab_index_entry of this name. */ std::vector cu_indices; - /* Minimize CU_INDICES, sorting them and removing duplicates as - appropriate. */ - void minimize (); + /* Sort CU_INDICES. */ + void sort (); }; /* The symbol table. This is a power-of-2-sized hash table. */ @@ -198,16 +197,16 @@ struct mapped_symtab } /* If there are no elements in the symbol table, then reduce the table - size to zero. Otherwise call symtab_index_entry::minimize each entry + size to zero. Otherwise call symtab_index_entry::sort each entry in the symbol table. */ - void minimize () + void minimize_and_sort () { if (m_element_count == 0) m_data.resize (0); for (symtab_index_entry &item : m_data) - item.minimize (); + item.sort (); } /* Add an entry to SYMTAB. NAME is the name of the symbol. CU_INDEX is @@ -417,69 +416,19 @@ mapped_symtab::add_index_entry (const char *name, int is_static, /* See symtab_index_entry. */ void -symtab_index_entry::minimize () +symtab_index_entry::sort () { if (name == nullptr || cu_indices.empty ()) return; - /* We sort the indexes in a funny way: GDB_INDEX_SYMBOL_KIND_UNUSED5 - is always sorted last; then otherwise we sort by numeric value. - This ensures that we prefer the definition when both a definition - and a declaration (stub type) are seen. */ + /* Sort the entries based on the CU offset. */ std::sort (cu_indices.begin (), cu_indices.end (), [] (offset_type vala, offset_type valb) { - auto kinda = GDB_INDEX_SYMBOL_KIND_VALUE (vala); - auto kindb = GDB_INDEX_SYMBOL_KIND_VALUE (valb); - if (kinda != kindb) - { - /* Declaration sorts last. */ - if (kinda == GDB_INDEX_SYMBOL_KIND_UNUSED5) - return false; - if (kindb == GDB_INDEX_SYMBOL_KIND_UNUSED5) - return true; - } return vala < valb; }); auto from = std::unique (cu_indices.begin (), cu_indices.end ()); cu_indices.erase (from, cu_indices.end ()); - - /* Rewrite GDB_INDEX_SYMBOL_KIND_UNUSED5. This ensures that a type - declaration will be deleted by the subsequent squashing step, if - warranted. */ - for (auto &val : cu_indices) - { - gdb_index_symbol_kind kind = GDB_INDEX_SYMBOL_KIND_VALUE (val); - if (kind != GDB_INDEX_SYMBOL_KIND_UNUSED5) - continue; - - offset_type newval = 0; - DW2_GDB_INDEX_CU_SET_VALUE (newval, GDB_INDEX_CU_VALUE (val)); - DW2_GDB_INDEX_SYMBOL_STATIC_SET_VALUE - (newval, GDB_INDEX_SYMBOL_STATIC_VALUE (val)); - DW2_GDB_INDEX_SYMBOL_KIND_SET_VALUE (newval, - GDB_INDEX_SYMBOL_KIND_TYPE); - - val = newval; - } - - /* We don't want to enter a type more than once, so - remove any such duplicates from the list as well. When doing - this, we want to keep the entry from the first CU -- but this is - implicit due to the sort. This choice is done because it's - similar to what gdb historically did for partial symbols. */ - gdb::unordered_set seen; - from = std::remove_if (cu_indices.begin (), cu_indices.end (), - [&] (offset_type val) - { - gdb_index_symbol_kind kind = GDB_INDEX_SYMBOL_KIND_VALUE (val); - if (kind != GDB_INDEX_SYMBOL_KIND_TYPE) - return false; - - val &= ~GDB_INDEX_CU_MASK; - return !seen.insert (val).second; - }); - cu_indices.erase (from, cu_indices.end ()); } /* A form of 'const char *' suitable for container keys. Only the @@ -1307,16 +1256,7 @@ write_cooked_index (cooked_index *table, || entry->tag == DW_TAG_enumerator) kind = GDB_INDEX_SYMBOL_KIND_VARIABLE; else if (tag_is_type (entry->tag)) - { - /* If we added a type declaration, we want to note this - fact for later, because we don't want a type declaration - to cause the real definition to be omitted from the - index. GDB_INDEX_SYMBOL_KIND_UNUSED5 is used here, but - rewritten later before the index is written. */ - kind = ((entry->flags & IS_TYPE_DECLARATION) == 0 - ? GDB_INDEX_SYMBOL_KIND_TYPE - : GDB_INDEX_SYMBOL_KIND_UNUSED5); - } + kind = GDB_INDEX_SYMBOL_KIND_TYPE; else kind = GDB_INDEX_SYMBOL_KIND_OTHER; @@ -1458,7 +1398,7 @@ write_gdbindex (dwarf2_per_bfd *per_bfd, cooked_index *table, /* Now that we've processed all symbols we can shrink their cu_indices lists. */ - symtab.minimize (); + symtab.minimize_and_sort (); data_buf symtab_vec, constant_pool; diff --git a/gdb/dwarf2/read-gdb-index.c b/gdb/dwarf2/read-gdb-index.c index b0022e8cba3..69a8a87b257 100644 --- a/gdb/dwarf2/read-gdb-index.c +++ b/gdb/dwarf2/read-gdb-index.c @@ -262,7 +262,6 @@ mapped_gdb_index::build_name_components (dwarf2_per_objfile *per_objfile) std::vector these_entries; offset_view vec (constant_pool.slice (symbol_vec_index (idx))); offset_type vec_len = vec[0]; - bool global_seen = false; for (offset_type vec_idx = 0; vec_idx < vec_len; ++vec_idx) { offset_type cu_index_and_attrs = vec[vec_idx + 1]; @@ -302,11 +301,6 @@ mapped_gdb_index::build_name_components (dwarf2_per_objfile *per_objfile) tag = (dwarf_tag) DW_TAG_GDB_INDEX_TYPE; else { - /* Work around gold/15646. */ - if (global_seen) - continue; - global_seen = true; - tag = DW_TAG_structure_type; this_lang = language_cplus; } diff --git a/gdb/testsuite/gdb.base/gold-gdb-index.c b/gdb/testsuite/gdb.base/gdb-index-many-types-1.c similarity index 68% rename from gdb/testsuite/gdb.base/gold-gdb-index.c rename to gdb/testsuite/gdb.base/gdb-index-many-types-1.c index 2bcef13c3ba..00b793ca4ee 100644 --- a/gdb/testsuite/gdb.base/gold-gdb-index.c +++ b/gdb/testsuite/gdb.base/gdb-index-many-types-1.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2020-2025 Free Software Foundation, Inc. + Copyright 2025 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,15 +15,34 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "gold-gdb-index.h" - -namespace N1 -{ - void foo () { C1::baz (); } -} +#include "gdb-index-many-types.h" int main () { + foo_func_a (0); + foo_func_b (0); + bar_func_a (0); + bar_func_b (0); + baz_func_a (0); + baz_func_b (0); return 0; } + +void +foo_func_c (foo_t *obj) +{ + (void) obj; +} + +void +bar_func_c (bar_t *obj) +{ + (void) obj; +} + +void +baz_func_c (baz_t *obj) +{ + (void) obj; +} diff --git a/gdb/testsuite/gdb.base/gold-gdb-index-2.c b/gdb/testsuite/gdb.base/gdb-index-many-types-2.c similarity index 61% rename from gdb/testsuite/gdb.base/gold-gdb-index-2.c rename to gdb/testsuite/gdb.base/gdb-index-many-types-2.c index ac0a60aae5d..2c0a27f30ba 100644 --- a/gdb/testsuite/gdb.base/gold-gdb-index-2.c +++ b/gdb/testsuite/gdb.base/gdb-index-many-types-2.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2020-2025 Free Software Foundation, Inc. + Copyright 2025 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,9 +15,42 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -#include "gold-gdb-index.h" +#include "gdb-index-many-types.h" -namespace N1 +typedef struct foo_t { - void bar () { C1::baz (); } + int foo_t_1; + int foo_t_2; +} foo_t; + +typedef struct woof_t +{ + int woof_t_1; + int woof_t_2; +} woof_t; + +static void +woof_func (woof_t *obj) +{ + (void) obj; +} + +void +foo_func_a (foo_t *obj) +{ + woof_func (0); + (void) obj; +} + +void +baz_func_a (baz_t *obj) +{ + (void) obj; +} + +void +bar_func_a (bar_t *obj) +{ + woof_func (0); + (void) obj; } diff --git a/gdb/testsuite/gdb.base/gold-gdb-index.h b/gdb/testsuite/gdb.base/gdb-index-many-types-3.c similarity index 61% rename from gdb/testsuite/gdb.base/gold-gdb-index.h rename to gdb/testsuite/gdb.base/gdb-index-many-types-3.c index a26b8568297..54425ed5156 100644 --- a/gdb/testsuite/gdb.base/gold-gdb-index.h +++ b/gdb/testsuite/gdb.base/gdb-index-many-types-3.c @@ -1,6 +1,6 @@ /* This testcase is part of GDB, the GNU debugger. - Copyright 2020-2025 Free Software Foundation, Inc. + Copyright 2025 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -15,11 +15,42 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ -namespace N1 +#include "gdb-index-many-types.h" + +typedef struct woof_t { - class C1 - { - public: - static void baz () {} - }; + double woof_t_3; + double woof_t_4; +} woof_t; + +static void +woof_func (woof_t *obj) +{ + (void) obj; +} + +typedef struct bar_t +{ + int bar_t_1; + int bar_t_2; +} bar_t; + +void +bar_func_b (bar_t *obj) +{ + woof_func (0); + (void) obj; +} + +void +baz_func_b (baz_t *obj) +{ + (void) obj; +} + +void +foo_func_b (foo_t *obj) +{ + woof_func (0); + (void) obj; } diff --git a/gdb/testsuite/gdb.base/gdb-index-many-types.exp b/gdb/testsuite/gdb.base/gdb-index-many-types.exp new file mode 100644 index 00000000000..d7a1a7b9294 --- /dev/null +++ b/gdb/testsuite/gdb.base/gdb-index-many-types.exp @@ -0,0 +1,157 @@ +# Copyright 2025 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Check that adding an index to an executable (both a gdb index and a +# dwarf-5 index are tested), doesn't prevent GDB from seeing the +# expected types. + +standard_testfile -1.c -2.c -3.c .h + +# One of the tests uses this Python file. The test_* proc checks that +# GDB supports Python tests. Some of the other procs don't use this +# Python file. +set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py] + +if {[build_executable "building" $testfile \ + [list $srcfile $srcfile2 $srcfile3]] == -1} { + return +} + +# Run 'info types TYPENAME', expect to see an entry from FILENAME for +# the line matching PATTERN. +proc check_info_types { testfile typename filename pattern } { + with_test_prefix "$filename '$pattern'" { + clean_restart $testfile + + set line_num [gdb_get_line_number $pattern $filename] + gdb_test "info types $typename" \ + "File \[^\r\n\]+/${filename}:(?:\r\n${::decimal}:\[^\r\n\]+)*\r\n${line_num}:\[^\r\n\]+.*" + } +} + +# Start GDB with FILENAME, and examine some of the types. This proc +# might seem to be using clean_restart a little too much, but we need +# to be really careful here. As we examine one type, e.g. foo_t, this +# might cause GDB to fully parse a CU, which then means examining +# bar_t gives the expected result. When, if we'd first looked for +# bar_t, then (due to an index bug) we might not have found the +# correct type definition. +# +# The only way we can be sure that an earlier test isn't going to +# trigger CU expansion is to restart GDB before every query. +proc run_test { filename } { + # Print all the types for which there is only one representation. + foreach type { foo_t bar_t baz_t } { + clean_restart $filename + gdb_test "ptype $type" \ + [multi_line \ + "type = struct $type {" \ + "\\s+int ${type}_1;" \ + "\\s+int ${type}_2;" \ + "}"] + } + + # There are two different versions of woof_t. For now, when using + # `ptype` GDB will just display the first one it finds, which could + # legitimately be either. + set woof_int_re [multi_line \ + "type = struct woof_t {" \ + "\\s+int woof_t_1;" \ + "\\s+int woof_t_2;" \ + "}"] + set woof_double_re [multi_line \ + "type = struct woof_t {" \ + "\\s+double woof_t_3;" \ + "\\s+double woof_t_4;" \ + "}"] + clean_restart $filename + gdb_test_multiple "ptype woof_t" "" { + -re -wrap $woof_int_re { + pass $gdb_test_name + } + -re -wrap $woof_double_re { + pass $gdb_test_name + } + } + + # Check for declarations and definitions of some types. + check_info_types $filename foo_t $::srcfile2 "typedef struct foo_t" + check_info_types $filename foo_t $::srcfile4 "typedef struct foo_t foo_t;" + check_info_types $filename bar_t $::srcfile3 "typedef struct bar_t" + check_info_types $filename bar_t $::srcfile4 "typedef struct bar_t bar_t;" + check_info_types $filename baz_t $::srcfile4 "typedef struct baz_t" + check_info_types $filename baz_t $::srcfile4 "\} baz_t;" + check_info_types $filename woof_t $::srcfile2 "typedef struct woof_t" + check_info_types $filename woof_t $::srcfile3 "typedef struct woof_t" + + # Use Python to look for type symbols. + if { [allow_python_tests] } { + foreach_with_prefix type { foo_t bar_t baz_t } { + clean_restart $filename + gdb_test_no_output "source $::pyfile" "import python scripts" + gdb_test "py-show-type $type" \ + [multi_line \ + "Looking for type '$type':" \ + " Found 3 type symbols" \ + " 1: struct $type \\{ int ${type}_1; int ${type}_2; \\}" \ + " 2: struct $type \\{ int ${type}_1; int ${type}_2; \\}" \ + " 3: struct $type \\{ int ${type}_1; int ${type}_2; \\}"] + } + + clean_restart $filename + gdb_test_no_output "source $::pyfile" "import python scripts" + gdb_test "py-show-type woof_t" \ + [multi_line \ + "Looking for type 'woof_t':" \ + " Found 2 type symbols" \ + " 1: struct woof_t \\{ (?:int|double) woof_t_(?:1|3); (?:int|double) woof_t_(?:2|4); \\}" \ + " 2: struct woof_t \\{ (?:int|double) woof_t_(?:1|3); (?:int|double) woof_t_(?:2|4); \\}"] + + } +} + +with_test_prefix "no index" { + run_test $testfile +} + +# The previous call to 'run_test' will have left GDB active. Check if +# BINFILE already has an index. If it does then we must be running +# with one of the boardfiles that adds an index. We could possibly +# try to remove the index, but for now, just don't run the following +# parts which rely on adding an index. +set index_type [get_index_type $binfile "check debug style"] +if { $index_type ne "cooked" } { + unsupported "cannot test without a cooked index" + return +} + +foreach_with_prefix index_type { gdb dwarf5 } { + set binfile_with_index ${binfile}-idx-${index_type} + + remote_exec build "cp $binfile $binfile_with_index" + + if { $index_type eq "gdb" } { + set style "" + } else { + set style "-dwarf-5" + } + + if {[ensure_gdb_index $binfile_with_index $style] != 1} { + unsupported "couldn't add $index_type index" + return + } + + run_test [file tail $binfile_with_index] +} diff --git a/gdb/testsuite/gdb.base/gdb-index-many-types.h b/gdb/testsuite/gdb.base/gdb-index-many-types.h new file mode 100644 index 00000000000..eb5447ad678 --- /dev/null +++ b/gdb/testsuite/gdb.base/gdb-index-many-types.h @@ -0,0 +1,42 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2025 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef GDB_INDEX_MANY_TYPES_H +#define GDB_INDEX_MANY_TYPES_H + +typedef struct foo_t foo_t; +typedef struct bar_t bar_t; + +extern void foo_func_a (foo_t *obj); +extern void foo_func_b (foo_t *obj); +extern void foo_func_c (foo_t *obj); + +extern void bar_func_a (bar_t *obj); +extern void bar_func_b (bar_t *obj); +extern void bar_func_c (bar_t *obj); + +typedef struct baz_t +{ + int baz_t_1; + int baz_t_2; +} baz_t; + +extern void baz_func_a (baz_t *obj); +extern void baz_func_b (baz_t *obj); +extern void baz_func_c (baz_t *obj); + +#endif /* GDB_INDEX_MANY_TYPES_H */ diff --git a/gdb/testsuite/gdb.base/gdb-index-many-types.py b/gdb/testsuite/gdb.base/gdb-index-many-types.py new file mode 100644 index 00000000000..b1de4f9cbed --- /dev/null +++ b/gdb/testsuite/gdb.base/gdb-index-many-types.py @@ -0,0 +1,54 @@ +# Copyright 2025 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + + +class TypeViewer(gdb.Command): + """A command which takes a string and looks up types with that name. + + The types are expected to all be structs. This command prints a + basic representation of the struct. This is only going to work when + used with the types defined in the gdb-index-many-types test source + files.""" + + def __init__(self): + super().__init__("py-show-type", gdb.COMMAND_USER) + + def invoke(self, args, from_tty): + argv = gdb.string_to_argv(args) + if argv[0] == "": + raise gdb.GdbError("missing argument") + print("Looking for type '" + argv[0] + "':") + syms = gdb.lookup_static_symbols(argv[0], gdb.SYMBOL_TYPE_DOMAIN) + count = len(syms) + print(" Found %d type symbol%s" % (count, "" if count == 1 else "s")) + for i, s in enumerate(syms, start=1): + t = s.type + if t is None: + print(" %d: No type." % i) + else: + fields = "struct " + argv[0] + " {" + try: + for f in t.fields(): + if len(fields) > 0: + fields = fields + " " + fields = fields + "%s %s;" % (str(f.type), f.name) + except: + pass + fields = fields + " }" + + print(" %d: %s" % (i, fields)) + + +TypeViewer() diff --git a/gdb/testsuite/gdb.base/gold-gdb-index.exp b/gdb/testsuite/gdb.base/gold-gdb-index.exp deleted file mode 100644 index c91fd3ae28b..00000000000 --- a/gdb/testsuite/gdb.base/gold-gdb-index.exp +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2020-2025 Free Software Foundation, Inc. - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . */ - -# This tests the gdb workaround for PR binutils/15646. - -standard_testfile .c gold-gdb-index-2.c - -if { [have_fuse_ld_gold] == 0} { - return -1 -} - -if {[prepare_for_testing "failed to prepare" $testfile "$srcfile $srcfile2" \ - {debug c++ additional_flags=-fuse-ld=gold \ - ldflags=-Wl,--gdb-index \ - additional_flags=-ggnu-pubnames}]} { - return -1 -} - -if { [have_index $binfile] != "gdb_index" } { - return -1 -} - -if {![runto_main]} { - return 0 -} - -gdb_test_no_output "nosharedlibrary" - -gdb_test_no_output "set breakpoint pending off" -gdb_test "break N1::misspelled" "Function \"N1::misspelled\" not defined\." - -gdb_test_multiple "maint info symtabs" "" { - -re -wrap "\{ symtab \[^\r\n\]*gold-gdb-index-2.c.*" { - fail $gdb_test_name - } - -re -wrap "" { - pass $gdb_test_name - } -}