diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index c3711a0d2f5..6bd40033770 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1059,6 +1059,7 @@ COMMON_SFILES = \
 	dwarf2/comp-unit-head.c \
 	dwarf2/cooked-index.c \
 	dwarf2/cu.c \
+	dwarf2/die.c \
 	dwarf2/dwz.c \
 	dwarf2/expr.c \
 	dwarf2/frame-tailcall.c \
diff --git a/gdb/dwarf2/die.c b/gdb/dwarf2/die.c
new file mode 100644
index 00000000000..d409d24d7f6
--- /dev/null
+++ b/gdb/dwarf2/die.c
@@ -0,0 +1,229 @@
+/* DWARF DIEs
+
+   Copyright (C) 1994-2023 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 "defs.h"
+#include "dwarf2/die.h"
+#include "dwarf2/stringify.h"
+
+/* See die.h.  */
+
+struct die_info *
+die_info::allocate (struct obstack *obstack, int num_attrs)
+{
+  size_t size = sizeof (struct die_info);
+
+  if (num_attrs > 1)
+    size += (num_attrs - 1) * sizeof (struct attribute);
+
+  struct die_info *die = (struct die_info *) obstack_alloc (obstack, size);
+  memset (die, 0, size);
+  return die;
+}
+
+/* See die.h.  */
+
+hashval_t
+die_info::hash (const void *item)
+{
+  const struct die_info *die = (const struct die_info *) item;
+
+  return to_underlying (die->sect_off);
+}
+
+/* See die.h.  */
+
+int
+die_info::eq (const void *item_lhs, const void *item_rhs)
+{
+  const struct die_info *die_lhs = (const struct die_info *) item_lhs;
+  const struct die_info *die_rhs = (const struct die_info *) item_rhs;
+
+  return die_lhs->sect_off == die_rhs->sect_off;
+}
+
+static void
+dump_die_shallow (struct ui_file *f, int indent, struct die_info *die)
+{
+  unsigned int i;
+
+  gdb_printf (f, "%*sDie: %s (abbrev %d, offset %s)\n",
+	      indent, "",
+	      dwarf_tag_name (die->tag), die->abbrev,
+	      sect_offset_str (die->sect_off));
+
+  if (die->parent != NULL)
+    gdb_printf (f, "%*s  parent at offset: %s\n",
+		indent, "",
+		sect_offset_str (die->parent->sect_off));
+
+  gdb_printf (f, "%*s  has children: %s\n",
+	      indent, "",
+	      dwarf_bool_name (die->child != NULL));
+
+  gdb_printf (f, "%*s  attributes:\n", indent, "");
+
+  for (i = 0; i < die->num_attrs; ++i)
+    {
+      gdb_printf (f, "%*s    %s (%s) ",
+		  indent, "",
+		  dwarf_attr_name (die->attrs[i].name),
+		  dwarf_form_name (die->attrs[i].form));
+
+      switch (die->attrs[i].form)
+	{
+	case DW_FORM_addr:
+	case DW_FORM_addrx:
+	case DW_FORM_GNU_addr_index:
+	  gdb_printf (f, "address: ");
+	  gdb_puts (hex_string (die->attrs[i].as_address ()), f);
+	  break;
+	case DW_FORM_block2:
+	case DW_FORM_block4:
+	case DW_FORM_block:
+	case DW_FORM_block1:
+	  gdb_printf (f, "block: size %s",
+		      pulongest (die->attrs[i].as_block ()->size));
+	  break;
+	case DW_FORM_exprloc:
+	  gdb_printf (f, "expression: size %s",
+		      pulongest (die->attrs[i].as_block ()->size));
+	  break;
+	case DW_FORM_data16:
+	  gdb_printf (f, "constant of 16 bytes");
+	  break;
+	case DW_FORM_ref_addr:
+	  gdb_printf (f, "ref address: ");
+	  gdb_puts (hex_string (die->attrs[i].as_unsigned ()), f);
+	  break;
+	case DW_FORM_GNU_ref_alt:
+	  gdb_printf (f, "alt ref address: ");
+	  gdb_puts (hex_string (die->attrs[i].as_unsigned ()), f);
+	  break;
+	case DW_FORM_ref1:
+	case DW_FORM_ref2:
+	case DW_FORM_ref4:
+	case DW_FORM_ref8:
+	case DW_FORM_ref_udata:
+	  gdb_printf (f, "constant ref: 0x%lx (adjusted)",
+		      (long) (die->attrs[i].as_unsigned ()));
+	  break;
+	case DW_FORM_data1:
+	case DW_FORM_data2:
+	case DW_FORM_data4:
+	case DW_FORM_data8:
+	case DW_FORM_udata:
+	  gdb_printf (f, "constant: %s",
+		      pulongest (die->attrs[i].as_unsigned ()));
+	  break;
+	case DW_FORM_sec_offset:
+	  gdb_printf (f, "section offset: %s",
+		      pulongest (die->attrs[i].as_unsigned ()));
+	  break;
+	case DW_FORM_ref_sig8:
+	  gdb_printf (f, "signature: %s",
+		      hex_string (die->attrs[i].as_signature ()));
+	  break;
+	case DW_FORM_string:
+	case DW_FORM_strp:
+	case DW_FORM_line_strp:
+	case DW_FORM_strx:
+	case DW_FORM_GNU_str_index:
+	case DW_FORM_GNU_strp_alt:
+	  gdb_printf (f, "string: \"%s\" (%s canonicalized)",
+		      die->attrs[i].as_string ()
+		      ? die->attrs[i].as_string () : "",
+		      die->attrs[i].canonical_string_p () ? "is" : "not");
+	  break;
+	case DW_FORM_flag:
+	  if (die->attrs[i].as_boolean ())
+	    gdb_printf (f, "flag: TRUE");
+	  else
+	    gdb_printf (f, "flag: FALSE");
+	  break;
+	case DW_FORM_flag_present:
+	  gdb_printf (f, "flag: TRUE");
+	  break;
+	case DW_FORM_indirect:
+	  /* The reader will have reduced the indirect form to
+	     the "base form" so this form should not occur.  */
+	  gdb_printf (f,
+		      "unexpected attribute form: DW_FORM_indirect");
+	  break;
+	case DW_FORM_sdata:
+	case DW_FORM_implicit_const:
+	  gdb_printf (f, "constant: %s",
+		      plongest (die->attrs[i].as_signed ()));
+	  break;
+	default:
+	  gdb_printf (f, "unsupported attribute form: %d.",
+		      die->attrs[i].form);
+	  break;
+	}
+      gdb_printf (f, "\n");
+    }
+}
+
+static void
+dump_die_1 (struct ui_file *f, int level, int max_level, struct die_info *die)
+{
+  int indent = level * 4;
+
+  gdb_assert (die != NULL);
+
+  if (level >= max_level)
+    return;
+
+  dump_die_shallow (f, indent, die);
+
+  if (die->child != NULL)
+    {
+      gdb_printf (f, "%*s  Children:", indent, "");
+      if (level + 1 < max_level)
+	{
+	  gdb_printf (f, "\n");
+	  dump_die_1 (f, level + 1, max_level, die->child);
+	}
+      else
+	{
+	  gdb_printf (f,
+		      " [not printed, max nesting level reached]\n");
+	}
+    }
+
+  if (die->sibling != NULL && level > 0)
+    {
+      dump_die_1 (f, level, max_level, die->sibling);
+    }
+}
+
+/* See die.h.  */
+
+void
+die_info::dump (int max_level)
+{
+  dump_die_1 (gdb_stdlog, 0, max_level, this);
+}
+
+/* See die.h.  */
+
+void
+die_info::error_dump ()
+{
+  dump_die_shallow (gdb_stderr, 0, this);
+}
diff --git a/gdb/dwarf2/die.h b/gdb/dwarf2/die.h
index ae46514c859..cdee46996d6 100644
--- a/gdb/dwarf2/die.h
+++ b/gdb/dwarf2/die.h
@@ -21,10 +21,31 @@
 #define GDB_DWARF2_DIE_H
 
 #include "complaints.h"
+#include "dwarf2/attribute.h"
 
 /* This data structure holds a complete die structure.  */
 struct die_info
 {
+  /* Allocate a new die_info on OBSTACK.  NUM_ATTRS is the number of
+     attributes that are needed.  */
+  static die_info *allocate (struct obstack *obstack, int num_attrs);
+
+  /* Trivial hash function for die_info: the hash value of a DIE is
+     its offset in .debug_info for this objfile.  */
+  static hashval_t hash (const void *item);
+
+  /* Trivial comparison function for die_info structures: two DIEs
+     are equal if they have the same offset.  */
+  static int eq (const void *item_lhs, const void *item_rhs);
+
+  /* Dump this DIE and any children to MAX_LEVEL.  They are written to
+     gdb_stdlog.  Note this is called from the pdie user command in
+     gdb-gdb.gdb.  */
+  void dump (int max_level);
+
+  /* Shallowly dump this DIE to gdb_stderr.  */
+  void error_dump ();
+
   /* Return the named attribute or NULL if not there, but do not
      follow DW_AT_specification, etc.  */
   struct attribute *attr (dwarf_attribute name)
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index cd937f24ee7..fb9075d221a 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -1127,15 +1127,6 @@ static const char *dwarf2_physname (const char *name, struct die_info *die,
 static struct die_info *dwarf2_extension (struct die_info *die,
 					  struct dwarf2_cu **);
 
-static void dump_die_shallow (struct ui_file *, int indent, struct die_info *);
-
-static void dump_die_for_error (struct die_info *);
-
-static void dump_die_1 (struct ui_file *, int level, int max_level,
-			struct die_info *);
-
-/*static*/ void dump_die (struct die_info *, int max_level);
-
 static void store_in_ref_table (struct die_info *,
 				struct dwarf2_cu *);
 
@@ -1172,8 +1163,6 @@ static int attr_to_dynamic_prop (const struct attribute *attr,
 
 static struct dwarf_block *dwarf_alloc_block (struct dwarf2_cu *);
 
-static struct die_info *dwarf_alloc_die (struct dwarf2_cu *, int);
-
 static void dwarf_decode_macros (struct dwarf2_cu *, unsigned int, int);
 
 static void fill_in_loclist_baton (struct dwarf2_cu *cu,
@@ -6064,7 +6053,7 @@ read_cutu_die_from_dwo (dwarf2_cu *cu,
 		  section->get_name (),
 		  (unsigned) (begin_info_ptr - section->buffer),
 		  bfd_get_filename (abfd));
-      dump_die (comp_unit_die, dwarf_die_debug);
+      comp_unit_die->dump (dwarf_die_debug);
     }
 
   /* Skip dummy compilation units.  */
@@ -7686,29 +7675,6 @@ process_queue (dwarf2_per_objfile *per_objfile)
 			   objfile_name (per_objfile->objfile));
 }
 
-/* Trivial hash function for die_info: the hash value of a DIE
-   is its offset in .debug_info for this objfile.  */
-
-static hashval_t
-die_hash (const void *item)
-{
-  const struct die_info *die = (const struct die_info *) item;
-
-  return to_underlying (die->sect_off);
-}
-
-/* Trivial comparison function for die_info structures: two DIEs
-   are equal if they have the same offset.  */
-
-static int
-die_eq (const void *item_lhs, const void *item_rhs)
-{
-  const struct die_info *die_lhs = (const struct die_info *) item_lhs;
-  const struct die_info *die_rhs = (const struct die_info *) item_rhs;
-
-  return die_lhs->sect_off == die_rhs->sect_off;
-}
-
 /* Load the DIEs associated with PER_CU into memory.
 
    In some cases, the caller, while reading partial symbols, will need to load
@@ -7735,8 +7701,8 @@ load_full_comp_unit (dwarf2_per_cu_data *this_cu,
   gdb_assert (cu->die_hash == NULL);
   cu->die_hash =
     htab_create_alloc_ex (cu->header.get_length_without_initial () / 12,
-			  die_hash,
-			  die_eq,
+			  die_info::hash,
+			  die_info::eq,
 			  NULL,
 			  &cu->comp_unit_obstack,
 			  hashtab_obstack_allocate,
@@ -17884,7 +17850,7 @@ read_die_and_siblings (const struct die_reader_specs *reader,
 		  reader->die_section->get_name (),
 		  (unsigned) (info_ptr - reader->die_section->buffer),
 		  bfd_get_filename (reader->abfd));
-      dump_die (die, dwarf_die_debug);
+      die->dump (dwarf_die_debug);
     }
 
   return die;
@@ -17923,7 +17889,8 @@ read_full_die_1 (const struct die_reader_specs *reader,
 	   abbrev_number,
 	   bfd_get_filename (abfd));
 
-  die = dwarf_alloc_die (cu, abbrev->num_attrs + num_extra_attrs);
+  die = die_info::allocate (&cu->comp_unit_obstack,
+			    abbrev->num_attrs + num_extra_attrs);
   die->sect_off = sect_off;
   die->tag = abbrev->tag;
   die->abbrev = abbrev_number;
@@ -17990,7 +17957,7 @@ read_full_die (const struct die_reader_specs *reader,
 		  reader->die_section->get_name (),
 		  (unsigned) (info_ptr - reader->die_section->buffer),
 		  bfd_get_filename (reader->abfd));
-      dump_die (*diep, dwarf_die_debug);
+      (*diep)->dump (dwarf_die_debug);
     }
 
   return result;
@@ -22187,176 +22154,6 @@ dwarf2_extension (struct die_info *die, struct dwarf2_cu **ext_cu)
   return follow_die_ref (die, attr, ext_cu);
 }
 
-static void
-dump_die_shallow (struct ui_file *f, int indent, struct die_info *die)
-{
-  unsigned int i;
-
-  gdb_printf (f, "%*sDie: %s (abbrev %d, offset %s)\n",
-	      indent, "",
-	      dwarf_tag_name (die->tag), die->abbrev,
-	      sect_offset_str (die->sect_off));
-
-  if (die->parent != NULL)
-    gdb_printf (f, "%*s  parent at offset: %s\n",
-		indent, "",
-		sect_offset_str (die->parent->sect_off));
-
-  gdb_printf (f, "%*s  has children: %s\n",
-	      indent, "",
-	      dwarf_bool_name (die->child != NULL));
-
-  gdb_printf (f, "%*s  attributes:\n", indent, "");
-
-  for (i = 0; i < die->num_attrs; ++i)
-    {
-      gdb_printf (f, "%*s    %s (%s) ",
-		  indent, "",
-		  dwarf_attr_name (die->attrs[i].name),
-		  dwarf_form_name (die->attrs[i].form));
-
-      switch (die->attrs[i].form)
-	{
-	case DW_FORM_addr:
-	case DW_FORM_addrx:
-	case DW_FORM_GNU_addr_index:
-	  gdb_printf (f, "address: ");
-	  gdb_puts (hex_string (die->attrs[i].as_address ()), f);
-	  break;
-	case DW_FORM_block2:
-	case DW_FORM_block4:
-	case DW_FORM_block:
-	case DW_FORM_block1:
-	  gdb_printf (f, "block: size %s",
-		      pulongest (die->attrs[i].as_block ()->size));
-	  break;
-	case DW_FORM_exprloc:
-	  gdb_printf (f, "expression: size %s",
-		      pulongest (die->attrs[i].as_block ()->size));
-	  break;
-	case DW_FORM_data16:
-	  gdb_printf (f, "constant of 16 bytes");
-	  break;
-	case DW_FORM_ref_addr:
-	  gdb_printf (f, "ref address: ");
-	  gdb_puts (hex_string (die->attrs[i].as_unsigned ()), f);
-	  break;
-	case DW_FORM_GNU_ref_alt:
-	  gdb_printf (f, "alt ref address: ");
-	  gdb_puts (hex_string (die->attrs[i].as_unsigned ()), f);
-	  break;
-	case DW_FORM_ref1:
-	case DW_FORM_ref2:
-	case DW_FORM_ref4:
-	case DW_FORM_ref8:
-	case DW_FORM_ref_udata:
-	  gdb_printf (f, "constant ref: 0x%lx (adjusted)",
-		      (long) (die->attrs[i].as_unsigned ()));
-	  break;
-	case DW_FORM_data1:
-	case DW_FORM_data2:
-	case DW_FORM_data4:
-	case DW_FORM_data8:
-	case DW_FORM_udata:
-	  gdb_printf (f, "constant: %s",
-		      pulongest (die->attrs[i].as_unsigned ()));
-	  break;
-	case DW_FORM_sec_offset:
-	  gdb_printf (f, "section offset: %s",
-		      pulongest (die->attrs[i].as_unsigned ()));
-	  break;
-	case DW_FORM_ref_sig8:
-	  gdb_printf (f, "signature: %s",
-		      hex_string (die->attrs[i].as_signature ()));
-	  break;
-	case DW_FORM_string:
-	case DW_FORM_strp:
-	case DW_FORM_line_strp:
-	case DW_FORM_strx:
-	case DW_FORM_GNU_str_index:
-	case DW_FORM_GNU_strp_alt:
-	  gdb_printf (f, "string: \"%s\" (%s canonicalized)",
-		      die->attrs[i].as_string ()
-		      ? die->attrs[i].as_string () : "",
-		      die->attrs[i].canonical_string_p () ? "is" : "not");
-	  break;
-	case DW_FORM_flag:
-	  if (die->attrs[i].as_boolean ())
-	    gdb_printf (f, "flag: TRUE");
-	  else
-	    gdb_printf (f, "flag: FALSE");
-	  break;
-	case DW_FORM_flag_present:
-	  gdb_printf (f, "flag: TRUE");
-	  break;
-	case DW_FORM_indirect:
-	  /* The reader will have reduced the indirect form to
-	     the "base form" so this form should not occur.  */
-	  gdb_printf (f,
-		      "unexpected attribute form: DW_FORM_indirect");
-	  break;
-	case DW_FORM_sdata:
-	case DW_FORM_implicit_const:
-	  gdb_printf (f, "constant: %s",
-		      plongest (die->attrs[i].as_signed ()));
-	  break;
-	default:
-	  gdb_printf (f, "unsupported attribute form: %d.",
-		      die->attrs[i].form);
-	  break;
-	}
-      gdb_printf (f, "\n");
-    }
-}
-
-static void
-dump_die_for_error (struct die_info *die)
-{
-  dump_die_shallow (gdb_stderr, 0, die);
-}
-
-static void
-dump_die_1 (struct ui_file *f, int level, int max_level, struct die_info *die)
-{
-  int indent = level * 4;
-
-  gdb_assert (die != NULL);
-
-  if (level >= max_level)
-    return;
-
-  dump_die_shallow (f, indent, die);
-
-  if (die->child != NULL)
-    {
-      gdb_printf (f, "%*s  Children:", indent, "");
-      if (level + 1 < max_level)
-	{
-	  gdb_printf (f, "\n");
-	  dump_die_1 (f, level + 1, max_level, die->child);
-	}
-      else
-	{
-	  gdb_printf (f,
-		      " [not printed, max nesting level reached]\n");
-	}
-    }
-
-  if (die->sibling != NULL && level > 0)
-    {
-      dump_die_1 (f, level, max_level, die->sibling);
-    }
-}
-
-/* This is called from the pdie macro in gdbinit.in.
-   It's not static so gcc will keep a copy callable from gdb.  */
-
-void
-dump_die (struct die_info *die, int max_level)
-{
-  dump_die_1 (gdb_stdlog, 0, max_level, die);
-}
-
 static void
 store_in_ref_table (struct die_info *die, struct dwarf2_cu *cu)
 {
@@ -22385,7 +22182,7 @@ follow_die_ref_or_sig (struct die_info *src_die, const struct attribute *attr,
     die = follow_die_sig (src_die, attr, ref_cu);
   else
     {
-      dump_die_for_error (src_die);
+      src_die->error_dump ();
       error (_("Dwarf Error: Expected reference attribute [in module %s]"),
 	     objfile_name ((*ref_cu)->per_objfile->objfile));
     }
@@ -22884,7 +22681,7 @@ follow_die_sig (struct die_info *src_die, const struct attribute *attr,
   die = follow_die_sig_1 (src_die, sig_type, ref_cu);
   if (die == NULL)
     {
-      dump_die_for_error (src_die);
+      src_die->error_dump ();
       error (_("Dwarf Error: Problem reading signatured DIE %s referenced"
 	       " from DIE at %s [in module %s]"),
 	     hex_string (signature), sect_offset_str (src_die->sect_off),
@@ -23027,8 +22824,8 @@ read_signatured_type (signatured_type *sig_type,
       gdb_assert (cu->die_hash == NULL);
       cu->die_hash =
 	htab_create_alloc_ex (cu->header.get_length_without_initial () / 12,
-			      die_hash,
-			      die_eq,
+			      die_info::hash,
+			      die_info::eq,
 			      NULL,
 			      &cu->comp_unit_obstack,
 			      hashtab_obstack_allocate,
@@ -23344,20 +23141,6 @@ dwarf_alloc_block (struct dwarf2_cu *cu)
   return XOBNEW (&cu->comp_unit_obstack, struct dwarf_block);
 }
 
-static struct die_info *
-dwarf_alloc_die (struct dwarf2_cu *cu, int num_attrs)
-{
-  struct die_info *die;
-  size_t size = sizeof (struct die_info);
-
-  if (num_attrs > 1)
-    size += (num_attrs - 1) * sizeof (struct attribute);
-
-  die = (struct die_info *) obstack_alloc (&cu->comp_unit_obstack, size);
-  memset (die, 0, sizeof (struct die_info));
-  return (die);
-}
-
 
 
 /* Macro support.  */
diff --git a/gdb/gdb-gdb.gdb.in b/gdb/gdb-gdb.gdb.in
index 8d97c95a70e..2ff4b9d8f5d 100644
--- a/gdb/gdb-gdb.gdb.in
+++ b/gdb/gdb-gdb.gdb.in
@@ -28,10 +28,10 @@ set prompt (top-gdb)
 
 define pdie
   if $argc == 1
-    call dump_die ($arg0, 1)
+    call $arg0->dump (1)
   else
     if $argc == 2
-      call dump_die ($arg0, $arg1)
+      call $arg0->dump ($arg1)
     else
       printf "Syntax: pdie die [depth]\n"
     end
