From: Andrew Burgess <aburgess@redhat.com>
To: gdb-patches@sourceware.org
Cc: Andrew Burgess <aburgess@redhat.com>,
Simon Marchi <simark@simark.ca>, Tom Tromey <tom@tromey.com>
Subject: [PATCHv4] gdb: resolve class name via DW_AT_signature in cooked index
Date: Wed, 16 Sep 2026 12:38:38 +0100 [thread overview]
Message-ID: <e78a75720042dba3d3b052883d2d84d374ffc157.1789555833.git.aburgess@redhat.com> (raw)
In-Reply-To: <897f5eb957bdfd90cd3fd5efa662021ed5c2aef2.1788269262.git.aburgess@redhat.com>
In v4:
- I believe I've addressed all the issues that Simon raised during
his v3 review. Most of the issues were straight forward so I'll
not mention them all here. If I've missed any then appologies.
Here are the two issues that I think are worth additional
explanation:
- The crash Simon reported when using the cc-with-gdb-index board
was causd by the empty name string I was previously using being
emitted into the generated gdb_index. When that index is read
back in the empty string causes a crash.
What this actually means is that a baddly formed gdb_index can
already trigger a GDB crash, but that's outside the scope of this
patch.
The fix I propose is, as was discussed elsewhere in the thread, to
remove entries that end up with no name (due to bad DWARF). But
this leads to another problem, because we end up needing to check
a entry's parent's name, and the parent could be from a different
shard, we need to ensure that all the names have been resolved
before we look at the get_paent()->name. To handle this I propose
that we split set_contents into two separate phases, a first
resolve names phase, then a second finalize phase.
With this done we should no longer encounter any entry with a NULL
name after finalization, which means we no longer generate an
invalid index, which means GDB will not trigger it's reading a
corrupted index bug.
- Simon did mention performance at the point where we merge togethe
the signature to name map. I haveb't tried to improve this yet as
that will just add more complexity, but we could potentially do
something like the parent_map_map, where we have a data structure
that holds a vector of maps. This would make merging the maps
quick, moving the cost to the lookup instead, but this might be an
improvement we want to make later on.
In v3:
- I forgot to run check-all-boards on v2. There were some failures
due to GDB's output not matching the patterns, I've fixed what I
could in this iteration.
- While looking at the different patterns I reworded one of the
complaint messages in cooked-index-shard.c to match a warning that
is emitted from elsewhere in the DWARF reader. There's no real
functional change, it's just the wording that's updated.
- There are still some check-all-boards failures, but I believe
these are all wider issues when running DWARF assembler tests
using the check-all-boards rule, so I'm ignoring them for now.
In v2:
- Complete rewrite to address the issues that Tom raised.
- Rebase to HEAD and retest.
- New tests added to cover the DWO case and an error case.
Thanks,
Andrew
~~~~~
This commit fixes PR gdb/33447, an issue where looking up qualified
member function names was not working for C++ binaries compiled with
Clang when using the -fdebug-types-section flag.
Before this commit we would see this behaviour:
(gdb) print base1::a_function
There is no field named a_function
When what we expect to see is:
(gdb) print base1::a_function
$1 = {void (const base1 * const)} 0x403060 <base1::a_function() const>
The problem is that the cooked index is unable to determine the name
of the parent class `base1` in this case, and so decides not to index
`base1` or any of its child DIEs, which includes its member functions.
The problem was discovered by running gdb.cp/cpexprs-debug-types.exp
with Clang:
make check-gdb TESTS=gdb.cp/cpexprs-debug-types.exp \
RUNTESTFLAGS='CXX_FOR_TARGET=clang++ CC_FOR_TARGET=clang'
The cpexprs-debug-types.exp test forces use of the
'-fdebug-types-section' flag, which is not on by default. With this
flag, class definitions are placed in type units, and the compile unit
contains only a declaration stub for each class. Both clang++ and g++
emit these stubs, but they differ in one detail: GCC includes
DW_AT_name on the stub, while clang++ does not, the stub carries only
DW_AT_declaration and DW_AT_signature. The class name is only
available in the type unit, reachable by following the signature.
For example, Clang emits this in a CU:
<1><2e7d>: DW_TAG_class_type
DW_AT_declaration : 1
DW_AT_signature : 0x3abb...
<2><2eac>: DW_TAG_subprogram
DW_AT_name : a_function
DW_AT_declaration : 1
The definition for a_function is elsewhere in the same CU:
<1><3142>: DW_TAG_subprogram
DW_AT_specification: <0x2eac>
And in a TU elsewhere:
Compilation Unit @ offset 0xd2e:
... snip ...
Signature: 0x3abb...
<0><d46>: Abbrev Number: 1 (DW_TAG_type_unit)
... snip ...
<1><d51>: Abbrev Number: 30 (DW_TAG_class_type)
... snip ...
<d57> DW_AT_name : (indexed string: 0xaa): base1
To find the DW_AT_name the cooked index needs to look up the type
within the TU. Without the name the cooked indexer skips indexing
`base1` as well as its children.
This look up used to work; it works in GDB 17. PR gdb/33447
incorrectly identifies commit c879f4dc3e317cf6353a45a803ecf00d577a13d8
as the commit that introduced the regression. This is actually the
last working commit. The problem was introduced by the next commit in
the same series:
commit 86ac8c546235a67d6a6bb29476a3a9ac8f7a620a
Date: Thu Jan 2 15:28:18 2025 -0700
Convert lookup_symbol_in_objfile
Prior to this commit GDB's symbol lookup had two phases, a search
through already expanded symtabs, and a search via
lookup_symbol_via_quick_fns. After the above commit only
lookup_symbol_via_quick_fns remains.
The lookup_symbol_via_quick_fns lookup, which relies on the indexer,
was always broken, but the first phase, searching via expanded
symtabs, could correctly find the type name via the signature.
An initial attempt to solve this problem tried to fix this problem
within cooked_indexer::scan_attributes, calling lookup_signatured_type
and finding the name that way. However, there were three problems
with this approach:
1. Possible thread safety issues; calling lookup_signatured_type for
a DWO file ends up calling lookup_dwo_signatured_type, which can
call add_type_unit and finalize_all_units, which modify state
that is shared between parser threads.
2. The dwarf2_per_cu::type_offset_in_section for a TU is only set
when the cutu_reader is constructed to parse that TU. Calling
lookup_signatured_type doesn't fully parse the TU, it just finds
the TU. The original code relied on type_offset_in_section being
valid in order to then parse the TU and extract the name. This
would break if the CU was processed before the TU.
3. For skeletonless TUs, these are not indexed until late in the
indexing process, after all the CUs have been indexed. This
means that if the TU referenced by the signature was skeletonless
then the original approach would fail to find it.
The new approach presented here is modeled more along the lines of the
deferred parent handling that already exists within the indexer. The
following changes have been made:
1. abbrev.c: Add DW_AT_signature to the set of attributes that mark a
DIE as "interesting" in abbrev_table::read. Without this,
the unnamed class stub was classified as uninteresting at the
abbreviation level and scan_attributes was never called for it.
2. cooked-indexer.c (cooked_indexer::scan_attributes): Capture the
DW_AT_signature attribute.
3. cooked-indexer.c (cooked_indexer::index_dies): There are two
different jobs done here:
(a) If a DIE has no name, but does have a signature, then allow the
entry to be created with a NULL name. Record that the
cooked_index_entry had a deferred name, and the signature with
which the name can be found.
(b) If we are indexing a TU, make a record of the signature, and
the name of the primary type within the TU. This builds a
signature to name map.
4. cooked-index-worker.c (cooked_index_worker::done_reading): Merge
together all of the signature to name maps built in (3b).
5. cooked-index-shard.c (cooked_index_shard::resolve_deferred_names):
A new function, look through all of the cooked_index_entry objects
that were recorded in (3a), for each use the signature to name map
created in (4) to lookup the name, and update the entry.
If the name cannot be found then remove the cooked_index_entry from
the entries list. The entry still lives on the obstack, but will
no longer be found when iterating over all entries.
6. cooked-index-shard.c (cooked-index-shard::finalize): If the parent
of an entry has no name, then discard the entry's parent. This
prevents a nameless entry being found via the get_parent method.
We don't want any entries with a NULL name to be found after this
point as GDB currently assumes the names will not be NULL.
7. cooked-index.c (cooked_index::wait): After waiting, if we have
completed finalization then emit any complaints that came from the
finalization phase.
8. cooked-index.c (cooked_index::set_contents): This function gets a
complete rewrite. We now need to perform finalization in two
phases.
Initially I wanted to place all the new work into the
cooked_index_shard::finalize call, this would involve resolving
deferred names, resolving deferred parents, and also detaching
nameless parents from their children.
The problem with this is that a parent could arrive from a
different shard, and that shard, running on a separate thread,
might not have resolved the name of the parent yet.
As a result, GDB might decide to detach a parent as nameless, when
if we waited a little longer the parent's name would be resolved
correctly.
To fix this race condition we need to ensure that all names are
resolved before we make any decisions about whether or not to
detach a parent for being nameless.
And so, set_contents is now split into two phases. During the
first phase we use a task_group to call resolve_deferred_names on
every shard. This ensures every entry that can have a name will
have a name. The done callback for this first phase starts the
second phase.
The second phase is just the old code from this function. We use a
task_group to call finalize on every shard. The done callback
for this second phase is unchanged, writing out the index and
updating the index state.
Thanks to this two phase approach we can now, within the finalize
phase, detach nameless parents as we know that if they were going
to get a name then they would have one.
The first phase can generate a complaint so this function now makes
use of complaint_interceptor to capture these. I also added a
complaint_interceptor within the second phase even though it is not
needed right now. This is pretty cheap, and means that if phase
two does ever add a complaint in the future, then this would "just
work".
9. cooked-index-entry.c (cooked_index_entry::write_scope): Add an
assert that we are not processing an entry without a name. Entries
like this should be removed during finalization.
There are a bunch of header file changes to add the new data members,
and accessor functions, which the steps above depend on.
Added three new DWARF assembler tests.
gdb.dwarf2/sig-type-unnamed-class.exp
gdb.dwarf2/sig-type-unnamed-class-dwo.exp:
These reproduce the problem case; there's a nameless declaration
stub that references its full type via DW_AT_signature. To match
the Clang output as much as possible the member function definition
is separate and makes use of DW_AT_specification. The -dwo test
places the DWARF into a DWO file and uses a skeletonless TU.
gdb.dwarf2/sig-type-unnamed-class-bad-sig.exp:
This one tests some invalid DWARF, the type DIE references a
signature that doesn't exist. In this case GDB just leaves the
cooked_index_entry with a NULL name string, which means symbols
cannot be found. I think this is fine though, the DWARF is
corrupted in this case.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33447
---
gdb/dwarf2/abbrev.c | 1 +
gdb/dwarf2/cooked-index-entry.c | 6 +
gdb/dwarf2/cooked-index-entry.h | 25 ++-
gdb/dwarf2/cooked-index-shard.c | 64 ++++++++
gdb/dwarf2/cooked-index-shard.h | 57 +++++++
gdb/dwarf2/cooked-index-worker.c | 9 +
gdb/dwarf2/cooked-index-worker.h | 38 +++++
gdb/dwarf2/cooked-index.c | 99 ++++++++---
gdb/dwarf2/cooked-index.h | 6 +
gdb/dwarf2/cooked-indexer.c | 35 +++-
gdb/dwarf2/cooked-indexer.h | 1 +
.../sig-type-unnamed-class-bad-sig.exp | 155 ++++++++++++++++++
.../gdb.dwarf2/sig-type-unnamed-class-dwo.exp | 143 ++++++++++++++++
.../gdb.dwarf2/sig-type-unnamed-class.exp | 106 ++++++++++++
14 files changed, 716 insertions(+), 29 deletions(-)
create mode 100644 gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class-bad-sig.exp
create mode 100644 gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class-dwo.exp
create mode 100644 gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class.exp
diff --git a/gdb/dwarf2/abbrev.c b/gdb/dwarf2/abbrev.c
index 44d5c87a5f7..e99acc5e752 100644
--- a/gdb/dwarf2/abbrev.c
+++ b/gdb/dwarf2/abbrev.c
@@ -160,6 +160,7 @@ abbrev_table::read (struct dwarf2_section_info *section,
case DW_AT_specification:
case DW_AT_abstract_origin:
case DW_AT_extension:
+ case DW_AT_signature:
has_specification_or_origin = true;
break;
diff --git a/gdb/dwarf2/cooked-index-entry.c b/gdb/dwarf2/cooked-index-entry.c
index 21e25e94615..4c3246144d8 100644
--- a/gdb/dwarf2/cooked-index-entry.c
+++ b/gdb/dwarf2/cooked-index-entry.c
@@ -233,6 +233,12 @@ cooked_index_entry::write_scope (struct obstack *storage,
{
if (get_parent () != nullptr)
get_parent ()->write_scope (storage, sep, flags);
+
+ /* Any entry without a name, or with an empty name, will have been
+ filtered out while the entries were being created, or during
+ finalization. */
+ gdb_assert (name != nullptr && *name != '\0');
+
/* When computing the Ada linkage name, the entry might not have
been canonicalized yet, so use the 'name'. */
const char *local_name = ((flags & (FOR_MAIN | FOR_ADA_LINKAGE_NAME)) != 0
diff --git a/gdb/dwarf2/cooked-index-entry.h b/gdb/dwarf2/cooked-index-entry.h
index 60ea581cbbe..b1056a33ffe 100644
--- a/gdb/dwarf2/cooked-index-entry.h
+++ b/gdb/dwarf2/cooked-index-entry.h
@@ -79,6 +79,11 @@ union cooked_index_entry_ref
parent_map::addr_type deferred;
};
+/* Type that maps DW_AT_signature values for a TU to the name of the
+ primary type within the TU. */
+
+using signature_to_name_map = gdb::unordered_map<ULONGEST, const char *>;
+
/* Return a string representation of FLAGS. */
std::string to_string (cooked_index_flag flags);
@@ -252,10 +257,22 @@ struct cooked_index_entry : public allocate_on_obstack<cooked_index_entry>
defined in some CU that is included by many other CUs. */
iteration_status visit_defining_cus (per_cu_callback callback) const;
- /* The name as it appears in DWARF. This always points into one of
- the mapped DWARF sections. Note that this may be the name or the
- linkage name -- two entries are created for DIEs which have both
- attributes. */
+ /* The entry's name. If not NULL then this must point to a string
+ which will outlive this entry. This usually means that NAME
+ points either into the mapped DWARF, or into storage within the
+ owning cooked_index_shard.
+
+ This may be the name or the linkage name -- two entries are
+ created for DIEs which have both attributes.
+
+ The NAME can be NULL. When a DIE lacks a name but has a
+ signature we create an entry with a NULL name and then try to
+ lookup the name via the signature during finalization. If the
+ name via signature lookup fails then the entry continues to exist
+ with a NULL name, however, the entry should be removed from the
+ entries vector, and should be removed as a parent, so entries
+ with a NULL name should not be discovered during normal
+ processing. */
const char *name;
/* The canonical name. This may be equal to NAME. */
const char *canonical = nullptr;
diff --git a/gdb/dwarf2/cooked-index-shard.c b/gdb/dwarf2/cooked-index-shard.c
index 91dc9ab8945..e864c4c59d5 100644
--- a/gdb/dwarf2/cooked-index-shard.c
+++ b/gdb/dwarf2/cooked-index-shard.c
@@ -20,6 +20,8 @@
#include "dwarf2/cooked-index-shard.h"
#include "dwarf2/tag.h"
#include "dwarf2/index-common.h"
+#include "dwarf2/read.h"
+#include "dwarf2/error.h"
#include "cp-support.h"
#include "c-lang.h"
#include "ada-lang.h"
@@ -104,6 +106,7 @@ cooked_index_shard::add (sect_offset die_offset, enum dwarf_tag tag,
&& parent_entry.resolved == nullptr
&& m_main == nullptr
&& language_may_use_plain_main (lang)
+ && name != nullptr
&& streq (name, "main"))
m_main = result;
@@ -189,6 +192,52 @@ struct cooked_index_entry_name_ptr_eq
/* See cooked-index-shard.h. */
+void
+cooked_index_shard::resolve_deferred_names
+ (const signature_to_name_map &sig_names)
+{
+ bool need_to_cleanup_entries = false;
+ for (const auto &[entry, signature] : m_deferred_names)
+ {
+ if (const auto it = sig_names.find (signature);
+ it != sig_names.end ())
+ {
+ /* Each entry should only occur once in M_DEFERRED_NAMES,
+ and the entry should only be added when it has no name. */
+ gdb_assert (entry->name == nullptr);
+
+ /* Patch the name. */
+ entry->name = it->second;
+ }
+ else
+ {
+ need_to_cleanup_entries = true;
+ complaint (_(DWARF_ERROR_PREFIX
+ "Cannot find signatured DIE %s referenced from DIE "
+ "at %s [in module %s]"),
+ hex_string (signature),
+ sect_offset_str (entry->die_offset),
+ entry->per_cu->per_bfd ()->filename ());
+ }
+ }
+
+ /* If we failed to resolve the name of an entry via its signature
+ then remove the entry from the m_entries vector. This should be
+ rare, and should only happen when we have corrupted DWARF. The
+ entries still live on the obstack, so parent points are still
+ valid, but removing entries from the index means we don't try to
+ search them when looking for index hits. */
+ if (need_to_cleanup_entries)
+ m_entries.erase (std::remove_if (m_entries.begin (), m_entries.end (),
+ [] (const cooked_index_entry *e)
+ {
+ return e->name == nullptr;
+ }),
+ m_entries.end ());
+}
+
+/* See cooked-index-shard.h. */
+
void
cooked_index_shard::finalize (const parent_map_map *parent_maps)
{
@@ -216,6 +265,11 @@ cooked_index_shard::finalize (const parent_map_map *parent_maps)
for (cooked_index_entry *entry : m_entries)
{
+ /* Entries without a name, or with an empty name, are filtered
+ out either as the entries are created, or during the call to
+ resolve_deferred_names. */
+ gdb_assert (entry->name != nullptr && *entry->name != '\0');
+
if ((entry->flags & IS_PARENT_DEFERRED) != 0)
{
const cooked_index_entry *new_parent
@@ -223,6 +277,16 @@ cooked_index_shard::finalize (const parent_map_map *parent_maps)
entry->resolve_parent (new_parent);
}
+ /* Remove a parent reference if the parent has no name. This
+ leaves ENTRY as an orphan, but this only happens if the DWARF
+ is corrupted and we failed to find a name for the parent. We
+ can safely check the parent's name at this point because all
+ deferred names will have been resolved in all shards before
+ finalize is called on any shard. */
+ if (const cooked_index_entry *parent = entry->get_parent ();
+ parent != nullptr && parent->name == nullptr)
+ entry->set_parent (nullptr);
+
/* Note that this code must be kept in sync with
cooked_index::get_main -- if canonicalization is required
here, then a check might be required there. */
diff --git a/gdb/dwarf2/cooked-index-shard.h b/gdb/dwarf2/cooked-index-shard.h
index 84c37958c83..191182a132b 100644
--- a/gdb/dwarf2/cooked-index-shard.h
+++ b/gdb/dwarf2/cooked-index-shard.h
@@ -26,6 +26,7 @@
#include "addrmap.h"
#include "gdbsupport/iterator-range.h"
#include "gdbsupport/string-set.h"
+#include "complaints.h"
/* An index of interesting DIEs. This is "cooked", in contrast to a
mapped .debug_names or .gdb_index, which are "raw". An entry in
@@ -79,6 +80,16 @@ class cooked_index_shard
for completion, will be returned. */
range find (const std::string &name, bool completing) const;
+ /* Record that ENTRY didn't have a name attribute, but did have a
+ DW_AT_signature attribute, SIGNATURE. ENTRY will have a NULL
+ name string pointer. We will patch the name of ENTRY during
+ finalization once the TUs have been parsed, the correct TU will
+ be found using SIGNATURE. */
+ void add_deferred_name (cooked_index_entry *entry, ULONGEST signature)
+ {
+ m_deferred_names.push_back ({entry, signature});
+ }
+
private:
/* Return the entry that is believed to represent the program's
@@ -124,6 +135,38 @@ class cooked_index_shard
This may be invoked in a worker thread. */
void finalize (const parent_map_map *parent_maps);
+ /* Use SIG_NAMES to look up the name of any entry in
+ m_deferred_names. This should be called a single time, and must
+ be called before finalize is called. Every shard must have its
+ deferred names resolved before finalize can be called on any
+ shard as finalize can lookup cross-shard entries, and we need to
+ ensure that those entries have their name. */
+ void resolve_deferred_names (const signature_to_name_map &sig_names);
+
+ /* Called after each phase of the finalization process. Store
+ COMPLAINTS so they can be reported later on the main thread. */
+ void merge_finalize_complaints (complaint_collection &&complaints)
+ {
+ if (m_finalize_complaints.empty ())
+ m_finalize_complaints = std::move (complaints);
+ else
+ {
+ /* The current version of gdb::unordered_set doesn't support
+ the merge method that std::unordered_set supports. If we
+ update gdb::unordered_set then we could switch this to use
+ merge(). */
+ m_finalize_complaints.insert (complaints.begin (), complaints.end ());
+ }
+ }
+
+ /* Return the set of complaints emitted during the finalization
+ process. We move these complaints out of the shard as these are
+ only emitted once, and don't need to be stored beyond that. */
+ complaint_collection release_finalize_complaints ()
+ {
+ return std::move (m_finalize_complaints);
+ }
+
/* Storage for the entries. */
auto_obstack m_storage;
/* List of all entries. */
@@ -134,6 +177,20 @@ class cooked_index_shard
addrmap_fixed *m_addrmap = nullptr;
/* Storage for canonical names. */
gdb::string_set m_names;
+
+ /* Entries without a name, but with a signature. These entries will
+ have a NULL name, but we need to patch these up with a real name
+ during finalization. */
+ struct deferred_name
+ {
+ cooked_index_entry *entry;
+ ULONGEST signature;
+ };
+ std::vector<deferred_name> m_deferred_names;
+
+ /* Any complaints emitted during the call to finalize are stored
+ here until they can be emitted on the main thread. */
+ complaint_collection m_finalize_complaints;
};
using cooked_index_shard_up = std::unique_ptr<cooked_index_shard>;
diff --git a/gdb/dwarf2/cooked-index-worker.c b/gdb/dwarf2/cooked-index-worker.c
index 723e027172e..054b25ba147 100644
--- a/gdb/dwarf2/cooked-index-worker.c
+++ b/gdb/dwarf2/cooked-index-worker.c
@@ -249,6 +249,15 @@ cooked_index_worker::done_reading ()
m_all_parents_map.add_map (*one_result.get_parent_map ());
}
+ {
+ scoped_time_it time_it ("DWARF add signature name map", m_per_command_time);
+
+ /* Combine all of the signature to name maps. */
+ for (cooked_index_worker_result &one_result : m_results)
+ for (const auto &[sig, name] : one_result.get_sig_name_map ())
+ m_all_sig_names_map.emplace (sig, name);
+ }
+
/* Update all the CU inclusion information. */
for (auto &item : m_results)
item.invert_cu_inclusions ();
diff --git a/gdb/dwarf2/cooked-index-worker.h b/gdb/dwarf2/cooked-index-worker.h
index 1799c0b9e08..262b727b983 100644
--- a/gdb/dwarf2/cooked-index-worker.h
+++ b/gdb/dwarf2/cooked-index-worker.h
@@ -83,6 +83,25 @@ class cooked_index_worker_result
return m_shard->add (name);
}
+ /* Record that ENTRY was missing a name attribute, but did have a
+ DW_AT_signature attribute, SIGNATURE. When the TUs are
+ processed, which might not happen until after CU processing for
+ skeletonless TUs, we can find a name and supply it at that
+ point. */
+ void add_deferred_name (cooked_index_entry *entry, ULONGEST signature)
+ {
+ m_shard->add_deferred_name (entry, signature);
+ }
+
+ /* Called when processing TUs to record that the primary type within
+ a type unit with SIGNATURE, was called NAME. This information
+ will be used during finalization to fix-up the name of any
+ entries passed to add_deferred_name. */
+ void add_signatured_type_name (ULONGEST signature, const char *name)
+ {
+ m_sig_name_map.emplace (signature, name);
+ }
+
/* Install the current addrmap into the shard being constructed,
then transfer ownership of the index to the caller. */
cooked_index_shard_up release_shard ()
@@ -153,6 +172,12 @@ class cooked_index_worker_result
discovered. */
void invert_cu_inclusions ();
+ /* The signature to name map for this worker. */
+ const signature_to_name_map &get_sig_name_map () const
+ {
+ return m_sig_name_map;
+ }
+
private:
/* The abbrev table cache used by this indexer. */
abbrev_table_cache m_abbrev_table_cache;
@@ -188,6 +213,9 @@ class cooked_index_worker_result
/* Parent map for each CU that is read. */
parent_map m_parent_map;
+ /* Signature to name map for the primary type in a TU. */
+ signature_to_name_map m_sig_name_map;
+
/* A writeable addrmap being constructed by this scanner. */
addrmap_mutable m_addrmap;
@@ -272,6 +300,12 @@ class cooked_index_worker
return &m_all_parents_map;
}
+ /* Return the map containing the complete signature to name information. */
+ const signature_to_name_map &get_sig_name_map () const
+ {
+ return m_all_sig_names_map;
+ }
+
protected:
/* Let cooked_index call the 'set' and 'write_to_cache' methods. */
@@ -319,6 +353,10 @@ class cooked_index_worker
parent relationships. */
parent_map_map m_all_parents_map;
+ /* Map from signature to name of primary type within a TU. This is
+ the combined map, built after all the workers have finished. */
+ signature_to_name_map m_all_sig_names_map;
+
/* Current state of this object. */
cooked_state m_state = cooked_state::INITIAL;
/* Mutex and condition variable used to synchronize. */
diff --git a/gdb/dwarf2/cooked-index.c b/gdb/dwarf2/cooked-index.c
index 167e39ffc89..799835bca61 100644
--- a/gdb/dwarf2/cooked-index.c
+++ b/gdb/dwarf2/cooked-index.c
@@ -58,7 +58,23 @@ cooked_index::wait (cooked_state desired_state, bool allow_quit)
if (m_state == nullptr)
return;
- if (m_state->wait (desired_state, allow_quit))
+ bool done = m_state->wait (desired_state, allow_quit);
+
+ /* Emit any cached complaints if we have finalized and we are on the
+ main thread. Check for the requested state or the DONE flag
+ here, we might have only asked for MAIN_AVAILABLE, but if the
+ workers are quick then they might be done, in which case we
+ should emit the complaints now. */
+ if (!m_finalize_complaints_emitted
+ && is_main_thread ()
+ && (desired_state >= cooked_state::FINALIZED || done))
+ {
+ m_finalize_complaints_emitted = true;
+ for (auto &shard : m_shards)
+ re_emit_complaints (shard->release_finalize_complaints ());
+ }
+
+ if (done)
{
/* Only the main thread can modify this. */
gdb_assert (is_main_thread ());
@@ -74,31 +90,70 @@ cooked_index::set_contents ()
m_state->set (cooked_state::MAIN_AVAILABLE);
- /* This is run after finalization is done -- but not before. If
- this task were submitted earlier, it would have to wait for
- finalization. However, that would take a slot in the global
- thread pool, and if enough such tasks were submitted at once, it
- would cause a livelock. */
- gdb::task_group finalizers ([this] ()
- {
- m_state->set (cooked_state::FINALIZED);
- m_state->write_to_cache (index_for_writing ());
- m_state->set (cooked_state::CACHE_DONE);
- });
-
- for (auto &shard : m_shards)
+ /* Finalization is done in two phases, which we build up in reverse order.
+ During the second phase we call finalize on each shard then update the
+ state to FINALIZED then CACHE_DONE. */
+ std::shared_ptr<gdb::task_group> phase2
+ = std::make_shared<gdb::task_group> ([this] ()
{
- auto this_shard = shard.get ();
+ /* This is run after finalization is done -- but not before. If this
+ task were submitted earlier, it would have to wait for finalization.
+ However, that would take a slot in the global thread pool, and if
+ enough such tasks were submitted at once, it would cause a
+ livelock. */
+ m_state->set (cooked_state::FINALIZED);
+ m_state->write_to_cache (index_for_writing ());
+ m_state->set (cooked_state::CACHE_DONE);
+ });
+
+ /* Arrange to call finalize on each shard. */
+ for (cooked_index_shard_up &shard : m_shards)
+ {
+ cooked_index_shard *this_shard = shard.get ();
const parent_map_map *parent_maps = m_state->get_parent_map_map ();
- finalizers.add_task ([this, this_shard, parent_maps] ()
- {
- scoped_time_it time_it ("DWARF finalize worker",
- m_state->m_per_command_time);
- this_shard->finalize (parent_maps);
- });
+ phase2->add_task ([this, this_shard, parent_maps] ()
+ {
+ complaint_interceptor complaint_handler;
+
+ scoped_time_it time_it ("DWARF finalize worker",
+ m_state->m_per_command_time);
+
+ this_shard->finalize (parent_maps);
+
+ this_shard->merge_finalize_complaints (complaint_handler.release ());
+ });
}
- finalizers.start ();
+ /* In the first phase we resolve any deferred cooked_index_entry names.
+ These names are needed in the second phase, but due to cross shard child
+ to parent references, trying to resolve deferred names in the same phase
+ as the names are used would lead to data races. */
+ gdb::task_group phase1 ([phase2] ()
+ {
+ /* This is run once after all the other phase1 tasks are done. */
+ phase2->start ();
+ });
+
+ /* Arrange to call resolve_deferred_names on each shard. */
+ for (cooked_index_shard_up &shard : m_shards)
+ {
+ cooked_index_shard *this_shard = shard.get ();
+ const signature_to_name_map *sig_name_map
+ = &m_state->get_sig_name_map ();
+ phase1.add_task ([this, this_shard, sig_name_map] ()
+ {
+ complaint_interceptor complaint_handler;
+
+ scoped_time_it time_it ("DWARF resolve deferred names worker",
+ m_state->m_per_command_time);
+
+ this_shard->resolve_deferred_names (*sig_name_map);
+
+ this_shard->merge_finalize_complaints (complaint_handler.release ());
+ });
+ }
+
+ phase1.start ();
}
cooked_index::~cooked_index ()
diff --git a/gdb/dwarf2/cooked-index.h b/gdb/dwarf2/cooked-index.h
index 2e177cb62dd..3a29f8434e1 100644
--- a/gdb/dwarf2/cooked-index.h
+++ b/gdb/dwarf2/cooked-index.h
@@ -179,6 +179,12 @@ class cooked_index : public dwarf_scanner_base
that the state is CACHE_DONE -- it's important to note that only
the main thread may change the value of this pointer. */
cooked_index_worker_up m_state;
+
+ /* Any complaints raised during finalization are held within the
+ shards in M_SHARDS. Once the main thread has waited for
+ finalization to be complete then the cached complaints are
+ emitted, and this flag is set to true. */
+ bool m_finalize_complaints_emitted = false;
};
/* An implementation of quick_symbol_functions for the cooked DWARF
diff --git a/gdb/dwarf2/cooked-indexer.c b/gdb/dwarf2/cooked-indexer.c
index 581c0eb0763..59077b87920 100644
--- a/gdb/dwarf2/cooked-indexer.c
+++ b/gdb/dwarf2/cooked-indexer.c
@@ -157,6 +157,7 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu,
parent_map::addr_type *maybe_defer,
bool *is_enum_class,
bool *is_inlined,
+ std::optional<ULONGEST> *signature,
bool for_specification)
{
bool is_declaration = false;
@@ -234,6 +235,16 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu,
attr.get_ref_die_offset () };
break;
+ case DW_AT_signature:
+ /* The DW_AT_signature could also be a direct reference to a
+ type DIE. We don't currently try to capture those
+ signatures as right now we're only capturing this in
+ order to handle the fact that some TUs are parsed after
+ the parallel CU parsing. */
+ if (attr.form == DW_FORM_ref_sig8)
+ signature->emplace (attr.as_signature ());
+ break;
+
case DW_AT_external:
if (attr.as_boolean ())
*flags &= ~IS_STATIC;
@@ -380,7 +391,7 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu,
scan_attributes (scanning_per_cu, new_reader, new_info_ptr,
new_info_ptr, new_abbrev, name, linkage_name,
flags, nullptr, parent_entry, maybe_defer,
- is_enum_class, is_inlined, true);
+ is_enum_class, is_inlined, signature, true);
}
if (!for_specification)
@@ -547,6 +558,7 @@ cooked_indexer::index_dies (cutu_reader *reader,
const cooked_index_entry *this_parent_entry = parent_entry;
bool is_enum_class = false;
bool is_inlined = false;
+ std::optional<ULONGEST> signature;
/* The scope of a DW_TAG_entry_point cooked_index_entry is the one of
its surrounding subroutine. */
@@ -556,7 +568,7 @@ cooked_indexer::index_dies (cutu_reader *reader,
= scan_attributes (reader->cu ()->per_cu, reader, info_ptr, info_ptr,
abbrev, &name, &linkage_name, &flags, &sibling,
&this_parent_entry, &defer, &is_enum_class,
- &is_inlined, false);
+ &is_inlined, &signature, false);
/* A DW_TAG_entry_point inherits its static/extern property from
the enclosing subroutine. */
if (abbrev->tag == DW_TAG_entry_point)
@@ -611,7 +623,11 @@ cooked_indexer::index_dies (cutu_reader *reader,
cooked_index_entry *this_entry = nullptr;
/* Always use the reader's CU for the entry CU. */
dwarf2_per_cu *cu_for_entry = reader->cu ()->per_cu;
- if (name != nullptr)
+
+ /* If scan_attributes failed to find a name then we might still
+ want to create an entry if we think we might later find a
+ name via the signature. */
+ if (name != nullptr || signature.has_value ())
{
if (defer != 0)
this_entry
@@ -624,6 +640,11 @@ cooked_indexer::index_dies (cutu_reader *reader,
= m_index_storage->add (this_die, abbrev->tag, flags,
m_language, name,
this_parent_entry, cu_for_entry);
+
+ /* Record that this entry doesn't have a valid name and
+ needs patching. */
+ if (name == nullptr)
+ m_index_storage->add_deferred_name (this_entry, signature.value ());
}
else if (this_parent_entry != nullptr)
{
@@ -637,6 +658,14 @@ cooked_indexer::index_dies (cutu_reader *reader,
m_die_range_map->add_entry (addr, addr, this_parent_entry);
}
+ /* If THIS_DIE is the primary type within a TU, and has a valid
+ name, then add an entry mapping the signature to the name. */
+ if (const signatured_type *st = cu_for_entry->as_signatured_type ();
+ name != nullptr
+ && st != nullptr
+ && this_die == st->type_offset_in_section)
+ m_index_storage->add_signatured_type_name (st->signature, name);
+
if (linkage_name != nullptr)
{
/* We only want this to be "main" if it has a linkage name
diff --git a/gdb/dwarf2/cooked-indexer.h b/gdb/dwarf2/cooked-indexer.h
index 563742c90f6..393ef9fca5a 100644
--- a/gdb/dwarf2/cooked-indexer.h
+++ b/gdb/dwarf2/cooked-indexer.h
@@ -86,6 +86,7 @@ class cooked_indexer
parent_map::addr_type *maybe_defer,
bool *is_enum_class,
bool *is_inlined,
+ std::optional<ULONGEST> *signature,
bool for_specification);
/* Handle DW_TAG_imported_unit, by scanning the DIE to find
diff --git a/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class-bad-sig.exp b/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class-bad-sig.exp
new file mode 100644
index 00000000000..39a221a1b75
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class-bad-sig.exp
@@ -0,0 +1,155 @@
+# Copyright 2026 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 <http://www.gnu.org/licenses/>.
+
+# The test sets up a type DIE without a DW_AT_name, the type DIE links
+# to a type within a TU via DW_AT_signature. Normally GDB would
+# use the DW_AT_signature to find the name of the type DIE.
+#
+# However, in this test there is no type DIE with the specified
+# signature!
+#
+# This test checks that GDB can emit a complaint when this situation is
+# encountered. The test also checks that trying to find a method
+# within the type using the qualified name will fail.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+require dwarf2_support
+
+standard_testfile main-foo.c .S
+
+# Build the test program using VERSION for the CU/TU DWARF version.
+proc run_test { version } {
+ # Create the DWARF.
+ set asm_file [standard_output_file $::srcfile2]
+ Dwarf::assemble {
+ filename $asm_file
+ add_dummy_cus 0
+ } {
+ upvar version version
+
+ get_func_info foo
+ get_func_info main
+
+ declare_labels method_decl
+
+ tu { version $version } 0xdeadbeefdeadbeef the_type {
+ DW_TAG_type_unit {
+ DW_AT_language @DW_LANG_C_plus_plus
+ } {
+ the_type: DW_TAG_class_type {
+ DW_AT_name the_type
+ DW_AT_byte_size 1 sdata
+ }
+ }
+ }
+
+ cu { version $version } {
+ compile_unit {
+ DW_AT_language @DW_LANG_C_plus_plus
+ } {
+ DW_TAG_class_type {
+ DW_AT_declaration 1 flag
+ DW_AT_signature 0xdeadbeef01234567 ref_sig8
+ } {
+ method_decl: DW_TAG_subprogram {
+ DW_AT_name method
+ DW_AT_linkage_name _ZN8the_type6methodEv
+ DW_AT_declaration 1 flag
+ }
+ }
+
+ DW_TAG_subprogram {
+ DW_AT_specification %$method_decl
+ DW_AT_low_pc $foo_start DW_FORM_addr
+ DW_AT_high_pc $foo_end DW_FORM_addr
+ }
+
+ DW_TAG_subprogram {
+ DW_AT_name main
+ DW_AT_low_pc $main_start DW_FORM_addr
+ DW_AT_high_pc $main_end DW_FORM_addr
+ }
+ }
+ }
+ }
+
+ set testfile_name ${::testfile}-${version}
+ set binfile_name [standard_output_file $testfile_name]
+ if { [build_executable "failed to build" $testfile_name \
+ [list $asm_file $::srcfile] {nodebug}] } {
+ return
+ }
+
+ clean_restart $testfile_name
+
+ set expect_complaint true
+ # If we have a .gdb_index already then the part of this test that
+ # checks for a warning when loading the executable is not going to
+ # work as the warning will have already been generated when the
+ # index was added.
+ if {[get_index_type $testfile_name] == "gdb"} {
+ set expect_complaint false
+ }
+
+ clean_restart
+
+ set host_binfile [gdb_remote_download host $binfile_name]
+ if { [is_remote host] } {
+ # For some remote host boards gdb_remote_download returns an
+ # absolute path, but for others it returns a relative path.
+ # The path reported in the complaint message is always
+ # absolute. Handle this with an optional prefix pattern.
+ set binfile_re "\[^\r\n\]*[string_to_regexp $host_binfile]"
+ } elseif {[section_get $binfile_name ".gnu_debuglink"] ne ""} {
+ set binfile_re "\[^\r\n\]+[string_to_regexp ${testfile_name}.debug]"
+ } else {
+ set binfile_re [string_to_regexp $binfile_name]
+ }
+
+ # Load the executable and check we see the expected complaint from
+ # the DWARF indexer.
+ gdb_test_no_output "maint set dwarf synchronous on"
+ gdb_test_no_output "set complaints 100"
+ set saw_complaint false
+ gdb_test_multiple "file $host_binfile" "file command" -lbl {
+ -re "^\r\nDuring symbol reading: DWARF Error: Cannot find signatured DIE 0xdeadbeef01234567 referenced from DIE at $::hex \\\[in module $binfile_re\\\](?=\r\n)" {
+ set saw_complaint true
+ exp_continue
+ }
+ -re "^\r\n$::gdb_prompt $" {
+ gdb_assert { $saw_complaint == $expect_complaint } $gdb_test_name
+ }
+ }
+
+ if { ![readnow] } {
+ # Check that GDB is unable to find this via the index. If we
+ # load the full symbols then GDB is able to resolve this print
+ # via the linkage name.
+ #
+ # It is unfortunate that there is a difference in behaviour
+ # depending on whether GDB is using the index or not, but
+ # remember, the DWARF is corrupted at this point, so it's
+ # probably OK if things don't fully work, so long as GDB
+ # doesn't crash.
+ gdb_test "print the_type::method" \
+ "^There is no field named method"
+ }
+}
+
+foreach_with_prefix version { 4 5 } {
+ run_test $version
+}
diff --git a/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class-dwo.exp b/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class-dwo.exp
new file mode 100644
index 00000000000..bc6420d7aa9
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class-dwo.exp
@@ -0,0 +1,143 @@
+# Copyright 2026 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 <http://www.gnu.org/licenses/>.
+
+# Like sig-type-unnamed-class.exp, but with split DWARF. The type
+# unit in the DWO file is skeletonless, which is the norm when using
+# split DWARF.
+#
+# Check that the cooked index correctly resolves the parent of a member
+# function when the class declaration stub in the DWO compile unit has
+# DW_AT_signature but no DW_AT_name, and the referenced type unit is
+# skeletonless.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+require dwarf2_support
+
+standard_testfile main-foo.c -dw.S
+
+# Build the test program using VERSION for the CU/TU DWARF version.
+
+proc run_test { version } {
+ set asm_file [standard_output_file $::srcfile2]
+
+ # Setup some state based on DWARF version.
+ if { $version == 5 } {
+ set dwo_cu_opts [list fission 1 version 5 dwo_id 0xF00D]
+ set skel_cu_opts [list version 5 dwo_id 0xF00D]
+ set dwo_name_attr DW_AT_dwo_name
+ } else {
+ set dwo_cu_opts [list fission 1 version 4]
+ set skel_cu_opts [list version 4]
+ set dwo_name_attr DW_AT_GNU_dwo_name
+ }
+
+ set testfile_name ${::testfile}-${version}
+
+ Dwarf::assemble $asm_file {
+ upvar version version
+ upvar dwo_cu_opts dwo_cu_opts
+ upvar skel_cu_opts skel_cu_opts
+ upvar dwo_name_attr dwo_name_attr
+ upvar testfile_name testfile_name
+
+ declare_labels method_decl
+
+ get_func_info foo
+
+ # Type unit in the DWO file. This is skeletonless: there is no
+ # corresponding skeleton TU in the main file.
+ tu {
+ fission 1
+ version $version
+ } 0xdeadbeef01234567 the_type {
+ type_unit {
+ DW_AT_language @DW_LANG_C_plus_plus
+ } {
+ the_type: DW_TAG_class_type {
+ DW_AT_name the_type
+ DW_AT_byte_size 1 DW_FORM_sdata
+ }
+ }
+ }
+
+ set debug_addr_base [debug_addr_label]
+
+ # Compile unit in the DWO file. Contains a class declaration
+ # stub with DW_AT_signature but no DW_AT_name, replicating what
+ # Clang emits with -fdebug-types-section. The class name must
+ # be resolved by following the signature to the type unit.
+ cu $dwo_cu_opts {
+ compile_unit {
+ DW_AT_language @DW_LANG_C_plus_plus
+ DW_AT_name ${::srcfile}
+ DW_AT_comp_dir .
+ if { $version == 4 } {
+ DW_AT_GNU_dwo_id 0xF00D DW_FORM_data8
+ }
+ } {
+ DW_TAG_class_type {
+ DW_AT_declaration 1 DW_FORM_flag
+ DW_AT_signature 0xdeadbeef01234567 DW_FORM_ref_sig8
+ } {
+ method_decl: DW_TAG_subprogram {
+ DW_AT_name method
+ DW_AT_linkage_name _ZN8the_type6methodEv
+ DW_AT_declaration 1 DW_FORM_flag
+ }
+ }
+
+ DW_TAG_subprogram {
+ DW_AT_specification :$method_decl
+ DW_AT_low_pc $foo_start DW_FORM_GNU_addr_index
+ DW_AT_high_pc $foo_end DW_FORM_GNU_addr_index
+ }
+ }
+ }
+
+ # Skeleton CU in the main file.
+ cu $skel_cu_opts {
+ compile_unit {
+ $dwo_name_attr ${testfile_name}-dw.dwo DW_FORM_strp
+ DW_AT_comp_dir .
+ DW_AT_GNU_addr_base $debug_addr_base
+ if { $version == 4 } {
+ DW_AT_GNU_dwo_id 0xF00D DW_FORM_data8
+ }
+ } {}
+ }
+ }
+
+ set binfile_name [standard_output_file $testfile_name]
+ set obj [standard_output_file "${testfile_name}-dw.o"]
+ if {[build_executable_and_dwo_files "build exec and dwo" $binfile_name \
+ {nodebug} \
+ [list $asm_file {nodebug split-dwo} $obj] \
+ [list $::srcfile {nodebug}]]} {
+ return
+ }
+
+ clean_restart $testfile_name
+
+ # Check that GDB was able to find the parent for "method", and as
+ # a result, can correctly find this field of "the_type".
+ gdb_test "print the_type::method" \
+ [string_to_regexp " <the_type::method()>"]
+}
+
+foreach_with_prefix version { 4 5 } {
+ run_test $version
+}
diff --git a/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class.exp b/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class.exp
new file mode 100644
index 00000000000..6f39e2c823d
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/sig-type-unnamed-class.exp
@@ -0,0 +1,106 @@
+# Copyright 2026 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 <http://www.gnu.org/licenses/>.
+
+# Check that the cooked index correctly resolves the parent of a member
+# function when the class declaration stub in the compile unit has
+# DW_AT_signature but no DW_AT_name.
+#
+# This replicates what Clang emits with -fdebug-types-section. The
+# compile unit contains an unnamed DW_TAG_class_type declaration with
+# only DW_AT_declaration, DW_AT_signature, and child member function
+# declarations.
+#
+# The class name must be resolved by following the signature to the
+# type unit. Without this, the member function definitions (which use
+# DW_AT_specification to point at the child declarations) end up with
+# no parent, and qualified lookup fails.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+require dwarf2_support
+
+standard_testfile main-foo.c .S
+
+# Build the test program using VERSION for the CU/TU DWARF version.
+proc run_test { version } {
+ # Create the DWARF.
+ set asm_file [standard_output_file $::srcfile2]
+ Dwarf::assemble {
+ filename $asm_file
+ add_dummy_cus 0
+ } {
+ upvar version version
+
+ get_func_info foo
+ get_func_info main
+
+ declare_labels method_decl
+
+ tu { version $version } 0xdeadbeef01234567 the_type {
+ DW_TAG_type_unit {
+ DW_AT_language @DW_LANG_C_plus_plus
+ } {
+ the_type: DW_TAG_class_type {
+ DW_AT_name the_type
+ DW_AT_byte_size 1 sdata
+ }
+ }
+ }
+
+ cu { version $version } {
+ compile_unit {
+ DW_AT_language @DW_LANG_C_plus_plus
+ } {
+ DW_TAG_class_type {
+ DW_AT_declaration 1 flag
+ DW_AT_signature 0xdeadbeef01234567 ref_sig8
+ } {
+ method_decl: DW_TAG_subprogram {
+ DW_AT_name method
+ DW_AT_linkage_name _ZN8the_type6methodEv
+ DW_AT_declaration 1 flag
+ }
+ }
+
+ DW_TAG_subprogram {
+ DW_AT_specification %$method_decl
+ DW_AT_low_pc $foo_start DW_FORM_addr
+ DW_AT_high_pc $foo_end DW_FORM_addr
+ }
+
+ DW_TAG_subprogram {
+ DW_AT_name main
+ DW_AT_low_pc $main_start DW_FORM_addr
+ DW_AT_high_pc $main_end DW_FORM_addr
+ }
+ }
+ }
+ }
+
+ if { [prepare_for_testing "failed to prepare" ${::testfile}-${version} \
+ [list $asm_file $::srcfile] {nodebug}] } {
+ return
+ }
+
+ # Check that GDB was able to find the parent for "method", and as
+ # a result, can correctly find this field of "the_type".
+ gdb_test "print the_type::method" \
+ [string_to_regexp " <the_type::method()>"]
+}
+
+foreach_with_prefix version { 4 5 } {
+ run_test $version
+}
base-commit: 6e3ecea0e3ca191e81e82ee0194c49eea1ffb101
--
2.25.4
prev parent reply other threads:[~2026-09-16 11:39 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-19 10:03 [PATCH] [GDB 18] " Andrew Burgess
2026-08-21 17:07 ` Tom Tromey
2026-08-28 21:30 ` [PATCHv2] " Andrew Burgess
2026-09-01 13:31 ` [PATCHv3] " Andrew Burgess
2026-09-10 16:11 ` Simon Marchi
2026-09-11 19:18 ` Tom Tromey
2026-09-12 2:06 ` Simon Marchi
2026-09-14 13:23 ` Andrew Burgess
2026-09-14 14:57 ` Simon Marchi
2026-09-14 15:38 ` Tom Tromey
2026-09-15 10:25 ` Andrew Burgess
2026-09-15 15:01 ` Tom Tromey
2026-09-15 15:55 ` Simon Marchi
2026-09-15 17:21 ` Simon Marchi
2026-09-16 11:40 ` Andrew Burgess
2026-09-16 11:45 ` Andrew Burgess
2026-09-16 11:48 ` Andrew Burgess
2026-09-14 15:36 ` Tom Tromey
2026-09-10 16:18 ` Simon Marchi
2026-09-16 11:38 ` Andrew Burgess [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=e78a75720042dba3d3b052883d2d84d374ffc157.1789555833.git.aburgess@redhat.com \
--to=aburgess@redhat.com \
--cc=gdb-patches@sourceware.org \
--cc=simark@simark.ca \
--cc=tom@tromey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox