[v2,15/17] Rewrite .debug_names reader

Message ID 20240117-debug-names-fix-v2-15-dbd5971a9c31@tromey.com
State New
Headers
Series Rewrite .debug_names reader and writer |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_gdb_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_gdb_check--master-arm fail Testing failed
linaro-tcwg-bot/tcwg_gdb_check--master-aarch64 fail Testing failed

Commit Message

Tom Tromey Jan. 17, 2024, 4:39 p.m. UTC
  This rewrites the .debug_names reader to follow the spec.

Since it was first written, gdb's .debug_names writer has been
incorrect -- while the form of the section has been ok, the contents
have been very gdb-specific.

This patch fixes the reader side of this equation, rewriting the
reader to create a cooked index internally -- an important detail
because it allows for the deletion of a lot of code, and it means the
various readers will agree more often.

This reader checks for a new augmentation string.  For the time being,
all other producers are ignored -- the old GDB ones because they are
wrong, and clang because it does not emit DW_IDX_parent.  (If there
are any other producers, I'm unaware of them.)

While the new reader mostly runs in a worker thread, it does not try
to distribute its work.  This could be done by partitioning the name
table.  The parent computations could also be done in parallel after
all names have been read.  I haven't attempted this.

Note that this patch temporarily regresses gdb.base/gdb-index-err.exp.
This test writes an index using gdb -- but at this particular stage,
gdb cannot read the indexes it creates.  Rather than merge the patches
into a mega-patch, I've chosen to just accept this temporary
regression.

In v1 of this patch, I made the new reader more strict about requiring
.debug_aranges.  In v2, I've backed this out and kept the previous
logic.  This solved a few test failures, though it's arguably not the
right approach.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=25950
---
 gdb/dwarf2/read-debug-names.c                      | 954 ++++++++-------------
 .../gdb.dwarf2/debug-names-bad-cu-index.exp        |  20 +-
 gdb/testsuite/lib/dwarf.exp                        |   7 +-
 3 files changed, 389 insertions(+), 592 deletions(-)
  

Patch

diff --git a/gdb/dwarf2/read-debug-names.c b/gdb/dwarf2/read-debug-names.c
index b4ff36f0ed6..52caff725ca 100644
--- a/gdb/dwarf2/read-debug-names.c
+++ b/gdb/dwarf2/read-debug-names.c
@@ -20,6 +20,7 @@ 
 #include "defs.h"
 #include "read-debug-names.h"
 #include "dwarf2/aranges.h"
+#include "dwarf2/cooked-index.h"
 
 #include "complaints.h"
 #include "cp-support.h"
@@ -28,22 +29,62 @@ 
 #include "read.h"
 #include "stringify.h"
 
-/* A description of the mapped .debug_names.
-   Uninitialized map has CU_COUNT 0.  */
+/* This is just like cooked_index_functions, but overrides a single
+   method so the test suite can distinguish the .debug_names case from
+   the ordinary case.  */
+struct dwarf2_debug_names_index : public cooked_index_functions
+{
+  /* This dumps minimal information about .debug_names.  It is called
+     via "mt print objfiles".  The gdb.dwarf2/gdb-index.exp testcase
+     uses this to verify that .debug_names has been loaded.  */
+  void dump (struct objfile *objfile) override
+  {
+    gdb_printf (".debug_names: exists\n");
+    /* This could call the superclass method if that's useful.  */
+  }
+};
+
+/* This is like a cooked index, but as it has been ingested from
+   .debug_names, it can't be used to write out an index.  */
+class debug_names_index : public cooked_index
+{
+public:
+
+  using cooked_index::cooked_index;
+
+  cooked_index *index_for_writing () override
+  { return nullptr; }
+
+  quick_symbol_functions_up make_quick_functions () const override
+  { return quick_symbol_functions_up (new dwarf2_debug_names_index); }
+};
+
+/* A description of the mapped .debug_names.  */
 
-struct mapped_debug_names final : public mapped_index_base
+struct mapped_debug_names_reader
 {
-  bfd_endian dwarf5_byte_order;
-  bool dwarf5_is_dwarf64;
-  bool augmentation_is_gdb;
-  uint8_t offset_size;
+  const gdb_byte *scan_one_entry (const char *name,
+				  const gdb_byte *entry,
+				  cooked_index_entry **result,
+				  std::optional<ULONGEST> &parent);
+  void scan_entries (uint32_t index, const char *name, const gdb_byte *entry);
+  void scan_all_names ();
+
+  dwarf2_per_objfile *per_objfile = nullptr;
+  bfd *abfd = nullptr;
+  bfd_endian dwarf5_byte_order {};
+  bool dwarf5_is_dwarf64 = false;
+  bool augmentation_is_gdb = false;
+  uint8_t offset_size = 0;
   uint32_t cu_count = 0;
-  uint32_t tu_count, bucket_count, name_count;
-  const gdb_byte *cu_table_reordered, *tu_table_reordered;
-  const uint32_t *bucket_table_reordered, *hash_table_reordered;
-  const gdb_byte *name_table_string_offs_reordered;
-  const gdb_byte *name_table_entry_offs_reordered;
-  const gdb_byte *entry_pool;
+  uint32_t tu_count = 0, bucket_count = 0, name_count = 0;
+  const gdb_byte *cu_table_reordered = nullptr;
+  const gdb_byte *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;
+  const gdb_byte *name_table_entry_offs_reordered = nullptr;
+  const gdb_byte *entry_pool = nullptr;
 
   struct index_val
   {
@@ -64,41 +105,252 @@  struct mapped_debug_names final : public mapped_index_base
 
   std::unordered_map<ULONGEST, index_val> abbrev_map;
 
-  const char *namei_to_name
-    (uint32_t namei, dwarf2_per_objfile *per_objfile) const;
+  std::unique_ptr<cooked_index_shard> shard;
+  std::vector<std::pair<cooked_index_entry *, ULONGEST>> needs_parent;
+  std::vector<std::vector<cooked_index_entry *>> all_entries;
+};
 
-  /* Implementation of the mapped_index_base virtual interface, for
-     the name_components cache.  */
+/* 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.  */
 
-  const char *symbol_name_at
-    (offset_type idx, dwarf2_per_objfile *per_objfile) const override
-  { return namei_to_name (idx, per_objfile); }
+const gdb_byte *
+mapped_debug_names_reader::scan_one_entry (const char *name,
+					   const gdb_byte *entry,
+					   cooked_index_entry **result,
+					   std::optional<ULONGEST> &parent)
+{
+  unsigned int bytes_read;
+  const ULONGEST abbrev = read_unsigned_leb128 (abfd, entry, &bytes_read);
+  entry += bytes_read;
+  if (abbrev == 0)
+    return nullptr;
 
-  size_t symbol_name_count () const override
-  { return this->name_count; }
+  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));
+      return nullptr;
+    }
 
-  quick_symbol_functions_up make_quick_functions () const override;
-};
+  const auto &indexval = indexval_it->second;
+  cooked_index_flag flags = 0;
+  sect_offset die_offset {};
+  enum language lang = language_unknown;
+  dwarf2_per_cu_data *per_cu = nullptr;
+  for (const auto &attr : indexval.attr_vec)
+    {
+      ULONGEST ull;
+      switch (attr.form)
+	{
+	case DW_FORM_implicit_const:
+	  ull = attr.implicit_const;
+	  break;
+	case DW_FORM_flag_present:
+	  ull = 1;
+	  break;
+	case DW_FORM_udata:
+	  ull = read_unsigned_leb128 (abfd, entry, &bytes_read);
+	  entry += bytes_read;
+	  break;
+	case DW_FORM_ref4:
+	  ull = read_4_bytes (abfd, entry);
+	  entry += 4;
+	  break;
+	case DW_FORM_ref8:
+	  ull = read_8_bytes (abfd, entry);
+	  entry += 8;
+	  break;
+	case DW_FORM_ref_sig8:
+	  ull = read_8_bytes (abfd, entry);
+	  entry += 8;
+	  break;
+	default:
+	  complaint (_("Unsupported .debug_names form %s [in module %s]"),
+		     dwarf_form_name (attr.form),
+		     bfd_get_filename (abfd));
+	  return nullptr;
+	}
+      switch (attr.dw_idx)
+	{
+	case DW_IDX_compile_unit:
+	  {
+	    /* Don't crash on bad data.  */
+	    if (ull >= per_objfile->per_bfd->all_comp_units.size ())
+	      {
+		complaint (_(".debug_names entry has bad CU index %s"
+			     " [in module %s]"),
+			   pulongest (ull),
+			   bfd_get_filename (abfd));
+		continue;
+	      }
+	  }
+	  per_cu = per_objfile->per_bfd->get_cu (ull);
+	  break;
+	case DW_IDX_type_unit:
+	  /* Don't crash on bad data.  */
+	  if (ull >= per_objfile->per_bfd->all_type_units.size ())
+	    {
+	      complaint (_(".debug_names entry has bad TU index %s"
+			   " [in module %s]"),
+			 pulongest (ull),
+			 bfd_get_filename (abfd));
+	      continue;
+	    }
+	  {
+	    int nr_cus = per_objfile->per_bfd->all_comp_units.size ();
+	    per_cu = per_objfile->per_bfd->get_cu (nr_cus + 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 == NULL)
+	    per_cu = per_objfile->per_bfd->get_cu (0);
+	  break;
+	case DW_IDX_parent:
+	  parent = ull;
+	  break;
+	case DW_IDX_GNU_internal:
+	  if (augmentation_is_gdb && ull != 0)
+	    flags |= IS_STATIC;
+	  break;
+	case DW_IDX_GNU_main:
+	  if (augmentation_is_gdb && ull != 0)
+	    flags |= IS_MAIN;
+	  break;
+	case DW_IDX_GNU_language:
+	  if (augmentation_is_gdb)
+	    lang = dwarf_lang_to_enum_language (ull);
+	  break;
+	case DW_IDX_GNU_linkage_name:
+	  if (augmentation_is_gdb && ull != 0)
+	    flags |= IS_LINKAGE;
+	  break;
+	}
+    }
+
+  /* Skip if we couldn't find a valid CU/TU index.  */
+  if (per_cu != nullptr)
+    *result = shard->add (die_offset, (dwarf_tag) indexval.dwarf_tag, flags,
+			  lang, name, nullptr, per_cu);
+  return entry;
+}
+
+/* Scan all the entries for NAME, at name slot INDEX.  */
 
-struct dwarf2_debug_names_index : public dwarf2_base_index_functions
+void
+mapped_debug_names_reader::scan_entries (uint32_t index,
+					 const char *name,
+					 const gdb_byte *entry)
 {
-  void dump (struct objfile *objfile) override;
-
-  bool expand_symtabs_matching
-    (struct objfile *objfile,
-     gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
-     const lookup_name_info *lookup_name,
-     gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
-     gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
-     block_search_flags search_flags,
-     domain_enum domain,
-     enum search_domain kind) override;
+  std::vector<cooked_index_entry *> these_entries;
+  
+  while (true)
+    {
+      std::optional<ULONGEST> parent;
+      cooked_index_entry *this_entry;
+      entry = scan_one_entry (name, entry, &this_entry, parent);
+
+      if (entry == nullptr)
+	break;
+
+      these_entries.push_back (this_entry);
+      if (parent.has_value ())
+	needs_parent.emplace_back (this_entry, *parent);
+    }
+
+  all_entries[index] = std::move (these_entries);
+}
+
+/* Scan the name table and create all the entries.  */
+
+void
+mapped_debug_names_reader::scan_all_names ()
+{
+  all_entries.resize (name_count);
+
+  /* In the first pass, create all the entries.  */
+  for (uint32_t i = 0; i < name_count; ++i)
+    {
+      const ULONGEST namei_string_offs
+	= extract_unsigned_integer ((name_table_string_offs_reordered
+				     + i * offset_size),
+				    offset_size, dwarf5_byte_order);
+      const char *name = read_indirect_string_at_offset (per_objfile,
+							 namei_string_offs);
+
+      const ULONGEST namei_entry_offs
+	= extract_unsigned_integer ((name_table_entry_offs_reordered
+				     + i * offset_size),
+				    offset_size, dwarf5_byte_order);
+      const gdb_byte *entry = entry_pool + namei_entry_offs;
+
+      scan_entries (i, name, entry);
+    }
+
+  /* Now update the parent pointers for all entries.  This has to be
+     done in a funny way because DWARF specifies the parent entry to
+     point to a name -- but we don't know which specific one.  */
+  for (auto [entry, parent_idx] : needs_parent)
+    {
+      /* Name entries are indexed from 1 in DWARF.  */
+      std::vector<cooked_index_entry *> &entries = all_entries[parent_idx - 1];
+      for (const auto &parent : entries)
+	if (parent->lang == entry->lang)
+	  {
+	    entry->set_parent (parent);
+	    break;
+	  }
+    }
+}
+
+/* A reader for .debug_names.  */
+
+struct cooked_index_debug_names : public cooked_index_worker
+{
+  cooked_index_debug_names (dwarf2_per_objfile *per_objfile,
+			    mapped_debug_names_reader &&map)
+    : cooked_index_worker (per_objfile),
+      m_map (std::move (map))
+  { }
+
+  void do_reading () override;
+
+  mapped_debug_names_reader m_map;
 };
 
-quick_symbol_functions_up
-mapped_debug_names::make_quick_functions () const
+void
+cooked_index_debug_names::do_reading ()
 {
-  return quick_symbol_functions_up (new dwarf2_debug_names_index);
+  complaint_interceptor complaint_handler;
+  std::vector<gdb_exception> exceptions;
+  try
+    {
+      m_map.scan_all_names ();
+    }
+  catch (const gdb_exception &exc)
+    {
+      exceptions.push_back (std::move (exc));
+    }
+
+  dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd;
+  per_bfd->quick_file_names_table
+    = create_quick_file_names_table (per_bfd->all_units.size ());
+  m_results.emplace_back (nullptr,
+			  complaint_handler.release (),
+			  std::move (exceptions));
+  std::vector<std::unique_ptr<cooked_index_shard>> indexes;
+  indexes.push_back (std::move (m_map.shard));
+  cooked_index *table
+    = (gdb::checked_static_cast<cooked_index *>
+       (per_bfd->index_table.get ()));
+  table->set_contents (std::move (indexes));
+
+  bfd_thread_cleanup ();
 }
 
 /* Check the signatured type hash table from .debug_names.  */
@@ -106,7 +358,7 @@  mapped_debug_names::make_quick_functions () const
 static bool
 check_signatured_type_table_from_debug_names
   (dwarf2_per_objfile *per_objfile,
-   const mapped_debug_names &map,
+   const mapped_debug_names_reader &map,
    struct dwarf2_section_info *section)
 {
   struct objfile *objfile = per_objfile->objfile;
@@ -143,32 +395,18 @@  check_signatured_type_table_from_debug_names
   return true;
 }
 
-/* Read the address map data from DWARF-5 .debug_aranges, and use it to
-   populate the index_addrmap.  */
-
-static void
-create_addrmap_from_aranges (dwarf2_per_objfile *per_objfile,
-			     struct dwarf2_section_info *section)
-{
-  dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
-
-  addrmap_mutable mutable_map;
-  deferred_warnings warnings;
-
-  section->read (per_objfile->objfile);
-  if (read_addrmap_from_aranges (per_objfile, section, &mutable_map,
-				 &warnings))
-    per_bfd->index_addrmap
-      = new (&per_bfd->obstack) addrmap_fixed (&per_bfd->obstack,
-					       &mutable_map);
-
-  warnings.emit ();
-}
-
 /* DWARF-5 debug_names reader.  */
 
-/* DWARF-5 augmentation string for GDB's DW_IDX_GNU_* extension.  */
-static const gdb_byte dwarf5_augmentation[] = { 'G', 'D', 'B', 0 };
+/* The old, no-longer-supported GDB augmentation.  */
+static const gdb_byte old_gdb_augmentation[]
+     = { 'G', 'D', 'B', 0 };
+static_assert (sizeof (old_gdb_augmentation) % 4 == 0);
+
+/* DWARF-5 augmentation string for GDB's DW_IDX_GNU_* extension.  This
+   must have a size that is a multiple of 4.  */
+static const gdb_byte dwarf5_augmentation[]
+     = { 'G', 'D', 'B', '2', 0, 0, 0, 0 };
+static_assert (sizeof (dwarf5_augmentation) % 4 == 0);
 
 /* A helper function that reads the .debug_names section in SECTION
    and fills in MAP.  FILENAME is the name of the file containing the
@@ -177,11 +415,13 @@  static const gdb_byte dwarf5_augmentation[] = { 'G', 'D', 'B', 0 };
    Returns true if all went well, false otherwise.  */
 
 static bool
-read_debug_names_from_section (struct objfile *objfile,
+read_debug_names_from_section (dwarf2_per_objfile *per_objfile,
 			       const char *filename,
 			       struct dwarf2_section_info *section,
-			       mapped_debug_names &map)
+			       mapped_debug_names_reader &map)
 {
+  struct objfile *objfile = per_objfile->objfile;
+
   if (section->empty ())
     return false;
 
@@ -192,11 +432,13 @@  read_debug_names_from_section (struct objfile *objfile,
 
   section->read (objfile);
 
+  map.per_objfile = per_objfile;
   map.dwarf5_byte_order = gdbarch_byte_order (objfile->arch ());
 
   const gdb_byte *addr = section->buffer;
 
-  bfd *const abfd = section->get_bfd_owner ();
+  bfd *abfd = section->get_bfd_owner ();
+  map.abfd = abfd;
 
   unsigned int bytes_read;
   LONGEST length = read_initial_length (abfd, addr, &bytes_read);
@@ -275,11 +517,27 @@  read_debug_names_from_section (struct objfile *objfile,
      string.  This value is rounded up to a multiple of 4.  */
   uint32_t augmentation_string_size = read_4_bytes (abfd, addr);
   addr += 4;
+  augmentation_string_size += (-augmentation_string_size) & 3;
+
+  if (augmentation_string_size == sizeof (old_gdb_augmentation)
+      && memcmp (addr, old_gdb_augmentation,
+		 sizeof (old_gdb_augmentation)) == 0)
+    {
+      warning (_(".debug_names created by an old version of gdb; ignoring"));
+      return false;
+    }
+
   map.augmentation_is_gdb = ((augmentation_string_size
 			      == sizeof (dwarf5_augmentation))
 			     && memcmp (addr, dwarf5_augmentation,
 					sizeof (dwarf5_augmentation)) == 0);
-  augmentation_string_size += (-augmentation_string_size) & 3;
+
+  if (!map.augmentation_is_gdb)
+    {
+      warning (_(".debug_names not created by gdb; ignoring"));
+      return false;
+    }
+
   addr += augmentation_string_size;
 
   /* List of CUs */
@@ -312,7 +570,7 @@  read_debug_names_from_section (struct objfile *objfile,
 	break;
 
       const auto insertpair
-	= map.abbrev_map.emplace (index_num, mapped_debug_names::index_val ());
+	= map.abbrev_map.emplace (index_num, mapped_debug_names_reader::index_val ());
       if (!insertpair.second)
 	{
 	  warning (_("Section .debug_names in %s has duplicate index %s, "
@@ -320,13 +578,13 @@  read_debug_names_from_section (struct objfile *objfile,
 		   filename, pulongest (index_num));
 	  return false;
 	}
-      mapped_debug_names::index_val &indexval = insertpair.first->second;
+      mapped_debug_names_reader::index_val &indexval = insertpair.first->second;
       indexval.dwarf_tag = read_unsigned_leb128 (abfd, addr, &bytes_read);
       addr += bytes_read;
 
       for (;;)
 	{
-	  mapped_debug_names::index_val::attr attr;
+	  mapped_debug_names_reader::index_val::attr attr;
 	  attr.dw_idx = read_unsigned_leb128 (abfd, addr, &bytes_read);
 	  addr += bytes_read;
 	  attr.form = read_unsigned_leb128 (abfd, addr, &bytes_read);
@@ -360,9 +618,9 @@  read_debug_names_from_section (struct objfile *objfile,
 
 static bool
 check_cus_from_debug_names_list (dwarf2_per_bfd *per_bfd,
-				 const mapped_debug_names &map,
-				 dwarf2_section_info &section,
-				 bool is_dwz)
+				  const mapped_debug_names_reader &map,
+				  dwarf2_section_info &section,
+				  bool is_dwz)
 {
   int nr_cus = per_bfd->all_comp_units.size ();
 
@@ -424,8 +682,8 @@  check_cus_from_debug_names_list (dwarf2_per_bfd *per_bfd,
 
 static bool
 check_cus_from_debug_names (dwarf2_per_bfd *per_bfd,
-			    const mapped_debug_names &map,
-			    const mapped_debug_names &dwz_map)
+			     const mapped_debug_names_reader &map,
+			     const mapped_debug_names_reader &dwz_map)
 {
   if (!check_cus_from_debug_names_list (per_bfd, map, per_bfd->info,
 					false /* is_dwz */))
@@ -439,22 +697,23 @@  check_cus_from_debug_names (dwarf2_per_bfd *per_bfd,
 					  true /* is_dwz */);
 }
 
-/* See read-debug-names.h.  */
+/* This does all the work for dwarf2_read_debug_names, but putting it
+   into a separate function makes some cleanup a bit simpler.  */
 
-bool
-dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile)
+static bool
+do_dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile)
 {
-  auto map = std::make_unique<mapped_debug_names> ();
-  mapped_debug_names dwz_map;
+  mapped_debug_names_reader map;
+  mapped_debug_names_reader dwz_map;
   struct objfile *objfile = per_objfile->objfile;
   dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
 
-  if (!read_debug_names_from_section (objfile, objfile_name (objfile),
-				      &per_bfd->debug_names, *map))
+  if (!read_debug_names_from_section (per_objfile, objfile_name (objfile),
+				      &per_bfd->debug_names, map))
     return false;
 
   /* Don't use the index if it's empty.  */
-  if (map->name_count == 0)
+  if (map.name_count == 0)
     return false;
 
   /* If there is a .dwz file, read it so we can get its CU list as
@@ -462,7 +721,7 @@  dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile)
   dwz_file *dwz = dwarf2_get_dwz_file (per_bfd);
   if (dwz != NULL)
     {
-      if (!read_debug_names_from_section (objfile,
+      if (!read_debug_names_from_section (per_objfile,
 					  bfd_get_filename (dwz->dwz_bfd.get ()),
 					  &dwz->debug_names, dwz_map))
 	{
@@ -473,525 +732,54 @@  dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile)
     }
 
   create_all_units (per_objfile);
-  if (!check_cus_from_debug_names (per_bfd, *map, dwz_map))
-    {
-      per_bfd->all_units.clear ();
-      return false;
-    }
+  if (!check_cus_from_debug_names (per_bfd, map, dwz_map))
+    return false;
 
-  if (map->tu_count != 0)
+  if (map.tu_count != 0)
     {
       /* We can only handle a single .debug_types when we have an
 	 index.  */
       if (per_bfd->types.size () > 1)
-	{
-	  per_bfd->all_units.clear ();
-	  return false;
-	}
+	return false;
 
       dwarf2_section_info *section
 	= (per_bfd->types.size () == 1
 	   ? &per_bfd->types[0]
 	   : &per_bfd->info);
 
-      if (!check_signatured_type_table_from_debug_names
-	     (per_objfile, *map, section))
-	{
-	  per_bfd->all_units.clear ();
-	  return false;
-	}
-    }
-
-  create_addrmap_from_aranges (per_objfile, &per_bfd->debug_aranges);
-
-  per_bfd->index_table = std::move (map);
-  per_bfd->quick_file_names_table =
-    create_quick_file_names_table (per_bfd->all_units.size ());
-
-  return true;
-}
-
-/* Type used to manage iterating over all CUs looking for a symbol for
-   .debug_names.  */
-
-class dw2_debug_names_iterator
-{
-public:
-  dw2_debug_names_iterator (const mapped_debug_names &map,
-			    block_search_flags block_index,
-			    domain_enum domain,
-			    const char *name, dwarf2_per_objfile *per_objfile)
-    : m_map (map), m_block_index (block_index), m_domain (domain),
-      m_addr (find_vec_in_debug_names (map, name, per_objfile)),
-      m_per_objfile (per_objfile)
-  {}
-
-  dw2_debug_names_iterator (const mapped_debug_names &map,
-			    search_domain search, uint32_t namei,
-			    dwarf2_per_objfile *per_objfile,
-			    domain_enum domain = UNDEF_DOMAIN)
-    : m_map (map),
-      m_domain (domain),
-      m_search (search),
-      m_addr (find_vec_in_debug_names (map, namei, per_objfile)),
-      m_per_objfile (per_objfile)
-  {}
-
-  dw2_debug_names_iterator (const mapped_debug_names &map,
-			    block_search_flags block_index, domain_enum domain,
-			    uint32_t namei, dwarf2_per_objfile *per_objfile)
-    : m_map (map), m_block_index (block_index), m_domain (domain),
-      m_addr (find_vec_in_debug_names (map, namei, per_objfile)),
-      m_per_objfile (per_objfile)
-  {}
-
-  /* Return the next matching CU or NULL if there are no more.  */
-  dwarf2_per_cu_data *next ();
-
-private:
-  static const gdb_byte *find_vec_in_debug_names (const mapped_debug_names &map,
-						  const char *name,
-						  dwarf2_per_objfile *per_objfile);
-  static const gdb_byte *find_vec_in_debug_names (const mapped_debug_names &map,
-						  uint32_t namei,
-						  dwarf2_per_objfile *per_objfile);
-
-  /* The internalized form of .debug_names.  */
-  const mapped_debug_names &m_map;
-
-  /* Restrict the search to these blocks.  */
-  block_search_flags m_block_index = (SEARCH_GLOBAL_BLOCK
-				      | SEARCH_STATIC_BLOCK);
-
-  /* The kind of symbol we're looking for.  */
-  const domain_enum m_domain = UNDEF_DOMAIN;
-  const search_domain m_search = ALL_DOMAIN;
-
-  /* The list of CUs from the index entry of the symbol, or NULL if
-     not found.  */
-  const gdb_byte *m_addr;
-
-  dwarf2_per_objfile *m_per_objfile;
-};
-
-const char *
-mapped_debug_names::namei_to_name
-  (uint32_t namei, dwarf2_per_objfile *per_objfile) const
-{
-  const ULONGEST namei_string_offs
-    = extract_unsigned_integer ((name_table_string_offs_reordered
-				 + namei * offset_size),
-				offset_size,
-				dwarf5_byte_order);
-  return read_indirect_string_at_offset (per_objfile, namei_string_offs);
-}
-
-/* Find a slot in .debug_names for the object named NAME.  If NAME is
-   found, return pointer to its pool data.  If NAME cannot be found,
-   return NULL.  */
-
-const gdb_byte *
-dw2_debug_names_iterator::find_vec_in_debug_names
-  (const mapped_debug_names &map, const char *name,
-   dwarf2_per_objfile *per_objfile)
-{
-  int (*cmp) (const char *, const char *);
-
-  gdb::unique_xmalloc_ptr<char> without_params;
-  if (current_language->la_language == language_cplus
-      || current_language->la_language == language_fortran
-      || current_language->la_language == language_d)
-    {
-      /* NAME is already canonical.  Drop any qualifiers as
-	 .debug_names does not contain any.  */
-
-      if (strchr (name, '(') != NULL)
-	{
-	  without_params = cp_remove_params (name);
-	  if (without_params != NULL)
-	    name = without_params.get ();
-	}
-    }
-
-  cmp = (case_sensitivity == case_sensitive_on ? strcmp : strcasecmp);
-
-  const uint32_t full_hash = dwarf5_djb_hash (name);
-  uint32_t namei
-    = extract_unsigned_integer (reinterpret_cast<const gdb_byte *>
-				(map.bucket_table_reordered
-				 + (full_hash % map.bucket_count)), 4,
-				map.dwarf5_byte_order);
-  if (namei == 0)
-    return NULL;
-  --namei;
-  if (namei >= map.name_count)
-    {
-      complaint (_("Wrong .debug_names with name index %u but name_count=%u "
-		   "[in module %s]"),
-		 namei, map.name_count,
-		 objfile_name (per_objfile->objfile));
-      return NULL;
-    }
-
-  for (;;)
-    {
-      const uint32_t namei_full_hash
-	= extract_unsigned_integer (reinterpret_cast<const gdb_byte *>
-				    (map.hash_table_reordered + namei), 4,
-				    map.dwarf5_byte_order);
-      if (full_hash % map.bucket_count != namei_full_hash % map.bucket_count)
-	return NULL;
-
-      if (full_hash == namei_full_hash)
-	{
-	  const char *const namei_string = map.namei_to_name (namei, per_objfile);
-
-#if 0 /* An expensive sanity check.  */
-	  if (namei_full_hash != dwarf5_djb_hash (namei_string))
-	    {
-	      complaint (_("Wrong .debug_names hash for string at index %u "
-			   "[in module %s]"),
-			 namei, objfile_name (dwarf2_per_objfile->objfile));
-	      return NULL;
-	    }
-#endif
-
-	  if (cmp (namei_string, name) == 0)
-	    {
-	      const ULONGEST namei_entry_offs
-		= extract_unsigned_integer ((map.name_table_entry_offs_reordered
-					     + namei * map.offset_size),
-					    map.offset_size, map.dwarf5_byte_order);
-	      return map.entry_pool + namei_entry_offs;
-	    }
-	}
-
-      ++namei;
-      if (namei >= map.name_count)
-	return NULL;
+      if (!check_signatured_type_table_from_debug_names (per_objfile,
+							 map, section))
+	return false;
     }
-}
-
-const gdb_byte *
-dw2_debug_names_iterator::find_vec_in_debug_names
-  (const mapped_debug_names &map, uint32_t namei, dwarf2_per_objfile *per_objfile)
-{
-  if (namei >= map.name_count)
-    {
-      complaint (_("Wrong .debug_names with name index %u but name_count=%u "
-		   "[in module %s]"),
-		 namei, map.name_count,
-		 objfile_name (per_objfile->objfile));
-      return NULL;
-    }
-
-  const ULONGEST namei_entry_offs
-    = extract_unsigned_integer ((map.name_table_entry_offs_reordered
-				 + namei * map.offset_size),
-				map.offset_size, map.dwarf5_byte_order);
-  return map.entry_pool + namei_entry_offs;
-}
-
-/* See dw2_debug_names_iterator.  */
-
-dwarf2_per_cu_data *
-dw2_debug_names_iterator::next ()
-{
-  if (m_addr == NULL)
-    return NULL;
-
-  dwarf2_per_bfd *per_bfd = m_per_objfile->per_bfd;
-  struct objfile *objfile = m_per_objfile->objfile;
-  bfd *const abfd = objfile->obfd.get ();
 
- again:
-
-  unsigned int bytes_read;
-  const ULONGEST abbrev = read_unsigned_leb128 (abfd, m_addr, &bytes_read);
-  m_addr += bytes_read;
-  if (abbrev == 0)
-    return NULL;
-
-  const auto indexval_it = m_map.abbrev_map.find (abbrev);
-  if (indexval_it == m_map.abbrev_map.cend ())
-    {
-      complaint (_("Wrong .debug_names undefined abbrev code %s "
-		   "[in module %s]"),
-		 pulongest (abbrev), objfile_name (objfile));
-      return NULL;
-    }
-  const mapped_debug_names::index_val &indexval = indexval_it->second;
-  enum class symbol_linkage {
-    unknown,
-    static_,
-    extern_,
-  } symbol_linkage_ = symbol_linkage::unknown;
-  dwarf2_per_cu_data *per_cu = NULL;
-  for (const mapped_debug_names::index_val::attr &attr : indexval.attr_vec)
-    {
-      ULONGEST ull;
-      switch (attr.form)
-	{
-	case DW_FORM_implicit_const:
-	  ull = attr.implicit_const;
-	  break;
-	case DW_FORM_flag_present:
-	  ull = 1;
-	  break;
-	case DW_FORM_udata:
-	  ull = read_unsigned_leb128 (abfd, m_addr, &bytes_read);
-	  m_addr += bytes_read;
-	  break;
-	case DW_FORM_ref4:
-	  ull = read_4_bytes (abfd, m_addr);
-	  m_addr += 4;
-	  break;
-	case DW_FORM_ref8:
-	  ull = read_8_bytes (abfd, m_addr);
-	  m_addr += 8;
-	  break;
-	case DW_FORM_ref_sig8:
-	  ull = read_8_bytes (abfd, m_addr);
-	  m_addr += 8;
-	  break;
-	default:
-	  complaint (_("Unsupported .debug_names form %s [in module %s]"),
-		     dwarf_form_name (attr.form),
-		     objfile_name (objfile));
-	  return NULL;
-	}
-      switch (attr.dw_idx)
-	{
-	case DW_IDX_compile_unit:
-	  {
-	    /* Don't crash on bad data.  */
-	    if (ull >= per_bfd->all_comp_units.size ())
-	      {
-		complaint (_(".debug_names entry has bad CU index %s"
-			     " [in module %s]"),
-			   pulongest (ull),
-			   objfile_name (objfile));
-		continue;
-	      }
-	  }
-	  per_cu = per_bfd->get_index_cu (ull);
-	  break;
-	case DW_IDX_type_unit:
-	  /* Don't crash on bad data.  */
-	  if (ull >= per_bfd->all_type_units.size ())
-	    {
-	      complaint (_(".debug_names entry has bad TU index %s"
-			   " [in module %s]"),
-			 pulongest (ull),
-			 objfile_name (objfile));
-	      continue;
-	    }
-	  {
-	    per_cu = per_bfd->get_index_tu (ull);
-	  }
-	  break;
-	case DW_IDX_die_offset:
-	  /* 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 == NULL)
-	    per_cu = per_bfd->get_index_cu (0);
-	  break;
-	case DW_IDX_GNU_internal:
-	  if (!m_map.augmentation_is_gdb)
-	    break;
-	  symbol_linkage_ = symbol_linkage::static_;
-	  break;
-	case DW_IDX_GNU_external:
-	  if (!m_map.augmentation_is_gdb)
-	    break;
-	  symbol_linkage_ = symbol_linkage::extern_;
-	  break;
-	}
-    }
-
-  /* Skip if we couldn't find a valid CU/TU index.  */
-  if (per_cu == nullptr)
-    goto again;
-
-  /* Skip if already read in.  */
-  if (m_per_objfile->symtab_set_p (per_cu))
-    goto again;
+  per_bfd->debug_aranges.read (per_objfile->objfile);
+  addrmap_mutable addrmap;
+  deferred_warnings warnings;
+  read_addrmap_from_aranges (per_objfile, &per_bfd->debug_aranges,
+			     &addrmap, &warnings);
+  warnings.emit ();
 
-  /* Check static vs global.  */
-  if (symbol_linkage_ != symbol_linkage::unknown)
-    {
-      if (symbol_linkage_ == symbol_linkage::static_)
-	{
-	  if ((m_block_index & SEARCH_STATIC_BLOCK) == 0)
-	    goto again;
-	}
-      else
-	{
-	  if ((m_block_index & SEARCH_GLOBAL_BLOCK) == 0)
-	    goto again;
-	}
-    }
+  map.shard = std::make_unique<cooked_index_shard> ();
+  map.shard->install_addrmap (&addrmap);
 
-  /* Match dw2_symtab_iter_next, symbol_kind
-     and debug_names::psymbol_tag.  */
-  switch (m_domain)
-    {
-    case VAR_DOMAIN:
-      switch (indexval.dwarf_tag)
-	{
-	case DW_TAG_variable:
-	case DW_TAG_subprogram:
-	/* Some types are also in VAR_DOMAIN.  */
-	case DW_TAG_typedef:
-	case DW_TAG_structure_type:
-	  break;
-	default:
-	  goto again;
-	}
-      break;
-    case STRUCT_DOMAIN:
-      switch (indexval.dwarf_tag)
-	{
-	case DW_TAG_typedef:
-	case DW_TAG_structure_type:
-	  break;
-	default:
-	  goto again;
-	}
-      break;
-    case LABEL_DOMAIN:
-      switch (indexval.dwarf_tag)
-	{
-	case 0:
-	case DW_TAG_variable:
-	  break;
-	default:
-	  goto again;
-	}
-      break;
-    case MODULE_DOMAIN:
-      switch (indexval.dwarf_tag)
-	{
-	case DW_TAG_module:
-	  break;
-	default:
-	  goto again;
-	}
-      break;
-    default:
-      break;
-    }
+  cooked_index *idx
+    = new debug_names_index (per_objfile,
+			     (std::make_unique<cooked_index_debug_names>
+			      (per_objfile, std::move (map))));
+  per_bfd->index_table.reset (idx);
 
-  /* Match dw2_expand_symtabs_matching, symbol_kind and
-     debug_names::psymbol_tag.  */
-  switch (m_search)
-    {
-    case VARIABLES_DOMAIN:
-      switch (indexval.dwarf_tag)
-	{
-	case DW_TAG_variable:
-	  break;
-	default:
-	  goto again;
-	}
-      break;
-    case FUNCTIONS_DOMAIN:
-      switch (indexval.dwarf_tag)
-	{
-	case DW_TAG_subprogram:
-	  break;
-	default:
-	  goto again;
-	}
-      break;
-    case TYPES_DOMAIN:
-      switch (indexval.dwarf_tag)
-	{
-	case DW_TAG_typedef:
-	case DW_TAG_structure_type:
-	  break;
-	default:
-	  goto again;
-	}
-      break;
-    case MODULES_DOMAIN:
-      switch (indexval.dwarf_tag)
-	{
-	case DW_TAG_module:
-	  break;
-	default:
-	  goto again;
-	}
-    default:
-      break;
-    }
+  idx->start_reading ();
 
-  return per_cu;
+  return true;
 }
 
-/* This dumps minimal information about .debug_names.  It is called
-   via "mt print objfiles".  The gdb.dwarf2/gdb-index.exp testcase
-   uses this to verify that .debug_names has been loaded.  */
-
-void
-dwarf2_debug_names_index::dump (struct objfile *objfile)
-{
-  gdb_printf (".debug_names: exists\n");
-}
+/* See read-debug-names.h.  */
 
 bool
-dwarf2_debug_names_index::expand_symtabs_matching
-  (struct objfile *objfile,
-   gdb::function_view<expand_symtabs_file_matcher_ftype> file_matcher,
-   const lookup_name_info *lookup_name,
-   gdb::function_view<expand_symtabs_symbol_matcher_ftype> symbol_matcher,
-   gdb::function_view<expand_symtabs_exp_notify_ftype> expansion_notify,
-   block_search_flags search_flags,
-   domain_enum domain,
-   enum search_domain kind)
+dwarf2_read_debug_names (dwarf2_per_objfile *per_objfile)
 {
-  dwarf2_per_objfile *per_objfile = get_dwarf2_per_objfile (objfile);
-
-  dw_expand_symtabs_matching_file_matcher (per_objfile, file_matcher);
-
-  /* This invariant is documented in quick-functions.h.  */
-  gdb_assert (lookup_name != nullptr || symbol_matcher == nullptr);
-  if (lookup_name == nullptr)
-    {
-      for (dwarf2_per_cu_data *per_cu
-	     : all_units_range (per_objfile->per_bfd))
-	{
-	  QUIT;
-
-	  if (!dw2_expand_symtabs_matching_one (per_cu, per_objfile,
-						file_matcher,
-						expansion_notify))
-	    return false;
-	}
-      return true;
-    }
-
-  mapped_debug_names &map
-    = *(gdb::checked_static_cast<mapped_debug_names *>
-	(per_objfile->per_bfd->index_table.get ()));
-
-  bool result
-    = dw2_expand_symtabs_matching_symbol (map, *lookup_name,
-					  symbol_matcher,
-					  [&] (offset_type namei)
-    {
-      /* The name was matched, now expand corresponding CUs that were
-	 marked.  */
-      dw2_debug_names_iterator iter (map, kind, namei, per_objfile, domain);
-
-      struct dwarf2_per_cu_data *per_cu;
-      while ((per_cu = iter.next ()) != NULL)
-	if (!dw2_expand_symtabs_matching_one (per_cu, per_objfile,
-					      file_matcher,
-					      expansion_notify))
-	  return false;
-      return true;
-    }, per_objfile);
-
+  bool result = do_dwarf2_read_debug_names (per_objfile);
+  if (!result)
+    per_objfile->per_bfd->all_units.clear ();
   return result;
 }
diff --git a/gdb/testsuite/gdb.dwarf2/debug-names-bad-cu-index.exp b/gdb/testsuite/gdb.dwarf2/debug-names-bad-cu-index.exp
index ff5baf9833c..c3ef2e2a1e0 100644
--- a/gdb/testsuite/gdb.dwarf2/debug-names-bad-cu-index.exp
+++ b/gdb/testsuite/gdb.dwarf2/debug-names-bad-cu-index.exp
@@ -73,16 +73,22 @@  Dwarf::assemble {
     }
 }
 
-if [prepare_for_testing "failed to prepare" $testfile "${asm_file} ${srcfile}" \
-	[list additional_flags=-nostartfiles]] {
+if {[build_executable ${testfile}.exp $testfile "${asm_file} ${srcfile}" \
+	 [list additional_flags=-nostartfiles]] == -1} {
     return -1
 }
 
-# Verify that .debug_names section is not ignored.
-set index [have_index $binfile]
-gdb_assert { [string equal $index "debug_names"] } ".debug_names used"
+clean_restart
 
-set re "During symbol reading: .debug_names entry has bad CU index 1 "
 with_complaints 1 {
-    gdb_test "p _start" "$re.*"
+    gdb_test_no_output "maint set dwarf synchronous on"
+
+    gdb_load $binfile
+
+    # Verify that .debug_names section is not ignored.
+    set index [have_index $binfile]
+    gdb_assert { [string equal $index "debug_names"] } ".debug_names used"
+
+    set re "During symbol reading: .debug_names entry has bad CU index 1 "
+    gdb_assert {[regexp $re.* $gdb_file_cmd_msg]} "saw complaint"
 }
diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
index 88d6acfe6fc..d085f835f07 100644
--- a/gdb/testsuite/lib/dwarf.exp
+++ b/gdb/testsuite/lib/dwarf.exp
@@ -3066,8 +3066,11 @@  namespace eval Dwarf {
 	_op .4byte $abbrev_table_size "Abbrev_table_size"
 
 	# Header - augmentation string.
-	_op .4byte 4 "Augmentation_string_size"
-	_op .ascii [_quote GDB] "Augmentation_string"
+	_op .4byte 8 "Augmentation_string_size"
+	_op .ascii [_quote GDB2] "Augmentation_string"
+	_op .byte 0
+	_op .byte 0
+	_op .byte 0
 
 	# List of CUs.
 	set comment "CU offset"