Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH v4 0/8] Correctly handle inline functions with dwz
@ 2026-02-24  1:16 Tom Tromey
  2026-02-24  1:16 ` [PATCH v4 1/8] Don't call add_dependence from index_imported_unit Tom Tromey
                   ` (9 more replies)
  0 siblings, 10 replies; 14+ messages in thread
From: Tom Tromey @ 2026-02-24  1:16 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey, Eli Zaretskii

The new indexer does not correctly handle inline functions when 'dwz'
is used to compress the DWARF.  This series fixes the bug, cleaning up
a number of other things on the way.

I've separately regression tested each patch in this series on x86-64
Fedora 41.  I've also regression tested the series as a whole with the
dwz, gdb-index, and debug-names boards.

Even with that I messed up somehow, so v1 didn't actually fix the bug
in question.  I must have modified the patches after testing..?

Anyway in v2 I've moved the line recording the CU inclusion and added
a comment explaining the placement.  I re-ran the aformentioned tests
and didn't touch anything.

Signed-off-by: Tom Tromey <tom@tromey.com>
---
Changes in v4:
- Update assert in .debug_names writer, and add regression test
- Link to v3: https://inbox.sourceware.org/gdb-patches/20260220-dw-inline-fixup-pr-symtab-30728-2-v3-0-98ae8ab28fab@tromey.com

Changes in v3:
- Minor updates per review
- Rebased; which required some changes to the
  iterate_over_one_compunit_symtab patch
- Link to v2: https://inbox.sourceware.org/gdb-patches/20260126-dw-inline-fixup-pr-symtab-30728-2-v2-0-8ab183d1911c@tromey.com

Changes in v2:
- Fixed fix from Tom de Vries for IS_INLINED debug display
- Moved line to record inclusion
- Link to v1: https://inbox.sourceware.org/gdb-patches/20260125-dw-inline-fixup-pr-symtab-30728-2-v1-0-e9973a4a401a@tromey.com

---
Tom Tromey (8):
      Don't call add_dependence from index_imported_unit
      Skip partial units in process_psymtab_comp_unit
      Don't consider DW_TAG_inlined_subroutine as interesting
      Combine two cases in cooked_index_functions::search
      Remove C++ special case from process_imported_unit_die
      Have iterate_over_one_compunit_symtab search included symtabs
      Handle inline functions with dwz
      Update .debug_names documentation

 gdb/doc/gdb.texinfo                      |  37 +++++
 gdb/dwarf2/abbrev.c                      |   1 -
 gdb/dwarf2/cooked-index-entry.c          |  24 ++++
 gdb/dwarf2/cooked-index-entry.h          |  39 +++++-
 gdb/dwarf2/cooked-index-worker.c         |  14 ++
 gdb/dwarf2/cooked-index-worker.h         |  26 ++--
 gdb/dwarf2/cooked-indexer.c              |  60 ++++++---
 gdb/dwarf2/cooked-indexer.h              |   8 +-
 gdb/dwarf2/cu.h                          |   6 +-
 gdb/dwarf2/index-write.c                 | 224 ++++++++++++++++++-------------
 gdb/dwarf2/read.c                        |  85 +++++++-----
 gdb/dwarf2/read.h                        |  37 +++++
 gdb/symfile-debug.c                      |  23 ++--
 gdb/testsuite/gdb.cp/breakpoint-locs.exp |   8 ++
 14 files changed, 415 insertions(+), 177 deletions(-)
---
base-commit: 2a2b24ecc13988a6d87619f53b36faa2bcb61beb
change-id: 20260125-dw-inline-fixup-pr-symtab-30728-2-7c31bf38fbe3

Best regards,
-- 
Tom Tromey <tom@tromey.com>


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v4 1/8] Don't call add_dependence from index_imported_unit
  2026-02-24  1:16 [PATCH v4 0/8] Correctly handle inline functions with dwz Tom Tromey
@ 2026-02-24  1:16 ` Tom Tromey
  2026-02-24  1:16 ` [PATCH v4 2/8] Skip partial units in process_psymtab_comp_unit Tom Tromey
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Tom Tromey @ 2026-02-24  1:16 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

cooked_indexer::index_imported_unit calls add_dependence.  However,
this field is only needed during full symbol reading.  Remove this
call.
---
 gdb/dwarf2/cooked-indexer.c | 6 +-----
 gdb/dwarf2/cu.h             | 6 +++---
 2 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/gdb/dwarf2/cooked-indexer.c b/gdb/dwarf2/cooked-indexer.c
index 40f62de80eb..3221ccd34f8 100644
--- a/gdb/dwarf2/cooked-indexer.c
+++ b/gdb/dwarf2/cooked-indexer.c
@@ -430,11 +430,7 @@ cooked_indexer::index_imported_unit (cutu_reader *reader,
   cutu_reader *new_reader
     = ensure_cu_exists (reader, *target, true);
   if (new_reader != nullptr)
-    {
-      index_dies (new_reader, new_reader->info_ptr (), nullptr, false);
-
-      reader->cu ()->add_dependence (new_reader->cu ()->per_cu);
-    }
+    index_dies (new_reader, new_reader->info_ptr (), nullptr, false);
 
   return info_ptr;
 }
diff --git a/gdb/dwarf2/cu.h b/gdb/dwarf2/cu.h
index 1361898accc..8ece6ddc010 100644
--- a/gdb/dwarf2/cu.h
+++ b/gdb/dwarf2/cu.h
@@ -300,9 +300,9 @@ struct dwarf2_cu
      symbols are being read.  */
   buildsym_compunit_up m_builder;
 
-  /* A set of pointers to dwarf2_per_cu objects for compilation units referenced
-     by this one.  Only used during full symbol processing; partial symbol
-     tables do not have dependencies.  */
+  /* A set of pointers to dwarf2_per_cu objects for compilation units
+     referenced by this one.  Only used during full symbol
+     processing.  */
   gdb::unordered_set<dwarf2_per_cu *> m_dependencies;
 
 public:

-- 
2.49.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v4 2/8] Skip partial units in process_psymtab_comp_unit
  2026-02-24  1:16 [PATCH v4 0/8] Correctly handle inline functions with dwz Tom Tromey
  2026-02-24  1:16 ` [PATCH v4 1/8] Don't call add_dependence from index_imported_unit Tom Tromey
@ 2026-02-24  1:16 ` Tom Tromey
  2026-02-24  1:16 ` [PATCH v4 3/8] Don't consider DW_TAG_inlined_subroutine as interesting Tom Tromey
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Tom Tromey @ 2026-02-24  1:16 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

process_psymtab_comp_unit passes 'false' for the 'skip_partial'
argument to the cutu_reader constructor, but then proceeds to skip
partial units.  This patch simplifies the code by passing 'true'
instead.
---
 gdb/dwarf2/read.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 302b137526f..2cbdd7c6dc8 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -3373,7 +3373,7 @@ cooked_index_worker_debug_info::process_unit
     {
       auto new_reader
 	= std::make_unique<cutu_reader> (*this_cu, *per_objfile, nullptr,
-					 false, std::nullopt,
+					 true, std::nullopt,
 					 storage->get_abbrev_table_cache ());
 
       if (new_reader->is_dummy ())
@@ -3387,7 +3387,7 @@ cooked_index_worker_debug_info::process_unit
 
   if (this_cu->is_debug_types ())
     process_type_unit (reader, storage);
-  else if (reader->top_level_die ()->tag != DW_TAG_partial_unit)
+  else
     {
       bool nope = false;
       if (this_cu->scanned.compare_exchange_strong (nope, true))

-- 
2.49.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v4 3/8] Don't consider DW_TAG_inlined_subroutine as interesting
  2026-02-24  1:16 [PATCH v4 0/8] Correctly handle inline functions with dwz Tom Tromey
  2026-02-24  1:16 ` [PATCH v4 1/8] Don't call add_dependence from index_imported_unit Tom Tromey
  2026-02-24  1:16 ` [PATCH v4 2/8] Skip partial units in process_psymtab_comp_unit Tom Tromey
@ 2026-02-24  1:16 ` Tom Tromey
  2026-02-24  1:16 ` [PATCH v4 4/8] Combine two cases in cooked_index_functions::search Tom Tromey
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Tom Tromey @ 2026-02-24  1:16 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

tag_interesting_for_index returns true for DW_TAG_inlined_subroutine
-- but this tag only appears where the function is actually inlined,
which is not interesting for the index.
---
 gdb/dwarf2/abbrev.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/gdb/dwarf2/abbrev.c b/gdb/dwarf2/abbrev.c
index 49564db275b..44d5c87a5f7 100644
--- a/gdb/dwarf2/abbrev.c
+++ b/gdb/dwarf2/abbrev.c
@@ -46,7 +46,6 @@ tag_interesting_for_index (dwarf_tag tag)
     case DW_TAG_enumerator:
     case DW_TAG_imported_declaration:
     case DW_TAG_imported_unit:
-    case DW_TAG_inlined_subroutine:
     case DW_TAG_interface_type:
     case DW_TAG_module:
     case DW_TAG_namespace:

-- 
2.49.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v4 4/8] Combine two cases in cooked_index_functions::search
  2026-02-24  1:16 [PATCH v4 0/8] Correctly handle inline functions with dwz Tom Tromey
                   ` (2 preceding siblings ...)
  2026-02-24  1:16 ` [PATCH v4 3/8] Don't consider DW_TAG_inlined_subroutine as interesting Tom Tromey
@ 2026-02-24  1:16 ` Tom Tromey
  2026-02-24  1:16 ` [PATCH v4 5/8] Remove C++ special case from process_imported_unit_die Tom Tromey
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Tom Tromey @ 2026-02-24  1:16 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

This combines a couple of 'if' statements in
cooked_index_functions::search.  This simplifies the code a little and
also makes a subsequent patch a bit simpler as well.
---
 gdb/dwarf2/read.c | 27 ++++++++++++---------------
 1 file changed, 12 insertions(+), 15 deletions(-)

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 2cbdd7c6dc8..5917e697aad 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -14192,25 +14192,22 @@ cooked_index_functions::search
 	      || !entry->matches (domain))
 	    continue;
 
-	  if (lang_matcher != nullptr)
+	  /* If LANG_MATCHER is non-NULL, try to skip CUs with a
+	     non-matching language.  The other case here is a bit of a
+	     hack to support .gdb_index.  Since .gdb_index does not
+	     record languages, and since we want to know the language
+	     to avoid excessive CU expansion due to false matches, if
+	     we see a symbol with an unknown language we find the CU's
+	     language.  Only the .gdb_index reader creates such
+	     symbols.  */
+	  enum language entry_lang = entry->lang;
+	  if (lang_matcher != nullptr || entry_lang == language_unknown)
 	    {
-	      /* Try to skip CUs with non-matching language.  */
 	      entry->per_cu->ensure_lang (per_objfile);
-	      if (!entry->per_cu->maybe_multi_language ()
+	      if (lang_matcher != nullptr
+		  && !entry->per_cu->maybe_multi_language ()
 		  && !lang_matcher (entry->per_cu->lang ()))
 		continue;
-	    }
-
-	  /* This is a bit of a hack to support .gdb_index.  Since
-	     .gdb_index does not record languages, and since we want
-	     to know the language to avoid excessive CU expansion due
-	     to false matches, if we see a symbol with an unknown
-	     language we find the CU's language.  Only the .gdb_index
-	     reader creates such symbols.  */
-	  enum language entry_lang = entry->lang;
-	  if (entry_lang == language_unknown)
-	    {
-	      entry->per_cu->ensure_lang (per_objfile);
 	      entry_lang = entry->per_cu->lang ();
 	    }
 

-- 
2.49.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v4 5/8] Remove C++ special case from process_imported_unit_die
  2026-02-24  1:16 [PATCH v4 0/8] Correctly handle inline functions with dwz Tom Tromey
                   ` (3 preceding siblings ...)
  2026-02-24  1:16 ` [PATCH v4 4/8] Combine two cases in cooked_index_functions::search Tom Tromey
@ 2026-02-24  1:16 ` Tom Tromey
  2026-02-24  9:18   ` Tom de Vries
  2026-02-24  1:16 ` [PATCH v4 6/8] Have iterate_over_one_compunit_symtab search included symtabs Tom Tromey
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 14+ messages in thread
From: Tom Tromey @ 2026-02-24  1:16 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

process_imported_unit_die has a special case for C++, added as a
performance improvement.

While I somewhat agree with the general idea of this snippet --
importing a compilation unit seems like a strange thing to do, given
that partial units exist -- I think there are two issues with it.

First, it is specific to C++.  I don't see any real reason that this
should be the case.

Second, it does not have a corresponding bit of code in the indexer.
This means that the cooked index's view of the DWARF differs from the
full reader's view here.  This causes regressions in this series,
because the indexer assumes that reading a CU will cause all the
imported CUs to be read as a side effect -- but that does not happen
here.

I think fixing this in the indexer is not trivial.  The reason for
this is that the indexer works in parallel, and once a reader has
acquired the "scan" bit for a CU, it is obligated to read it.
However, in this case this would require making a new cooked indexer.

Instead, because (1) this is weird and rare DWARF anyway, and (2) this
is just a performance optimization, I propose removing this.
---
 gdb/dwarf2/read.c | 9 ---------
 1 file changed, 9 deletions(-)

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 5917e697aad..e48745d7969 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -4890,15 +4890,6 @@ process_imported_unit_die (struct die_info *die, struct dwarf2_cu *cu)
       dwarf2_per_cu *per_cu
 	= dwarf2_find_containing_unit ({ &section, sect_off }, per_objfile);
 
-      /* We're importing a C++ compilation unit with tag DW_TAG_compile_unit
-	 into another compilation unit, at root level.  Regard this as a hint,
-	 and ignore it.  This is a best effort, it only works if unit_type and
-	 lang are already set.  */
-      if (die->parent && die->parent->parent == NULL
-	  && per_cu->unit_type (false) == DW_UT_compile
-	  && per_cu->lang (false) == language_cplus)
-	return;
-
       /* If necessary, add it to the queue and load its DIEs.  */
       if (maybe_queue_comp_unit (cu, per_cu, per_objfile))
 	load_full_comp_unit (per_cu, per_objfile, false, cu->lang ());

-- 
2.49.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v4 6/8] Have iterate_over_one_compunit_symtab search included symtabs
  2026-02-24  1:16 [PATCH v4 0/8] Correctly handle inline functions with dwz Tom Tromey
                   ` (4 preceding siblings ...)
  2026-02-24  1:16 ` [PATCH v4 5/8] Remove C++ special case from process_imported_unit_die Tom Tromey
@ 2026-02-24  1:16 ` Tom Tromey
  2026-02-24  1:17 ` [PATCH v4 7/8] Handle inline functions with dwz Tom Tromey
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Tom Tromey @ 2026-02-24  1:16 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

A latent bug in the search-via-psyms series was that it neglected to
update iterate_over_one_compunit_symtab to search included symtabs.

I think lookups that needed this search used to work by accident -- an
included CU would be expanded but not searched, but a search of all
compunits() would then find it.

This patch corrects the oversight.  I'm not sure if this bug is
readily visible without the next patch.
---
 gdb/symfile-debug.c | 23 ++++++++++++++---------
 1 file changed, 14 insertions(+), 9 deletions(-)

diff --git a/gdb/symfile-debug.c b/gdb/symfile-debug.c
index 6562dcad433..2c9deba20ae 100644
--- a/gdb/symfile-debug.c
+++ b/gdb/symfile-debug.c
@@ -171,17 +171,12 @@ objfile::forget_cached_source_info ()
    the specified compunit symtab is also searched.  */
 
 static bool
-iterate_over_one_compunit_symtab (const char *name,
+iterate_over_one_compunit_symtab (const char *base_name,
+				  const char *name,
 				  const char *real_path,
 				  compunit_symtab *cust,
 				  gdb::function_view<bool (symtab *)> callback)
 {
-  const char *base_name = lbasename (name);
-
-  /* Skip included compunits.  */
-  if (cust->user != nullptr)
-    return false;
-
   for (symtab *s : cust->filetabs ())
     {
       if (compare_filenames_for_search (s->filename (), name))
@@ -224,6 +219,11 @@ iterate_over_one_compunit_symtab (const char *name,
 	}
     }
 
+  for (compunit_symtab *iter : cust->includes)
+    if (iterate_over_one_compunit_symtab (base_name, name, real_path,
+					  iter, callback))
+      return true;
+
   return false;
 }
 
@@ -257,10 +257,15 @@ objfile::map_symtabs_matching_filename
 
   auto listener = [&] (compunit_symtab *symtab)
   {
+    /* Skip included compunits, as they are searched by
+       iterate_over_one_compunit_symtab.  */
+    if (symtab->user != nullptr)
+      return true;
+
     /* CALLBACK returns false to keep going and true to continue, so
        we have to invert the result here, for search.  */
-    return !iterate_over_one_compunit_symtab (name, real_path, symtab,
-					      callback);
+    return !iterate_over_one_compunit_symtab (name_basename, name, real_path,
+					      symtab, callback);
   };
 
   for (const auto &iter : qf)

-- 
2.49.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v4 7/8] Handle inline functions with dwz
  2026-02-24  1:16 [PATCH v4 0/8] Correctly handle inline functions with dwz Tom Tromey
                   ` (5 preceding siblings ...)
  2026-02-24  1:16 ` [PATCH v4 6/8] Have iterate_over_one_compunit_symtab search included symtabs Tom Tromey
@ 2026-02-24  1:17 ` Tom Tromey
  2026-02-24  1:17 ` [PATCH v4 8/8] Update .debug_names documentation Tom Tromey
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Tom Tromey @ 2026-02-24  1:17 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey

Currently, gdb does not properly handle inline functions when dwz is
used.  This can be seen by running gdb.cp/breakpoint-locs.exp with the
cc-with-dwz target board.

The problem here is that inline functions need special handling in the
dwz case.

First, recall that a DWARF partial unit cannot, in general, be read in
isolation, as it may not have a language.  To handle this, gdb defers
scanning partial units directly, and instead scans them in the context
of some including CU.

Entries coming from the PU are attributed to this reading CU.  If
multiple CUs import a PU, gdb has the reader threads race to see which
one does the actual reading.

However, if an inline function is moved into a partial unit, then that
means it has potentially been inlined somewhere in every including CU.

Thus, when linespec searches for this function, each such including CU
should be expanded.  But because gdb only attributed the function's
index entry to one CU, only that particular one is expanded.

This patch fixes this bug.  All inclusions of a PU are recorded.
Entries coming from a PU are attributed to that PU.  For most entries
coming from the PU, a single "canonical" outer CU is chosen to expand.
However, when an inline function is found, all such CUs are expanded.

A change was also needed to the index writer to handle this case.
There, entries coming from a PU should note the correct including CU.
This must be done because, with .debug_names or .gdb_index, gdb does
not have information about unit imports.  Handling inline functions
here means writing out a separate entry for each outermost CU that
includes the function's PU.

I did consider changing the cooked indexer to create an internal table
more similar to what would be created by the .debug_names (e.g.)
reader: that is, multiple entries for each inline function.  However,
this seemed more expensive at read time, and a main goal of the cooked
indexer is to be fast.

This version updates an assert in the .debug_names writer, and adds a
regression test for that.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=30728
---
 gdb/dwarf2/cooked-index-entry.c          |  24 ++++
 gdb/dwarf2/cooked-index-entry.h          |  39 +++++-
 gdb/dwarf2/cooked-index-worker.c         |  14 ++
 gdb/dwarf2/cooked-index-worker.h         |  26 ++--
 gdb/dwarf2/cooked-indexer.c              |  54 ++++++--
 gdb/dwarf2/cooked-indexer.h              |   8 +-
 gdb/dwarf2/index-write.c                 | 224 ++++++++++++++++++-------------
 gdb/dwarf2/read.c                        |  49 +++++--
 gdb/dwarf2/read.h                        |  37 +++++
 gdb/testsuite/gdb.cp/breakpoint-locs.exp |   8 ++
 10 files changed, 348 insertions(+), 135 deletions(-)

diff --git a/gdb/dwarf2/cooked-index-entry.c b/gdb/dwarf2/cooked-index-entry.c
index c24dcf445fe..2f61e5c4c57 100644
--- a/gdb/dwarf2/cooked-index-entry.c
+++ b/gdb/dwarf2/cooked-index-entry.c
@@ -18,6 +18,7 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "dwarf2/cooked-index-entry.h"
+#include "dwarf2/read.h"
 #include "dwarf2/tag.h"
 #include "gdbsupport/selftest.h"
 
@@ -33,6 +34,7 @@ to_string (cooked_index_flag flags)
     MAP_ENUM_FLAG (IS_TYPE_DECLARATION),
     MAP_ENUM_FLAG (IS_PARENT_DEFERRED),
     MAP_ENUM_FLAG (IS_SYNTHESIZED),
+    MAP_ENUM_FLAG (IS_INLINED),
   };
 
   return flags.to_string (mapping);
@@ -240,6 +242,28 @@ cooked_index_entry::write_scope (struct obstack *storage,
   obstack_grow (storage, sep, strlen (sep));
 }
 
+/* See cooked_index_entry.h.  */
+
+void
+cooked_index_entry::force_set_language () const
+{
+  gdb_assert (lang == language_unknown);
+  lang = per_cu->lang ();
+}
+
+/* See cooked-index-entry.h.  */
+
+bool
+cooked_index_entry::visit_defining_cus (per_cu_callback callback) const
+{
+  if ((flags & IS_INLINED) == 0)
+    return callback (per_cu->canonical_outermost_cu ());
+
+  return per_cu->recursively_visit_cus (callback);
+}
+
+/* See cooked-index-entry.h.  */
+
 INIT_GDB_FILE (dwarf2_entry)
 {
 #if GDB_SELF_TEST
diff --git a/gdb/dwarf2/cooked-index-entry.h b/gdb/dwarf2/cooked-index-entry.h
index a64b2674056..9eed2ca9c46 100644
--- a/gdb/dwarf2/cooked-index-entry.h
+++ b/gdb/dwarf2/cooked-index-entry.h
@@ -44,6 +44,9 @@ enum cooked_index_flag_enum : unsigned char
   /* True if this entry was synthesized by gdb (as opposed to coming
      directly from the DWARF).  */
   IS_SYNTHESIZED = 32,
+  /* True if this is a function that has DW_AT_inline set in a way
+     that indicates it was inlined.  */
+  IS_INLINED = 64,
 };
 DEF_ENUM_FLAGS_TYPE (enum cooked_index_flag_enum, cooked_index_flag);
 
@@ -222,6 +225,31 @@ struct cooked_index_entry : public allocate_on_obstack<cooked_index_entry>
     return m_parent_entry.deferred;
   }
 
+  /* Force the language to be set to the CU's language.  This may only
+     be called when the language is unknown, which can only happen
+     with .gdb_index.  This method is const because it is called from
+     a location that normally handles const entries.  */
+  void force_set_language () const;
+
+  /* Type of callback used when visiting defining CUs.  */
+  using per_cu_callback = gdb::function_view<bool (dwarf2_per_cu *)>;
+
+  /* Calls CALLBACK for each CU that "defines" this entry.
+
+     If any call to CALLBACK returns false, this immediately returns
+     false (skipping the remaining calls); otherwise returns true.
+
+     For most entries, there is a single defining CU, which is the
+     canonical outermost includer of the CU holding the entry.  In the
+     most typical case (i.e., where "dwz" has not been used), this is
+     just the entry's CU.
+
+     However, conceptually an inlined subroutine is defined in each
+     such outermost includer -- if a subroutine is inlined in many
+     places, and then "dwz" is run, the IS_INLINED subroutine may be
+     defined in some CU that is included by many other CUs.  */
+  bool 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
@@ -233,11 +261,16 @@ struct cooked_index_entry : public allocate_on_obstack<cooked_index_entry>
   enum dwarf_tag tag;
   /* Any flags attached to this entry.  */
   cooked_index_flag flags;
-  /* The language of this symbol.  */
-  ENUM_BITFIELD (language) lang : LANGUAGE_BITS;
+  /* The language of this symbol.  This is mutable because there is a
+     situation where the language is initially unknown, and then only
+     filled in later.  In particular this can happen when using
+     .gdb_index.  See also cooked_index_functions::search.  */
+  mutable ENUM_BITFIELD (language) lang : LANGUAGE_BITS;
   /* The offset of this DIE.  */
   sect_offset die_offset;
-  /* The CU from which this entry originates.  */
+  /* The CU from which this entry originates.  This may point to a
+     partial CU, which can't be expanded in isolation; see
+     visit_defining_cus.  */
   dwarf2_per_cu *per_cu;
 
 private:
diff --git a/gdb/dwarf2/cooked-index-worker.c b/gdb/dwarf2/cooked-index-worker.c
index ef2427d0a71..723e027172e 100644
--- a/gdb/dwarf2/cooked-index-worker.c
+++ b/gdb/dwarf2/cooked-index-worker.c
@@ -105,6 +105,16 @@ cooked_index_worker_result::emit_complaints_and_exceptions
 
 /* See cooked-index-worker.h.  */
 
+void
+cooked_index_worker_result::invert_cu_inclusions ()
+{
+  for (auto &iter : m_inclusions)
+    for (dwarf2_per_cu *included : iter.second)
+      included->add_includer (iter.first);
+}
+
+/* See cooked-index-worker.h.  */
+
 void
 cooked_index_worker::start ()
 {
@@ -239,6 +249,10 @@ cooked_index_worker::done_reading ()
       m_all_parents_map.add_map (*one_result.get_parent_map ());
   }
 
