]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdb/dwarf2: skip unrecognised extended opcodes in line-number programs
authorMatthew Lugg <mlugg@mlugg.co.uk>
Fri, 6 Feb 2026 12:27:16 +0000 (12:27 +0000)
committerTom Tromey <tom@tromey.com>
Sat, 14 Feb 2026 17:24:54 +0000 (10:24 -0700)
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 <tom@tromey.com>
gdb/dwarf2/line-program.c
gdb/testsuite/gdb.dwarf2/dw2-line-unknown-extended-opcode.c [new file with mode: 0644]
gdb/testsuite/gdb.dwarf2/dw2-line-unknown-extended-opcode.exp [new file with mode: 0644]

index 15c4024cb113c98e33abecba667fcee42ca49d50..e6e9235f7bc09190f855cda260be3fee457158f9 100644 (file)
@@ -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 (file)
index 0000000..90829a8
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.  */
+
+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 (file)
index 0000000..552da38
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>.
+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.*"