From: Tom Tromey <tromey@adacore.com>
To: gdb-patches@sourceware.org
Cc: Tom Tromey <tromey@adacore.com>
Subject: [PATCH] Do not crash on recursive pointer types
Date: Fri, 14 Nov 2025 12:02:03 -0700 [thread overview]
Message-ID: <20251114190203.2369142-1-tromey@adacore.com> (raw)
In Ada, it's possible to create a recursive pointer type, or two
mutually recursive pointer types. gdb will crash when reading the
DWARF for these.
These types aren't actually useful, and so are unlikely to appear in
real code. Still, gdb shouldn't crash.
This patch handles this case by noticing recursive pointer types and
representing them as pointer-to-void.
---
gdb/dwarf2/read.c | 84 ++++++++++++++++++-
gdb/testsuite/gdb.ada/recursive-access.exp | 31 +++++++
.../gdb.ada/recursive-access/pack.ads | 26 ++++++
.../gdb.ada/recursive-access/prog.adb | 23 +++++
4 files changed, 163 insertions(+), 1 deletion(-)
create mode 100644 gdb/testsuite/gdb.ada/recursive-access.exp
create mode 100644 gdb/testsuite/gdb.ada/recursive-access/pack.ads
create mode 100644 gdb/testsuite/gdb.ada/recursive-access/prog.adb
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 29f100b7638..f31f0b30858 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -916,6 +916,9 @@ 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 die_info *follow_die_offset (const section_and_offset &target,
+ dwarf2_cu **ref_cu);
+
static struct die_info *follow_die_ref_or_sig (struct die_info *,
const struct attribute *,
struct dwarf2_cu **);
@@ -12725,6 +12728,75 @@ namespace_name (struct die_info *die, int *is_anonymous, struct dwarf2_cu *cu)
return name;
}
+/* A convenience structure that holds a CU and a DIE. */
+
+struct cu_die_pair
+{
+ die_info *die;
+ dwarf2_cu *cu;
+
+ /* Return true if this object holds a valid DIE. */
+ bool valid () const
+ {
+ return die != nullptr;
+ }
+
+ /* Compare two cu_die_pair objects. */
+ bool operator!= (const cu_die_pair &other) const
+ {
+ return die != other.die || cu != other.cu;
+ }
+};
+
+/* Follow the DW_AT_type reference, if any, from ORIG. The return
+ value will be valid only when DW_AT_type exists. In other cases,
+ including when the incoming ORIG is not valid, an invalid
+ cu_die_pair is returned. */
+
+static cu_die_pair
+follow_type (cu_die_pair orig)
+{
+ /* This simplifies the caller a little. */
+ if (!orig.valid ())
+ return {};
+
+ attribute *type_attr = dwarf2_attr (orig.die, DW_AT_type, orig.cu);
+ if (type_attr == nullptr || !type_attr->form_is_ref ())
+ return {};
+
+ cu_die_pair result = orig;
+ sect_offset sect_off = type_attr->get_ref_die_offset ();
+ const dwarf2_section_info §ion = get_section_for_ref (*type_attr,
+ orig.cu);
+ result.die = follow_die_offset ({ §ion, sect_off }, &result.cu);
+
+ /* Note that we do not check for DW_TAG_pointer_type here. GNAT, at
+ least, will emit recursive pointers that "indirect" via typedefs,
+ so checkpoint purely for pointer types would not find these. */
+ return result;
+}
+
+/* Return true if DIE is a self-referential pointer type; false
+ otherwise. CU is the origin of DIE. */
+
+static bool
+is_recursive_pointer (die_info *die, dwarf2_cu *cu)
+{
+ cu_die_pair tortoise { die, cu };
+ gdb_assert (tortoise.valid ());
+ cu_die_pair hare = follow_type (tortoise);
+
+ while (tortoise != hare)
+ {
+ tortoise = follow_type (tortoise);
+ hare = follow_type (follow_type (hare));
+ if (!tortoise.valid () || !hare.valid ())
+ return false;
+ }
+
+ return true;
+}
+
/* Extract all information from a DW_TAG_pointer_type DIE and add to
the user defined type vector. */
@@ -12739,7 +12811,17 @@ read_tag_pointer_type (struct die_info *die, struct dwarf2_cu *cu)
int byte_size;
struct type *target_type;
- target_type = die_type (die, cu);
+ /* In Ada, it's possible to create a self-referential pointer type.
+ These aren't useful, but nevertheless we take care to avoid a gdb
+ crash in this situation. Instead just turn these into a
+ pointer-to-void. */
+ if (is_recursive_pointer (die, cu))
+ {
+ type_allocator alloc (cu->per_objfile->objfile, cu->lang ());
+ target_type = alloc.new_type (TYPE_CODE_VOID, 0, nullptr);
+ }
+ else
+ target_type = die_type (die, cu);
/* The die_type call above may have already set the type for this DIE. */
type = get_die_type (die, cu);
diff --git a/gdb/testsuite/gdb.ada/recursive-access.exp b/gdb/testsuite/gdb.ada/recursive-access.exp
new file mode 100644
index 00000000000..833be936a10
--- /dev/null
+++ b/gdb/testsuite/gdb.ada/recursive-access.exp
@@ -0,0 +1,31 @@
+# Copyright 2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Test that recursive access types do not cause a crash.
+
+load_lib "ada.exp"
+
+require allow_ada_tests
+
+standard_ada_testfile prog
+
+if {[gdb_compile_ada "${srcfile}" "${binfile}" executable {debug}] != ""} {
+ return
+}
+
+clean_restart ${testfile}
+
+# The bug was that even reading these types caused a crash.
+gdb_test_no_output "maint expand-symtabs"
diff --git a/gdb/testsuite/gdb.ada/recursive-access/pack.ads b/gdb/testsuite/gdb.ada/recursive-access/pack.ads
new file mode 100644
index 00000000000..fe06edc56e5
--- /dev/null
+++ b/gdb/testsuite/gdb.ada/recursive-access/pack.ads
@@ -0,0 +1,26 @@
+-- Copyright 2025 Free Software Foundation, Inc.
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package Pack is
+ -- With GCC, this makes a directly self-referential pointer type.
+ type Direct;
+ subtype Sub is Direct;
+ type Direct is access Sub;
+
+ -- With GCC, this makes two mutually recursive pointer types.
+ type Second;
+ type First is access Second;
+ type Second is access First;
+end Pack;
diff --git a/gdb/testsuite/gdb.ada/recursive-access/prog.adb b/gdb/testsuite/gdb.ada/recursive-access/prog.adb
new file mode 100644
index 00000000000..f740343feb8
--- /dev/null
+++ b/gdb/testsuite/gdb.ada/recursive-access/prog.adb
@@ -0,0 +1,23 @@
+-- Copyright 2025 Free Software Foundation, Inc.
+--
+-- This program is free software; you can redistribute it and/or modify
+-- it under the terms of the GNU General Public License as published by
+-- the Free Software Foundation; either version 3 of the License, or
+-- (at your option) any later version.
+--
+-- This program is distributed in the hope that it will be useful,
+-- but WITHOUT ANY WARRANTY; without even the implied warranty of
+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+-- GNU General Public License for more details.
+--
+-- You should have received a copy of the GNU General Public License
+-- along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+with Pack; use Pack;
+
+procedure Prog is
+ X : Direct := null;
+ Y : Second := new First;
+begin
+ null;
+end Prog;
base-commit: 24e9fdc6ef2c27fab8416737d4ed8129accdc551
--
2.51.1
next reply other threads:[~2025-11-14 19:02 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-11-14 19:02 Tom Tromey [this message]
2025-11-15 21:27 ` Kevin Buettner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251114190203.2369142-1-tromey@adacore.com \
--to=tromey@adacore.com \
--cc=gdb-patches@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox