Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Tom Tromey <tom@tromey.com>
To: gdb-patches@sourceware.org
Cc: Tom Tromey <tom@tromey.com>
Subject: [PATCH 1/7] Move lnp_state_machine to new file
Date: Sun, 07 Sep 2025 13:29:13 -0600	[thread overview]
Message-ID: <20250907-breakpoint-cu-expand-v1-1-dd2beef9d475@tromey.com> (raw)
In-Reply-To: <20250907-breakpoint-cu-expand-v1-0-dd2beef9d475@tromey.com>

This patch moves lnp_state_machine and some supporting code to a new
file, dwarf2/line-program.c.  The main benefit of this is shrinking
dwarf2/read.c a bit.
---
 gdb/Makefile.in           |   1 +
 gdb/dwarf2/line-program.c | 731 ++++++++++++++++++++++++++++++++++++++++++++
 gdb/dwarf2/line-program.h |  47 +++
 gdb/dwarf2/read.c         | 757 +---------------------------------------------
 gdb/dwarf2/read.h         |  31 ++
 5 files changed, 814 insertions(+), 753 deletions(-)

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 7654fb1aa04465e570b2e06f9a223b95448b0ca2..d7f8b95cae736255d4d26bc6c85cb8e22675c9f1 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1910,6 +1910,7 @@ DWARF2_SRCS = \
 	dwarf2/index-write.c \
 	dwarf2/leb.c \
 	dwarf2/line-header.c \
+	dwarf2/line-program.c \
 	dwarf2/loc.c \
 	dwarf2/macro.c \
 	dwarf2/parent-map.c \
