Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH v3 0/7] Handle foreign type units in .debug_names
@ 2026-05-11 15:40 Simon Marchi
  2026-05-11 15:40 ` [PATCH v3 1/7] gdb/dwarf: move dwo_unit and dwo_file to dwo.h Simon Marchi
                   ` (6 more replies)
  0 siblings, 7 replies; 12+ messages in thread
From: Simon Marchi @ 2026-05-11 15:40 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

This is v3 of:

  https://inbox.sourceware.org/gdb-patches/20260416200256.386186-1-simon.marchi@efficios.com/

I merged the first patch of v2 ("gdb: refuse to produce .gdb_index when
skeletonless type units are present"), ensuring that GDB does not
produce bogus indexes in presence of foreign type units.

I addressed the review comments from v2, which were all pretty minor.
The first patch of this series moves the dwo data structures to a new
dwarf2/dwo.h header file, instead of dwarf2/read.h.  That is the only
one worth re-reviewing, I think, all the others have the Approved-By tag
already.

Simon Marchi (7):
  gdb/dwarf: move dwo_unit and dwo_file to dwo.h
  gdb/dwarf: move dwarf2_cu::section to cu.c
  gdb/dwarf: add dwo_file::find_tus
  gdb/dwarf: generate foreign type units in .debug_names
  gdb/dwarf: add debug output in read-debug-names.c
  gdb/dwarf: add more context to complaints in
    mapped_debug_names_reader::scan_one_entry
  gdb/dwarf: read foreign type units

 gdb/dwarf2/cu.c                               |  11 +
 gdb/dwarf2/dwo.h                              | 239 +++++++++++
 gdb/dwarf2/index-write.c                      | 165 ++++++--
 gdb/dwarf2/read-debug-names.c                 | 357 +++++++++++++---
 gdb/dwarf2/read.c                             | 390 ++++++++----------
 gdb/dwarf2/read.h                             | 117 ++++--
 gdb/dwarf2/types.h                            |   2 +
 ...dwarf5-fission-debug-types-debug-names.exp |  29 ++
 8 files changed, 975 insertions(+), 335 deletions(-)
 create mode 100644 gdb/dwarf2/dwo.h
 create mode 100644 gdb/testsuite/boards/dwarf5-fission-debug-types-debug-names.exp


base-commit: 62cc7210c87a08a9e10955d9a240cfe0c0b5ee13
-- 
2.54.0


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

* [PATCH v3 1/7] gdb/dwarf: move dwo_unit and dwo_file to dwo.h
  2026-05-11 15:40 [PATCH v3 0/7] Handle foreign type units in .debug_names Simon Marchi
@ 2026-05-11 15:40 ` Simon Marchi
  2026-05-13 15:18   ` Tom Tromey
  2026-05-11 15:40 ` [PATCH v3 2/7] gdb/dwarf: move dwarf2_cu::section to cu.c Simon Marchi
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 12+ messages in thread
From: Simon Marchi @ 2026-05-11 15:40 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi

From: Simon Marchi <simon.marchi@polymtl.ca>

Move these types and some related things to a new dwo.h header file.  My
goal is to allow index-write.c to see these types, in a later patch.  I
initially moved them to read.h, but Tom suggested in review that read.h
was getting a bit too large.

Change-Id: Ia32e0643f95561d3a1bfb67d501c8e20f5682f0e
---
 gdb/dwarf2/dwo.h  | 219 ++++++++++++++++++++++++++++++++++++++++++++++
 gdb/dwarf2/read.c | 170 +----------------------------------
 gdb/dwarf2/read.h |  44 +---------
 3 files changed, 221 insertions(+), 212 deletions(-)
 create mode 100644 gdb/dwarf2/dwo.h

diff --git a/gdb/dwarf2/dwo.h b/gdb/dwarf2/dwo.h
new file mode 100644
index 000000000000..025932b1e969
--- /dev/null
+++ b/gdb/dwarf2/dwo.h
@@ -0,0 +1,219 @@
+/* DWARF 2 debugging format support for GDB.
+
+   Copyright (C) 1994-2026 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef GDB_DWARF2_DWO_H
+#define GDB_DWARF2_DWO_H
+
+#include "dwarf2/section.h"
+#include "gdb_bfd.h"
+#include "gdbsupport/unordered_set.h"
+#include "hashtab.h"
+
+/* These sections are what may appear in a (real or virtual) DWO file.  */
+
+struct dwo_sections
+{
+  struct dwarf2_section_info abbrev;
+  struct dwarf2_section_info line;
+  struct dwarf2_section_info loc;
+  struct dwarf2_section_info loclists;
+  struct dwarf2_section_info macinfo;
+  struct dwarf2_section_info macro;
+  struct dwarf2_section_info rnglists;
+  struct dwarf2_section_info str;
+  struct dwarf2_section_info str_offsets;
+  /* In the case of a virtual DWO file, these two are unused.  */
+  std::vector<dwarf2_section_info> infos;
+  std::vector<dwarf2_section_info> types;
+};
+
+/* CUs/TUs in DWP/DWO files.  */
+
+struct dwo_unit
+{
+  /* Backlink to the containing struct dwo_file.  */
+  struct dwo_file *dwo_file = nullptr;
+
+  /* The "id" that distinguishes this CU/TU.
+     .debug_info calls this "dwo_id", .debug_types calls this "signature".
+     Since signatures came first, we stick with it for consistency.  */
+  ULONGEST signature = 0;
+
+  /* The section this CU/TU lives in, in the DWO file.  */
+  dwarf2_section_info *section = nullptr;
+
+  /* This is set if SECTION is owned by this dwo_unit.  */
+  dwarf2_section_info_up section_holder;
+
+  /* Same as dwarf2_per_cu::{sect_off,length} but in the DWO section.  */
+  sect_offset sect_off {};
+  unsigned int length = 0;
+
+  /* For types, offset in the type's DIE of the type defined by this TU.  */
+  cu_offset type_offset_in_tu;
+};
+
+using dwo_unit_up = std::unique_ptr<dwo_unit>;
+
+/* Hash function for dwo_unit objects, based on the signature.  */
+
+struct dwo_unit_hash
+{
+  using is_transparent = void;
+
+  std::size_t operator() (ULONGEST signature) const noexcept
+  { return signature; }
+
+  std::size_t operator() (const dwo_unit_up &unit) const noexcept
+  { return (*this) (unit->signature); }
+};
+
+/* Equal function for dwo_unit objects, based on the signature.
+
+   The signature is assumed to be unique within the DWO file.  So while object
+   file CU dwo_id's always have the value zero, that's OK, assuming each object
+   file DWO file has only one CU, and that's the rule for now.  */
+
+struct dwo_unit_eq
+{
+  using is_transparent = void;
+
+  bool operator() (ULONGEST sig, const dwo_unit_up  &unit) const noexcept
+  { return sig == unit->signature; }
+
+  bool operator() (const dwo_unit_up &a, const dwo_unit_up &b) const noexcept
+  { return (*this) (a->signature, b); }
+};
+
+/* Set of dwo_unit object, using their signature as identity.  */
+
+using dwo_unit_set = gdb::unordered_set<dwo_unit_up, dwo_unit_hash, dwo_unit_eq>;
+
+/* Data for one DWO file.
+
+   This includes virtual DWO files (a virtual DWO file is a DWO file as it
+   appears in a DWP file).  DWP files don't really have DWO files per se -
+   comdat folding of types "loses" the DWO file they came from, and from
+   a high level view DWP files appear to contain a mass of random types.
+   However, to maintain consistency with the non-DWP case we pretend DWP
+   files contain virtual DWO files, and we assign each TU with one virtual
+   DWO file (generally based on the line and abbrev section offsets -
+   a heuristic that seems to work in practice).  */
+
+struct dwo_file
+{
+  dwo_file () = default;
+  DISABLE_COPY_AND_ASSIGN (dwo_file);
+
+  /* The DW_AT_GNU_dwo_name or DW_AT_dwo_name attribute.
+     For virtual DWO files the name is constructed from the section offsets
+     of abbrev,line,loc,str_offsets so that we combine virtual DWO files
+     from related CU+TUs.  */
+  std::string dwo_name;
+
+  /* The DW_AT_comp_dir attribute.  */
+  const char *comp_dir = nullptr;
+
+  /* The bfd, when the file is open.  Otherwise this is NULL.
+     This is unused(NULL) for virtual DWO files where we use dwp_file.dbfd.  */
+  gdb_bfd_ref_ptr dbfd;
+
+  /* The sections that make up this DWO file.
+     Remember that for virtual DWO files in DWP V2 or DWP V5, these are virtual
+     sections (for lack of a better name).  */
+  struct dwo_sections sections {};
+
+  /* The CUs in the file.
+
+     Multiple CUs per DWO are supported as an extension to handle LLVM's Link
+     Time Optimization output (where multiple source files may be compiled into
+     a single object/dwo pair).  */
+  dwo_unit_set cus;
+
+  /* Table of TUs in the file.  */
+  dwo_unit_set tus;
+};
+
+using dwo_file_up = std::unique_ptr<dwo_file>;
+
+/* This is used when looking up entries in a dwo_file_set.  */
+
+struct dwo_file_search
+{
+  /* Name of the DWO to look for.  */
+  const char *dwo_name;
+
+  /* Compilation directory to look for.  */
+  const char *comp_dir;
+};
+
+/* Hash function for dwo_file objects, using their dwo_name and comp_dir as
+   identity.  */
+
+struct dwo_file_hash
+{
+  using is_transparent = void;
+
+  std::size_t operator() (const dwo_file_search &search) const noexcept
+  {
+    hashval_t hash = htab_hash_string (search.dwo_name);
+
+    if (search.comp_dir != nullptr)
+      hash += htab_hash_string (search.comp_dir);
+
+    return hash;
+  }
+
+  std::size_t operator() (const dwo_file_up &file) const noexcept
+  {
+    return (*this) ({ file->dwo_name.c_str (), file->comp_dir });
+  }
+};
+
+/* Equal function for dwo_file objects, using their dwo_name and comp_dir as
+   identity.  */
+
+struct dwo_file_eq
+{
+  using is_transparent = void;
+
+  bool operator() (const dwo_file_search &search,
+		   const dwo_file_up &dwo_file) const noexcept
+  {
+    if (search.dwo_name != dwo_file->dwo_name)
+      return false;
+
+    if (search.comp_dir == nullptr || dwo_file->comp_dir == nullptr)
+      return search.comp_dir == dwo_file->comp_dir;
+
+    return streq (search.comp_dir, dwo_file->comp_dir);
+  }
+
+  bool operator() (const dwo_file_up &a, const dwo_file_up &b) const noexcept
+  {
+    return (*this) ({ a->dwo_name.c_str (), a->comp_dir }, b);
+  }
+};
+
+/* Set of dwo_file objects, using their dwo_name and comp_dir as identity.  */
+
+using dwo_file_up_set
+  = gdb::unordered_set<dwo_file_up, dwo_file_hash, dwo_file_eq>;
+
+#endif /* GDB_DWARF2_DWO_H */
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index efe96f6510da..46aa0201cc63 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -31,6 +31,7 @@
 #include "dwarf2/abbrev.h"
 #include "dwarf2/aranges.h"
 #include "dwarf2/attribute.h"
+#include "dwarf2/dwo.h"
 #include "dwarf2/unit-head.h"
 #include "dwarf2/cooked-index-worker.h"
 #include "dwarf2/cooked-indexer.h"
@@ -65,7 +66,6 @@
 #include "dwarf2/expr.h"
 #include "dwarf2/loc.h"
 #include "cp-support.h"
-#include "hashtab.h"
 #include "command.h"
 #include "cli/cli-cmds.h"
 #include "block.h"
@@ -266,86 +266,6 @@ struct loclists_rnglists_header
   unsigned int offset_entry_count;
 };
 
-/* These sections are what may appear in a (real or virtual) DWO file.  */
-
-struct dwo_sections
-{
-  struct dwarf2_section_info abbrev;
-  struct dwarf2_section_info line;
-  struct dwarf2_section_info loc;
-  struct dwarf2_section_info loclists;
-  struct dwarf2_section_info macinfo;
-  struct dwarf2_section_info macro;
-  struct dwarf2_section_info rnglists;
-  struct dwarf2_section_info str;
-  struct dwarf2_section_info str_offsets;
-  /* In the case of a virtual DWO file, these two are unused.  */
-  std::vector<dwarf2_section_info> infos;
-  std::vector<dwarf2_section_info> types;
-};
-
-/* CUs/TUs in DWP/DWO files.  */
-
-struct dwo_unit
-{
-  /* Backlink to the containing struct dwo_file.  */
-  struct dwo_file *dwo_file = nullptr;
-
-  /* The "id" that distinguishes this CU/TU.
-     .debug_info calls this "dwo_id", .debug_types calls this "signature".
-     Since signatures came first, we stick with it for consistency.  */
-  ULONGEST signature = 0;
-
-  /* The section this CU/TU lives in, in the DWO file.  */
-  dwarf2_section_info *section = nullptr;
-
-  /* This is set if SECTION is owned by this dwo_unit.  */
-  dwarf2_section_info_up section_holder;
-
-  /* Same as dwarf2_per_cu::{sect_off,length} but in the DWO section.  */
-  sect_offset sect_off {};
-  unsigned int length = 0;
-
-  /* For types, offset in the type's DIE of the type defined by this TU.  */
-  cu_offset type_offset_in_tu;
-};
-
-using dwo_unit_up = std::unique_ptr<dwo_unit>;
-
-/* Hash function for dwo_unit objects, based on the signature.  */
-
-struct dwo_unit_hash
-{
-  using is_transparent = void;
-
-  std::size_t operator() (ULONGEST signature) const noexcept
-  { return signature; }
-
-  std::size_t operator() (const dwo_unit_up &unit) const noexcept
-  { return (*this) (unit->signature); }
-};
-
-/* Equal function for dwo_unit objects, based on the signature.
-
-   The signature is assumed to be unique within the DWO file.  So while object
-   file CU dwo_id's always have the value zero, that's OK, assuming each object
-   file DWO file has only one CU, and that's the rule for now.  */
-
-struct dwo_unit_eq
-{
-  using is_transparent = void;
-
-  bool operator() (ULONGEST sig, const dwo_unit_up  &unit) const noexcept
-  { return sig == unit->signature; }
-
-  bool operator() (const dwo_unit_up &a, const dwo_unit_up &b) const noexcept
-  { return (*this) (a->signature, b); }
-};
-
-/* Set of dwo_unit object, using their signature as identity.  */
-
-using dwo_unit_set = gdb::unordered_set<dwo_unit_up, dwo_unit_hash, dwo_unit_eq>;
-
 /* include/dwarf2.h defines the DWP section codes.
    It defines a max value but it doesn't define a min value, which we
    use for error checking, so provide one.  */
@@ -355,94 +275,6 @@ enum dwp_v2_section_ids
   DW_SECT_MIN = 1
 };
 
-/* Data for one DWO file.
-
-   This includes virtual DWO files (a virtual DWO file is a DWO file as it
-   appears in a DWP file).  DWP files don't really have DWO files per se -
-   comdat folding of types "loses" the DWO file they came from, and from
-   a high level view DWP files appear to contain a mass of random types.
-   However, to maintain consistency with the non-DWP case we pretend DWP
-   files contain virtual DWO files, and we assign each TU with one virtual
-   DWO file (generally based on the line and abbrev section offsets -
-   a heuristic that seems to work in practice).  */
-
-struct dwo_file
-{
-  dwo_file () = default;
-  DISABLE_COPY_AND_ASSIGN (dwo_file);
-
-  /* The DW_AT_GNU_dwo_name or DW_AT_dwo_name attribute.
-     For virtual DWO files the name is constructed from the section offsets
-     of abbrev,line,loc,str_offsets so that we combine virtual DWO files
-     from related CU+TUs.  */
-  std::string dwo_name;
-
-  /* The DW_AT_comp_dir attribute.  */
-  const char *comp_dir = nullptr;
-
-  /* The bfd, when the file is open.  Otherwise this is NULL.
-     This is unused(NULL) for virtual DWO files where we use dwp_file.dbfd.  */
-  gdb_bfd_ref_ptr dbfd;
-
-  /* The sections that make up this DWO file.
-     Remember that for virtual DWO files in DWP V2 or DWP V5, these are virtual
-     sections (for lack of a better name).  */
-  struct dwo_sections sections {};
-
-  /* The CUs in the file.
-
-     Multiple CUs per DWO are supported as an extension to handle LLVM's Link
-     Time Optimization output (where multiple source files may be compiled into
-     a single object/dwo pair).  */
-  dwo_unit_set cus;
-
-  /* Table of TUs in the file.  */
-  dwo_unit_set tus;
-};
-
-/* See dwarf2/read.h.  */
-
-std::size_t
-dwo_file_hash::operator() (const dwo_file_search &search) const noexcept
-{
-  hashval_t hash = htab_hash_string (search.dwo_name);
-
-  if (search.comp_dir != nullptr)
-    hash += htab_hash_string (search.comp_dir);
-
-  return hash;
-}
-
-/* See dwarf2/read.h.  */
-
-std::size_t
-dwo_file_hash::operator() (const dwo_file_up &file) const noexcept
-{
-  return (*this) ({ file->dwo_name.c_str (), file->comp_dir });
-}
-
-/* See dwarf2/read.h.  */
-
-bool
-dwo_file_eq::operator() (const dwo_file_search &search,
-			 const dwo_file_up &dwo_file) const noexcept
-{
-  if (search.dwo_name != dwo_file->dwo_name)
-    return false;
-
-  if (search.comp_dir == nullptr || dwo_file->comp_dir == nullptr)
-    return search.comp_dir == dwo_file->comp_dir;
-
-  return streq (search.comp_dir, dwo_file->comp_dir);
-}
-
-/* See dwarf2/read.h.  */
-
-bool
-dwo_file_eq::operator() (const dwo_file_up &a,
-			 const dwo_file_up &b) const noexcept
-{ return (*this) ({ a->dwo_name.c_str (), a->comp_dir }, b); }
-
 /* These sections are what may appear in a DWP file.  */
 
 struct dwp_sections
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index c65ee7f86990..63c3129ad9a8 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -22,6 +22,7 @@
 
 #include <queue>
 #include "dwarf2/abbrev.h"
+#include "dwarf2/dwo.h"
 #include "dwarf2/unit-head.h"
 #include "dwarf2/file-and-dir.h"
 #include "dwarf2/index-cache.h"
@@ -529,49 +530,6 @@ using signatured_type_set
   = gdb::unordered_set<signatured_type *, signatured_type_hash,
 		       signatured_type_eq>;
 
-struct dwo_file;
-
-using dwo_file_up = std::unique_ptr<dwo_file>;
-
-/* This is used when looking up entries in a dwo_file_set.  */
-
-struct dwo_file_search
-{
-  /* Name of the DWO to look for.  */
-  const char *dwo_name;
-
-  /* Compilation directory to look for.  */
-  const char *comp_dir;
-};
-
-/* Hash function for dwo_file objects, using their dwo_name and comp_dir as
-   identity.  */
-
-struct dwo_file_hash
-{
-  using is_transparent = void;
-
-  std::size_t operator() (const dwo_file_search &search) const noexcept;
-  std::size_t operator() (const dwo_file_up &file) const noexcept;
-};
-
-/* Equal function for dwo_file objects, using their dwo_name and comp_dir as
-   identity.  */
-
-struct dwo_file_eq
-{
-  using is_transparent = void;
-
-  bool operator() (const dwo_file_search &search,
-		   const dwo_file_up &dwo_file) const noexcept;
-  bool operator() (const dwo_file_up &a, const dwo_file_up &b) const noexcept;
-};
-
-/* Set of dwo_file objects, using their dwo_name and comp_dir as identity.  */
-
-using dwo_file_up_set
-  = gdb::unordered_set<dwo_file_up, dwo_file_hash, dwo_file_eq>;
-
 struct dwp_file;
 
 using dwp_file_up = std::unique_ptr<dwp_file>;
-- 
2.54.0


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

* [PATCH v3 2/7] gdb/dwarf: move dwarf2_cu::section to cu.c
  2026-05-11 15:40 [PATCH v3 0/7] Handle foreign type units in .debug_names Simon Marchi
  2026-05-11 15:40 ` [PATCH v3 1/7] gdb/dwarf: move dwo_unit and dwo_file to dwo.h Simon Marchi
@ 2026-05-11 15:40 ` Simon Marchi
  2026-05-11 15:40 ` [PATCH v3 3/7] gdb/dwarf: add dwo_file::find_tus Simon Marchi
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Simon Marchi @ 2026-05-11 15:40 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi, Tom Tromey

Following the previous patch that moves the dwo_unit structure from
dwarf2/read.c to dwarf2/dwo.h, dwarf2_cu::section has no reason to be
implemented in dwarf2/read.c anymore.  Move it to dwarf2/cu.c.

Change-Id: I67e2bb42d878ac18e4bf3460d75f1394477a46ce
Approved-By: Tom Tromey <tom@tromey.com>
---
 gdb/dwarf2/cu.c   | 11 +++++++++++
 gdb/dwarf2/read.c | 14 +-------------
 2 files changed, 12 insertions(+), 13 deletions(-)

diff --git a/gdb/dwarf2/cu.c b/gdb/dwarf2/cu.c
index c3bfd15f14a1..3c89bd960d56 100644
--- a/gdb/dwarf2/cu.c
+++ b/gdb/dwarf2/cu.c
@@ -58,6 +58,17 @@ dwarf2_cu::dwarf2_cu (dwarf2_per_cu *per_cu, dwarf2_per_objfile *per_objfile)
 
 /* See cu.h.  */
 
+const dwarf2_section_info &
+dwarf2_cu::section () const
+{
+  if (this->dwo_unit != nullptr)
+    return *this->dwo_unit->section;
+  else
+    return *this->per_cu->section ();
+}
+
+/* See cu.h.  */
+
 struct type *
 dwarf2_cu::addr_sized_int_type (bool unsigned_p) const
 {
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 46aa0201cc63..4d797b0e29b9 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -5765,19 +5765,7 @@ read_file_scope (struct die_info *die, struct dwarf2_cu *cu)
     }
 }
 
-/* See cu.h.
-
-   This function is defined in this file (instead of cu.c) because it needs
-   to see the definition of struct dwo_unit.  */
-
-const dwarf2_section_info &
-dwarf2_cu::section () const
-{
-  if (this->dwo_unit != nullptr)
-    return *this->dwo_unit->section;
-  else
-    return *this->per_cu->section ();
-}
+/* See cu.h.  */
 
 /* See cu.h.
 
-- 
2.54.0


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

* [PATCH v3 3/7] gdb/dwarf: add dwo_file::find_tus
  2026-05-11 15:40 [PATCH v3 0/7] Handle foreign type units in .debug_names Simon Marchi
  2026-05-11 15:40 ` [PATCH v3 1/7] gdb/dwarf: move dwo_unit and dwo_file to dwo.h Simon Marchi
  2026-05-11 15:40 ` [PATCH v3 2/7] gdb/dwarf: move dwarf2_cu::section to cu.c Simon Marchi
@ 2026-05-11 15:40 ` Simon Marchi
  2026-05-11 15:40 ` [PATCH v3 4/7] gdb/dwarf: generate foreign type units in .debug_names Simon Marchi
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Simon Marchi @ 2026-05-11 15:40 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi, Tom Tromey

Add this little helper to make finding a TU a bit simpler.  I always
find the STL way of doing things cryptic, so I think that hiding it in
small helper methods makes the code clearer.

A subsequent patch will add more uses of it

Change-Id: Ibfb20d0e44c65d2ff729f3e0980ec4435f223aef
Approved-By: Tom Tromey <tom@tromey.com>
---
 gdb/dwarf2/dwo.h  | 11 +++++++++++
 gdb/dwarf2/read.c |  6 ++----
 2 files changed, 13 insertions(+), 4 deletions(-)

diff --git a/gdb/dwarf2/dwo.h b/gdb/dwarf2/dwo.h
index 025932b1e969..47d7bdde7925 100644
--- a/gdb/dwarf2/dwo.h
+++ b/gdb/dwarf2/dwo.h
@@ -121,6 +121,17 @@ struct dwo_file
   dwo_file () = default;
   DISABLE_COPY_AND_ASSIGN (dwo_file);
 
+  /* Look for a type unit with signature SIGNATURE in this dwo_file.
+
+     Return nullptr if not found.  */
+  dwo_unit *find_tu (ULONGEST signature) const
+  {
+    if (auto it = this->tus.find (signature); it != this->tus.end ())
+      return it->get ();
+
+    return nullptr;
+  }
+
   /* The DW_AT_GNU_dwo_name or DW_AT_dwo_name attribute.
      For virtual DWO files the name is constructed from the section offsets
      of abbrev,line,loc,str_offsets so that we combine virtual DWO files
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 4d797b0e29b9..9cb7b83cc339 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -2311,12 +2311,10 @@ lookup_dwo_signatured_type (struct dwarf2_cu *cu, ULONGEST sig)
   /* Note: cu->dwo_unit is the dwo_unit that references this TU, not the
      dwo_unit of the TU itself.  */
   dwo_file *dwo_file = cu->dwo_unit->dwo_file;
-  auto it = dwo_file->tus.find (sig);
-  if (it == dwo_file->tus.end ())
+  dwo_unit *dwo_entry = dwo_file->find_tu (sig);
+  if (dwo_entry == nullptr)
     return nullptr;
 
-  dwo_unit *dwo_entry = it->get ();
-
   /* If the global table doesn't have an entry for this TU, add one.  */
   if (sig_type_it == per_bfd->signatured_types.end ())
     {
-- 
2.54.0


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

* [PATCH v3 4/7] gdb/dwarf: generate foreign type units in .debug_names
  2026-05-11 15:40 [PATCH v3 0/7] Handle foreign type units in .debug_names Simon Marchi
                   ` (2 preceding siblings ...)
  2026-05-11 15:40 ` [PATCH v3 3/7] gdb/dwarf: add dwo_file::find_tus Simon Marchi
@ 2026-05-11 15:40 ` Simon Marchi
  2026-05-11 15:40 ` [PATCH v3 5/7] gdb/dwarf: add debug output in read-debug-names.c Simon Marchi
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 12+ messages in thread
From: Simon Marchi @ 2026-05-11 15:40 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi, Tom Tromey

The DWARF 5 .debug_names index makes the distinction between local type
units and foreign type units.  Local type units are those present
directly in the main file.  Foreign type units are those present in .dwo
files.  GDB only knows how to produce local type units today, which
leads to invalid indexes whenever there are type units in .dwo files.

To observe this, run test gdb.dwarf2/fission-with-type-unit.exp with
board cc-with-debug-names.  The test passes, but we see this in the log:

    (gdb) file
/home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.dwarf2/fission-with-type-unit/fission-with-type-unit^M
    Reading symbols from
/home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.dwarf2/fission-with-type-unit/fission-with-type-unit...^M
    warning: Section .debug_names has incorrect entry in TU table,
ignoring .debug_names.^M

These are the units involved in this test.  The first and last CUs are
dummy CUs added by the DWARF assembler, they are not important.

  - CU at offset 0x0  of .debug_info     in fission-with-type-unit -- dummy
  - CU at offset 0xc  of .debug_info     in fission-with-type-unit
  - CU at offset 0x25 of .debug_info     in fission-with-type-unit -- dummy
  - TU at offset 0x0  of .debug_info.dwo in fission-with-type-unit-dw.dwo

This is the content of the produced .debug_names:

Contents of the .debug_names section:

    $ readelf --debug-dump=gdb_index testsuite/outputs/gdb.dwarf2/fission-with-type-unit/fission-with-type-unit
    ...
    Version 5
    Augmentation string: 47 44 42 33 00 00 00 00  ("GDB3")
    CU table:
    [  0] 0
    [  1] 0xc
    [  2] 0x25

    TU table:
    [  0] 0

    Foreign TU table:

    Used 0 of 0 buckets.

    Symbol table:
    [  1] global_var: <0><1> DW_TAG_variable DW_IDX_compile_unit=(udata)=1 DW_IDX_die_offset=(ref_addr)=<0x1c> DW_IDX_GNU_language=(udata)=0 DW_IDX_GNU_internal=(flag_present)=1
    [  2] int:
            <0x8><2> DW_TAG_base_type DW_IDX_compile_unit=(udata)=1 DW_IDX_die_offset=(ref_addr)=<0x15> DW_IDX_GNU_language=(udata)=0
            <0xf><3> DW_TAG_base_type DW_IDX_type_unit=(udata)=0 DW_IDX_die_offset=(ref_addr)=<0x19> DW_IDX_GNU_language=(udata)=0

The TU table claims that there is a TU at offset 0 in the main file
(fission-with-type-unit).  This is wrong: the TU is in
fission-with-type-unit-dw.dwo.  It should be listed in the Foreign TU
table instead.

This patch therefore teaches GDB to use the foreign TU table for TUs in
.dwo files.

A note about foreign type units and skeletons: in the early history of
split DWARF and type units, gcc 4.x used to create skeletons for type
units in .dwo files, but subsequent versions don't.  DWARF 5 doesn't
have support for type unit skeletons at all.  So skeletons for type
units are mostly a historical curiosity at this point, the norm is to
not have them.  But if for some reason a type unit in a .dwo file had a
matching skeleton in the main file, then it would be ok for that TU to
be listed in the "TU table".  The offset would be that of the skeleton.

While the list of CUs and local TUs contain the offset within the
.debug_info section where to find the unit, the foreign TU list only
contains the 8-byte signature of the types.  With just that, a reader
wouldn't be able to easily locate a .dwo that contain the type with a
given signature.

To help with this, index entries for foreign type units may also include
a reference to a compilation unit that can be followed in order to find
a .dwo file containing the type.  This patch implements it.

Implementation details
----------------------

The first change is the addition of the dwo_unit::per_cu field, which
allows going from the dwo_unit to the dwarf2_per_cu structure (which
describes the skeleton) that was used to lookup this dwo_unit.  This
fields starts at nullptr, and it gets set in lookup_dwo_cutu whenever we
look up the dwo_unit for a given dwarf2_per_cu.  This will come handy
later.  I made this field an std::atomic, because I think it would be
possible to craft a weird test case that would make two indexer threads
try to set the field on the same dwo_unit.  During normal operation, we
expect the field for each dwo_unit representing a CU to be written
exactly once.

In index-write.c, change the get_unit_lists function in
dwarf2/index-write.c to segregate local and foreign type units.  Then,
update write_debug_names to emit the list of foreign TUs in the
.debug_names header.  This consists of a list of type signatures.

In debug_names::build, for foreign type units, emit a
DW_IDX_compile_unit field.  This is the reference to the CU that can be
used to locate the .dwo file containing that type unit.  To obtain the
value for this field, look up a CU in the same dwo_file that has its
dwo_unit::per_cu field set (typically there will be exactly one CU, and
the field will be set).

With this patch, the index for the test case above looks like:

    $ readelf --debug-dump=gdb_index testsuite/outputs/gdb.dwarf2/fission-with-type-unit/fission-with-type-unit
    ...
    Version 5
    Augmentation string: 47 44 42 33 00 00 00 00  ("GDB3")
    CU table:
    [  0] 0
    [  1] 0xc
    [  2] 0x25

    TU table:

    Foreign TU table:
    [  0] 000000000000cafe

    Used 0 of 0 buckets.

    Symbol table:
    [  1] global_var: <0><1> DW_TAG_variable DW_IDX_compile_unit=(udata)=1 DW_IDX_die_offset=(ref_addr)=<0x1c> DW_IDX_GNU_language=(udata)=0 DW_IDX_GNU_internal=(flag_present)=1
    [  2] int:
            <0x8><2> DW_TAG_base_type DW_IDX_compile_unit=(udata)=1 DW_IDX_die_offset=(ref_addr)=<0x15> DW_IDX_GNU_language=(udata)=0
            <0xf><3> DW_TAG_base_type DW_IDX_type_unit=(udata)=0 DW_IDX_compile_unit=(udata)=1 DW_IDX_die_offset=(ref_addr)=<0x19> DW_IDX_GNU_language=(udata)=0   ...

We can see that the TU is correctly placed in the foreign TU list, and
that the index entry (the last line) points to the TU at index 0, but
also to the CU at index 1, which is indeed the CU that the reader can
follow to find the type unit.

With this patch, GDB still rejects the index:

    (gdb) file /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.dwarf2/fission-with-type-unit/fission-with-type-unit
    Reading symbols from /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.dwarf2/fission-with-type-unit/fission-with-type-unit...
    warning: Section .debug_names in /home/simark/build/binutils-gdb/gdb/testsuite/outputs/gdb.dwarf2/fission-with-type-unit/fission-with-type-unit has unsupported 1 foreign TUs, ignoring .debug_names.

But at least, we don't produce a bogus index anymore, that's already an
improvement.  A following patch in this series implements the reading
side

Change-Id: I311fd7b4ca57d9ff6d64ae08df805c6635961eed
Approved-By: Tom Tromey <tom@tromey.com>
---
 gdb/dwarf2/dwo.h         |   9 +++
 gdb/dwarf2/index-write.c | 165 +++++++++++++++++++++++++++++++++------
 gdb/dwarf2/read.c        |   9 +++
 gdb/dwarf2/read.h        |  12 +++
 4 files changed, 171 insertions(+), 24 deletions(-)

diff --git a/gdb/dwarf2/dwo.h b/gdb/dwarf2/dwo.h
index 47d7bdde7925..f7631ee3de6e 100644
--- a/gdb/dwarf2/dwo.h
+++ b/gdb/dwarf2/dwo.h
@@ -20,11 +20,15 @@
 #ifndef GDB_DWARF2_DWO_H
 #define GDB_DWARF2_DWO_H
 
+#include <atomic>
+
 #include "dwarf2/section.h"
 #include "gdb_bfd.h"
 #include "gdbsupport/unordered_set.h"
 #include "hashtab.h"
 
+struct dwarf2_per_cu;
+
 /* These sections are what may appear in a (real or virtual) DWO file.  */
 
 struct dwo_sections
@@ -50,6 +54,11 @@ struct dwo_unit
   /* Backlink to the containing struct dwo_file.  */
   struct dwo_file *dwo_file = nullptr;
 
+  /* For compile units only, the per-CU structure that represents the skeleton
+     that we used to reach this dwo_unit.  It starts as nullptr, and gets set
+     in cutu_reader::lookup_dwo_cutu.  */
+  std::atomic<dwarf2_per_cu *> per_cu = nullptr;
+
   /* The "id" that distinguishes this CU/TU.
      .debug_info calls this "dwo_id", .debug_types calls this "signature".
      Since signatures came first, we stick with it for consistency.  */
diff --git a/gdb/dwarf2/index-write.c b/gdb/dwarf2/index-write.c
index 1fd6cd7f32be..2b92b2cae472 100644
--- a/gdb/dwarf2/index-write.c
+++ b/gdb/dwarf2/index-write.c
@@ -22,6 +22,7 @@
 
 #include "addrmap.h"
 #include "cli/cli-decode.h"
+#include "cli/cli-style.h"
 #include "exceptions.h"
 #include "gdbsupport/byte-vector.h"
 #include "gdbsupport/enumerate.h"
@@ -41,6 +42,7 @@
 #include "dwarf2/tag.h"
 #include "dwarf2/read-debug-names.h"
 #include "extract-store-integer.h"
+#include "ui-out.h"
 
 #include <algorithm>
 #include <map>
@@ -633,6 +635,10 @@ write_address_map (const addrmap *addrmap, data_buf &addr_vec,
 		       addrmap_index_data.previous_cu_index);
 }
 
+/* Is this symbol a compile unit, local type unit (type unit in the main file)
+   or foreign type unit (type unit in a .dwo file)?  */
+enum class unit_kind { cu, local_tu, foreign_tu };
+
 /* Return true if TU is a foreign type unit, that is a type unit defined in a
    .dwo file without a corresponding skeleton in the main file.  */
 
@@ -645,6 +651,22 @@ is_foreign_tu (const signatured_type *tu)
   return endswith (tu->section ()->get_name (), ".dwo");
 }
 
+/* Determine the unit_kind for PER_CU.  */
+
+static unit_kind
+classify_unit (const dwarf2_per_cu &per_cu)
+{
+  if (auto sig_type = per_cu.as_signatured_type (); sig_type != nullptr)
+    {
+      if (is_foreign_tu (sig_type))
+	return unit_kind::foreign_tu;
+      else
+	return unit_kind::local_tu;
+    }
+  else
+    return unit_kind::cu;
+}
+
 /* DWARF-5 .debug_names builder.  */
 class debug_names
 {
@@ -668,9 +690,6 @@ class debug_names
     return dwarf5_is_dwarf64 ? 8 : 4;
   }
 
-  /* Is this symbol from DW_TAG_compile_unit or DW_TAG_type_unit?  */
-  enum class unit_kind { cu, tu };
-
   /* Insert one symbol.  */
   void insert (const cooked_index_entry *entry)
   {
@@ -744,9 +763,8 @@ class debug_names
 
 	    for (dwarf2_per_cu *one_cu : per_cus)
 	      {
-		unit_kind kind = (entry->per_cu->is_debug_types ()
-				  ? unit_kind::tu
-				  : unit_kind::cu);
+		unit_kind kind = classify_unit (*one_cu);
+
 		/* Some Ada parentage is synthesized by the reader and so
 		   must be ignored here.  */
 		const cooked_index_entry *parent = entry->get_parent ();
@@ -773,6 +791,15 @@ class debug_names
 		       : DW_IDX_type_unit);
 		    m_abbrev_table.append_unsigned_leb128 (DW_FORM_udata);
 
+		    /* For foreign TUs only: the index of a CU that can be used
+		       to locate the .dwo file containing that TU.  */
+		    if (kind == unit_kind::foreign_tu)
+		      {
+			m_abbrev_table.append_unsigned_leb128
+			  (DW_IDX_compile_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);
@@ -839,6 +866,69 @@ class debug_names
 		gdb_assert (it != m_cu_index_htab.cend ());
 		m_entry_pool.append_unsigned_leb128 (it->second);
 
+		/* For foreign TUs only: the index of a CU that can be used to
+		   locate the .dwo file containing that TU.
+
+		   This code implements this snippet of the DWARF 5 standard,
+		   from section 6.1.1.2, "Structure of the Name Index":
+
+		     When an index entry refers to a foreign type unit, it may
+		     have attributes for both CU and (foreign) TU.  For such
+		     entries, the CU attribute gives the consumer a reference
+		     to the CU that may be used to locate a split DWARF object
+		     file that contains the type unit
+
+		   So, the idea here is to find a CU in the same .dwo file as
+		   the type unit (there will typically be one) and write the
+		   offset to its skeleton.  */
+		if (kind == unit_kind::foreign_tu)
+		  {
+		    /* If we know about this TU that is only listed in some .dwo
+		       file, it means that we looked up the .dwo file from a CU
+		       skeleton at some point during indexing, and recorded it
+		       in ENTRY->PER_CU.  */
+		    gdb_assert (entry->per_cu != nullptr);
+
+		    signatured_type *sig_type
+		      = entry->per_cu->as_signatured_type ();
+
+		    /* We know it's a TU, because it has been classified as
+		       foreign_tu.  */
+		    gdb_assert (sig_type != nullptr);
+
+		    /* We know it has a dwo_unit, because it's foreign.  */
+		    gdb_assert (sig_type->dwo_unit != nullptr);
+
+		    /* Find a CU in the same .dwo file as the TU.  */
+		    dwarf2_per_cu *per_cu = nullptr;
+		    for (auto &dwo_cu : sig_type->dwo_unit->dwo_file->cus)
+		      if (dwo_cu->per_cu != nullptr)
+			{
+			  per_cu = dwo_cu->per_cu;
+			  break;
+			}
+
+		    /* It would be really weird to not find a CU that we looked
+		       up in this .dwo file.  Otherwise, how would we know about
+		       this foreign TU?  But it might be possible to craft a bad
+		       faith .dwo file that does this by having a one TU with a
+		       skeleton and one TU without a skeleton in the same .dwo
+		       file (i.e. non-standard DWARF 5).  So, just in case, we
+		       error out gracefully.  */
+		    if (per_cu == nullptr)
+		      error (_("Could not find a CU for foreign type unit in "
+			       "DWO file %ps"),
+				styled_string (file_name_style.style (),
+					       (sig_type->dwo_unit->dwo_file
+						->dwo_name.c_str ())));
+
+		    /* This is the CU that can be used to find the .dwo file
+		       containing the type unit, write its offset.  */
+		    const auto cu_it = m_cu_index_htab.find (per_cu);
+		    gdb_assert (cu_it != m_cu_index_htab.cend ());
+		    m_entry_pool.append_unsigned_leb128 (cu_it->second);
+		  }
+
 		/* DIE offset.  */
 		m_entry_pool.append_uint (dwarf5_offset_size (),
 					  m_dwarf5_byte_order,
@@ -1369,8 +1459,9 @@ struct unit_lists
   /* Compilation units.  */
   std::vector<const dwarf2_per_cu *> comp;
 
-  /* Type units.  */
-  std::vector<const signatured_type *> type;
+  /* Type units, local and foreign.  */
+  std::vector<const signatured_type *> local_type;
+  std::vector<const signatured_type *> foreign_type;
 };
 
 /* Get sorted (by section offset) lists of comp units and type units.  */
@@ -1381,16 +1472,28 @@ get_unit_lists (const dwarf2_per_bfd &per_bfd)
   unit_lists lists;
 
   for (const auto &unit : per_bfd.all_units)
-    if (const signatured_type *sig_type = unit->as_signatured_type ();
-	sig_type != nullptr)
-      lists.type.emplace_back (sig_type);
-    else
-      lists.comp.emplace_back (unit.get ());
+    switch (classify_unit (*unit))
+      {
+      case unit_kind::cu:
+	lists.comp.emplace_back (unit.get ());
+	break;
+
+      case unit_kind::local_tu:
+	lists.local_type.emplace_back (unit->as_signatured_type ());
+	break;
+
+      case unit_kind::foreign_tu:
+	lists.foreign_type.emplace_back (unit->as_signatured_type ());
+	break;
+      }
 
   auto by_sect_off = [] (const dwarf2_per_cu *lhs, const dwarf2_per_cu *rhs)
 		       { return lhs->sect_off () < rhs->sect_off (); };
 
-  /* Sort both lists, even though it is technically not always required:
+  auto by_sig = [] (const signatured_type *lhs, const signatured_type *rhs)
+		  { return lhs->signature < rhs->signature; };
+
+  /* Sort the lists, even though it is technically not always required:
 
       - while .gdb_index requires the CU list to be sorted, DWARF 5 doesn't
 	say anything about the order of CUs in .debug_names.
@@ -1400,7 +1503,8 @@ get_unit_lists (const dwarf2_per_bfd &per_bfd)
      However, it helps make sure that GDB produce a stable and predictable
      output, which is nice.  */
   std::sort (lists.comp.begin (), lists.comp.end (), by_sect_off);
-  std::sort (lists.type.begin (), lists.type.end (), by_sect_off);
+  std::sort (lists.local_type.begin (), lists.local_type.end (), by_sect_off);
+  std::sort (lists.foreign_type.begin (), lists.foreign_type.end (), by_sig);
 
   return lists;
 }
@@ -1428,10 +1532,9 @@ write_gdbindex (dwarf2_per_bfd *per_bfd, cooked_index *table,
      that DWARF 5's .debug_names does with "foreign type units".  If the
      executable has such skeletonless type units, refuse to produce an index,
      instead of producing a bogus one.  */
-  for (const signatured_type *tu : units.type)
-    if (is_foreign_tu (tu))
-      error (_("Found foreign (skeletonless) type unit, unable to produce "
-	       ".gdb_index.  Consider using .debug_names instead."));
+  if (!units.foreign_type.empty ())
+    error (_("Found foreign (skeletonless) type unit, unable to produce "
+	     ".gdb_index.  Consider using .debug_names instead."));
 
   int counter = 0;
 
@@ -1457,7 +1560,7 @@ write_gdbindex (dwarf2_per_bfd *per_bfd, cooked_index *table,
   /* Write type units.  */
   data_buf types_cu_list;
 
-  for (const signatured_type *sig_type : units.type)
+  for (const signatured_type *sig_type : units.local_type)
     {
       const auto insertpair = cu_index_htab.emplace (sig_type, counter);
       gdb_assert (insertpair.second);
@@ -1530,7 +1633,7 @@ write_debug_names (dwarf2_per_bfd *per_bfd, cooked_index *table,
 
   data_buf type_unit_list;
 
-  for (auto [i, per_cu] : gdb::ranges::views::enumerate (units.type))
+  for (auto [i, per_cu] : gdb::ranges::views::enumerate (units.local_type))
     {
       nametable.add_cu (per_cu, i);
       type_unit_list.append_uint (nametable.dwarf5_offset_size (),
@@ -1538,9 +1641,21 @@ write_debug_names (dwarf2_per_bfd *per_bfd, cooked_index *table,
 				  to_underlying (per_cu->sect_off ()));
     }
 
+  data_buf foreign_type_unit_list;
+
+  for (auto [i, sig_type] : gdb::ranges::views::enumerate (units.foreign_type))
+    {
+      /* The numbering of foreign type units follows the numbering of local type
+	 units.  */
+      nametable.add_cu (sig_type, units.local_type.size () + i);
+      foreign_type_unit_list.append_uint (8, dwarf5_byte_order,
+					  sig_type->signature);
+    }
+
   /* Verify that all units are represented.  */
   gdb_assert (units.comp.size () == per_bfd->num_comp_units);
-  gdb_assert (units.type.size () == per_bfd->num_type_units);
+  gdb_assert (units.local_type.size () + units.foreign_type.size ()
+	      == per_bfd->num_type_units);
 
   for (const cooked_index_entry *entry : table->all_entries ())
     nametable.insert (entry);
@@ -1557,6 +1672,7 @@ write_debug_names (dwarf2_per_bfd *per_bfd, cooked_index *table,
   expected_bytes += bytes_of_header;
   expected_bytes += comp_unit_list.size ();
   expected_bytes += type_unit_list.size ();
+  expected_bytes += foreign_type_unit_list.size ();
   expected_bytes += nametable.bytes ();
   data_buf header;
 
@@ -1583,11 +1699,11 @@ write_debug_names (dwarf2_per_bfd *per_bfd, cooked_index *table,
 
   /* local_type_unit_count - The number of TUs in the local TU
      list.  */
-  header.append_uint (4, dwarf5_byte_order, units.type.size ());
+  header.append_uint (4, dwarf5_byte_order, units.local_type.size ());
 
   /* foreign_type_unit_count - The number of TUs in the foreign TU
      list.  */
-  header.append_uint (4, dwarf5_byte_order, 0);
+  header.append_uint (4, dwarf5_byte_order, units.foreign_type.size ());
 
   /* bucket_count - The number of hash buckets in the hash lookup
      table.  GDB does not use the hash table, so there's also no need
@@ -1613,6 +1729,7 @@ write_debug_names (dwarf2_per_bfd *per_bfd, cooked_index *table,
   header.file_write (out_file);
   comp_unit_list.file_write (out_file);
   type_unit_list.file_write (out_file);
+  foreign_type_unit_list.file_write (out_file);
   nametable.file_write (out_file, out_file_str);
 
   assert_file_size (out_file, expected_bytes);
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 9cb7b83cc339..bcf62a21d80d 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -7179,6 +7179,15 @@ cutu_reader::lookup_dwo_cutu (dwarf2_cu *cu, const char *dwo_name,
 		("DWO %s %s(%s) found: @%s", kind, dwo_name,
 		 hex_string (signature),
 		 host_address_to_string (dwo_unit_it->get ()));
+
+	      /* Record the dwarf2_per_cu that was used to look up this
+		 dwo_unit.  There will typically be exactly one skeleton
+		 pointing to each DWO CU.  But if for some mysterious reason
+		 there are multiple skeletons pointing to the same DWO CU, it's
+		 fine, we just need to remember one.  This will keep the last
+		 one seen.  */
+	      (*dwo_unit_it)->per_cu = cu->per_cu;
+
 	      return dwo_unit_it->get ();
 	    }
 	}
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index 63c3129ad9a8..4477d85361ef 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -277,6 +277,7 @@ struct dwarf2_per_cu
   /* If this dwarf2_per_cu is a signatured_type, return "this" cast to
      signatured_type.  Otherwise, return nullptr.  */
   signatured_type *as_signatured_type ();
+  const signatured_type *as_signatured_type () const;
 
   dwarf2_per_bfd *per_bfd () const
   { return m_per_bfd; }
@@ -496,6 +497,17 @@ dwarf2_per_cu::as_signatured_type ()
   return nullptr;
 }
 
+/* See dwarf2_per_cu declaration.  */
+
+inline const signatured_type *
+dwarf2_per_cu::as_signatured_type () const
+{
+  if (m_is_debug_types)
+    return static_cast<const signatured_type *> (this);
+
+  return nullptr;
+}
+
 /* Hash a signatured_type object based on its signature.  */
 
 struct signatured_type_hash
-- 
2.54.0


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

* [PATCH v3 5/7] gdb/dwarf: add debug output in read-debug-names.c
  2026-05-11 15:40 [PATCH v3 0/7] Handle foreign type units in .debug_names Simon Marchi
                   ` (3 preceding siblings ...)
  2026-05-11 15:40 ` [PATCH v3 4/7] gdb/dwarf: generate foreign type units in .debug_names Simon Marchi
@ 2026-05-11 15:40 ` Simon Marchi
  2026-05-11 15:40 ` [PATCH v3 6/7] gdb/dwarf: add more context to complaints in mapped_debug_names_reader::scan_one_entry Simon Marchi
  2026-05-11 15:40 ` [PATCH v3 7/7] gdb/dwarf: read foreign type units Simon Marchi
  6 siblings, 0 replies; 12+ messages in thread
From: Simon Marchi @ 2026-05-11 15:40 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi, Tom Tromey

From: Simon Marchi <simon.marchi@polymtl.ca>

Add a bunch of debug prints in read-debug-names.c to dump what we are
reading.  Some of if was helpful to me when debugging my changes to the
.debug_names reader and writer.

The debug prints are activated by "set debug dwarf-read" (I didn't feel
like we needed a separate knob for that).  There are two levels of
verbosity.  At level 1, we print everything that happens once, like the
header, counts, offsets of important parts of the index, etc.  At
levels > 1, we print information about each abbrev, CU/TU and index
entry.

The output is made such that it is easy look at the output of readelf of
llvm-dwarfdump on the side and confirm that GDB is reading the index
right.

Here are some examples of the output.

Header and some abbrevs:

    [dwarf-read] read_debug_names_from_section: start: reading .debug_names from /home/simark/build/babeltrace/src/lib/.libs/libbabeltrace2.so.0.0.0
      [dwarf-read] read_debug_names_from_section: section size: 0x19d4e, initial length: 0x19d4a, dwarf64: 0, offset_size: 4
      [dwarf-read] read_debug_names_from_section: version: 5
      [dwarf-read] read_debug_names_from_section: cu_count: 56, tu_count: 0, foreign_tu_count: 0
      [dwarf-read] read_debug_names_from_section: bucket_count: 0, name_count: 3418, abbrev_table_size: 374, augmentation_string_size: 8
      [dwarf-read] read_debug_names_from_section: augmentation string: "GDB3\x00\x00\x00\x00"
      [dwarf-read] read_debug_names_from_section:   abbrev 1: tag DW_TAG_namespace, 4 attributes
      [dwarf-read] read_debug_names_from_section:     DW_IDX_compile_unit DW_FORM_udata
      [dwarf-read] read_debug_names_from_section:     DW_IDX_die_offset DW_FORM_ref_addr
      [dwarf-read] read_debug_names_from_section:     DW_IDX_GNU_language DW_FORM_udata
      [dwarf-read] read_debug_names_from_section:     DW_IDX_parent DW_FORM_data4
      [dwarf-read] read_debug_names_from_section:   abbrev 2: tag DW_TAG_enumerator, 4 attributes
      [dwarf-read] read_debug_names_from_section:     DW_IDX_compile_unit DW_FORM_udata
      [dwarf-read] read_debug_names_from_section:     DW_IDX_die_offset DW_FORM_ref_addr
      [dwarf-read] read_debug_names_from_section:     DW_IDX_GNU_language DW_FORM_udata
      [dwarf-read] read_debug_names_from_section:     DW_IDX_GNU_internal DW_FORM_flag_present

Excerpt of the CU list:

    [dwarf-read] build_and_check_cu_list_from_debug_names: building CU list from .debug_names (56 CUs)
    [dwarf-read] build_and_check_cu_list_from_debug_names:   CU 0: offset 0x0
    [dwarf-read] build_and_check_cu_list_from_debug_names:   CU 1: offset 0x1bac
    [dwarf-read] build_and_check_cu_list_from_debug_names:   CU 2: offset 0x4262
    [dwarf-read] build_and_check_cu_list_from_debug_names:   CU 3: offset 0x4ff0
    [dwarf-read] build_and_check_cu_list_from_debug_names:   CU 4: offset 0x59e9
    [dwarf-read] build_and_check_cu_list_from_debug_names:   CU 5: offset 0x67af
    [dwarf-read] build_and_check_cu_list_from_debug_names:   CU 6: offset 0x8e1a
    [dwarf-read] build_and_check_cu_list_from_debug_names:   CU 7: offset 0xa5cc
    [dwarf-read] build_and_check_cu_list_from_debug_names:   CU 8: offset 0xcd39

Some entries:

    [dwarf-read] scan_all_names: start: scanning 3418 names from .debug_names
      [dwarf-read] scan_entries: scanning entries for name 1: "(anonymous namespace)" (entry pool offset 0x0)
      [dwarf-read] scan_one_entry:   entry pool offset 0x0: abbrev 1, tag DW_TAG_namespace
      [dwarf-read] scan_one_entry:     DW_IDX_compile_unit (DW_FORM_udata): 55
      [dwarf-read] scan_one_entry:     DW_IDX_die_offset (DW_FORM_ref_addr): 0x7d06b
      [dwarf-read] scan_one_entry:     DW_IDX_GNU_language (DW_FORM_udata): 33
      [dwarf-read] scan_one_entry:     DW_IDX_parent (DW_FORM_data4): 38201
      [dwarf-read] scan_one_entry:     -> die_offset 0x7d06b, per_cu offset 0x5af0e
      [dwarf-read] scan_one_entry:   entry pool offset 0xb: end of entries (abbrev 0)
      [dwarf-read] scan_entries: scanning entries for name 2: "AUTO_SEEK_STREAM_STATE_PACKET_BEGAN" (entry pool offset 0xc)
      [dwarf-read] scan_one_entry:   entry pool offset 0xc: abbrev 2, tag DW_TAG_enumerator
      [dwarf-read] scan_one_entry:     DW_IDX_compile_unit (DW_FORM_udata): 17
      [dwarf-read] scan_one_entry:     DW_IDX_die_offset (DW_FORM_ref_addr): 0x1beea
      [dwarf-read] scan_one_entry:     DW_IDX_GNU_language (DW_FORM_udata): 29
      [dwarf-read] scan_one_entry:     DW_IDX_GNU_internal (DW_FORM_flag_present): 1
      [dwarf-read] scan_one_entry:     -> die_offset 0x1beea, per_cu offset 0x1a28e
      [dwarf-read] scan_one_entry:   entry pool offset 0x13: end of entries (abbrev 0)

Change-Id: I4a40bfb73fa7feccb5038814a7d1a1bcd4f6231b
Approved-By: Tom Tromey <tom@tromey.com>
---
 gdb/dwarf2/read-debug-names.c | 120 +++++++++++++++++++++++++++++++++-
 gdb/dwarf2/read.c             |  24 +------
 gdb/dwarf2/read.h             |  24 +++++++
 3 files changed, 143 insertions(+), 25 deletions(-)

diff --git a/gdb/dwarf2/read-debug-names.c b/gdb/dwarf2/read-debug-names.c
index 8e488377439b..6ac65d101b16 100644
--- a/gdb/dwarf2/read-debug-names.c
+++ b/gdb/dwarf2/read-debug-names.c
@@ -170,7 +170,12 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
   const ULONGEST abbrev = read_unsigned_leb128 (abfd, entry, &bytes_read);
   entry += bytes_read;
   if (abbrev == 0)
-    return nullptr;
+    {
+      dwarf_read_debug_printf_v
+	("  entry pool offset 0x%tx: end of entries (abbrev 0)",
+	 offset_in_entry_pool);
+      return nullptr;
+    }
 
   const auto indexval_it = abbrev_map.find (abbrev);
   if (indexval_it == abbrev_map.cend ())
@@ -182,6 +187,12 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
     }
 
   const auto &indexval = indexval_it->second;
+
+  dwarf_read_debug_printf_v
+    ("  entry pool offset 0x%tx: abbrev %s, tag %s",
+     offset_in_entry_pool, pulongest (abbrev),
+     dwarf_tag_name (indexval.dwarf_tag));
+
   cooked_index_flag flags = 0;
   sect_offset die_offset {};
   enum language lang = language_unknown;
@@ -238,6 +249,14 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
 				  bfd_get_filename (abfd)));
 	  return nullptr;
 	}
+
+      dwarf_read_debug_printf_v ("    %s (%s): %s",
+				 get_DW_IDX_name (attr.dw_idx),
+				 dwarf_form_name (attr.form),
+				 (attr.form == DW_FORM_ref_addr
+				  ? hex_string (ull)
+				  : pulongest (ull)));
+
       switch (attr.dw_idx)
 	{
 	case DW_IDX_compile_unit:
@@ -302,6 +321,11 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
   /* Skip if we couldn't find a valid CU/TU index.  */
   if (per_cu != nullptr)
     {
+      dwarf_read_debug_printf_v
+	("    -> die_offset %s, per_cu offset %s",
+	 sect_offset_str (die_offset),
+	 sect_offset_str (per_cu->sect_off ()));
+
       *result
 	= indices[next_shard].add (die_offset, (dwarf_tag) indexval.dwarf_tag,
 				   flags, lang, name, nullptr, per_cu);
@@ -312,6 +336,8 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
 
       entry_pool_offsets_to_entries.emplace (offset_in_entry_pool, *result);
     }
+  else
+    dwarf_read_debug_printf_v ("    -> no valid CU/TU, skipping");
 
   return entry;
 }
@@ -323,6 +349,12 @@ mapped_debug_names_reader::scan_entries (uint32_t index,
 					 const char *name,
 					 const gdb_byte *entry)
 {
+  /* Print a 1-based index, because this is what readelf and llvm-dwarfdump
+     do.  This makes it easier to compare output side-by-side.  */
+  dwarf_read_debug_printf_v
+    ("scanning entries for name %u: \"%s\" (entry pool offset 0x%tx)",
+     index + 1, name, entry - entry_pool);
+
   std::vector<cooked_index_entry *> these_entries;
 
   while (true)
@@ -347,6 +379,9 @@ mapped_debug_names_reader::scan_entries (uint32_t index,
 void
 mapped_debug_names_reader::scan_all_names ()
 {
+  DWARF_READ_SCOPED_DEBUG_START_END
+    ("scanning %u names from .debug_names", name_count);
+
   all_entries.resize (name_count);
 
   /* In the first pass, create all the entries.  */
@@ -377,6 +412,9 @@ mapped_debug_names_reader::scan_all_names ()
 
      Otherwise, the DW_IDX_parent value is an offset into the entry pool, which
      is not ambiguous.  */
+  dwarf_read_debug_printf ("resolving %zu parent pointers",
+			   needs_parent.size ());
+
   for (auto &[entry, parent_val] : needs_parent)
     {
       if (augmentation_is_gdb && gdb_augmentation_version == 2)
@@ -463,6 +501,9 @@ build_and_check_tu_list_from_debug_names (dwarf2_per_objfile *per_objfile,
 					  mapped_debug_names_reader &map,
 					  dwarf2_section_info *section)
 {
+  dwarf_read_debug_printf ("building TU list from .debug_names (%u TUs)",
+			   map.tu_count);
+
   struct objfile *objfile = per_objfile->objfile;
   dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
 
@@ -477,6 +518,9 @@ build_and_check_tu_list_from_debug_names (dwarf2_per_objfile *per_objfile,
 			  map.offset_size,
 			  map.dwarf5_byte_order));
 
+      dwarf_read_debug_printf_v ("  TU %u: offset %s", i,
+				 sect_offset_str (sect_off));
+
       /* Find the matching dwarf2_per_cu.  */
       dwarf2_per_cu *per_cu = dwarf2_find_unit ({ section, sect_off },
 						per_bfd);
@@ -508,15 +552,24 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile,
 			       struct dwarf2_section_info *section,
 			       mapped_debug_names_reader &map)
 {
+  DWARF_READ_SCOPED_DEBUG_START_END
+    ("reading .debug_names from %s", filename);
+
   struct objfile *objfile = per_objfile->objfile;
 
   if (section->empty ())
-    return false;
+    {
+      dwarf_read_debug_printf ("section is empty");
+      return false;
+    }
 
   /* Older elfutils strip versions could keep the section in the main
      executable while splitting it for the separate debug info file.  */
   if ((section->get_flags () & SEC_HAS_CONTENTS) == 0)
-    return false;
+    {
+      dwarf_read_debug_printf ("section has no contents");
+      return false;
+    }
 
   section->read (objfile);
 
@@ -534,6 +587,11 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile,
 
   map.dwarf5_is_dwarf64 = bytes_read != 4;
   map.offset_size = map.dwarf5_is_dwarf64 ? 8 : 4;
+
+  dwarf_read_debug_printf ("section size: %s, initial length: %s, "
+			   "dwarf64: %d, offset_size: %d",
+			   hex_string (section->size), hex_string (length),
+			   map.dwarf5_is_dwarf64, map.offset_size);
   if (bytes_read + length != section->size)
     {
       /* There may be multiple per-CU indices.  */
@@ -549,6 +607,9 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile,
   /* The version number.  */
   uint16_t version = read_2_bytes (abfd, addr);
   addr += 2;
+
+  dwarf_read_debug_printf ("version: %d", version);
+
   if (version != 5)
     {
       warning (_("Section .debug_names in %ps has unsupported version %d, "
@@ -585,6 +646,11 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile,
      list.  */
   uint32_t foreign_tu_count = read_4_bytes (abfd, addr);
   addr += 4;
+
+  dwarf_read_debug_printf ("cu_count: %u, tu_count: %u, "
+			   "foreign_tu_count: %u",
+			   map.cu_count, map.tu_count, foreign_tu_count);
+
   if (foreign_tu_count != 0)
     {
       warning (_("Section .debug_names in %ps has unsupported %lu foreign TUs, "
@@ -613,11 +679,34 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile,
      string.  This value is rounded up to a multiple of 4.  */
   uint32_t augmentation_string_size = read_4_bytes (abfd, addr);
   addr += 4;
+
+  dwarf_read_debug_printf ("bucket_count: %u, name_count: %u, "
+			   "abbrev_table_size: %u, "
+			   "augmentation_string_size: %u",
+			   map.bucket_count, map.name_count,
+			   abbrev_table_size, augmentation_string_size);
+
   augmentation_string_size += (-augmentation_string_size) & 3;
 
   const auto augmentation_string
     = gdb::make_array_view (addr, augmentation_string_size);
 
+  if (dwarf_read_debug >= 1)
+    {
+      std::string aug_repr;
+      for (size_t i = 0; i < augmentation_string_size; i++)
+	{
+	  gdb_byte b = addr[i];
+	  if (c_isprint (b))
+	    aug_repr += b;
+	  else
+	    aug_repr += string_printf ("\\x%02x", b);
+	}
+
+      dwarf_read_debug_printf ("augmentation string: \"%s\"",
+			       aug_repr.c_str ());
+    }
+
   if (augmentation_string == gdb::make_array_view (dwarf5_augmentation_1))
     {
       warning (_(".debug_names created by an old version of gdb; ignoring"));
@@ -704,7 +793,26 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile,
 	    break;
 	  indexval.attr_vec.push_back (std::move (attr));
 	}
+
+      dwarf_read_debug_printf_v
+	("  abbrev %s: tag %s, %zu attributes",
+	 pulongest (index_num), dwarf_tag_name (indexval.dwarf_tag),
+	 indexval.attr_vec.size ());
+
+      for (const auto &attr : indexval.attr_vec)
+	dwarf_read_debug_printf_v
+	  ("    %s %s%s",
+	   get_DW_IDX_name (attr.dw_idx),
+	   dwarf_form_name (attr.form),
+	   (attr.form == DW_FORM_implicit_const
+	    ? string_printf (" (%s)",
+			     plongest (attr.implicit_const)).c_str ()
+	    : ""));
     }
+
+  dwarf_read_debug_printf ("%zu abbreviations read",
+			   map.abbrev_map.size ());
+
   if (addr != abbrev_table_start + abbrev_table_size)
     {
       warning (_("Section .debug_names in %ps has abbreviation_table "
@@ -728,6 +836,9 @@ build_and_check_cu_list_from_debug_names (dwarf2_per_bfd *per_bfd,
 					  mapped_debug_names_reader &map,
 					  dwarf2_section_info &section)
 {
+  dwarf_read_debug_printf ("building CU list from .debug_names (%u CUs)",
+			   map.cu_count);
+
   int nr_cus = per_bfd->num_comp_units;
 
   if (map.cu_count != nr_cus)
@@ -745,6 +856,9 @@ build_and_check_cu_list_from_debug_names (dwarf2_per_bfd *per_bfd,
 			  map.offset_size,
 			  map.dwarf5_byte_order));
 
+      dwarf_read_debug_printf_v ("  CU %u: offset %s", i,
+				 sect_offset_str (sect_off));
+
       /* Find the matching dwarf2_per_cu.  */
       dwarf2_per_cu *per_cu = dwarf2_find_unit ({ &section, sect_off }, per_bfd);
 
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index bcf62a21d80d..d061bc22b12d 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -99,28 +99,8 @@
 #include "extract-store-integer.h"
 #include "cli/cli-style.h"
 
-/* When == 1, print basic high level tracing messages.
-   When > 1, be more verbose.
-   This is in contrast to the low level DIE reading of dwarf_die_debug.  */
-static unsigned int dwarf_read_debug = 0;
-
-/* Print a "dwarf-read" debug statement if dwarf_read_debug is >= 1.  */
-
-#define dwarf_read_debug_printf(fmt, ...) \
-  debug_prefixed_printf_cond (dwarf_read_debug >= 1, "dwarf-read", fmt, \
-			      ##__VA_ARGS__)
-
-/* Print a "dwarf-read" debug statement if dwarf_read_debug is >= 2.  */
-
-#define dwarf_read_debug_printf_v(fmt, ...) \
-  debug_prefixed_printf_cond (dwarf_read_debug >= 2, "dwarf-read", fmt, \
-			      ##__VA_ARGS__)
-
-/* Print "dwarf-read" start/end debug statements.  */
-
-#define DWARF_READ_SCOPED_DEBUG_START_END(fmt, ...)                           \
-  scoped_debug_start_end ([] { return dwarf_read_debug >= 1; }, "dwarf-read", \
-			  fmt, ##__VA_ARGS__)
+/* See read.h.  */
+unsigned int dwarf_read_debug = 0;
 
 /* When non-zero, dump DIEs after they are read in.  */
 static unsigned int dwarf_die_debug = 0;
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index 4477d85361ef..a0811fe0d377 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -1209,6 +1209,30 @@ type *dwarf2_fetch_die_type_sect_off (sect_offset sect_off,
 				      dwarf2_per_objfile *per_objfile,
 				      const char **var_name = nullptr);
 
+/* When == 1, print basic high level tracing messages.
+   When > 1, be more verbose.
+   This is in contrast to the low level DIE reading of dwarf_die_debug.  */
+
+extern unsigned int dwarf_read_debug;
+
+/* Print a "dwarf-read" debug statement if dwarf_read_debug is >= 1.  */
+
+#define dwarf_read_debug_printf(fmt, ...) 				\
+  debug_prefixed_printf_cond (dwarf_read_debug >= 1, "dwarf-read", fmt,	\
+			      ##__VA_ARGS__)
+
+/* Print a "dwarf-read" debug statement if dwarf_read_debug is >= 2.  */
+
+#define dwarf_read_debug_printf_v(fmt, ...) \
+  debug_prefixed_printf_cond (dwarf_read_debug >= 2, "dwarf-read", fmt,	\
+			      ##__VA_ARGS__)
+
+/* Print "dwarf-read" start/end debug statements.  */
+
+#define DWARF_READ_SCOPED_DEBUG_START_END(fmt, ...)			      \
+  scoped_debug_start_end ([] { return dwarf_read_debug >= 1; }, "dwarf-read", \
+			  fmt, ##__VA_ARGS__)
+
 /* When non-zero, dump line number entries as they are read in.  */
 extern unsigned int dwarf_line_debug;
 
-- 
2.54.0


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

* [PATCH v3 6/7] gdb/dwarf: add more context to complaints in mapped_debug_names_reader::scan_one_entry
  2026-05-11 15:40 [PATCH v3 0/7] Handle foreign type units in .debug_names Simon Marchi
                   ` (4 preceding siblings ...)
  2026-05-11 15:40 ` [PATCH v3 5/7] gdb/dwarf: add debug output in read-debug-names.c Simon Marchi
@ 2026-05-11 15:40 ` Simon Marchi
  2026-05-11 15:40 ` [PATCH v3 7/7] gdb/dwarf: read foreign type units Simon Marchi
  6 siblings, 0 replies; 12+ messages in thread
From: Simon Marchi @ 2026-05-11 15:40 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi, Tom Tromey

From: Simon Marchi <simon.marchi@polymtl.ca>

I realize that complaints are not often enabled and shown, but if we
emit them, I think they should include enough context to help a user who
wishes to dig in the problem to pinpoint where the problem happened
exactly.  For this reason, change the complaints in
mapped_debug_names_reader::scan_one_entry to include precisions
regarding which index entry is problematic exactly.

I am not attached to the particular format.  I thought that because this
is extra contextual information, I would put it in the square brackets
at the end (which only shows the module name currently).  However, it
would be nice to converge towards a format that we could replicate
elsewhere in the DWARF reader, so feel free to chime in.

I factored out the formatting to a separate function, because I will be
adding more of these in a subsequent patch, and it's easier to have the
formatting centralized.

I have not touched the "Unsupported .debug_names form" warning.  If that
comes up, we probably don't care about the specific index entry, we just
need to implement support for that form.

Change-Id: Id68a0ae406a3ca620408576b81f893f05c7e3df2
Approved-By: Tom Tromey <tom@tromey.com>
---
 gdb/dwarf2/read-debug-names.c | 43 ++++++++++++++++++++++++++---------
 1 file changed, 32 insertions(+), 11 deletions(-)

diff --git a/gdb/dwarf2/read-debug-names.c b/gdb/dwarf2/read-debug-names.c
index 6ac65d101b16..487e2ea87edb 100644
--- a/gdb/dwarf2/read-debug-names.c
+++ b/gdb/dwarf2/read-debug-names.c
@@ -155,6 +155,25 @@ struct mapped_debug_names_reader
   std::vector<std::vector<cooked_index_entry *>> all_entries;
 };
 
+/* Emit a complaint about a specific index entry.  */
+
+static void ATTRIBUTE_PRINTF (4, 5)
+complain_about_index_entry (bfd *abfd, const char *name,
+			    ptrdiff_t offset_in_entry_pool, const char *fmt,
+			    ...)
+{
+  va_list ap;
+  va_start (ap, fmt);
+  std::string msg = string_vprintf (fmt, ap);
+  va_end (ap);
+
+  msg += string_printf (_(" [in module %s, index entry for name %s,"
+			   " entry pool offset 0x%tx]"),
+			bfd_get_filename (abfd), name, offset_in_entry_pool);
+
+  complaint ("%s", msg.c_str ());
+}
+
 /* Scan a single entry from the entries table.  Set *RESULT and PARENT
    (if needed) and return the updated pointer on success, or return
    nullptr on error, or at the end of the table.  */
@@ -180,9 +199,11 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
   const auto indexval_it = abbrev_map.find (abbrev);
   if (indexval_it == abbrev_map.cend ())
     {
-      complaint (_("Wrong .debug_names undefined abbrev code %s "
-		   "[in module %s]"),
-		 pulongest (abbrev), bfd_get_filename (abfd));
+      complain_about_index_entry (abfd, name, offset_in_entry_pool,
+				  _("Wrong .debug_names abbrev code %s"),
+				  pulongest (abbrev));
+      /* We can't go past this entry because we don't know its size, stop
+	 reading this entry chain.  */
       return nullptr;
     }
 
@@ -264,10 +285,10 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
 	    /* Don't crash on bad data.  */
 	    if (ull >= this->comp_units.size ())
 	      {
-		complaint (_(".debug_names entry has bad CU index %s"
-			     " [in module %s]"),
-			   pulongest (ull),
-			   bfd_get_filename (abfd));
+		complain_about_index_entry
+		  (abfd, name, offset_in_entry_pool,
+		   _(".debug_names entry has bad CU index %s"),
+		   pulongest (ull));
 		continue;
 	      }
 
@@ -279,10 +300,10 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
 	    /* Don't crash on bad data.  */
 	    if (ull >= this->type_units.size ())
 	      {
-		complaint (_(".debug_names entry has bad TU index %s"
-			     " [in module %s]"),
-			   pulongest (ull),
-			   bfd_get_filename (abfd));
+		complain_about_index_entry
+		  (abfd, name, offset_in_entry_pool,
+		   _(".debug_names entry has bad TU index %s"),
+		   pulongest (ull));
 		continue;
 	      }
 
-- 
2.54.0


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

* [PATCH v3 7/7] gdb/dwarf: read foreign type units
  2026-05-11 15:40 [PATCH v3 0/7] Handle foreign type units in .debug_names Simon Marchi
                   ` (5 preceding siblings ...)
  2026-05-11 15:40 ` [PATCH v3 6/7] gdb/dwarf: add more context to complaints in mapped_debug_names_reader::scan_one_entry Simon Marchi
@ 2026-05-11 15:40 ` Simon Marchi
  2026-05-19 19:42   ` Guinevere Larsen
  6 siblings, 1 reply; 12+ messages in thread
From: Simon Marchi @ 2026-05-11 15:40 UTC (permalink / raw)
  To: gdb-patches; +Cc: Simon Marchi, Tom Tromey

In DWARF 5, foreign type units are type units present in .dwo files.
Type units in .dwo files don't have a matching skeleton in the main
file.  When an .debug_names index is present, it can't include those
type units in the regular type unit list, since that list specifies
offsets in the main file.  They are instead listed in the foreign TU
list, which is basically just a list of type signatures.

In order to help the debugger locate these units (i.e. find the .dwo
file containing them), individual index entries referencing foreign type
units may also include a reference to a compile unit that the debugger
can follow to find the appropriate .dwo file.

This patch implements reading the .debug_names foreign TU list and using
these "hint" CUs to locate foreign type units.  I use the term "hint"
throughout the code, but I don't mind another name if someone has a
better idea.

The first part is to read the actual foreign TU list from the
.debug_names index and create signatured_type objects out of them.  This
is done in the new function create_foreign_type_units_from_debug_names.
Append the newly created signatured_type to the
mapped_debug_names_reader::foreign_type_units vector, which will be used
later to resolve DW_IDX_type_unit indices.  Populate the
dwarf2_per_bfd::signatured_types set, which contains signatured_types
indexed by signature.  And finally, transfer ownership of the object to
the dwarf2_per_bfd::all_units vector.

Previously, all dwarf2_per_cu (including signatured_type) objects were
created with a non-nullptr section.  With foreign type units, we don't
know the section at creation time.  We also don't know the offset into
section nor the size of the unit.  Therefore, add a
dwarf2_per_bfd::allocate_signatured_type overload that takes just the
signature.  Remove the "section != nullptr" assert from the
dwarf2_per_cu constructor, but add it to the other allocate_* methods.

Since the new create_foreign_type_units_from_debug_names function adds
items to the dwarf2_per_bfd::all_units vector, the vector needs to be
sorted after create_foreign_type_units_from_debug_names runs.  Remove
the finalize_all_units call from create_all_units, making the callers
responsible to call it.

The next step is to read the hint CU attributes when scanning the index
entries.  Rework mapped_debug_names_reader::scan_one_entry to remember
which kind of unit references it saw for the entry (comp, type and/or
foreign type) and then figure out what this means.  The logic is:

 - Did the entry reference a foreign type unit?  If so, it's a foreign
   type unit.  Does it also reference a hint CU?  If not, drop the
   entry, there's nothing useful we can do with it.

 - Otherwise, did the entry reference a (non-foreign) type unit?  Then
   it's a regular type unit. If so, it shouldn't also have a
   DW_IDX_compile_unit.

 - Otherwise, did the entry reference a comp unit?  If so, it's a comp
   unit.

 - Otherwise, we don't know what unit the entry references, it's an
   error.

Since the .debug_name index attaches hint CU attributes to individual
index entries, my initial implementation added the hint CU information
to the cooked_index_entry structure.  I am not sure why DWARF 5 chose to
do it this way, as opposed to attaching one hint per foreign TU.  Does
this mean that two type units with the same signature could be different
and, for a specific index entry, it would be important to find one
specific instance of the type unit over the others?  I have no idea.

However, I know that the current GDB DWARF reader is not able to load
multiple type units with the same signature but different content.  Once
it loads one type unit with a given signature, all subsequent references
to that signature will use that loaded type unit.  I therefore chose to
have the .debug_names reader record just one hint CU per foreign TU.
This avoids growing the cooked_index_entry structure for nothing, and
having to pass through this information through multiple layers.

The next step is to locate the .dwo file containing the foreign TUs when
we need them.  This sometimes makes use of the hint CU, but not always.
I identified these 3 code paths:

 1. When the type unit gets expanded directly, for instance if you use
    "ptype" and there is a direct match into the type unit.  This case
    is handled in load_full_type_unit, calling a new function
    fill_in_sig_entry_from_per_cu_hint.  This one uses the hint recorded
    by the .debug_names reader.  When a cooked index entry exists and
    refers to a foreign TU for which the section is not yet known, we
    know that there exists a hint, otherwise we wouldn't have created
    the entry in the first place.

 2. The second one is when the type unit is referenced by some other
    unit.  This case is handled in follow_die_sig_1, calling another new
    function fill_in_sig_entry_from_dwo_file.  In this case, we know
    which unit is referring to the TU, so we use that unit's dwo file to
    fill in the details.  As explained in the comment, this is sometimes
    just an optimization, but sometimes also necessary, if the TU
    does not have a hint, due to it not containing any indexed name.

 3. Similarly, in dwarf2_base_index_functions::expand_all_symtabs, we
    might have to handle foreign type units for which we don't have a
    hint.  I initially implemented something in two passes, to go dig in
    the dwo_file structures to find those TUs, but ended up choosing to
    just skip them, for the reasons explained in the comment there.

Setting a dwarf2_per_cu's section a posteriori breaks the assumed
ordering of the dwarf2_per_bfd::all_units vector.  After setting the
section, re-sort the vector.

Add a target board to exercise this new code.  This board builds with:

 - type units (-fdebug-types-section)
 - split DWARF (-gsplit-dwarf)
 - .debug_names index (created by GDB)

I ran the whole testsuite with this board file and it's not perfect, but
the results are comparable to the dwarf5-fission-debug-types board, for
instance.

There is one known failure that I am unable to get to the bottom of.  It
seems orthogonal to my change though, more like an indexer or symbol
reader issue.  There are maybe more of this kind, but this is one
example:

  FAIL: gdb.ada/tick_length_array_enum_idx.exp: ptype variable_table'length (GDB internal error)

  /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:1839: internal-error: search_one: Assertion `symtab != nullptr' failed.

The problem appears to be that a cooked index lookup for symbol
variable_table says that a given TU should contain a match.  But then
trying to expand the TU makes dw2_instantiate_symtab yield a nullptr
compunit_symtab, I think because the symbol reader found nothing
interesting symbol-wise.  And then the assert in search_one triggers.

The issue seems sensitive to some aspects of the environment (gnat
version?).  I am able to reproduce the issue on Arch Linux (gnat 15)
with:

  $ make check TESTS="gdb.ada/tick_length_array_enum_idx.exp" RUNTESTFLAGS="--target_board=dwarf5-fission-debug-types-debug-names"

But it doesn't reproduce on Debian 13 (gnat 14), Ubuntu 24.04 (gnat
13) or Fedora Rawhide (gnat 16).

Change-Id: I0d4ccc1cbbce3a337794341744d24091e8549d7f
Approved-By: Tom Tromey <tom@tromey.com>
---
 gdb/dwarf2/read-debug-names.c                 | 212 ++++++++++++++----
 gdb/dwarf2/read.c                             | 167 +++++++++++++-
 gdb/dwarf2/read.h                             |  37 ++-
 gdb/dwarf2/types.h                            |   2 +
 ...dwarf5-fission-debug-types-debug-names.exp |  29 +++
 5 files changed, 392 insertions(+), 55 deletions(-)
 create mode 100644 gdb/testsuite/boards/dwarf5-fission-debug-types-debug-names.exp

diff --git a/gdb/dwarf2/read-debug-names.c b/gdb/dwarf2/read-debug-names.c
index 487e2ea87edb..a062c1ad2562 100644
--- a/gdb/dwarf2/read-debug-names.c
+++ b/gdb/dwarf2/read-debug-names.c
@@ -88,9 +88,13 @@ struct mapped_debug_names_reader
 
   uint8_t offset_size = 0;
   uint32_t cu_count = 0;
-  uint32_t tu_count = 0, bucket_count = 0, name_count = 0;
+  uint32_t tu_count = 0;
+  uint32_t foreign_tu_count = 0;
+  uint32_t bucket_count = 0;
+  uint32_t name_count = 0;
   const gdb_byte *cu_table_reordered = nullptr;
   const gdb_byte *tu_table_reordered = nullptr;
+  const gdb_byte *foreign_tu_table_reordered = nullptr;
   const uint32_t *bucket_table_reordered = nullptr;
   const uint32_t *hash_table_reordered = nullptr;
   const gdb_byte *name_table_string_offs_reordered = nullptr;
@@ -122,7 +126,11 @@ struct mapped_debug_names_reader
 
   /* List of local TUs in the same order as found in the index (DWARF 5 section
      6.1.1.4.3).  */
-  std::vector<dwarf2_per_cu *> type_units;
+  std::vector<signatured_type *> type_units;
+
+  /* List of foreign TUs in the same order as found in the index (DWARF 5
+     section 6.1.1.4.4).  */
+  std::vector<signatured_type *> foreign_type_units;
 
   /* Even though the scanning of .debug_names and creation of the
      cooked index entries is done serially, we create multiple shards
@@ -217,7 +225,10 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
   cooked_index_flag flags = 0;
   sect_offset die_offset {};
   enum language lang = language_unknown;
-  dwarf2_per_cu *per_cu = nullptr;
+  dwarf2_per_cu *comp_unit = nullptr;
+  signatured_type *type_unit = nullptr;
+  signatured_type *foreign_type_unit = nullptr;
+
   for (const auto &attr : indexval.attr_vec)
     {
       ULONGEST ull;
@@ -282,7 +293,6 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
 	{
 	case DW_IDX_compile_unit:
 	  {
-	    /* Don't crash on bad data.  */
 	    if (ull >= this->comp_units.size ())
 	      {
 		complain_about_index_entry
@@ -292,13 +302,17 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
 		continue;
 	      }
 
-	    per_cu = this->comp_units[ull];
+	    comp_unit = this->comp_units[ull];
 	    break;
 	  }
 	case DW_IDX_type_unit:
 	  {
-	    /* Don't crash on bad data.  */
-	    if (ull >= this->type_units.size ())
+	    if (ull < this->type_units.size ())
+	      type_unit = this->type_units[ull];
+	    else if (auto foreign_idx = ull - this->type_units.size ();
+		     foreign_idx < this->foreign_type_units.size ())
+	      foreign_type_unit = this->foreign_type_units[foreign_idx];
+	    else
 	      {
 		complain_about_index_entry
 		  (abfd, name, offset_in_entry_pool,
@@ -307,15 +321,10 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
 		continue;
 	      }
 
-	    per_cu = this->type_units[ull];
 	    break;
 	  }
 	case DW_IDX_die_offset:
 	  die_offset = sect_offset (ull);
-	  /* In a per-CU index (as opposed to a per-module index), index
-	     entries without CU attribute implicitly refer to the single CU.  */
-	  if (per_cu == nullptr)
-	    per_cu = this->comp_units[0];
 	  break;
 	case DW_IDX_parent:
 	  parent = ull;
@@ -339,27 +348,117 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
 	}
     }
 
-  /* Skip if we couldn't find a valid CU/TU index.  */
-  if (per_cu != nullptr)
+  /* From DWARF 5 section 6.1.1.3 ("Per-CU versus Per-Module Indexes"):
+
+       In a per-CU index, the CU list may have only a single entry, and index
+       entries may omit the CU attribute.  */
+  if (comp_unit == nullptr
+      && type_unit == nullptr
+      && foreign_type_unit == nullptr)
     {
-      dwarf_read_debug_printf_v
-	("    -> die_offset %s, per_cu offset %s",
-	 sect_offset_str (die_offset),
-	 sect_offset_str (per_cu->sect_off ()));
+      if (this->comp_units.size () != 1)
+	{
+	  complain_about_index_entry
+	   (abfd, name, offset_in_entry_pool,
+	    _(".debug_names entry is missing CU index, but index has %zu CUs "
+	      "(expecting 1)"),
+	    this->comp_units.size ());
+	  return entry;
+	}
 
-      *result
-	= indices[next_shard].add (die_offset, (dwarf_tag) indexval.dwarf_tag,
-				   flags, lang, name, nullptr, per_cu);
-
-      ++next_shard;
-      if (next_shard == indices.size ())
-	next_shard = 0;
-
-      entry_pool_offsets_to_entries.emplace (offset_in_entry_pool, *result);
+      comp_unit = this->comp_units[0];
     }
-  else
-    dwarf_read_debug_printf_v ("    -> no valid CU/TU, skipping");
 
+  /* Figure out which unit this entry refers to.  */
+  dwarf2_per_cu *actual_unit;
+
+  if (foreign_type_unit != nullptr)
+    {
+      if (type_unit != nullptr)
+	{
+	  complain_about_index_entry
+	    (abfd, name, offset_in_entry_pool,
+	     _(".debug_names entry refers to two TUs"));
+	  return entry;
+	}
+
+      /* Implement this part of DWARF 5 section 6.1.1.2 ("Structure of the Name
+	 Index"):
+
+	   When an index entry refers to a foreign type unit, it may have
+	   attributes for both CU and (foreign) TU.  For such entries, the CU
+	   attribute gives the consumer a reference to the CU that may be used
+	   to locate a split DWARF object file that contains the type unit.  */
+      if (comp_unit == nullptr)
+	{
+	  /* If a foreign type unit does not say which compile unit to follow
+	     to find it, it's pretty much useless.  */
+	  complain_about_index_entry
+	    (abfd, name, offset_in_entry_pool,
+	     _(".debug_names entry refers to foreign type unit but "
+	       "no compile unit"));
+	  return entry;
+	}
+
+      actual_unit = foreign_type_unit;
+
+      /* .debug_names attaches one "hint" CU per index entry, insinuating that
+	 for some names / index entries, it is important which .dwo we choose to
+	 locate the TU.  Even if it's important, the DWARF reader is not
+	 currently able to load multiple versions of the same TU.  So just
+	 record one hint CU for each foreign TU.  */
+      if (foreign_type_unit->hint_per_cu == nullptr)
+	{
+	  foreign_type_unit->hint_per_cu = comp_unit;
+	  dwarf_read_debug_printf_v
+	    ("    hint CU for foreign type unit with signature %s: "
+	     "unit offset %s",
+	     hex_string (foreign_type_unit->signature),
+	     sect_offset_str (comp_unit->sect_off ()));
+	}
+    }
+  else if (type_unit != nullptr)
+    {
+      if (comp_unit != nullptr)
+	{
+	  complain_about_index_entry
+	    (abfd, name, offset_in_entry_pool,
+	     _(".debug_names entry refers to both a CU and a TU"));
+	  return entry;
+	}
+
+      actual_unit = type_unit;
+    }
+  else if (comp_unit != nullptr)
+    actual_unit = comp_unit;
+  else
+    {
+      complain_about_index_entry
+	(abfd, name, offset_in_entry_pool,
+	 _(".debug_names entry is missing a CU or TU reference"));
+      return entry;
+    }
+
+  gdb_assert (actual_unit != nullptr);
+
+  if (foreign_type_unit != nullptr)
+    dwarf_read_debug_printf_v ("    -> signature %s, DIE offset: %s",
+			       hex_string (foreign_type_unit->signature),
+			       sect_offset_str (die_offset));
+  else
+    dwarf_read_debug_printf_v ("    -> unit offset %s, DIE offset %s",
+			       sect_offset_str (actual_unit->sect_off ()),
+			       sect_offset_str (die_offset));
+
+  *result = indices[next_shard].add (die_offset,
+				     (dwarf_tag) indexval.dwarf_tag, flags,
+				     lang, name, nullptr, actual_unit);
+
+  ++next_shard;
+  if (next_shard == indices.size ())
+    next_shard = 0;
+
+  entry_pool_offsets_to_entries.emplace (offset_in_entry_pool, *result);
   return entry;
 }
 
@@ -553,7 +652,7 @@ build_and_check_tu_list_from_debug_names (dwarf2_per_objfile *per_objfile,
 	  return false;
 	}
 
-      map.type_units.emplace_back (per_cu);
+      map.type_units.emplace_back (per_cu->as_signatured_type ());
     }
 
   return true;
