* [PATCH] gdb, d: Fix PR 33200 - Segfault in d_lookup_symbol
@ 2025-07-30 11:42 Iain Buclaw
2025-07-31 16:52 ` Tom Tromey
0 siblings, 1 reply; 6+ messages in thread
From: Iain Buclaw @ 2025-07-30 11:42 UTC (permalink / raw)
To: gdb-patches; +Cc: Iain Buclaw
If d_lookup_symbol tries to locate a member variable within a class or
struct type with no a name, type->name() is NULL, so the assignment to
std::string classname crashes gdb.
This can happen in some older versions of gdc, where debug info was
generated too eagerly in complex cases involving types referencing each
other recursively.
This patch adds a guard against this case, allowing the caller of
d_lookup_symbol to search other blocks for the right symbol.
The asm test was generated by namelessclass.d, with the name of the
class removed to synthesize the failure.
---
gdb/d-namespace.c | 4 +
gdb/testsuite/gdb.dlang/namelessclass.d | 33 ++
gdb/testsuite/gdb.dlang/namelessclass.exp | 44 ++
gdb/testsuite/gdb.dlang/namelessclass.s | 654 ++++++++++++++++++++++
4 files changed, 735 insertions(+)
create mode 100644 gdb/testsuite/gdb.dlang/namelessclass.d
create mode 100644 gdb/testsuite/gdb.dlang/namelessclass.exp
create mode 100644 gdb/testsuite/gdb.dlang/namelessclass.s
diff --git a/gdb/d-namespace.c b/gdb/d-namespace.c
index b5e046efaa7..84872ad9f11 100644
--- a/gdb/d-namespace.c
+++ b/gdb/d-namespace.c
@@ -131,6 +131,10 @@ d_lookup_symbol (const struct language_defn *langdef,
return {};
type = check_typedef (lang_this.symbol->type ()->target_type ());
+ /* If type name is NULL, abandon trying to find this symbol. */
+ if (type->name () == NULL)
+ return {};
+
classname = type->name ();
nested = name;
}
diff --git a/gdb/testsuite/gdb.dlang/namelessclass.d b/gdb/testsuite/gdb.dlang/namelessclass.d
new file mode 100644
index 00000000000..797ff5d50b7
--- /dev/null
+++ b/gdb/testsuite/gdb.dlang/namelessclass.d
@@ -0,0 +1,33 @@
+/* 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/>. */
+
+/* This is the original source for namelessclass.s. This file is never
+ compiled by the test suite, since the assembler output of gdc,
+ namelessclass.s, is used instead. */
+
+module object;
+
+void _d_newclassT(T)() {}
+extern(C) void _d_callfinalizer(void*) {}
+
+extern(C) void main()
+{
+ scope c = new class {
+ void doit() {
+ return; // set breakpoint here
+ }
+ };
+ c.doit();
+}
diff --git a/gdb/testsuite/gdb.dlang/namelessclass.exp b/gdb/testsuite/gdb.dlang/namelessclass.exp
new file mode 100644
index 00000000000..8d59ec60d38
--- /dev/null
+++ b/gdb/testsuite/gdb.dlang/namelessclass.exp
@@ -0,0 +1,44 @@
+# Copyright 2014-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 nameless classes that may be output by the compiler.
+# PR gdb/33200
+
+load_lib dwarf.exp
+
+# Do not run in environments which do not support D.
+# This test can only be run on x86-like targets which support DWARF.
+require dwarf2_support allow_d_tests
+
+require is_x86_64_m64_target
+
+standard_testfile .s
+set dsrcfile "${testfile}.d"
+
+if {[prepare_for_testing "failed to prepare" $testfile $srcfile {nodebug}]} {
+ return -1
+}
+
+if {![runto_main]} {
+ return -1
+}
+
+# Set a breakpoint in the anonymous class::doit (). */
+set line [gdb_get_line_number "set breakpoint here" $dsrcfile]
+gdb_breakpoint $line
+gdb_continue_to_breakpoint "continue to breakpoint at line $line"
+
+# Any output is accepted as valid as long as gdb does not segfault.
+gdb_test "print do_not_segfault" ".*"
diff --git a/gdb/testsuite/gdb.dlang/namelessclass.s b/gdb/testsuite/gdb.dlang/namelessclass.s
new file mode 100644
index 00000000000..c6193834b29
--- /dev/null
+++ b/gdb/testsuite/gdb.dlang/namelessclass.s
@@ -0,0 +1,654 @@
+/* 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/>. */
+
+/* This file was generated using:
+
+ $ gdc namelessclass.d -g -S -dA -fno-weak-templates
+
+ with
+
+ $ gdc -v
+ Target: x86_64-pc-linux-gnu
+ Thread model: posix
+ gcc version 14.2.0 (GCC)
+
+ This is a test for d/33200. */
+
+ .file "namelessclass.d"
+ .text
+.Ltext0:
+ .globl _d_callfinalizer
+ .type _d_callfinalizer, @function
+_d_callfinalizer:
+.LFB0:
+ .file 1 "namelessclass.d"
+ # namelessclass.d:23:16
+ .loc 1 23 16
+ .cfi_startproc
+# BLOCK 2 seq:0
+# PRED: ENTRY (FALLTHRU)
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset 6, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register 6
+ movq %rdi, -8(%rbp)
+ # namelessclass.d:23:16
+ .loc 1 23 16
+ nop
+ # namelessclass.d:23:41
+ .loc 1 23 41
+ popq %rbp
+ .cfi_def_cfa 7, 8
+# SUCC: EXIT [always] namelessclass.d:23:16
+ ret
+ .cfi_endproc
+.LFE0:
+ .size _d_callfinalizer, .-_d_callfinalizer
+ .globl main
+ .type main, @function
+main:
+.LFB1:
+ # namelessclass.d:25:16
+ .loc 1 25 16
+ .cfi_startproc
+# BLOCK 2 seq:0
+# PRED: ENTRY (FALLTHRU)
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset 6, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register 6
+ subq $32, %rsp
+ # namelessclass.d:27:15
+ .loc 1 27 15
+ pxor %xmm0, %xmm0
+ movaps %xmm0, -32(%rbp)
+ movq %xmm0, -16(%rbp)
+ leaq _D6object4mainUZ12__anonclass16__vtblZ(%rip), %rax
+ movq %rax, -32(%rbp)
+ movq $0, -16(%rbp)
+ # namelessclass.d:27:11
+ .loc 1 27 11
+ leaq -32(%rbp), %rax
+ movq %rax, -8(%rbp)
+ # namelessclass.d:32:11
+ .loc 1 32 11
+ movq -8(%rbp), %rax
+ movq (%rax), %rax
+ movq 8(%rax), %rdx
+ movq -8(%rbp), %rax
+ movq %rax, %rdi
+ call *%rdx
+.LVL0:
+ # namelessclass.d:27:11
+ .loc 1 27 11
+ movq -8(%rbp), %rax
+ movq %rax, %rdi
+ call _d_callfinalizer@PLT
+ # namelessclass.d:25:16
+ .loc 1 25 16
+ movl $0, %eax
+ # namelessclass.d:33:1
+ .loc 1 33 1
+ leave
+ .cfi_def_cfa 7, 8
+# SUCC: EXIT [always]
+ ret
+ .cfi_endproc
+.LFE1:
+ .size main, .-main
+ .align 2
+ .globl _D6object4mainUZ12__anonclass14doitMFZv
+ .type _D6object4mainUZ12__anonclass14doitMFZv, @function
+_D6object4mainUZ12__anonclass14doitMFZv:
+.LFB2:
+ # namelessclass.d:28:14
+ .loc 1 28 14
+ .cfi_startproc
+# BLOCK 2 seq:0
+# PRED: ENTRY (FALLTHRU)
+ pushq %rbp
+ .cfi_def_cfa_offset 16
+ .cfi_offset 6, -16
+ movq %rsp, %rbp
+ .cfi_def_cfa_register 6
+ movq %rdi, -8(%rbp)
+ # namelessclass.d:29:13
+ .loc 1 29 13
+ nop
+ # namelessclass.d:30:9
+ .loc 1 30 9
+ popq %rbp
+ .cfi_def_cfa 7, 8
+# SUCC: EXIT [always] namelessclass.d:29:13
+ ret
+ .cfi_endproc
+.LFE2:
+ .size _D6object4mainUZ12__anonclass14doitMFZv, .-_D6object4mainUZ12__anonclass14doitMFZv
+ .weak _D6object4mainUZ12__anonclass17__ClassZ
+ .section .data.rel.local._D6object4mainUZ12__anonclass17__ClassZ,"awG",@progbits,_D6object4mainUZ12__anonclass17__ClassZ,comdat
+ .align 32
+ .type _D6object4mainUZ12__anonclass17__ClassZ, @gnu_unique_object
+ .size _D6object4mainUZ12__anonclass17__ClassZ, 168
+_D6object4mainUZ12__anonclass17__ClassZ:
+ .quad 0
+ .quad 0
+ .quad 24
+ .quad _D6object4mainUZ12__anonclass16__initZ
+ .quad 24
+ .quad _D6object4mainUZ12__anonclass17__ClassZ.1827
+ .quad 2
+ .quad _D6object4mainUZ12__anonclass16__vtblZ
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+ .long 52
+ .zero 4
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 0
+ .quad 1
+ .long 0
+ .long 0
+ .long 0
+ .long 0
+ .weak _D6object4mainUZ12__anonclass16__vtblZ
+ .section .data.rel.ro.local._D6object4mainUZ12__anonclass16__vtblZ,"awG",@progbits,_D6object4mainUZ12__anonclass16__vtblZ,comdat
+ .align 8
+ .type _D6object4mainUZ12__anonclass16__vtblZ, @object
+ .size _D6object4mainUZ12__anonclass16__vtblZ, 16
+_D6object4mainUZ12__anonclass16__vtblZ:
+ .quad _D6object4mainUZ12__anonclass17__ClassZ
+ .quad _D6object4mainUZ12__anonclass14doitMFZv
+ .weak _D6object4mainUZ12__anonclass16__initZ
+ .section .data.rel.ro.local._D6object4mainUZ12__anonclass16__initZ,"awG",@progbits,_D6object4mainUZ12__anonclass16__initZ,comdat
+ .align 16
+ .type _D6object4mainUZ12__anonclass16__initZ, @object
+ .size _D6object4mainUZ12__anonclass16__initZ, 24
+_D6object4mainUZ12__anonclass16__initZ:
+ .quad _D6object4mainUZ12__anonclass16__vtblZ
+ .zero 8
+ .quad 0
+ .internal _D6object4mainUZ12__anonclass17__ClassZ.1827
+ .weak _D6object4mainUZ12__anonclass17__ClassZ.1827
+ .section .rodata._D6object4mainUZ12__anonclass17__ClassZ.1827,"aG",@progbits,_D6object4mainUZ12__anonclass17__ClassZ.1827,comdat
+ .align 16
+ .type _D6object4mainUZ12__anonclass17__ClassZ.1827, @object
+ .size _D6object4mainUZ12__anonclass17__ClassZ.1827, 24
+_D6object4mainUZ12__anonclass17__ClassZ.1827:
+ .ascii "object.main.__anonclass1"
+ .text
+.Letext0:
+ .file 2 "<no_file>"
+ .section .debug_info,"",@progbits
+.Ldebug_info0:
+ .long 0x12b # Length of Compilation Unit Info
+ .value 0x5 # DWARF version number
+ .byte 0x1 # DW_UT_compile
+ .byte 0x8 # Pointer Size (in bytes)
+ .long .Ldebug_abbrev0 # Offset Into Abbrev. Section
+ .uleb128 0x5 # (DIE (0xc) DW_TAG_compile_unit)
+ .long .LASF6 # DW_AT_producer: "GNU D 14.2.0"
+ .byte 0x13 # DW_AT_language
+ .long .LASF0 # DW_AT_name: "namelessclass.d"
+ .long .LASF1 # DW_AT_comp_dir: "/binutils-gdb/gdb/testsuite/gdb.dlang"
+ .quad .Ltext0 # DW_AT_low_pc
+ .quad .Letext0-.Ltext0 # DW_AT_high_pc
+ .long .Ldebug_line0 # DW_AT_stmt_list
+ .uleb128 0x6 # (DIE (0x2e) DW_TAG_module)
+ .long .LASF7 # DW_AT_name: "object"
+ .byte 0x1 # DW_AT_decl_file (namelessclass.d)
+ .byte 0x14 # DW_AT_decl_line
+ .byte 0x1 # DW_AT_decl_column
+ .long 0x42 # DW_AT_sibling
+ .uleb128 0x7 # (DIE (0x3a) DW_TAG_imported_module)
+ .byte 0x2 # DW_AT_decl_file (<no_file>)
+ .byte 0x1 # DW_AT_decl_line
+ .long 0x2e # DW_AT_import
+ .byte 0 # end of children of DIE 0x2e
+ .uleb128 0x8 # (DIE (0x42) DW_TAG_subprogram)
+ # DW_AT_external
+ .long .LASF8 # DW_AT_name: "main"
+ .byte 0x1 # DW_AT_decl_file (namelessclass.d)
+ .byte 0x19 # DW_AT_decl_line
+ .byte 0x10 # DW_AT_decl_column
+ .long 0xe4 # DW_AT_type
+ .quad .LFB1 # DW_AT_low_pc
+ .quad .LFE1-.LFB1 # DW_AT_high_pc
+ .uleb128 0x1 # DW_AT_frame_base
+ .byte 0x9c # DW_OP_call_frame_cfa
+ # DW_AT_call_all_tail_calls
+ .long 0xe4 # DW_AT_sibling
+ .uleb128 0x9 # (DIE (0x64) DW_TAG_class_type)
+ .long .LASF9 # DW_AT_name: ""
+ .byte 0x18 # DW_AT_byte_size
+ .byte 0x1 # DW_AT_decl_file (namelessclass.d)
+ .byte 0x1b # DW_AT_decl_line
+ .byte 0xf # DW_AT_decl_column
+ .long 0x64 # DW_AT_containing_type
+ .long 0xd6 # DW_AT_sibling
+ .uleb128 0x2 # (DIE (0x75) DW_TAG_member)
+ .long .LASF2 # DW_AT_name: "__vptr"
+ .long 0xf6 # DW_AT_type
+ .byte 0 # DW_AT_data_member_location
+ # DW_AT_artificial
+ # DW_AT_accessibility (0x1)
+ .uleb128 0x2 # (DIE (0x7f) DW_TAG_member)
+ .long .LASF3 # DW_AT_name: "__monitor"
+ .long 0x100 # DW_AT_type
+ .byte 0x8 # DW_AT_data_member_location
+ # DW_AT_artificial
+ # DW_AT_accessibility (0x1)
+ .uleb128 0xa # (DIE (0x89) DW_TAG_member)
+ .long .LASF4 # DW_AT_name: "this"
+ .byte 0x1 # DW_AT_decl_file (namelessclass.d)
+ .byte 0x1b # DW_AT_decl_line
+ .byte 0xb # DW_AT_decl_column
+ .long 0x102 # DW_AT_type
+ .byte 0x10 # DW_AT_data_member_location
+ .byte 0x1 # DW_AT_accessibility
+ .uleb128 0xb # (DIE (0x97) DW_TAG_subprogram)
+ # DW_AT_external
+ .long .LASF10 # DW_AT_name: "doit"
+ .byte 0x1 # DW_AT_decl_file (namelessclass.d)
+ .byte 0x1c # DW_AT_decl_line
+ .byte 0xe # DW_AT_decl_column
+ .long .LASF11 # DW_AT_linkage_name: "_D6object4mainUZ12__anonclass14doitMFZv"
+ .byte 0x1 # DW_AT_virtuality
+ .uleb128 0x2 # DW_AT_vtable_elem_location
+ .byte 0x10 # DW_OP_constu
+ .uleb128 0x1
+ .long 0x64 # DW_AT_containing_type
+ .byte 0x1 # DW_AT_accessibility
+ .quad .LFB2 # DW_AT_low_pc
+ .quad .LFE2-.LFB2 # DW_AT_high_pc
+ .uleb128 0x1 # DW_AT_frame_base
+ .byte 0x9c # DW_OP_call_frame_cfa
+ # DW_AT_call_all_calls
+ .uleb128 0x1 # (DIE (0xbe) DW_TAG_pointer_type)
+ # DW_AT_byte_size (0x8)
+ .long 0x64 # DW_AT_type
+ .uleb128 0x3 # (DIE (0xc3) DW_TAG_const_type)
+ .long 0xbe # DW_AT_type
+ .uleb128 0x4 # (DIE (0xc8) DW_TAG_formal_parameter)
+ .long .LASF4 # DW_AT_name: "this"
+ .long 0xc3 # DW_AT_type
+ # DW_AT_artificial
+ .uleb128 0x2 # DW_AT_location
+ .byte 0x91 # DW_OP_fbreg
+ .sleb128 -24
+ .byte 0 # end of children of DIE 0x97
+ .byte 0 # end of children of DIE 0x64
+ .uleb128 0xc # (DIE (0xd6) DW_TAG_variable)
+ .ascii "c\0" # DW_AT_name
+ .byte 0x1 # DW_AT_decl_file (namelessclass.d)
+ .byte 0x1b # DW_AT_decl_line
+ .byte 0xb # DW_AT_decl_column
+ .long 0xbe # DW_AT_type
+ .uleb128 0x2 # DW_AT_location
+ .byte 0x91 # DW_OP_fbreg
+ .sleb128 -24
+ .byte 0 # end of children of DIE 0x42
+ .uleb128 0xd # (DIE (0xe4) DW_TAG_base_type)
+ .byte 0x4 # DW_AT_byte_size
+ .byte 0x5 # DW_AT_encoding
+ .ascii "int\0" # DW_AT_name
+ .uleb128 0xe # (DIE (0xeb) DW_TAG_subroutine_type)
+ .long 0xe4 # DW_AT_type
+ .long 0xf6 # DW_AT_sibling
+ .uleb128 0xf # (DIE (0xf4) DW_TAG_unspecified_parameters)
+ .byte 0 # end of children of DIE 0xeb
+ .uleb128 0x1 # (DIE (0xf6) DW_TAG_pointer_type)
+ # DW_AT_byte_size (0x8)
+ .long 0xfb # DW_AT_type
+ .uleb128 0x1 # (DIE (0xfb) DW_TAG_pointer_type)
+ # DW_AT_byte_size (0x8)
+ .long 0xeb # DW_AT_type
+ .uleb128 0x10 # (DIE (0x100) DW_TAG_pointer_type)
+ .byte 0x8 # DW_AT_byte_size
+ .uleb128 0x3 # (DIE (0x102) DW_TAG_const_type)
+ .long 0x100 # DW_AT_type
+ .uleb128 0x11 # (DIE (0x107) DW_TAG_subprogram)
+ # DW_AT_external
+ .long .LASF12 # DW_AT_name: "_d_callfinalizer"
+ .byte 0x1 # DW_AT_decl_file (namelessclass.d)
+ .byte 0x17 # DW_AT_decl_line
+ .byte 0x10 # DW_AT_decl_column
+ .quad .LFB0 # DW_AT_low_pc
+ .quad .LFE0-.LFB0 # DW_AT_high_pc
+ .uleb128 0x1 # DW_AT_frame_base
+ .byte 0x9c # DW_OP_call_frame_cfa
+ # DW_AT_call_all_calls
+ .uleb128 0x4 # (DIE (0x121) DW_TAG_formal_parameter)
+ .long .LASF5 # DW_AT_name: "__param_0"
+ .long 0x100 # DW_AT_type
+ # DW_AT_artificial
+ .uleb128 0x2 # DW_AT_location
+ .byte 0x91 # DW_OP_fbreg
+ .sleb128 -24
+ .byte 0 # end of children of DIE 0x107
+ .byte 0 # end of children of DIE 0xc
+ .section .debug_abbrev,"",@progbits
+.Ldebug_abbrev0:
+ .uleb128 0x1 # (abbrev code)
+ .uleb128 0xf # (TAG: DW_TAG_pointer_type)
+ .byte 0 # DW_children_no
+ .uleb128 0xb # (DW_AT_byte_size)
+ .uleb128 0x21 # (DW_FORM_implicit_const)
+ .sleb128 8
+ .uleb128 0x49 # (DW_AT_type)
+ .uleb128 0x13 # (DW_FORM_ref4)
+ .byte 0
+ .byte 0
+ .uleb128 0x2 # (abbrev code)
+ .uleb128 0xd # (TAG: DW_TAG_member)
+ .byte 0 # DW_children_no
+ .uleb128 0x3 # (DW_AT_name)
+ .uleb128 0xe # (DW_FORM_strp)
+ .uleb128 0x49 # (DW_AT_type)
+ .uleb128 0x13 # (DW_FORM_ref4)
+ .uleb128 0x38 # (DW_AT_data_member_location)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x34 # (DW_AT_artificial)
+ .uleb128 0x19 # (DW_FORM_flag_present)
+ .uleb128 0x32 # (DW_AT_accessibility)
+ .uleb128 0x21 # (DW_FORM_implicit_const)
+ .sleb128 1
+ .byte 0
+ .byte 0
+ .uleb128 0x3 # (abbrev code)
+ .uleb128 0x26 # (TAG: DW_TAG_const_type)
+ .byte 0 # DW_children_no
+ .uleb128 0x49 # (DW_AT_type)
+ .uleb128 0x13 # (DW_FORM_ref4)
+ .byte 0
+ .byte 0
+ .uleb128 0x4 # (abbrev code)
+ .uleb128 0x5 # (TAG: DW_TAG_formal_parameter)
+ .byte 0 # DW_children_no
+ .uleb128 0x3 # (DW_AT_name)
+ .uleb128 0xe # (DW_FORM_strp)
+ .uleb128 0x49 # (DW_AT_type)
+ .uleb128 0x13 # (DW_FORM_ref4)
+ .uleb128 0x34 # (DW_AT_artificial)
+ .uleb128 0x19 # (DW_FORM_flag_present)
+ .uleb128 0x2 # (DW_AT_location)
+ .uleb128 0x18 # (DW_FORM_exprloc)
+ .byte 0
+ .byte 0
+ .uleb128 0x5 # (abbrev code)
+ .uleb128 0x11 # (TAG: DW_TAG_compile_unit)
+ .byte 0x1 # DW_children_yes
+ .uleb128 0x25 # (DW_AT_producer)
+ .uleb128 0xe # (DW_FORM_strp)
+ .uleb128 0x13 # (DW_AT_language)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x3 # (DW_AT_name)
+ .uleb128 0x1f # (DW_FORM_line_strp)
+ .uleb128 0x1b # (DW_AT_comp_dir)
+ .uleb128 0x1f # (DW_FORM_line_strp)
+ .uleb128 0x11 # (DW_AT_low_pc)
+ .uleb128 0x1 # (DW_FORM_addr)
+ .uleb128 0x12 # (DW_AT_high_pc)
+ .uleb128 0x7 # (DW_FORM_data8)
+ .uleb128 0x10 # (DW_AT_stmt_list)
+ .uleb128 0x17 # (DW_FORM_sec_offset)
+ .byte 0
+ .byte 0
+ .uleb128 0x6 # (abbrev code)
+ .uleb128 0x1e # (TAG: DW_TAG_module)
+ .byte 0x1 # DW_children_yes
+ .uleb128 0x3 # (DW_AT_name)
+ .uleb128 0xe # (DW_FORM_strp)
+ .uleb128 0x3a # (DW_AT_decl_file)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x3b # (DW_AT_decl_line)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x39 # (DW_AT_decl_column)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x1 # (DW_AT_sibling)
+ .uleb128 0x13 # (DW_FORM_ref4)
+ .byte 0
+ .byte 0
+ .uleb128 0x7 # (abbrev code)
+ .uleb128 0x3a # (TAG: DW_TAG_imported_module)
+ .byte 0 # DW_children_no
+ .uleb128 0x3a # (DW_AT_decl_file)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x3b # (DW_AT_decl_line)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x18 # (DW_AT_import)
+ .uleb128 0x13 # (DW_FORM_ref4)
+ .byte 0
+ .byte 0
+ .uleb128 0x8 # (abbrev code)
+ .uleb128 0x2e # (TAG: DW_TAG_subprogram)
+ .byte 0x1 # DW_children_yes
+ .uleb128 0x3f # (DW_AT_external)
+ .uleb128 0x19 # (DW_FORM_flag_present)
+ .uleb128 0x3 # (DW_AT_name)
+ .uleb128 0xe # (DW_FORM_strp)
+ .uleb128 0x3a # (DW_AT_decl_file)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x3b # (DW_AT_decl_line)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x39 # (DW_AT_decl_column)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x49 # (DW_AT_type)
+ .uleb128 0x13 # (DW_FORM_ref4)
+ .uleb128 0x11 # (DW_AT_low_pc)
+ .uleb128 0x1 # (DW_FORM_addr)
+ .uleb128 0x12 # (DW_AT_high_pc)
+ .uleb128 0x7 # (DW_FORM_data8)
+ .uleb128 0x40 # (DW_AT_frame_base)
+ .uleb128 0x18 # (DW_FORM_exprloc)
+ .uleb128 0x7c # (DW_AT_call_all_tail_calls)
+ .uleb128 0x19 # (DW_FORM_flag_present)
+ .uleb128 0x1 # (DW_AT_sibling)
+ .uleb128 0x13 # (DW_FORM_ref4)
+ .byte 0
+ .byte 0
+ .uleb128 0x9 # (abbrev code)
+ .uleb128 0x2 # (TAG: DW_TAG_class_type)
+ .byte 0x1 # DW_children_yes
+ .uleb128 0x3 # (DW_AT_name)
+ .uleb128 0xe # (DW_FORM_strp)
+ .uleb128 0xb # (DW_AT_byte_size)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x3a # (DW_AT_decl_file)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x3b # (DW_AT_decl_line)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x39 # (DW_AT_decl_column)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x1d # (DW_AT_containing_type)
+ .uleb128 0x13 # (DW_FORM_ref4)
+ .uleb128 0x1 # (DW_AT_sibling)
+ .uleb128 0x13 # (DW_FORM_ref4)
+ .byte 0
+ .byte 0
+ .uleb128 0xa # (abbrev code)
+ .uleb128 0xd # (TAG: DW_TAG_member)
+ .byte 0 # DW_children_no
+ .uleb128 0x3 # (DW_AT_name)
+ .uleb128 0xe # (DW_FORM_strp)
+ .uleb128 0x3a # (DW_AT_decl_file)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x3b # (DW_AT_decl_line)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x39 # (DW_AT_decl_column)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x49 # (DW_AT_type)
+ .uleb128 0x13 # (DW_FORM_ref4)
+ .uleb128 0x38 # (DW_AT_data_member_location)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x32 # (DW_AT_accessibility)
+ .uleb128 0xb # (DW_FORM_data1)
+ .byte 0
+ .byte 0
+ .uleb128 0xb # (abbrev code)
+ .uleb128 0x2e # (TAG: DW_TAG_subprogram)
+ .byte 0x1 # DW_children_yes
+ .uleb128 0x3f # (DW_AT_external)
+ .uleb128 0x19 # (DW_FORM_flag_present)
+ .uleb128 0x3 # (DW_AT_name)
+ .uleb128 0xe # (DW_FORM_strp)
+ .uleb128 0x3a # (DW_AT_decl_file)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x3b # (DW_AT_decl_line)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x39 # (DW_AT_decl_column)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x6e # (DW_AT_linkage_name)
+ .uleb128 0xe # (DW_FORM_strp)
+ .uleb128 0x4c # (DW_AT_virtuality)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x4d # (DW_AT_vtable_elem_location)
+ .uleb128 0x18 # (DW_FORM_exprloc)
+ .uleb128 0x1d # (DW_AT_containing_type)
+ .uleb128 0x13 # (DW_FORM_ref4)
+ .uleb128 0x32 # (DW_AT_accessibility)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x11 # (DW_AT_low_pc)
+ .uleb128 0x1 # (DW_FORM_addr)
+ .uleb128 0x12 # (DW_AT_high_pc)
+ .uleb128 0x7 # (DW_FORM_data8)
+ .uleb128 0x40 # (DW_AT_frame_base)
+ .uleb128 0x18 # (DW_FORM_exprloc)
+ .uleb128 0x7a # (DW_AT_call_all_calls)
+ .uleb128 0x19 # (DW_FORM_flag_present)
+ .byte 0
+ .byte 0
+ .uleb128 0xc # (abbrev code)
+ .uleb128 0x34 # (TAG: DW_TAG_variable)
+ .byte 0 # DW_children_no
+ .uleb128 0x3 # (DW_AT_name)
+ .uleb128 0x8 # (DW_FORM_string)
+ .uleb128 0x3a # (DW_AT_decl_file)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x3b # (DW_AT_decl_line)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x39 # (DW_AT_decl_column)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x49 # (DW_AT_type)
+ .uleb128 0x13 # (DW_FORM_ref4)
+ .uleb128 0x2 # (DW_AT_location)
+ .uleb128 0x18 # (DW_FORM_exprloc)
+ .byte 0
+ .byte 0
+ .uleb128 0xd # (abbrev code)
+ .uleb128 0x24 # (TAG: DW_TAG_base_type)
+ .byte 0 # DW_children_no
+ .uleb128 0xb # (DW_AT_byte_size)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x3e # (DW_AT_encoding)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x3 # (DW_AT_name)
+ .uleb128 0x8 # (DW_FORM_string)
+ .byte 0
+ .byte 0
+ .uleb128 0xe # (abbrev code)
+ .uleb128 0x15 # (TAG: DW_TAG_subroutine_type)
+ .byte 0x1 # DW_children_yes
+ .uleb128 0x49 # (DW_AT_type)
+ .uleb128 0x13 # (DW_FORM_ref4)
+ .uleb128 0x1 # (DW_AT_sibling)
+ .uleb128 0x13 # (DW_FORM_ref4)
+ .byte 0
+ .byte 0
+ .uleb128 0xf # (abbrev code)
+ .uleb128 0x18 # (TAG: DW_TAG_unspecified_parameters)
+ .byte 0 # DW_children_no
+ .byte 0
+ .byte 0
+ .uleb128 0x10 # (abbrev code)
+ .uleb128 0xf # (TAG: DW_TAG_pointer_type)
+ .byte 0 # DW_children_no
+ .uleb128 0xb # (DW_AT_byte_size)
+ .uleb128 0xb # (DW_FORM_data1)
+ .byte 0
+ .byte 0
+ .uleb128 0x11 # (abbrev code)
+ .uleb128 0x2e # (TAG: DW_TAG_subprogram)
+ .byte 0x1 # DW_children_yes
+ .uleb128 0x3f # (DW_AT_external)
+ .uleb128 0x19 # (DW_FORM_flag_present)
+ .uleb128 0x3 # (DW_AT_name)
+ .uleb128 0xe # (DW_FORM_strp)
+ .uleb128 0x3a # (DW_AT_decl_file)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x3b # (DW_AT_decl_line)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x39 # (DW_AT_decl_column)
+ .uleb128 0xb # (DW_FORM_data1)
+ .uleb128 0x11 # (DW_AT_low_pc)
+ .uleb128 0x1 # (DW_FORM_addr)
+ .uleb128 0x12 # (DW_AT_high_pc)
+ .uleb128 0x7 # (DW_FORM_data8)
+ .uleb128 0x40 # (DW_AT_frame_base)
+ .uleb128 0x18 # (DW_FORM_exprloc)
+ .uleb128 0x7a # (DW_AT_call_all_calls)
+ .uleb128 0x19 # (DW_FORM_flag_present)
+ .byte 0
+ .byte 0
+ .byte 0
+ .section .debug_aranges,"",@progbits
+ .long 0x2c # Length of Address Ranges Info
+ .value 0x2 # DWARF aranges version
+ .long .Ldebug_info0 # Offset of Compilation Unit Info
+ .byte 0x8 # Size of Address
+ .byte 0 # Size of Segment Descriptor
+ .value 0 # Pad to 16 byte boundary
+ .value 0
+ .quad .Ltext0 # Address
+ .quad .Letext0-.Ltext0 # Length
+ .quad 0
+ .quad 0
+ .section .debug_line,"",@progbits
+.Ldebug_line0:
+ .section .debug_str,"MS",@progbits,1
+.LASF7:
+ .string "object"
+.LASF3:
+ .string "__monitor"
+.LASF9:
+ .string ""
+.LASF2:
+ .string "__vptr"
+.LASF5:
+ .string "__param_0"
+.LASF12:
+ .string "_d_callfinalizer"
+.LASF11:
+ .string "_D6object4mainUZ12__anonclass14doitMFZv"
+.LASF4:
+ .string "this"
+.LASF8:
+ .string "main"
+.LASF6:
+ .string "GNU D 14.2.0"
+.LASF10:
+ .string "doit"
+ .section .debug_line_str,"MS",@progbits,1
+.LASF0:
+ .string "namelessclass.d"
+.LASF1:
+ .string "/binutils-gdb/gdb/testsuite/gdb.dlang"
+ .ident "GCC: (GNU) 14.2.0"
+ .section .note.GNU-stack,"",@progbits
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] gdb, d: Fix PR 33200 - Segfault in d_lookup_symbol
2025-07-30 11:42 [PATCH] gdb, d: Fix PR 33200 - Segfault in d_lookup_symbol Iain Buclaw
@ 2025-07-31 16:52 ` Tom Tromey
2025-07-31 18:25 ` Iain Buclaw
0 siblings, 1 reply; 6+ messages in thread
From: Tom Tromey @ 2025-07-31 16:52 UTC (permalink / raw)
To: Iain Buclaw; +Cc: gdb-patches
>>>>> "Iain" == Iain Buclaw <ibuclaw@gdcproject.org> writes:
Hi. Thanks for the patch.
Iain> The asm test was generated by namelessclass.d, with the name of the
Iain> class removed to synthesize the failure.
It's strongly preferred to use the "DWARF assembler", if possible, for
tests like this. There was a script posted to the list that helps
convert a .o to the corresponding Tcl code... which reminds me to go
find out what happened to that.
Iain> + /* If type name is NULL, abandon trying to find this symbol. */
Iain> + if (type->name () == NULL)
Iain> + return {};
We're using 'nullptr' in new code.
Tom
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] gdb, d: Fix PR 33200 - Segfault in d_lookup_symbol
2025-07-31 16:52 ` Tom Tromey
@ 2025-07-31 18:25 ` Iain Buclaw
2025-07-31 23:09 ` [PATCH v2] " Iain Buclaw
0 siblings, 1 reply; 6+ messages in thread
From: Iain Buclaw @ 2025-07-31 18:25 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
Excerpts from Tom Tromey's message of Juli 31, 2025 6:52 pm:
>>>>>> "Iain" == Iain Buclaw <ibuclaw@gdcproject.org> writes:
>
> Hi. Thanks for the patch.
>
> Iain> The asm test was generated by namelessclass.d, with the name of the
> Iain> class removed to synthesize the failure.
>
> It's strongly preferred to use the "DWARF assembler", if possible, for
> tests like this. There was a script posted to the list that helps
> convert a .o to the corresponding Tcl code... which reminds me to go
> find out what happened to that.
>
I did think about using Dwarf::assemble, some guidance might be required
on getting it right if I were to resort to hand writing it.
If such a script were committed to ./contrib, that would be extremely
helpful, there's another patch I have for PR 33201 which again is only
reproducible in gdc's "d21" compiler proper (it's mixed C++ and D, so
the assumption is the D support code is dealing with debug generated by
g++), so a contrived test needs to be written up for that too.
> Iain> + /* If type name is NULL, abandon trying to find this symbol. */
> Iain> + if (type->name () == NULL)
> Iain> + return {};
>
> We're using 'nullptr' in new code.
>
Noted, thanks.
Iain.
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2] gdb, d: Fix PR 33200 - Segfault in d_lookup_symbol
2025-07-31 18:25 ` Iain Buclaw
@ 2025-07-31 23:09 ` Iain Buclaw
2025-08-04 17:22 ` Tom Tromey
2025-08-04 17:43 ` Tom Tromey
0 siblings, 2 replies; 6+ messages in thread
From: Iain Buclaw @ 2025-07-31 23:09 UTC (permalink / raw)
To: gdb-patches; +Cc: tom, Iain Buclaw
After some trial and error, I think I've managed to create a minimal
Dwarf test case.
---
If d_lookup_symbol tries to locate a member variable within a class or
struct type with no a name, type->name() is NULL, so the assignment to
std::string classname crashes gdb.
This can happen in some older versions of gdc, where debug info was
generated too eagerly in complex cases involving types referencing each
other recursively.
This patch adds a guard against this case, allowing the caller of
d_lookup_symbol to search other blocks for the right symbol.
---
gdb/d-namespace.c | 4 ++
gdb/testsuite/gdb.dlang/namelessclass.c | 31 ++++++++++
gdb/testsuite/gdb.dlang/namelessclass.exp | 75 +++++++++++++++++++++++
3 files changed, 110 insertions(+)
create mode 100644 gdb/testsuite/gdb.dlang/namelessclass.c
create mode 100644 gdb/testsuite/gdb.dlang/namelessclass.exp
diff --git a/gdb/d-namespace.c b/gdb/d-namespace.c
index b5e046efaa7..cdfa67a051c 100644
--- a/gdb/d-namespace.c
+++ b/gdb/d-namespace.c
@@ -131,6 +131,10 @@ d_lookup_symbol (const struct language_defn *langdef,
return {};
type = check_typedef (lang_this.symbol->type ()->target_type ());
+ /* If type name is NULL, abandon trying to find this symbol. */
+ if (type->name () == nullptr)
+ return {};
+
classname = type->name ();
nested = name;
}
diff --git a/gdb/testsuite/gdb.dlang/namelessclass.c b/gdb/testsuite/gdb.dlang/namelessclass.c
new file mode 100644
index 00000000000..837e33f5764
--- /dev/null
+++ b/gdb/testsuite/gdb.dlang/namelessclass.c
@@ -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/>. */
+
+/* DWARF will describe this function as being inside an anonymous class within
+ a D module. */
+
+void
+doit (void *this)
+{
+ asm ("doit_label: .globl doit_label");
+}
+
+int
+main (void)
+{
+ doit (0);
+ return 0;
+}
+
diff --git a/gdb/testsuite/gdb.dlang/namelessclass.exp b/gdb/testsuite/gdb.dlang/namelessclass.exp
new file mode 100644
index 00000000000..3246ae33849
--- /dev/null
+++ b/gdb/testsuite/gdb.dlang/namelessclass.exp
@@ -0,0 +1,75 @@
+# 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 nameless classes that may be output by the compiler.
+# PR gdb/33200
+
+load_lib "d-support.exp"
+load_lib "dwarf.exp"
+
+# Do not run in environments which do not support D.
+require dwarf2_support allow_d_tests
+
+standard_testfile .c -dw.S
+
+# Make some DWARF for the test.
+set asm_file [standard_output_file $srcfile2]
+Dwarf::assemble $asm_file {
+ cu {} {
+ compile_unit {
+ {language @DW_LANG_D}
+ } {
+ declare_labels class_label class_ptr_label
+ set class_size 1
+ set ptr_size 4
+
+ module {
+ {name namelessclass}
+ } {
+ class_label: class_type {
+ {byte_size $class_size sdata}
+ {name ""}
+ }
+ class_ptr_label: pointer_type {
+ {byte_size $ptr_size data1}
+ {type :$class_label}
+ }
+ subprogram {
+ {MACRO_AT_func {"doit"}}
+ {external 1 flag_present}
+ } {
+ formal_parameter {
+ {name "this"}
+ {type :$class_ptr_label}
+ }
+ }
+ }
+ }
+ }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+ [list $srcfile $asm_file] {nodebug}] } {
+ return -1
+}
+
+gdb_test_no_output "set language d"
+
+if {![runto "doit"]} {
+ return -1
+}
+
+# Any output is accepted as valid as long as gdb does not segfault.
+gdb_test "print do_not_segfault" ".*"
--
2.43.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2] gdb, d: Fix PR 33200 - Segfault in d_lookup_symbol
2025-07-31 23:09 ` [PATCH v2] " Iain Buclaw
@ 2025-08-04 17:22 ` Tom Tromey
2025-08-04 17:43 ` Tom Tromey
1 sibling, 0 replies; 6+ messages in thread
From: Tom Tromey @ 2025-08-04 17:22 UTC (permalink / raw)
To: Iain Buclaw; +Cc: gdb-patches, tom
>>>>> "Iain" == Iain Buclaw <ibuclaw@gdcproject.org> writes:
Iain> After some trial and error, I think I've managed to create a minimal
Iain> Dwarf test case.
Nice. This looks good to me, thank you.
Approved-By: Tom Tromey <tom@tromey.com>
Tom
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v2] gdb, d: Fix PR 33200 - Segfault in d_lookup_symbol
2025-07-31 23:09 ` [PATCH v2] " Iain Buclaw
2025-08-04 17:22 ` Tom Tromey
@ 2025-08-04 17:43 ` Tom Tromey
1 sibling, 0 replies; 6+ messages in thread
From: Tom Tromey @ 2025-08-04 17:43 UTC (permalink / raw)
To: Iain Buclaw; +Cc: gdb-patches, tom
>>>>> "Iain" == Iain Buclaw <ibuclaw@gdcproject.org> writes:
Iain> After some trial and error, I think I've managed to create a minimal
Iain> Dwarf test case.
I forgot to mention -- if it isn't too late, please add a "Bug:" trailer
with a link to the bug. Thanks.
Tom
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-08-04 17:44 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-07-30 11:42 [PATCH] gdb, d: Fix PR 33200 - Segfault in d_lookup_symbol Iain Buclaw
2025-07-31 16:52 ` Tom Tromey
2025-07-31 18:25 ` Iain Buclaw
2025-07-31 23:09 ` [PATCH v2] " Iain Buclaw
2025-08-04 17:22 ` Tom Tromey
2025-08-04 17:43 ` Tom Tromey
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox