gdb/dwarf: continue on missing dwo in dwarf5

Message ID 20251225120520.603317-1-liuke.gehry@bytedance.com
State New
Headers
Series gdb/dwarf: continue on missing dwo in dwarf5 |

Commit Message

Sockke Dec. 25, 2025, 12:05 p.m. UTC
  Under DWARF5, when a target CU is missing due to DWP file overflow,
the returned skeleton CU is incorrectly terminated early because of a
type mismatch. This bug prevents call stack backtraces from being
printed in certain debugging sessions, hindering further debugging.

---
 gdb/dwarf2/read.c                             |   5 +
 gdb/testsuite/gdb.dwarf2/missing-dwo-cu-v5.S  | 256 ++++++++++++++++++
 .../gdb.dwarf2/missing-dwo-cu-v5.exp          |  42 +++
 3 files changed, 303 insertions(+)
 create mode 100644 gdb/testsuite/gdb.dwarf2/missing-dwo-cu-v5.S
 create mode 100644 gdb/testsuite/gdb.dwarf2/missing-dwo-cu-v5.exp
  

Comments

Simon Marchi Dec. 28, 2025, 5:42 a.m. UTC | #1
On 2025-12-25 07:05, Sockke wrote:
> Under DWARF5, when a target CU is missing due to DWP file overflow,
> the returned skeleton CU is incorrectly terminated early because of a
> type mismatch. This bug prevents call stack backtraces from being
> printed in certain debugging sessions, hindering further debugging.

First question, and sorry if it has been asked before, but do you or
your employer have a copyright assigment on file with the FSF?  It is
required in order to contribute code to GDB.

Then a technical question: can you expand on what is the sequence of
things going wrong?  That would help me determine if the fix is correct.
Your fix consists of treating skeleton units like complete units and
trying to process them as such.  But I'm not sure it's really valid of
worthwhile doing that.  I doubt that processing a skeleton unit by
itself can really produce anything useful.  So erroring out is perhaps a
better choice.

> ---
>  gdb/dwarf2/read.c                             |   5 +
>  gdb/testsuite/gdb.dwarf2/missing-dwo-cu-v5.S  | 256 ++++++++++++++++++
>  .../gdb.dwarf2/missing-dwo-cu-v5.exp          |  42 +++
>  3 files changed, 303 insertions(+)
>  create mode 100644 gdb/testsuite/gdb.dwarf2/missing-dwo-cu-v5.S
>  create mode 100644 gdb/testsuite/gdb.dwarf2/missing-dwo-cu-v5.exp
> 
> diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
> index 4e2644ba..7b4e96fb 100644
> --- a/gdb/dwarf2/read.c
> +++ b/gdb/dwarf2/read.c
> @@ -4871,6 +4871,7 @@ process_full_comp_unit (dwarf2_cu *cu)
>       type.  */
>    switch (cu->dies->tag)
>      {
> +    case DW_TAG_skeleton_unit:
>      case DW_TAG_compile_unit:
>      case DW_TAG_partial_unit:
>      case DW_TAG_type_unit:
> @@ -5129,6 +5130,7 @@ process_die (struct die_info *die, struct dwarf2_cu *cu)
>      {
>      case DW_TAG_padding:
>        break;
> +    case DW_TAG_skeleton_unit:
>      case DW_TAG_compile_unit:
>      case DW_TAG_partial_unit:
>        read_file_scope (die, cu);
> @@ -18928,6 +18930,9 @@ cutu_reader::prepare_one_comp_unit (struct dwarf2_cu *cu,
>  
>    switch (m_top_level_die->tag)
>      {
> +    case DW_TAG_skeleton_unit:
> +      cu->per_cu->set_unit_type (DW_UT_skeleton);
> +      break;
>      case DW_TAG_compile_unit:
>        cu->per_cu->set_unit_type (DW_UT_compile);
>        break;
> diff --git a/gdb/testsuite/gdb.dwarf2/missing-dwo-cu-v5.S b/gdb/testsuite/gdb.dwarf2/missing-dwo-cu-v5.S
> new file mode 100644
> index 00000000..1b0673c2
> --- /dev/null
> +++ b/gdb/testsuite/gdb.dwarf2/missing-dwo-cu-v5.S
> @@ -0,0 +1,256 @@
> +/* 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/>.  */
> +
> +	.file	"missing-dwo-cu-v5.c"
> +	.text
> +.Ltext0:
> +	.globl	func
> +	.type	func, @function
> +func:
> +.LFB0:
> +	.file 1 "missing-dwo-cu-v5.c"
> +	# missing-dwo-cu-v5.c:20
> +	.loc 1 20 0
> +	.cfi_startproc
> +	pushq	%rbp	#
> +	.cfi_def_cfa_offset 16
> +	.cfi_offset 6, -16
> +	movq	%rsp, %rbp	#,

[snip]

Can you explain what this does, and if it is really necessary to have
such an assembly file?  We would prefer not to add more such
architecture-specific tests, making a test using the DWARF assembler
would be preferred (see testsuite/gdb.dwarf2/calling-convention.exp for
instance).

Simon
  

Patch

diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 4e2644ba..7b4e96fb 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -4871,6 +4871,7 @@  process_full_comp_unit (dwarf2_cu *cu)
      type.  */
   switch (cu->dies->tag)
     {
+    case DW_TAG_skeleton_unit:
     case DW_TAG_compile_unit:
     case DW_TAG_partial_unit:
     case DW_TAG_type_unit:
@@ -5129,6 +5130,7 @@  process_die (struct die_info *die, struct dwarf2_cu *cu)
     {
     case DW_TAG_padding:
       break;
+    case DW_TAG_skeleton_unit:
     case DW_TAG_compile_unit:
     case DW_TAG_partial_unit:
       read_file_scope (die, cu);
@@ -18928,6 +18930,9 @@  cutu_reader::prepare_one_comp_unit (struct dwarf2_cu *cu,
 
   switch (m_top_level_die->tag)
     {
+    case DW_TAG_skeleton_unit:
+      cu->per_cu->set_unit_type (DW_UT_skeleton);
+      break;
     case DW_TAG_compile_unit:
       cu->per_cu->set_unit_type (DW_UT_compile);
       break;
diff --git a/gdb/testsuite/gdb.dwarf2/missing-dwo-cu-v5.S b/gdb/testsuite/gdb.dwarf2/missing-dwo-cu-v5.S
new file mode 100644
index 00000000..1b0673c2
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/missing-dwo-cu-v5.S
@@ -0,0 +1,256 @@ 
+/* 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/>.  */
+
+	.file	"missing-dwo-cu-v5.c"
+	.text
+.Ltext0:
+	.globl	func
+	.type	func, @function
+func:
+.LFB0:
+	.file 1 "missing-dwo-cu-v5.c"
+	# missing-dwo-cu-v5.c:20
+	.loc 1 20 0
+	.cfi_startproc
+	pushq	%rbp	#
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq	%rsp, %rbp	#,
+	.cfi_def_cfa_register 6
+	movl	%edi, -4(%rbp)	# arg, arg
+	# missing-dwo-cu-v5.c:21
+	.loc 1 21 0
+	movl	-4(%rbp), %eax	# arg, tmp61
+	addl	$1, %eax	#, D.1617
+	# missing-dwo-cu-v5.c:22
+	.loc 1 22 0
+	popq	%rbp	#
+	.cfi_def_cfa 7, 8
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	func, .-func
+	.globl	main
+	.type	main, @function
+main:
+.LFB1:
+	# missing-dwo-cu-v5.c:26
+	.loc 1 26 0
+	.cfi_startproc
+	pushq	%rbp	#
+	.cfi_def_cfa_offset 16
+	.cfi_offset 6, -16
+	movq	%rsp, %rbp	#,
+	.cfi_def_cfa_register 6
+	# missing-dwo-cu-v5.c:27
+	.loc 1 27 0
+	movl	$-1, %edi	#,
+	call	func	#
+	# missing-dwo-cu-v5.c:28
+	.loc 1 28 0
+	popq	%rbp	#
+	.cfi_def_cfa 7, 8
+	ret
+	.cfi_endproc
+.LFE1:
+	.size	main, .-main
+.Letext0:
+	.section	.debug_info.dwo,"e",@progbits
+# Removed the DWO CU.
+	.section	.debug_info,"",@progbits
+.Lskeleton_debug_info0:
+	.4byte	.Lskeleton_debug_info0_end - .Lskeleton_debug_info0 - 4 # Length of Compilation Unit Info
+	.2byte	0x5	# DWARF version number
+	.byte	0x4	# DW_UT_skeleton
+	.byte	0x8	# Address size
+	.4byte	.Lskeleton_debug_abbrev0	# Offset Into Abbrev. Section
+	.8byte	0x0123456789abcdef	# Signature tying to the DWO CU
+	.uleb128 0x1	# (DIE) DW_TAG_skeleton_unit
+	.8byte	.Ltext0	# DW_AT_low_pc
+	.8byte	.Letext0-.Ltext0	# DW_AT_high_pc
+	.4byte	.Ldebug_line0	# DW_AT_stmt_list
+	.ascii "/tmp/src/gdb/testsuite\0"	# DW_AT_comp_dir
+	.asciz DWO	# DW_AT_dwo_name
+	.4byte	.Ldebug_addr0_begin	# DW_AT_GNU_addr_base
+	.4byte	.Ldebug_ranges0_begin	# DW_AT_GNU_ranges_base
+	.8byte	0x0123456789abcdef	# DW_AT_GNU_dwo_id (for compatibility)
+.Lskeleton_debug_info0_end:
+	.section	.debug_abbrev,"",@progbits
+.Lskeleton_debug_abbrev0:
+	.uleb128 0x1	# (abbrev code)
+	.uleb128 0x4a	# (TAG: DW_TAG_skeleton_unit)
+	.byte	0	# DW_children_no
+	.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)
+	.uleb128 0x1b	# (DW_AT_comp_dir)
+	.uleb128 0x8	# (DW_FORM_string)
+	.uleb128 0x76	# (DW_AT_dwo_name)
+	.uleb128 0x8	# (DW_FORM_string)
+	.uleb128 0x2133	# (DW_AT_GNU_addr_base)
+	.uleb128 0x17	# (DW_FORM_sec_offset)
+	.uleb128 0x2132	# (DW_AT_GNU_ranges_base)
+	.uleb128 0x17	# (DW_FORM_sec_offset)
+	.uleb128 0x2131	# (DW_AT_GNU_dwo_id)
+	.uleb128 0x7	# (DW_FORM_data8)
+	.byte	0
+	.byte	0
+	.byte	0	# end of skeleton .debug_abbrev
+	.section	.debug_abbrev.dwo,"e",@progbits
+.Ldebug_abbrev0:
+	.uleb128 0x1	# (abbrev code)
+	.uleb128 0x11	# (TAG: DW_TAG_compile_unit)
+	.byte	0x1	# DW_children_yes
+	.uleb128 0x25	# (DW_AT_producer)
+	.uleb128 0x8	# (DW_FORM_string)
+	.uleb128 0x13	# (DW_AT_language)
+	.uleb128 0xb	# (DW_FORM_data1)
+	.uleb128 0x3	# (DW_AT_name)
+	.uleb128 0x8	# (DW_FORM_string)
+	.uleb128 0x1b	# (DW_AT_comp_dir)
+	.uleb128 0x8	# (DW_FORM_string)
+	.uleb128 0x2131	# (DW_AT_GNU_dwo_id)
+	.uleb128 0x7	# (DW_FORM_data8)
+	.byte	0
+	.byte	0
+	.uleb128 0x2	# (abbrev code)
+	.uleb128 0x2e	# (TAG: DW_TAG_subprogram) for func
+	.byte	0x1	# DW_children_yes
+	.uleb128 0x3f	# (DW_AT_external)
+	.uleb128 0x19	# (DW_FORM_flag_present)
+	.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 0x27	# (DW_AT_prototyped)
+	.uleb128 0x19	# (DW_FORM_flag_present)
+	.uleb128 0x49	# (DW_AT_type)
+	.uleb128 0x13	# (DW_FORM_ref4)
+	.uleb128 0x11	# (DW_AT_low_pc)
+	.uleb128 0x1f01	# (DW_FORM_GNU_addr_index)
+	.uleb128 0x12	# (DW_AT_high_pc)
+	.uleb128 0x7	# (DW_FORM_data8)
+	.uleb128 0x40	# (DW_AT_frame_base)
+	.uleb128 0x18	# (DW_FORM_exprloc)
+	.uleb128 0x1	# (DW_AT_sibling)
+	.uleb128 0x13	# (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x3	# (abbrev code)
+	.uleb128 0x5	# (TAG: DW_TAG_formal_parameter)
+	.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 0x49	# (DW_AT_type)
+	.uleb128 0x13	# (DW_FORM_ref4)
+	.uleb128 0x2	# (DW_AT_location)
+	.uleb128 0x18	# (DW_FORM_exprloc)
+	.byte	0
+	.byte	0
+	.uleb128 0x4	# (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 0x5	# (abbrev code)
+	.uleb128 0x2e	# (TAG: DW_TAG_subprogram) for main
+	.byte	0	# DW_children_no
+	.uleb128 0x3f	# (DW_AT_external)
+	.uleb128 0x19	# (DW_FORM_flag_present)
+	.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 0x49	# (DW_AT_type)
+	.uleb128 0x13	# (DW_FORM_ref4)
+	.uleb128 0x11	# (DW_AT_low_pc)
+	.uleb128 0x1f01	# (DW_FORM_GNU_addr_index)
+	.uleb128 0x12	# (DW_AT_high_pc)
+	.uleb128 0x7	# (DW_FORM_data8)
+	.uleb128 0x40	# (DW_AT_frame_base)
+	.uleb128 0x18	# (DW_FORM_exprloc)
+	.byte	0
+	.byte	0
+	.byte	0	# end of .debug_abbrev.dwo
+	.section	.debug_line,"",@progbits
+.Ldebug_line0:
+	.section	.debug_line.dwo,"e",@progbits
+.Lskeleton_debug_line0:
+	.4byte	.LELT0-.LSLT0	# Length of Source Line Info
+.LSLT0:
+	.2byte	0x4	# DWARF Version
+	.4byte	.LELTP0-.LASLTP0	# Prolog Length
+.LASLTP0:
+	.byte	0x1	# Minimum Instruction Length
+	.byte	0x1	# Maximum Operations Per Instruction
+	.byte	0x1	# Default is_stmt_start flag
+	.byte	0xf6	# Line Base Value (Special Opcodes)
+	.byte	0xf5	# Line Range Value (Special Opcodes)
+	.byte	0xa	# Special Opcode Base
+	.byte	0	# opcode: 0x1 has 0 args
+	.byte	0x1	# opcode: 0x2 has 1 args
+	.byte	0x1	# opcode: 0x3 has 1 args
+	.byte	0x1	# opcode: 0x4 has 1 args
+	.byte	0x1	# opcode: 0x5 has 1 args
+	.byte	0	# opcode: 0x6 has 0 args
+	.byte	0	# opcode: 0x7 has 0 args
+	.byte	0	# opcode: 0x8 has 0 args
+	.byte	0x1	# opcode: 0x9 has 1 args
+	.byte	0	# End directory table
+	.ascii "missing-dwo-cu-v5.c\0"	# File Entry: 0x1
+	.uleb128 0
+	.uleb128 0
+	.uleb128 0
+	.byte	0	# End file name table
+.LELTP0:
+.LELT0:
+	.section	.debug_addr,"",@progbits
+.Ldebug_addr0:
+	# Shift the real entries down by a non-zero amount to test
+	# DW_AT_GNU_addr_base.
+	.8byte	0,0
+.Ldebug_addr0_begin:
+	.8byte	.LFB0	# DW_AT_low_pc
+	.8byte	.LFB1	# DW_AT_low_pc
+	.section	.debug_ranges,"",@progbits
+.Ldebug_ranges0:
+	# Shift the real entries down by a non-zero amount to test
+	# DW_AT_GNU_ranges_base.
+	.8byte	0,0
+.Ldebug_ranges0_begin:
+	# Note: Since the DW_TAG_compile_unit specifies low_pc, that sets
+	# the base address, and thus we have to subtract it here.
+	.8byte	.LFB0 - .Ltext0	# Offset 0
+	.8byte	.LFE0 - .Ltext0
+	.8byte	0
+	.8byte	0
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gdb/testsuite/gdb.dwarf2/missing-dwo-cu-v5.exp b/gdb/testsuite/gdb.dwarf2/missing-dwo-cu-v5.exp
new file mode 100644
index 00000000..e65c2466
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/missing-dwo-cu-v5.exp
@@ -0,0 +1,42 @@ 
+# 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/>.
+
+load_lib dwarf.exp
+
+# We run objcopy locally to split out the .dwo file.
+require {!is_remote host}
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+require dwarf2_support
+
+# This test can only be run on x86-64 targets.
+require is_x86_64_m64_target
+
+# Explicitly use the shared assembly source.
+standard_testfile .S
+set obj [standard_output_file "${testfile}.o"]
+set dwo [standard_output_file "${testfile}.dwo"]
+if [build_executable_and_dwo_files "$testfile.exp" "${binfile}" {nodebug} \
+	[list $srcfile \
+	     [list nodebug split-dwo additional_flags=-DDWO=\"$dwo\"] \
+	     $obj]] {
+    return
+}
+
+# Start a fresh GDB without loading an executable.
+clean_restart
+gdb_test_lines "symbol-file $binfile" "" {Reading symbols from .*} \
+    -re-not {DWARF Error: unexpected tag 'DW_TAG_skeleton_unit' at offset}
+gdb_test "ptype main" "type = <unknown .*"