@@ -665,22 +764,12 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile,
 
   /* foreign_type_unit_count - The number of TUs in the foreign TU
      list.  */
-  uint32_t foreign_tu_count = read_4_bytes (abfd, addr);
+  map.foreign_tu_count = read_4_bytes (abfd, addr);
   addr += 4;
 
   dwarf_read_debug_printf ("cu_count: %u, tu_count: %u, "
 			   "foreign_tu_count: %u",
-			   map.cu_count, map.tu_count, foreign_tu_count);
-
-  if (foreign_tu_count != 0)
-    {
-      warning (_("Section .debug_names in %ps has unsupported %lu foreign TUs, "
-		 "ignoring .debug_names."),
-	       styled_string (file_name_style.style (),
-			      filename),
-	       static_cast<unsigned long> (foreign_tu_count));
-      return false;
-    }
+			   map.cu_count, map.tu_count, map.foreign_tu_count);
 
   /* bucket_count - The number of hash buckets in the hash lookup
      table.  */
@@ -761,6 +850,10 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile,
   map.tu_table_reordered = addr;
   addr += map.tu_count * map.offset_size;
 
+  /* List of foreign TUs */
+  map.foreign_tu_table_reordered = addr;
+  addr += map.foreign_tu_count * sizeof (std::uint64_t);
+
   /* Hash Lookup Table */
   map.bucket_table_reordered = reinterpret_cast<const uint32_t *> (addr);
   addr += map.bucket_count * 4;