+  /* Update all the CU inclusion information.  */
+  for (auto &item : m_results)
+    item.invert_cu_inclusions ();
+
   dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd;
   cooked_index *table
     = (gdb::checked_static_cast<cooked_index *>
diff --git a/gdb/dwarf2/cooked-index-worker.h b/gdb/dwarf2/cooked-index-worker.h
index d46415bf8bc..1799c0b9e08 100644
--- a/gdb/dwarf2/cooked-index-worker.h
+++ b/gdb/dwarf2/cooked-index-worker.h
@@ -66,17 +66,6 @@ class cooked_index_worker_result
 
   /* Add an entry to the index.  The arguments describe the entry; see
      cooked-index.h.  The new entry is returned.  */
-  cooked_index_entry *add (sect_offset die_offset, enum dwarf_tag tag,
-			   cooked_index_flag flags,
-			   const char *name,
-			   cooked_index_entry_ref parent_entry,
-			   dwarf2_per_cu *per_cu)
-  {
-    return m_shard->add (die_offset, tag, flags, per_cu->lang (),
-			 name, parent_entry, per_cu);
-  }
-
-  /* Overload that allows the language to be specified.  */
   cooked_index_entry *add (sect_offset die_offset, enum dwarf_tag tag,
 			   cooked_index_flag flags, enum language lang,
 			   const char *name,
@@ -154,6 +143,16 @@ class cooked_index_worker_result
   void emit_complaints_and_exceptions
        (gdb::unordered_set<gdb_exception> &seen_exceptions);
 
+  /* Record that the FROM CU included the TO CU.  */
+  void record_inclusion (dwarf2_per_cu *from, dwarf2_per_cu *to)
+  {
+    m_inclusions[from].push_back (to);
+  }
+
+  /* Update CUs with all the inclusion information that we've
+     discovered.  */
+  void invert_cu_inclusions ();
+
 private:
   /* The abbrev table cache used by this indexer.  */
   abbrev_table_cache m_abbrev_table_cache;
@@ -198,6 +197,11 @@ class cooked_index_worker_result
 
   /* Exceptions that we're storing to emit later.  */
   std::vector<gdb_exception> m_exceptions;
+
+  /* Map from a CU to a list of all the CUs that it directly
+     includes.  */
+  gdb::unordered_map<dwarf2_per_cu *,
+		     std::vector<dwarf2_per_cu *>> m_inclusions;
 };
 
 /* The possible states of the index.  See the explanatory comment
diff --git a/gdb/dwarf2/cooked-indexer.c b/gdb/dwarf2/cooked-indexer.c
index 3221ccd34f8..26be0aff7bf 100644
--- a/gdb/dwarf2/cooked-indexer.c
+++ b/gdb/dwarf2/cooked-indexer.c
@@ -27,9 +27,8 @@
 /* See cooked-indexer.h.  */
 
 cooked_indexer::cooked_indexer (cooked_index_worker_result *storage,
-				dwarf2_per_cu *per_cu, enum language language)
+				enum language language)
   : m_index_storage (storage),
-    m_per_cu (per_cu),
     m_language (language),
     m_die_range_map (storage->get_parent_map ())
 {
@@ -102,6 +101,17 @@ cooked_indexer::ensure_cu_exists (cutu_reader *reader,
      Doing this check here avoids self-imports as well.  */
   if (for_scanning)
     {
+      /* Record the inclusion precisely here.  Inclusions are done to
+	 ensure that imported units cause the correct expansions when
+	 searched; and in particular that all including units are
+	 expanded when finding an inline function.  So, this must be
+	 done for all units that are importing "for scanning".
+	 However, doing this any later than this spot would be too
+	 late -- because we want to do this for all importing units,
+	 not just the one that happens to win the race to do the
+	 scanning.  */
+      m_index_storage->record_inclusion (reader->cu ()->per_cu, per_cu);
+
       bool nope = false;
       if (!per_cu->scanned.compare_exchange_strong (nope, true))
 	return nullptr;
@@ -146,6 +156,7 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu,
 				 const cooked_index_entry **parent_entry,
 				 parent_map::addr_type *maybe_defer,
 				 bool *is_enum_class,
+				 bool *is_inlined,
 				 bool for_specification)
 {
   bool is_declaration = false;
@@ -281,6 +292,16 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu,
 				  scanning_per_cu, abbrev->tag);
 	    }
 	  break;
+
+	case DW_AT_inline:
+	  if (abbrev->tag == DW_TAG_subprogram)
+	    {
+	      std::optional<ULONGEST> val = attr.unsigned_constant ();
+	      if (val.has_value () && (*val == DW_INL_inlined
+				       || *val == DW_INL_declared_inlined))
+		*is_inlined = true;
+	    }
+	  break;
 	}
     }
 
@@ -359,7 +380,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, true);
+			 is_enum_class, is_inlined, true);
     }
 
   if (!for_specification)
@@ -427,8 +448,7 @@ cooked_indexer::index_imported_unit (cutu_reader *reader,
   if (!target.has_value ())
     return info_ptr;
 
-  cutu_reader *new_reader
-    = ensure_cu_exists (reader, *target, true);
+  cutu_reader *new_reader = ensure_cu_exists (reader, *target, true);
   if (new_reader != nullptr)
     index_dies (new_reader, new_reader->info_ptr (), nullptr, false);
 
@@ -526,6 +546,7 @@ cooked_indexer::index_dies (cutu_reader *reader,
       sect_offset sibling {};
       const cooked_index_entry *this_parent_entry = parent_entry;
       bool is_enum_class = false;
+      bool is_inlined = false;
 
       /* The scope of a DW_TAG_entry_point cooked_index_entry is the one of
 	 its surrounding subroutine.  */
@@ -534,7 +555,8 @@ cooked_indexer::index_dies (cutu_reader *reader,
       info_ptr
 	= scan_attributes (reader->cu ()->per_cu, reader, info_ptr, info_ptr,
 			   abbrev, &name, &linkage_name, &flags, &sibling,
-			   &this_parent_entry, &defer, &is_enum_class, false);
+			   &this_parent_entry, &defer, &is_enum_class,
+			   &is_inlined, false);
       /* A DW_TAG_entry_point inherits its static/extern property from
 	 the enclosing subroutine.  */
       if (abbrev->tag == DW_TAG_entry_point)
@@ -583,18 +605,25 @@ cooked_indexer::index_dies (cutu_reader *reader,
 	    }
 	}
 
+      if (is_inlined)
+	flags |= IS_INLINED;
+
       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 (defer != 0)
 	    this_entry
 	      = m_index_storage->add (this_die, abbrev->tag,
-				      flags | IS_PARENT_DEFERRED, name,
-				      defer, m_per_cu);
+				      flags | IS_PARENT_DEFERRED,
+				      m_language, name,
+				      defer, cu_for_entry);
 	  else
 	    this_entry
-	      = m_index_storage->add (this_die, abbrev->tag, flags, name,
-				      this_parent_entry, m_per_cu);
+	      = m_index_storage->add (this_die, abbrev->tag, flags,
+				      m_language, name,
+				      this_parent_entry, cu_for_entry);
 	}
 
       if (linkage_name != nullptr)
@@ -607,11 +636,10 @@ cooked_indexer::index_dies (cutu_reader *reader,
 	     have linkage name present but name is absent.  */
 	  if (name != nullptr
 	      || (abbrev->tag != DW_TAG_subprogram
-		  && abbrev->tag != DW_TAG_inlined_subroutine
 		  && abbrev->tag != DW_TAG_entry_point))
 	    flags = flags | IS_LINKAGE;
-	  m_index_storage->add (this_die, abbrev->tag, flags,
-				linkage_name, nullptr, m_per_cu);
+	  m_index_storage->add (this_die, abbrev->tag, flags, m_language,
+				linkage_name, nullptr, cu_for_entry);
 	}
 
       if (abbrev->has_children)
diff --git a/gdb/dwarf2/cooked-indexer.h b/gdb/dwarf2/cooked-indexer.h
index 5cb5bae33f1..563742c90f6 100644
--- a/gdb/dwarf2/cooked-indexer.h
+++ b/gdb/dwarf2/cooked-indexer.h
@@ -37,7 +37,7 @@ struct section_and_offset;
 class cooked_indexer
 {
 public:
-  cooked_indexer (cooked_index_worker_result *storage, dwarf2_per_cu *per_cu,
+  cooked_indexer (cooked_index_worker_result *storage,
 		  enum language language);
 
   DISABLE_COPY_AND_ASSIGN (cooked_indexer);
@@ -85,6 +85,7 @@ class cooked_indexer
 				   const cooked_index_entry **parent_entry,
 				   parent_map::addr_type *maybe_defer,
 				   bool *is_enum_class,
+				   bool *is_inlined,
 				   bool for_specification);
 
   /* Handle DW_TAG_imported_unit, by scanning the DIE to find
@@ -104,11 +105,6 @@ class cooked_indexer
 
   /* The storage object, where the results are kept.  */
   cooked_index_worker_result *m_index_storage;
-  /* The CU that we are reading on behalf of.  This object might be
-     asked to index one CU but to treat the results as if they come
-     from some including CU; in this case the including CU would be
-     recorded here.  */
-  dwarf2_per_cu *m_per_cu;
   /* The language that we're assuming when reading.  */
   enum language m_language;
 
diff --git a/gdb/dwarf2/index-write.c b/gdb/dwarf2/index-write.c
index c4e75898f4c..0e4bec11928 100644
--- a/gdb/dwarf2/index-write.c
+++ b/gdb/dwarf2/index-write.c
@@ -683,6 +683,10 @@ class debug_names
     /* The next available abbrev number.  */
     int next_abbrev = 1;
 
+    /* Storage for the CU list for inline entries.  This is outside
+       the loops to try to avoid repeated allocations.  */
+    std::vector<dwarf2_per_cu *> per_cus;
+
     for (auto &[name, these_entries] : m_name_to_value_set)
       {
 	/* Sort the items within each bucket.  This ensures that the
@@ -711,108 +715,137 @@ class debug_names
 
 	for (const cooked_index_entry *entry : these_entries)
 	  {
-	    unit_kind kind = (entry->per_cu->is_debug_types ()
-			      ? unit_kind::tu
-			      : unit_kind::cu);
-	    /* Some Ada parentage is synthesized by the reader and so
-	       must be ignored here.  */
-	    const cooked_index_entry *parent = entry->get_parent ();
-	    if (parent != nullptr && (parent->flags & IS_SYNTHESIZED) != 0)
-	      parent = nullptr;
-
-	    int &abbrev = m_indexkey_to_abbrev[index_key (entry->tag,
-							  kind,
-							  entry->flags,
-							  entry->lang,
-							  parent != nullptr)];
-	    if (abbrev == 0)
+	    /* Accumulate all the relevant CUs.  This is needed for
+	       the DW_TAG_inlined_subroutine special case.  */
+	    per_cus.clear ();
+	    entry->visit_defining_cus ([&] (dwarf2_per_cu *one_cu)
+	      {
+		per_cus.push_back (one_cu);
+		return true;
+	      });
+	    /* Make sure the output is stable.  */
+	    std::sort (per_cus.begin (), per_cus.end (),
+		       [] (const dwarf2_per_cu *lhs, const dwarf2_per_cu *rhs)
+		       {
+			 return lhs->index < rhs->index;
+		       });
+
+	    for (dwarf2_per_cu *one_cu : per_cus)
 	      {
-		abbrev = next_abbrev++;
+		unit_kind kind = (entry->per_cu->is_debug_types ()
+				  ? unit_kind::tu
+				  : unit_kind::cu);
+		/* Some Ada parentage is synthesized by the reader and so
+		   must be ignored here.  */
+		const cooked_index_entry *parent = entry->get_parent ();
+		if (parent != nullptr && (parent->flags & IS_SYNTHESIZED) != 0)
+		  parent = nullptr;
+
+		int &abbrev = m_indexkey_to_abbrev[index_key (entry->tag,
+							      kind,
+							      entry->flags,
+							      entry->lang,
+							      parent != nullptr)];
+		if (abbrev == 0)
+		  {
+		    abbrev = next_abbrev++;
+
+		    /* Abbrev number and tag.  */
+		    m_abbrev_table.append_unsigned_leb128 (abbrev);
+		    m_abbrev_table.append_unsigned_leb128 (entry->tag);
+
+		    /* Unit index.  */
+		    m_abbrev_table.append_unsigned_leb128
+		      (kind == unit_kind::cu
+		       ? DW_IDX_compile_unit
+		       : DW_IDX_type_unit);
+		    m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata);
+
+		    /* DIE offset.  */
+		    m_abbrev_table.append_unsigned_leb128 (DW_IDX_die_offset);
+		    m_abbrev_table.append_unsigned_leb128 (DW_FORM_ref_addr);
+
+		    /* Language.  */
+		    m_abbrev_table.append_unsigned_leb128
+		      (DW_IDX_GNU_language);
+		    m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata);
+
+		    /* Internal linkage flag.  */
+		    if (!tag_is_type (entry->tag)
+			&& (entry->flags & IS_STATIC) != 0)
+		      {
+			m_abbrev_table.append_unsigned_leb128
+			  (DW_IDX_GNU_internal);
+			m_abbrev_table.append_unsigned_leb128
+			  (DW_FORM_flag_present);
+		      }
+
+		    /* Main subprogram flag.  */
+		    if ((entry->flags & IS_MAIN) != 0)
+		      {
+			m_abbrev_table.append_unsigned_leb128
+			  (DW_IDX_GNU_main);
+			m_abbrev_table.append_unsigned_leb128
+			  (DW_FORM_flag_present);
+		      }
+
+		    /* Linkage name flag.  */
+		    if ((entry->flags & IS_LINKAGE) != 0)
+		      {
+			m_abbrev_table.append_unsigned_leb128
+			  (DW_IDX_GNU_linkage_name);
+			m_abbrev_table.append_unsigned_leb128
+			  (DW_FORM_flag_present);
+		      }
+
+		    /* Parent offset.  */
+		    if (parent != nullptr)
+		      {
+			m_abbrev_table.append_unsigned_leb128 (DW_IDX_parent);
+			m_abbrev_table.append_unsigned_leb128 (DW_FORM_data4);
+		      }
+
+		    /* Terminate attributes list.  */
+		    m_abbrev_table.append_unsigned_leb128 (0);
+		    m_abbrev_table.append_unsigned_leb128 (0);
+		  }
 
-		/* Abbrev number and tag.  */
-		m_abbrev_table.append_unsigned_leb128 (abbrev);
-		m_abbrev_table.append_unsigned_leb128 (entry->tag);
+		/* Record the offset in the pool at which this entry will
+		   reside.  */
+		const auto offset_inserted
+		  = (m_entry_pool_offsets.emplace (entry, m_entry_pool.size ())
+		     .second);
+		gdb_assert (offset_inserted
+			    || (entry->flags & IS_INLINED) != 0);
+
+		/* Write the entry to the pool, starting with the
+		   abbrev number.  */
+		m_entry_pool.append_unsigned_leb128 (abbrev);
 
 		/* Unit index.  */
-		m_abbrev_table.append_unsigned_leb128
-		  (kind == unit_kind::cu
-		   ? DW_IDX_compile_unit
-		   : DW_IDX_type_unit);
-		m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata);
+		const auto it = m_cu_index_htab.find (one_cu);
+		gdb_assert (it != m_cu_index_htab.cend ());
+		m_entry_pool.append_unsigned_leb128 (it->second);
 
 		/* DIE offset.  */
-		m_abbrev_table.append_unsigned_leb128 (DW_IDX_die_offset);
-		m_abbrev_table.append_unsigned_leb128 (DW_FORM_ref_addr);
+		m_entry_pool.append_uint (dwarf5_offset_size (),
+					  m_dwarf5_byte_order,
+					  to_underlying (entry->die_offset));
 
 		/* Language.  */
-		m_abbrev_table.append_unsigned_leb128 (DW_IDX_GNU_language);
-		m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata);
-
-		/* Internal linkage flag.  */
-		if (!tag_is_type (entry->tag)
-		    && (entry->flags & IS_STATIC) != 0)
-		  {
-		    m_abbrev_table.append_unsigned_leb128 (DW_IDX_GNU_internal);
-		    m_abbrev_table.append_unsigned_leb128 (DW_FORM_flag_present);
-		  }
-
-		/* Main subprogram flag.  */
-		if ((entry->flags & IS_MAIN) != 0)
-		  {
-		    m_abbrev_table.append_unsigned_leb128 (DW_IDX_GNU_main);
-		    m_abbrev_table.append_unsigned_leb128 (DW_FORM_flag_present);
-		  }
-
-		/* Linkage name flag.  */
-		if ((entry->flags & IS_LINKAGE) != 0)
-		  {
-		    m_abbrev_table.append_unsigned_leb128 (DW_IDX_GNU_linkage_name);
-		    m_abbrev_table.append_unsigned_leb128 (DW_FORM_flag_present);
-		  }
+		m_entry_pool.append_unsigned_leb128
+		  (entry->per_cu->dw_lang ());
 
 		/* Parent offset.  */
 		if (parent != nullptr)
 		  {
-		    m_abbrev_table.append_unsigned_leb128 (DW_IDX_parent);
-		    m_abbrev_table.append_unsigned_leb128 (DW_FORM_data4);
-		  }
-
-		/* Terminate attributes list.  */
-		m_abbrev_table.append_unsigned_leb128 (0);
-		m_abbrev_table.append_unsigned_leb128 (0);
-	      }
-
-	    /* Record the offset in the pool at which this entry will
-	       reside.  */
-	    const auto offset_inserted
-	      = (m_entry_pool_offsets.emplace (entry, m_entry_pool.size ())
-		 .second);
-	    gdb_assert (offset_inserted);
-
-	    /* Write the entry to the pool, starting with the abbrev number.  */
-	    m_entry_pool.append_unsigned_leb128 (abbrev);
-
-	    /* Unit index.  */
-	    const auto it = m_cu_index_htab.find (entry->per_cu);
-	    gdb_assert (it != m_cu_index_htab.cend ());
-	    m_entry_pool.append_unsigned_leb128 (it->second);
-
-	    /* DIE offset.  */
-	    m_entry_pool.append_uint (dwarf5_offset_size (),
-				      m_dwarf5_byte_order,
-				      to_underlying (entry->die_offset));
+		    m_offsets_to_patch.emplace_back
+		      (m_entry_pool.size (), parent);
 
-	    /* Language.  */
-	    m_entry_pool.append_unsigned_leb128 (entry->per_cu->dw_lang ());
-
-	    /* Parent offset.  */
-	    if (parent != nullptr)
-	      {
-		m_offsets_to_patch.emplace_back (m_entry_pool.size (), parent);
-
-		/* Write a dummy number, this gets patched later.  */
-		m_entry_pool.append_uint (4, m_dwarf5_byte_order,
-					  0xfafafafa);
+		    /* Write a dummy number, this gets patched later.  */
+		    m_entry_pool.append_uint (4, m_dwarf5_byte_order,
+					      0xfafafafa);
+		  }
 	      }
 	  }
 
@@ -1239,9 +1272,6 @@ write_cooked_index (cooked_index *table,
 
   for (const cooked_index_entry *entry : table->all_entries ())
     {
-      const auto it = cu_index_htab.find (entry->per_cu);
-      gdb_assert (it != cu_index_htab.cend ());
-
       const char *name = entry->full_name (symtab->obstack ());
 
       if (entry->lang == language_ada)
@@ -1281,8 +1311,14 @@ write_cooked_index (cooked_index *table,
       else
 	kind = GDB_INDEX_SYMBOL_KIND_OTHER;
 
-      symtab->add_index_entry (name, (entry->flags & IS_STATIC) != 0,
-			       kind, it->second);
+      entry->visit_defining_cus ([&] (dwarf2_per_cu *per_cu)
+	{
+	  const auto it = cu_index_htab.find (per_cu);
+	  gdb_assert (it != cu_index_htab.cend ());
+	  symtab->add_index_entry (name, (entry->flags & IS_STATIC) != 0,
+				   kind, it->second);
+	  return true;
+	});
     }
 }
 
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index e48745d7969..504c8422e04 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -1642,6 +1642,11 @@ static struct compunit_symtab *
 dw2_instantiate_symtab (dwarf2_per_cu *per_cu, dwarf2_per_objfile *per_objfile,
 			bool skip_partial)
 {
+  /* Expand the corresponding canonical outermost CU.  This will
+     expand the desired CU as a side effect when following the
+     DW_TAG_imported_unit.  */
+  per_cu = per_cu->canonical_outermost_cu ();
+
   if (!per_objfile->symtab_set_p (per_cu))
     {
       free_cached_comp_units freer (per_objfile);
@@ -3393,7 +3398,7 @@ cooked_index_worker_debug_info::process_unit
       if (this_cu->scanned.compare_exchange_strong (nope, true))
 	{
 	  gdb_assert (storage != nullptr);
-	  cooked_indexer indexer (storage, this_cu, reader->cu ()->lang ());
+	  cooked_indexer indexer (storage, reader->cu ()->lang ());
 	  indexer.make_index (reader);
 	}
     }
@@ -3413,7 +3418,7 @@ cooked_index_worker_debug_info::process_type_unit
     return;
 
   gdb_assert (storage != nullptr);
-  cooked_indexer indexer (storage, per_cu, cu->lang ());
+  cooked_indexer indexer (storage, cu->lang ());
   indexer.make_index (reader);
 }
 
@@ -14191,15 +14196,15 @@ cooked_index_functions::search
 	     we see a symbol with an unknown language we find the CU's
 	     language.  Only the .gdb_index reader creates such
 	     symbols.  */
-	  enum language entry_lang = entry->lang;
-	  if (lang_matcher != nullptr || entry_lang == language_unknown)
+	  if (lang_matcher != nullptr || entry->lang == language_unknown)
 	    {
 	      entry->per_cu->ensure_lang (per_objfile);
 	      if (lang_matcher != nullptr
 		  && !entry->per_cu->maybe_multi_language ()
 		  && !lang_matcher (entry->per_cu->lang ()))
 		continue;
-	      entry_lang = entry->per_cu->lang ();
+	      else if (entry->lang == language_unknown)
+		entry->force_set_language ();
 	    }
 
 	  /* We've found the base name of the symbol; now walk its
@@ -14208,7 +14213,7 @@ cooked_index_functions::search
 	  bool found = true;
 
 	  const cooked_index_entry *parent = entry->get_parent ();
-	  const language_defn *lang_def = language_def (entry_lang);
+	  const language_defn *lang_def = language_def (entry->lang);
 	  for (int i = name_vec.size () - 1; i > 0; --i)
 	    {
 	      /* If we ran out of entries, or if this segment doesn't
@@ -14264,8 +14269,12 @@ cooked_index_functions::search
 	  else if (!symbol_matcher (full_name))
 	    continue;
 
-	  if (!dw2_search_one (entry->per_cu, per_objfile, cus_to_skip,
-			       file_matcher, listener, nullptr))
+	  bool check = entry->visit_defining_cus ([&] (dwarf2_per_cu *per_cu)
+	    {
+	      return dw2_search_one (per_cu, per_objfile, cus_to_skip,
+				     file_matcher, listener, nullptr);
+	    });
+	  if (!check)
 	    return false;
 	}
     }
@@ -18028,6 +18037,30 @@ dwarf2_per_cu::ensure_lang (dwarf2_per_objfile *per_objfile)
 		      true, std::nullopt, abbrev_table_cache);
 }
 
+/* See read.h.  */
+
+bool
+dwarf2_per_cu::recursively_visit_cus (per_cu_callback callback)
+{
+  if (including_cus.empty ())
+    return callback (this);
+  for (dwarf2_per_cu *iter : including_cus)
+    if (!iter->recursively_visit_cus (callback))
+      return false;
+  return true;
+}
+
+/* See read.h.  */
+
+dwarf2_per_cu *
+dwarf2_per_cu::canonical_outermost_cu ()
+{
+  dwarf2_per_cu *iter = this;
+  while (!iter->including_cus.empty ())
+    iter = *iter->including_cus.begin ();
+  return iter;
+}
+
 /* Return the unit from ALL_UNITS that potentially contains TARGET.
 
    Since the unit lengths may not be known yet, this function doesn't check that
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index 50bab220888..33ea33692cd 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -251,6 +251,15 @@ struct dwarf2_per_cu
   /* Backlink to the owner of this.  */
   dwarf2_per_bfd *m_per_bfd;
 
+  /* Compare two CUs.  */
+  struct compare
+  {
+    bool operator() (const dwarf2_per_cu *lhs, const dwarf2_per_cu *rhs) const
+    {
+      return lhs->index < rhs->index;
+    }
+  };
+
 public:
   /* The file and directory for this CU.  This is cached so that we
      don't need to re-examine the DWO in some situations.  This may be
@@ -280,6 +289,13 @@ struct dwarf2_per_cu
      http://sourceware.org/bugzilla/show_bug.cgi?id=15021.  */
   std::vector<dwarf2_per_cu *> imported_symtabs;
 
+  /* Pointer to all the CUs that include this CU via
+     DW_TAG_imported_unit.  This is used for the special handling of
+     inline functions appearing in partial units.  An ordered set is
+     used to ensure that the canonical CU is the same across
+     invocations of gdb.  */
+  std::set<dwarf2_per_cu *, compare> including_cus;
+
   bool is_debug_types () const
   { return m_is_debug_types; }
 
@@ -431,6 +447,27 @@ struct dwarf2_per_cu
 
   /* Free any cached file names.  */
   void free_cached_file_names ();
+
+  /* Indicate that INCLUDER includes this CU via
+     DW_TAG_imported_unit.  */
+  void add_includer (dwarf2_per_cu *includer)
+  {
+    including_cus.emplace (includer);
+  }
+
+  /* Type of callback used when visiting defining CUs.  */
+  using per_cu_callback = gdb::function_view<bool (dwarf2_per_cu *)>;
+
+  /* Calls CALLBACK for each CU that is the outermost includer of
+     ONE_CU.  If ONE_CU has no includers, it calls CALLBACK on ONE_CU.
+     If any call to CALLBACK returns false, this immediately returns
+     false (skipping the remaining calls); otherwise returns true.  */
+  bool recursively_visit_cus (per_cu_callback callback);
+
+  /* Return a canonical outermost CU corresponding to this CU.  If
+     this CU is standalone (not included by other CUs), then this
+     method will simply return 'this'.  */
+  dwarf2_per_cu *canonical_outermost_cu ();
 };
 
 /* Entry in the signatured_types hash table.  */
diff --git a/gdb/testsuite/gdb.cp/breakpoint-locs.exp b/gdb/testsuite/gdb.cp/breakpoint-locs.exp
index 5895cd4549d..2ab046d3be8 100644
--- a/gdb/testsuite/gdb.cp/breakpoint-locs.exp
+++ b/gdb/testsuite/gdb.cp/breakpoint-locs.exp
@@ -27,3 +27,11 @@ if { [prepare_for_testing "failed to prepare" $testfile "$srcfile $srcfile2"\
 }
 
 gdb_test "break N1::C1::baz" "\\(2 locations\\)"
+
+if {[have_index $binfile] == ""} {
+    set index_dir [host_standard_output_file ""]
+    # This is a regression test for a crash that occurred in the
+    # dwz+inlining series.  See PR symtab/30728.
+    gdb_test_no_output "save gdb-index -dwarf-5 $index_dir" \
+	"save gdb-index"
+}

-- 
2.49.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH v4 8/8] Update .debug_names documentation
  2026-02-24  1:16 [PATCH v4 0/8] Correctly handle inline functions with dwz Tom Tromey
                   ` (6 preceding siblings ...)
  2026-02-24  1:17 ` [PATCH v4 7/8] Handle inline functions with dwz Tom Tromey
@ 2026-02-24  1:17 ` Tom Tromey
  2026-02-24  9:46 ` [PATCH v4 0/8] Correctly handle inline functions with dwz Tom de Vries
  2026-04-07 19:10 ` Tom Tromey
  9 siblings, 0 replies; 14+ messages in thread
From: Tom Tromey @ 2026-02-24  1:17 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tom Tromey, Eli Zaretskii

This updates the .debug_names documentation to explain some DWARF
issues that we've handled in gdb.

This list still isn't exhaustive.  I think there are some situations
where gdb may examine a declaration (which DWARF says not to do), but
I didn't document this as I don't recall the details.

Approved-By: Eli Zaretskii <eliz@gnu.org>
---
 gdb/doc/gdb.texinfo | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 3b2401117c7..86b0b8164fd 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -23313,6 +23313,43 @@ source name.
 
 @end table
 
+@value{GDBN} also has some special handling for cases not considered
+in the DWARF specification.
+
+@itemize @bullet
+@item
+The @code{DW_IDX_parent} for a C-style enumerator does not point at
+the entry for @code{enum} itself, but rather the parent of the type.
+The reason for this is that C-style enumerators are injected into the
+containing scope, and so their name is not qualified by the
+@code{enum}; and furthermore there is no way to distinguish between
+C-style enumerators and @code{enum class}-style enumerators in
+@samp{.debug_names}.
+
+@item
+Similarly, @code{DW_IDX_parent} is omitted for any linkage name
+entries that are written.
+
+@item
+Definitions in partial units are handled differently.  These most
+typically are seen in the output of @code{dwz}.
+
+In general, a DWARF partial unit cannot be read in isolation, but only
+by reading it in the context of some other unit that references it via
+@code{DW_TAG_imported_unit}.
+
+Therefore, an ordinary definition in a partial unit is attributed to
+one of the outermost containing units.  This is done by referencing
+this containing CU in the @code{DW_IDX_compile_unit} attribute.
+
+A further special case applies to @code{DW_TAG_inlined_subroutine}
+entries.  An inlined subroutine appearing in a partial unit may be
+inlined in all of the outermost compilation units that directly or
+indirectly include the partial unit.  Therefore, in this case,
+@value{GDBN} will emit a separate index entry for the entry, once for
+each such containing unit.
+@end itemize
+
 @node Symbol Errors
 @section Errors Reading Symbol Files
 

-- 
2.49.0


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v4 5/8] Remove C++ special case from process_imported_unit_die
  2026-02-24  1:16 ` [PATCH v4 5/8] Remove C++ special case from process_imported_unit_die Tom Tromey
@ 2026-02-24  9:18   ` Tom de Vries
  2026-04-07 18:54     ` Tom Tromey
  0 siblings, 1 reply; 14+ messages in thread
From: Tom de Vries @ 2026-02-24  9:18 UTC (permalink / raw)
  To: Tom Tromey, gdb-patches

On 2/24/26 2:16 AM, Tom Tromey wrote:
> process_imported_unit_die has a special case for C++, added as a
> performance improvement.
> 
> While I somewhat agree with the general idea of this snippet --
> importing a compilation unit seems like a strange thing to do, given
> that partial units exist -- I think there are two issues with it.

Hi Tom,

this is the type of debuginfo generated by gcc for lto.

To briefly reiterate:
- at an early stage of compilation, type info is generated for each
   compilation unit, resulting in a CU with only types, so no addresses.
- then lto optimization happens, resulting in one blob of code per
   optimization unit (an lto partition), and debug info is generated for
   that blob (a CU with name "<artifical>").
- the artificial CU imports the CUs in the partition.

Importing a CU feels strange to me, but the DWARF standard (quoting v4) 
seems to allow it:
...
3.1.2 Imported Unit Entries
The place where a normal or partial unit is imported is represented by a 
debugging information entry with the tag DW_TAG_imported_unit.

An imported unit entry contains a DW_AT_import attribute whose value is 
a reference to the normal or partial compilation unit whose declarations
logically belong at the place of the imported unit entry.

An imported unit entry does not necessarily correspond to any entity or 
construct in the source program.  It is merely “glue” used to relate a 
partial unit, or a compilation unit used as a partial unit, to a place 
in some other compilation unit.
...

> First, it is specific to C++.  I don't see any real reason that this
> should be the case.

I implemented the optimization for cases where I could prove it was safe.

For C++, we have a global namespace so importing the types from a CU 
once or twice into the global namespace should have the same effect.

For C, each CU has it's own namespace, so each import is significant.

The DWARF standard refers to this difference here:
...
C Example
The C++ example in this Section might appear to be equally valid as a C 
example. However, it is prudent to include a DW_TAG_imported_unit in the 
primary unit (see Figure 84) with an DW_AT_import attribute that refers 
to the proper unit in the section group.
The C rules for consistency of global (file scope) symbols across 
compilations are less strict than for C++; inclusion of the import unit 
attribute assures that the declarations of the proper section group are 
considered before declarations from other compilations.
...

It could be that with the current implementation, this difference is no 
longer relevant, I'm not sure.

> Second, it does not have a corresponding bit of code in the indexer.
> This means that the cooked index's view of the DWARF differs from the
> full reader's view here.  This causes regressions in this series,
> because the indexer assumes that reading a CU will cause all the
> imported CUs to be read as a side effect -- but that does not happen
> here.
> 
> I think fixing this in the indexer is not trivial.  The reason for
> this is that the indexer works in parallel, and once a reader has
> acquired the "scan" bit for a CU, it is obligated to read it.
> However, in this case this would require making a new cooked indexer.
> 

I see.

> Instead, because (1) this is weird and rare DWARF anyway, and (2) this
> is just a performance optimization, I propose removing this.

I agree removing it is the right thing to do.

I also feels weird to me, though the DWARF standard explicitly allows it.

But from the perspective of a linux distro using gcc and lto for package 
compilation, it's the opposite of rare.

Anyway, I've filed a PR to reimplement this in some form or another ( 
https://sourceware.org/bugzilla/show_bug.cgi?id=33922 ).

Thanks,
- Tom

> ---
>   gdb/dwarf2/read.c | 9 ---------
>   1 file changed, 9 deletions(-)
> 
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index 5917e697aad..e48745d7969 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -4890,15 +4890,6 @@ process_imported_unit_die (struct die_info *die, struct dwarf2_cu *cu)
>         dwarf2_per_cu *per_cu
>   	= dwarf2_find_containing_unit ({ &section, sect_off }, per_objfile);
>   
> -      /* We're importing a C++ compilation unit with tag DW_TAG_compile_unit
> -	 into another compilation unit, at root level.  Regard this as a hint,
> -	 and ignore it.  This is a best effort, it only works if unit_type and
> -	 lang are already set.  */
> -      if (die->parent && die->parent->parent == NULL
> -	  && per_cu->unit_type (false) == DW_UT_compile
> -	  && per_cu->lang (false) == language_cplus)
> -	return;
> -
>         /* If necessary, add it to the queue and load its DIEs.  */
>         if (maybe_queue_comp_unit (cu, per_cu, per_objfile))
>   	load_full_comp_unit (per_cu, per_objfile, false, cu->lang ());
> 


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v4 0/8] Correctly handle inline functions with dwz
  2026-02-24  1:16 [PATCH v4 0/8] Correctly handle inline functions with dwz Tom Tromey
                   ` (7 preceding siblings ...)
  2026-02-24  1:17 ` [PATCH v4 8/8] Update .debug_names documentation Tom Tromey
@ 2026-02-24  9:46 ` Tom de Vries
  2026-04-07 19:10 ` Tom Tromey
  9 siblings, 0 replies; 14+ messages in thread
From: Tom de Vries @ 2026-02-24  9:46 UTC (permalink / raw)
  To: Tom Tromey, gdb-patches; +Cc: Eli Zaretskii

On 2/24/26 2:16 AM, Tom Tromey wrote:
> The new indexer does not correctly handle inline functions when 'dwz'
> is used to compress the DWARF.  This series fixes the bug, cleaning up
> a number of other things on the way.
> 
> I've separately regression tested each patch in this series on x86-64
> Fedora 41.  I've also regression tested the series as a whole with the
> dwz, gdb-index, and debug-names boards.
> 
> Even with that I messed up somehow, so v1 didn't actually fix the bug
> in question.  I must have modified the patches after testing..?
> 
> Anyway in v2 I've moved the line recording the CU inclusion and added
> a comment explaining the placement.  I re-ran the aformentioned tests
> and didn't touch anything.
> 

Hi Tom,

I've looked through the series, reviewed the smaller patches and browsed 
  the larger one (#7).

LGTM.

Acked-By: Tom de Vries <tdevries@suse.de>

Thanks,
- Tom

> Signed-off-by: Tom Tromey <tom@tromey.com>
> ---
> Changes in v4:
> - Update assert in .debug_names writer, and add regression test
> - Link to v3: https://inbox.sourceware.org/gdb-patches/20260220-dw-inline-fixup-pr-symtab-30728-2-v3-0-98ae8ab28fab@tromey.com
> 
> Changes in v3:
> - Minor updates per review
> - Rebased; which required some changes to the
>    iterate_over_one_compunit_symtab patch
> - Link to v2: https://inbox.sourceware.org/gdb-patches/20260126-dw-inline-fixup-pr-symtab-30728-2-v2-0-8ab183d1911c@tromey.com
> 
> Changes in v2:
> - Fixed fix from Tom de Vries for IS_INLINED debug display
> - Moved line to record inclusion
> - Link to v1: https://inbox.sourceware.org/gdb-patches/20260125-dw-inline-fixup-pr-symtab-30728-2-v1-0-e9973a4a401a@tromey.com
> 
> ---
> Tom Tromey (8):
>        Don't call add_dependence from index_imported_unit
>        Skip partial units in process_psymtab_comp_unit
>        Don't consider DW_TAG_inlined_subroutine as interesting
>        Combine two cases in cooked_index_functions::search
>        Remove C++ special case from process_imported_unit_die
>        Have iterate_over_one_compunit_symtab search included symtabs
>        Handle inline functions with dwz
>        Update .debug_names documentation
> 
>   gdb/doc/gdb.texinfo                      |  37 +++++
>   gdb/dwarf2/abbrev.c                      |   1 -
>   gdb/dwarf2/cooked-index-entry.c          |  24 ++++
>   gdb/dwarf2/cooked-index-entry.h          |  39 +++++-
>   gdb/dwarf2/cooked-index-worker.c         |  14 ++
>   gdb/dwarf2/cooked-index-worker.h         |  26 ++--
>   gdb/dwarf2/cooked-indexer.c              |  60 ++++++---
>   gdb/dwarf2/cooked-indexer.h              |   8 +-
>   gdb/dwarf2/cu.h                          |   6 +-
>   gdb/dwarf2/index-write.c                 | 224 ++++++++++++++++++-------------
>   gdb/dwarf2/read.c                        |  85 +++++++-----
>   gdb/dwarf2/read.h                        |  37 +++++
>   gdb/symfile-debug.c                      |  23 ++--
>   gdb/testsuite/gdb.cp/breakpoint-locs.exp |   8 ++
>   14 files changed, 415 insertions(+), 177 deletions(-)
> ---
> base-commit: 2a2b24ecc13988a6d87619f53b36faa2bcb61beb
> change-id: 20260125-dw-inline-fixup-pr-symtab-30728-2-7c31bf38fbe3
> 
> Best regards,


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v4 5/8] Remove C++ special case from process_imported_unit_die
  2026-02-24  9:18   ` Tom de Vries
@ 2026-04-07 18:54     ` Tom Tromey
  2026-04-08 11:17       ` Tom de Vries
  0 siblings, 1 reply; 14+ messages in thread
From: Tom Tromey @ 2026-04-07 18:54 UTC (permalink / raw)
  To: Tom de Vries; +Cc: Tom Tromey, gdb-patches

>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:

>> Instead, because (1) this is weird and rare DWARF anyway, and (2) this
>> is just a performance optimization, I propose removing this.

Tom> I agree removing it is the right thing to do.

Tom> I also feels weird to me, though the DWARF standard explicitly allows it.

Tom> But from the perspective of a linux distro using gcc and lto for
Tom> package compilation, it's the opposite of rare.

Tom> Anyway, I've filed a PR to reimplement this in some form or another (
Tom> https://sourceware.org/bugzilla/show_bug.cgi?id=33922 ).

Thanks for filing that.

I think the patch you posted there seems promising.  I wonder if it
could help with another case of over-eager CU expansion.

Tom

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v4 0/8] Correctly handle inline functions with dwz
  2026-02-24  1:16 [PATCH v4 0/8] Correctly handle inline functions with dwz Tom Tromey
                   ` (8 preceding siblings ...)
  2026-02-24  9:46 ` [PATCH v4 0/8] Correctly handle inline functions with dwz Tom de Vries
@ 2026-04-07 19:10 ` Tom Tromey
  9 siblings, 0 replies; 14+ messages in thread
From: Tom Tromey @ 2026-04-07 19:10 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

>>>>> "Tom" == Tom Tromey <tom@tromey.com> writes:

Tom> The new indexer does not correctly handle inline functions when 'dwz'
Tom> is used to compress the DWARF.  This series fixes the bug, cleaning up
Tom> a number of other things on the way.

Tom> I've separately regression tested each patch in this series on x86-64
Tom> Fedora 41.  I've also regression tested the series as a whole with the
Tom> dwz, gdb-index, and debug-names boards.

Tom> Even with that I messed up somehow, so v1 didn't actually fix the bug
Tom> in question.  I must have modified the patches after testing..?

Tom> Anyway in v2 I've moved the line recording the CU inclusion and added
Tom> a comment explaining the placement.  I re-ran the aformentioned tests
Tom> and didn't touch anything.

I'm going to check this in now.

Tom

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH v4 5/8] Remove C++ special case from process_imported_unit_die
  2026-04-07 18:54     ` Tom Tromey
@ 2026-04-08 11:17       ` Tom de Vries
  0 siblings, 0 replies; 14+ messages in thread
From: Tom de Vries @ 2026-04-08 11:17 UTC (permalink / raw)
  To: Tom Tromey; +Cc: gdb-patches

On 4/7/26 8:54 PM, Tom Tromey wrote:
>>>>>> "Tom" == Tom de Vries <tdevries@suse.de> writes:
> 
>>> Instead, because (1) this is weird and rare DWARF anyway, and (2) this
>>> is just a performance optimization, I propose removing this.
> 
> Tom> I agree removing it is the right thing to do.
> 
> Tom> I also feels weird to me, though the DWARF standard explicitly allows it.
> 
> Tom> But from the perspective of a linux distro using gcc and lto for
> Tom> package compilation, it's the opposite of rare.
> 
> Tom> Anyway, I've filed a PR to reimplement this in some form or another (
> Tom> https://sourceware.org/bugzilla/show_bug.cgi?id=33922 ).
> 
> Thanks for filing that.
> 
> I think the patch you posted there seems promising.  I wonder if it
> could help with another case of over-eager CU expansion.

I haven't looked at the other PR, but I posted an RFC documenting the 
current state of the patch.

Thanks,
- Tom

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2026-04-08 11:17 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-02-24  1:16 [PATCH v4 0/8] Correctly handle inline functions with dwz Tom Tromey
2026-02-24  1:16 ` [PATCH v4 1/8] Don't call add_dependence from index_imported_unit Tom Tromey
2026-02-24  1:16 ` [PATCH v4 2/8] Skip partial units in process_psymtab_comp_unit Tom Tromey
2026-02-24  1:16 ` [PATCH v4 3/8] Don't consider DW_TAG_inlined_subroutine as interesting Tom Tromey
2026-02-24  1:16 ` [PATCH v4 4/8] Combine two cases in cooked_index_functions::search Tom Tromey
2026-02-24  1:16 ` [PATCH v4 5/8] Remove C++ special case from process_imported_unit_die Tom Tromey
2026-02-24  9:18   ` Tom de Vries
2026-04-07 18:54     ` Tom Tromey
2026-04-08 11:17       ` Tom de Vries
2026-02-24  1:16 ` [PATCH v4 6/8] Have iterate_over_one_compunit_symtab search included symtabs Tom Tromey
2026-02-24  1:17 ` [PATCH v4 7/8] Handle inline functions with dwz Tom Tromey
2026-02-24  1:17 ` [PATCH v4 8/8] Update .debug_names documentation Tom Tromey
2026-02-24  9:46 ` [PATCH v4 0/8] Correctly handle inline functions with dwz Tom de Vries
2026-04-07 19:10 ` Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox