From: Matthew Lugg Date: Fri, 6 Feb 2026 12:27:16 +0000 (+0000) Subject: gdb/dwarf2: skip unrecognised extended opcodes in line-number programs X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1daba1c5ceb27d25265ba2801f5be307d21a6abd;p=thirdparty%2Fbinutils-gdb.git gdb/dwarf2: skip unrecognised extended opcodes in line-number programs Although the DWARF 5 specification does not outright state that consumers should ignore unrecognised opcodes, it does make sure that this is possible (by always encoding the length of operands), and in fact states the following: > The DWARF format is organized so that a consumer can skip over data > which it does not recognize. This may allow a consumer to read and > process files generated according to a later version of this standard > or which contain vendor extensions, albeit possibly in a degraded > manner. In addition, it is mentioned in the body of DWARF Issue 180426.2 that producers can, today, include padding bytes in line number programs by emitting an unused extended opcode: > In a related thread on dwarf-discuss, Cary pointed out that given how > DWARF works, any undefined extended opcode could be used for this > purpose. [...] consumers already know how to skip an unrecognized > extended opcode. Previously, if GDB encountered an unknown extended opcode in a DWARF line number program, it would stop evaluating the program with an error. This commit causes it to instead skip the extended opcode, leaving the virtual machine state unmodified. As well as being supported by the quotes above, this is consistent with GDB's *existing* handling of the case where a *standard* LNP opcode is unrecognised. The motivation for this change is binaries emitted by the Zig compiler, which currently uses extended opcode 0 for padding bytes (the use case being addressed in DWARF 6 by the DWARF issue referenced above). LLDB is capable of parsing these line number programs, but GDB was not prior to this commit. Tested by running the test suite (including the added test case) and confirming that every test which previously passed continues to pass. Also manually tested on a binary produced by the Zig compiler, where previously GDB was missing some line number information, but now has complete and correct information. Approved-By: Tom Tromey --- diff --git a/gdb/dwarf2/line-program.c b/gdb/dwarf2/line-program.c index 15c4024cb11..e6e9235f7bc 100644 --- a/gdb/dwarf2/line-program.c +++ b/gdb/dwarf2/line-program.c @@ -672,8 +672,19 @@ dwarf_decode_lines (struct dwarf2_cu *cu, unrelocated_addr lowpc) } break; default: - complaint (_("mangled .debug_line section")); - return; + complaint (_("unrecognized line number program opcode %d"), op_code); + /* Unknown extended opcode, ignore it. */ + line_ptr = extended_end; + break; + case 0: + /* This is also an unknown opcode, but this one is used by + the Zig compiler (perhaps others too?) specifically as a + "padding" instruction. Also, although it is technically + available, future DWARF revisions are very unlikely to use + opcode 0. Therefore, it's better to elide the warning in + this case, and ignore the instruction silently. */ + line_ptr = extended_end; + break; } /* Make sure that we parsed the extended op correctly. If e.g. we expected a different address size than the producer used, diff --git a/gdb/testsuite/gdb.dwarf2/dw2-line-unknown-extended-opcode.c b/gdb/testsuite/gdb.dwarf2/dw2-line-unknown-extended-opcode.c new file mode 100644 index 00000000000..90829a8f3b0 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-line-unknown-extended-opcode.c @@ -0,0 +1,32 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 2026 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 . */ + +void +foo (void) +{ + +} + +int +main() +{ + asm ("main_label: .globl main_label"); + foo (); + + asm ("main_label_2: .globl main_label_2"); + return 0; +} diff --git a/gdb/testsuite/gdb.dwarf2/dw2-line-unknown-extended-opcode.exp b/gdb/testsuite/gdb.dwarf2/dw2-line-unknown-extended-opcode.exp new file mode 100644 index 00000000000..552da389598 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/dw2-line-unknown-extended-opcode.exp @@ -0,0 +1,89 @@ +# Copyright 2026 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 . +load_lib dwarf.exp + +# This test can only be run on targets which support DWARF-2 and use gas. +require dwarf2_support + +# The .c files use __attribute__. +require is_c_compiler_gcc + +standard_testfile .c -dw.S + +set asm_file [standard_output_file $srcfile2] +set f [open $asm_file "w"] +Dwarf::assemble { file_id $f } { + declare_labels Llines + global srcdir subdir srcfile + + get_func_info main + + cu {} { + compile_unit { + DW_AT_language @DW_LANG_C + DW_AT_name dw2-line-unknown-extended-opcode.exp + DW_AT_stmt_list $Llines DW_FORM_sec_offset + } { + subprogram { + DW_AT_external 1 flag + MACRO_AT_func {main} + } + } + } + + lines {version 2 default_is_stmt 1} Llines { + include_dir "${srcdir}/${subdir}" + file_name "$srcfile" 1 + + program { + global f + + DW_LNE_set_address $main_start + line 26 + DW_LNS_copy + + # Emit non-standard (but not in the vendor-specific range) extended + # opcode 0 with 15 dummy operand bytes which GDB should ignore. + puts $f " .byte 0" + puts $f " .uleb128 16" + puts $f " .byte 0" + puts $f " .fill 15, 1, 42" + + DW_LNE_set_address main_label + line 28 + DW_LNS_copy + + DW_LNE_set_address main_label_2 + line 31 + DW_LNS_copy + + DW_LNE_set_address $main_end + DW_LNE_end_sequence + } + } +} +close $f + +if { [prepare_for_testing "failed to prepare" ${testfile} \ + [list $srcfile $asm_file] {nodebug}] } { + return +} + +if {![runto_main]} { + return +} + +gdb_breakpoint "$srcfile:31" +gdb_continue_to_breakpoint "return from main" "\[^\r\n\]*:31\r\n.*"