aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
authorTom Tromey <tom@tromey.com>2025-09-06 11:56:40 -0600
committerTom Tromey <tom@tromey.com>2025-09-08 18:51:58 -0600
commit5b17433341939d3f8c18f2fcb5fe270cf9a9f73e (patch)
tree5bbeab73bbfdc221ca065a0f3e9cfcaae7d337c5 /gdb
parentc4a3646be3ef64e3752f112087b6dc8432ee6fdc (diff)
downloadbinutils-5b17433341939d3f8c18f2fcb5fe270cf9a9f73e.zip
binutils-5b17433341939d3f8c18f2fcb5fe270cf9a9f73e.tar.gz
binutils-5b17433341939d3f8c18f2fcb5fe270cf9a9f73e.tar.bz2
Move lnp_state_machine to new file
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. Approved-By: Simon Marchi <simon.marchi@efficios.com>
Diffstat (limited to 'gdb')
-rw-r--r--gdb/Makefile.in2
-rw-r--r--gdb/dwarf2/line-program.c731
-rw-r--r--gdb/dwarf2/line-program.h47
-rw-r--r--gdb/dwarf2/read.c757
-rw-r--r--gdb/dwarf2/read.h31
5 files changed, 815 insertions, 753 deletions
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 7654fb1..689f37f 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1344,6 +1344,7 @@ HFILES_NO_SRCDIR = \
dwarf2/expr.h \
dwarf2/index-cache.h \
dwarf2/index-common.h \
+ dwarf2/line-program.h \
dwarf2/loc.h \
dwarf2/read.h \
dwarf2/read-debug-names.h \
@@ -1910,6 +1911,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 0000000..72bdd1f
--- /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 0000000..c844032
--- /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 29650c5..efff522 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);
@@ -1063,19 +1057,6 @@ static void process_cu_includes (dwarf2_per_objfile *per_objfile);
/* 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)
{
complaint (_("location expression too complex"));
@@ -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 74ec420..b9be541 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -54,6 +54,7 @@ struct dwarf2_cu;
struct dwarf2_debug_sections;
struct dwarf2_per_bfd;
struct dwarf2_per_cu;
+struct file_entry;
struct mapped_index;
struct mapped_debug_names;
struct signatured_type;
@@ -1351,4 +1352,34 @@ 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);
+
+/* 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 */