diff --git a/gdb/dwarf2/line-program.c b/gdb/dwarf2/line-program.c
new file mode 100644
index 0000000000000000000000000000000000000000..72bdd1f5a748ce228114105597b1e5a162254f69
--- /dev/null
+++ b/gdb/dwarf2/line-program.c
@@ -0,0 +1,731 @@
+/* DWARF 2 debugging format support for GDB.
+
+   Copyright (C) 1994-2025 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#include "dwarf2/line-program.h"
+#include "dwarf2/cu.h"
+#include "dwarf2/line-header.h"
+#include "dwarf2/read.h"
+#include "buildsym.h"
+#include "complaints.h"
+#include "filenames.h"
+#include "gdbarch.h"
+
+static void
+dwarf2_debug_line_missing_file_complaint ()
+{
+  complaint (_(".debug_line section has line data without a file"));
+}
+
+static void
+dwarf2_debug_line_missing_end_sequence_complaint ()
+{
+  complaint (_(".debug_line section has line "
+	       "program sequence without an end"));
+}
+
+/* State machine to track the state of the line number program.  */
+
+class lnp_state_machine
+{
+public:
+  /* Initialize a machine state for the start of a line number
+     program.  */
+  lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch, line_header *lh);
+
+  file_entry *current_file ()
+  {
+    /* lh->file_names is 0-based, but the file name numbers in the
+       statement program are 1-based.  */
+    return m_line_header->file_name_at (m_file);
+  }
+
+  /* Record the line in the state machine.  END_SEQUENCE is true if
+     we're processing the end of a sequence.  */
+  void record_line (bool end_sequence);
+
+  /* Check ADDRESS is -1, -2, or zero and less than UNRELOCATED_LOWPC, and if
+     true nop-out rest of the lines in this sequence.  */
+  void check_line_address (struct dwarf2_cu *cu,
+			   const gdb_byte *line_ptr,
+			   unrelocated_addr unrelocated_lowpc,
+			   unrelocated_addr address);
+
+  void handle_set_discriminator (unsigned int discriminator)
+  {
+    m_discriminator = discriminator;
+    m_line_has_non_zero_discriminator |= discriminator != 0;
+  }
+
+  /* Handle DW_LNE_set_address.  */
+  void handle_set_address (unrelocated_addr address)
+  {
+    m_op_index = 0;
+    m_address
+      = (unrelocated_addr) gdbarch_adjust_dwarf2_line (m_gdbarch,
+						       (CORE_ADDR) address,
+						       false);
+  }
+
+  /* Handle DW_LNS_advance_pc.  */
+  void handle_advance_pc (CORE_ADDR adjust);
+
+  /* Handle a special opcode.  */
+  void handle_special_opcode (unsigned char op_code);
+
+  /* Handle DW_LNS_advance_line.  */
+  void handle_advance_line (int line_delta)
+  {
+    advance_line (line_delta);
+  }
+
+  /* Handle DW_LNS_set_file.  */
+  void handle_set_file (file_name_index file);
+
+  /* Handle DW_LNS_negate_stmt.  */
+  void handle_negate_stmt ()
+  {
+    m_flags ^= LEF_IS_STMT;
+  }
+
+  /* Handle DW_LNS_const_add_pc.  */
+  void handle_const_add_pc ();
+
+  /* Handle DW_LNS_fixed_advance_pc.  */
+  void handle_fixed_advance_pc (CORE_ADDR addr_adj)
+  {
+    addr_adj = gdbarch_adjust_dwarf2_line (m_gdbarch, addr_adj, true);
+    m_address = (unrelocated_addr) ((CORE_ADDR) m_address + addr_adj);
+    m_op_index = 0;
+  }
+
+  /* Handle DW_LNS_copy.  */
+  void handle_copy ()
+  {
+    record_line (false);
+    m_discriminator = 0;
+    m_flags &= ~LEF_PROLOGUE_END;
+    m_flags &= ~LEF_EPILOGUE_BEGIN;
+  }
+
+  /* Handle DW_LNE_end_sequence.  */
+  void handle_end_sequence ()
+  {
+    m_currently_recording_lines = true;
+  }
+
+  /* Handle DW_LNS_set_prologue_end.  */
+  void handle_set_prologue_end ()
+  {
+    m_flags |= LEF_PROLOGUE_END;
+  }
+
+  void handle_set_epilogue_begin ()
+  {
+    m_flags |= LEF_EPILOGUE_BEGIN;
+  }
+
+private:
+  /* Advance the line by LINE_DELTA.  */
+  void advance_line (int line_delta)
+  {
+    m_line += line_delta;
+
+    if (line_delta != 0)
+      m_line_has_non_zero_discriminator = m_discriminator != 0;
+  }
+
+  struct dwarf2_cu *m_cu;
+
+  gdbarch *m_gdbarch;
+
+  /* The line number header.  */
+  line_header *m_line_header;
+
+  /* These are part of the standard DWARF line number state machine,
+     and initialized according to the DWARF spec.  */
+
+  unsigned char m_op_index = 0;
+  /* The line table index of the current file.  */
+  file_name_index m_file = 1;
+  unsigned int m_line = 1;
+
+  /* These are initialized in the constructor.  */
+
+  unrelocated_addr m_address;
+  linetable_entry_flags m_flags;
+  unsigned int m_discriminator = 0;
+
+  /* Additional bits of state we need to track.  */
+
+  /* The last file a line number was recorded for.  */
+  struct subfile *m_last_subfile = NULL;
+
+  /* The address of the last line entry.  */
+  unrelocated_addr m_last_address;
+
+  /* Set to true when a previous line at the same address (using
+     m_last_address) had LEF_IS_STMT set in m_flags.  This is reset to false
+     when a line entry at a new address (m_address different to
+     m_last_address) is processed.  */
+  bool m_stmt_at_address = false;
+
+  /* When true, record the lines we decode.  */
+  bool m_currently_recording_lines = true;
+
+  /* The last line number that was recorded, used to coalesce
+     consecutive entries for the same line.  This can happen, for
+     example, when discriminators are present.  PR 17276.  */
+  unsigned int m_last_line = 0;
+  bool m_line_has_non_zero_discriminator = false;
+};
+
+void
+lnp_state_machine::handle_advance_pc (CORE_ADDR adjust)
+{
+  CORE_ADDR addr_adj = (((m_op_index + adjust)
+			 / m_line_header->maximum_ops_per_instruction)
+			* m_line_header->minimum_instruction_length);
+  addr_adj = gdbarch_adjust_dwarf2_line (m_gdbarch, addr_adj, true);
+  m_address = (unrelocated_addr) ((CORE_ADDR) m_address + addr_adj);
+  m_op_index = ((m_op_index + adjust)
+		% m_line_header->maximum_ops_per_instruction);
+}
+
+void
+lnp_state_machine::handle_special_opcode (unsigned char op_code)
+{
+  unsigned char adj_opcode = op_code - m_line_header->opcode_base;
+  unsigned char adj_opcode_d = adj_opcode / m_line_header->line_range;
+  unsigned char adj_opcode_r = adj_opcode % m_line_header->line_range;
+  CORE_ADDR addr_adj = (((m_op_index + adj_opcode_d)
+			 / m_line_header->maximum_ops_per_instruction)
+			* m_line_header->minimum_instruction_length);
+  addr_adj = gdbarch_adjust_dwarf2_line (m_gdbarch, addr_adj, true);
+  m_address = (unrelocated_addr) ((CORE_ADDR) m_address + addr_adj);
+  m_op_index = ((m_op_index + adj_opcode_d)
+		% m_line_header->maximum_ops_per_instruction);
+
+  int line_delta = m_line_header->line_base + adj_opcode_r;
+  advance_line (line_delta);
+  record_line (false);
+  m_discriminator = 0;
+  m_flags &= ~LEF_PROLOGUE_END;
+  m_flags &= ~LEF_EPILOGUE_BEGIN;
+}
+
+void
+lnp_state_machine::handle_set_file (file_name_index file)
+{
+  m_file = file;
+
+  const file_entry *fe = current_file ();
+  if (fe == NULL)
+    dwarf2_debug_line_missing_file_complaint ();
+  else
+    {
+      m_line_has_non_zero_discriminator = m_discriminator != 0;
+      dwarf2_start_subfile (m_cu, *fe, *m_line_header);
+    }
+}
+
+void
+lnp_state_machine::handle_const_add_pc ()
+{
+  CORE_ADDR adjust
+    = (255 - m_line_header->opcode_base) / m_line_header->line_range;
+
+  CORE_ADDR addr_adj
+    = (((m_op_index + adjust)
+	/ m_line_header->maximum_ops_per_instruction)
+       * m_line_header->minimum_instruction_length);
+
+  addr_adj = gdbarch_adjust_dwarf2_line (m_gdbarch, addr_adj, true);
+  m_address = (unrelocated_addr) ((CORE_ADDR) m_address + addr_adj);
+  m_op_index = ((m_op_index + adjust)
+		% m_line_header->maximum_ops_per_instruction);
+}
+
+/* Return non-zero if we should add LINE to the line number table.
+   LINE is the line to add, LAST_LINE is the last line that was added,
+   LAST_SUBFILE is the subfile for LAST_LINE.
+   LINE_HAS_NON_ZERO_DISCRIMINATOR is non-zero if LINE has ever
+   had a non-zero discriminator.
+
+   We have to be careful in the presence of discriminators.
+   E.g., for this line:
+
+     for (i = 0; i < 100000; i++);
+
+   clang can emit four line number entries for that one line,
+   each with a different discriminator.
+   See gdb.dwarf2/dw2-single-line-discriminators.exp for an example.
+
+   However, we want gdb to coalesce all four entries into one.
+   Otherwise the user could stepi into the middle of the line and
+   gdb would get confused about whether the pc really was in the
+   middle of the line.
+
+   Things are further complicated by the fact that two consecutive
+   line number entries for the same line is a heuristic used by gcc
+   to denote the end of the prologue.  So we can't just discard duplicate
+   entries, we have to be selective about it.  The heuristic we use is
+   that we only collapse consecutive entries for the same line if at least
+   one of those entries has a non-zero discriminator.  PR 17276.
+
+   Note: Addresses in the line number state machine can never go backwards
+   within one sequence, thus this coalescing is ok.  */
+
+static int
+dwarf_record_line_p (struct dwarf2_cu *cu,
+		     unsigned int line, unsigned int last_line,
+		     int line_has_non_zero_discriminator,
+		     struct subfile *last_subfile)
+{
+  if (cu->get_builder ()->get_current_subfile () != last_subfile)
+    return 1;
+  if (line != last_line)
+    return 1;
+  /* Same line for the same file that we've seen already.
+     As a last check, for pr 17276, only record the line if the line
+     has never had a non-zero discriminator.  */
+  if (!line_has_non_zero_discriminator)
+    return 1;
+  return 0;
+}
+
+/* Use the CU's builder to record line number LINE beginning at
+   address ADDRESS in the line table of subfile SUBFILE.  */
+
+static void
+dwarf_record_line_1 (struct gdbarch *gdbarch, struct subfile *subfile,
+		     unsigned int line, unrelocated_addr address,
+		     linetable_entry_flags flags,
+		     struct dwarf2_cu *cu)
+{
+  unrelocated_addr addr
+    = unrelocated_addr (gdbarch_addr_bits_remove (gdbarch,
+						  (CORE_ADDR) address));
+
+  if (cu != nullptr)
+    {
+      if (dwarf_line_debug)
+	gdb_printf (gdb_stdlog, "Recording line %u, file %s, address %s\n",
+		    line, lbasename (subfile->name.c_str ()),
+		    paddress (gdbarch, (CORE_ADDR) address));
+
+      cu->get_builder ()->record_line (subfile, line, addr, flags);
+    }
+}
+
+/* Subroutine of dwarf_decode_lines_1 to simplify it.
+   Mark the end of a set of line number records.
+   The arguments are the same as for dwarf_record_line_1.
+   If SUBFILE is NULL the request is ignored.  */
+
+static void
+dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
+		   unrelocated_addr address, struct dwarf2_cu *cu)
+{
+  if (subfile == NULL)
+    return;
+
+  if (dwarf_line_debug)
+    {
+      gdb_printf (gdb_stdlog,
+		  "Finishing current line, file %s, address %s\n",
+		  lbasename (subfile->name.c_str ()),
+		  paddress (gdbarch, (CORE_ADDR) address));
+    }
+
+  dwarf_record_line_1 (gdbarch, subfile, 0, address, LEF_IS_STMT, cu);
+}
+
+void
+lnp_state_machine::record_line (bool end_sequence)
+{
+  if (dwarf_line_debug)
+    {
+      gdb_printf (gdb_stdlog,
+		  "Processing actual line %u: file %u,"
+		  " address %s, is_stmt %u, prologue_end %u,"
+		  " epilogue_begin %u, discrim %u%s\n",
+		  m_line, m_file,
+		  paddress (m_gdbarch, (CORE_ADDR) m_address),
+		  (m_flags & LEF_IS_STMT) != 0,
+		  (m_flags & LEF_PROLOGUE_END) != 0,
+		  (m_flags & LEF_EPILOGUE_BEGIN) != 0,
+		  m_discriminator,
+		  (end_sequence ? "\t(end sequence)" : ""));
+    }
+
+  file_entry *fe = current_file ();
+
+  if (fe == NULL)
+    dwarf2_debug_line_missing_file_complaint ();
+  /* For now we ignore lines not starting on an instruction boundary.
+     But not when processing end_sequence for compatibility with the
+     previous version of the code.  */
+  else if (m_op_index == 0 || end_sequence)
+    {
+      /* When we switch files we insert an end maker in the first file,
+	 switch to the second file and add a new line entry.  The
+	 problem is that the end marker inserted in the first file will
+	 discard any previous line entries at the same address.  If the
+	 line entries in the first file are marked as is-stmt, while
+	 the new line in the second file is non-stmt, then this means
+	 the end marker will discard is-stmt lines so we can have a
+	 non-stmt line.  This means that there are less addresses at
+	 which the user can insert a breakpoint.
+
+	 To improve this we track the last address in m_last_address,
+	 and whether we have seen an is-stmt at this address.  Then
+	 when switching files, if we have seen a stmt at the current
+	 address, and we are switching to create a non-stmt line, then
+	 discard the new line.  */
+      bool file_changed
+	= m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
+      bool ignore_this_line
+	= ((file_changed && !end_sequence && m_last_address == m_address
+	    && ((m_flags & LEF_IS_STMT) == 0)
+	    && m_stmt_at_address)
+	   || (!end_sequence && m_line == 0));
+
+      if ((file_changed && !ignore_this_line) || end_sequence)
+	{
+	  dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
+			     m_currently_recording_lines ? m_cu : nullptr);
+	}
+
+      if (!end_sequence && !ignore_this_line)
+	{
+	  linetable_entry_flags lte_flags = m_flags;
+	  if (m_cu->producer_is_codewarrior ())
+	    lte_flags |= LEF_IS_STMT;
+
+	  if (dwarf_record_line_p (m_cu, m_line, m_last_line,
+				   m_line_has_non_zero_discriminator,
+				   m_last_subfile))
+	    {
+	      buildsym_compunit *builder = m_cu->get_builder ();
+	      dwarf_record_line_1 (m_gdbarch,
+				   builder->get_current_subfile (),
+				   m_line, m_address, lte_flags,
+				   m_currently_recording_lines ? m_cu : nullptr);
+
+	      m_last_subfile = m_cu->get_builder ()->get_current_subfile ();
+	      m_last_line = m_line;
+	    }
+	}
+    }
+
+  /* Track whether we have seen any IS_STMT true at m_address in case we
+     have multiple line table entries all at m_address.  */
+  if (m_last_address != m_address)
+    {
+      m_stmt_at_address = false;
+      m_last_address = m_address;
+    }
+  m_stmt_at_address |= (m_flags & LEF_IS_STMT) != 0;
+}
+
+lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
+				      line_header *lh)
+  : m_cu (cu),
+    m_gdbarch (arch),
+    m_line_header (lh),
+    /* Call `gdbarch_adjust_dwarf2_line' on the initial 0 address as
+       if there was a line entry for it so that the backend has a
+       chance to adjust it and also record it in case it needs it.
+       This is currently used by MIPS code,
+       cf. `mips_adjust_dwarf2_line'.  */
+    m_address ((unrelocated_addr) gdbarch_adjust_dwarf2_line (arch, 0, 0)),
+    m_flags (lh->default_is_stmt ? LEF_IS_STMT : (linetable_entry_flags) 0),
+    m_last_address (m_address)
+{
+}
+
+void
+lnp_state_machine::check_line_address (struct dwarf2_cu *cu,
+				       const gdb_byte *line_ptr,
+				       unrelocated_addr unrelocated_lowpc,
+				       unrelocated_addr address)
+{
+  /* Linkers resolve a symbolic relocation referencing a GC'd function to 0,
+     -1 or -2 (-2 is used by certain lld versions, see
+     https://github.com/llvm/llvm-project/commit/e618ccbf431f6730edb6d1467a127c3a52fd57f7).
+     If ADDRESS is 0, ignoring the opcode will err if the text section is
+     located at 0x0.  In this case, additionally check that if
+     ADDRESS < UNRELOCATED_LOWPC.  */
+
+  if ((address == (unrelocated_addr) 0 && address < unrelocated_lowpc)
+      || address == (unrelocated_addr) -1
+      || address == (unrelocated_addr) -2)
+    {
+      /* This line table is for a function which has been
+	 GCd by the linker.  Ignore it.  PR gdb/12528 */
+
+      struct objfile *objfile = cu->per_objfile->objfile;
+      long line_offset = line_ptr - get_debug_line_section (cu)->buffer;
+
+      complaint (_(".debug_line address at offset 0x%lx is 0 [in module %s]"),
+		 line_offset, objfile_name (objfile));
+      m_currently_recording_lines = false;
+      /* Note: m_currently_recording_lines is left as false until we see
+	 DW_LNE_end_sequence.  */
+    }
+}
+
+/* Subroutine of dwarf_decode_lines to simplify it.
+   Process the line number information in LH.  */
+
+static void
+dwarf_decode_lines_1 (struct line_header *lh, struct dwarf2_cu *cu,
+		      unrelocated_addr lowpc)
+{
+  const gdb_byte *line_ptr, *extended_end;
+  const gdb_byte *line_end;
+  unsigned int bytes_read, extended_len;
+  unsigned char op_code, extended_op;
+  struct objfile *objfile = cu->per_objfile->objfile;
+  bfd *abfd = objfile->obfd.get ();
+  struct gdbarch *gdbarch = objfile->arch ();
+
+  line_ptr = lh->statement_program_start;
+  line_end = lh->statement_program_end;
+
+  /* Read the statement sequences until there's nothing left.  */
+  while (line_ptr < line_end)
+    {
+      /* The DWARF line number program state machine.  Reset the state
+	 machine at the start of each sequence.  */
+      lnp_state_machine state_machine (cu, gdbarch, lh);
+      bool end_sequence = false;
+
+      /* Start a subfile for the current file of the state
+	 machine.  */
+      const file_entry *fe = state_machine.current_file ();
+
+      if (fe != NULL)
+	dwarf2_start_subfile (cu, *fe, *lh);
+
+      /* Decode the table.  */
+      while (line_ptr < line_end && !end_sequence)
+	{
+	  op_code = read_1_byte (abfd, line_ptr);
+	  line_ptr += 1;
+
+	  if (op_code >= lh->opcode_base)
+	    {
+	      /* Special opcode.  */
+	      state_machine.handle_special_opcode (op_code);
+	    }
+	  else switch (op_code)
+	    {
+	    case DW_LNS_extended_op:
+	      extended_len = read_unsigned_leb128 (abfd, line_ptr,
+						   &bytes_read);
+	      line_ptr += bytes_read;
+	      extended_end = line_ptr + extended_len;
+	      extended_op = read_1_byte (abfd, line_ptr);
+	      line_ptr += 1;
+	      if (DW_LNE_lo_user <= extended_op
+		  && extended_op <= DW_LNE_hi_user)
+		{
+		  /* Vendor extension, ignore.  */
+		  line_ptr = extended_end;
+		  break;
+		}
+	      switch (extended_op)
+		{
+		case DW_LNE_end_sequence:
+		  state_machine.handle_end_sequence ();
+		  end_sequence = true;
+		  break;
+		case DW_LNE_set_address:
+		  {
+		    unrelocated_addr address
+		      = cu->header.read_address (abfd, line_ptr, &bytes_read);
+		    line_ptr += bytes_read;
+
+		    state_machine.check_line_address (cu, line_ptr, lowpc,
+						      address);
+		    state_machine.handle_set_address (address);
+		  }
+		  break;
+		case DW_LNE_define_file:
+		  {
+		    const char *cur_file;
+		    unsigned int mod_time, length;
+		    dir_index dindex;
+
+		    cur_file = read_direct_string (abfd, line_ptr,
+						   &bytes_read);
+		    line_ptr += bytes_read;
+		    dindex = (dir_index)
+		      read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+		    line_ptr += bytes_read;
+		    mod_time =
+		      read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+		    line_ptr += bytes_read;
+		    length =
+		      read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+		    line_ptr += bytes_read;
+		    lh->add_file_name (cur_file, dindex, mod_time, length);
+		  }
+		  break;
+		case DW_LNE_set_discriminator:
+		  {
+		    /* The discriminator is not interesting to the
+		       debugger; just ignore it.  We still need to
+		       check its value though:
+		       if there are consecutive entries for the same
+		       (non-prologue) line we want to coalesce them.
+		       PR 17276.  */
+		    unsigned int discr
+		      = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+		    line_ptr += bytes_read;
+
+		    state_machine.handle_set_discriminator (discr);
+		  }
+		  break;
+		default:
+		  complaint (_("mangled .debug_line section"));
+		  return;
+		}
+	      /* Make sure that we parsed the extended op correctly.  If e.g.
+		 we expected a different address size than the producer used,
+		 we may have read the wrong number of bytes.  */
+	      if (line_ptr != extended_end)
+		{
+		  complaint (_("mangled .debug_line section"));
+		  return;
+		}
+	      break;
+	    case DW_LNS_copy:
+	      state_machine.handle_copy ();
+	      break;
+	    case DW_LNS_advance_pc:
+	      {
+		CORE_ADDR adjust
+		  = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+		line_ptr += bytes_read;
+
+		state_machine.handle_advance_pc (adjust);
+	      }
+	      break;
+	    case DW_LNS_advance_line:
+	      {
+		int line_delta
+		  = read_signed_leb128 (abfd, line_ptr, &bytes_read);
+		line_ptr += bytes_read;
+
+		state_machine.handle_advance_line (line_delta);
+	      }
+	      break;
+	    case DW_LNS_set_file:
+	      {
+		file_name_index file
+		  = (file_name_index) read_unsigned_leb128 (abfd, line_ptr,
+							    &bytes_read);
+		line_ptr += bytes_read;
+
+		state_machine.handle_set_file (file);
+	      }
+	      break;
+	    case DW_LNS_set_column:
+	      (void) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+	      line_ptr += bytes_read;
+	      break;
+	    case DW_LNS_negate_stmt:
+	      state_machine.handle_negate_stmt ();
+	      break;
+	    case DW_LNS_set_basic_block:
+	      break;
+	    /* Add to the address register of the state machine the
+	       address increment value corresponding to special opcode
+	       255.  I.e., this value is scaled by the minimum
+	       instruction length since special opcode 255 would have
+	       scaled the increment.  */
+	    case DW_LNS_const_add_pc:
+	      state_machine.handle_const_add_pc ();
+	      break;
+	    case DW_LNS_fixed_advance_pc:
+	      {
+		CORE_ADDR addr_adj = read_2_bytes (abfd, line_ptr);
+		line_ptr += 2;
+
+		state_machine.handle_fixed_advance_pc (addr_adj);
+	      }
+	      break;
+	    case DW_LNS_set_prologue_end:
+	      state_machine.handle_set_prologue_end ();
+	      break;
+	    case DW_LNS_set_epilogue_begin:
+	      state_machine.handle_set_epilogue_begin ();
+	      break;
+	    default:
+	      {
+		/* Unknown standard opcode, ignore it.  */
+		int i;
+
+		for (i = 0; i < lh->standard_opcode_lengths[op_code]; i++)
+		  {
+		    (void) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
+		    line_ptr += bytes_read;
+		  }
+	      }
+	    }
+	}
+
+      if (!end_sequence)
+	dwarf2_debug_line_missing_end_sequence_complaint ();
+
+      /* We got a DW_LNE_end_sequence (or we ran off the end of the buffer,
+	 in which case we still finish recording the last line).  */
+      state_machine.record_line (true);
+    }
+}
+
+/* See dwarf2/line-program.h.  */
+
+void
+dwarf_decode_lines (struct line_header *lh, struct dwarf2_cu *cu,
+		    unrelocated_addr lowpc, int decode_mapping)
+{
+  if (decode_mapping)
+    dwarf_decode_lines_1 (lh, cu, lowpc);
+
+  /* Make sure a symtab is created for every file, even files
+     which contain only variables (i.e. no code with associated
+     line numbers).  */
+  buildsym_compunit *builder = cu->get_builder ();
+  struct compunit_symtab *cust = builder->get_compunit_symtab ();
+
+  for (auto &fe : lh->file_names ())
+    {
+      dwarf2_start_subfile (cu, fe, *lh);
+      subfile *sf = builder->get_current_subfile ();
+
+      if (sf->symtab == nullptr)
+	sf->symtab = allocate_symtab (cust, sf->name.c_str (),
+				      sf->name_for_id.c_str ());
+
+      fe.symtab = sf->symtab;
+    }
+}
diff --git a/gdb/dwarf2/line-program.h b/gdb/dwarf2/line-program.h
new file mode 100644
index 0000000000000000000000000000000000000000..c84403253e635225d7c27d49a91dd1581fa0fa2e
--- /dev/null
+++ b/gdb/dwarf2/line-program.h
@@ -0,0 +1,47 @@
+/* DWARF 2 debugging format support for GDB.
+
+   Copyright (C) 1994-2025 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef GDB_DWARF2_LINE_PROGRAM_H
+#define GDB_DWARF2_LINE_PROGRAM_H
+
+/* Decode the Line Number Program (LNP) for the given line_header
+   structure and CU.  The actual information extracted and the type
+   of structures created from the LNP depends on the value of PST.
+
+   FND holds the CU file name and directory, if known.
+   It is used for relative paths in the line table.
+
+   NOTE: It is important that psymtabs have the same file name (via
+   strcmp) as the corresponding symtab.  Since the directory is not
+   used in the name of the symtab we don't use it in the name of the
+   psymtabs we create.  E.g. expand_line_sal requires this when
+   finding psymtabs to expand.  A good testcase for this is
+   mb-inline.exp.
+
+   LOWPC is the lowest address in CU (or 0 if not known).
+
+   Boolean DECODE_MAPPING specifies we need to fully decode .debug_line
+   for its PC<->lines mapping information.  Otherwise only the filename
+   table is read in.  */
+
+extern void dwarf_decode_lines (struct line_header *lh,
+				struct dwarf2_cu *cu,
+				unrelocated_addr lowpc, int decode_mapping);
+
+#endif /* GDB_DWARF2_LINE_PROGRAM_H */
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 29650c577378f9165898de8fec399f72ffefae9d..efff522d9b36f70fb7f37d426ab744bb6d4085b3 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -38,6 +38,7 @@
 #include "dwarf2/index-cache.h"
 #include "dwarf2/leb.h"
 #include "dwarf2/line-header.h"
+#include "dwarf2/line-program.h"
 #include "dwarf2/dwz.h"
 #include "dwarf2/macro.h"
 #include "dwarf2/die.h"
@@ -787,13 +788,6 @@ static line_header_up dwarf_decode_line_header (sect_offset sect_off,
 						struct dwarf2_cu *cu,
 						const char *comp_dir);
 
-static void dwarf_decode_lines (struct line_header *,
-				struct dwarf2_cu *,
-				unrelocated_addr, int decode_mapping);
-
-static void dwarf2_start_subfile (dwarf2_cu *cu, const file_entry &fe,
-				  const line_header &lh);
-
 static struct symbol *new_symbol (struct die_info *, struct type *,
 				  struct dwarf2_cu *, struct symbol * = NULL);
 
@@ -1062,19 +1056,6 @@ static void process_cu_includes (dwarf2_per_objfile *per_objfile);
 \f
 /* Various complaints about symbol reading that don't abort the process.  */
 
-static void
-dwarf2_debug_line_missing_file_complaint (void)
-{
-  complaint (_(".debug_line section has line data without a file"));
-}
-
-static void
-dwarf2_debug_line_missing_end_sequence_complaint (void)
-{
-  complaint (_(".debug_line section has line "
-	       "program sequence without an end"));
-}
-
 static void
 dwarf2_complex_location_expr_complaint (void)
 {
@@ -15759,9 +15740,9 @@ die_specification (struct die_info *die, struct dwarf2_cu **spec_cu)
     return follow_die_ref (die, spec_attr, spec_cu);
 }
 
-/* A convenience function to find the proper .debug_line section for a CU.  */
+/* See dwarf2/read.h.  */
 
-static struct dwarf2_section_info *
+struct dwarf2_section_info *
 get_debug_line_section (struct dwarf2_cu *cu)
 {
   struct dwarf2_section_info *section;
@@ -15881,739 +15862,9 @@ compute_include_file_name (const struct line_header *lh, const file_entry &fe,
   return include_name;
 }
 
-/* State machine to track the state of the line number program.  */
-
-class lnp_state_machine
-{
-public:
-  /* Initialize a machine state for the start of a line number
-     program.  */
-  lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch, line_header *lh);
-
-  file_entry *current_file ()
-  {
-    /* lh->file_names is 0-based, but the file name numbers in the
-       statement program are 1-based.  */
-    return m_line_header->file_name_at (m_file);
-  }
-
-  /* Record the line in the state machine.  END_SEQUENCE is true if
-     we're processing the end of a sequence.  */
-  void record_line (bool end_sequence);
-
-  /* Check ADDRESS is -1, -2, or zero and less than UNRELOCATED_LOWPC, and if
-     true nop-out rest of the lines in this sequence.  */
-  void check_line_address (struct dwarf2_cu *cu,
-			   const gdb_byte *line_ptr,
-			   unrelocated_addr unrelocated_lowpc,
-			   unrelocated_addr address);
-
-  void handle_set_discriminator (unsigned int discriminator)
-  {
-    m_discriminator = discriminator;
-    m_line_has_non_zero_discriminator |= discriminator != 0;
-  }
-
-  /* Handle DW_LNE_set_address.  */
-  void handle_set_address (unrelocated_addr address)
-  {
-    m_op_index = 0;
-    m_address
-      = (unrelocated_addr) gdbarch_adjust_dwarf2_line (m_gdbarch,
-						       (CORE_ADDR) address,
-						       false);
-  }
-
-  /* Handle DW_LNS_advance_pc.  */
-  void handle_advance_pc (CORE_ADDR adjust);
-
-  /* Handle a special opcode.  */
-  void handle_special_opcode (unsigned char op_code);
-
-  /* Handle DW_LNS_advance_line.  */
-  void handle_advance_line (int line_delta)
-  {
-    advance_line (line_delta);
-  }
-
-  /* Handle DW_LNS_set_file.  */
-  void handle_set_file (file_name_index file);
-
-  /* Handle DW_LNS_negate_stmt.  */
-  void handle_negate_stmt ()
-  {
-    m_flags ^= LEF_IS_STMT;
-  }
-
-  /* Handle DW_LNS_const_add_pc.  */
-  void handle_const_add_pc ();
-
-  /* Handle DW_LNS_fixed_advance_pc.  */
-  void handle_fixed_advance_pc (CORE_ADDR addr_adj)
-  {
-    addr_adj = gdbarch_adjust_dwarf2_line (m_gdbarch, addr_adj, true);
-    m_address = (unrelocated_addr) ((CORE_ADDR) m_address + addr_adj);
-    m_op_index = 0;
-  }
-
-  /* Handle DW_LNS_copy.  */
-  void handle_copy ()
-  {
-    record_line (false);
-    m_discriminator = 0;
-    m_flags &= ~LEF_PROLOGUE_END;
-    m_flags &= ~LEF_EPILOGUE_BEGIN;
-  }
-
-  /* Handle DW_LNE_end_sequence.  */
-  void handle_end_sequence ()
-  {
-    m_currently_recording_lines = true;
-  }
-
-  /* Handle DW_LNS_set_prologue_end.  */
-  void handle_set_prologue_end ()
-  {
-    m_flags |= LEF_PROLOGUE_END;
-  }
-
-  void handle_set_epilogue_begin ()
-  {
-    m_flags |= LEF_EPILOGUE_BEGIN;
-  }
-
-private:
-  /* Advance the line by LINE_DELTA.  */
-  void advance_line (int line_delta)
-  {
-    m_line += line_delta;
-
-    if (line_delta != 0)
-      m_line_has_non_zero_discriminator = m_discriminator != 0;
-  }
-
-  struct dwarf2_cu *m_cu;
-
-  gdbarch *m_gdbarch;
-
-  /* The line number header.  */
-  line_header *m_line_header;
-
-  /* These are part of the standard DWARF line number state machine,
-     and initialized according to the DWARF spec.  */
-
-  unsigned char m_op_index = 0;
-  /* The line table index of the current file.  */
-  file_name_index m_file = 1;
-  unsigned int m_line = 1;
-
-  /* These are initialized in the constructor.  */
-
-  unrelocated_addr m_address;
-  linetable_entry_flags m_flags;
-  unsigned int m_discriminator = 0;
-
-  /* Additional bits of state we need to track.  */
-
-  /* The last file a line number was recorded for.  */
-  struct subfile *m_last_subfile = NULL;
-
-  /* The address of the last line entry.  */
-  unrelocated_addr m_last_address;
-
-  /* Set to true when a previous line at the same address (using
-     m_last_address) had LEF_IS_STMT set in m_flags.  This is reset to false
-     when a line entry at a new address (m_address different to
-     m_last_address) is processed.  */
-  bool m_stmt_at_address = false;
-
-  /* When true, record the lines we decode.  */
-  bool m_currently_recording_lines = true;
-
-  /* The last line number that was recorded, used to coalesce
-     consecutive entries for the same line.  This can happen, for
-     example, when discriminators are present.  PR 17276.  */
-  unsigned int m_last_line = 0;
-  bool m_line_has_non_zero_discriminator = false;
-};
-
-void
-lnp_state_machine::handle_advance_pc (CORE_ADDR adjust)
-{
-  CORE_ADDR addr_adj = (((m_op_index + adjust)
-			 / m_line_header->maximum_ops_per_instruction)
-			* m_line_header->minimum_instruction_length);
-  addr_adj = gdbarch_adjust_dwarf2_line (m_gdbarch, addr_adj, true);
-  m_address = (unrelocated_addr) ((CORE_ADDR) m_address + addr_adj);
-  m_op_index = ((m_op_index + adjust)
-		% m_line_header->maximum_ops_per_instruction);
-}
-
-void
-lnp_state_machine::handle_special_opcode (unsigned char op_code)
-{
-  unsigned char adj_opcode = op_code - m_line_header->opcode_base;
-  unsigned char adj_opcode_d = adj_opcode / m_line_header->line_range;
-  unsigned char adj_opcode_r = adj_opcode % m_line_header->line_range;
-  CORE_ADDR addr_adj = (((m_op_index + adj_opcode_d)
-			 / m_line_header->maximum_ops_per_instruction)
-			* m_line_header->minimum_instruction_length);
-  addr_adj = gdbarch_adjust_dwarf2_line (m_gdbarch, addr_adj, true);
-  m_address = (unrelocated_addr) ((CORE_ADDR) m_address + addr_adj);
-  m_op_index = ((m_op_index + adj_opcode_d)
-		% m_line_header->maximum_ops_per_instruction);
-
-  int line_delta = m_line_header->line_base + adj_opcode_r;
-  advance_line (line_delta);
-  record_line (false);
-  m_discriminator = 0;
-  m_flags &= ~LEF_PROLOGUE_END;
-  m_flags &= ~LEF_EPILOGUE_BEGIN;
-}
-
-void
-lnp_state_machine::handle_set_file (file_name_index file)
-{
-  m_file = file;
-
-  const file_entry *fe = current_file ();
-  if (fe == NULL)
-    dwarf2_debug_line_missing_file_complaint ();
-  else
-    {
-      m_line_has_non_zero_discriminator = m_discriminator != 0;
-      dwarf2_start_subfile (m_cu, *fe, *m_line_header);
-    }
-}
-
-void
-lnp_state_machine::handle_const_add_pc ()
-{
-  CORE_ADDR adjust
-    = (255 - m_line_header->opcode_base) / m_line_header->line_range;
-
-  CORE_ADDR addr_adj
-    = (((m_op_index + adjust)
-	/ m_line_header->maximum_ops_per_instruction)
-       * m_line_header->minimum_instruction_length);
-
-  addr_adj = gdbarch_adjust_dwarf2_line (m_gdbarch, addr_adj, true);
-  m_address = (unrelocated_addr) ((CORE_ADDR) m_address + addr_adj);
-  m_op_index = ((m_op_index + adjust)
-		% m_line_header->maximum_ops_per_instruction);
-}
-
-/* Return non-zero if we should add LINE to the line number table.
-   LINE is the line to add, LAST_LINE is the last line that was added,
-   LAST_SUBFILE is the subfile for LAST_LINE.
-   LINE_HAS_NON_ZERO_DISCRIMINATOR is non-zero if LINE has ever
-   had a non-zero discriminator.
-
-   We have to be careful in the presence of discriminators.
-   E.g., for this line:
-
-     for (i = 0; i < 100000; i++);
-
-   clang can emit four line number entries for that one line,
-   each with a different discriminator.
-   See gdb.dwarf2/dw2-single-line-discriminators.exp for an example.
-
-   However, we want gdb to coalesce all four entries into one.
-   Otherwise the user could stepi into the middle of the line and
-   gdb would get confused about whether the pc really was in the
-   middle of the line.
-
-   Things are further complicated by the fact that two consecutive
-   line number entries for the same line is a heuristic used by gcc
-   to denote the end of the prologue.  So we can't just discard duplicate
-   entries, we have to be selective about it.  The heuristic we use is
-   that we only collapse consecutive entries for the same line if at least
-   one of those entries has a non-zero discriminator.  PR 17276.
-
-   Note: Addresses in the line number state machine can never go backwards
-   within one sequence, thus this coalescing is ok.  */
-
-static int
-dwarf_record_line_p (struct dwarf2_cu *cu,
-		     unsigned int line, unsigned int last_line,
-		     int line_has_non_zero_discriminator,
-		     struct subfile *last_subfile)
-{
-  if (cu->get_builder ()->get_current_subfile () != last_subfile)
-    return 1;
-  if (line != last_line)
-    return 1;
-  /* Same line for the same file that we've seen already.
-     As a last check, for pr 17276, only record the line if the line
-     has never had a non-zero discriminator.  */
-  if (!line_has_non_zero_discriminator)
-    return 1;
-  return 0;
-}
-
-/* Use the CU's builder to record line number LINE beginning at
-   address ADDRESS in the line table of subfile SUBFILE.  */
-
-static void
-dwarf_record_line_1 (struct gdbarch *gdbarch, struct subfile *subfile,
-		     unsigned int line, unrelocated_addr address,
-		     linetable_entry_flags flags,
-		     struct dwarf2_cu *cu)
-{
-  unrelocated_addr addr
-    = unrelocated_addr (gdbarch_addr_bits_remove (gdbarch,
-						  (CORE_ADDR) address));
-
-  if (cu != nullptr)
-    {
-      if (dwarf_line_debug)
-	gdb_printf (gdb_stdlog, "Recording line %u, file %s, address %s\n",
-		    line, lbasename (subfile->name.c_str ()),
-		    paddress (gdbarch, (CORE_ADDR) address));
-
-      cu->get_builder ()->record_line (subfile, line, addr, flags);
-    }
-}
-
-/* Subroutine of dwarf_decode_lines_1 to simplify it.
-   Mark the end of a set of line number records.
-   The arguments are the same as for dwarf_record_line_1.
-   If SUBFILE is NULL the request is ignored.  */
-
-static void
-dwarf_finish_line (struct gdbarch *gdbarch, struct subfile *subfile,
-		   unrelocated_addr address, struct dwarf2_cu *cu)
-{
-  if (subfile == NULL)
-    return;
-
-  if (dwarf_line_debug)
-    {
-      gdb_printf (gdb_stdlog,
-		  "Finishing current line, file %s, address %s\n",
-		  lbasename (subfile->name.c_str ()),
-		  paddress (gdbarch, (CORE_ADDR) address));
-    }
-
-  dwarf_record_line_1 (gdbarch, subfile, 0, address, LEF_IS_STMT, cu);
-}
-
-void
-lnp_state_machine::record_line (bool end_sequence)
-{
-  if (dwarf_line_debug)
-    {
-      gdb_printf (gdb_stdlog,
-		  "Processing actual line %u: file %u,"
-		  " address %s, is_stmt %u, prologue_end %u,"
-		  " epilogue_begin %u, discrim %u%s\n",
-		  m_line, m_file,
-		  paddress (m_gdbarch, (CORE_ADDR) m_address),
-		  (m_flags & LEF_IS_STMT) != 0,
-		  (m_flags & LEF_PROLOGUE_END) != 0,
-		  (m_flags & LEF_EPILOGUE_BEGIN) != 0,
-		  m_discriminator,
-		  (end_sequence ? "\t(end sequence)" : ""));
-    }
-
-  file_entry *fe = current_file ();
-
-  if (fe == NULL)
-    dwarf2_debug_line_missing_file_complaint ();
-  /* For now we ignore lines not starting on an instruction boundary.
-     But not when processing end_sequence for compatibility with the
-     previous version of the code.  */
-  else if (m_op_index == 0 || end_sequence)
-    {
-      /* When we switch files we insert an end maker in the first file,
-	 switch to the second file and add a new line entry.  The
-	 problem is that the end marker inserted in the first file will
-	 discard any previous line entries at the same address.  If the
-	 line entries in the first file are marked as is-stmt, while
-	 the new line in the second file is non-stmt, then this means
-	 the end marker will discard is-stmt lines so we can have a
-	 non-stmt line.  This means that there are less addresses at
-	 which the user can insert a breakpoint.
-
-	 To improve this we track the last address in m_last_address,
-	 and whether we have seen an is-stmt at this address.  Then
-	 when switching files, if we have seen a stmt at the current
-	 address, and we are switching to create a non-stmt line, then
-	 discard the new line.  */
-      bool file_changed
-	= m_last_subfile != m_cu->get_builder ()->get_current_subfile ();
-      bool ignore_this_line
-	= ((file_changed && !end_sequence && m_last_address == m_address
-	    && ((m_flags & LEF_IS_STMT) == 0)
-	    && m_stmt_at_address)
-	   || (!end_sequence && m_line == 0));
-
-      if ((file_changed && !ignore_this_line) || end_sequence)
-	{
-	  dwarf_finish_line (m_gdbarch, m_last_subfile, m_address,
-			     m_currently_recording_lines ? m_cu : nullptr);
-	}
-
-      if (!end_sequence && !ignore_this_line)
-	{
-	  linetable_entry_flags lte_flags = m_flags;
-	  if (m_cu->producer_is_codewarrior ())
-	    lte_flags |= LEF_IS_STMT;
-
-	  if (dwarf_record_line_p (m_cu, m_line, m_last_line,
-				   m_line_has_non_zero_discriminator,
-				   m_last_subfile))
-	    {
-	      buildsym_compunit *builder = m_cu->get_builder ();
-	      dwarf_record_line_1 (m_gdbarch,
-				   builder->get_current_subfile (),
-				   m_line, m_address, lte_flags,
-				   m_currently_recording_lines ? m_cu : nullptr);
-
-	      m_last_subfile = m_cu->get_builder ()->get_current_subfile ();
-	      m_last_line = m_line;
-	    }
-	}
-    }
-
-  /* Track whether we have seen any IS_STMT true at m_address in case we
-     have multiple line table entries all at m_address.  */
-  if (m_last_address != m_address)
-    {
-      m_stmt_at_address = false;
-      m_last_address = m_address;
-    }
-  m_stmt_at_address |= (m_flags & LEF_IS_STMT) != 0;
-}
-
-lnp_state_machine::lnp_state_machine (struct dwarf2_cu *cu, gdbarch *arch,
-				      line_header *lh)
-  : m_cu (cu),
-    m_gdbarch (arch),
-    m_line_header (lh),
-    /* Call `gdbarch_adjust_dwarf2_line' on the initial 0 address as
-       if there was a line entry for it so that the backend has a
-       chance to adjust it and also record it in case it needs it.
-       This is currently used by MIPS code,
-       cf. `mips_adjust_dwarf2_line'.  */
-    m_address ((unrelocated_addr) gdbarch_adjust_dwarf2_line (arch, 0, 0)),
-    m_flags (lh->default_is_stmt ? LEF_IS_STMT : (linetable_entry_flags) 0),
-    m_last_address (m_address)
-{
-}
+/* See dwarf2/read.h.  */
 
 void
