[v2,3/3] gdb/dwarf: move cooked_indexer to cooked-indexer.{h, c}

Message ID 20250306203105.259225-3-simon.marchi@polymtl.ca
State New
Headers
Series [v2,1/3] gdb/dwarf: move cutu_reader to read.h |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gdb_build--master-aarch64 fail Patch failed to apply
linaro-tcwg-bot/tcwg_gdb_build--master-arm fail Patch failed to apply

Commit Message

Simon Marchi March 6, 2025, 8:30 p.m. UTC
  From: Simon Marchi <simon.marchi@polymtl.ca>

Move the cooked_indexer class declaration to a new cooked-indexer.h
file, and the implementation to cooked-indexer.c.

Change-Id: Ibff3b06045b2af65fa9516097acf732d7c2d9414
---
 gdb/Makefile.in             |   2 +
 gdb/dwarf2/cooked-indexer.c | 689 +++++++++++++++++++++++++++++
 gdb/dwarf2/cooked-indexer.h | 120 ++++++
 gdb/dwarf2/read.c           | 833 +-----------------------------------
 gdb/dwarf2/read.h           |  87 ++++
 5 files changed, 910 insertions(+), 821 deletions(-)
 create mode 100644 gdb/dwarf2/cooked-indexer.c
 create mode 100644 gdb/dwarf2/cooked-indexer.h
  

Comments

Tom Tromey March 7, 2025, 2:06 p.m. UTC | #1
>>>>> "Simon" == simon marchi <simon.marchi@polymtl.ca> writes:

Simon> From: Simon Marchi <simon.marchi@polymtl.ca>
Simon> Move the cooked_indexer class declaration to a new cooked-indexer.h
Simon> file, and the implementation to cooked-indexer.c.

Thanks once again.
Approved-By: Tom Tromey <tom@tromey.com>

Tom
  
Simon Marchi March 7, 2025, 2:51 p.m. UTC | #2
On 2025-03-07 09:06, Tom Tromey wrote:
>>>>>> "Simon" == simon marchi <simon.marchi@polymtl.ca> writes:
> 
> Simon> From: Simon Marchi <simon.marchi@polymtl.ca>
> Simon> Move the cooked_indexer class declaration to a new cooked-indexer.h
> Simon> file, and the implementation to cooked-indexer.c.
> 
> Thanks once again.
> Approved-By: Tom Tromey <tom@tromey.com>
> 
> Tom

Thanks, I pushed the series.

Simon
  

Patch

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index bf41f4142fbf..b176e9cedf2a 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1098,6 +1098,7 @@  COMMON_SFILES = \
 	dwarf2/comp-unit-head.c \
 	dwarf2/cooked-index.c \
 	dwarf2/cooked-index-storage.c \
+	dwarf2/cooked-indexer.c \
 	dwarf2/cu.c \
 	dwarf2/die.c \
 	dwarf2/dwz.c \
@@ -1356,6 +1357,7 @@  HFILES_NO_SRCDIR = \
 	dwarf2/aranges.h \
 	dwarf2/cooked-index.h \
 	dwarf2/cooked-index-storage.h \
+	dwarf2/cooked-indexer.h \
 	dwarf2/cu.h \
 	dwarf2/frame-tailcall.h \
 	dwarf2/frame.h \
diff --git a/gdb/dwarf2/cooked-indexer.c b/gdb/dwarf2/cooked-indexer.c
new file mode 100644
index 000000000000..2f362d8272d1
--- /dev/null
+++ b/gdb/dwarf2/cooked-indexer.c
@@ -0,0 +1,689 @@ 
+/* DWARF indexer
+
+   Copyright (C) 2022-2025 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/>.  */
+
+#include "dwarf2/cooked-indexer.h"
+#include "dwarf2/cooked-index-storage.h"
+#include "dwarf2/error.h"
+
+/* See cooked-indexer.h.  */
+
+cooked_indexer::cooked_indexer (cooked_index_storage *storage,
+				dwarf2_per_cu *per_cu, enum language language)
+  : m_index_storage (storage),
+    m_per_cu (per_cu),
+    m_language (language),
+    m_die_range_map (storage->get_parent_map ())
+{
+}
+
+/* See cooked-indexer.h.  */
+
+void
+cooked_indexer::check_bounds (cutu_reader *reader)
+{
+  dwarf2_cu *cu = reader->cu ();
+
+  if (cu->per_cu->addresses_seen)
+    return;
+
+  unrelocated_addr best_lowpc = {}, best_highpc = {};
+  /* Possibly set the default values of LOWPC and HIGHPC from
+     `DW_AT_ranges'.  */
+  dwarf2_find_base_address (reader->comp_unit_die (), cu);
+  enum pc_bounds_kind cu_bounds_kind
+    = dwarf2_get_pc_bounds (reader->comp_unit_die (), &best_lowpc, &best_highpc,
+			    cu, m_index_storage->get_addrmap (), cu->per_cu);
+  if (cu_bounds_kind == PC_BOUNDS_HIGH_LOW && best_lowpc < best_highpc)
+    {
+      /* Store the contiguous range if it is not empty; it can be
+	 empty for CUs with no code.  addrmap requires CORE_ADDR, so
+	 we cast here.  */
+      m_index_storage->get_addrmap ()->set_empty ((CORE_ADDR) best_lowpc,
+						  (CORE_ADDR) best_highpc - 1,
+						  cu->per_cu);
+
+      cu->per_cu->addresses_seen = true;
+    }
+}
+
+/* Helper function that returns true if TAG can have a linkage
+   name.  */
+
+static bool
+tag_can_have_linkage_name (enum dwarf_tag tag)
+{
+  switch (tag)
+    {
+    case DW_TAG_variable:
+    case DW_TAG_subprogram:
+      return true;
+
+    default:
+      return false;
+    }
+}
+
+/* See cooked-indexer.h.  */
+
+cutu_reader *
+cooked_indexer::ensure_cu_exists (cutu_reader *reader,
+				  dwarf2_per_objfile *per_objfile,
+				  sect_offset sect_off, bool is_dwz,
+				  bool for_scanning)
+{
+  /* Lookups for type unit references are always in the CU, and
+     cross-CU references will crash.  */
+  if (reader->cu ()->per_cu->is_dwz == is_dwz
+      && reader->cu ()->header.offset_in_cu_p (sect_off))
+    return reader;
+
+  dwarf2_per_cu *per_cu
+    = dwarf2_find_containing_comp_unit (sect_off, is_dwz,
+					per_objfile->per_bfd);
+
+  /* When scanning, we only want to visit a given CU a single time.
+     Doing this check here avoids self-imports as well.  */
+  if (for_scanning)
+    {
+      bool nope = false;
+      if (!per_cu->scanned.compare_exchange_strong (nope, true))
+	return nullptr;
+    }
+
+  cutu_reader *result = m_index_storage->get_reader (per_cu);
+  if (result == nullptr)
+    {
+      cutu_reader new_reader (per_cu, per_objfile, nullptr, nullptr, false,
+			      language_minimal,
+			      &m_index_storage->get_abbrev_table_cache ());
+
+      if (new_reader.is_dummy () || new_reader.comp_unit_die () == nullptr
+	  || !new_reader.comp_unit_die ()->has_children)
+	return nullptr;
+
+      auto copy = std::make_unique<cutu_reader> (std::move (new_reader));
+      result = m_index_storage->preserve (std::move (copy));
+    }
+
+  if (result->is_dummy () || result->comp_unit_die () == nullptr
+      || !result->comp_unit_die ()->has_children)
+    return nullptr;
+
+  if (for_scanning)
+    check_bounds (result);
+
+  return result;
+}
+
+/* See cooked-indexer.h.  */
+
+const gdb_byte *
+cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu,
+				 cutu_reader *reader,
+				 const gdb_byte *watermark_ptr,
+				 const gdb_byte *info_ptr,
+				 const abbrev_info *abbrev,
+				 const char **name,
+				 const char **linkage_name,
+				 cooked_index_flag *flags,
+				 sect_offset *sibling_offset,
+				 const cooked_index_entry **parent_entry,
+				 parent_map::addr_type *maybe_defer,
+				 bool *is_enum_class,
+				 bool for_specification)
+{
+  bool origin_is_dwz = false;
+  bool is_declaration = false;
+  sect_offset origin_offset {};
+
+  std::optional<unrelocated_addr> low_pc;
+  std::optional<unrelocated_addr> high_pc;
+  bool high_pc_relative = false;
+
+  for (int i = 0; i < abbrev->num_attrs; ++i)
+    {
+      attribute attr;
+      info_ptr = reader->read_attribute (&attr, &abbrev->attrs[i], info_ptr);
+
+      /* Store the data if it is of an attribute we want to keep in a
+	 partial symbol table.  */
+      switch (attr.name)
+	{
+	case DW_AT_name:
+	  switch (abbrev->tag)
+	    {
+	    case DW_TAG_compile_unit:
+	    case DW_TAG_partial_unit:
+	    case DW_TAG_type_unit:
+	      /* Compilation units have a DW_AT_name that is a filename, not
+		 a source language identifier.  */
+	      break;
+
+	    default:
+	      if (*name == nullptr)
+		*name = attr.as_string ();
+	      break;
+	    }
+	  break;
+
+	case DW_AT_linkage_name:
+	case DW_AT_MIPS_linkage_name:
+	  /* Note that both forms of linkage name might appear.  We
+	     assume they will be the same, and we only store the last
+	     one we see.  */
+	  if (*linkage_name == nullptr)
+	    *linkage_name = attr.as_string ();
+	  break;
+
+	/* DWARF 4 has defined a dedicated DW_AT_main_subprogram
+	   attribute to indicate the starting function of the program...  */
+	case DW_AT_main_subprogram:
+	  if (attr.as_boolean ())
+	    *flags |= IS_MAIN;
+	  break;
+
+	/* ... however with older versions the DW_CC_program value of
+	   the DW_AT_calling_convention attribute was used instead as
+	   the only means available.  We handle both variants then.  */
+	case DW_AT_calling_convention:
+	  if (attr.constant_value (DW_CC_normal) == DW_CC_program)
+	    *flags |= IS_MAIN;
+	  break;
+
+	case DW_AT_declaration:
+	  is_declaration = attr.as_boolean ();
+	  break;
+
+	case DW_AT_sibling:
+	  if (sibling_offset != nullptr)
+	    *sibling_offset = attr.get_ref_die_offset ();
+	  break;
+
+	case DW_AT_specification:
+	case DW_AT_abstract_origin:
+	case DW_AT_extension:
+	  origin_offset = attr.get_ref_die_offset ();
+	  origin_is_dwz = attr.form == DW_FORM_GNU_ref_alt;
+	  break;
+
+	case DW_AT_external:
+	  if (attr.as_boolean ())
+	    *flags &= ~IS_STATIC;
+	  break;
+
+	case DW_AT_enum_class:
+	  if (attr.as_boolean ())
+	    *is_enum_class = true;
+	  break;
+
+	case DW_AT_low_pc:
+	  low_pc = attr.as_address ();
+	  break;
+
+	case DW_AT_high_pc:
+	  high_pc = attr.as_address ();
+	  if (reader->cu ()->header.version >= 4 && attr.form_is_constant ())
+	    high_pc_relative = true;
+	  break;
+
+	case DW_AT_location:
+	  if (!scanning_per_cu->addresses_seen && attr.form_is_block ())
+	    {
+	      struct dwarf_block *locdesc = attr.as_block ();
+	      CORE_ADDR addr;
+	      dwarf2_cu *cu = reader->cu ();
+
+	      if (decode_locdesc (locdesc, cu, &addr)
+		  && (addr != 0
+		      || cu->per_objfile->per_bfd->has_section_at_zero))
+		{
+		  low_pc = (unrelocated_addr) addr;
+		  /* For variables, we don't want to try decoding the
+		     type just to find the size -- for gdb's purposes
+		     we only need the address of a variable.  */
+		  high_pc = (unrelocated_addr) (addr + 1);
+		  high_pc_relative = false;
+		}
+	    }
+	  break;
+
+	case DW_AT_ranges:
+	  if (!scanning_per_cu->addresses_seen)
+	    {
+	      /* Offset in the .debug_ranges or .debug_rnglist section
+		 (depending on DWARF version).  */
+	      ULONGEST ranges_offset = attr.as_unsigned ();
+
+	      /* See dwarf2_cu::gnu_ranges_base's doc for why we might
+		 want to add this value.  */
+	      ranges_offset += reader->cu ()->gnu_ranges_base;
+
+	      unrelocated_addr lowpc, highpc;
+	      dwarf2_ranges_read (ranges_offset, &lowpc, &highpc, reader->cu (),
+				  m_index_storage->get_addrmap (),
+				  scanning_per_cu, abbrev->tag);
+	    }
+	  break;
+	}
+    }
+
+  /* We don't want to examine declarations, but if we found a
+     declaration when handling DW_AT_specification or the like, then
+     that is ok.  Similarly, we allow an external variable without a
+     location; those are resolved via minimal symbols.  */
+  if (is_declaration && !for_specification
+      && !(abbrev->tag == DW_TAG_variable && (*flags & IS_STATIC) == 0))
+    {
+      /* We always want to recurse into some types, but we may not
+	 want to treat them as definitions.  */
+      if ((abbrev->tag == DW_TAG_class_type
+	   || abbrev->tag == DW_TAG_structure_type
+	   || abbrev->tag == DW_TAG_union_type
+	   || abbrev->tag == DW_TAG_namespace)
+	  && abbrev->has_children)
+	*flags |= IS_TYPE_DECLARATION;
+      else
+	{
+	  *linkage_name = nullptr;
+	  *name = nullptr;
+	}
+    }
+  else if ((*name == nullptr
+	    || (*linkage_name == nullptr
+		&& tag_can_have_linkage_name (abbrev->tag))
+	    || (*parent_entry == nullptr && m_language != language_c))
+	   && origin_offset != sect_offset (0))
+    {
+      cutu_reader *new_reader
+	= ensure_cu_exists (reader, reader->cu ()->per_objfile, origin_offset,
+			    origin_is_dwz, false);
+      if (new_reader == nullptr)
+	error (_(DWARF_ERROR_PREFIX
+		 "cannot follow reference to DIE at %s"
+		 " [in module %s]"),
+	       sect_offset_str (origin_offset),
+	       bfd_get_filename (reader->abfd ()));
+
+      const gdb_byte *new_info_ptr
+	= (new_reader->buffer () + to_underlying (origin_offset));
+
+      if (*parent_entry == nullptr)
+	{
+	  /* We only perform immediate lookups of parents for DIEs
+	     from earlier in this CU.  This avoids any problem
+	     with a NULL result when when we see a reference to a
+	     DIE in another CU that we may or may not have
+	     imported locally.  */
+	  parent_map::addr_type addr = parent_map::form_addr (new_info_ptr);
+	  if (new_reader->cu () != reader->cu ()
+	      || new_info_ptr > watermark_ptr)
+	    *maybe_defer = addr;
+	  else
+	    *parent_entry = m_die_range_map->find (addr);
+	}
+
+      unsigned int bytes_read;
+      const abbrev_info *new_abbrev
+	= new_reader->peek_die_abbrev (new_info_ptr, &bytes_read);
+
+      if (new_abbrev == nullptr)
+	error (_(DWARF_ERROR_PREFIX
+		 "Unexpected null DIE at offset %s [in module %s]"),
+	       sect_offset_str (origin_offset),
+	       bfd_get_filename (new_reader->abfd ()));
+
+      new_info_ptr += bytes_read;
+
+      if (new_reader->cu () == reader->cu () && new_info_ptr == watermark_ptr)
+	{
+	  /* Self-reference, we're done.  */
+	}
+      else
+	scan_attributes (scanning_per_cu, new_reader, new_info_ptr,
+			 new_info_ptr, new_abbrev, name, linkage_name,
+			 flags, nullptr, parent_entry, maybe_defer,
+			 is_enum_class, true);
+    }
+
+  if (!for_specification)
+    {
+      if (m_language == language_ada
+	  && *linkage_name == nullptr)
+	*linkage_name = *name;
+
+      if (!scanning_per_cu->addresses_seen && low_pc.has_value ()
+	  && (reader->cu ()->per_objfile->per_bfd->has_section_at_zero
+	      || *low_pc != (unrelocated_addr) 0)
+	  && high_pc.has_value ())
+	{
+	  if (high_pc_relative)
+	    high_pc = (unrelocated_addr) ((ULONGEST) *high_pc
+					  + (ULONGEST) *low_pc);
+
+	  if (*high_pc > *low_pc)
+	    {
+	      /* Need CORE_ADDR casts for addrmap.  */
+	      m_index_storage->get_addrmap ()->set_empty
+		((CORE_ADDR) *low_pc, (CORE_ADDR) *high_pc - 1,
+		 scanning_per_cu);
+	    }
+	}
+
+      if (abbrev->tag == DW_TAG_namespace && *name == nullptr)
+	*name = "(anonymous namespace)";
+
+      /* Keep in sync with new_symbol.  */
+      if (abbrev->tag == DW_TAG_subprogram
+	  && (m_language == language_ada
+	      || m_language == language_fortran))
+	*flags &= ~IS_STATIC;
+    }
+
+  return info_ptr;
+}
+
+/* See cooked-indexer.h.  */
+
+const gdb_byte *
+cooked_indexer::index_imported_unit (cutu_reader *reader,
+				     const gdb_byte *info_ptr,
+				     const abbrev_info *abbrev)
+{
+  sect_offset sect_off {};
+  bool is_dwz = false;
+
+  for (int i = 0; i < abbrev->num_attrs; ++i)
+    {
+      /* Note that we never need to reprocess attributes here.  */
+      attribute attr;
+      info_ptr = reader->read_attribute (&attr, &abbrev->attrs[i], info_ptr);
+
+      if (attr.name == DW_AT_import)
+	{
+	  sect_off = attr.get_ref_die_offset ();
+	  is_dwz = (attr.form == DW_FORM_GNU_ref_alt
+		    || reader->cu ()->per_cu->is_dwz);
+	}
+    }
+
+  /* Did not find DW_AT_import.  */
+  if (sect_off == sect_offset (0))
+    return info_ptr;
+
+  dwarf2_per_objfile *per_objfile = reader->cu ()->per_objfile;
+  cutu_reader *new_reader
+    = ensure_cu_exists (reader, per_objfile, sect_off, is_dwz, true);
+  if (new_reader != nullptr)
+    {
+      index_dies (new_reader, new_reader->info_ptr (), nullptr, false);
+
+      reader->cu ()->add_dependence (new_reader->cu ()->per_cu);
+    }
+
+  return info_ptr;
+}
+
+/* See cooked-indexer.h.  */
+
+const gdb_byte *
+cooked_indexer::recurse (cutu_reader *reader,
+			 const gdb_byte *info_ptr,
+			 std::variant<const cooked_index_entry *,
+				      parent_map::addr_type> parent,
+			 bool fully)
+{
+  info_ptr = index_dies (reader, info_ptr, parent, fully);
+
+  if (!std::holds_alternative<const cooked_index_entry *> (parent))
+    return info_ptr;
+  const cooked_index_entry *parent_entry
+    = std::get<const cooked_index_entry *> (parent);
+
+  if (parent_entry != nullptr)
+    {
+      /* Both start and end are inclusive, so use both "+ 1" and "- 1" to
+	 limit the range to the children of parent_entry.  */
+      parent_map::addr_type start
+	= parent_map::form_addr (reader->buffer ()
+				 + to_underlying (parent_entry->die_offset)
+				 + 1);
+      parent_map::addr_type end = parent_map::form_addr (info_ptr - 1);
+      m_die_range_map->add_entry (start, end, parent_entry);
+    }
+
+  return info_ptr;
+}
+
+/* See cooked-indexer.h.  */
+
+const gdb_byte *
+cooked_indexer::index_dies (cutu_reader *reader,
+			    const gdb_byte *info_ptr,
+			    std::variant<const cooked_index_entry *,
+					 parent_map::addr_type> parent,
+			    bool fully)
+{
+  const gdb_byte *end_ptr
+    = (reader->buffer () + to_underlying (reader->cu ()->header.sect_off)
+       + reader->cu ()->header.get_length_with_initial ());
+
+  while (info_ptr < end_ptr)
+    {
+      sect_offset this_die = (sect_offset) (info_ptr - reader->buffer ());
+      unsigned int bytes_read;
+      const abbrev_info *abbrev
+	= reader->peek_die_abbrev (info_ptr, &bytes_read);
+      info_ptr += bytes_read;
+      if (abbrev == nullptr)
+	break;
+
+      if (abbrev->tag == DW_TAG_imported_unit)
+	{
+	  info_ptr = index_imported_unit (reader, info_ptr, abbrev);
+	  continue;
+	}
+
+      parent_map::addr_type defer {};
+      if (std::holds_alternative<parent_map::addr_type> (parent))
+	defer = std::get<parent_map::addr_type> (parent);
+      const cooked_index_entry *parent_entry = nullptr;
+      if (std::holds_alternative<const cooked_index_entry *> (parent))
+	parent_entry = std::get<const cooked_index_entry *> (parent);
+
+      /* If a DIE parent is a DW_TAG_subprogram, then the DIE is only
+	 interesting if it's a DW_TAG_subprogram or a DW_TAG_entry_point.  */
+      bool die_interesting
+	= (abbrev->interesting
+	   && (parent_entry == nullptr
+	       || parent_entry->tag != DW_TAG_subprogram
+	       || abbrev->tag == DW_TAG_subprogram
+	       || abbrev->tag == DW_TAG_entry_point));
+
+      if (!die_interesting)
+	{
+	  info_ptr = reader->skip_one_die (info_ptr, abbrev, !fully);
+	  if (fully && abbrev->has_children)
+	    info_ptr = index_dies (reader, info_ptr, parent, fully);
+	  continue;
+	}
+
+      const char *name = nullptr;
+      const char *linkage_name = nullptr;
+      cooked_index_flag flags = IS_STATIC;
+      sect_offset sibling {};
+      const cooked_index_entry *this_parent_entry = parent_entry;
+      bool is_enum_class = false;
+
+      /* The scope of a DW_TAG_entry_point cooked_index_entry is the one of
+	 its surrounding subroutine.  */
+      if (abbrev->tag == DW_TAG_entry_point)
+	this_parent_entry = parent_entry->get_parent ();
+      info_ptr
+	= scan_attributes (reader->cu ()->per_cu, reader, info_ptr, info_ptr,
+			   abbrev, &name, &linkage_name, &flags, &sibling,
+			   &this_parent_entry, &defer, &is_enum_class, false);
+      /* A DW_TAG_entry_point inherits its static/extern property from
+	 the enclosing subroutine.  */
+      if (abbrev->tag == DW_TAG_entry_point)
+	{
+	  flags &= ~IS_STATIC;
+	  flags |= parent_entry->flags & IS_STATIC;
+	}
+
+      if (abbrev->tag == DW_TAG_namespace
+	  && m_language == language_cplus
+	  && strcmp (name, "::") == 0)
+	{
+	  /* GCC 4.0 and 4.1 had a bug (PR c++/28460) where they
+	     generated bogus DW_TAG_namespace DIEs with a name of "::"
+	     for the global namespace.  Work around this problem
+	     here.  */
+	  name = nullptr;
+	}
+
+      cooked_index_entry *this_entry = nullptr;
+      if (name != nullptr)
+	{
+	  if (defer != 0)
+	    this_entry
+	      = m_index_storage->add (this_die, abbrev->tag,
+				      flags | IS_PARENT_DEFERRED, name,
+				      defer, m_per_cu);
+	  else
+	    this_entry
+	      = m_index_storage->add (this_die, abbrev->tag, flags, name,
+				      this_parent_entry, m_per_cu);
+	}
+
+      if (linkage_name != nullptr)
+	{
+	  /* We only want this to be "main" if it has a linkage name
+	     but not an ordinary name.  */
+	  if (name != nullptr)
+	    flags = flags & ~IS_MAIN;
+	  /* Set the IS_LINKAGE on for everything except when functions
+	     have linkage name present but name is absent.  */
+	  if (name != nullptr
+	      || (abbrev->tag != DW_TAG_subprogram
+		  && abbrev->tag != DW_TAG_inlined_subroutine
+		  && abbrev->tag != DW_TAG_entry_point))
+	    flags = flags | IS_LINKAGE;
+	  m_index_storage->add (this_die, abbrev->tag, flags,
+				linkage_name, nullptr, m_per_cu);
+	}
+
+      if (abbrev->has_children)
+	{
+	  switch (abbrev->tag)
+	    {
+	    case DW_TAG_class_type:
+	    case DW_TAG_interface_type:
+	    case DW_TAG_structure_type:
+	    case DW_TAG_union_type:
+	      if (m_language != language_c && this_entry != nullptr)
+		{
+		  info_ptr = recurse (reader, info_ptr, this_entry, fully);
+		  continue;
+		}
+	      break;
+
+	    case DW_TAG_enumeration_type:
+	      /* Some versions of gdc could emit an "enum class"
+		 without a name, which is nonsensical.  These are
+		 skipped.  */
+	      if (is_enum_class && this_entry == nullptr)
+		continue;
+
+	      /* We need to recurse even for an anonymous enumeration.
+		 Which scope we record as the parent scope depends on
+		 whether we're reading an "enum class".  If so, we use
+		 the enum itself as the parent, yielding names like
+		 "enum_class::enumerator"; otherwise we inject the
+		 names into our own parent scope.  */
+	      {
+		std::variant<const cooked_index_entry *,
+			     parent_map::addr_type> recurse_parent;
+		if (is_enum_class)
+		  {
+		    gdb_assert (this_entry != nullptr);
+		    recurse_parent = this_entry;
+		  }
+		else if (defer != 0)
+		  recurse_parent = defer;
+		else
+		  recurse_parent = this_parent_entry;
+
+		info_ptr = recurse (reader, info_ptr, recurse_parent, fully);
+	      }
+	      continue;
+
+	    case DW_TAG_module:
+	      if (this_entry == nullptr)
+		break;
+	      [[fallthrough]];
+	    case DW_TAG_namespace:
+	      /* We don't check THIS_ENTRY for a namespace, to handle
+		 the ancient G++ workaround pointed out above.  */
+	      info_ptr = recurse (reader, info_ptr, this_entry, fully);
+	      continue;
+
+	    case DW_TAG_subprogram:
+	      if ((m_language == language_fortran
+		   || m_language == language_ada)
+		  && this_entry != nullptr)
+		{
+		  info_ptr = recurse (reader, info_ptr, this_entry, true);
+		  continue;
+		}
+	      break;
+	    }
+
+	  if (sibling != sect_offset (0))
+	    {
+	      const gdb_byte *sibling_ptr
+		= reader->buffer () + to_underlying (sibling);
+
+	      if (sibling_ptr < info_ptr)
+		complaint (_("DW_AT_sibling points backwards"));
+	      else if (sibling_ptr > reader->buffer_end ())
+		reader->section ()->overflow_complaint ();
+	      else
+		info_ptr = sibling_ptr;
+	    }
+	  else
+	    info_ptr = reader->skip_children (info_ptr);
+	}
+    }
+
+  return info_ptr;
+}
+
+/* See cooked-indexer.h.  */
+
+void
+cooked_indexer::make_index (cutu_reader *reader)
+{
+  check_bounds (reader);
+  find_file_and_directory (reader->comp_unit_die (), reader->cu ());
+
+  if (!reader->comp_unit_die ()->has_children)
+    return;
+
+  index_dies (reader, reader->info_ptr (), nullptr, false);
+}
diff --git a/gdb/dwarf2/cooked-indexer.h b/gdb/dwarf2/cooked-indexer.h
new file mode 100644
index 000000000000..a33f5d18c47b
--- /dev/null
+++ b/gdb/dwarf2/cooked-indexer.h
@@ -0,0 +1,120 @@ 
+/* DWARF indexer
+
+   Copyright (C) 2022-2025 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_COOKED_INDEXER_H
+#define GDB_DWARF2_COOKED_INDEXER_H
+
+#include "dwarf2/cooked-index.h"
+#include "dwarf2/parent-map.h"
+#include "dwarf2/types.h"
+#include <variant>
+
+struct abbrev_info;
+struct cooked_index_storage;
+struct cutu_reader;
+struct dwarf2_per_cu;
+struct dwarf2_per_objfile;
+
+/* An instance of this is created to index a CU.  */
+
+class cooked_indexer
+{
+public:
+  cooked_indexer (cooked_index_storage *storage, dwarf2_per_cu *per_cu,
+		  enum language language);
+
+  DISABLE_COPY_AND_ASSIGN (cooked_indexer);
+
+  /* Index the given CU.  */
+  void make_index (cutu_reader *reader);
+
+private:
+
+  /* A helper function to scan the PC bounds of READER and record them
+     in the storage's addrmap.  */
+  void check_bounds (cutu_reader *reader);
+
+  /* Ensure that the indicated CU exists.  The cutu_reader for it is
+     returned.  FOR_SCANNING is true if the caller intends to scan all
+     the DIEs in the CU; when false, this use is assumed to be to look
+     up just a single DIE.  */
+  cutu_reader *ensure_cu_exists (cutu_reader *reader,
+				 dwarf2_per_objfile *per_objfile,
+				 sect_offset sect_off,
+				 bool is_dwz,
+				 bool for_scanning);
+
+  /* Index DIEs in the READER starting at INFO_PTR.  PARENT is
+     the entry for the enclosing scope (nullptr at top level).  FULLY
+     is true when a full scan must be done -- in some languages,
+     function scopes must be fully explored in order to find nested
+     functions.  This returns a pointer to just after the spot where
+     reading stopped.  */
+  const gdb_byte *index_dies (cutu_reader *reader,
+			      const gdb_byte *info_ptr,
+			      std::variant<const cooked_index_entry *,
+					   parent_map::addr_type> parent,
+			      bool fully);
+
+  /* Scan the attributes for a given DIE and update the out
+     parameters.  Returns a pointer to the byte after the DIE.  */
+  const gdb_byte *scan_attributes (dwarf2_per_cu *scanning_per_cu,
+				   cutu_reader *reader,
+				   const gdb_byte *watermark_ptr,
+				   const gdb_byte *info_ptr,
+				   const abbrev_info *abbrev,
+				   const char **name,
+				   const char **linkage_name,
+				   cooked_index_flag *flags,
+				   sect_offset *sibling_offset,
+				   const cooked_index_entry **parent_entry,
+				   parent_map::addr_type *maybe_defer,
+				   bool *is_enum_class,
+				   bool for_specification);
+
+  /* Handle DW_TAG_imported_unit, by scanning the DIE to find
+     DW_AT_import, and then scanning the referenced CU.  Returns a
+     pointer to the byte after the DIE.  */
+  const gdb_byte *index_imported_unit (cutu_reader *reader,
+				       const gdb_byte *info_ptr,
+				       const abbrev_info *abbrev);
+
+  /* Recursively read DIEs, recording the section offsets in
+     m_die_range_map and then calling index_dies.  */
+  const gdb_byte *recurse (cutu_reader *reader,
+			   const gdb_byte *info_ptr,
+			   std::variant<const cooked_index_entry *,
+					parent_map::addr_type> parent_entry,
+			   bool fully);
+
+  /* The storage object, where the results are kept.  */
+  cooked_index_storage *m_index_storage;
+  /* The CU that we are reading on behalf of.  This object might be
+     asked to index one CU but to treat the results as if they come
+     from some including CU; in this case the including CU would be
+     recorded here.  */
+  dwarf2_per_cu *m_per_cu;
+  /* The language that we're assuming when reading.  */
+  enum language m_language;
+
+  /* Map from DIE ranges to newly-created entries.  */
+  parent_map *m_die_range_map;
+};
+
+#endif /* GDB_DWARF2_COOKED_INDEXER_H */
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 765940d0b0c1..ecef4b8a02e8 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -33,6 +33,7 @@ 
 #include "dwarf2/attribute.h"
 #include "dwarf2/comp-unit-head.h"
 #include "dwarf2/cooked-index-storage.h"
+#include "dwarf2/cooked-indexer.h"
 #include "dwarf2/cu.h"
 #include "dwarf2/index-cache.h"
 #include "dwarf2/leb.h"
@@ -92,7 +93,6 @@ 
 #include "run-on-main-thread.h"
 #include "dwarf2/parent-map.h"
 #include "dwarf2/error.h"
-#include <variant>
 #include "gdbsupport/unordered_set.h"
 #include "extract-store-integer.h"
 
@@ -750,9 +750,6 @@  show_dwarf_synchronous (struct ui_file *file, int from_tty,
 
 /* local function prototypes */
 
-static void dwarf2_find_base_address (struct die_info *die,
-				      struct dwarf2_cu *cu);
-
 static void build_type_psymtabs_reader (cutu_reader *reader,
 					cooked_index_storage *storage);
 
@@ -869,31 +866,6 @@  static struct dwarf2_section_info *cu_debug_loc_section (struct dwarf2_cu *cu);
 static struct dwarf2_section_info *cu_debug_rnglists_section
   (struct dwarf2_cu *cu, dwarf_tag tag);
 
-/* How dwarf2_get_pc_bounds constructed its *LOWPC and *HIGHPC return
-   values.  Keep the items ordered with increasing constraints compliance.  */
-enum pc_bounds_kind
-{
-  /* No attribute DW_AT_low_pc, DW_AT_high_pc or DW_AT_ranges was found.  */
-  PC_BOUNDS_NOT_PRESENT,
-
-  /* Some of the attributes DW_AT_low_pc, DW_AT_high_pc or DW_AT_ranges
-     were present but they do not form a valid range of PC addresses.  */
-  PC_BOUNDS_INVALID,
-
-  /* Discontiguous range was found - that is DW_AT_ranges was found.  */
-  PC_BOUNDS_RANGES,
-
-  /* Contiguous range was found - DW_AT_low_pc and DW_AT_high_pc were found.  */
-  PC_BOUNDS_HIGH_LOW,
-};
-
-static enum pc_bounds_kind dwarf2_get_pc_bounds (struct die_info *,
-						 unrelocated_addr *,
-						 unrelocated_addr *,
-						 struct dwarf2_cu *,
-						 addrmap_mutable *,
-						 void *);
-
 static void get_scope_pc_bounds (struct die_info *,
 				 unrelocated_addr *, unrelocated_addr *,
 				 struct dwarf2_cu *);
@@ -937,9 +909,6 @@  static const char *namespace_name (struct die_info *die,
 
 static void process_enumeration_scope (struct die_info *, struct dwarf2_cu *);
 
-static bool decode_locdesc (struct dwarf_block *, struct dwarf2_cu *,
-			    CORE_ADDR *addr);
-
 static enum dwarf_array_dim_ordering read_array_order (struct die_info *,
 						       struct dwarf2_cu *);
 
@@ -1004,10 +973,6 @@  static void dwarf2_symbol_mark_computed (const struct attribute *attr,
 					 struct dwarf2_cu *cu,
 					 int is_block);
 
-static dwarf2_per_cu *dwarf2_find_containing_comp_unit
-  (sect_offset sect_off, unsigned int offset_in_dwz,
-   dwarf2_per_bfd *per_bfd);
-
 static struct type *set_die_type (struct die_info *, struct type *,
 				  struct dwarf2_cu *, bool = false);
 
@@ -1091,9 +1056,6 @@  dwarf2_per_cu_deleter::operator() (dwarf2_per_cu *data)
     delete data;
 }
 
-static file_and_directory &find_file_and_directory
-     (struct die_info *die, struct dwarf2_cu *cu);
-
 static const char *compute_include_file_name
      (const struct line_header *lh,
       const file_entry &fe,
@@ -2426,15 +2388,9 @@  dwarf2_initialize_objfile (struct objfile *objfile,
   return true;
 }
 
-
-
-/* Find the base address of the compilation unit for range lists and
-   location lists.  It will normally be specified by DW_AT_low_pc.
-   In DWARF-3 draft 4, the base address could be overridden by
-   DW_AT_entry_pc.  It's been removed, but GCC still uses this for
-   compilation units with discontinuous ranges.  */
+/* See read.h.  */
 
-static void
+void
 dwarf2_find_base_address (struct die_info *die, struct dwarf2_cu *cu)
 {
   struct attribute *attr;
@@ -3503,98 +3459,6 @@  get_type_unit_group (struct dwarf2_cu *cu, const struct attribute *stmt_list)
   return tu_group;
 }
 
-/* An instance of this is created to index a CU.  */
-
-class cooked_indexer
-{
-public:
-  cooked_indexer (cooked_index_storage *storage, dwarf2_per_cu *per_cu,
-		  enum language language)
-    : m_index_storage (storage),
-      m_per_cu (per_cu),
-      m_language (language),
-      m_die_range_map (storage->get_parent_map ())
-  {
-  }
-
-  DISABLE_COPY_AND_ASSIGN (cooked_indexer);
-
-  /* Index the given CU.  */
-  void make_index (cutu_reader *reader);
-
-private:
-
-  /* A helper function to scan the PC bounds of READER and record them
-     in the storage's addrmap.  */
-  void check_bounds (cutu_reader *reader);
-
-  /* Ensure that the indicated CU exists.  The cutu_reader for it is
-     returned.  FOR_SCANNING is true if the caller intends to scan all
-     the DIEs in the CU; when false, this use is assumed to be to look
-     up just a single DIE.  */
-  cutu_reader *ensure_cu_exists (cutu_reader *reader,
-				 dwarf2_per_objfile *per_objfile,
-				 sect_offset sect_off,
-				 bool is_dwz,
-				 bool for_scanning);
-
-  /* Index DIEs in the READER starting at INFO_PTR.  PARENT is
-     the entry for the enclosing scope (nullptr at top level).  FULLY
-     is true when a full scan must be done -- in some languages,
-     function scopes must be fully explored in order to find nested
-     functions.  This returns a pointer to just after the spot where
-     reading stopped.  */
-  const gdb_byte *index_dies (cutu_reader *reader,
-			      const gdb_byte *info_ptr,
-			      std::variant<const cooked_index_entry *,
-					   parent_map::addr_type> parent,
-			      bool fully);
-
-  /* Scan the attributes for a given DIE and update the out
-     parameters.  Returns a pointer to the byte after the DIE.  */
-  const gdb_byte *scan_attributes (dwarf2_per_cu *scanning_per_cu,
-				   cutu_reader *reader,
-				   const gdb_byte *watermark_ptr,
-				   const gdb_byte *info_ptr,
-				   const abbrev_info *abbrev,
-				   const char **name,
-				   const char **linkage_name,
-				   cooked_index_flag *flags,
-				   sect_offset *sibling_offset,
-				   const cooked_index_entry **parent_entry,
-				   parent_map::addr_type *maybe_defer,
-				   bool *is_enum_class,
-				   bool for_specification);
-
-  /* Handle DW_TAG_imported_unit, by scanning the DIE to find
-     DW_AT_import, and then scanning the referenced CU.  Returns a
-     pointer to the byte after the DIE.  */
-  const gdb_byte *index_imported_unit (cutu_reader *reader,
-				       const gdb_byte *info_ptr,
-				       const abbrev_info *abbrev);
-
-  /* Recursively read DIEs, recording the section offsets in
-     m_die_range_map and then calling index_dies.  */
-  const gdb_byte *recurse (cutu_reader *reader,
-			   const gdb_byte *info_ptr,
-			   std::variant<const cooked_index_entry *,
-					parent_map::addr_type> parent_entry,
-			   bool fully);
-
-  /* The storage object, where the results are kept.  */
-  cooked_index_storage *m_index_storage;
-  /* The CU that we are reading on behalf of.  This object might be
-     asked to index one CU but to treat the results as if they come
-     from some including CU; in this case the including CU would be
-     recorded here.  */
-  dwarf2_per_cu *m_per_cu;
-  /* The language that we're assuming when reading.  */
-  enum language m_language;
-
-  /* Map from DIE ranges to newly-created entries.  */
-  parent_map *m_die_range_map;
-};
-
 /* Subroutine of dwarf2_build_psymtabs_hard to simplify it.
    Process compilation unit THIS_CU for a psymtab.  */
 
@@ -6264,7 +6128,7 @@  read_import_statement (struct die_info *die, struct dwarf2_cu *cu)
 		       &objfile->objfile_obstack);
 }
 
