command will now give an error. Previously the 'b' flag would
always override the 'r' flag.
+maintenance info line-table
+ Add an EPILOGUE-BEGIN column to the output of the command. It indicates
+ if the line is considered the start of the epilgoue, and thus a point at
+ which the frame can be considered destroyed.
+
* New commands
info missing-debug-handler
{
gdb_byte insn;
+ std::optional<CORE_ADDR> epilogue = find_epilogue_using_linetable (pc);
+
+ /* PC is pointing at the next instruction to be executed. If it is
+ equal to the epilogue start, it means we're right before it starts,
+ so the stack is still valid. */
+ if (epilogue)
+ return pc > epilogue;
+
if (target_read_memory (pc, &insn, 1))
return 0; /* Can't read memory at pc. */
e.is_stmt = (flags & LEF_IS_STMT) != 0;
e.set_unrelocated_pc (pc);
e.prologue_end = (flags & LEF_PROLOGUE_END) != 0;
+ e.epilogue_begin = (flags & LEF_EPILOGUE_BEGIN) != 0;
}
\f
/* Indicates this PC is a good location to place a breakpoint at the first
instruction past a function prologue. */
LEF_PROLOGUE_END = 1 << 2,
+
+ /* Indicated that this PC is part of the epilogue of a function, making
+ software watchpoints unreliable. */
+ LEF_EPILOGUE_BEGIN = 1 << 3,
};
DEF_ENUM_FLAGS_TYPE (enum linetable_entry_flag, linetable_entry_flags);
compunit_symtab: simple.cpp ((struct compunit_symtab *) 0x6210000ff450)
symtab: /home/gnu/src/simple.cpp ((struct symtab *) 0x6210000ff4d0)
linetable: ((struct linetable *) 0x62100012b760):
-INDEX LINE ADDRESS IS-STMT PROLOGUE-END
+INDEX LINE ADDRESS IS-STMT PROLOGUE-END EPILOGUE-BEGIN
0 3 0x0000000000401110 Y
-1 4 0x0000000000401114 Y Y
+1 4 0x0000000000401114 Y Y Y
2 9 0x0000000000401120 Y
3 10 0x0000000000401124 Y Y
-4 10 0x0000000000401129
+4 10 0x0000000000401129 Y Y
5 15 0x0000000000401130 Y
6 16 0x0000000000401134 Y Y
7 16 0x0000000000401139
-8 21 0x0000000000401140 Y
+8 21 0x0000000000401140 Y Y
9 22 0x000000000040114f Y Y
-10 22 0x0000000000401154
+10 22 0x0000000000401154 Y
11 END 0x000000000040115a Y
@end smallexample
@noindent
The @samp{IS-STMT} column indicates if the address is a recommended breakpoint
location to represent a line or a statement. The @samp{PROLOGUE-END} column
indicates that a given address is an adequate place to set a breakpoint at the
-first instruction following a function prologue.
+first instruction following a function prologue. The @samp{EPILOGUE-BEGIN}
+column indicates that a given address marks the point where a block's frame is
+destroyed, making local variables hard or impossible to find.
@kindex set always-read-ctf [on|off]
@kindex show always-read-ctf
record_line (false);
m_discriminator = 0;
m_flags &= ~LEF_PROLOGUE_END;
+ m_flags &= ~LEF_EPILOGUE_BEGIN;
}
/* Handle DW_LNE_end_sequence. */
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)
record_line (false);
m_discriminator = 0;
m_flags &= ~LEF_PROLOGUE_END;
+ m_flags &= ~LEF_EPILOGUE_BEGIN;
}
void
{
gdb_printf (gdb_stdlog,
"Processing actual line %u: file %u,"
- " address %s, is_stmt %u, prologue_end %u, discrim %u%s\n",
+ " 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)" : ""));
}
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. */
/* Leave space for 6 digits of index and line number. After that the
tables will just not format as well. */
struct ui_out *uiout = current_uiout;
- ui_out_emit_table table_emitter (uiout, 6, -1, "line-table");
+ ui_out_emit_table table_emitter (uiout, 7, -1, "line-table");
uiout->table_header (6, ui_left, "index", _("INDEX"));
uiout->table_header (6, ui_left, "line", _("LINE"));
uiout->table_header (18, ui_left, "rel-address", _("REL-ADDRESS"));
uiout->table_header (18, ui_left, "unrel-address", _("UNREL-ADDRESS"));
uiout->table_header (7, ui_left, "is-stmt", _("IS-STMT"));
uiout->table_header (12, ui_left, "prologue-end", _("PROLOGUE-END"));
+ uiout->table_header (14, ui_left, "epilogue-begin", _("EPILOGUE-BEGIN"));
uiout->table_body ();
for (int i = 0; i < linetable->nitems; ++i)
CORE_ADDR (item->unrelocated_pc ()));
uiout->field_string ("is-stmt", item->is_stmt ? "Y" : "");
uiout->field_string ("prologue-end", item->prologue_end ? "Y" : "");
+ uiout->field_string ("epilogue-begin", item->epilogue_begin ? "Y" : "");
uiout->text ("\n");
}
}
/* See symtab.h. */
+std::optional<CORE_ADDR>
+find_epilogue_using_linetable (CORE_ADDR func_addr)
+{
+ CORE_ADDR start_pc, end_pc;
+
+ if (!find_pc_partial_function (func_addr, nullptr, &start_pc, &end_pc))
+ return {};
+
+ const struct symtab_and_line sal = find_pc_line (start_pc, 0);
+ if (sal.symtab != nullptr && sal.symtab->language () != language_asm)
+ {
+ struct objfile *objfile = sal.symtab->compunit ()->objfile ();
+ unrelocated_addr unrel_start
+ = unrelocated_addr (start_pc - objfile->text_section_offset ());
+ unrelocated_addr unrel_end
+ = unrelocated_addr (end_pc - objfile->text_section_offset ());
+
+ const linetable *linetable = sal.symtab->linetable ();
+ /* This should find the last linetable entry of the current function.
+ It is probably where the epilogue begins, but since the DWARF 5
+ spec doesn't guarantee it, we iterate backwards through the function
+ until we either find it or are sure that it doesn't exist. */
+ auto it = std::lower_bound
+ (linetable->item, linetable->item + linetable->nitems, unrel_end,
+ [] (const linetable_entry <e, unrelocated_addr pc)
+ {
+ return lte.unrelocated_pc () < pc;
+ });
+
+ while (it->unrelocated_pc () >= unrel_start)
+ {
+ if (it->epilogue_begin)
+ return {it->pc (objfile)};
+ it --;
+ }
+ }
+ return {};
+}
+
+/* See symtab.h. */
+
symbol *
find_function_alias_target (bound_minimal_symbol msymbol)
{
function prologue. */
bool prologue_end : 1;
+ /* True if this location marks the start of the epilogue. */
+ bool epilogue_begin : 1;
+
private:
/* The address for this entry. */
bool group_by_objfile,
const info_sources_filter &filter);
+/* This function returns the address at which the function epilogue begins,
+ according to the linetable.
+
+ Returns an empty optional if EPILOGUE_BEGIN is never set in the
+ linetable. */
+
+std::optional<CORE_ADDR> find_epilogue_using_linetable (CORE_ADDR func_addr);
+
#endif /* !defined(SYMTAB_H) */
--- /dev/null
+/* Copyright 2023 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+void
+__attribute__((used))
+trivial (void)
+{
+ asm ("trivial_label: .global trivial_label"); /* trivial function */
+}
+
+char global;
+
+void
+watch (void)
+{ /* watch start */
+ asm ("watch_label: .global watch_label");
+ asm ("mov $0x0, %rax");
+ int local = 0; /* watch prologue */
+
+ asm ("watch_start: .global watch_start");
+ asm ("mov $0x1, %rax");
+ local = 1; /* watch assign */
+ asm ("watch_reassign: .global watch_reassign");
+ asm ("mov $0x2, %rax");
+ local = 2; /* watch reassign */
+ asm ("watch_end: .global watch_end"); /* watch end */
+}
+
+int
+main (void)
+{ /* main prologue */
+ asm ("main_label: .global main_label");
+ global = 0;
+ asm ("main_fun_call: .global main_fun_call");
+ watch (); /* main function call */
+ asm ("main_epilogue: .global main_epilogue");
+ global = 10;
+ return 0; /* main end */
+}
--- /dev/null
+# Copyright 2022-2023 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# Check that GDB can honor the epilogue_begin flag the compiler can place
+# in the line-table data.
+# We test 2 things: 1. that a software watchpoint triggered in an epilogue
+# is correctly ignored
+# 2. that GDB can mark the same line as both prologue and epilogue
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+require dwarf2_support
+# restricted to x86 to make it simpler to follow a variable
+require is_x86_64_m64_target
+
+standard_testfile .c .S
+
+set trivial_line [gdb_get_line_number "trivial function"]
+set main_prologue [gdb_get_line_number "main prologue"]
+set main_epilogue [gdb_get_line_number "main end"]
+set watch_start_line [gdb_get_line_number "watch start"]
+
+set asm_file [standard_output_file $srcfile2]
+
+# The producer will be set to clang because at the time of writing
+# we only care about epilogues if the producer is clang. When the
+# producer is GCC, variables use CFA locations, so watchpoints can
+# continue working even on epilogues.
+Dwarf::assemble $asm_file {
+ global srcdir subdir srcfile srcfile2
+ global trivial_line main_prologue main_epilogue watch_start_line
+ declare_labels lines_label
+
+ get_func_info main
+ get_func_info trivial
+ get_func_info watch
+
+ cu {} {
+ compile_unit {
+ {language @DW_LANG_C}
+ {name dw2-prologue-end.c}
+ {stmt_list ${lines_label} DW_FORM_sec_offset}
+ {producer "clang version 17.0.1"}
+ } {
+ declare_labels char_label
+
+ char_label: base_type {
+ {name char}
+ {encoding @DW_ATE_signed}
+ {byte_size 1 DW_FORM_sdata}
+ }
+
+ subprogram {
+ {external 1 flag}
+ {name trivial}
+ {low_pc $trivial_start addr}
+ {high_pc "$trivial_start + $trivial_len" addr}
+ }
+ subprogram {
+ {external 1 flag}
+ {name watch}
+ {low_pc $watch_start addr}
+ {high_pc "$watch_start + $watch_len" addr}
+ } {
+ DW_TAG_variable {
+ {name local}
+ {type :$char_label}
+ {DW_AT_location {DW_OP_reg0} SPECIAL_expr}
+ }
+ }
+ subprogram {
+ {external 1 flag}
+ {name main}
+ {low_pc $main_start addr}
+ {high_pc "$main_start + $main_len" addr}
+ }
+ }
+ }
+
+ lines {version 5} lines_label {
+ set diridx [include_dir "${srcdir}/${subdir}"]
+ file_name "$srcfile" $diridx
+
+ program {
+ DW_LNS_set_file $diridx
+ DW_LNE_set_address $trivial_start
+ line $trivial_line
+ DW_LNS_set_prologue_end
+ DW_LNS_set_epilogue_begin
+ DW_LNS_copy
+
+ DW_LNE_set_address watch
+ line $watch_start_line
+ DW_LNS_copy
+
+ DW_LNE_set_address watch_start
+ line [gdb_get_line_number "watch assign"]
+ DW_LNS_set_prologue_end
+ DW_LNS_copy
+
+ DW_LNE_set_address watch_reassign
+ line [gdb_get_line_number "watch reassign"]
+ DW_LNS_set_epilogue_begin
+ DW_LNS_copy
+
+ DW_LNE_set_address watch_end
+ line [gdb_get_line_number "watch end"]
+ DW_LNS_copy
+
+ DW_LNE_set_address $main_start
+ line $main_prologue
+ DW_LNS_set_prologue_end
+ DW_LNS_copy
+
+ DW_LNE_set_address main_fun_call
+ line [gdb_get_line_number "main function call"]
+ DW_LNS_copy
+
+ DW_LNE_set_address main_epilogue
+ line $main_epilogue
+ DW_LNS_set_epilogue_begin
+ DW_LNS_copy
+
+ DW_LNE_end_sequence
+ }
+ }
+}
+
+if { [prepare_for_testing "failed to prepare" ${testfile} \
+ [list $srcfile $asm_file] {nodebug}] } {
+ return -1
+}
+
+if ![runto_main] {
+ return -1
+}
+
+# Moving to the scope with a local variable.
+gdb_breakpoint $watch_start_line
+gdb_continue_to_breakpoint "continuing to function" ".*"
+gdb_test "next" "local = 2.*" "stepping to epilogue"
+
+# Forcing software watchpoints because hardware ones don't care if we
+# are in the epilogue or not.
+gdb_test_no_output "set can-use-hw-watchpoints 0"
+
+# Test that the software watchpoint will not trigger in this case
+gdb_test "watch local" "\[W|w\]atchpoint .: local" "set watchpoint"
+gdb_test "continue" ".*\[W|w\]atchpoint . deleted.*" \
+ "confirm watchpoint doesn't trigger"
+
+# First we test that the trivial function has a line with both a prologue
+# and an epilogue. Do this by finding a line that has 3 Y columns
+set sep "\[ \t\]"
+set hex_number "0x\[0-9a-f\]+"
+gdb_test_multiple "maint info line-table" "test epilogue in linetable" -lbl {
+ -re "\[0-9\]$sep+$trivial_line$sep+$hex_number$sep+$hex_number$sep+Y$sep+Y$sep+Y" {
+ pass $gdb_test_name
+ }
+}
-re ".*linetable: \\(\\(struct linetable \\*\\) 0x0\\):\r\nNo line table.\r\n" {
exp_continue
}
- -re ".*linetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+REL-ADDRESS\[ \t\]+UNREL-ADDRESS\[ \t\]+IS-STMT\[ \t\]PROLOGUE-END *\r\n" {
+ -re ".*linetable: \\(\\(struct linetable \\*\\) $hex\\):\r\nINDEX\[ \t\]+LINE\[ \t\]+REL-ADDRESS\[ \t\]+UNREL-ADDRESS\[ \t\]+IS-STMT\[ \t\]PROLOGUE-END\[ \t\]EPILOGUE-BEGIN *\r\n" {
exp_continue
}
}
_op .byte $_default_is_stmt "default_is_stmt"
_op .byte 1 "line_base"
_op .byte 1 "line_range"
- _op .byte 11 "opcode_base"
+ _op .byte 12 "opcode_base"
# The standard_opcode_lengths table. The number of arguments
- # for each of the standard opcodes. Generating 10 entries here
- # matches the use of 11 in the opcode_base above. These 10
+ # for each of the standard opcodes. Generating 11 entries here
+ # matches the use of 12 in the opcode_base above. These 10
# entries match the 9 standard opcodes for DWARF2 plus
- # DW_LNS_prologue_end from DWARF3.
+ # DW_LNS_prologue_end and DW_LNS_epilogue_begin from DWARF3.
_op .byte 0 "standard opcode 1"
_op .byte 1 "standard opcode 2"
_op .byte 1 "standard opcode 3"
_op .byte 0 "standard opcode 8"
_op .byte 1 "standard opcode 9"
_op .byte 0 "standard opcode 10"
+ _op .byte 0 "standard opcode 11"
# Add a directory entry to the line table header's directory table.
#
_op .byte 0x0a
}
+ proc DW_LNS_set_epilogue_begin {} {
+ _op .byte 0x0b
+ }
+
proc DW_LNS_advance_pc {offset} {
_op .byte 2
_op .uleb128 ${offset}