[gdb] Add maint set skip-prologue With aarch64, I run into: ... (gdb) break callee.adb:22^M Breakpoint 1 at 0x401f78: file caller.adb, line 21.^M (gdb) FAIL: gdb.ada/inline-section-gc.exp: break callee.adb:22 ... while this is expected (copied from x86_64): ... (gdb) break callee.adb:22^M Breakpoint 1 at 0x401f10: file callee.adb, line 22.^M (gdb) PASS: gdb.ada/inline-section-gc.exp: break callee.adb:22 ... The difference comes from the gdbarch_skip_prologue implementation. For x86_64, we have: ... if (find_pc_partial_function (start_pc, NULL, &func_addr, NULL)) { CORE_ADDR post_prologue_pc = skip_prologue_using_sal (gdbarch, func_addr); struct compunit_symtab *cust = find_pc_compunit_symtab (func_addr); /* LLVM backend (Clang/Flang) always emits a line note before the prologue and another one after. We trust clang and newer Intel compilers to emit usable line notes. */ if (post_prologue_pc && (cust != NULL && cust->producer () != nullptr && (producer_is_llvm (cust->producer ()) || producer_is_icc_ge_19 (cust->producer ())))) return std::max (start_pc, post_prologue_pc); } ... and because the producer is GCC, we don't trust the line table result, and use the architecture-specific prologue analyzer, which skips no insns. For aarch64, we have: ... if (find_pc_partial_function (pc, NULL, &func_addr, NULL)) { CORE_ADDR post_prologue_pc = skip_prologue_using_sal (gdbarch, func_addr); if (post_prologue_pc != 0) return std::max (pc, post_prologue_pc); } ... and consequently we trust the line table result and skip past a few insns, which breaks test-case assumptions. Add a command "maint set skip-prologue line-table", that forces usage of the line-table result if available, which allows us to reproduce the same problem on x86_64-linux. Also add a command "maint set skip-prologue none", that forces skipping no insn, which allows us to fix the FAIL on aarch64. Likewise, fix one more test-case. Tested on x86_64-linux and aarch64-linux. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29419 Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=29409 --- gdb/arch-utils.c | 27 ++++++++++++- gdb/maint.c | 59 +++++++++++++++++++++++++++++ gdb/maint.h | 9 +++++ gdb/testsuite/gdb.ada/inline-section-gc.exp | 2 + gdb/testsuite/gdb.opt/inline-small-func.exp | 2 + 5 files changed, 98 insertions(+), 1 deletion(-) diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c index 9bd4f0ddae6..02520e49861 100644 --- a/gdb/arch-utils.c +++ b/gdb/arch-utils.c @@ -36,6 +36,7 @@ #include "reggroups.h" #include "auxv.h" #include "observable.h" +#include "maint.h" #include "gdbsupport/version.h" @@ -1041,6 +1042,23 @@ default_print_insn (bfd_vma memaddr, disassemble_info *info) return (*disassemble_fn) (memaddr, info); } +/* Helper function for gdbarch_skip_prologue_noexcept. */ + +static CORE_ADDR +gdbarch_skip_prologue_line_table (struct gdbarch *gdbarch, CORE_ADDR pc) +{ + CORE_ADDR func_addr; + + if (find_pc_partial_function (pc, NULL, &func_addr, NULL)) + { + CORE_ADDR post_prologue_pc + = skip_prologue_using_sal (gdbarch, func_addr); + return std::max (pc, post_prologue_pc); + } + + return gdbarch_skip_prologue (gdbarch, pc); +} + /* See arch-utils.h. */ CORE_ADDR @@ -1050,7 +1068,14 @@ gdbarch_skip_prologue_noexcept (gdbarch *gdbarch, CORE_ADDR pc) noexcept try { - new_pc = gdbarch_skip_prologue (gdbarch, pc); + if (skip_prologue_method == spm_default) + new_pc = gdbarch_skip_prologue (gdbarch, pc); + else if (skip_prologue_method == spm_line_table) + new_pc = gdbarch_skip_prologue_line_table (gdbarch, pc); + else if (skip_prologue_method == spm_none) + new_pc = pc; + else + gdb_assert_not_reached (""); } catch (const gdb_exception &ex) {} diff --git a/gdb/maint.c b/gdb/maint.c index 76ac7bece38..1a04d777bdc 100644 --- a/gdb/maint.c +++ b/gdb/maint.c @@ -1204,6 +1204,55 @@ Selftests have been disabled for this build.\n")); #endif } +/* See maint.h. */ + +spm_value skip_prologue_method = spm_default; + +/* Valid string arguments for maint set skip-prologue. */ + +static const char skip_prologue_method_default[] = "default"; +static const char skip_prologue_method_line_table[] = "line-table"; +static const char skip_prologue_method_line_none[] = "none"; + +/* List of all valid string arguments for maint set skip-prologue. */ + +static const char *skip_prologue_method_enum[] = { + skip_prologue_method_default, + skip_prologue_method_line_table, + skip_prologue_method_line_none, + nullptr +}; + +/* Current string argument for maint set skip-prologue. */ + +static const char *skip_prologue_method_value = skip_prologue_method_default; + +/* Set skip_prologue_method. */ + +static void +set_skip_prologue_method_command (const char *args, int from_tty, + cmd_list_element *c) +{ + if (skip_prologue_method_value == skip_prologue_method_default) + skip_prologue_method = spm_default; + else if (skip_prologue_method_value == skip_prologue_method_line_table) + skip_prologue_method = spm_line_table; + else if (skip_prologue_method_value == skip_prologue_method_line_none) + skip_prologue_method = spm_none; + else + gdb_assert_not_reached ("Invalid skip_prologue_method kind value."); +} + +/* Show skip_prologue_method. */ + +static void +show_skip_prologue_method_command (struct ui_file *file, int from_tty, + struct cmd_list_element *cmd, + const char *value) +{ + gdb_printf (file, _("Skip prologue method is %s.\n"), value); +} + void _initialize_maint_cmds (); void @@ -1475,5 +1524,15 @@ such as demangling symbol names."), &set_selftest_cmdlist, &show_selftest_cmdlist); + add_setshow_enum_cmd ("skip-prologue", class_maintenance, + skip_prologue_method_enum, + &skip_prologue_method_value, + _("Set skip-prologue method."), + _("Show skip-prologue method."), + _("Control the skip-prologue method."), + set_skip_prologue_method_command, + show_skip_prologue_method_command, + &maintenance_set_cmdlist, &maintenance_show_cmdlist); + update_thread_pool_size (); } diff --git a/gdb/maint.h b/gdb/maint.h index 00b1e981ea6..0955bd9d76e 100644 --- a/gdb/maint.h +++ b/gdb/maint.h @@ -66,4 +66,13 @@ class scoped_command_stats extern obj_section *maint_obj_section_from_bfd_section (bfd *abfd, asection *asection, objfile *ofile); + +/* Values for maint set skip-prologue. */ + +enum spm_value { spm_default, spm_line_table, spm_none }; + +/* Current value of maint set skip-prologue. */ + +extern spm_value skip_prologue_method; + #endif /* MAINT_H */ diff --git a/gdb/testsuite/gdb.ada/inline-section-gc.exp b/gdb/testsuite/gdb.ada/inline-section-gc.exp index 1f6ef667a87..20d181b1f44 100644 --- a/gdb/testsuite/gdb.ada/inline-section-gc.exp +++ b/gdb/testsuite/gdb.ada/inline-section-gc.exp @@ -34,6 +34,8 @@ if {[gdb_compile_ada "${srcfile}" "${binfile}" executable $options] != ""} { clean_restart ${testfile} +gdb_test_no_output "maint set skip-prologue none" + set bp_location [gdb_get_line_number "BREAK" ${testdir}/callee.adb] # The bug here was that gdb would set a breakpoint with two locations, # one of them at 0x0. diff --git a/gdb/testsuite/gdb.opt/inline-small-func.exp b/gdb/testsuite/gdb.opt/inline-small-func.exp index 3be1955d3b6..7af5f1a31a0 100644 --- a/gdb/testsuite/gdb.opt/inline-small-func.exp +++ b/gdb/testsuite/gdb.opt/inline-small-func.exp @@ -49,6 +49,8 @@ if ![runto_main] { # below will only contain a single breakpoint. delete_breakpoints +gdb_test_no_output "maint set skip-prologue none" + # Place a breakpoint within the function in the header file. set linenum [gdb_get_line_number "callee: body" $srcfile2] gdb_breakpoint "${srcfile2}:${linenum}"