-static file_and_directory &
+file_and_directory &
 find_file_and_directory (struct die_info *die, struct dwarf2_cu *cu)
 {
   if (cu->per_cu->fnd != nullptr)
@@ -9724,12 +9588,9 @@  dwarf2_ranges_process (unsigned offset, struct dwarf2_cu *cu, dwarf_tag tag,
   return 1;
 }
 
-/* Get low and high pc attributes from DW_AT_ranges attribute value OFFSET.
-   Return 1 if the attributes are present and valid, otherwise, return 0.
-   TAG is passed to dwarf2_ranges_process.  If MAP is not NULL, then
-   ranges in MAP are set, using DATUM as the value.  */
+/* See read.h.  */
 
-static int
+int
 dwarf2_ranges_read (unsigned offset, unrelocated_addr *low_return,
 		    unrelocated_addr *high_return, struct dwarf2_cu *cu,
 		    addrmap_mutable *map, void *datum, dwarf_tag tag)
@@ -9900,11 +9761,9 @@  dwarf_get_pc_bounds_ranges_or_highlow_pc (die_info *die, unrelocated_addr *low,
     return ret;
 }
 
-/* Get low and high pc attributes from a die.  See enum pc_bounds_kind
-   definition for the return value.  *LOWPC and *HIGHPC are set iff
-   neither PC_BOUNDS_NOT_PRESENT nor PC_BOUNDS_INVALID are returned.  */
+/* See read.h.  */
 
-static enum pc_bounds_kind
+pc_bounds_kind
 dwarf2_get_pc_bounds (struct die_info *die, unrelocated_addr *lowpc,
 		      unrelocated_addr *highpc, struct dwarf2_cu *cu,
 		      addrmap_mutable *map, void *datum)
@@ -14828,649 +14687,6 @@  cutu_reader::read_toplevel_die (die_info **diep, const gdb_byte *info_ptr,
 
   return result;
 }
-
-
-void
-cooked_indexer::check_bounds (cutu_reader *reader)
-{
-  dwarf2_cu *cu = reader->cu ();
-
-  if (cu->per_cu->addresses_seen)
-    return;
-
-  unrelocated_addr best_lowpc = {}, best_highpc = {};
-  /* Possibly set the default values of LOWPC and HIGHPC from
-     `DW_AT_ranges'.  */
-  dwarf2_find_base_address (reader->comp_unit_die (), cu);
-  enum pc_bounds_kind cu_bounds_kind
-    = dwarf2_get_pc_bounds (reader->comp_unit_die (), &best_lowpc, &best_highpc,
-			    cu, m_index_storage->get_addrmap (), cu->per_cu);
-  if (cu_bounds_kind == PC_BOUNDS_HIGH_LOW && best_lowpc < best_highpc)
-    {
-      /* Store the contiguous range if it is not empty; it can be
-	 empty for CUs with no code.  addrmap requires CORE_ADDR, so
-	 we cast here.  */
-      m_index_storage->get_addrmap ()->set_empty ((CORE_ADDR) best_lowpc,
-						  (CORE_ADDR) best_highpc - 1,
-						  cu->per_cu);
-
-      cu->per_cu->addresses_seen = true;
-    }
-}
-
-/* Helper function that returns true if TAG can have a linkage
-   name.  */
-
-static bool
-tag_can_have_linkage_name (enum dwarf_tag tag)
-{
-  switch (tag)
-    {
-    case DW_TAG_variable:
-    case DW_TAG_subprogram:
-      return true;
-
-    default:
-      return false;
-    }
-}
-
-cutu_reader *
-cooked_indexer::ensure_cu_exists (cutu_reader *reader,
-				  dwarf2_per_objfile *per_objfile,
-				  sect_offset sect_off, bool is_dwz,
-				  bool for_scanning)
-{
-  /* Lookups for type unit references are always in the CU, and
-     cross-CU references will crash.  */
-  if (reader->cu ()->per_cu->is_dwz == is_dwz
-      && reader->cu ()->header.offset_in_cu_p (sect_off))
-    return reader;
-
-  dwarf2_per_cu *per_cu
-    = dwarf2_find_containing_comp_unit (sect_off, is_dwz,
-					per_objfile->per_bfd);
-
-  /* When scanning, we only want to visit a given CU a single time.
-     Doing this check here avoids self-imports as well.  */
-  if (for_scanning)
-    {
-      bool nope = false;
-      if (!per_cu->scanned.compare_exchange_strong (nope, true))
-	return nullptr;
-    }
-
-  cutu_reader *result = m_index_storage->get_reader (per_cu);
-  if (result == nullptr)
-    {
-      cutu_reader new_reader (per_cu, per_objfile, nullptr, nullptr, false,
-			      language_minimal,
-			      &m_index_storage->get_abbrev_table_cache ());
-
-      if (new_reader.is_dummy () || new_reader.comp_unit_die () == nullptr
-	  || !new_reader.comp_unit_die ()->has_children)
-	return nullptr;
-
-      auto copy = std::make_unique<cutu_reader> (std::move (new_reader));
-      result = m_index_storage->preserve (std::move (copy));
-    }
-
-  if (result->is_dummy () || result->comp_unit_die () == nullptr
-      || !result->comp_unit_die ()->has_children)
-    return nullptr;
-
-  if (for_scanning)
-    check_bounds (result);
-
-  return result;
-}
-
-const gdb_byte *
-cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu,
-				 cutu_reader *reader,
-				 const gdb_byte *watermark_ptr,
-				 const gdb_byte *info_ptr,
-				 const abbrev_info *abbrev,
-				 const char **name,
-				 const char **linkage_name,
-				 cooked_index_flag *flags,
-				 sect_offset *sibling_offset,
-				 const cooked_index_entry **parent_entry,
-				 parent_map::addr_type *maybe_defer,
-				 bool *is_enum_class,
-				 bool for_specification)
-{
-  bool origin_is_dwz = false;
-  bool is_declaration = false;
-  sect_offset origin_offset {};
-
-  std::optional<unrelocated_addr> low_pc;
-  std::optional<unrelocated_addr> high_pc;
-  bool high_pc_relative = false;
-
-  for (int i = 0; i < abbrev->num_attrs; ++i)
-    {
-      attribute attr;
-      info_ptr = reader->read_attribute (&attr, &abbrev->attrs[i], info_ptr);
-
-      /* Store the data if it is of an attribute we want to keep in a
-	 partial symbol table.  */
-      switch (attr.name)
-	{
-	case DW_AT_name:
-	  switch (abbrev->tag)
-	    {
-	    case DW_TAG_compile_unit:
-	    case DW_TAG_partial_unit:
-	    case DW_TAG_type_unit:
-	      /* Compilation units have a DW_AT_name that is a filename, not
-		 a source language identifier.  */
-	      break;
-
-	    default:
-	      if (*name == nullptr)
-		*name = attr.as_string ();
-	      break;
-	    }
-	  break;
-
-	case DW_AT_linkage_name:
-	case DW_AT_MIPS_linkage_name:
-	  /* Note that both forms of linkage name might appear.  We
-	     assume they will be the same, and we only store the last
-	     one we see.  */
-	  if (*linkage_name == nullptr)
-	    *linkage_name = attr.as_string ();
-	  break;
-
-	/* DWARF 4 has defined a dedicated DW_AT_main_subprogram
-	   attribute to indicate the starting function of the program...  */
-	case DW_AT_main_subprogram:
-	  if (attr.as_boolean ())
-	    *flags |= IS_MAIN;
-	  break;
-
-	/* ... however with older versions the DW_CC_program value of
-	   the DW_AT_calling_convention attribute was used instead as
-	   the only means available.  We handle both variants then.  */
-	case DW_AT_calling_convention:
-	  if (attr.constant_value (DW_CC_normal) == DW_CC_program)
-	    *flags |= IS_MAIN;
-	  break;
-
-	case DW_AT_declaration:
-	  is_declaration = attr.as_boolean ();
-	  break;
-
-	case DW_AT_sibling:
-	  if (sibling_offset != nullptr)
-	    *sibling_offset = attr.get_ref_die_offset ();
-	  break;
-
-	case DW_AT_specification:
-	case DW_AT_abstract_origin:
-	case DW_AT_extension:
-	  origin_offset = attr.get_ref_die_offset ();
-	  origin_is_dwz = attr.form == DW_FORM_GNU_ref_alt;
-	  break;
-
-	case DW_AT_external:
-	  if (attr.as_boolean ())
-	    *flags &= ~IS_STATIC;
-	  break;
-
-	case DW_AT_enum_class:
-	  if (attr.as_boolean ())
-	    *is_enum_class = true;
-	  break;
-
-	case DW_AT_low_pc:
-	  low_pc = attr.as_address ();
-	  break;
-
-	case DW_AT_high_pc:
-	  high_pc = attr.as_address ();
-	  if (reader->cu ()->header.version >= 4 && attr.form_is_constant ())
-	    high_pc_relative = true;
-	  break;
-
-	case DW_AT_location:
-	  if (!scanning_per_cu->addresses_seen && attr.form_is_block ())
-	    {
-	      struct dwarf_block *locdesc = attr.as_block ();
-	      CORE_ADDR addr;
-	      dwarf2_cu *cu = reader->cu ();
-
-	      if (decode_locdesc (locdesc, cu, &addr)
-		  && (addr != 0
-		      || cu->per_objfile->per_bfd->has_section_at_zero))
-		{
-		  low_pc = (unrelocated_addr) addr;
-		  /* For variables, we don't want to try decoding the
-		     type just to find the size -- for gdb's purposes
-		     we only need the address of a variable.  */
-		  high_pc = (unrelocated_addr) (addr + 1);
-		  high_pc_relative = false;
-		}
-	    }
-	  break;
-
-	case DW_AT_ranges:
-	  if (!scanning_per_cu->addresses_seen)
-	    {
-	      /* Offset in the .debug_ranges or .debug_rnglist section
-		 (depending on DWARF version).  */
-	      ULONGEST ranges_offset = attr.as_unsigned ();
-
-	      /* See dwarf2_cu::gnu_ranges_base's doc for why we might
-		 want to add this value.  */
-	      ranges_offset += reader->cu ()->gnu_ranges_base;
-
-	      unrelocated_addr lowpc, highpc;
-	      dwarf2_ranges_read (ranges_offset, &lowpc, &highpc, reader->cu (),
-				  m_index_storage->get_addrmap (),
-				  scanning_per_cu, abbrev->tag);
-	    }
-	  break;
-	}
-    }
-
-  /* We don't want to examine declarations, but if we found a
-     declaration when handling DW_AT_specification or the like, then
-     that is ok.  Similarly, we allow an external variable without a
-     location; those are resolved via minimal symbols.  */
-  if (is_declaration && !for_specification
-      && !(abbrev->tag == DW_TAG_variable && (*flags & IS_STATIC) == 0))
-    {
-      /* We always want to recurse into some types, but we may not
-	 want to treat them as definitions.  */
-      if ((abbrev->tag == DW_TAG_class_type
-	   || abbrev->tag == DW_TAG_structure_type
-	   || abbrev->tag == DW_TAG_union_type
-	   || abbrev->tag == DW_TAG_namespace)
-	  && abbrev->has_children)
-	*flags |= IS_TYPE_DECLARATION;
-      else
-	{
-	  *linkage_name = nullptr;
-	  *name = nullptr;
-	}
-    }
-  else if ((*name == nullptr
-	    || (*linkage_name == nullptr
-		&& tag_can_have_linkage_name (abbrev->tag))
-	    || (*parent_entry == nullptr && m_language != language_c))
-	   && origin_offset != sect_offset (0))
-    {
-      cutu_reader *new_reader
-	= ensure_cu_exists (reader, reader->cu ()->per_objfile, origin_offset,
-			    origin_is_dwz, false);
-      if (new_reader == nullptr)
-	error (_(DWARF_ERROR_PREFIX
-		 "cannot follow reference to DIE at %s"
-		 " [in module %s]"),
-	       sect_offset_str (origin_offset),
-	       bfd_get_filename (reader->abfd ()));
-
-      const gdb_byte *new_info_ptr
-	= (new_reader->buffer () + to_underlying (origin_offset));
-
-      if (*parent_entry == nullptr)
-	{
-	  /* We only perform immediate lookups of parents for DIEs
-	     from earlier in this CU.  This avoids any problem
-	     with a NULL result when when we see a reference to a
-	     DIE in another CU that we may or may not have
-	     imported locally.  */
-	  parent_map::addr_type addr = parent_map::form_addr (new_info_ptr);
-	  if (new_reader->cu () != reader->cu ()
-	      || new_info_ptr > watermark_ptr)
-	    *maybe_defer = addr;
-	  else
-	    *parent_entry = m_die_range_map->find (addr);
-	}
-
-      unsigned int bytes_read;
-      const abbrev_info *new_abbrev
-	= new_reader->peek_die_abbrev (new_info_ptr, &bytes_read);
-
-      if (new_abbrev == nullptr)
-	error (_(DWARF_ERROR_PREFIX
-		 "Unexpected null DIE at offset %s [in module %s]"),
-	       sect_offset_str (origin_offset),
-	       bfd_get_filename (new_reader->abfd ()));
-
-      new_info_ptr += bytes_read;
-
-      if (new_reader->cu () == reader->cu () && new_info_ptr == watermark_ptr)
-	{
-	  /* Self-reference, we're done.  */
-	}
-      else
-	scan_attributes (scanning_per_cu, new_reader, new_info_ptr,
-			 new_info_ptr, new_abbrev, name, linkage_name,
-			 flags, nullptr, parent_entry, maybe_defer,
-			 is_enum_class, true);
-    }
-
-  if (!for_specification)
-    {
-      if (m_language == language_ada
-	  && *linkage_name == nullptr)
-	*linkage_name = *name;
-
-      if (!scanning_per_cu->addresses_seen && low_pc.has_value ()
-	  && (reader->cu ()->per_objfile->per_bfd->has_section_at_zero
-	      || *low_pc != (unrelocated_addr) 0)
-	  && high_pc.has_value ())
-	{
-	  if (high_pc_relative)
-	    high_pc = (unrelocated_addr) ((ULONGEST) *high_pc
-					  + (ULONGEST) *low_pc);
-
-	  if (*high_pc > *low_pc)
-	    {
-	      /* Need CORE_ADDR casts for addrmap.  */
-	      m_index_storage->get_addrmap ()->set_empty
-		((CORE_ADDR) *low_pc, (CORE_ADDR) *high_pc - 1,
-		 scanning_per_cu);
-	    }
-	}
-
-      if (abbrev->tag == DW_TAG_namespace && *name == nullptr)
-	*name = "(anonymous namespace)";
-
-      /* Keep in sync with new_symbol.  */
-      if (abbrev->tag == DW_TAG_subprogram
-	  && (m_language == language_ada
-	      || m_language == language_fortran))
-	*flags &= ~IS_STATIC;
-    }
-
-  return info_ptr;
-}
-
-const gdb_byte *
-cooked_indexer::index_imported_unit (cutu_reader *reader,
-				     const gdb_byte *info_ptr,
-				     const abbrev_info *abbrev)
-{
-  sect_offset sect_off {};
-  bool is_dwz = false;
-
-  for (int i = 0; i < abbrev->num_attrs; ++i)
-    {
-      /* Note that we never need to reprocess attributes here.  */
-      attribute attr;
-      info_ptr = reader->read_attribute (&attr, &abbrev->attrs[i], info_ptr);
-
-      if (attr.name == DW_AT_import)
-	{
-	  sect_off = attr.get_ref_die_offset ();
-	  is_dwz = (attr.form == DW_FORM_GNU_ref_alt
-		    || reader->cu ()->per_cu->is_dwz);
-	}
-    }
-
-  /* Did not find DW_AT_import.  */
-  if (sect_off == sect_offset (0))
-    return info_ptr;
-
-  dwarf2_per_objfile *per_objfile = reader->cu ()->per_objfile;
-  cutu_reader *new_reader
-    = ensure_cu_exists (reader, per_objfile, sect_off, is_dwz, true);
-  if (new_reader != nullptr)
-    {
-      index_dies (new_reader, new_reader->info_ptr (), nullptr, false);
-
-      reader->cu ()->add_dependence (new_reader->cu ()->per_cu);
-    }
-
-  return info_ptr;
-}
-
-const gdb_byte *
-cooked_indexer::recurse (cutu_reader *reader,
-			 const gdb_byte *info_ptr,
-			 std::variant<const cooked_index_entry *,
-				      parent_map::addr_type> parent,
-			 bool fully)
-{
-  info_ptr = index_dies (reader, info_ptr, parent, fully);
-
-  if (!std::holds_alternative<const cooked_index_entry *> (parent))
-    return info_ptr;
-  const cooked_index_entry *parent_entry
-    = std::get<const cooked_index_entry *> (parent);
-
-  if (parent_entry != nullptr)
-    {
-      /* Both start and end are inclusive, so use both "+ 1" and "- 1" to
-	 limit the range to the children of parent_entry.  */
-      parent_map::addr_type start
-	= parent_map::form_addr (reader->buffer ()
-				 + to_underlying (parent_entry->die_offset)
-				 + 1);
-      parent_map::addr_type end = parent_map::form_addr (info_ptr - 1);
-      m_die_range_map->add_entry (start, end, parent_entry);
-    }
-
-  return info_ptr;
-}
-
-const gdb_byte *
-cooked_indexer::index_dies (cutu_reader *reader,
-			    const gdb_byte *info_ptr,
-			    std::variant<const cooked_index_entry *,
-					 parent_map::addr_type> parent,
-			    bool fully)
-{
-  const gdb_byte *end_ptr
-    = (reader->buffer () + to_underlying (reader->cu ()->header.sect_off)
-       + reader->cu ()->header.get_length_with_initial ());
-
-  while (info_ptr < end_ptr)
-    {
-      sect_offset this_die = (sect_offset) (info_ptr - reader->buffer ());
-      unsigned int bytes_read;
-      const abbrev_info *abbrev
-	= reader->peek_die_abbrev (info_ptr, &bytes_read);
-      info_ptr += bytes_read;
-      if (abbrev == nullptr)
-	break;
-
-      if (abbrev->tag == DW_TAG_imported_unit)
-	{
-	  info_ptr = index_imported_unit (reader, info_ptr, abbrev);
-	  continue;
-	}
-
-      parent_map::addr_type defer {};
-      if (std::holds_alternative<parent_map::addr_type> (parent))
-	defer = std::get<parent_map::addr_type> (parent);
-      const cooked_index_entry *parent_entry = nullptr;
-      if (std::holds_alternative<const cooked_index_entry *> (parent))
-	parent_entry = std::get<const cooked_index_entry *> (parent);
-
-      /* If a DIE parent is a DW_TAG_subprogram, then the DIE is only
-	 interesting if it's a DW_TAG_subprogram or a DW_TAG_entry_point.  */
-      bool die_interesting
-	= (abbrev->interesting
-	   && (parent_entry == nullptr
-	       || parent_entry->tag != DW_TAG_subprogram
-	       || abbrev->tag == DW_TAG_subprogram
-	       || abbrev->tag == DW_TAG_entry_point));
-
-      if (!die_interesting)
-	{
-	  info_ptr = reader->skip_one_die (info_ptr, abbrev, !fully);
-	  if (fully && abbrev->has_children)
-	    info_ptr = index_dies (reader, info_ptr, parent, fully);
-	  continue;
-	}
-
-      const char *name = nullptr;
-      const char *linkage_name = nullptr;
-      cooked_index_flag flags = IS_STATIC;
-      sect_offset sibling {};
-      const cooked_index_entry *this_parent_entry = parent_entry;
-      bool is_enum_class = false;
-
-      /* The scope of a DW_TAG_entry_point cooked_index_entry is the one of
-	 its surrounding subroutine.  */
-      if (abbrev->tag == DW_TAG_entry_point)
-	this_parent_entry = parent_entry->get_parent ();
-      info_ptr
-	= scan_attributes (reader->cu ()->per_cu, reader, info_ptr, info_ptr,
-			   abbrev, &name, &linkage_name, &flags, &sibling,
-			   &this_parent_entry, &defer, &is_enum_class, false);
-      /* A DW_TAG_entry_point inherits its static/extern property from
-	 the enclosing subroutine.  */
-      if (abbrev->tag == DW_TAG_entry_point)
-	{
-	  flags &= ~IS_STATIC;
-	  flags |= parent_entry->flags & IS_STATIC;
-	}
-
-      if (abbrev->tag == DW_TAG_namespace
-	  && m_language == language_cplus
-	  && strcmp (name, "::") == 0)
-	{
-	  /* GCC 4.0 and 4.1 had a bug (PR c++/28460) where they
-	     generated bogus DW_TAG_namespace DIEs with a name of "::"
-	     for the global namespace.  Work around this problem
-	     here.  */
-	  name = nullptr;
-	}
-
-      cooked_index_entry *this_entry = nullptr;
-      if (name != nullptr)
-	{
-	  if (defer != 0)
-	    this_entry
-	      = m_index_storage->add (this_die, abbrev->tag,
-				      flags | IS_PARENT_DEFERRED, name,
-				      defer, m_per_cu);
-	  else
-	    this_entry
-	      = m_index_storage->add (this_die, abbrev->tag, flags, name,
-				      this_parent_entry, m_per_cu);
-	}
-
-      if (linkage_name != nullptr)
-	{
-	  /* We only want this to be "main" if it has a linkage name
-	     but not an ordinary name.  */
-	  if (name != nullptr)
-	    flags = flags & ~IS_MAIN;
-	  /* Set the IS_LINKAGE on for everything except when functions
-	     have linkage name present but name is absent.  */
-	  if (name != nullptr
-	      || (abbrev->tag != DW_TAG_subprogram
-		  && abbrev->tag != DW_TAG_inlined_subroutine
-		  && abbrev->tag != DW_TAG_entry_point))
-	    flags = flags | IS_LINKAGE;
-	  m_index_storage->add (this_die, abbrev->tag, flags,
-				linkage_name, nullptr, m_per_cu);
-	}
-
-      if (abbrev->has_children)
-	{
-	  switch (abbrev->tag)
-	    {
-	    case DW_TAG_class_type:
-	    case DW_TAG_interface_type:
-	    case DW_TAG_structure_type:
-	    case DW_TAG_union_type:
-	      if (m_language != language_c && this_entry != nullptr)
-		{
-		  info_ptr = recurse (reader, info_ptr, this_entry, fully);
-		  continue;
-		}
-	      break;
-
-	    case DW_TAG_enumeration_type:
-	      /* Some versions of gdc could emit an "enum class"
-		 without a name, which is nonsensical.  These are
-		 skipped.  */
-	      if (is_enum_class && this_entry == nullptr)
-		continue;
-
-	      /* We need to recurse even for an anonymous enumeration.
-		 Which scope we record as the parent scope depends on
-		 whether we're reading an "enum class".  If so, we use
-		 the enum itself as the parent, yielding names like
-		 "enum_class::enumerator"; otherwise we inject the
-		 names into our own parent scope.  */
-	      {
-		std::variant<const cooked_index_entry *,
-			     parent_map::addr_type> recurse_parent;
-		if (is_enum_class)
-		  {
-		    gdb_assert (this_entry != nullptr);
-		    recurse_parent = this_entry;
-		  }
-		else if (defer != 0)
-		  recurse_parent = defer;
-		else
-		  recurse_parent = this_parent_entry;
-
-		info_ptr = recurse (reader, info_ptr, recurse_parent, fully);
-	      }
-	      continue;
-
-	    case DW_TAG_module:
-	      if (this_entry == nullptr)
-		break;
-	      [[fallthrough]];
-	    case DW_TAG_namespace:
-	      /* We don't check THIS_ENTRY for a namespace, to handle
-		 the ancient G++ workaround pointed out above.  */
-	      info_ptr = recurse (reader, info_ptr, this_entry, fully);
-	      continue;
-
-	    case DW_TAG_subprogram:
-	      if ((m_language == language_fortran
-		   || m_language == language_ada)
-		  && this_entry != nullptr)
-		{
-		  info_ptr = recurse (reader, info_ptr, this_entry, true);
-		  continue;
-		}
-	      break;
-	    }
-
-	  if (sibling != sect_offset (0))
-	    {
-	      const gdb_byte *sibling_ptr
-		= reader->buffer () + to_underlying (sibling);
-
-	      if (sibling_ptr < info_ptr)
-		complaint (_("DW_AT_sibling points backwards"));
-	      else if (sibling_ptr > reader->buffer_end ())
-		reader->section ()->overflow_complaint ();
-	      else
-		info_ptr = sibling_ptr;
-	    }
-	  else
-	    info_ptr = reader->skip_children (info_ptr);
-	}
-    }
-
-  return info_ptr;
-}
-
-void
-cooked_indexer::make_index (cutu_reader *reader)
-{
-  check_bounds (reader);
-  find_file_and_directory (reader->comp_unit_die (), reader->cu ());
-
-  if (!reader->comp_unit_die ()->has_children)
-    return;
-
-  index_dies (reader, reader->info_ptr (), nullptr, false);
-}
 
 struct compunit_symtab *
 cooked_index_functions::find_compunit_symtab_by_address
@@ -19888,33 +19104,9 @@  read_signatured_type (signatured_type *sig_type,
   sig_type->tu_read = 1;
 }
 
-/* Decode simple location descriptions.
-
-   Given a pointer to a DWARF block that defines a location, compute
-   the location.  Returns true if the expression was computable by
-   this function, false otherwise.  On a true return, *RESULT is set.
-
-   Note that this function does not implement a full DWARF expression
-   evaluator.  Instead, it is used for a few limited purposes:
-
-   - Getting the address of a symbol that has a constant address.  For
-   example, if a symbol has a location like "DW_OP_addr", the address
-   can be extracted.
-
-   - Getting the offset of a virtual function in its vtable.  There
-   are two forms of this, one of which involves DW_OP_deref -- so this
-   function handles derefs in a peculiar way to make this 'work'.
-   (Probably this area should be rewritten.)
-
-   - Getting the offset of a field, when it is constant.
-
-   Opcodes that cannot be part of a constant expression, for example
-   those involving registers, simply result in a return of
-   'false'.
-
-   This function may emit a complaint.  */
+/* See read.h.  */
 
-static bool
+bool
 decode_locdesc (struct dwarf_block *blk, struct dwarf2_cu *cu,
 		CORE_ADDR *result)
 {
@@ -20434,10 +19626,9 @@  dwarf2_find_containing_comp_unit
   return low;
 }
 
-/* Locate the .debug_info compilation unit from CU's objfile which contains
-   the DIE at OFFSET.  Raises an error on failure.  */
+/* See read.h.  */
 
-static dwarf2_per_cu *
+dwarf2_per_cu *
 dwarf2_find_containing_comp_unit (sect_offset sect_off,
 				  unsigned int offset_in_dwz,
 				  dwarf2_per_bfd *per_bfd)
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index dcaea14fb49b..ef83bcfaf174 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -1222,4 +1222,91 @@  extern void create_all_units (dwarf2_per_objfile *per_objfile);
 
 extern htab_up create_quick_file_names_table (unsigned int nr_initial_entries);
 
+/* Find the base address of the compilation unit for range lists and
+   location lists.  It will normally be specified by DW_AT_low_pc.
+   In DWARF-3 draft 4, the base address could be overridden by
+   DW_AT_entry_pc.  It's been removed, but GCC still uses this for
+   compilation units with discontinuous ranges.  */
+
+extern void dwarf2_find_base_address (die_info *die, dwarf2_cu *cu);
+
+/* How dwarf2_get_pc_bounds constructed its *LOWPC and *HIGHPC return
+   values.  Keep the items ordered with increasing constraints compliance.  */
+
+enum pc_bounds_kind
+{
+  /* No attribute DW_AT_low_pc, DW_AT_high_pc or DW_AT_ranges was found.  */
+  PC_BOUNDS_NOT_PRESENT,
+
+  /* Some of the attributes DW_AT_low_pc, DW_AT_high_pc or DW_AT_ranges
+	were present but they do not form a valid range of PC addresses.  */
+  PC_BOUNDS_INVALID,
+
+  /* Discontiguous range was found - that is DW_AT_ranges was found.  */
+  PC_BOUNDS_RANGES,
+
+  /* Contiguous range was found - DW_AT_low_pc and DW_AT_high_pc were found.  */
+  PC_BOUNDS_HIGH_LOW,
+};
+
+/* Get low and high pc attributes from a die.  See enum pc_bounds_kind
+   definition for the return value.  *LOWPC and *HIGHPC are set iff
+   neither PC_BOUNDS_NOT_PRESENT nor PC_BOUNDS_INVALID are returned.  */
+
+extern pc_bounds_kind dwarf2_get_pc_bounds (die_info *die,
+					    unrelocated_addr *lowpc,
+					    unrelocated_addr *highpc,
+					    dwarf2_cu *cu, addrmap_mutable *map,
+					    void *datum);
+
+/* Locate the .debug_info compilation unit from CU's objfile which contains
+   the DIE at OFFSET.  Raises an error on failure.  */
+
+extern dwarf2_per_cu *dwarf2_find_containing_comp_unit (sect_offset sect_off,
+							unsigned int
+							  offset_in_dwz,
+							dwarf2_per_bfd
+							  *per_bfd);
+
+/* Decode simple location descriptions.
+
+   Given a pointer to a DWARF block that defines a location, compute
+   the location.  Returns true if the expression was computable by
+   this function, false otherwise.  On a true return, *RESULT is set.
+
+   Note that this function does not implement a full DWARF expression
+   evaluator.  Instead, it is used for a few limited purposes:
+
+   - Getting the address of a symbol that has a constant address.  For
+   example, if a symbol has a location like "DW_OP_addr", the address
+   can be extracted.
+
+   - Getting the offset of a virtual function in its vtable.  There
+   are two forms of this, one of which involves DW_OP_deref -- so this
+   function handles derefs in a peculiar way to make this 'work'.
+   (Probably this area should be rewritten.)
+
+   - Getting the offset of a field, when it is constant.
+
+   Opcodes that cannot be part of a constant expression, for example
+   those involving registers, simply result in a return of
+   'false'.
+
+   This function may emit a complaint.  */
+
+extern bool decode_locdesc (dwarf_block *blk, dwarf2_cu *cu, CORE_ADDR *result);
+
+/* Get low and high pc attributes from DW_AT_ranges attribute value OFFSET.
+   Return 1 if the attributes are present and valid, otherwise, return 0.
+   TAG is passed to dwarf2_ranges_process.  If MAP is not NULL, then
+   ranges in MAP are set, using DATUM as the value.  */
+
+extern int dwarf2_ranges_read (unsigned offset, unrelocated_addr *low_return,
+			       unrelocated_addr *high_return, dwarf2_cu *cu,
+			       addrmap_mutable *map, void *datum,
+			       dwarf_tag tag);
+
+extern file_and_directory &find_file_and_directory (die_info *die,
+						    dwarf2_cu *cu);
+
 #endif /* GDB_DWARF2_READ_H */