-lnp_state_machine::check_line_address (struct dwarf2_cu *cu,
-				       const gdb_byte *line_ptr,
-				       unrelocated_addr unrelocated_lowpc,
-				       unrelocated_addr address)
-{
-  /* Linkers resolve a symbolic relocation referencing a GC'd function to 0,
-     -1 or -2 (-2 is used by certain lld versions, see
-     https://github.com/llvm/llvm-project/commit/e618ccbf431f6730edb6d1467a127c3a52fd57f7).
-     If ADDRESS is 0, ignoring the opcode will err if the text section is
-     located at 0x0.  In this case, additionally check that if
-     ADDRESS < UNRELOCATED_LOWPC.  */
-
-  if ((address == (unrelocated_addr) 0 && address < unrelocated_lowpc)
-      || address == (unrelocated_addr) -1
-      || address == (unrelocated_addr) -2)
-    {
-      /* This line table is for a function which has been
-	 GCd by the linker.  Ignore it.  PR gdb/12528 */
-
-      struct objfile *objfile = cu->per_objfile->objfile;
-      long line_offset = line_ptr - get_debug_line_section (cu)->buffer;
-
-      complaint (_(".debug_line address at offset 0x%lx is 0 [in module %s]"),
-		 line_offset, objfile_name (objfile));
-      m_currently_recording_lines = false;
-      /* Note: m_currently_recording_lines is left as false until we see
-	 DW_LNE_end_sequence.  */
-    }
-}
-
-/* Subroutine of dwarf_decode_lines to simplify it.
-   Process the line number information in LH.  */
-
-static void
-dwarf_decode_lines_1 (struct line_header *lh, struct dwarf2_cu *cu,
-		      unrelocated_addr lowpc)
-{
-  const gdb_byte *line_ptr, *extended_end;
-  const gdb_byte *line_end;
-  unsigned int bytes_read, extended_len;
-  unsigned char op_code, extended_op;
-  struct objfile *objfile = cu->per_objfile->objfile;
-  bfd *abfd = objfile->obfd.get ();
-  struct gdbarch *gdbarch = objfile->arch ();
-
-  line_ptr = lh->statement_program_start;
-  line_end = lh->statement_program_end;
-
-  /* Read the statement sequences until there's nothing left.  */
-  while (line_ptr < line_end)
-    {
-      /* The DWARF line number program state machine.  Reset the state
-	 machine at the start of each sequence.  */
-      lnp_state_machine state_machine (cu, gdbarch, lh);
-      bool end_sequence = false;
-
-      /* Start a subfile for the current file of the state
-	 machine.  */
-      const file_entry *fe = state_machine.current_file ();
-
-      if (fe != NULL)
-	dwarf2_start_subfile (cu, *fe, *lh);
-
-      /* Decode the table.  */
-      while (line_ptr < line_end && !end_sequence)
-	{
-	  op_code = read_1_byte (abfd, line_ptr);
-	  line_ptr += 1;
-
-	  if (op_code >= lh->opcode_base)
-	    {
-	      /* Special opcode.  */
-	      state_machine.handle_special_opcode (op_code);
-	    }
-	  else switch (op_code)
-	    {
-	    case DW_LNS_extended_op:
-	      extended_len = read_unsigned_leb128 (abfd, line_ptr,
-						   &bytes_read);
-	      line_ptr += bytes_read;
-	      extended_end = line_ptr + extended_len;
-	      extended_op = read_1_byte (abfd, line_ptr);
-	      line_ptr += 1;
-	      if (DW_LNE_lo_user <= extended_op
-		  && extended_op <= DW_LNE_hi_user)
-		{
-		  /* Vendor extension, ignore.  */
-		  line_ptr = extended_end;
-		  break;
-		}
-	      switch (extended_op)
-		{
-		case DW_LNE_end_sequence:
-		  state_machine.handle_end_sequence ();
-		  end_sequence = true;
-		  break;
-		case DW_LNE_set_address:
-		  {
-		    unrelocated_addr address
-		      = cu->header.read_address (abfd, line_ptr, &bytes_read);
-		    line_ptr += bytes_read;
-
-		    state_machine.check_line_address (cu, line_ptr, lowpc,
-						      address);
-		    state_machine.handle_set_address (address);
-		  }
-		  break;
-		case DW_LNE_define_file:
-		  {
-		    const char *cur_file;
-		    unsigned int mod_time, length;
-		    dir_index dindex;
-
-		    cur_file = read_direct_string (abfd, line_ptr,
-						   &bytes_read);
-		    line_ptr += bytes_read;
-		    dindex = (dir_index)
-		      read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
-		    line_ptr += bytes_read;
-		    mod_time =
-		      read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
-		    line_ptr += bytes_read;
-		    length =
-		      read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
-		    line_ptr += bytes_read;
-		    lh->add_file_name (cur_file, dindex, mod_time, length);
-		  }
-		  break;
-		case DW_LNE_set_discriminator:
-		  {
-		    /* The discriminator is not interesting to the
-		       debugger; just ignore it.  We still need to
-		       check its value though:
-		       if there are consecutive entries for the same
-		       (non-prologue) line we want to coalesce them.
-		       PR 17276.  */
-		    unsigned int discr
-		      = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
-		    line_ptr += bytes_read;
-
-		    state_machine.handle_set_discriminator (discr);
-		  }
-		  break;
-		default:
-		  complaint (_("mangled .debug_line section"));
-		  return;
-		}
-	      /* Make sure that we parsed the extended op correctly.  If e.g.
-		 we expected a different address size than the producer used,
-		 we may have read the wrong number of bytes.  */
-	      if (line_ptr != extended_end)
-		{
-		  complaint (_("mangled .debug_line section"));
-		  return;
-		}
-	      break;
-	    case DW_LNS_copy:
-	      state_machine.handle_copy ();
-	      break;
-	    case DW_LNS_advance_pc:
-	      {
-		CORE_ADDR adjust
-		  = read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
-		line_ptr += bytes_read;
-
-		state_machine.handle_advance_pc (adjust);
-	      }
-	      break;
-	    case DW_LNS_advance_line:
-	      {
-		int line_delta
-		  = read_signed_leb128 (abfd, line_ptr, &bytes_read);
-		line_ptr += bytes_read;
-
-		state_machine.handle_advance_line (line_delta);
-	      }
-	      break;
-	    case DW_LNS_set_file:
-	      {
-		file_name_index file
-		  = (file_name_index) read_unsigned_leb128 (abfd, line_ptr,
-							    &bytes_read);
-		line_ptr += bytes_read;
-
-		state_machine.handle_set_file (file);
-	      }
-	      break;
-	    case DW_LNS_set_column:
-	      (void) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
-	      line_ptr += bytes_read;
-	      break;
-	    case DW_LNS_negate_stmt:
-	      state_machine.handle_negate_stmt ();
-	      break;
-	    case DW_LNS_set_basic_block:
-	      break;
-	    /* Add to the address register of the state machine the
-	       address increment value corresponding to special opcode
-	       255.  I.e., this value is scaled by the minimum
-	       instruction length since special opcode 255 would have
-	       scaled the increment.  */
-	    case DW_LNS_const_add_pc:
-	      state_machine.handle_const_add_pc ();
-	      break;
-	    case DW_LNS_fixed_advance_pc:
-	      {
-		CORE_ADDR addr_adj = read_2_bytes (abfd, line_ptr);
-		line_ptr += 2;
-
-		state_machine.handle_fixed_advance_pc (addr_adj);
-	      }
-	      break;
-	    case DW_LNS_set_prologue_end:
-	      state_machine.handle_set_prologue_end ();
-	      break;
-	    case DW_LNS_set_epilogue_begin:
-	      state_machine.handle_set_epilogue_begin ();
-	      break;
-	    default:
-	      {
-		/* Unknown standard opcode, ignore it.  */
-		int i;
-
-		for (i = 0; i < lh->standard_opcode_lengths[op_code]; i++)
-		  {
-		    (void) read_unsigned_leb128 (abfd, line_ptr, &bytes_read);
-		    line_ptr += bytes_read;
-		  }
-	      }
-	    }
-	}
-
-      if (!end_sequence)
-	dwarf2_debug_line_missing_end_sequence_complaint ();
-
-      /* We got a DW_LNE_end_sequence (or we ran off the end of the buffer,
-	 in which case we still finish recording the last line).  */
-      state_machine.record_line (true);
-    }
-}
-
-/* Decode the Line Number Program (LNP) for the given line_header
-   structure and CU.  The actual information extracted and the type
-   of structures created from the LNP depends on the value of PST.
-
-   FND holds the CU file name and directory, if known.
-   It is used for relative paths in the line table.
-
-   NOTE: It is important that psymtabs have the same file name (via
-   strcmp) as the corresponding symtab.  Since the directory is not
-   used in the name of the symtab we don't use it in the name of the
-   psymtabs we create.  E.g. expand_line_sal requires this when
-   finding psymtabs to expand.  A good testcase for this is
-   mb-inline.exp.
-
-   LOWPC is the lowest address in CU (or 0 if not known).
-
-   Boolean DECODE_MAPPING specifies we need to fully decode .debug_line
-   for its PC<->lines mapping information.  Otherwise only the filename
-   table is read in.  */
-
-static void
-dwarf_decode_lines (struct line_header *lh, struct dwarf2_cu *cu,
-		    unrelocated_addr lowpc, int decode_mapping)
-{
-  if (decode_mapping)
-    dwarf_decode_lines_1 (lh, cu, lowpc);
-
-  /* Make sure a symtab is created for every file, even files
-     which contain only variables (i.e. no code with associated
-     line numbers).  */
-  buildsym_compunit *builder = cu->get_builder ();
-  struct compunit_symtab *cust = builder->get_compunit_symtab ();
-
-  for (auto &fe : lh->file_names ())
-    {
-      dwarf2_start_subfile (cu, fe, *lh);
-      subfile *sf = builder->get_current_subfile ();
-
-      if (sf->symtab == nullptr)
-	sf->symtab = allocate_symtab (cust, sf->name.c_str (),
-				      sf->name_for_id.c_str ());
-
-      fe.symtab = sf->symtab;
-    }
-}
-
-/* Start a subfile for DWARF.  FILENAME is the name of the file and
-   DIRNAME the name of the source directory which contains FILENAME
-   or NULL if not known.
-   This routine tries to keep line numbers from identical absolute and
-   relative file names in a common subfile.
-
-   Using the `list' example from the GDB testsuite, which resides in
-   /srcdir and compiling it with Irix6.2 cc in /compdir using a filename
-   of /srcdir/list0.c yields the following debugging information for list0.c:
-
-   DW_AT_name:          /srcdir/list0.c
-   DW_AT_comp_dir:      /compdir
-   files.files[0].name: list0.h
-   files.files[0].dir:  /srcdir
-   files.files[1].name: list0.c
-   files.files[1].dir:  /srcdir
-
-   The line number information for list0.c has to end up in a single
-   subfile, so that `break /srcdir/list0.c:1' works as expected.
-   start_subfile will ensure that this happens provided that we pass the
-   concatenation of files.files[1].dir and files.files[1].name as the
-   subfile's name.  */
-
-static void
 dwarf2_start_subfile (dwarf2_cu *cu, const file_entry &fe,
 		      const line_header &lh)
 {
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index 74ec4204cadbc38d9d66f8a6a5cac432297a88ff..12c8666a7350246c71d1bbf97119ce6fa1d25a50 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -1351,4 +1351,35 @@ extern file_and_directory &find_file_and_directory (die_info *die,
 extern const dwarf2_section_info &get_section_for_ref
   (const attribute &attr, dwarf2_cu *cu);
 
+/* A convenience function to find the proper .debug_line section for a CU.  */
+
+extern struct dwarf2_section_info *get_debug_line_section
+  (struct dwarf2_cu *cu);
+
+struct file_entry;
+/* Start a subfile for DWARF.  FILENAME is the name of the file and
+   DIRNAME the name of the source directory which contains FILENAME
+   or NULL if not known.
+   This routine tries to keep line numbers from identical absolute and
+   relative file names in a common subfile.
+
+   Using the `list' example from the GDB testsuite, which resides in
+   /srcdir and compiling it with Irix6.2 cc in /compdir using a filename
+   of /srcdir/list0.c yields the following debugging information for list0.c:
+
+   DW_AT_name:          /srcdir/list0.c
+   DW_AT_comp_dir:      /compdir
+   files.files[0].name: list0.h
+   files.files[0].dir:  /srcdir
+   files.files[1].name: list0.c
+   files.files[1].dir:  /srcdir
+
+   The line number information for list0.c has to end up in a single
+   subfile, so that `break /srcdir/list0.c:1' works as expected.
+   start_subfile will ensure that this happens provided that we pass the
+   concatenation of files.files[1].dir and files.files[1].name as the
+   subfile's name.  */
+extern void dwarf2_start_subfile (dwarf2_cu *cu, const file_entry &fe,
+				  const line_header &lh);
+
 #endif /* GDB_DWARF2_READ_H */

-- 
2.49.0


  reply	other threads:[~2025-09-07 19:30 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-07 19:29 [PATCH 0/7] Shrink dwarf2/read.c and clean up lnp_state_machine Tom Tromey
2025-09-07 19:29 ` Tom Tromey [this message]
2025-09-08 16:04   ` [PATCH 1/7] Move lnp_state_machine to new file Simon Marchi
2025-09-09  0:51     ` Tom Tromey
2025-09-09  0:52       ` Simon Marchi
2025-09-09  2:33         ` Eli Zaretskii
2025-09-09 23:25           ` Tom Tromey
2025-09-07 19:29 ` [PATCH 2/7] Move compute_include_file_name earlier Tom Tromey
2025-09-08 16:08   ` Simon Marchi
2025-09-09  0:46     ` Tom Tromey
2025-09-07 19:29 ` [PATCH 3/7] Boolify line-program.c Tom Tromey
2025-09-08 17:07   ` Simon Marchi
2025-09-09  0:48     ` Tom Tromey
2025-09-09  0:50       ` Simon Marchi
2025-09-09  1:11         ` Tom Tromey
2025-09-09 23:55           ` Tom Tromey
2025-09-10  1:51             ` Simon Marchi
2025-09-07 19:29 ` [PATCH 4/7] Change dwarf_record_line_p to be a method Tom Tromey
2025-09-07 19:29 ` [PATCH 5/7] Add m_builder member to lnp_state_machine Tom Tromey
2025-09-08 17:12   ` Simon Marchi
2025-09-07 19:29 ` [PATCH 6/7] Change dwarf_finish_line to be a method Tom Tromey
2025-09-08 17:14   ` Simon Marchi
2025-09-07 19:29 ` [PATCH 7/7] Change dwarf_record_line_1 " Tom Tromey
2025-09-08 17:17   ` Simon Marchi
2025-09-08 17:18 ` [PATCH 0/7] Shrink dwarf2/read.c and clean up lnp_state_machine Simon Marchi
2025-09-09  0:54   ` Tom Tromey

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250907-breakpoint-cu-expand-v1-1-dd2beef9d475@tromey.com \
    --to=tom@tromey.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox