From: Tom Tromey Date: Fri, 14 Nov 2025 14:49:50 +0000 (-0700) Subject: Do not crash on recursive pointer types X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e831f91245f8d96d19af7c957eef7ad861905eff;p=thirdparty%2Fbinutils-gdb.git Do not crash on recursive pointer types 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. Approved-by: Kevin Buettner --- diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c index 961cf430e0b..8bc98342db8 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 **); @@ -12738,6 +12741,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. */ @@ -12752,7 +12824,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 . + +# 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 . + +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 . + +with Pack; use Pack; + +procedure Prog is + X : Direct := null; + Y : Second := new First; +begin + null; +end Prog;