Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [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