@@ -918,6 +1011,35 @@ build_and_check_cu_lists_from_debug_names (dwarf2_per_bfd *per_bfd,
   return build_and_check_cu_list_from_debug_names (per_bfd, dwz_map, dwz->info);
 }
 
+/* Create signatured_type objects for the foreign TU list in MAP.  */
+
+static void
+create_foreign_type_units_from_debug_names (dwarf2_per_bfd *per_bfd,
+					    mapped_debug_names_reader &map)
+{
+  for (uint32_t i = 0; i < map.foreign_tu_count; ++i)
+    {
+      /* The list of foreign TUs is a list of 64-bit (DW_FORM_ref_sig8) type
+	 signatures representing type units placed in .dwo files.  All we know
+	 about them for now is the signature.  */
+      const gdb_byte *ptr
+	= map.foreign_tu_table_reordered + i * sizeof (std::uint64_t);
+      std::uint64_t sig
+	= extract_unsigned_integer (ptr, sizeof (std::uint64_t),
+				    map.dwarf5_byte_order);
+
+      dwarf_read_debug_printf_v ("  Foreign TU %u (%u): signature %s", i,
+				 i + map.tu_count, hex_string (sig));
+
+      signatured_type_up sig_type
+	= per_bfd->allocate_signatured_type (sig);
+
+      map.foreign_type_units.emplace_back (sig_type.get ());
+      per_bfd->signatured_types.emplace (sig_type.get ());
+      per_bfd->all_units.emplace_back (sig_type.release ());
+    }
+}
+
 /* See read-debug-names.h.  */
 
 bool
@@ -975,6 +1097,12 @@ dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile)
 	return false;
     }
 
+  create_foreign_type_units_from_debug_names (per_objfile->per_bfd, map);
+
+  /* create_foreign_type_units_from_debug_names may add more entries to the
+     ALL_UNITS vector, so it must be called before finalize_all_units.  */
+  finalize_all_units (per_objfile->per_bfd);
+
   per_bfd->debug_aranges.read (per_objfile->objfile);
 
   /* There is a single address map for the whole index (coming from
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index d061bc22b12d..4d8923d3ca7a 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -695,6 +695,13 @@ static struct die_info *follow_die_ref (struct die_info *,
 					const struct attribute *,
 					struct dwarf2_cu **);
 
+static void fill_in_sig_entry_from_dwo_entry (dwarf2_per_objfile *per_objfile,
+					      signatured_type *sig_entry,
+					      dwo_unit *dwo_entry);
+
+static void fill_in_sig_entry_from_per_cu_hint
+  (dwarf2_per_objfile &per_objfile, signatured_type &sig_type);
+
 static struct die_info *follow_die_sig (struct die_info *,
 					const struct attribute *,
 					struct dwarf2_cu **);
@@ -1415,6 +1422,7 @@ dwarf2_per_bfd::allocate_per_cu (dwarf2_section_info *section,
 				 sect_offset sect_off, unsigned int length,
 				 bool is_dwz)
 {
+  gdb_assert (section != nullptr);
   dwarf2_per_cu_up result (new dwarf2_per_cu (this, section, sect_off,
 					      length, is_dwz));
   result->index = all_units.size ();
@@ -1431,6 +1439,7 @@ dwarf2_per_bfd::allocate_signatured_type (dwarf2_section_info *section,
 					  bool is_dwz,
 					  ULONGEST signature)
 {
+  gdb_assert (section != nullptr);
   auto result
     = std::make_unique<signatured_type> (this, section, sect_off, length,
 					 is_dwz, signature);
@@ -1439,6 +1448,20 @@ dwarf2_per_bfd::allocate_signatured_type (dwarf2_section_info *section,
   return result;
 }
 
+/* See read.h.  */
+
+signatured_type_up
+dwarf2_per_bfd::allocate_signatured_type (ULONGEST signature)
+{
+  auto result
+    = std::make_unique<signatured_type> (this, nullptr,
+					 invalid_sect_offset,
+					 0, false, signature);
+  result->index = all_units.size ();
+  this->num_type_units++;
+  return result;
+}
+
 /* Subroutine of dw2_get_file_names_reader to simplify it.
    Return the file name for the given file_entry.
    CU_INFO describes the CU's DW_AT_name and DW_AT_comp_dir.
@@ -1722,6 +1745,20 @@ dwarf2_base_index_functions::expand_all_symtabs (struct objfile *objfile)
 
   for (dwarf2_per_cu *per_cu : all_units_range (per_objfile->per_bfd))
     {
+      /* If a .debug_names index contains a foreign TU but no index entry
+	 references it, the TU won't have a hint CU.  This is a problem, because
+	 we won't be able to locate it.  Skip them, for the following reasons:
+
+	  - If they don't contain anything worthy of a named index entry, they
+	    are unlikely to contain anything interesting, symbol-wise.
+	  - They are likely to be referred to by some other unit (otherwise,
+	    why does it exist?), so will get expanded anyway.  */
+      if (signatured_type *sig_type = per_cu->as_signatured_type ();
+	  (sig_type != nullptr
+	   && sig_type->section () == nullptr
+	   && sig_type->hint_per_cu == nullptr))
+	continue;
+
       /* We don't want to directly expand a partial CU, because if we
 	 read it with the wrong language, then assertion failures can
 	 be triggered later on.  See PR symtab/23010.  So, tell
@@ -2223,8 +2260,7 @@ add_type_unit (dwarf2_per_bfd *per_bfd, dwarf2_section_info *section,
   return emplace_ret.first;
 }
 
-/* Subroutine of lookup_dwo_signatured_type and lookup_dwp_signatured_type.
-   Fill in SIG_ENTRY with DWO_ENTRY.  */
+/* Fill in the missing details in SIG_ENTRY from DWO_ENTRY.  */
 
 static void
 fill_in_sig_entry_from_dwo_entry (dwarf2_per_objfile *per_objfile,
@@ -2680,6 +2716,10 @@ cutu_reader::cutu_reader (dwarf2_per_cu &this_cu,
 {
   struct objfile *objfile = per_objfile.objfile;
   struct dwarf2_section_info *section = this_cu.section ();
+
+  /* Any foreign TU must have been located before getting here.  */
+  gdb_assert (section != nullptr);
+
   bfd *abfd = section->get_bfd_owner ();
   const gdb_byte *begin_info_ptr;
   struct dwarf2_section_info *abbrev_section;
@@ -3278,6 +3318,7 @@ cooked_index_worker_debug_info::do_reading ()
   dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd;
 
   create_all_units (m_per_objfile);
+  finalize_all_units (m_per_objfile->per_bfd);
   process_type_units (m_per_objfile, &m_index_storage);
 
   if (!per_bfd->debug_aranges.empty ())
@@ -3358,6 +3399,19 @@ read_comp_units_from_section (dwarf2_per_objfile *per_objfile,
 
 /* See read.h.  */
 
+void
+dwarf2_per_bfd::sort_all_units ()
+{
+  std::sort (this->all_units.begin (), this->all_units.end (),
+	     [] (const dwarf2_per_cu_up &a, const dwarf2_per_cu_up &b)
+	       {
+		 return all_units_less_than (*a, { b->section (),
+						   b->sect_off () });
+	       });
+}
+
+/* See read.h.  */
+
 void
 finalize_all_units (dwarf2_per_bfd *per_bfd)
 {
@@ -3365,14 +3419,7 @@ finalize_all_units (dwarf2_per_bfd *per_bfd)
   gdb_assert (per_bfd->all_units.size ()
 	      == per_bfd->num_comp_units + per_bfd->num_type_units);
 
-  /* Ensure that the all_units vector is in the expected order for
-     dwarf2_find_containing_unit to be able to perform a binary search.  */
-  std::sort (per_bfd->all_units.begin (), per_bfd->all_units.end (),
-	     [] (const dwarf2_per_cu_up &a, const dwarf2_per_cu_up &b)
-	       {
-		 return all_units_less_than (*a, { b->section (),
-						   b->sect_off () });
-	       });
+  per_bfd->sort_all_units ();
 }
 
 /* See read.h.  */
@@ -3410,7 +3457,6 @@ create_all_units (dwarf2_per_objfile *per_objfile)
 
   per_objfile->per_bfd->signatured_types = std::move (sig_types);
 
-  finalize_all_units (per_objfile->per_bfd);
   remove_all_units.disable ();
 }
 
@@ -17104,6 +17150,77 @@ dwarf2_get_die_type (cu_offset die_offset, dwarf2_per_cu *per_cu,
   return get_die_type_at_offset (die_offset_sect, per_cu, per_objfile);
 }
 
+/* Fill in the missing details in SIG_TYPE from DWO_FILE.
+
+   Error out if there isn't a type unit with the appropriate signature in
+   DWO_FILE.  */
+
+static void
+fill_in_sig_entry_from_dwo_file (dwarf2_per_objfile &per_objfile,
+				 signatured_type &sig_type, dwo_file &dwo_file)
+{
+  gdb_assert (sig_type.section () == nullptr);
+
+  dwo_unit *dwo_tu = dwo_file.find_tu (sig_type.signature);
+  if (dwo_tu == nullptr)
+    error (_(DWARF_ERROR_PREFIX
+	     "Unable to locate type unit with signature %s in DWO file %s "
+	     "[in module %s]"),
+	   hex_string (sig_type.signature), dwo_file.dwo_name.c_str (),
+	   objfile_name (per_objfile.objfile));
+
+  fill_in_sig_entry_from_dwo_entry (&per_objfile, &sig_type, dwo_tu);
+  sig_type.set_section (dwo_tu->section);
+  sig_type.set_sect_off (dwo_tu->sect_off);
+  sig_type.set_length (dwo_tu->length);
+
+  /* Setting SIG_TYPE's section invalidates the ALL_UNITS vector order,
+     re-sort it.  */
+  per_objfile.per_bfd->sort_all_units ();
+}
+
+/* Fill in the missing details in SIG_TYPE from its hint CU.
+
+   Error out if we're unable to locate the .dwo file using the hint CU.  */
+
+static void
+fill_in_sig_entry_from_per_cu_hint (dwarf2_per_objfile &per_objfile,
+				    signatured_type &sig_type)
+{
+  gdb_assert (sig_type.section () == nullptr);
+  gdb_assert (sig_type.hint_per_cu != nullptr);
+
+  dwarf2_per_cu *hint_per_cu = sig_type.hint_per_cu;
+  dwo_unit *dwo_unit;
+
+  /* If a dwarf2_cu already exists for HINT_PER_CU, no need to build another
+     one.  */
+  if (dwarf2_cu *hint_cu = per_objfile.get_cu (hint_per_cu);
+      hint_cu != nullptr)
+    dwo_unit = hint_cu->dwo_unit;
+  else
+    {
+      /* Constructing this cutu_reader will look for the .dwo file, creating
+	 the dwo_file if necessary.  */
+      abbrev_table_cache abbrev_table_cache;
+      cutu_reader reader (*hint_per_cu, per_objfile, nullptr, true,
+			  std::nullopt, abbrev_table_cache);
+
+      /* dwo_unit is owned by the per_bfd, which outlives the reader.  */
+      dwo_unit = reader.cu ()->dwo_unit;
+    }
+
+  if (dwo_unit == nullptr)
+    error (_(DWARF_ERROR_PREFIX
+	     "Hint compilation unit for foreign type unit with signature %s is "
+	     "not in a DWO file [in module %s]"),
+	   hex_string (sig_type.signature),
+	   objfile_name (per_objfile.objfile));
+
+  fill_in_sig_entry_from_dwo_file (per_objfile, sig_type,
+				   *dwo_unit->dwo_file);
+}
+
 /* Follow type unit SIG_TYPE referenced by SRC_DIE.
    On entry *REF_CU is the CU of SRC_DIE.
    On exit *REF_CU is the CU of the result.
@@ -17119,6 +17236,28 @@ follow_die_sig_1 (struct die_info *src_die, struct signatured_type *sig_type,
      we can get here for DW_AT_imported_declaration where we need
      the DIE not the type.  */
 
+  /* If SIG_TYPE's section is not set, it means it's a .debug_names foreign
+     type unit for which we don't know the containing file or section yet.
+     Use the referencing CU as the hint: we know there must exist a TU with
+     the correct signature in its .dwo file, and that .dwo is already open,
+     might as well use it.
+
+     But this is not only an optimization.  If a .debug_names foreign unit
+     does not have any index entry referencing it, then we don't have any
+     "hint CU" for it.  The only hint we have is the referencing CU.  */
+  if (sig_type->section () == nullptr)
+    {
+      if ((*ref_cu)->dwo_unit == nullptr)
+	error (_(DWARF_ERROR_PREFIX
+		 "Unit referencing foreign type unit with signature %s is "
+		 "not from a DWO file [in module %s]"),
+	       hex_string (sig_type->signature),
+	       objfile_name (per_objfile->objfile));
+
+      fill_in_sig_entry_from_dwo_file (*per_objfile, *sig_type,
+				       *(*ref_cu)->dwo_unit->dwo_file);
+    }
+
   dwarf2_cu *sig_cu = ensure_loaded_type_unit (sig_type, per_objfile);
 
   if (sig_cu == nullptr)
@@ -17299,6 +17438,12 @@ load_full_type_unit (signatured_type *sig_type,
   gdb_assert (sig_type->is_debug_types ());
   gdb_assert (per_objfile->get_cu (sig_type) == nullptr);
 
+  /* If the section is not set, this is a .debug_names foreign type unit for
+     which we don't know the containing file nor section yet.  For these, we
+     must have a "hint" CU to follow to find the file and section.  */
+  if (sig_type->section () == nullptr)
+    fill_in_sig_entry_from_per_cu_hint (*per_objfile, *sig_type);
+
   abbrev_table_cache abbrev_table_cache;
   cutu_reader reader (*sig_type, *per_objfile, nullptr, false,
 		      std::nullopt, abbrev_table_cache);
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index a0811fe0d377..12dc6224d8c0 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -110,7 +110,6 @@ struct dwarf2_per_cu
       m_per_bfd (per_bfd)
   {
     gdb_assert (per_bfd != nullptr);
-    gdb_assert (section != nullptr);
   }
 
 private:
@@ -285,9 +284,24 @@ struct dwarf2_per_cu
   dwarf2_section_info *section () const
   { return m_section; }
 
+  /* Set the section of this unit.  */
+  void set_section (dwarf2_section_info *section)
+  {
+    gdb_assert (section != nullptr);
+    gdb_assert (m_section == nullptr);
+    m_section = section;
+  }
+
   sect_offset sect_off () const
   { return m_sect_off; }
 
+  /* Set the section offset of this unit.  */
+  void set_sect_off (sect_offset sect_off)
+  {
+    gdb_assert (m_sect_off == invalid_sect_offset);
+    m_sect_off = sect_off;
+  }
+
   bool is_dwz () const
   { return m_is_dwz; }
 
@@ -482,6 +496,12 @@ struct signatured_type : public dwarf2_per_cu
   /* Containing DWO unit.
      This field is valid iff per_cu.reading_dwo_directly.  */
   struct dwo_unit *dwo_unit = nullptr;
+
+  /* When using a .debug_names index, the section is not initially known for
+     foreign type units (aka skeletonless type units).  This is a reference to
+     a CU that can be used to locate the .dwo file and section containing the
+     type unit.  */
+  dwarf2_per_cu *hint_per_cu = nullptr;
 };
 
 using signatured_type_up = std::unique_ptr<signatured_type>;
@@ -576,6 +596,10 @@ struct dwarf2_per_bfd
     return this->all_units[index].get ();
   }
 
+  /* Ensure that the all_units vector is in the expected order for
+     dwarf2_find_containing_unit to be able to perform a binary search.  */
+  void sort_all_units ();
+
   /* Return the separate '.dwz' debug file.  If there is no
      .gnu_debugaltlink or .debug_sup section in the file, then the
      result depends on REQUIRE: if REQUIRE is true, error out; if
@@ -610,6 +634,12 @@ struct dwarf2_per_bfd
 					       bool is_dwz,
 					       ULONGEST signature);
 
+  /* A convenience function to allocate a signatured_type.  The
+     returned object has its "index" field set properly.
+
+     This one is used when only the signature is known at creation time.  */
+  signatured_type_up allocate_signatured_type (ULONGEST signature);
+
   /* Map all the DWARF section data needed when scanning
      .debug_info.  */
   void map_info_sections (struct objfile *objfile);
@@ -1334,7 +1364,10 @@ extern const char *read_indirect_string_at_offset
 
 extern void finalize_all_units (dwarf2_per_bfd *per_bfd);
 
-/* Create a list of all compilation units in OBJFILE.  */
+/* Create a list of all compilation units in OBJFILE.
+
+   After it is done creating all units, the caller is responsible for calling
+   finalize_all_units.  */
 
 extern void create_all_units (dwarf2_per_objfile *per_objfile);
 
diff --git a/gdb/dwarf2/types.h b/gdb/dwarf2/types.h
index 169b0fd08678..cb8ce33940ca 100644
--- a/gdb/dwarf2/types.h
+++ b/gdb/dwarf2/types.h
@@ -31,6 +31,8 @@ DEFINE_OFFSET_TYPE (cu_offset, unsigned int);
    section.  */
 DEFINE_OFFSET_TYPE (sect_offset, uint64_t);
 
+constexpr auto invalid_sect_offset = static_cast<sect_offset> (-1);
+
 static inline const char *
 sect_offset_str (sect_offset offset)
 {
diff --git a/gdb/testsuite/boards/dwarf5-fission-debug-types-debug-names.exp b/gdb/testsuite/boards/dwarf5-fission-debug-types-debug-names.exp
new file mode 100644
index 000000000000..be8bebed1a43
--- /dev/null
+++ b/gdb/testsuite/boards/dwarf5-fission-debug-types-debug-names.exp
@@ -0,0 +1,29 @@
+# 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/>.
+
+# A board that compiles with DWARF 5, split DWARF (fission) and type units,
+# and generates a .debug_names index using GDB.
+#
+# Example usage:
+# bash$ make check \
+#   RUNTESTFLAGS='--target_board=dwarf5-fission-debug-types-debug-names'
+
+set CC_WITH_TWEAKS_FLAGS "-n"
+load_board_description "cc-with-tweaks"
+
+set_board_info debug_flags \
+    [join { "-gdwarf-5" \
+	    "-gsplit-dwarf" \
+	    "-fdebug-types-section" }]
-- 
2.54.0


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

* Re: [PATCH v3 1/7] gdb/dwarf: move dwo_unit and dwo_file to dwo.h
  2026-05-11 15:40 ` [PATCH v3 1/7] gdb/dwarf: move dwo_unit and dwo_file to dwo.h Simon Marchi
@ 2026-05-13 15:18   ` Tom Tromey
  2026-05-13 17:30     ` Simon Marchi
  0 siblings, 1 reply; 12+ messages in thread
From: Tom Tromey @ 2026-05-13 15:18 UTC (permalink / raw)
  To: Simon Marchi; +Cc: gdb-patches, Simon Marchi

>>>>> "Simon" == Simon Marchi <simon.marchi@efficios.com> writes:

Simon> From: Simon Marchi <simon.marchi@polymtl.ca>
Simon> Move these types and some related things to a new dwo.h header file.  My
Simon> goal is to allow index-write.c to see these types, in a later patch.  I
Simon> initially moved them to read.h, but Tom suggested in review that read.h
Simon> was getting a bit too large.

Looks good.
Approved-By: Tom Tromey <tom@tromey.com>

Tom

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

* Re: [PATCH v3 1/7] gdb/dwarf: move dwo_unit and dwo_file to dwo.h
  2026-05-13 15:18   ` Tom Tromey
@ 2026-05-13 17:30     ` Simon Marchi
  0 siblings, 0 replies; 12+ messages in thread
From: Simon Marchi @ 2026-05-13 17:30 UTC (permalink / raw)
  To: Tom Tromey, Simon Marchi; +Cc: gdb-patches

On 5/13/26 11:18 AM, Tom Tromey wrote:
>>>>>> "Simon" == Simon Marchi <simon.marchi@efficios.com> writes:
> 
> Simon> From: Simon Marchi <simon.marchi@polymtl.ca>
> Simon> Move these types and some related things to a new dwo.h header file.  My
> Simon> goal is to allow index-write.c to see these types, in a later patch.  I
> Simon> initially moved them to read.h, but Tom suggested in review that read.h
> Simon> was getting a bit too large.
> 
> Looks good.
> Approved-By: Tom Tromey <tom@tromey.com>
> 
> Tom

Pushed the series, thanks!

Simon

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

* Re: [PATCH v3 7/7] gdb/dwarf: read foreign type units
  2026-05-11 15:40 ` [PATCH v3 7/7] gdb/dwarf: read foreign type units Simon Marchi
@ 2026-05-19 19:42   ` Guinevere Larsen
  2026-05-19 20:10     ` Simon Marchi
  0 siblings, 1 reply; 12+ messages in thread
From: Guinevere Larsen @ 2026-05-19 19:42 UTC (permalink / raw)
  To: Simon Marchi, gdb-patches; +Cc: Tom Tromey

[-- Attachment #1: Type: text/plain, Size: 35787 bytes --]

Hi Simon!

This commit seems to have made the 
gdb.dwarf2/debug-names-bad-cu-index.exp have some racy cases. I've 
attached a log of the failed run in this email to help you figure out 
what could be going on.

On 5/11/26 12:40 PM, Simon Marchi wrote:
> In DWARF 5, foreign type units are type units present in .dwo files.
> Type units in .dwo files don't have a matching skeleton in the main
> file.  When an .debug_names index is present, it can't include those
> type units in the regular type unit list, since that list specifies
> offsets in the main file.  They are instead listed in the foreign TU
> list, which is basically just a list of type signatures.
>
> In order to help the debugger locate these units (i.e. find the .dwo
> file containing them), individual index entries referencing foreign type
> units may also include a reference to a compile unit that the debugger
> can follow to find the appropriate .dwo file.
>
> This patch implements reading the .debug_names foreign TU list and using
> these "hint" CUs to locate foreign type units.  I use the term "hint"
> throughout the code, but I don't mind another name if someone has a
> better idea.
>
> The first part is to read the actual foreign TU list from the
> .debug_names index and create signatured_type objects out of them.  This
> is done in the new function create_foreign_type_units_from_debug_names.
> Append the newly created signatured_type to the
> mapped_debug_names_reader::foreign_type_units vector, which will be used
> later to resolve DW_IDX_type_unit indices.  Populate the
> dwarf2_per_bfd::signatured_types set, which contains signatured_types
> indexed by signature.  And finally, transfer ownership of the object to
> the dwarf2_per_bfd::all_units vector.
>
> Previously, all dwarf2_per_cu (including signatured_type) objects were
> created with a non-nullptr section.  With foreign type units, we don't
> know the section at creation time.  We also don't know the offset into
> section nor the size of the unit.  Therefore, add a
> dwarf2_per_bfd::allocate_signatured_type overload that takes just the
> signature.  Remove the "section != nullptr" assert from the
> dwarf2_per_cu constructor, but add it to the other allocate_* methods.
>
> Since the new create_foreign_type_units_from_debug_names function adds
> items to the dwarf2_per_bfd::all_units vector, the vector needs to be
> sorted after create_foreign_type_units_from_debug_names runs.  Remove
> the finalize_all_units call from create_all_units, making the callers
> responsible to call it.
>
> The next step is to read the hint CU attributes when scanning the index
> entries.  Rework mapped_debug_names_reader::scan_one_entry to remember
> which kind of unit references it saw for the entry (comp, type and/or
> foreign type) and then figure out what this means.  The logic is:
>
>   - Did the entry reference a foreign type unit?  If so, it's a foreign
>     type unit.  Does it also reference a hint CU?  If not, drop the
>     entry, there's nothing useful we can do with it.
>
>   - Otherwise, did the entry reference a (non-foreign) type unit?  Then
>     it's a regular type unit. If so, it shouldn't also have a
>     DW_IDX_compile_unit.
>
>   - Otherwise, did the entry reference a comp unit?  If so, it's a comp
>     unit.
>
>   - Otherwise, we don't know what unit the entry references, it's an
>     error.
>
> Since the .debug_name index attaches hint CU attributes to individual
> index entries, my initial implementation added the hint CU information
> to the cooked_index_entry structure.  I am not sure why DWARF 5 chose to
> do it this way, as opposed to attaching one hint per foreign TU.  Does
> this mean that two type units with the same signature could be different
> and, for a specific index entry, it would be important to find one
> specific instance of the type unit over the others?  I have no idea.
>
> However, I know that the current GDB DWARF reader is not able to load
> multiple type units with the same signature but different content.  Once
> it loads one type unit with a given signature, all subsequent references
> to that signature will use that loaded type unit.  I therefore chose to
> have the .debug_names reader record just one hint CU per foreign TU.
> This avoids growing the cooked_index_entry structure for nothing, and
> having to pass through this information through multiple layers.
>
> The next step is to locate the .dwo file containing the foreign TUs when
> we need them.  This sometimes makes use of the hint CU, but not always.
> I identified these 3 code paths:
>
>   1. When the type unit gets expanded directly, for instance if you use
>      "ptype" and there is a direct match into the type unit.  This case
>      is handled in load_full_type_unit, calling a new function
>      fill_in_sig_entry_from_per_cu_hint.  This one uses the hint recorded
>      by the .debug_names reader.  When a cooked index entry exists and
>      refers to a foreign TU for which the section is not yet known, we
>      know that there exists a hint, otherwise we wouldn't have created
>      the entry in the first place.
>
>   2. The second one is when the type unit is referenced by some other
>      unit.  This case is handled in follow_die_sig_1, calling another new
>      function fill_in_sig_entry_from_dwo_file.  In this case, we know
>      which unit is referring to the TU, so we use that unit's dwo file to
>      fill in the details.  As explained in the comment, this is sometimes
>      just an optimization, but sometimes also necessary, if the TU
>      does not have a hint, due to it not containing any indexed name.
>
>   3. Similarly, in dwarf2_base_index_functions::expand_all_symtabs, we
>      might have to handle foreign type units for which we don't have a
>      hint.  I initially implemented something in two passes, to go dig in
>      the dwo_file structures to find those TUs, but ended up choosing to
>      just skip them, for the reasons explained in the comment there.
>
> Setting a dwarf2_per_cu's section a posteriori breaks the assumed
> ordering of the dwarf2_per_bfd::all_units vector.  After setting the
> section, re-sort the vector.
>
> Add a target board to exercise this new code.  This board builds with:
>
>   - type units (-fdebug-types-section)
>   - split DWARF (-gsplit-dwarf)
>   - .debug_names index (created by GDB)
>
> I ran the whole testsuite with this board file and it's not perfect, but
> the results are comparable to the dwarf5-fission-debug-types board, for
> instance.
>
> There is one known failure that I am unable to get to the bottom of.  It
> seems orthogonal to my change though, more like an indexer or symbol
> reader issue.  There are maybe more of this kind, but this is one
> example:
>
>    FAIL: gdb.ada/tick_length_array_enum_idx.exp: ptype variable_table'length (GDB internal error)
>
>    /home/smarchi/src/binutils-gdb/gdb/dwarf2/read.c:1839: internal-error: search_one: Assertion `symtab != nullptr' failed.
>
> The problem appears to be that a cooked index lookup for symbol
> variable_table says that a given TU should contain a match.  But then
> trying to expand the TU makes dw2_instantiate_symtab yield a nullptr
> compunit_symtab, I think because the symbol reader found nothing
> interesting symbol-wise.  And then the assert in search_one triggers.
>
> The issue seems sensitive to some aspects of the environment (gnat
> version?).  I am able to reproduce the issue on Arch Linux (gnat 15)
> with:
>
>    $ make check TESTS="gdb.ada/tick_length_array_enum_idx.exp" RUNTESTFLAGS="--target_board=dwarf5-fission-debug-types-debug-names"
>
> But it doesn't reproduce on Debian 13 (gnat 14), Ubuntu 24.04 (gnat
> 13) or Fedora Rawhide (gnat 16).
>
> Change-Id: I0d4ccc1cbbce3a337794341744d24091e8549d7f
> Approved-By: Tom Tromey <tom@tromey.com>
> ---
>   gdb/dwarf2/read-debug-names.c                 | 212 ++++++++++++++----
>   gdb/dwarf2/read.c                             | 167 +++++++++++++-
>   gdb/dwarf2/read.h                             |  37 ++-
>   gdb/dwarf2/types.h                            |   2 +
>   ...dwarf5-fission-debug-types-debug-names.exp |  29 +++
>   5 files changed, 392 insertions(+), 55 deletions(-)
>   create mode 100644 gdb/testsuite/boards/dwarf5-fission-debug-types-debug-names.exp
>
> diff --git a/gdb/dwarf2/read-debug-names.c b/gdb/dwarf2/read-debug-names.c
> index 487e2ea87edb..a062c1ad2562 100644
> --- a/gdb/dwarf2/read-debug-names.c
> +++ b/gdb/dwarf2/read-debug-names.c
> @@ -88,9 +88,13 @@ struct mapped_debug_names_reader
>   
>     uint8_t offset_size = 0;
>     uint32_t cu_count = 0;
> -  uint32_t tu_count = 0, bucket_count = 0, name_count = 0;
> +  uint32_t tu_count = 0;
> +  uint32_t foreign_tu_count = 0;
> +  uint32_t bucket_count = 0;
> +  uint32_t name_count = 0;
>     const gdb_byte *cu_table_reordered = nullptr;
>     const gdb_byte *tu_table_reordered = nullptr;
> +  const gdb_byte *foreign_tu_table_reordered = nullptr;
>     const uint32_t *bucket_table_reordered = nullptr;
>     const uint32_t *hash_table_reordered = nullptr;
>     const gdb_byte *name_table_string_offs_reordered = nullptr;
> @@ -122,7 +126,11 @@ struct mapped_debug_names_reader
>   
>     /* List of local TUs in the same order as found in the index (DWARF 5 section
>        6.1.1.4.3).  */
> -  std::vector<dwarf2_per_cu *> type_units;
> +  std::vector<signatured_type *> type_units;
> +
> +  /* List of foreign TUs in the same order as found in the index (DWARF 5
> +     section 6.1.1.4.4).  */
> +  std::vector<signatured_type *> foreign_type_units;
>   
>     /* Even though the scanning of .debug_names and creation of the
>        cooked index entries is done serially, we create multiple shards
> @@ -217,7 +225,10 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
>     cooked_index_flag flags = 0;
>     sect_offset die_offset {};
>     enum language lang = language_unknown;
> -  dwarf2_per_cu *per_cu = nullptr;
> +  dwarf2_per_cu *comp_unit = nullptr;
> +  signatured_type *type_unit = nullptr;
> +  signatured_type *foreign_type_unit = nullptr;
> +
>     for (const auto &attr : indexval.attr_vec)
>       {
>         ULONGEST ull;
> @@ -282,7 +293,6 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
>   	{
>   	case DW_IDX_compile_unit:
>   	  {
> -	    /* Don't crash on bad data.  */
>   	    if (ull >= this->comp_units.size ())
>   	      {
>   		complain_about_index_entry
> @@ -292,13 +302,17 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
>   		continue;
>   	      }
>   
> -	    per_cu = this->comp_units[ull];
> +	    comp_unit = this->comp_units[ull];
>   	    break;
>   	  }
>   	case DW_IDX_type_unit:
>   	  {
> -	    /* Don't crash on bad data.  */
> -	    if (ull >= this->type_units.size ())
> +	    if (ull < this->type_units.size ())
> +	      type_unit = this->type_units[ull];
> +	    else if (auto foreign_idx = ull - this->type_units.size ();
> +		     foreign_idx < this->foreign_type_units.size ())
> +	      foreign_type_unit = this->foreign_type_units[foreign_idx];
> +	    else
>   	      {
>   		complain_about_index_entry
>   		  (abfd, name, offset_in_entry_pool,
> @@ -307,15 +321,10 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
>   		continue;
>   	      }
>   
> -	    per_cu = this->type_units[ull];
>   	    break;
>   	  }
>   	case DW_IDX_die_offset:
>   	  die_offset = sect_offset (ull);
> -	  /* In a per-CU index (as opposed to a per-module index), index
> -	     entries without CU attribute implicitly refer to the single CU.  */
> -	  if (per_cu == nullptr)
> -	    per_cu = this->comp_units[0];
>   	  break;
>   	case DW_IDX_parent:
>   	  parent = ull;
> @@ -339,27 +348,117 @@ mapped_debug_names_reader::scan_one_entry (const char *name,
>   	}
>       }
>   
> -  /* Skip if we couldn't find a valid CU/TU index.  */
> -  if (per_cu != nullptr)
> +  /* From DWARF 5 section 6.1.1.3 ("Per-CU versus Per-Module Indexes"):
> +
> +       In a per-CU index, the CU list may have only a single entry, and index
> +       entries may omit the CU attribute.  */
> +  if (comp_unit == nullptr
> +      && type_unit == nullptr
> +      && foreign_type_unit == nullptr)
>       {
> -      dwarf_read_debug_printf_v
> -	("    -> die_offset %s, per_cu offset %s",
> -	 sect_offset_str (die_offset),
> -	 sect_offset_str (per_cu->sect_off ()));
> +      if (this->comp_units.size () != 1)
> +	{
> +	  complain_about_index_entry
> +	   (abfd, name, offset_in_entry_pool,
> +	    _(".debug_names entry is missing CU index, but index has %zu CUs "
> +	      "(expecting 1)"),
> +	    this->comp_units.size ());
> +	  return entry;
> +	}
>   
> -      *result
> -	= indices[next_shard].add (die_offset, (dwarf_tag) indexval.dwarf_tag,
> -				   flags, lang, name, nullptr, per_cu);
> -
> -      ++next_shard;
> -      if (next_shard == indices.size ())
> -	next_shard = 0;
> -
> -      entry_pool_offsets_to_entries.emplace (offset_in_entry_pool, *result);
> +      comp_unit = this->comp_units[0];
>       }
> -  else
> -    dwarf_read_debug_printf_v ("    -> no valid CU/TU, skipping");
>   
> +  /* Figure out which unit this entry refers to.  */
> +  dwarf2_per_cu *actual_unit;
> +
> +  if (foreign_type_unit != nullptr)
> +    {
> +      if (type_unit != nullptr)
> +	{
> +	  complain_about_index_entry
> +	    (abfd, name, offset_in_entry_pool,
> +	     _(".debug_names entry refers to two TUs"));
> +	  return entry;
> +	}
> +
> +      /* Implement this part of DWARF 5 section 6.1.1.2 ("Structure of the Name
> +	 Index"):
> +
> +	   When an index entry refers to a foreign type unit, it may have
> +	   attributes for both CU and (foreign) TU.  For such entries, the CU
> +	   attribute gives the consumer a reference to the CU that may be used
> +	   to locate a split DWARF object file that contains the type unit.  */
> +      if (comp_unit == nullptr)
> +	{
> +	  /* If a foreign type unit does not say which compile unit to follow
> +	     to find it, it's pretty much useless.  */
> +	  complain_about_index_entry
> +	    (abfd, name, offset_in_entry_pool,
> +	     _(".debug_names entry refers to foreign type unit but "
> +	       "no compile unit"));
> +	  return entry;
> +	}
> +
> +      actual_unit = foreign_type_unit;
> +
> +      /* .debug_names attaches one "hint" CU per index entry, insinuating that
> +	 for some names / index entries, it is important which .dwo we choose to
> +	 locate the TU.  Even if it's important, the DWARF reader is not
> +	 currently able to load multiple versions of the same TU.  So just
> +	 record one hint CU for each foreign TU.  */
> +      if (foreign_type_unit->hint_per_cu == nullptr)
> +	{
> +	  foreign_type_unit->hint_per_cu = comp_unit;
> +	  dwarf_read_debug_printf_v
> +	    ("    hint CU for foreign type unit with signature %s: "
> +	     "unit offset %s",
> +	     hex_string (foreign_type_unit->signature),
> +	     sect_offset_str (comp_unit->sect_off ()));
> +	}
> +    }
> +  else if (type_unit != nullptr)
> +    {
> +      if (comp_unit != nullptr)
> +	{
> +	  complain_about_index_entry
> +	    (abfd, name, offset_in_entry_pool,
> +	     _(".debug_names entry refers to both a CU and a TU"));
> +	  return entry;
> +	}
> +
> +      actual_unit = type_unit;
> +    }
> +  else if (comp_unit != nullptr)
> +    actual_unit = comp_unit;
> +  else
> +    {
> +      complain_about_index_entry
> +	(abfd, name, offset_in_entry_pool,
> +	 _(".debug_names entry is missing a CU or TU reference"));
> +      return entry;
> +    }
> +
> +  gdb_assert (actual_unit != nullptr);
> +
> +  if (foreign_type_unit != nullptr)
> +    dwarf_read_debug_printf_v ("    -> signature %s, DIE offset: %s",
> +			       hex_string (foreign_type_unit->signature),
> +			       sect_offset_str (die_offset));
> +  else
> +    dwarf_read_debug_printf_v ("    -> unit offset %s, DIE offset %s",
> +			       sect_offset_str (actual_unit->sect_off ()),
> +			       sect_offset_str (die_offset));
> +
> +  *result = indices[next_shard].add (die_offset,
> +				     (dwarf_tag) indexval.dwarf_tag, flags,
> +				     lang, name, nullptr, actual_unit);
> +
> +  ++next_shard;
> +  if (next_shard == indices.size ())
> +    next_shard = 0;
> +
> +  entry_pool_offsets_to_entries.emplace (offset_in_entry_pool, *result);
>     return entry;
>   }
>   
> @@ -553,7 +652,7 @@ build_and_check_tu_list_from_debug_names (dwarf2_per_objfile *per_objfile,
>   	  return false;
>   	}
>   
> -      map.type_units.emplace_back (per_cu);
> +      map.type_units.emplace_back (per_cu->as_signatured_type ());
>       }
>   
>     return true;
> @@ -665,22 +764,12 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile,
>   
>     /* foreign_type_unit_count - The number of TUs in the foreign TU
>        list.  */
> -  uint32_t foreign_tu_count = read_4_bytes (abfd, addr);
> +  map.foreign_tu_count = read_4_bytes (abfd, addr);
>     addr += 4;
>   
>     dwarf_read_debug_printf ("cu_count: %u, tu_count: %u, "
>   			   "foreign_tu_count: %u",
> -			   map.cu_count, map.tu_count, foreign_tu_count);
> -
> -  if (foreign_tu_count != 0)
> -    {
> -      warning (_("Section .debug_names in %ps has unsupported %lu foreign TUs, "
> -		 "ignoring .debug_names."),
> -	       styled_string (file_name_style.style (),
> -			      filename),
> -	       static_cast<unsigned long> (foreign_tu_count));
> -      return false;
> -    }
> +			   map.cu_count, map.tu_count, map.foreign_tu_count);
>   
>     /* bucket_count - The number of hash buckets in the hash lookup
>        table.  */
> @@ -761,6 +850,10 @@ read_debug_names_from_section (dwarf2_per_objfile *per_objfile,
>     map.tu_table_reordered = addr;
>     addr += map.tu_count * map.offset_size;
>   
> +  /* List of foreign TUs */
> +  map.foreign_tu_table_reordered = addr;
> +  addr += map.foreign_tu_count * sizeof (std::uint64_t);
> +
>     /* Hash Lookup Table */
>     map.bucket_table_reordered = reinterpret_cast<const uint32_t *> (addr);
>     addr += map.bucket_count * 4;
> @@ -918,6 +1011,35 @@ build_and_check_cu_lists_from_debug_names (dwarf2_per_bfd *per_bfd,
>     return build_and_check_cu_list_from_debug_names (per_bfd, dwz_map, dwz->info);
>   }
>   
> +/* Create signatured_type objects for the foreign TU list in MAP.  */
> +
> +static void
> +create_foreign_type_units_from_debug_names (dwarf2_per_bfd *per_bfd,
> +					    mapped_debug_names_reader &map)
> +{
> +  for (uint32_t i = 0; i < map.foreign_tu_count; ++i)
> +    {
> +      /* The list of foreign TUs is a list of 64-bit (DW_FORM_ref_sig8) type
> +	 signatures representing type units placed in .dwo files.  All we know
> +	 about them for now is the signature.  */
> +      const gdb_byte *ptr
> +	= map.foreign_tu_table_reordered + i * sizeof (std::uint64_t);
> +      std::uint64_t sig
> +	= extract_unsigned_integer (ptr, sizeof (std::uint64_t),
> +				    map.dwarf5_byte_order);
> +
> +      dwarf_read_debug_printf_v ("  Foreign TU %u (%u): signature %s", i,
> +				 i + map.tu_count, hex_string (sig));
> +
> +      signatured_type_up sig_type
> +	= per_bfd->allocate_signatured_type (sig);
> +
> +      map.foreign_type_units.emplace_back (sig_type.get ());
> +      per_bfd->signatured_types.emplace (sig_type.get ());
> +      per_bfd->all_units.emplace_back (sig_type.release ());
> +    }
> +}
> +
>   /* See read-debug-names.h.  */
>   
>   bool
> @@ -975,6 +1097,12 @@ dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile)
>   	return false;
>       }
>   
> +  create_foreign_type_units_from_debug_names (per_objfile->per_bfd, map);
> +
> +  /* create_foreign_type_units_from_debug_names may add more entries to the
> +     ALL_UNITS vector, so it must be called before finalize_all_units.  */
> +  finalize_all_units (per_objfile->per_bfd);
> +
>     per_bfd->debug_aranges.read (per_objfile->objfile);
>   
>     /* There is a single address map for the whole index (coming from
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index d061bc22b12d..4d8923d3ca7a 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -695,6 +695,13 @@ static struct die_info *follow_die_ref (struct die_info *,
>   					const struct attribute *,
>   					struct dwarf2_cu **);
>   
> +static void fill_in_sig_entry_from_dwo_entry (dwarf2_per_objfile *per_objfile,
> +					      signatured_type *sig_entry,
> +					      dwo_unit *dwo_entry);
> +
> +static void fill_in_sig_entry_from_per_cu_hint
> +  (dwarf2_per_objfile &per_objfile, signatured_type &sig_type);
> +
>   static struct die_info *follow_die_sig (struct die_info *,
>   					const struct attribute *,
>   					struct dwarf2_cu **);
> @@ -1415,6 +1422,7 @@ dwarf2_per_bfd::allocate_per_cu (dwarf2_section_info *section,
>   				 sect_offset sect_off, unsigned int length,
>   				 bool is_dwz)
>   {
> +  gdb_assert (section != nullptr);
>     dwarf2_per_cu_up result (new dwarf2_per_cu (this, section, sect_off,
>   					      length, is_dwz));
>     result->index = all_units.size ();
> @@ -1431,6 +1439,7 @@ dwarf2_per_bfd::allocate_signatured_type (dwarf2_section_info *section,
>   					  bool is_dwz,
>   					  ULONGEST signature)
>   {
> +  gdb_assert (section != nullptr);
>     auto result
>       = std::make_unique<signatured_type> (this, section, sect_off, length,
>   					 is_dwz, signature);
> @@ -1439,6 +1448,20 @@ dwarf2_per_bfd::allocate_signatured_type (dwarf2_section_info *section,
>     return result;
>   }
>   
> +/* See read.h.  */
> +
> +signatured_type_up
> +dwarf2_per_bfd::allocate_signatured_type (ULONGEST signature)
> +{
> +  auto result
> +    = std::make_unique<signatured_type> (this, nullptr,
> +					 invalid_sect_offset,
> +					 0, false, signature);
> +  result->index = all_units.size ();
> +  this->num_type_units++;
> +  return result;
> +}
> +
>   /* Subroutine of dw2_get_file_names_reader to simplify it.
>      Return the file name for the given file_entry.
>      CU_INFO describes the CU's DW_AT_name and DW_AT_comp_dir.
> @@ -1722,6 +1745,20 @@ dwarf2_base_index_functions::expand_all_symtabs (struct objfile *objfile)
>   
>     for (dwarf2_per_cu *per_cu : all_units_range (per_objfile->per_bfd))
>       {
> +      /* If a .debug_names index contains a foreign TU but no index entry
> +	 references it, the TU won't have a hint CU.  This is a problem, because
> +	 we won't be able to locate it.  Skip them, for the following reasons:
> +
> +	  - If they don't contain anything worthy of a named index entry, they
> +	    are unlikely to contain anything interesting, symbol-wise.
> +	  - They are likely to be referred to by some other unit (otherwise,
> +	    why does it exist?), so will get expanded anyway.  */
> +      if (signatured_type *sig_type = per_cu->as_signatured_type ();
> +	  (sig_type != nullptr
> +	   && sig_type->section () == nullptr
> +	   && sig_type->hint_per_cu == nullptr))
> +	continue;
> +
>         /* We don't want to directly expand a partial CU, because if we
>   	 read it with the wrong language, then assertion failures can
>   	 be triggered later on.  See PR symtab/23010.  So, tell
> @@ -2223,8 +2260,7 @@ add_type_unit (dwarf2_per_bfd *per_bfd, dwarf2_section_info *section,
>     return emplace_ret.first;
>   }
>   
> -/* Subroutine of lookup_dwo_signatured_type and lookup_dwp_signatured_type.
> -   Fill in SIG_ENTRY with DWO_ENTRY.  */
> +/* Fill in the missing details in SIG_ENTRY from DWO_ENTRY.  */
>   
>   static void
>   fill_in_sig_entry_from_dwo_entry (dwarf2_per_objfile *per_objfile,
> @@ -2680,6 +2716,10 @@ cutu_reader::cutu_reader (dwarf2_per_cu &this_cu,
>   {
>     struct objfile *objfile = per_objfile.objfile;
>     struct dwarf2_section_info *section = this_cu.section ();
> +
> +  /* Any foreign TU must have been located before getting here.  */
> +  gdb_assert (section != nullptr);
> +
>     bfd *abfd = section->get_bfd_owner ();
>     const gdb_byte *begin_info_ptr;
>     struct dwarf2_section_info *abbrev_section;
> @@ -3278,6 +3318,7 @@ cooked_index_worker_debug_info::do_reading ()
>     dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd;
>   
>     create_all_units (m_per_objfile);
> +  finalize_all_units (m_per_objfile->per_bfd);
>     process_type_units (m_per_objfile, &m_index_storage);
>   
>     if (!per_bfd->debug_aranges.empty ())
> @@ -3358,6 +3399,19 @@ read_comp_units_from_section (dwarf2_per_objfile *per_objfile,
>   
>   /* See read.h.  */
>   
> +void
> +dwarf2_per_bfd::sort_all_units ()
> +{
> +  std::sort (this->all_units.begin (), this->all_units.end (),
> +	     [] (const dwarf2_per_cu_up &a, const dwarf2_per_cu_up &b)
> +	       {
> +		 return all_units_less_than (*a, { b->section (),
> +						   b->sect_off () });
> +	       });
> +}
> +
> +/* See read.h.  */
> +
>   void
>   finalize_all_units (dwarf2_per_bfd *per_bfd)
>   {
> @@ -3365,14 +3419,7 @@ finalize_all_units (dwarf2_per_bfd *per_bfd)
>     gdb_assert (per_bfd->all_units.size ()
>   	      == per_bfd->num_comp_units + per_bfd->num_type_units);
>   
> -  /* Ensure that the all_units vector is in the expected order for
> -     dwarf2_find_containing_unit to be able to perform a binary search.  */
> -  std::sort (per_bfd->all_units.begin (), per_bfd->all_units.end (),
> -	     [] (const dwarf2_per_cu_up &a, const dwarf2_per_cu_up &b)
> -	       {
> -		 return all_units_less_than (*a, { b->section (),
> -						   b->sect_off () });
> -	       });
> +  per_bfd->sort_all_units ();
>   }
>   
>   /* See read.h.  */
> @@ -3410,7 +3457,6 @@ create_all_units (dwarf2_per_objfile *per_objfile)
>   
>     per_objfile->per_bfd->signatured_types = std::move (sig_types);
>   
> -  finalize_all_units (per_objfile->per_bfd);
>     remove_all_units.disable ();
>   }
>   
> @@ -17104,6 +17150,77 @@ dwarf2_get_die_type (cu_offset die_offset, dwarf2_per_cu *per_cu,
>     return get_die_type_at_offset (die_offset_sect, per_cu, per_objfile);
>   }
>   
> +/* Fill in the missing details in SIG_TYPE from DWO_FILE.
> +
> +   Error out if there isn't a type unit with the appropriate signature in
> +   DWO_FILE.  */
> +
> +static void
> +fill_in_sig_entry_from_dwo_file (dwarf2_per_objfile &per_objfile,
> +				 signatured_type &sig_type, dwo_file &dwo_file)
> +{
> +  gdb_assert (sig_type.section () == nullptr);
> +
> +  dwo_unit *dwo_tu = dwo_file.find_tu (sig_type.signature);
> +  if (dwo_tu == nullptr)
> +    error (_(DWARF_ERROR_PREFIX
> +	     "Unable to locate type unit with signature %s in DWO file %s "
> +	     "[in module %s]"),
> +	   hex_string (sig_type.signature), dwo_file.dwo_name.c_str (),
> +	   objfile_name (per_objfile.objfile));
> +
> +  fill_in_sig_entry_from_dwo_entry (&per_objfile, &sig_type, dwo_tu);
> +  sig_type.set_section (dwo_tu->section);
> +  sig_type.set_sect_off (dwo_tu->sect_off);
> +  sig_type.set_length (dwo_tu->length);
> +
> +  /* Setting SIG_TYPE's section invalidates the ALL_UNITS vector order,
> +     re-sort it.  */
> +  per_objfile.per_bfd->sort_all_units ();
> +}
> +
> +/* Fill in the missing details in SIG_TYPE from its hint CU.
> +
> +   Error out if we're unable to locate the .dwo file using the hint CU.  */
> +
> +static void
> +fill_in_sig_entry_from_per_cu_hint (dwarf2_per_objfile &per_objfile,
> +				    signatured_type &sig_type)
> +{
> +  gdb_assert (sig_type.section () == nullptr);
> +  gdb_assert (sig_type.hint_per_cu != nullptr);
> +
> +  dwarf2_per_cu *hint_per_cu = sig_type.hint_per_cu;
> +  dwo_unit *dwo_unit;
> +
> +  /* If a dwarf2_cu already exists for HINT_PER_CU, no need to build another
> +     one.  */
> +  if (dwarf2_cu *hint_cu = per_objfile.get_cu (hint_per_cu);
> +      hint_cu != nullptr)
> +    dwo_unit = hint_cu->dwo_unit;
> +  else
> +    {
> +      /* Constructing this cutu_reader will look for the .dwo file, creating
> +	 the dwo_file if necessary.  */
> +      abbrev_table_cache abbrev_table_cache;
> +      cutu_reader reader (*hint_per_cu, per_objfile, nullptr, true,
> +			  std::nullopt, abbrev_table_cache);
> +
> +      /* dwo_unit is owned by the per_bfd, which outlives the reader.  */
> +      dwo_unit = reader.cu ()->dwo_unit;
> +    }
> +
> +  if (dwo_unit == nullptr)
> +    error (_(DWARF_ERROR_PREFIX
> +	     "Hint compilation unit for foreign type unit with signature %s is "
> +	     "not in a DWO file [in module %s]"),
> +	   hex_string (sig_type.signature),
> +	   objfile_name (per_objfile.objfile));
> +
> +  fill_in_sig_entry_from_dwo_file (per_objfile, sig_type,
> +				   *dwo_unit->dwo_file);
> +}
> +
>   /* Follow type unit SIG_TYPE referenced by SRC_DIE.
>      On entry *REF_CU is the CU of SRC_DIE.
>      On exit *REF_CU is the CU of the result.
> @@ -17119,6 +17236,28 @@ follow_die_sig_1 (struct die_info *src_die, struct signatured_type *sig_type,
>        we can get here for DW_AT_imported_declaration where we need
>        the DIE not the type.  */
>   
> +  /* If SIG_TYPE's section is not set, it means it's a .debug_names foreign
> +     type unit for which we don't know the containing file or section yet.
> +     Use the referencing CU as the hint: we know there must exist a TU with
> +     the correct signature in its .dwo file, and that .dwo is already open,
> +     might as well use it.
> +
> +     But this is not only an optimization.  If a .debug_names foreign unit
> +     does not have any index entry referencing it, then we don't have any
> +     "hint CU" for it.  The only hint we have is the referencing CU.  */
> +  if (sig_type->section () == nullptr)
> +    {
> +      if ((*ref_cu)->dwo_unit == nullptr)
> +	error (_(DWARF_ERROR_PREFIX
> +		 "Unit referencing foreign type unit with signature %s is "
> +		 "not from a DWO file [in module %s]"),
> +	       hex_string (sig_type->signature),
> +	       objfile_name (per_objfile->objfile));
> +
> +      fill_in_sig_entry_from_dwo_file (*per_objfile, *sig_type,
> +				       *(*ref_cu)->dwo_unit->dwo_file);
> +    }
> +
>     dwarf2_cu *sig_cu = ensure_loaded_type_unit (sig_type, per_objfile);
>   
>     if (sig_cu == nullptr)
> @@ -17299,6 +17438,12 @@ load_full_type_unit (signatured_type *sig_type,
>     gdb_assert (sig_type->is_debug_types ());
>     gdb_assert (per_objfile->get_cu (sig_type) == nullptr);
>   
> +  /* If the section is not set, this is a .debug_names foreign type unit for
> +     which we don't know the containing file nor section yet.  For these, we
> +     must have a "hint" CU to follow to find the file and section.  */
> +  if (sig_type->section () == nullptr)
> +    fill_in_sig_entry_from_per_cu_hint (*per_objfile, *sig_type);
> +
>     abbrev_table_cache abbrev_table_cache;
>     cutu_reader reader (*sig_type, *per_objfile, nullptr, false,
>   		      std::nullopt, abbrev_table_cache);
> diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
> index a0811fe0d377..12dc6224d8c0 100644
> --- a/gdb/dwarf2/read.h
> +++ b/gdb/dwarf2/read.h
> @@ -110,7 +110,6 @@ struct dwarf2_per_cu
>         m_per_bfd (per_bfd)
>     {
>       gdb_assert (per_bfd != nullptr);
> -    gdb_assert (section != nullptr);
>     }
>   
>   private:
> @@ -285,9 +284,24 @@ struct dwarf2_per_cu
>     dwarf2_section_info *section () const
>     { return m_section; }
>   
> +  /* Set the section of this unit.  */
> +  void set_section (dwarf2_section_info *section)
> +  {
> +    gdb_assert (section != nullptr);
> +    gdb_assert (m_section == nullptr);
> +    m_section = section;
> +  }
> +
>     sect_offset sect_off () const
>     { return m_sect_off; }
>   
> +  /* Set the section offset of this unit.  */
> +  void set_sect_off (sect_offset sect_off)
> +  {
> +    gdb_assert (m_sect_off == invalid_sect_offset);
> +    m_sect_off = sect_off;
> +  }
> +
>     bool is_dwz () const
>     { return m_is_dwz; }
>   
> @@ -482,6 +496,12 @@ struct signatured_type : public dwarf2_per_cu
>     /* Containing DWO unit.
>        This field is valid iff per_cu.reading_dwo_directly.  */
>     struct dwo_unit *dwo_unit = nullptr;
> +
> +  /* When using a .debug_names index, the section is not initially known for
> +     foreign type units (aka skeletonless type units).  This is a reference to
> +     a CU that can be used to locate the .dwo file and section containing the
> +     type unit.  */
> +  dwarf2_per_cu *hint_per_cu = nullptr;
>   };
>   
>   using signatured_type_up = std::unique_ptr<signatured_type>;
> @@ -576,6 +596,10 @@ struct dwarf2_per_bfd
>       return this->all_units[index].get ();
>     }
>   
> +  /* Ensure that the all_units vector is in the expected order for
> +     dwarf2_find_containing_unit to be able to perform a binary search.  */
> +  void sort_all_units ();
> +
>     /* Return the separate '.dwz' debug file.  If there is no
>        .gnu_debugaltlink or .debug_sup section in the file, then the
>        result depends on REQUIRE: if REQUIRE is true, error out; if
> @@ -610,6 +634,12 @@ struct dwarf2_per_bfd
>   					       bool is_dwz,
>   					       ULONGEST signature);
>   
> +  /* A convenience function to allocate a signatured_type.  The
> +     returned object has its "index" field set properly.
> +
> +     This one is used when only the signature is known at creation time.  */
> +  signatured_type_up allocate_signatured_type (ULONGEST signature);
> +
>     /* Map all the DWARF section data needed when scanning
>        .debug_info.  */
>     void map_info_sections (struct objfile *objfile);
> @@ -1334,7 +1364,10 @@ extern const char *read_indirect_string_at_offset
>   
>   extern void finalize_all_units (dwarf2_per_bfd *per_bfd);
>   
> -/* Create a list of all compilation units in OBJFILE.  */
> +/* Create a list of all compilation units in OBJFILE.
> +
> +   After it is done creating all units, the caller is responsible for calling
> +   finalize_all_units.  */
>   
>   extern void create_all_units (dwarf2_per_objfile *per_objfile);
>   
> diff --git a/gdb/dwarf2/types.h b/gdb/dwarf2/types.h
> index 169b0fd08678..cb8ce33940ca 100644
> --- a/gdb/dwarf2/types.h
> +++ b/gdb/dwarf2/types.h
> @@ -31,6 +31,8 @@ DEFINE_OFFSET_TYPE (cu_offset, unsigned int);
>      section.  */
>   DEFINE_OFFSET_TYPE (sect_offset, uint64_t);
>   
> +constexpr auto invalid_sect_offset = static_cast<sect_offset> (-1);
> +
>   static inline const char *
>   sect_offset_str (sect_offset offset)
>   {
> diff --git a/gdb/testsuite/boards/dwarf5-fission-debug-types-debug-names.exp b/gdb/testsuite/boards/dwarf5-fission-debug-types-debug-names.exp
> new file mode 100644
> index 000000000000..be8bebed1a43
> --- /dev/null
> +++ b/gdb/testsuite/boards/dwarf5-fission-debug-types-debug-names.exp
> @@ -0,0 +1,29 @@
> +# 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/>.
> +
> +# A board that compiles with DWARF 5, split DWARF (fission) and type units,
> +# and generates a .debug_names index using GDB.
> +#
> +# Example usage:
> +# bash$ make check \
> +#   RUNTESTFLAGS='--target_board=dwarf5-fission-debug-types-debug-names'
> +
> +set CC_WITH_TWEAKS_FLAGS "-n"
> +load_board_description "cc-with-tweaks"
> +
> +set_board_info debug_flags \
> +    [join { "-gdwarf-5" \
> +	    "-gsplit-dwarf" \
> +	    "-fdebug-types-section" }]


-- 
Cheers,
Guinevere Larsen
It/she

[-- Attachment #2: gdb.log --]
[-- Type: text/x-log, Size: 13909 bytes --]

Test run by gwenthekween on Tue May 19 16:25:14 2026
Native configuration is x86_64-pc-linux-gnu

		=== gdb tests ===

Schedule of variations:
    unix

Running target unix
Using /usr/share/dejagnu/baseboards/unix.exp as board description file for target.
Using /usr/share/dejagnu/config/unix.exp as generic interface file for target.
Using /home/gwenthekween/Documents/downstream-build/gdb/testsuite/../../../downstream-gdb/gdb/testsuite/config/unix.exp as tool-and-target-specific interface file.
Running /home/gwenthekween/Documents/downstream-build/gdb/testsuite/../../../downstream-gdb/gdb/testsuite/gdb.dwarf2/debug-names-bad-cu-index.exp ...
Executing on build: rm -rf /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index    (timeout = 300)
builtin_spawn -ignore SIGHUP rm -rf /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index
gdb_do_cache: universal_compile_options_c (  )
Executing on host: gcc    -fdiagnostics-color=never -c -o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/temp/2515157/ccopts.o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/temp/2515157/ccopts.c    (timeout = 300)
builtin_spawn -ignore SIGHUP gcc -fdiagnostics-color=never -c -o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/temp/2515157/ccopts.o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/temp/2515157/ccopts.c
gdb_do_cache: get_compiler_info_1 ( c )
get_compiler_info: gcc-15-2-1
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
Executing on host: gcc  -fno-stack-protector /home/gwenthekween/Documents/downstream-build/gdb/testsuite/../../../downstream-gdb/gdb/testsuite/gdb.dwarf2/_start.c  -fdiagnostics-color=never -g  -nostartfiles -lm   -o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/temp/2515157/func_addr.x    (timeout = 300)
builtin_spawn -ignore SIGHUP gcc -fno-stack-protector /home/gwenthekween/Documents/downstream-build/gdb/testsuite/../../../downstream-gdb/gdb/testsuite/gdb.dwarf2/_start.c -fdiagnostics-color=never -g -nostartfiles -lm -o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/temp/2515157/func_addr.x

Starting logfile: /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/gdb.in.1

builtin_spawn /home/gwenthekween/Documents/downstream-build/gdb/testsuite/../../gdb/gdb -nw -nx -q -iex set height 0 -iex set width 0 -data-directory /home/gwenthekween/Documents/downstream-build/gdb/data-directory -iex set interactive-mode on
(gdb) set height 0
(gdb) set width 0
(gdb) kill
The program is not being run.
(gdb) file /home/gwenthekween/Documents/downstream-build/gdb/testsuite/temp/2515157/func_addr.x
Reading symbols from /home/gwenthekween/Documents/downstream-build/gdb/testsuite/temp/2515157/func_addr.x...
(gdb) p _start_label - _start
$1 = 4
(gdb) disassemble _start
Dump of assembler code for function _start:
   0x0000000000400330 <+0>:	push   %rbp
   0x0000000000400331 <+1>:	mov    %rsp,%rbp
   0x0000000000400334 <+4>:	mov    $0x0,%edi
   0x0000000000400339 <+9>:	call   0x400320 <exit@plt>
End of assembler dump.
(gdb) with print asm-demangle on -- x/2i _start+9
   0x400339 <_start+9>:	call   0x400320 <exit@plt>
   0x40033e:	Cannot access memory at address 0x40033e
(gdb) gdb_do_cache: is_64_target (  )
gdb_do_cache: universal_compile_options_c (  )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
Executing on host: gcc  -fno-stack-protector  -fdiagnostics-color=never -c  -o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/temp/2515157/is_64_target.o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/temp/2515157/is_64_target.c    (timeout = 300)
builtin_spawn -ignore SIGHUP gcc -fno-stack-protector -fdiagnostics-color=never -c -o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/temp/2515157/is_64_target.o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/temp/2515157/is_64_target.c
gdb_do_cache: is_64_target (  )
gdb_do_cache: is_64_target (  )
gdb_do_cache: is_64_target (  )
gdb_do_cache: universal_compile_options_c (  )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
Executing on host: gcc  -fno-stack-protector  -fdiagnostics-color=never -c  -o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/debug-names-bad-cu-index0.o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/debug-names.S    (timeout = 300)
builtin_spawn -ignore SIGHUP gcc -fno-stack-protector -fdiagnostics-color=never -c -o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/debug-names-bad-cu-index0.o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/debug-names.S
gdb_do_cache: universal_compile_options_c (  )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
Executing on host: gcc  -fno-stack-protector  -fdiagnostics-color=never -c  -o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/debug-names-bad-cu-index1.o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/../../../downstream-gdb/gdb/testsuite/gdb.dwarf2/_start.c    (timeout = 300)
builtin_spawn -ignore SIGHUP gcc -fno-stack-protector -fdiagnostics-color=never -c -o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/debug-names-bad-cu-index1.o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/../../../downstream-gdb/gdb/testsuite/gdb.dwarf2/_start.c
gdb_do_cache: universal_compile_options_c (  )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
Executing on host: gcc  -fno-stack-protector /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/debug-names-bad-cu-index0.o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/debug-names-bad-cu-index1.o  -fdiagnostics-color=never  -nostartfiles -lm   -o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/debug-names-bad-cu-index    (timeout = 300)
builtin_spawn -ignore SIGHUP gcc -fno-stack-protector /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/debug-names-bad-cu-index0.o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/debug-names-bad-cu-index1.o -fdiagnostics-color=never -nostartfiles -lm -o /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/debug-names-bad-cu-index

Starting logfile: /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/gdb.in.2

builtin_spawn /home/gwenthekween/Documents/downstream-build/gdb/testsuite/../../gdb/gdb -nw -nx -q -iex set height 0 -iex set width 0 -data-directory /home/gwenthekween/Documents/downstream-build/gdb/data-directory -iex set interactive-mode on
(gdb) set height 0
(gdb) set width 0
(gdb) dir
Reinitialize source path to empty? (y or n) y
Source directories searched: $cdir:$cwd
(gdb) dir /home/gwenthekween/Documents/downstream-gdb/gdb/testsuite/gdb.dwarf2
Source directories searched: /home/gwenthekween/Documents/downstream-gdb/gdb/testsuite/gdb.dwarf2:$cdir:$cwd
(gdb) show complaints
Max number of complaints about incorrect symbols is 0.
(gdb) set complaints 1
(gdb) maint set dwarf synchronous on
(gdb) PASS: gdb.dwarf2/debug-names-bad-cu-index.exp: maint set dwarf synchronous on
kill
The program is not being run.
(gdb) file /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/debug-names-bad-cu-index
Reading symbols from /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/debug-names-bad-cu-index...
warning: Section .debug_names has incorrect entry in CU table, ignoring .debug_names.
(gdb) maint print objfiles debug-names-bad-cu-index

Object file /home/gwenthekween/Documents/downstream-build/gdb/testsuite/outputs/gdb.dwarf2/debug-names-bad-cu-index/debug-names-bad-cu-index:  Objfile at 0x36afb9e0, bfd at 0x3693ace0, 11 minsyms

Cooked index in use:

  entries:

    [0] ((cooked_index_entry *) 0x7faaf8004400)
    name:       _start
    canonical:  _start
    qualified:  _start
    DWARF tag:  DW_TAG_subprogram
    flags:      0x0 []
    DIE offset: 0xd
    CU index:   0
    parent:     ((cooked_index_entry *) 0)

    [1] ((cooked_index_entry *) 0x7faaf4006820)
    name:       int
    canonical:  int
    qualified:  int
    DWARF tag:  DW_TAG_base_type
    flags:      0x2 [IS_STATIC]
    DIE offset: 0x3e
    CU index:   1
    parent:     ((cooked_index_entry *) 0)

    [2] ((cooked_index_entry *) 0x7faaf40067f0)
    name:       struct_with_int_member
    canonical:  struct_with_int_member
    qualified:  struct_with_int_member
    DWARF tag:  DW_TAG_structure_type
    flags:      0x2 [IS_STATIC]
    DIE offset: 0x18
    CU index:   1
    parent:     ((cooked_index_entry *) 0)

    [3] ((cooked_index_entry *) 0x36fc5250)
    name:       int
    canonical:  int
    qualified:  int
    DWARF tag:  DW_TAG_base_type
    flags:      0x2 [IS_STATIC]
    DIE offset: 0x3e
    CU index:   1
    parent:     ((cooked_index_entry *) 0)

    [4] ((cooked_index_entry *) 0x36fc5220)
    name:       struct_with_int_member
    canonical:  struct_with_int_member
    qualified:  struct_with_int_member
    DWARF tag:  DW_TAG_structure_type
    flags:      0x2 [IS_STATIC]
    DIE offset: 0x18
    CU index:   1
    parent:     ((cooked_index_entry *) 0)

  main: ((cooked_index_entry *) 0)

  address maps:

    [0] ((addrmap *) 0x7faae4004220)

      [0x0] ((dwarf2_per_cu *) 0)

    [1] ((addrmap *) 0x7faadc004220)

      [0x0] ((dwarf2_per_cu *) 0)

    [2] ((addrmap *) 0x7fab08004220)

      [0x0] ((dwarf2_per_cu *) 0)

    [3] ((addrmap *) 0x7faaec004220)

      [0x0] ((dwarf2_per_cu *) 0)

    [4] ((addrmap *) 0x7fab00004220)

      [0x0] ((dwarf2_per_cu *) 0)

    [5] ((addrmap *) 0x7faae8004220)

      [0x0] ((dwarf2_per_cu *) 0)

    [6] ((addrmap *) 0x7faaf8004430)

      [0x0] ((dwarf2_per_cu *) 0)

    [7] ((addrmap *) 0x7faaf4006850)

      [0x0] ((dwarf2_per_cu *) 0)

    [8] ((addrmap *) 0x36fc5280)

      [0x0] ((dwarf2_per_cu *) 0)
      [0x400330] ((dwarf2_per_cu *) 0x7faaf40040e0)
      [0x40033e] ((dwarf2_per_cu *) 0)

(gdb) PASS: gdb.dwarf2/debug-names-bad-cu-index.exp: find index type
PASS: gdb.dwarf2/debug-names-bad-cu-index.exp: find index type, check type is valid
FAIL: gdb.dwarf2/debug-names-bad-cu-index.exp: .debug_names used
FAIL: gdb.dwarf2/debug-names-bad-cu-index.exp: saw complaint
set complaints 0
(gdb) testcase /home/gwenthekween/Documents/downstream-build/gdb/testsuite/../../../downstream-gdb/gdb/testsuite/gdb.dwarf2/debug-names-bad-cu-index.exp completed in 0 seconds

		=== gdb Summary ===

# of expected passes		3
# of unexpected failures	2
Executing on host: /home/gwenthekween/Documents/downstream-build/gdb/testsuite/../../gdb/gdb -nw -nx -q -iex "set height 0" -iex "set width 0" -data-directory /home/gwenthekween/Documents/downstream-build/gdb/data-directory -iex "set interactive-mode on" --version    (timeout = 300)
builtin_spawn -ignore SIGHUP /home/gwenthekween/Documents/downstream-build/gdb/testsuite/../../gdb/gdb -nw -nx -q -iex set height 0 -iex set width 0 -data-directory /home/gwenthekween/Documents/downstream-build/gdb/data-directory -iex set interactive-mode on --version
GNU gdb (GDB) 18.0.50.20260513-git
Copyright (C) 2026 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
/home/gwenthekween/Documents/downstream-build/gdb/gdb version  18.0.50.20260513-git -nw -nx -q -iex "set height 0" -iex "set width 0" -data-directory /home/gwenthekween/Documents/downstream-build/gdb/data-directory -iex "set interactive-mode on" 

runtest completed at Tue May 19 16:25:14 2026

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

* Re: [PATCH v3 7/7] gdb/dwarf: read foreign type units
  2026-05-19 19:42   ` Guinevere Larsen
@ 2026-05-19 20:10     ` Simon Marchi
  0 siblings, 0 replies; 12+ messages in thread
From: Simon Marchi @ 2026-05-19 20:10 UTC (permalink / raw)
  To: Guinevere Larsen, gdb-patches; +Cc: Tom Tromey

On 5/19/26 3:42 PM, Guinevere Larsen wrote:
> Hi Simon!
> 
> This commit seems to have made the gdb.dwarf2/debug-names-bad-cu-index.exp have some racy cases. I've attached a log of the failed run in this email to help you figure out what could be going on.

Thanks, this is:

https://sourceware.org/bugzilla/show_bug.cgi?id=34149

I just sent a patch:

https://inbox.sourceware.org/gdb-patches/20260519200917.342813-1-simon.marchi@efficios.com/T/#u

Simon

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

end of thread, other threads:[~2026-05-19 20:10 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-05-11 15:40 [PATCH v3 0/7] Handle foreign type units in .debug_names Simon Marchi
2026-05-11 15:40 ` [PATCH v3 1/7] gdb/dwarf: move dwo_unit and dwo_file to dwo.h Simon Marchi
2026-05-13 15:18   ` Tom Tromey
2026-05-13 17:30     ` Simon Marchi
2026-05-11 15:40 ` [PATCH v3 2/7] gdb/dwarf: move dwarf2_cu::section to cu.c Simon Marchi
2026-05-11 15:40 ` [PATCH v3 3/7] gdb/dwarf: add dwo_file::find_tus Simon Marchi
2026-05-11 15:40 ` [PATCH v3 4/7] gdb/dwarf: generate foreign type units in .debug_names Simon Marchi
2026-05-11 15:40 ` [PATCH v3 5/7] gdb/dwarf: add debug output in read-debug-names.c Simon Marchi
2026-05-11 15:40 ` [PATCH v3 6/7] gdb/dwarf: add more context to complaints in mapped_debug_names_reader::scan_one_entry Simon Marchi
2026-05-11 15:40 ` [PATCH v3 7/7] gdb/dwarf: read foreign type units Simon Marchi
2026-05-19 19:42   ` Guinevere Larsen
2026-05-19 20:10     ` Simon Marchi

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