diff options
48 files changed, 2663 insertions, 342 deletions
diff --git a/gas/testsuite/gas/loongarch/div_zero.l b/gas/testsuite/gas/loongarch/div_zero.l new file mode 100644 index 0000000..b30c665 --- /dev/null +++ b/gas/testsuite/gas/loongarch/div_zero.l @@ -0,0 +1,4 @@ +#source: div_zero.s +.*: Assembler messages: +.*: Warning: Divide by zero! +.*: Warning: Divide by zero! diff --git a/gas/testsuite/gas/loongarch/div_zero.s b/gas/testsuite/gas/loongarch/div_zero.s new file mode 100644 index 0000000..44a5c26 --- /dev/null +++ b/gas/testsuite/gas/loongarch/div_zero.s @@ -0,0 +1,2 @@ +addi.w $a0,$a1,2/0 +addi.d $a0,$a1,4%0 diff --git a/gas/testsuite/gas/loongarch/loongarch.exp b/gas/testsuite/gas/loongarch/loongarch.exp index 0e836f7..d2008f4 100644 --- a/gas/testsuite/gas/loongarch/loongarch.exp +++ b/gas/testsuite/gas/loongarch/loongarch.exp @@ -37,5 +37,6 @@ if [istarget loongarch*-*-*] { run_list_test "illegal-operand" run_list_test "pseudo_op_option_fail" run_list_test "negative_right_shift" + run_list_test "div_zero" } } diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 9275f8d..68b5e56 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -769,6 +769,7 @@ ALL_64_TARGET_OBS = \ mips-sde-tdep.o \ mips-tdep.o \ mips64-obsd-tdep.o \ + riscv-canonicalize-syscall-gen.o \ riscv-fbsd-tdep.o \ riscv-linux-tdep.o \ riscv-none-tdep.o \ @@ -40,6 +40,14 @@ namespace into which the library was loaded, if more than one namespace is active. +* New built-in convenience variables $_active_linker_namespaces and + $_current_linker_namespace. These show the number of active linkage + namespaces, and the namespace to which the current location belongs to. + In systems that don't support linkage namespaces, these always return 1 + and [[0]] respectively. + + * Add record full support for rv64gc architectures + * New commands maintenance check psymtabs @@ -54,6 +62,11 @@ show riscv numeric-register-names (e.g 'x1') or their abi names (e.g. 'ra'). Defaults to 'off', matching the old behaviour (abi names). +info linker-namespaces +info linker-namespaces [[N]] + Print information about the given linker namespace (identified as N), + or about all the namespaces if no argument is given. + * Changed commands info sharedlibrary diff --git a/gdb/configure b/gdb/configure index e8a649f..c029c30 100755 --- a/gdb/configure +++ b/gdb/configure @@ -28991,7 +28991,7 @@ else fi -if test "${enable_gdb_compile}" == yes; then +if test "${enable_gdb_compile}" = yes; then $as_echo "#define HAVE_COMPILE 1" >>confdefs.h diff --git a/gdb/configure.ac b/gdb/configure.ac index e0cc3c8..7ba799b 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -1231,7 +1231,7 @@ AC_ARG_ENABLE([gdb-compile], [GDB_CHECK_YES_NO_VAL([$enableval], [--enable-gdb-compile])], [enable_gdb_compile=yes]) -if test "${enable_gdb_compile}" == yes; then +if test "${enable_gdb_compile}" = yes; then AC_DEFINE(HAVE_COMPILE, 1, [Define if compiling support to gdb compile.]) CONFIG_OBS="$CONFIG_OBS \$(SUBDIR_GCC_COMPILE_OBS)" else diff --git a/gdb/configure.tgt b/gdb/configure.tgt index 18a15c0..44da225 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -534,8 +534,9 @@ riscv*-*-freebsd*) riscv*-*-linux*) # Target: Linux/RISC-V - gdb_target_obs="riscv-linux-tdep.o glibc-tdep.o \ - linux-tdep.o solib-svr4.o symfile-mem.o linux-record.o" + gdb_target_obs="riscv-linux-tdep.o riscv-canonicalize-syscall-gen.o \ + glibc-tdep.o linux-tdep.o solib-svr4.o symfile-mem.o \ + linux-record.o" ;; riscv*-*-*) diff --git a/gdb/disasm-selftests.c b/gdb/disasm-selftests.c index ffd25bd..3ccc174 100644 --- a/gdb/disasm-selftests.c +++ b/gdb/disasm-selftests.c @@ -21,6 +21,7 @@ #include "gdbsupport/selftest.h" #include "selftest-arch.h" #include "gdbarch.h" +#include "disasm-selftests.h" namespace selftests { @@ -329,6 +330,92 @@ memory_error_test (struct gdbarch *gdbarch) SELF_CHECK (saw_memory_error); } +/* Disassemble INSN (a GDBARCH insn), and return the result. */ + +static std::string +disassemble_one_insn_to_string (struct gdbarch *gdbarch, + gdb::array_view<const gdb_byte> insn) +{ + string_file buffer; + + class gdb_disassembler_test : public gdb_disassembler + { + public: + + explicit gdb_disassembler_test (struct gdbarch *gdbarch, + gdb::array_view<const gdb_byte> insn, + string_file &buffer) + : gdb_disassembler (gdbarch, + &buffer, + gdb_disassembler_test::read_memory), + m_insn (insn) + { + } + + int + print_insn (CORE_ADDR memaddr) + { + try + { + return gdb_disassembler::print_insn (memaddr); + } + catch (const gdb_exception_error &) + { + return -1; + } + } + + private: + gdb::array_view<const gdb_byte> m_insn; + + static int read_memory (bfd_vma memaddr, gdb_byte *myaddr, + unsigned int len, + struct disassemble_info *info) noexcept + { + gdb_disassembler_test *self + = static_cast<gdb_disassembler_test *>(info->application_data); + + if (len > self->m_insn.size ()) + return -1; + + for (size_t i = 0; i < len; i++) + myaddr[i] = self->m_insn[i]; + + return 0; + } + }; + + gdb_disassembler_test di (gdbarch, insn, buffer); + if (di.print_insn (0) != insn.size ()) + return ""; + + return buffer.string (); +} + +/* See disasm-selftests.h. */ + +void +disassemble_insn (gdbarch *gdbarch, gdb::byte_vector &insn, + const std::string &expected) +{ + std::string buffer + = disassemble_one_insn_to_string (gdbarch, insn); + + bool check_ok = buffer == expected; + + if (run_verbose () || !check_ok) + { + for (gdb_byte b : insn) + debug_printf ("0x%02x ", b); + debug_printf ("-> %s\n", buffer.c_str ()); + } + + if (!check_ok) + debug_printf ("expected: %s\n", expected.c_str ()); + + SELF_CHECK (check_ok); +} + } /* namespace selftests */ void _initialize_disasm_selftests (); diff --git a/gdb/disasm-selftests.h b/gdb/disasm-selftests.h new file mode 100644 index 0000000..29acf87 --- /dev/null +++ b/gdb/disasm-selftests.h @@ -0,0 +1,32 @@ +/* Copyright (C) 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_DISASM_SELFTESTS_H +#define GDB_DISASM_SELFTESTS_H + +namespace selftests +{ + +/* Check that disassembly of INSN (a GDBARCH insn) matches EXPECTED. */ + +void +disassemble_insn (gdbarch *gdbarch, gdb::byte_vector &insn, + const std::string &expected); + +} + +#endif /* GDB_DISASM_SELFTESTS_H */ diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index b251c8e..48b193b 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -7807,9 +7807,9 @@ previous instruction; otherwise, it will work in record mode, if the platform supports reverse execution, or stop if not. Currently, process record and replay is supported on ARM, Aarch64, -LoongArch, Moxie, PowerPC, PowerPC64, S/390, and x86 (i386/amd64) running -GNU/Linux. Process record and replay can be used both when native -debugging, and when remote debugging via @code{gdbserver}. +LoongArch, Moxie, PowerPC, PowerPC64, S/390, RISC-V and x86 (i386/amd64) +running GNU/Linux. Process record and replay can be used both when +native debugging, and when remote debugging via @code{gdbserver}. When recording an inferior, @value{GDBN} may print auxiliary information during stepping commands and commands displaying the execution history. @@ -13046,6 +13046,18 @@ variable which may be @samp{truecolor} or @samp{24bit}. Other color spaces are determined by the "Co" termcap which in turn depends on the @env{TERM} environment variable. +@vindex $_active_linker_namespaces@r{, convenience variable} +@item $_active_linker_namespaces +Number of active linkage namespaces in the inferior. In systems with no +support for linkage namespaces, this variable will always be set to @samp{1}. + +@vindex $_current_linker_namespace@r{, convenience variable} +@item $_current_linker_namespace +The namespace which contains the current location in the inferior. This +returns GDB's internal identifier for namespaces, which is @samp{[[@var{n}]]} +where @var{n} is a zero-based namespace number. In systems with no support +for linkage namespaces, this variable will always be set to @samp{[[0]]}. + @end table @node Convenience Funs @@ -22212,6 +22224,22 @@ less useful than setting a catchpoint, because it does not allow for conditions or commands as a catchpoint does. @table @code +@kindex info linker-namespaces +@item info linker-namespaces +@item info linker-namespaces @code{[[@var{n}]]} + +With no argument, print the number of linker namespaces which are +currently active --- that is, namespaces that have libraries loaded +into them. Then, it prints the number of libraries loaded into each +namespace, and all the libraries loaded into them, in the same way +as @code{info sharedlibrary}. + +If an argument @code{[[@var{n}]]} is provided, only prints the +library count and the libraried for the provided namespace @var{n}. +The surrounding square brackets are optional. +@end table + +@table @code @item set stop-on-solib-events @kindex set stop-on-solib-events This command controls whether @value{GDBN} should give you control diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi index 80f4c14..45f5f70 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -7136,6 +7136,8 @@ Direct 24-bit RGB colors. @end table +It is not possible to sub-class the @code{Color} class. + @node Architectures In Python @subsubsection Python representation of architectures @cindex Python architectures diff --git a/gdb/infrun.c b/gdb/infrun.c index 3d5ede8..0b87287 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -3972,7 +3972,8 @@ delete_just_stopped_threads_single_step_breakpoints (void) void print_target_wait_results (ptid_t waiton_ptid, ptid_t result_ptid, - const struct target_waitstatus &ws) + const struct target_waitstatus &ws, + process_stratum_target *proc_target) { infrun_debug_printf ("target_wait (%s [%s], status) =", waiton_ptid.to_string ().c_str (), @@ -3981,6 +3982,20 @@ print_target_wait_results (ptid_t waiton_ptid, ptid_t result_ptid, result_ptid.to_string ().c_str (), target_pid_to_str (result_ptid).c_str ()); infrun_debug_printf (" %s", ws.to_string ().c_str ()); + + if (proc_target != nullptr) + infrun_debug_printf (" from target %d (%s)", + proc_target->connection_number, + proc_target->shortname ()); +} + +/* Wrapper for print_target_wait_results above for convenience. */ + +static void +print_target_wait_results (ptid_t waiton_ptid, + const execution_control_state &ecs) +{ + print_target_wait_results (waiton_ptid, ecs.ptid, ecs.ws, ecs.target); } /* Select a thread at random, out of those which are resumed and have @@ -4332,7 +4347,8 @@ prepare_for_detach (void) event.ptid = do_target_wait_1 (inf, pid_ptid, &event.ws, 0); if (debug_infrun) - print_target_wait_results (pid_ptid, event.ptid, event.ws); + print_target_wait_results (pid_ptid, event.ptid, event.ws, + event.target); handle_one (event); } @@ -4388,7 +4404,7 @@ wait_for_inferior (inferior *inf) ecs.target = inf->process_target (); if (debug_infrun) - print_target_wait_results (minus_one_ptid, ecs.ptid, ecs.ws); + print_target_wait_results (minus_one_ptid, ecs); /* Now figure out what to do with the result of the result. */ handle_inferior_event (&ecs); @@ -4669,7 +4685,7 @@ fetch_inferior_event () switch_to_target_no_thread (ecs.target); if (debug_infrun) - print_target_wait_results (minus_one_ptid, ecs.ptid, ecs.ws); + print_target_wait_results (minus_one_ptid, ecs); /* If an error happens while handling the event, propagate GDB's knowledge of the executing state to the frontend/user running @@ -5232,7 +5248,8 @@ poll_one_curr_target (struct target_waitstatus *ws) event_ptid = target_wait (minus_one_ptid, ws, TARGET_WNOHANG); if (debug_infrun) - print_target_wait_results (minus_one_ptid, event_ptid, *ws); + print_target_wait_results (minus_one_ptid, event_ptid, *ws, + current_inferior ()->process_target ()); return event_ptid; } diff --git a/gdb/infrun.h b/gdb/infrun.h index 2067fd5..b9b64ac 100644 --- a/gdb/infrun.h +++ b/gdb/infrun.h @@ -256,7 +256,8 @@ extern void print_stop_event (struct ui_out *uiout, bool displays = true); /* Pretty print the results of target_wait, for debugging purposes. */ extern void print_target_wait_results (ptid_t waiton_ptid, ptid_t result_ptid, - const struct target_waitstatus &ws); + const struct target_waitstatus &ws, + process_stratum_target *proc_target); extern int signal_stop_state (int); diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c index 9e29ee2..c48d14e 100644 --- a/gdb/python/py-color.c +++ b/gdb/python/py-color.c @@ -64,7 +64,8 @@ create_color_object (const ui_file_style::color &color) bool gdbpy_is_color (PyObject *obj) { - return PyObject_IsInstance (obj, (PyObject *) &colorpy_object_type); + gdb_assert (obj != nullptr); + return PyObject_TypeCheck (obj, &colorpy_object_type) != 0; } /* See py-color.h. */ @@ -312,7 +313,7 @@ PyTypeObject colorpy_object_type = get_attr, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ "GDB color object", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ diff --git a/gdb/python/py-disasm.c b/gdb/python/py-disasm.c index 9ca8d22..17064dc 100644 --- a/gdb/python/py-disasm.c +++ b/gdb/python/py-disasm.c @@ -1311,12 +1311,13 @@ gdbpy_print_insn (struct gdbarch *gdbarch, CORE_ADDR memaddr, return {}; } - /* Check the result is a DisassemblerResult (or a sub-class). */ - if (!PyObject_IsInstance (result.get (), - (PyObject *) &disasm_result_object_type)) + /* Check the result is a DisassemblerResult. */ + if (!PyObject_TypeCheck (result.get (), &disasm_result_object_type)) { - PyErr_SetString (PyExc_TypeError, - _("Result is not a DisassemblerResult.")); + PyErr_Format + (PyExc_TypeError, + _("Result from Disassembler must be gdb.DisassemblerResult, not %s."), + Py_TYPE (result.get ())->tp_name); gdbpy_print_stack (); return std::optional<int> (-1); } diff --git a/gdb/python/py-registers.c b/gdb/python/py-registers.c index 78a806c..9be2e3c 100644 --- a/gdb/python/py-registers.c +++ b/gdb/python/py-registers.c @@ -403,8 +403,7 @@ gdbpy_parse_register_id (struct gdbarch *gdbarch, PyObject *pyo_reg_id, PyErr_SetString (PyExc_ValueError, "Bad register"); } /* The register could be a gdb.RegisterDescriptor object. */ - else if (PyObject_IsInstance (pyo_reg_id, - (PyObject *) ®ister_descriptor_object_type)) + else if (PyObject_TypeCheck (pyo_reg_id, ®ister_descriptor_object_type)) { register_descriptor_object *reg = (register_descriptor_object *) pyo_reg_id; diff --git a/gdb/python/py-unwind.c b/gdb/python/py-unwind.c index ab34971..d43d7e9 100644 --- a/gdb/python/py-unwind.c +++ b/gdb/python/py-unwind.c @@ -929,9 +929,9 @@ frame_unwind_python::sniff (const frame_info_ptr &this_frame, /* Received UnwindInfo, cache data. */ PyObject *pyo_unwind_info = PyTuple_GET_ITEM (pyo_execute_ret.get (), 0); - if (PyObject_IsInstance (pyo_unwind_info, - (PyObject *) &unwind_info_object_type) <= 0) - error (_("A Unwinder should return gdb.UnwindInfo instance.")); + if (!PyObject_TypeCheck (pyo_unwind_info, &unwind_info_object_type)) + error (_("an Unwinder should return gdb.UnwindInfo, not %s."), + Py_TYPE (pyo_unwind_info)->tp_name); { unwind_info_object *unwind_info = diff --git a/gdb/remote.c b/gdb/remote.c index 7dc057c..75cc21c 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -4943,7 +4943,7 @@ remote_target::process_initial_stop_replies (int from_tty) event_ptid = target_wait (waiton_ptid, &ws, TARGET_WNOHANG); if (remote_debug) - print_target_wait_results (waiton_ptid, event_ptid, ws); + print_target_wait_results (waiton_ptid, event_ptid, ws, this); switch (ws.kind ()) { diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c new file mode 100644 index 0000000..67e5410 --- /dev/null +++ b/gdb/riscv-canonicalize-syscall-gen.c @@ -0,0 +1,358 @@ +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py + + Copyright (C) 2024-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 "defs.h" +#include "riscv-linux-tdep.h" + +/* riscv64_canonicalize_syscall maps from the native riscv 64 Linux set + of syscall ids into a canonical set of syscall ids used by + process record. */ + +enum gdb_syscall +riscv64_canonicalize_syscall (int syscall) +{ + switch (syscall) + { + case 0: return gdb_sys_io_setup; + case 1: return gdb_sys_io_destroy; + case 2: return gdb_sys_io_submit; + case 3: return gdb_sys_io_cancel; + case 4: return gdb_sys_io_getevents; + case 5: return gdb_sys_setxattr; + case 6: return gdb_sys_lsetxattr; + case 7: return gdb_sys_fsetxattr; + case 8: return gdb_sys_getxattr; + case 9: return gdb_sys_lgetxattr; + case 10: return gdb_sys_fgetxattr; + case 11: return gdb_sys_listxattr; + case 12: return gdb_sys_llistxattr; + case 13: return gdb_sys_flistxattr; + case 14: return gdb_sys_removexattr; + case 15: return gdb_sys_lremovexattr; + case 16: return gdb_sys_fremovexattr; + case 17: return gdb_sys_getcwd; + case 18: return gdb_sys_lookup_dcookie; + case 19: return gdb_sys_eventfd2; + case 20: return gdb_sys_epoll_create1; + case 21: return gdb_sys_epoll_ctl; + case 22: return gdb_sys_epoll_pwait; + case 23: return gdb_sys_dup; + case 24: return gdb_sys_dup3; + case 25: return gdb_sys_fcntl; + case 26: return gdb_sys_inotify_init1; + case 27: return gdb_sys_inotify_add_watch; + case 28: return gdb_sys_inotify_rm_watch; + case 29: return gdb_sys_ioctl; + case 30: return gdb_sys_ioprio_set; + case 31: return gdb_sys_ioprio_get; + case 32: return gdb_sys_flock; + case 33: return gdb_sys_mknodat; + case 34: return gdb_sys_mkdirat; + case 35: return gdb_sys_unlinkat; + case 36: return gdb_sys_symlinkat; + case 37: return gdb_sys_linkat; + /* case 39: return gdb_sys_umount2; */ + case 40: return gdb_sys_mount; + case 41: return gdb_sys_pivot_root; + case 42: return gdb_sys_nfsservctl; + case 43: return gdb_sys_statfs; + case 44: return gdb_sys_fstatfs; + case 45: return gdb_sys_truncate; + case 46: return gdb_sys_ftruncate; + case 47: return gdb_sys_fallocate; + case 48: return gdb_sys_faccessat; + case 49: return gdb_sys_chdir; + case 50: return gdb_sys_fchdir; + case 51: return gdb_sys_chroot; + case 52: return gdb_sys_fchmod; + case 53: return gdb_sys_fchmodat; + case 54: return gdb_sys_fchownat; + case 55: return gdb_sys_fchown; + case 56: return gdb_sys_openat; + case 57: return gdb_sys_close; + case 58: return gdb_sys_vhangup; + case 59: return gdb_sys_pipe2; + case 60: return gdb_sys_quotactl; + case 61: return gdb_sys_getdents64; + case 62: return gdb_sys_lseek; + case 63: return gdb_sys_read; + case 64: return gdb_sys_write; + case 65: return gdb_sys_readv; + case 66: return gdb_sys_writev; + case 67: return gdb_sys_pread64; + case 68: return gdb_sys_pwrite64; + /* case 69: return gdb_sys_preadv; */ + /* case 70: return gdb_sys_pwritev; */ + case 71: return gdb_sys_sendfile; + case 72: return gdb_sys_pselect6; + case 73: return gdb_sys_ppoll; + /* case 74: return gdb_sys_signalfd4; */ + case 75: return gdb_sys_vmsplice; + case 76: return gdb_sys_splice; + case 77: return gdb_sys_tee; + case 78: return gdb_sys_readlinkat; + case 79: return gdb_sys_newfstatat; + case 80: return gdb_sys_fstat; + case 81: return gdb_sys_sync; + case 82: return gdb_sys_fsync; + case 83: return gdb_sys_fdatasync; + case 84: return gdb_sys_sync_file_range; + /* case 85: return gdb_sys_timerfd_create; */ + /* case 86: return gdb_sys_timerfd_settime; */ + /* case 87: return gdb_sys_timerfd_gettime; */ + /* case 88: return gdb_sys_utimensat; */ + case 89: return gdb_sys_acct; + case 90: return gdb_sys_capget; + case 91: return gdb_sys_capset; + case 92: return gdb_sys_personality; + case 93: return gdb_sys_exit; + case 94: return gdb_sys_exit_group; + case 95: return gdb_sys_waitid; + case 96: return gdb_sys_set_tid_address; + case 97: return gdb_sys_unshare; + case 98: return gdb_sys_futex; + case 99: return gdb_sys_set_robust_list; + case 100: return gdb_sys_get_robust_list; + case 101: return gdb_sys_nanosleep; + case 102: return gdb_sys_getitimer; + case 103: return gdb_sys_setitimer; + case 104: return gdb_sys_kexec_load; + case 105: return gdb_sys_init_module; + case 106: return gdb_sys_delete_module; + case 107: return gdb_sys_timer_create; + case 108: return gdb_sys_timer_gettime; + case 109: return gdb_sys_timer_getoverrun; + case 110: return gdb_sys_timer_settime; + case 111: return gdb_sys_timer_delete; + case 112: return gdb_sys_clock_settime; + case 113: return gdb_sys_clock_gettime; + case 114: return gdb_sys_clock_getres; + case 115: return gdb_sys_clock_nanosleep; + case 116: return gdb_sys_syslog; + case 117: return gdb_sys_ptrace; + case 118: return gdb_sys_sched_setparam; + case 119: return gdb_sys_sched_setscheduler; + case 120: return gdb_sys_sched_getscheduler; + case 121: return gdb_sys_sched_getparam; + case 122: return gdb_sys_sched_setaffinity; + case 123: return gdb_sys_sched_getaffinity; + case 124: return gdb_sys_sched_yield; + case 125: return gdb_sys_sched_get_priority_max; + case 126: return gdb_sys_sched_get_priority_min; + case 127: return gdb_sys_sched_rr_get_interval; + case 128: return gdb_sys_restart_syscall; + case 129: return gdb_sys_kill; + case 130: return gdb_sys_tkill; + case 131: return gdb_sys_tgkill; + case 132: return gdb_sys_sigaltstack; + case 133: return gdb_sys_rt_sigsuspend; + case 134: return gdb_sys_rt_sigaction; + case 135: return gdb_sys_rt_sigprocmask; + case 136: return gdb_sys_rt_sigpending; + case 137: return gdb_sys_rt_sigtimedwait; + case 138: return gdb_sys_rt_sigqueueinfo; + case 139: return gdb_sys_rt_sigreturn; + case 140: return gdb_sys_setpriority; + case 141: return gdb_sys_getpriority; + case 142: return gdb_sys_reboot; + case 143: return gdb_sys_setregid; + case 144: return gdb_sys_setgid; + case 145: return gdb_sys_setreuid; + case 146: return gdb_sys_setuid; + case 147: return gdb_sys_setresuid; + case 148: return gdb_sys_getresuid; + case 149: return gdb_sys_setresgid; + case 150: return gdb_sys_getresgid; + case 151: return gdb_sys_setfsuid; + case 152: return gdb_sys_setfsgid; + case 153: return gdb_sys_times; + case 154: return gdb_sys_setpgid; + case 155: return gdb_sys_getpgid; + case 156: return gdb_sys_getsid; + case 157: return gdb_sys_setsid; + case 158: return gdb_sys_getgroups; + case 159: return gdb_sys_setgroups; + case 160: return gdb_sys_uname; + case 161: return gdb_sys_sethostname; + case 162: return gdb_sys_setdomainname; + case 163: return gdb_sys_getrlimit; + case 164: return gdb_sys_setrlimit; + case 165: return gdb_sys_getrusage; + case 166: return gdb_sys_umask; + case 167: return gdb_sys_prctl; + case 168: return gdb_sys_getcpu; + case 169: return gdb_sys_gettimeofday; + case 170: return gdb_sys_settimeofday; + case 171: return gdb_sys_adjtimex; + case 172: return gdb_sys_getpid; + case 173: return gdb_sys_getppid; + case 174: return gdb_sys_getuid; + case 175: return gdb_sys_geteuid; + case 176: return gdb_sys_getgid; + case 177: return gdb_sys_getegid; + case 178: return gdb_sys_gettid; + case 179: return gdb_sys_sysinfo; + case 180: return gdb_sys_mq_open; + case 181: return gdb_sys_mq_unlink; + case 182: return gdb_sys_mq_timedsend; + case 183: return gdb_sys_mq_timedreceive; + case 184: return gdb_sys_mq_notify; + case 185: return gdb_sys_mq_getsetattr; + case 186: return gdb_sys_msgget; + case 187: return gdb_sys_msgctl; + case 188: return gdb_sys_msgrcv; + case 189: return gdb_sys_msgsnd; + case 190: return gdb_sys_semget; + case 191: return gdb_sys_semctl; + case 192: return gdb_sys_semtimedop; + case 193: return gdb_sys_semop; + case 194: return gdb_sys_shmget; + case 195: return gdb_sys_shmctl; + case 196: return gdb_sys_shmat; + case 197: return gdb_sys_shmdt; + case 198: return gdb_sys_socket; + case 199: return gdb_sys_socketpair; + case 200: return gdb_sys_bind; + case 201: return gdb_sys_listen; + case 202: return gdb_sys_accept; + case 203: return gdb_sys_connect; + case 204: return gdb_sys_getsockname; + case 205: return gdb_sys_getpeername; + case 206: return gdb_sys_sendto; + case 207: return gdb_sys_recvfrom; + case 208: return gdb_sys_setsockopt; + case 209: return gdb_sys_getsockopt; + case 210: return gdb_sys_shutdown; + case 211: return gdb_sys_sendmsg; + case 212: return gdb_sys_recvmsg; + case 213: return gdb_sys_readahead; + case 214: return gdb_sys_brk; + case 215: return gdb_sys_munmap; + case 216: return gdb_sys_mremap; + case 217: return gdb_sys_add_key; + case 218: return gdb_sys_request_key; + case 219: return gdb_sys_keyctl; + case 220: return gdb_sys_clone; + case 221: return gdb_sys_execve; + case 222: return gdb_sys_old_mmap; + case 223: return gdb_sys_fadvise64; + case 224: return gdb_sys_swapon; + case 225: return gdb_sys_swapoff; + case 226: return gdb_sys_mprotect; + case 227: return gdb_sys_msync; + case 228: return gdb_sys_mlock; + case 229: return gdb_sys_munlock; + case 230: return gdb_sys_mlockall; + case 231: return gdb_sys_munlockall; + case 232: return gdb_sys_mincore; + case 233: return gdb_sys_madvise; + case 234: return gdb_sys_remap_file_pages; + case 235: return gdb_sys_mbind; + case 236: return gdb_sys_get_mempolicy; + case 237: return gdb_sys_set_mempolicy; + case 238: return gdb_sys_migrate_pages; + case 239: return gdb_sys_move_pages; + /* case 240: return gdb_sys_rt_tgsigqueueinfo; */ + /* case 241: return gdb_sys_perf_event_open; */ + case 242: return gdb_sys_accept4; + /* case 243: return gdb_sys_recvmmsg; */ + /* case 258: return gdb_sys_riscv_hwprobe; */ + /* case 259: return gdb_sys_riscv_flush_icache; */ + case 260: return gdb_sys_wait4; + /* case 261: return gdb_sys_prlimit64; */ + /* case 262: return gdb_sys_fanotify_init; */ + /* case 263: return gdb_sys_fanotify_mark; */ + /* case 264: return gdb_sys_name_to_handle_at; */ + /* case 265: return gdb_sys_open_by_handle_at; */ + /* case 266: return gdb_sys_clock_adjtime; */ + /* case 267: return gdb_sys_syncfs; */ + /* case 268: return gdb_sys_setns; */ + /* case 269: return gdb_sys_sendmmsg; */ + /* case 270: return gdb_sys_process_vm_readv; */ + /* case 271: return gdb_sys_process_vm_writev; */ + /* case 272: return gdb_sys_kcmp; */ + /* case 273: return gdb_sys_finit_module; */ + /* case 274: return gdb_sys_sched_setattr; */ + /* case 275: return gdb_sys_sched_getattr; */ + /* case 276: return gdb_sys_renameat2; */ + /* case 277: return gdb_sys_seccomp; */ + case 278: return gdb_sys_getrandom; + /* case 279: return gdb_sys_memfd_create; */ + /* case 280: return gdb_sys_bpf; */ + /* case 281: return gdb_sys_execveat; */ + /* case 282: return gdb_sys_userfaultfd; */ + /* case 283: return gdb_sys_membarrier; */ + /* case 284: return gdb_sys_mlock2; */ + /* case 285: return gdb_sys_copy_file_range; */ + /* case 286: return gdb_sys_preadv2; */ + /* case 287: return gdb_sys_pwritev2; */ + /* case 288: return gdb_sys_pkey_mprotect; */ + /* case 289: return gdb_sys_pkey_alloc; */ + /* case 290: return gdb_sys_pkey_free; */ + case 291: return gdb_sys_statx; + /* case 292: return gdb_sys_io_pgetevents; */ + /* case 293: return gdb_sys_rseq; */ + /* case 294: return gdb_sys_kexec_file_load; */ + /* case 424: return gdb_sys_pidfd_send_signal; */ + /* case 425: return gdb_sys_io_uring_setup; */ + /* case 426: return gdb_sys_io_uring_enter; */ + /* case 427: return gdb_sys_io_uring_register; */ + /* case 428: return gdb_sys_open_tree; */ + /* case 429: return gdb_sys_move_mount; */ + /* case 430: return gdb_sys_fsopen; */ + /* case 431: return gdb_sys_fsconfig; */ + /* case 432: return gdb_sys_fsmount; */ + /* case 433: return gdb_sys_fspick; */ + /* case 434: return gdb_sys_pidfd_open; */ + /* case 435: return gdb_sys_clone3; */ + /* case 436: return gdb_sys_close_range; */ + /* case 437: return gdb_sys_openat2; */ + /* case 438: return gdb_sys_pidfd_getfd; */ + /* case 439: return gdb_sys_faccessat2; */ + /* case 440: return gdb_sys_process_madvise; */ + /* case 441: return gdb_sys_epoll_pwait2; */ + /* case 442: return gdb_sys_mount_setattr; */ + /* case 443: return gdb_sys_quotactl_fd; */ + /* case 444: return gdb_sys_landlock_create_ruleset; */ + /* case 445: return gdb_sys_landlock_add_rule; */ + /* case 446: return gdb_sys_landlock_restrict_self; */ + /* case 447: return gdb_sys_memfd_secret; */ + /* case 448: return gdb_sys_process_mrelease; */ + /* case 449: return gdb_sys_futex_waitv; */ + /* case 450: return gdb_sys_set_mempolicy_home_node; */ + /* case 451: return gdb_sys_cachestat; */ + /* case 452: return gdb_sys_fchmodat2; */ + /* case 453: return gdb_sys_map_shadow_stack; */ + /* case 454: return gdb_sys_futex_wake; */ + /* case 455: return gdb_sys_futex_wait; */ + /* case 456: return gdb_sys_futex_requeue; */ + /* case 457: return gdb_sys_statmount; */ + /* case 458: return gdb_sys_listmount; */ + /* case 459: return gdb_sys_lsm_get_self_attr; */ + /* case 460: return gdb_sys_lsm_set_self_attr; */ + /* case 461: return gdb_sys_lsm_list_modules; */ + /* case 462: return gdb_sys_mseal; */ + /* case 463: return gdb_sys_setxattrat; */ + /* case 464: return gdb_sys_getxattrat; */ + /* case 465: return gdb_sys_listxattrat; */ + /* case 466: return gdb_sys_removexattrat; */ + default: + return gdb_sys_no_syscall; + } +} diff --git a/gdb/riscv-linux-tdep.c b/gdb/riscv-linux-tdep.c index 4c0c65c..f21039a 100644 --- a/gdb/riscv-linux-tdep.c +++ b/gdb/riscv-linux-tdep.c @@ -25,6 +25,11 @@ #include "tramp-frame.h" #include "trad-frame.h" #include "gdbarch.h" +#include "record-full.h" +#include "linux-record.h" +#include "riscv-linux-tdep.h" + +extern unsigned int record_debug; /* The following value is derived from __NR_rt_sigreturn in <include/uapi/asm-generic/unistd.h> from the Linux source tree. */ @@ -173,6 +178,254 @@ riscv_linux_syscall_next_pc (const frame_info_ptr &frame) return pc + 4 /* Length of the ECALL insn. */; } +/* RISC-V process record-replay constructs: syscall, signal etc. */ + +static linux_record_tdep riscv_linux_record_tdep; + +using regnum_type = int; + +/* Record registers from first to last for process-record. */ + +static bool +save_registers (struct regcache *regcache, regnum_type first, regnum_type last) +{ + gdb_assert (regcache != nullptr); + + for (regnum_type i = first; i != last; ++i) + if (record_full_arch_list_add_reg (regcache, i)) + return false; + return true; +}; + +/* Record all registers but PC register for process-record. */ + +static bool +riscv_all_but_pc_registers_record (struct regcache *regcache) +{ + gdb_assert (regcache != nullptr); + + struct gdbarch *gdbarch = regcache->arch (); + riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch); + const struct riscv_gdbarch_features &features = tdep->isa_features; + + if (!save_registers (regcache, RISCV_ZERO_REGNUM + 1, RISCV_PC_REGNUM)) + return false; + + if (features.flen + && !save_registers (regcache, RISCV_FIRST_FP_REGNUM, + RISCV_LAST_FP_REGNUM + 1)) + return false; + + return true; +} + +/* Handler for riscv system call instruction recording. */ + +static int +riscv_linux_syscall_record (struct regcache *regcache, + unsigned long svc_number) +{ + gdb_assert (regcache != nullptr); + + enum gdb_syscall syscall_gdb = riscv64_canonicalize_syscall (svc_number); + + if (record_debug > 1) + gdb_printf (gdb_stdlog, "Made syscall %s.\n", plongest (svc_number)); + + if (syscall_gdb == gdb_sys_no_syscall) + { + warning (_("Process record and replay target doesn't " + "support syscall number %s\n"), plongest (svc_number)); + return -1; + } + + if (syscall_gdb == gdb_sys_sigreturn || syscall_gdb == gdb_sys_rt_sigreturn) + { + if (!riscv_all_but_pc_registers_record (regcache)) + return -1; + return 0; + } + + int ret = record_linux_system_call (syscall_gdb, regcache, + &riscv_linux_record_tdep); + if (ret != 0) + return ret; + + /* Record the return value of the system call. */ + if (record_full_arch_list_add_reg (regcache, RISCV_A0_REGNUM)) + return -1; + + return 0; +} + +/* Initialize the riscv64_linux_record_tdep. */ + +static void +riscv64_linux_record_tdep_init (struct gdbarch *gdbarch, + struct linux_record_tdep & + riscv_linux_record_tdep) +{ + gdb_assert (gdbarch != nullptr); + + /* These values are the size of the type that + will be used in a system call. */ + riscv_linux_record_tdep.size_pointer + = gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT; + riscv_linux_record_tdep.size__old_kernel_stat = 48; + riscv_linux_record_tdep.size_tms = 32; + riscv_linux_record_tdep.size_loff_t = 8; + riscv_linux_record_tdep.size_flock = 32; + riscv_linux_record_tdep.size_oldold_utsname = 45; + riscv_linux_record_tdep.size_ustat = 32; + riscv_linux_record_tdep.size_old_sigaction = 32; + riscv_linux_record_tdep.size_old_sigset_t = 8; + riscv_linux_record_tdep.size_rlimit = 16; + riscv_linux_record_tdep.size_rusage = 144; + riscv_linux_record_tdep.size_timeval = 8; + riscv_linux_record_tdep.size_timezone = 8; + riscv_linux_record_tdep.size_old_gid_t = 2; + riscv_linux_record_tdep.size_old_uid_t = 2; + riscv_linux_record_tdep.size_fd_set = 128; + riscv_linux_record_tdep.size_old_dirent = 268; + riscv_linux_record_tdep.size_statfs = 120; + riscv_linux_record_tdep.size_statfs64 = 120; + riscv_linux_record_tdep.size_sockaddr = 16; + riscv_linux_record_tdep.size_int + = gdbarch_int_bit (gdbarch) / TARGET_CHAR_BIT; + riscv_linux_record_tdep.size_long + = gdbarch_long_bit (gdbarch) / TARGET_CHAR_BIT; + riscv_linux_record_tdep.size_ulong + = gdbarch_long_bit (gdbarch) / TARGET_CHAR_BIT; + riscv_linux_record_tdep.size_msghdr = 104; + riscv_linux_record_tdep.size_itimerval = 16; + riscv_linux_record_tdep.size_stat = 128; + riscv_linux_record_tdep.size_old_utsname = 325; + riscv_linux_record_tdep.size_sysinfo = 112; + riscv_linux_record_tdep.size_msqid_ds = 104; + riscv_linux_record_tdep.size_shmid_ds = 88; + riscv_linux_record_tdep.size_new_utsname = 390; + riscv_linux_record_tdep.size_timex = 188; + riscv_linux_record_tdep.size_mem_dqinfo = 72; + riscv_linux_record_tdep.size_if_dqblk = 68; + riscv_linux_record_tdep.size_fs_quota_stat = 64; + riscv_linux_record_tdep.size_timespec = 16; + riscv_linux_record_tdep.size_pollfd = 8; + riscv_linux_record_tdep.size_NFS_FHSIZE = 32; + riscv_linux_record_tdep.size_knfsd_fh = 36; + riscv_linux_record_tdep.size_TASK_COMM_LEN = 4; + riscv_linux_record_tdep.size_sigaction = 24; + riscv_linux_record_tdep.size_sigset_t = 8; + riscv_linux_record_tdep.size_siginfo_t = 128; + riscv_linux_record_tdep.size_cap_user_data_t = 8; + riscv_linux_record_tdep.size_stack_t = 24; + riscv_linux_record_tdep.size_off_t = riscv_linux_record_tdep.size_long; + riscv_linux_record_tdep.size_stat64 = 136; + riscv_linux_record_tdep.size_gid_t = 4; + riscv_linux_record_tdep.size_uid_t = 4; + riscv_linux_record_tdep.size_PAGE_SIZE = 4096; + riscv_linux_record_tdep.size_flock64 = 32; + riscv_linux_record_tdep.size_user_desc = 37; + riscv_linux_record_tdep.size_io_event = 32; + riscv_linux_record_tdep.size_iocb = 64; + riscv_linux_record_tdep.size_epoll_event = 16; + riscv_linux_record_tdep.size_itimerspec + = riscv_linux_record_tdep.size_timespec * 2; + riscv_linux_record_tdep.size_mq_attr = 64; + riscv_linux_record_tdep.size_termios = 36; + riscv_linux_record_tdep.size_termios2 = 44; + riscv_linux_record_tdep.size_pid_t = 4; + riscv_linux_record_tdep.size_winsize = 8; + riscv_linux_record_tdep.size_serial_struct = 72; + riscv_linux_record_tdep.size_serial_icounter_struct = 80; + riscv_linux_record_tdep.size_hayes_esp_config = 12; + riscv_linux_record_tdep.size_size_t = 8; + riscv_linux_record_tdep.size_iovec = 16; + riscv_linux_record_tdep.size_time_t = 8; + + /* These values are the second argument of system call "sys_ioctl". + They are obtained from Linux Kernel source. */ + riscv_linux_record_tdep.ioctl_TCGETS = 0x5401; + riscv_linux_record_tdep.ioctl_TCSETS = 0x5402; + riscv_linux_record_tdep.ioctl_TCSETSW = 0x5403; + riscv_linux_record_tdep.ioctl_TCSETSF = 0x5404; + riscv_linux_record_tdep.ioctl_TCGETA = 0x5405; + riscv_linux_record_tdep.ioctl_TCSETA = 0x5406; + riscv_linux_record_tdep.ioctl_TCSETAW = 0x5407; + riscv_linux_record_tdep.ioctl_TCSETAF = 0x5408; + riscv_linux_record_tdep.ioctl_TCSBRK = 0x5409; + riscv_linux_record_tdep.ioctl_TCXONC = 0x540a; + riscv_linux_record_tdep.ioctl_TCFLSH = 0x540b; + riscv_linux_record_tdep.ioctl_TIOCEXCL = 0x540c; + riscv_linux_record_tdep.ioctl_TIOCNXCL = 0x540d; + riscv_linux_record_tdep.ioctl_TIOCSCTTY = 0x540e; + riscv_linux_record_tdep.ioctl_TIOCGPGRP = 0x540f; + riscv_linux_record_tdep.ioctl_TIOCSPGRP = 0x5410; + riscv_linux_record_tdep.ioctl_TIOCOUTQ = 0x5411; + riscv_linux_record_tdep.ioctl_TIOCSTI = 0x5412; + riscv_linux_record_tdep.ioctl_TIOCGWINSZ = 0x5413; + riscv_linux_record_tdep.ioctl_TIOCSWINSZ = 0x5414; + riscv_linux_record_tdep.ioctl_TIOCMGET = 0x5415; + riscv_linux_record_tdep.ioctl_TIOCMBIS = 0x5416; + riscv_linux_record_tdep.ioctl_TIOCMBIC = 0x5417; + riscv_linux_record_tdep.ioctl_TIOCMSET = 0x5418; + riscv_linux_record_tdep.ioctl_TIOCGSOFTCAR = 0x5419; + riscv_linux_record_tdep.ioctl_TIOCSSOFTCAR = 0x541a; + riscv_linux_record_tdep.ioctl_FIONREAD = 0x541b; + riscv_linux_record_tdep.ioctl_TIOCINQ + = riscv_linux_record_tdep.ioctl_FIONREAD; + riscv_linux_record_tdep.ioctl_TIOCLINUX = 0x541c; + riscv_linux_record_tdep.ioctl_TIOCCONS = 0x541d; + riscv_linux_record_tdep.ioctl_TIOCGSERIAL = 0x541e; + riscv_linux_record_tdep.ioctl_TIOCSSERIAL = 0x541f; + riscv_linux_record_tdep.ioctl_TIOCPKT = 0x5420; + riscv_linux_record_tdep.ioctl_FIONBIO = 0x5421; + riscv_linux_record_tdep.ioctl_TIOCNOTTY = 0x5422; + riscv_linux_record_tdep.ioctl_TIOCSETD = 0x5423; + riscv_linux_record_tdep.ioctl_TIOCGETD = 0x5424; + riscv_linux_record_tdep.ioctl_TCSBRKP = 0x5425; + riscv_linux_record_tdep.ioctl_TIOCTTYGSTRUCT = 0x5426; + riscv_linux_record_tdep.ioctl_TIOCSBRK = 0x5427; + riscv_linux_record_tdep.ioctl_TIOCCBRK = 0x5428; + riscv_linux_record_tdep.ioctl_TIOCGSID = 0x5429; + riscv_linux_record_tdep.ioctl_TCGETS2 = 0x802c542a; + riscv_linux_record_tdep.ioctl_TCSETS2 = 0x402c542b; + riscv_linux_record_tdep.ioctl_TCSETSW2 = 0x402c542c; + riscv_linux_record_tdep.ioctl_TCSETSF2 = 0x402c542d; + riscv_linux_record_tdep.ioctl_TIOCGPTN = 0x80045430; + riscv_linux_record_tdep.ioctl_TIOCSPTLCK = 0x40045431; + riscv_linux_record_tdep.ioctl_FIONCLEX = 0x5450; + riscv_linux_record_tdep.ioctl_FIOCLEX = 0x5451; + riscv_linux_record_tdep.ioctl_FIOASYNC = 0x5452; + riscv_linux_record_tdep.ioctl_TIOCSERCONFIG = 0x5453; + riscv_linux_record_tdep.ioctl_TIOCSERGWILD = 0x5454; + riscv_linux_record_tdep.ioctl_TIOCSERSWILD = 0x5455; + riscv_linux_record_tdep.ioctl_TIOCGLCKTRMIOS = 0x5456; + riscv_linux_record_tdep.ioctl_TIOCSLCKTRMIOS = 0x5457; + riscv_linux_record_tdep.ioctl_TIOCSERGSTRUCT = 0x5458; + riscv_linux_record_tdep.ioctl_TIOCSERGETLSR = 0x5459; + riscv_linux_record_tdep.ioctl_TIOCSERGETMULTI = 0x545a; + riscv_linux_record_tdep.ioctl_TIOCSERSETMULTI = 0x545b; + riscv_linux_record_tdep.ioctl_TIOCMIWAIT = 0x545c; + riscv_linux_record_tdep.ioctl_TIOCGICOUNT = 0x545d; + riscv_linux_record_tdep.ioctl_TIOCGHAYESESP = 0x545e; + riscv_linux_record_tdep.ioctl_TIOCSHAYESESP = 0x545f; + riscv_linux_record_tdep.ioctl_FIOQSIZE = 0x5460; + + /* These values are the second argument of system call "sys_fcntl" + and "sys_fcntl64". They are obtained from Linux Kernel source. */ + riscv_linux_record_tdep.fcntl_F_GETLK = 5; + riscv_linux_record_tdep.fcntl_F_GETLK64 = 12; + riscv_linux_record_tdep.fcntl_F_SETLK64 = 13; + riscv_linux_record_tdep.fcntl_F_SETLKW64 = 14; + + riscv_linux_record_tdep.arg1 = RISCV_A0_REGNUM; + riscv_linux_record_tdep.arg2 = RISCV_A1_REGNUM; + riscv_linux_record_tdep.arg3 = RISCV_A2_REGNUM; + riscv_linux_record_tdep.arg4 = RISCV_A3_REGNUM; + riscv_linux_record_tdep.arg5 = RISCV_A4_REGNUM; + riscv_linux_record_tdep.arg6 = RISCV_A5_REGNUM; +} + /* Initialize RISC-V Linux ABI info. */ static void @@ -205,6 +458,9 @@ riscv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tramp_frame_prepend_unwinder (gdbarch, &riscv_linux_sigframe); tdep->syscall_next_pc = riscv_linux_syscall_next_pc; + tdep->riscv_syscall_record = riscv_linux_syscall_record; + + riscv64_linux_record_tdep_init (gdbarch, riscv_linux_record_tdep); } /* Initialize RISC-V Linux target support. */ diff --git a/gdb/riscv-linux-tdep.h b/gdb/riscv-linux-tdep.h new file mode 100644 index 0000000..9dd9e37 --- /dev/null +++ b/gdb/riscv-linux-tdep.h @@ -0,0 +1,29 @@ +/* Copyright (C) 2024-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_RISCV_LINUX_TDEP_H +#define GDB_RISCV_LINUX_TDEP_H + +#include "linux-record.h" + +/* riscv64_canonicalize_syscall maps from the native riscv Linux set + of syscall ids into a canonical set of syscall ids used by + process record. */ + +extern enum gdb_syscall riscv64_canonicalize_syscall (int syscall); + +#endif /* GDB_RISCV_LINUX_TDEP_H */ diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c index 91f6dff..a735c09 100644 --- a/gdb/riscv-tdep.c +++ b/gdb/riscv-tdep.c @@ -54,9 +54,12 @@ #include "observable.h" #include "prologue-value.h" #include "arch/riscv.h" +#include "record-full.h" #include "riscv-ravenscar-thread.h" #include "gdbsupport/gdb-safe-ctype.h" +#include <vector> + /* The stack must be 16-byte aligned. */ #define SP_ALIGNMENT 16 @@ -1669,6 +1672,11 @@ public: int imm_signed () const { return m_imm.s; } + /* Fetch instruction from target memory at ADDR, return the content of + the instruction, and update LEN with the instruction length. */ + static ULONGEST fetch_instruction (struct gdbarch *gdbarch, + CORE_ADDR addr, int *len); + private: /* Extract 5 bit register field at OFFSET from instruction OPCODE. */ @@ -1814,11 +1822,6 @@ private: m_rs2 = decode_register_index_short (ival, OP_SH_CRS2S); } - /* Fetch instruction from target memory at ADDR, return the content of - the instruction, and update LEN with the instruction length. */ - static ULONGEST fetch_instruction (struct gdbarch *gdbarch, - CORE_ADDR addr, int *len); - /* The length of the instruction in bytes. Should be 2 or 4. */ int m_length; @@ -4433,6 +4436,9 @@ riscv_gdbarch_init (struct gdbarch_info info, set_gdbarch_stap_register_indirection_suffixes (gdbarch, stap_register_indirection_suffixes); + /* Process record-replay */ + set_gdbarch_process_record (gdbarch, riscv_process_record); + /* Hook in OS ABI-specific overrides, if they have been registered. */ gdbarch_init_osabi (info, gdbarch); @@ -4866,3 +4872,673 @@ equivalent change in the disassembler output."), &setriscvcmdlist, &showriscvcmdlist); } + +/* A wrapper to read register under number regnum to address addr. + Returns false if error happened and makes warning. */ + +static bool +try_read (struct regcache *regcache, int regnum, ULONGEST &addr) +{ + gdb_assert (regcache != nullptr); + + if (regcache->raw_read (regnum, &addr) + != register_status::REG_VALID) + { + warning (_("Can not read at address %lx"), addr); + return false; + } + return true; +} + +/* Helper class to record instruction. */ + +class riscv_recorded_insn final +{ +public: + /* Type for saved register. */ + using regnum_type = int; + /* Type for saved memory. First is address, second is length. */ + using memory_type = std::pair<CORE_ADDR, int>; + + /* Enum class that represents which type does recording belong to. */ + enum class record_type + { + UNKNOWN, + ORDINARY, + + /* Corner cases. */ + ECALL, + EBREAK, + }; + +private: + /* Type for set of registers that need to be saved. */ + using recorded_regs = std::vector<regnum_type>; + /* Type for set of memory records that need to be saved. */ + using recorded_mems = std::vector<memory_type>; + + /* Type for memory address, extracted from memory_type. */ + using mem_addr = decltype (std::declval<memory_type> ().first); + /* Type for memory length, extracted from memory_type. */ + using mem_len = decltype (std::declval<memory_type> ().second); + + /* Record type of current instruction. */ + record_type m_record_type = record_type::UNKNOWN; + + /* Flag that represents was there an error in current recording. */ + bool m_error_occured = false; + + /* Set of registers that need to be recorded. */ + recorded_regs m_regs; + /* Set of memory chunks that need to be recorded. */ + recorded_mems m_mems; + + /* Width in bytes of the general purpose registers for GDBARCH, + where recording is happening. */ + int m_xlen = 0; + + /* Helper for decode 16-bit instruction RS1. */ + static regnum_type + decode_crs1_short (ULONGEST opcode) noexcept + { + return ((opcode >> OP_SH_CRS1S) & OP_MASK_CRS1S) + 8; + } + + /* Helper for decode 16-bit instruction RS2. */ + static regnum_type + decode_crs2_short (ULONGEST opcode) noexcept + { + return ((opcode >> OP_SH_CRS2S) & OP_MASK_CRS2S) + 8; + } + + /* Helper for decode 16-bit instruction CRS1. */ + static regnum_type + decode_crs1 (ULONGEST opcode) noexcept + { + return ((opcode >> OP_SH_RD) & OP_MASK_RD); + } + + /* Helper for decode 16-bit instruction CRS2. */ + static regnum_type + decode_crs2 (ULONGEST opcode) noexcept + { + return ((opcode >> OP_SH_CRS2) & OP_MASK_CRS2); + } + + /* Helper for decode 32-bit instruction RD. */ + static regnum_type + decode_rd (ULONGEST ival) noexcept + { + return (ival >> OP_SH_RD) & OP_MASK_RD; + } + + /* Helper for decode 32-bit instruction RS1. */ + static regnum_type + decode_rs1 (ULONGEST ival) noexcept + { + return (ival >> OP_SH_RS1) & OP_MASK_RS1; + } + + /* Helper for decode 32-bit instruction RS2. */ + static regnum_type + decode_rs2 (ULONGEST ival) noexcept + { + return (ival >> OP_SH_RS2) & OP_MASK_RS2; + } + + /* Helper for decode 32-bit instruction CSR. */ + static regnum_type + decode_csr (ULONGEST ival) noexcept + { + return (ival >> OP_SH_CSR) & OP_MASK_CSR; + } + + /* Set ordinary record type. Always returns true. */ + bool + set_ordinary_record_type () noexcept + { + m_record_type = record_type::ORDINARY; + return true; + } + + /* Set error happened. Always returns false. */ + bool + set_error () noexcept + { + m_error_occured = true; + return false; + } + + /* Check if current recording has an error. */ + bool + has_error () const noexcept + { + return m_error_occured; + } + + /* Reads register. Sets error and returns false if error happened. */ + bool + read_reg (struct regcache *regcache, regnum_type reg, + ULONGEST &addr) noexcept + { + gdb_assert (regcache != nullptr); + + if (!try_read (regcache, reg, addr)) + return set_error (); + return true; + } + + /* Save register. Returns true or aborts if exception happened. */ + bool + save_reg (regnum_type regnum) noexcept + { + m_regs.emplace_back (regnum); + return true; + } + + /* Save memory chunk. Returns true or aborts if exception happened. */ + bool + save_mem (mem_addr addr, mem_len len) noexcept + { + m_mems.emplace_back (addr, len); + return true; + } + + /* Returns true if instruction needs only saving pc. */ + static bool + need_save_pc (ULONGEST ival) noexcept + { + return (is_beq_insn (ival) || is_bne_insn (ival) || is_blt_insn (ival) + || is_bge_insn (ival) || is_bltu_insn (ival) || is_bgeu_insn (ival) + || is_fence_insn (ival) || is_pause_insn (ival) + || is_fence_i_insn (ival)); + } + + /* Returns true if instruction is classified. */ + bool + try_save_pc (ULONGEST ival) noexcept + { + if (!need_save_pc (ival)) + return false; + + return set_ordinary_record_type (); + } + + /* Returns true if instruction needs only saving pc and rd. */ + static bool + need_save_pc_rd (ULONGEST ival) noexcept + { + return (is_lui_insn (ival) || is_auipc_insn (ival) || is_jal_insn (ival) + || is_jalr_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival) + || is_lw_insn (ival) || is_lbu_insn (ival) || is_lhu_insn (ival) + || is_addi_insn (ival) || is_slti_insn (ival) + || is_sltiu_insn (ival) || is_xori_insn (ival) || is_ori_insn (ival) + || is_andi_insn (ival) || is_slli_rv32_insn (ival) + || is_srli_rv32_insn (ival) || is_srai_rv32_insn (ival) + || is_add_insn (ival) || is_sub_insn (ival) || is_sll_insn (ival) + || is_slt_insn (ival) || is_sltu_insn (ival) || is_xor_insn (ival) + || is_srl_insn (ival) || is_sra_insn (ival) || is_or_insn (ival) + || is_and_insn (ival) || is_lwu_insn (ival) || is_ld_insn (ival) + || is_slli_insn (ival) || is_srli_insn (ival) || is_srai_insn (ival) + || is_addiw_insn (ival) || is_slliw_insn (ival) + || is_srliw_insn (ival) || is_sraiw_insn (ival) + || is_addw_insn (ival) || is_subw_insn (ival) || is_sllw_insn (ival) + || is_srlw_insn (ival) || is_sraw_insn (ival) || is_mul_insn (ival) + || is_mulh_insn (ival) || is_mulhsu_insn (ival) + || is_mulhu_insn (ival) || is_div_insn (ival) || is_divu_insn (ival) + || is_rem_insn (ival) || is_remu_insn (ival) || is_mulw_insn (ival) + || is_divw_insn (ival) || is_divuw_insn (ival) + || is_remw_insn (ival) || is_remuw_insn (ival) + || is_lr_w_insn (ival) || is_lr_d_insn (ival) + || is_fcvt_w_s_insn (ival) || is_fcvt_wu_s_insn (ival) + || is_fmv_x_s_insn (ival) || is_feq_s_insn (ival) + || is_flt_s_insn (ival) || is_fle_s_insn (ival) + || is_fclass_s_insn (ival) || is_fcvt_l_s_insn (ival) + || is_fcvt_lu_s_insn (ival) || is_feq_d_insn (ival) + || is_flt_d_insn (ival) || is_fle_d_insn (ival) + || is_fclass_d_insn (ival) || is_fcvt_w_d_insn (ival) + || is_fcvt_wu_d_insn (ival) || is_fcvt_l_d_insn (ival) + || is_fcvt_lu_d_insn (ival) || is_fmv_x_d_insn (ival)); + } + + /* Returns true if instruction is classified. This function can set + m_error_occured. */ + bool + try_save_pc_rd (ULONGEST ival) noexcept + { + if (!need_save_pc_rd (ival)) + return false; + + return (!save_reg (decode_rd (ival)) || set_ordinary_record_type ()); + } + + /* Returns true if instruction needs only saving pc and + floating point rd. */ + static bool + need_save_pc_fprd (ULONGEST ival) noexcept + { + return (is_flw_insn (ival) || is_fmadd_s_insn (ival) + || is_fmsub_s_insn (ival) || is_fnmsub_s_insn (ival) + || is_fnmadd_s_insn (ival) || is_fadd_s_insn (ival) + || is_fsub_s_insn (ival) || is_fmul_s_insn (ival) + || is_fdiv_s_insn (ival) || is_fsqrt_s_insn (ival) + || is_fsgnj_s_insn (ival) || is_fsgnjn_s_insn (ival) + || is_fsgnjx_s_insn (ival) || is_fmin_s_insn (ival) + || is_fmax_s_insn (ival) || is_fcvt_s_w_insn (ival) + || is_fcvt_s_wu_insn (ival) || is_fmv_s_x_insn (ival) + || is_fcvt_s_l_insn (ival) || is_fcvt_s_lu_insn (ival) + || is_fld_insn (ival) || is_fmadd_d_insn (ival) + || is_fmsub_d_insn (ival) || is_fnmsub_d_insn (ival) + || is_fnmadd_d_insn (ival) || is_fadd_d_insn (ival) + || is_fsub_d_insn (ival) || is_fmul_d_insn (ival) + || is_fdiv_d_insn (ival) || is_fsqrt_d_insn (ival) + || is_fsgnj_d_insn (ival) || is_fsgnjn_d_insn (ival) + || is_fsgnjx_d_insn (ival) || is_fmin_d_insn (ival) + || is_fmax_d_insn (ival) || is_fcvt_s_d_insn (ival) + || is_fcvt_d_s_insn (ival) || is_fcvt_d_w_insn (ival) + || is_fcvt_d_wu_insn (ival) || is_fcvt_d_l_insn (ival) + || is_fcvt_d_lu_insn (ival) || is_fmv_d_x_insn (ival)); + } + + /* Returns true if instruction is classified. This function can set + m_error_occured. */ + bool + try_save_pc_fprd (ULONGEST ival) noexcept + { + if (!need_save_pc_fprd (ival)) + return false; + + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_rd (ival)) + || set_ordinary_record_type ()); + } + + /* Returns true if instruction needs only saving pc, rd and csr. */ + static bool + need_save_pc_rd_csr (ULONGEST ival) noexcept + { + return (is_csrrw_insn (ival) || is_csrrs_insn (ival) || is_csrrc_insn (ival) + || is_csrrwi_insn (ival) || is_csrrsi_insn (ival) + || is_csrrc_insn (ival)); + } + + /* Returns true if instruction is classified. This function can set + m_error_occured. */ + bool + try_save_pc_rd_csr (ULONGEST ival) noexcept + { + if (!need_save_pc_rd_csr (ival)) + return false; + + return (!save_reg (decode_rd (ival)) + || !save_reg (RISCV_FIRST_CSR_REGNUM + decode_csr (ival)) + || set_ordinary_record_type ()); + } + + /* Returns the size of the memory chunk that needs to be saved if the + instruction belongs to the group that needs only saving pc and memory. + Otherwise returns 0. */ + static mem_len + need_save_pc_mem (ULONGEST ival) noexcept + { + if (is_sb_insn (ival)) + return 1; + if (is_sh_insn (ival)) + return 2; + if (is_sw_insn (ival) || is_fsw_insn (ival)) + return 4; + if (is_sd_insn (ival) || is_fsd_insn (ival)) + return 8; + return 0; + } + + /* Returns true if instruction is classified. This function can set + m_error_occured. */ + bool + try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept + { + gdb_assert (regcache != nullptr); + + mem_addr addr = mem_addr{}; + mem_len len = need_save_pc_mem (ival); + if (len <= 0) + return false; + + mem_len offset = EXTRACT_STYPE_IMM (ival); + return (!read_reg (regcache, decode_rs1 (ival), addr) + || !save_mem (addr + offset, len) || set_ordinary_record_type ()); + } + + /* Returns the size of the memory chunk that needs to be saved if the + instruction belongs to the group that needs only saving pc, rd and memory. + Otherwise returns 0. */ + static mem_len + need_save_pc_rd_mem (ULONGEST ival) noexcept + { + if (is_sc_w_insn (ival) || is_amoswap_w_insn (ival) + || is_amoadd_w_insn (ival) || is_amoxor_w_insn (ival) + || is_amoand_w_insn (ival) || is_amoor_w_insn (ival) + || is_amomin_w_insn (ival) || is_amomax_w_insn (ival) + || is_amominu_w_insn (ival) || is_amomaxu_w_insn (ival)) + return 4; + if (is_sc_d_insn (ival) || is_amoswap_d_insn (ival) + || is_amoadd_d_insn (ival) || is_amoxor_d_insn (ival) + || is_amoand_d_insn (ival) || is_amoor_d_insn (ival) + || is_amomin_d_insn (ival) || is_amomax_d_insn (ival) + || is_amominu_d_insn (ival) || is_amomaxu_d_insn (ival)) + return 8; + return 0; + } + + /* Returns true if instruction is classified. This function can set + m_error_occured. */ + bool + try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept + { + gdb_assert (regcache != nullptr); + + mem_len len = need_save_pc_rd_mem (ival); + mem_addr addr = 0; + if (len <= 0) + return false; + + return (!read_reg (regcache, decode_rs1 (ival), addr) + || !save_mem (addr, len) || !save_reg (decode_rd (ival)) + || set_ordinary_record_type ()); + } + + /* Returns true if instruction is successfully recordered. The length of + the instruction must be equal 4 bytes. */ + bool + record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept + { + gdb_assert (regcache != nullptr); + + if (is_ecall_insn (ival)) + { + m_record_type = record_type::ECALL; + return true; + } + + if (is_ebreak_insn (ival)) + { + m_record_type = record_type::EBREAK; + return true; + } + + if (try_save_pc (ival) || try_save_pc_rd (ival) || try_save_pc_fprd (ival) + || try_save_pc_rd_csr (ival) || try_save_pc_mem (ival, regcache) + || try_save_pc_rd_mem (ival, regcache)) + return !has_error (); + + warning (_("Currently this instruction with len 4(%lx) is unsupported"), + ival); + return false; + } + + /* Returns true if instruction is successfully recordered. The length of + the instruction must be equal 2 bytes. */ + bool + record_insn_len2 (ULONGEST ival, struct regcache *regcache) noexcept + { + gdb_assert (regcache != nullptr); + + mem_addr addr = mem_addr{}; + + /* The order here is very important, because + opcodes of some instructions may be the same. */ + + if (is_c_addi4spn_insn (ival) || is_c_lw_insn (ival) + || (m_xlen == 8 && is_c_ld_insn (ival))) + return (!save_reg (decode_crs2_short (ival)) + || set_ordinary_record_type ()); + + if (is_c_fld_insn (ival) || (m_xlen == 4 && is_c_flw_insn (ival))) + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival)) + || set_ordinary_record_type ()); + + if (is_c_fsd_insn (ival) || (m_xlen == 8 && is_c_sd_insn (ival))) + { + ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LD_IMM (ival)}; + return (!read_reg (regcache, decode_crs1_short (ival), addr) + || !save_mem (addr + offset, 8) || set_ordinary_record_type ()); + } + + if ((m_xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival)) + { + ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LW_IMM (ival)}; + return (!read_reg (regcache, decode_crs1_short (ival), addr) + || !save_mem (addr + offset, 4) || set_ordinary_record_type ()); + } + + if (is_c_nop_insn (ival)) + return set_ordinary_record_type (); + + if (is_c_addi_insn (ival)) + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ()); + + if (m_xlen == 4 && is_c_jal_insn (ival)) + return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ()); + + if ((m_xlen == 8 && is_c_addiw_insn (ival)) || is_c_li_insn (ival)) + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ()); + + if (is_c_addi16sp_insn (ival)) + return (!save_reg (RISCV_SP_REGNUM) || set_ordinary_record_type ()); + + if (is_c_lui_insn (ival)) + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ()); + + if (is_c_srli_insn (ival) || is_c_srai_insn (ival) || is_c_andi_insn (ival) + || is_c_sub_insn (ival) || is_c_xor_insn (ival) || is_c_or_insn (ival) + || is_c_and_insn (ival) || (m_xlen == 8 && is_c_subw_insn (ival)) + || (m_xlen == 8 && is_c_addw_insn (ival))) + return (!save_reg (decode_crs1_short (ival)) + || set_ordinary_record_type ()); + + if (is_c_j_insn (ival) || is_c_beqz_insn (ival) || is_c_bnez_insn (ival)) + return set_ordinary_record_type (); + + if (is_c_slli_insn (ival)) + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ()); + + if (is_c_fldsp_insn (ival) || (m_xlen == 4 && is_c_flwsp_insn (ival))) + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival)) + || set_ordinary_record_type ()); + + if (is_c_lwsp_insn (ival) || (m_xlen == 8 && is_c_ldsp_insn (ival))) + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ()); + + if (is_c_jr_insn (ival)) + return set_ordinary_record_type (); + + if (is_c_mv_insn (ival)) + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ()); + + if (is_c_ebreak_insn (ival)) + { + m_record_type = record_type::EBREAK; + return true; + } + + if (is_c_jalr_insn (ival)) + return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ()); + + if (is_c_add_insn (ival)) + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ()); + + if (is_c_fsdsp_insn (ival) || (m_xlen == 8 && is_c_sdsp_insn (ival))) + { + ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SDSP_IMM (ival)}; + return (!read_reg (regcache, RISCV_SP_REGNUM, addr) + || !save_mem (addr + offset, 8) || set_ordinary_record_type ()); + } + + if (is_c_swsp_insn (ival) || (m_xlen == 4 && is_c_fswsp_insn (ival))) + { + ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SWSP_IMM (ival)}; + return (!read_reg (regcache, RISCV_SP_REGNUM, addr) + || !save_mem (addr + offset, 4) || set_ordinary_record_type ()); + } + + warning (_("Currently this instruction with len 2(%lx) is unsupported"), + ival); + return false; + } + +public: + /* Iterator for registers that need to be recorded. */ + using regs_iter = recorded_regs::const_iterator; + /* Iterator for memory chunks that need to be recorded. */ + using mems_iter = recorded_mems::const_iterator; + + /* Record instruction at address addr. Returns false if error happened. */ + bool + record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept + { + gdb_assert (gdbarch != nullptr); + gdb_assert (regcache != nullptr); + + int m_length = 0; + m_xlen = riscv_isa_xlen (gdbarch); + ULONGEST ival = riscv_insn::fetch_instruction (gdbarch, addr, &m_length); + if (!save_reg (RISCV_PC_REGNUM)) + return false; + + if (m_length == 4) + return record_insn_len4 (ival, regcache); + + if (m_length == 2) + return record_insn_len2 (ival, regcache); + + /* 6 bytes or more. If the instruction is longer than 8 bytes, we don't + have full instruction bits in ival. At least, such long instructions + are not defined yet, so just ignore it. */ + gdb_assert (m_length > 0 && m_length % 2 == 0); + + warning (_("Can not record unknown instruction (opcode = %lx)"), ival); + return false; + } + + /* Get record type of instruction. */ + record_type + get_record_type () const noexcept + { + return m_record_type; + } + + /* Returns an iterator to the beginning of the registers that need + to be saved. */ + regs_iter + regs_begin () const noexcept + { + return m_regs.begin (); + } + + /* Returns an iterator to the end of the registers that need + to be saved. */ + regs_iter + regs_end () const noexcept + { + return m_regs.end (); + } + + /* Returns an iterator to the beginning of the memory chunks that need + to be saved. */ + mems_iter + mems_begin () const noexcept + { + return m_mems.begin (); + } + + /* Returns an iterator to the end of the memory chunks that need + to be saved. */ + mems_iter + mems_end () const noexcept + { + return m_mems.end (); + } +}; + +/* A helper function to record instruction using record API. */ + +static int +riscv_record_insn_details (struct gdbarch *gdbarch, struct regcache *regcache, + const riscv_recorded_insn &insn) +{ + gdb_assert (gdbarch != nullptr); + gdb_assert (regcache != nullptr); + + riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch); + auto regs_begin = insn.regs_begin (); + auto regs_end = insn.regs_end (); + if (std::any_of (regs_begin, + regs_end, + [®cache] (auto &®_it) + { + return record_full_arch_list_add_reg (regcache, reg_it); + })) + return -1; + + auto mems_begin = insn.mems_begin (); + auto mems_end = insn.mems_end (); + if (std::any_of (mems_begin, + mems_end, + [] (auto &&mem_it) + { + return record_full_arch_list_add_mem (mem_it.first, + mem_it.second); + })) + return -1; + + switch (insn.get_record_type ()) + { + case riscv_recorded_insn::record_type::ORDINARY: + break; + + case riscv_recorded_insn::record_type::ECALL: + { + if (!tdep->riscv_syscall_record) + { + warning (_("Syscall record is not supported")); + return -1; + } + ULONGEST reg_val = ULONGEST{}; + if (!try_read (regcache, RISCV_A7_REGNUM, reg_val)) + return -1; + return tdep->riscv_syscall_record (regcache, reg_val); + } + + case riscv_recorded_insn::record_type::EBREAK: + break; + + default: + return -1; + } + return 0; +} + +/* Parse the current instruction and record the values of the registers and + memory that will be changed in current instruction to record_arch_list. + Return -1 if something is wrong. */ + +int +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache, + CORE_ADDR addr) +{ + gdb_assert (gdbarch != nullptr); + gdb_assert (regcache != nullptr); + + riscv_recorded_insn insn; + if (!insn.record (gdbarch, regcache, addr)) + { + record_full_arch_list_add_end (); + return -1; + } + + int ret_val = riscv_record_insn_details (gdbarch, regcache, insn); + + if (record_full_arch_list_add_end ()) + return -1; + + return ret_val; +} diff --git a/gdb/riscv-tdep.h b/gdb/riscv-tdep.h index ad1e959..2903aef 100644 --- a/gdb/riscv-tdep.h +++ b/gdb/riscv-tdep.h @@ -35,7 +35,11 @@ enum RISCV_FP_REGNUM = 8, /* Frame Pointer. */ RISCV_A0_REGNUM = 10, /* First argument. */ RISCV_A1_REGNUM = 11, /* Second argument. */ - RISCV_A7_REGNUM = 17, /* Seventh argument. */ + RISCV_A2_REGNUM = 12, /* Third argument. */ + RISCV_A3_REGNUM = 13, /* Forth argument. */ + RISCV_A4_REGNUM = 14, /* Fifth argument. */ + RISCV_A5_REGNUM = 15, /* Sixth argument. */ + RISCV_A7_REGNUM = 17, /* Register to pass syscall number. */ RISCV_PC_REGNUM = 32, /* Program Counter. */ RISCV_NUM_INTEGER_REGS = 32, @@ -113,6 +117,10 @@ struct riscv_gdbarch_tdep : gdbarch_tdep_base /* Return the expected next PC assuming FRAME is stopped at a syscall instruction. */ CORE_ADDR (*syscall_next_pc) (const frame_info_ptr &frame) = nullptr; + + /* Syscall record. */ + int (*riscv_syscall_record) (struct regcache *regcache, + unsigned long svc_number) = nullptr; }; @@ -177,6 +185,12 @@ extern void riscv_supply_regset (const struct regset *regset, struct regcache *regcache, int regnum, const void *regs, size_t len); +/* Parse the current instruction, and record the values of the + registers and memory that will be changed by the current + instruction. Returns -1 if something goes wrong, 0 otherwise. */ +extern int riscv_process_record (struct gdbarch *gdbarch, + struct regcache *regcache, CORE_ADDR addr); + /* The names of the RISC-V target description features. */ extern const char *riscv_feature_name_csr; diff --git a/gdb/s390-tdep.c b/gdb/s390-tdep.c index d030a4d..a3b7658 100644 --- a/gdb/s390-tdep.c +++ b/gdb/s390-tdep.c @@ -41,6 +41,8 @@ #include "value.h" #include "inferior.h" #include "dwarf2/loc.h" +#include "gdbsupport/selftest.h" +#include "gdb/disasm-selftests.h" #include "features/s390-linux32.c" #include "features/s390x-linux64.c" @@ -7468,6 +7470,51 @@ s390_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) return gdbarch; } +#if GDB_SELF_TEST +namespace selftests { + +/* Return bfd_arch_info representing s390x. */ + +static const bfd_arch_info * +bfd_arch_info_s390x () +{ + return bfd_lookup_arch (bfd_arch_s390, bfd_mach_s390_64); +} + +/* Return gdbarch representing s390x. */ + +static gdbarch * +gdbarch_s390x () +{ + struct gdbarch_info info; + info.bfd_arch_info = bfd_arch_info_s390x (); + if (info.bfd_arch_info == nullptr) + return nullptr; + + info.osabi = GDB_OSABI_NONE; + return gdbarch_find_by_info (info); +} + +/* Check disassembly of s390x instructions. */ + +static void +disassemble_s390x () +{ + gdbarch *gdbarch = gdbarch_s390x (); + if (gdbarch == nullptr) + return; + + scoped_restore disassembler_options_restore + = make_scoped_restore (&s390_disassembler_options, "zarch"); + + gdb::byte_vector insn = { 0xb9, 0x68, 0x00, 0x03 }; + disassemble_insn (gdbarch, insn, "clzg\t%r0,%r3"); +} + +} /* namespace selftests */ + +#endif /* GDB_SELF_TEST */ + void _initialize_s390_tdep (); void _initialize_s390_tdep () @@ -7477,4 +7524,9 @@ _initialize_s390_tdep () initialize_tdesc_s390_linux32 (); initialize_tdesc_s390x_linux64 (); + +#if GDB_SELF_TEST + selftests::register_test ("disassemble-s390x", + selftests::disassemble_s390x); +#endif /* GDB_SELF_TEST */ } diff --git a/gdb/solib-svr4.c b/gdb/solib-svr4.c index 83cb389..2a2745d 100644 --- a/gdb/solib-svr4.c +++ b/gdb/solib-svr4.c @@ -451,6 +451,12 @@ svr4_maybe_add_namespace (svr4_info *info, CORE_ADDR lmid) info->namespace_id.push_back (lmid); info->active_namespaces.insert (i); + + /* Create or update the convenience variable "active_namespaces". + It only needs to be updated here, as this only changes when a + dlmopen or dlclose call happens. */ + set_internalvar_integer (lookup_internalvar ("_active_linker_namespaces"), + info->active_namespaces.size ()); } /* Return whether DEBUG_BASE is the default namespace of INFO. */ @@ -3552,6 +3558,54 @@ svr4_num_active_namespaces () return info->active_namespaces.size (); } +/* See solib_ops::get_solibs_in_ns in solist.h. */ +static std::vector<const solib *> +svr4_get_solibs_in_ns (int nsid) +{ + std::vector<const solib*> ns_solibs; + svr4_info *info = get_svr4_info (current_program_space); + + /* If the namespace ID is inactive, there will be no active + libraries, so we can have an early exit, as a treat. */ + if (info->active_namespaces.count (nsid) != 1) + return ns_solibs; + + /* Since we only have the names of solibs in a given namespace, + we'll need to walk through the solib list of the inferior and + find which solib objects correspond to which svr4_so. We create + an unordered map with the names and lm_info to check things + faster, and to be able to remove SOs from the map, to avoid + returning the dynamic linker multiple times. */ + CORE_ADDR debug_base = info->namespace_id[nsid]; + std::unordered_map<std::string, const lm_info_svr4 *> namespace_solibs; + for (svr4_so &so : info->solib_lists[debug_base]) + { + namespace_solibs[so.name] + = gdb::checked_static_cast<const lm_info_svr4 *> + (so.lm_info.get ()); + } + for (const solib &so: current_program_space->solibs ()) + { + auto *lm_inferior + = gdb::checked_static_cast<const lm_info_svr4 *> (so.lm_info.get ()); + + /* This is inspired by the svr4_same, by finding the svr4_so object + in the map, and then double checking if the lm_info is considered + the same. */ + if (namespace_solibs.count (so.so_original_name) > 0 + && namespace_solibs[so.so_original_name]->l_addr_inferior + == lm_inferior->l_addr_inferior) + { + ns_solibs.push_back (&so); + /* Remove the SO from the map, so that we don't end up + printing the dynamic linker multiple times. */ + namespace_solibs.erase (so.so_original_name); + } + } + + return ns_solibs; +} + const struct solib_ops svr4_so_ops = { svr4_relocate_section_addresses, @@ -3569,6 +3623,7 @@ const struct solib_ops svr4_so_ops = svr4_find_solib_addr, svr4_find_solib_ns, svr4_num_active_namespaces, + svr4_get_solibs_in_ns, }; void _initialize_svr4_solib (); diff --git a/gdb/solib.c b/gdb/solib.c index 4876f1a..5c5cfbd 100644 --- a/gdb/solib.c +++ b/gdb/solib.c @@ -1010,84 +1010,61 @@ solib_add (const char *pattern, int from_tty, int readsyms) } } -/* Implement the "info sharedlibrary" command. Walk through the - shared library list and print information about each attached - library matching PATTERN. If PATTERN is elided, print them - all. */ +/* Helper function for "info sharedlibrary" and "info namespace". + This receives a list of solibs to be printed, and prints a table + with all the relevant data. If PRINT_NAMESPACE is true, figure out + the solib_ops of the current gdbarch, to calculate the namespace + that contains an solib. + Returns true if one or more solibs are missing debug information, + false otherwise. */ static void -info_sharedlibrary_command (const char *pattern, int from_tty) +print_solib_list_table (std::vector<const solib *> solib_list, + bool print_namespace) { - bool so_missing_debug_info = false; - int addr_width; - int nr_libs; gdbarch *gdbarch = current_inferior ()->arch (); - struct ui_out *uiout = current_uiout; - - if (pattern) - { - char *re_err = re_comp (pattern); - - if (re_err) - error (_ ("Invalid regexp: %s"), re_err); - } - /* "0x", a little whitespace, and two hex digits per byte of pointers. */ - addr_width = 4 + (gdbarch_ptr_bit (gdbarch) / 4); - - update_solib_list (from_tty); - - /* ui_out_emit_table table_emitter needs to know the number of rows, - so we need to make two passes over the libs. */ + int addr_width = 4 + (gdbarch_ptr_bit (gdbarch) / 4); + const solib_ops *ops = gdbarch_so_ops (gdbarch); + struct ui_out *uiout = current_uiout; + bool so_missing_debug_info = false; - nr_libs = 0; - for (const solib &so : current_program_space->solibs ()) - { - if (!so.so_name.empty ()) - { - if (pattern && !re_exec (so.so_name.c_str ())) - continue; - ++nr_libs; - } - } + /* There are 3 conditions for this command to print solib namespaces, + first PRINT_NAMESPACE has to be true, second the solib_ops has to + support multiple namespaces, and third there must be more than one + active namespace. Fold all these into the PRINT_NAMESPACE condition. */ + print_namespace = print_namespace && ops->num_active_namespaces != nullptr + && ops->num_active_namespaces () > 1; - /* How many columns the table should have. If the inferior has - more than one namespace active, we need a column to show that. */ int num_cols = 4; - const solib_ops *ops = gdbarch_so_ops (gdbarch); - if (ops->num_active_namespaces != nullptr - && ops->num_active_namespaces () > 1) + if (print_namespace) num_cols++; { - ui_out_emit_table table_emitter (uiout, num_cols, nr_libs, + ui_out_emit_table table_emitter (uiout, num_cols, solib_list.size (), "SharedLibraryTable"); /* The "- 1" is because ui_out adds one space between columns. */ uiout->table_header (addr_width - 1, ui_left, "from", "From"); uiout->table_header (addr_width - 1, ui_left, "to", "To"); - if (ops->num_active_namespaces != nullptr - && ops->num_active_namespaces () > 1) + if (print_namespace) uiout->table_header (5, ui_left, "namespace", "NS"); uiout->table_header (12 - 1, ui_left, "syms-read", "Syms Read"); uiout->table_header (0, ui_noalign, "name", "Shared Object Library"); uiout->table_body (); - for (const solib &so : current_program_space->solibs ()) + for (const solib *so : solib_list) { - if (so.so_name.empty ()) - continue; - - if (pattern && !re_exec (so.so_name.c_str ())) + if (so->so_name.empty ()) continue; ui_out_emit_tuple tuple_emitter (uiout, "lib"); - if (so.addr_high != 0) + if (so->addr_high != 0) { - uiout->field_core_addr ("from", gdbarch, so.addr_low); - uiout->field_core_addr ("to", gdbarch, so.addr_high); + uiout->field_core_addr ("from", gdbarch, so->addr_low); + uiout->field_core_addr ("to", gdbarch, so->addr_high); } else { @@ -1095,12 +1072,11 @@ info_sharedlibrary_command (const char *pattern, int from_tty) uiout->field_skip ("to"); } - if (ops->num_active_namespaces != nullptr - && ops->num_active_namespaces ()> 1) + if (print_namespace) { try { - uiout->field_fmt ("namespace", "[[%d]]", ops->find_solib_ns (so)); + uiout->field_fmt ("namespace", "[[%d]]", ops->find_solib_ns (*so)); } catch (const gdb_exception_error &er) { @@ -1109,32 +1085,157 @@ info_sharedlibrary_command (const char *pattern, int from_tty) } if (!top_level_interpreter ()->interp_ui_out ()->is_mi_like_p () - && so.symbols_loaded && !objfile_has_symbols (so.objfile)) + && so->symbols_loaded && !objfile_has_symbols (so->objfile)) { so_missing_debug_info = true; uiout->field_string ("syms-read", "Yes (*)"); } else - uiout->field_string ("syms-read", so.symbols_loaded ? "Yes" : "No"); + uiout->field_string ("syms-read", so->symbols_loaded ? "Yes" : "No"); - uiout->field_string ("name", so.so_name, file_name_style.style ()); + uiout->field_string ("name", so->so_name, file_name_style.style ()); uiout->text ("\n"); } } - if (nr_libs == 0) + if (so_missing_debug_info) + uiout->message (_ ("(*): Shared library is missing " + "debugging information.\n")); +} + +/* Implement the "info sharedlibrary" command. Walk through the + shared library list and print information about each attached + library matching PATTERN. If PATTERN is elided, print them + all. */ + +static void +info_sharedlibrary_command (const char *pattern, int from_tty) +{ + struct ui_out *uiout = current_uiout; + + if (pattern) + { + char *re_err = re_comp (pattern); + + if (re_err) + error (_ ("Invalid regexp: %s"), re_err); + } + + update_solib_list (from_tty); + + /* ui_out_emit_table table_emitter needs to know the number of rows, + so we need to make two passes over the libs. */ + + std::vector<const solib *> print_libs; + for (const solib &so : current_program_space->solibs ()) + { + if (!so.so_name.empty ()) + { + if (pattern && !re_exec (so.so_name.c_str ())) + continue; + print_libs.push_back (&so); + } + } + + print_solib_list_table (print_libs, true); + + if (print_libs.size () == 0) { if (pattern) uiout->message (_ ("No shared libraries matched.\n")); else uiout->message (_ ("No shared libraries loaded at this time.\n")); } +} + +/* Implement the "info linker-namespaces" command. If the current + gdbarch's solib_ops object does not support multiple namespaces, + this command would just look like "info sharedlibrary", so point + the user to that command instead. + If solib_ops does support multiple namespaces, this command + will group the libraries by linker namespace, or only print the + libraries in the supplied namespace. */ +static void +info_linker_namespace_command (const char *pattern, int from_tty) +{ + const solib_ops *ops = gdbarch_so_ops (current_inferior ()->arch ()); + /* This command only really makes sense for inferiors that support + linker namespaces, so we can leave early. */ + if (ops->num_active_namespaces == nullptr) + error (_("Current inferior does not support linker namespaces." \ + "Use \"info sharedlibrary\" instead")); + + struct ui_out *uiout = current_uiout; + std::vector<std::pair<int, std::vector<const solib *>>> all_solibs_to_print; + + if (pattern != nullptr) + while (*pattern == ' ') + pattern++; + + if (pattern == nullptr || pattern[0] == '\0') + { + uiout->message (_ ("There are %d linker namespaces loaded\n"), + ops->num_active_namespaces ()); + + int printed = 0; + for (int i = 0; printed < ops->num_active_namespaces (); i++) + { + std::vector<const solib *> solibs_to_print + = ops->get_solibs_in_ns (i); + if (solibs_to_print.size () > 0) + { + all_solibs_to_print.push_back (std::make_pair + (i, solibs_to_print)); + printed++; + } + } + } else { - if (so_missing_debug_info) - uiout->message (_ ("(*): Shared library is missing " - "debugging information.\n")); + int ns; + /* Check if the pattern includes the optional [[ and ]] decorators. + To match multiple occurrences, '+' needs to be escaped, and every + escape sequence must be doubled to survive the compiler pass. */ + re_comp ("^\\[\\[[0-9]\\+\\]\\]$"); + if (re_exec (pattern)) + ns = strtol (pattern+2, nullptr, 10); + else + { + char * end = nullptr; + ns = strtol (pattern, &end, 10); + if (end[0] != '\0') + error (_ ("Invalid linker namespace identifier: %s"), pattern); + } + + all_solibs_to_print.push_back + (std::make_pair (ns, ops->get_solibs_in_ns (ns))); + } + + bool ns_separator = false; + + for (auto &solibs_pair : all_solibs_to_print) + { + if (ns_separator) + uiout->message ("\n\n"); + else + ns_separator = true; + int ns = solibs_pair.first; + std::vector<const solib *> solibs_to_print = solibs_pair.second; + if (solibs_to_print.size () == 0) + { + uiout->message (_("Linker namespace [[%d]] is not active.\n"), ns); + /* If we got here, a specific namespace was requested, so there + will only be one vector. We can leave early. */ + break; + } + uiout->message + (_ ("There are %ld libraries loaded in linker namespace [[%d]]\n"), + solibs_to_print.size (), ns); + uiout->message + (_ ("Displaying libraries for linker namespace [[%d]]:\n"), ns); + + print_solib_list_table (solibs_to_print, false); } } @@ -1715,6 +1816,44 @@ default_find_solib_addr (solib &so) return {}; } +/* Implementation of the current_linker_namespace convenience variable. + This returns the GDB internal identifier of the linker namespace, + for the current frame, in the form '[[<number>]]'. If the inferior + doesn't support linker namespaces, this always returns [[0]]. */ + +static value * +current_linker_namespace_make_value (gdbarch *gdbarch, internalvar *var, + void *ignore) +{ + const solib_ops *ops = gdbarch_so_ops (gdbarch); + const language_defn *lang = language_def (get_frame_language + (get_current_frame ())); + std::string nsid = "[[0]]"; + if (ops->find_solib_ns != nullptr) + { + CORE_ADDR curr_pc = get_frame_pc (get_current_frame ()); + for (const solib &so : current_program_space->solibs ()) + if (solib_contains_address_p (so, curr_pc)) + { + nsid = string_printf ("[[%d]]", ops->find_solib_ns (so)); + break; + } + } + + + /* If the PC is not in an SO, or the solib_ops doesn't support + linker namespaces, the inferior is in the default namespace. */ + return lang->value_string (gdbarch, nsid.c_str (), nsid.length ()); +} + +/* Implementation of `$_current_linker_namespace' variable. */ + +static const struct internalvar_funcs current_linker_namespace_funcs = +{ + current_linker_namespace_make_value, + nullptr, +}; + void _initialize_solib (); void @@ -1727,6 +1866,13 @@ _initialize_solib () }, "solib"); + /* Convenience variables for debugging linker namespaces. These are + set here, even if the solib_ops doesn't support them, + for consistency. */ + create_internalvar_type_lazy ("_current_linker_namespace", + ¤t_linker_namespace_funcs, nullptr); + set_internalvar_integer (lookup_internalvar ("_active_linker_namespaces"), 1); + add_com ( "sharedlibrary", class_files, sharedlibrary_command, _ ("Load shared object library symbols for files matching REGEXP.")); @@ -1737,6 +1883,9 @@ _initialize_solib () add_com ("nosharedlibrary", class_files, no_shared_libraries_command, _ ("Unload all shared object library symbols.")); + add_info ("linker-namespaces", info_linker_namespace_command, + _ ("Get information about linker namespaces in the inferior.")); + add_setshow_boolean_cmd ("auto-solib-add", class_support, &auto_solib_add, _ ("\ Set autoloading of shared library symbols."), diff --git a/gdb/solist.h b/gdb/solist.h index 0b7bbf9..6ab5a06 100644 --- a/gdb/solist.h +++ b/gdb/solist.h @@ -194,6 +194,10 @@ struct solib_ops /* Returns the number of active namespaces in the inferior. */ int (*num_active_namespaces) (); + + /* Returns all solibs for a given namespace. If the namespace is not + active, returns an empty vector. */ + std::vector<const solib *> (*get_solibs_in_ns) (int ns); }; /* A unique pointer to a so_list. */ diff --git a/gdb/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py new file mode 100755 index 0000000..30e52b7 --- /dev/null +++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +# pylint: disable=invalid-name + +# Copyright (C) 2024-2025 Free Software Foundation, Inc. +# Contributed by Timur Golubovich + +# 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/>. + + +# To get help message for this script, run: +# ./gdb/syscalls/riscv-canonicalize-syscall-gen.py --help + +# Execution result: + +# usage: riscv-canonicalize-syscall-gen.py [-h] -i INPUT +# +# Generate file gdb/riscv-canonicalize-syscall-gen.c from path to riscv linux syscalls. +# +# options: +# -h, --help show this help message and exit +# -i INPUT, --input INPUT +# path to riscv linux syscalls (glibc/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h) + +import argparse +import re +import sys +from pathlib import Path as _Path + +head = """\ +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py + + Copyright (C) 2024-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 "defs.h" +#include "riscv-linux-tdep.h" + +/* riscv64_canonicalize_syscall maps from the native riscv 64 Linux set + of syscall ids into a canonical set of syscall ids used by + process record. */ + +enum gdb_syscall +riscv64_canonicalize_syscall (int syscall) +{ + switch (syscall) + { +""" + +tail = """\ + default: + return gdb_sys_no_syscall; + } +} +""" + + +class Generator: + def _get_gdb_syscalls(self, gdb_syscalls_path: _Path) -> list[str]: + gdb_syscalls: list[str] = [] + with open(gdb_syscalls_path, "r", encoding="UTF-8") as file: + lines = file.readlines() + for line in lines: + match = re.search(r"\s*(?P<name>gdb_sys_[^S]+)\S*=", line) + if match: + gdb_syscalls.append(match.group("name").strip()) + return gdb_syscalls + + def _get_canon_syscalls_lines(self, syscalls_path: _Path, gdb_syscalls: list[str]) -> list[str]: + canon_syscalls: dict[int, str] = {} + with open(syscalls_path, "r", encoding="UTF-8") as file: + lines = file.readlines() + for line in lines: + match = re.match(r"#define\s+__NR_(?P<name>[^\s]+)\s+(?P<number>\d+)", line) + if match: + syscall_name = match.group("name") + syscall_num = int(match.group("number")) + gdb_syscall_name = f"gdb_sys_{syscall_name}" + if gdb_syscall_name in gdb_syscalls: + value = f" case {syscall_num}: return {gdb_syscall_name};\n" + canon_syscalls[syscall_num] = value + # this is a place for corner cases + elif syscall_name == "mmap": + gdb_old_syscall_name = "gdb_old_mmap" + value = f" case {syscall_num}: return {gdb_old_syscall_name};\n" + canon_syscalls[syscall_num] = value + else: + value = f" /* case {syscall_num}: return {gdb_syscall_name}; */\n" + canon_syscalls[syscall_num] = value + return [canon_syscalls[syscall_num] for syscall_num in sorted(canon_syscalls)] + + def generate(self, syscalls_path: _Path) -> None: + repo_path = _Path(__file__).parent.parent.parent + gdb_syscalls_path = repo_path / "gdb" / "linux-record.h" + canon_syscalls_path = repo_path / "gdb" / "riscv-canonicalize-syscall-gen.c" + + gdb_syscalls = self._get_gdb_syscalls(gdb_syscalls_path) + canon_syscalls_lines = self._get_canon_syscalls_lines(syscalls_path, gdb_syscalls) + + with open(canon_syscalls_path, "w", encoding="UTF-8") as file: + file.writelines(head) + file.writelines(canon_syscalls_lines) + file.writelines(tail) + + +help_message = """\ +Generate file gdb/riscv-canonicalize-syscall-gen.c +from path to riscv linux syscalls. +""" + + +def setup_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(description=help_message) + parser.add_argument( + "-i", + "--input", + type=_Path, + required=True, + help="path to riscv linux syscalls (glibc/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h)", + ) + return parser + + +def main(argv: list[str]) -> int: + try: + parser = setup_parser() + args = parser.parse_args(argv) + generator = Generator() + generator.generate(args.input) + return 0 + except RuntimeError as e: + print(str(e)) + return -1 + + +if __name__ == "__main__": + sys.exit(main(sys.argv[1:])) diff --git a/gdb/testsuite/gdb.base/bg-execution-repeat.exp b/gdb/testsuite/gdb.base/bg-execution-repeat.exp index b1496ee..d5580fb 100644 --- a/gdb/testsuite/gdb.base/bg-execution-repeat.exp +++ b/gdb/testsuite/gdb.base/bg-execution-repeat.exp @@ -67,6 +67,17 @@ proc test {continue_cmd} { # enable the "set var" command with an interrupt / continue& pair. gdb_test -no-prompt-anchor "interrupt" + set test "interrupt received" + set re [string_to_regexp "Program received signal SIGINT, Interrupt."] + gdb_expect { + -re $re { + pass $test + } + timeout { + fail "$test (timeout)" + } + } + # Allow the breakpoint to trigger. gdb_test -no-prompt-anchor "set var do_wait=0" diff --git a/gdb/testsuite/gdb.base/default.exp b/gdb/testsuite/gdb.base/default.exp index d4d6b20..3abd049 100644 --- a/gdb/testsuite/gdb.base/default.exp +++ b/gdb/testsuite/gdb.base/default.exp @@ -699,6 +699,8 @@ set show_conv_list \ {$_gdb_minor = 1} \ {$_shell_exitsignal = void} \ {$_shell_exitcode = 0} \ + {$_active_linker_namespaces = 1} \ + {$_current_linker_namespace = <error: No registers.>}\ } if [allow_python_tests] { append show_conv_list \ diff --git a/gdb/testsuite/gdb.base/dlmopen-ns-ids-main.c b/gdb/testsuite/gdb.base/dlmopen-ns-ids-main.c index 3bcd819..c7c038a 100644 --- a/gdb/testsuite/gdb.base/dlmopen-ns-ids-main.c +++ b/gdb/testsuite/gdb.base/dlmopen-ns-ids-main.c @@ -41,6 +41,12 @@ main (void) handle[2] = dlmopen (LM_ID_NEWLM, DSO_NAME, RTLD_LAZY | RTLD_LOCAL); assert (handle[2] != NULL); + for (dl = 2; dl >= 0; dl--) + { + fun = dlsym (handle[dl], "inc"); + fun (dl); + } + dlclose (handle[0]); /* TAG: first dlclose */ dlclose (handle[1]); /* TAG: second dlclose */ dlclose (handle[2]); /* TAG: third dlclose */ diff --git a/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp b/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp index 3ddc07e..8f52199 100644 --- a/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp +++ b/gdb/testsuite/gdb.base/dlmopen-ns-ids.exp @@ -105,4 +105,137 @@ proc test_info_shared {} { "after unloading everything" } +# Run all tests related to the linkage namespaces convenience +# variables, _active_namespaces and _current_namespaces. +proc_with_prefix test_conv_vars {} { + clean_restart $::binfile + + gdb_test "print \$_active_linker_namespaces" "1" \ + "1 namespace before starting inferior" + gdb_test "print \$_current_linker_namespace" "No registers." \ + "No current namespace before starting inferior" + + if { ![runto_main] } { + return + } + + gdb_test "print \$_active_linker_namespaces" "1" \ + "Before activating namespaces" + gdb_test "print \$_current_linker_namespace" ".*\"\\\[\\\[0\\\]\\\]\"" \ + "Still in the default namespace" + + gdb_breakpoint "inc" allow-pending + gdb_breakpoint [gdb_get_line_number "TAG: first dlclose"] + + foreach_with_prefix dl {3 2 1} { + gdb_continue_to_breakpoint "inc" + + gdb_test "print \$_current_linker_namespace" ".*\"\\\[\\\[$dl\\\]\\\]\"" \ + "Verify we're in namespace $dl" + } + + gdb_continue_to_breakpoint "first dlclose" + gdb_test "print \$_active_linker_namespaces" "4" "all SOs loaded" + + gdb_test "next" ".*second dlclose.*" "close one SO" + gdb_test "print \$_active_linker_namespaces" "3" "one SOs unloaded" + gdb_test "next" ".*third dlclose.*" "close another SO" + gdb_test "print \$_active_linker_namespaces" "2" "two SOs unloaded" + + # Restarting GDB so that we can test setting a breakpoint + # using the convenience variable, while a proper bp syntax + # isn't implemented for namespaces + clean_restart $::binfile + if {![runto_main]} { + return + } + + # We need to load one SO because you can't have confitional + # breakpoints and pending breakpoints at the same time with + # gdb_breakpoint. + gdb_test "next" ".*assert.*" "load the first SO" + gdb_breakpoint "inc if \$_streq(\$_current_linker_namespace, \"\[\[2\]\]\")" + gdb_continue_to_breakpoint "inc" + gdb_continue_to_end "" continue 1 +} + +# Run several tests relating to the command "info namespaces". +proc test_info_linker_namespaces {} { + clean_restart $::binfile + + if { ![runto_main] } { + return + } + + with_test_prefix "info linker-namespaces" { + gdb_breakpoint [gdb_get_line_number "TAG: first dlclose"] + gdb_continue_to_breakpoint "TAG: first dlclose" + } + + # First, test printing a single namespace, and ensure all of + # them are correct, using both syntaxes. + set found_all_libs false + gdb_test_multiple "info linker-namespaces \[\[0\]\]" "print namespace 0" -lbl { + -re "^\r\nThere are ($::decimal) libraries loaded in linker namespace \\\[\\\[0\\\]\\\]" { + # Some systems may add libc and libm to every loaded namespace, + # others may load only one or neither, because the SO doesn't + # actually use either library. The best we can do is check if + # we found the dynamic linker, and up to 2 more libraries. + set libs $expect_out(1,string) + set found_all_libs [expr $libs - 1 <= 2] + exp_continue + } + -re "^\r\n$::gdb_prompt $" { + gdb_assert $found_all_libs "the correct number of libraries was reported" + } + -re "(^\r\n)?\[^\r\n\]+(?=\r\n)" { + exp_continue + } + } + foreach_with_prefix ns {1 2 3} { + set found_test_so false + set found_all_libs false + gdb_test_multiple "info linker-namespaces $ns" "print namespace $ns" -lbl { + -re "^\r\nThere are ($::decimal) libraries loaded in linker namespace \\\[\\\[$ns\\\]\\\]" { + set libs $expect_out(1,string) + # Some systems may add libc and libm to every loaded namespace, + # others may load only one or neither, because the SO doesn't + # actually use either library. The best we can do is check if + # we found the dynamic linker, the test SO, and maybe up to 2 + # more libraries. + set found_all_libs [expr $libs - 2 <= 2] + exp_continue + } + -re "^\r\n\[^\r\n\]+${::binfile_lib}\[^\r\n\]*(?=\r\n)" { + set found_test_so true + exp_continue + } + -re "^\r\n$::gdb_prompt $" { + gdb_assert $found_test_so "this testfle's SO was reported" + gdb_assert $found_all_libs "the correct number of libraries was reported" + } + -re "(^\r\n)?\[^\r\n\]+(?=\r\n)" { + exp_continue + } + } + } + + # These patterns are simpler, and purposefully glob multiple lines. + # The point is to ensure that we find and display all the namespaces, + # without worrying about the libraries printed, since that was tested + # above. + gdb_test "info linker-namespaces" \ + [multi_line "There are 4 linker namespaces loaded" \ + "There are $::decimal libraries loaded in linker namespace ..0.." \ + ".*" \ + "There are $::decimal libraries loaded in linker namespace ..1.." \ + ".*" \ + "There are $::decimal libraries loaded in linker namespace ..2.." \ + ".*" \ + "There are $::decimal libraries loaded in linker namespace ..3.." \ + ".*" ] "print namespaces with no argument" +} + test_info_shared +test_conv_vars +test_info_linker_namespaces diff --git a/gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp b/gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp index 7b36f65..4b3894e 100644 --- a/gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp +++ b/gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp @@ -152,7 +152,7 @@ proc_with_prefix no_url { } { # Test that GDB cannot find dwz without debuginfod. clean_restart gdb_test "file ${binfile}_alt.o" \ - ".*could not find '.gnu_debugaltlink'.*" \ + ".*could not find supplementary DWARF file .*" \ "file [file tail ${binfile}_alt.o]" # Generate a core file and test that GDB cannot find the diff --git a/gdb/testsuite/gdb.dwarf2/macro-source-path-clang14-dw4.exp b/gdb/testsuite/gdb.dwarf2/macro-source-path-clang14-dw4.exp new file mode 100644 index 0000000..c0c2635 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/macro-source-path-clang14-dw4.exp @@ -0,0 +1,71 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2022-2025 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/>. + +# Generate binaries imitating different ways source file paths can be passed to +# compilers. Test printing macros from those binaries. + +# The do_test proc comes from macro-source-path.exp.tcl. +source $srcdir/$subdir/macro-source-path.exp.tcl + +# When adding a test here, please consider adding an equivalent case to +# `gdb.base/macro-source-path.exp`. + +# The following tests are based on the output of `clang-14 -gdwarf-4 +# -fdebug-macro -g3 <file>` (using its built-in assembler). With -gdwarf-4, +# clang produces a .debug_macinfo section, not a .debug_macro section. But +# this test still creates a .debug_macro section, that's good enough for what +# we want to test. + +## test.c +do_test filename 4 "test.c" 1 { +} { + {"test.c" 0} +} + +## ./test.c +do_test dot-filename 4 "test.c" 1 { + "." +} { + {"test.c" 1} +} + +## ../cwd/test.c +do_test dot-dot-cwd 4 "../cwd/test.c" 1 { + "../cwd" +} { + {"test.c" 1} +} + +## /tmp/cwd/test.c +do_test absolute-cwd 4 "/tmp/cwd/test.c" 1 { +} { + {"test.c" 0} +} + +## ../other/test.c +do_test dot-dot-other 4 "../other/test.c" 1 { + "../other" +} { + {"test.c" 1} +} + +## /tmp/other/test.c +do_test absolute-other 4 "/tmp/other/test.c" 1 { + "/tmp" +} { + {"other/test.c" 1} +} diff --git a/gdb/testsuite/gdb.dwarf2/macro-source-path-clang14-dw5.exp b/gdb/testsuite/gdb.dwarf2/macro-source-path-clang14-dw5.exp new file mode 100644 index 0000000..0b3239e --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/macro-source-path-clang14-dw5.exp @@ -0,0 +1,75 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2022-2025 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/>. + +# Generate binaries imitating different ways source file paths can be passed to +# compilers. Test printing macros from those binaries. + +# The do_test proc comes from macro-source-path.exp.tcl. +source $srcdir/$subdir/macro-source-path.exp.tcl + +# When adding a test here, please consider adding an equivalent case to +# `gdb.base/macro-source-path.exp`. + +# The following tests are based on the output of `clang-14 -gdwarf-5 +# -fdebug-macro -g3 <file>` (using its built-in assembler) + +## test.c +do_test filename 5 "test.c" 0 { + "/tmp/cwd" +} { + {"test.c" 0} +} + +## ./test.c +do_test dot-filename 5 "test.c" 1 { + "/tmp/cwd" + "." +} { + {"test.c" 0} + {"test.c" 1} +} + +## ../cwd/test.c +do_test dot-dot-cwd 5 "../cwd/test.c" 0 { + "/tmp/cwd" +} { + {"../cwd/test.c" 0} +} + +## /tmp/cwd/test.c +do_test absolute-cwd 5 "/tmp/cwd/test.c" 1 { + "/tmp/cwd" +} { + {"/tmp/cwd/test.c" 0} + {"test.c" 0} +} + +## ../other/test.c +do_test dot-dot-other 5 "../other/test.c" 0 { + "/tmp/cwd" +} { + {"../other/test.c" 0} +} + +## /tmp/other/test.c +do_test absolute-other 5 "/tmp/other/test.c" 1 { + "/tmp/cwd" + "/tmp" +} { + {"/tmp/other/test.c" 0} + {"other/test.c" 1} +} diff --git a/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld234-dw5.exp b/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld234-dw5.exp new file mode 100644 index 0000000..940f997 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld234-dw5.exp @@ -0,0 +1,70 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2022-2025 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/>. + +# Generate binaries imitating different ways source file paths can be passed to +# compilers. Test printing macros from those binaries. + +# The do_test proc comes from macro-source-path.exp.tcl. +source $srcdir/$subdir/macro-source-path.exp.tcl + +# When adding a test here, please consider adding an equivalent case to +# `gdb.base/macro-source-path.exp`. + +# The following tests are based on the output of `gcc -gdwarf-5 -g3 <file>`, +# gcc 11 paired with gas from binutils 2.34 (Ubuntu 20.04). It generates a v5 +# .debug_macro section, but a v3 .debug_line section. + +## test.c +do_test filename 3 "test.c" 1 { +} { + {"test.c" 0} +} + +## ./test.c +do_test dot-filename 3 "./test.c" 1 { + "." +} { + {"test.c" 1} +} + +## ../cwd/test.c +do_test dot-dot-cwd 3 "../cwd/test.c" 1 { + "../cwd" +} { + {"test.c" 1} +} + +## /tmp/cwd/test.c +do_test absolute-cwd 3 "/tmp/cwd/test.c" 1 { + "/tmp/cwd" +} { + {"test.c" 1} +} + +## ../other/test.c +do_test dot-dot-other 3 "../other/test.c" 1 { + "../other" +} { + {"test.c" 1} +} + +## /tmp/other/test.c +do_test absolute-other 3 "/tmp/other/test.c" 1 { + "/tmp/other" +} { + {"test.c" 1} +} diff --git a/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld238-dw4.exp b/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld238-dw4.exp new file mode 100644 index 0000000..dea0308 --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld238-dw4.exp @@ -0,0 +1,70 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2022-2025 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/>. + +# Generate binaries imitating different ways source file paths can be passed to +# compilers. Test printing macros from those binaries. + +# The do_test proc comes from macro-source-path.exp.tcl. +source $srcdir/$subdir/macro-source-path.exp.tcl + +# When adding a test here, please consider adding an equivalent case to +# `gdb.base/macro-source-path.exp`. + +# The following tests are based on the output of `gcc -gdwarf-4 -g3 <file>`, +# gcc 11 paired with gas from binutils 2.38. With -gdwarf-4, gcc generates a +# v4 (pre-standard) .debug_macro section. + +## test.c +do_test filename 4 "test.c" 1 { +} { + {"test.c" 0} +} + +## ./test.c +do_test dot-filename 4 "./test.c" 1 { + "." +} { + {"test.c" 1} +} + +## ../cwd/test.c +do_test dot-dot-cwd 4 "../cwd/test.c" 1 { + "../cwd" +} { + {"test.c" 1} +} + +## /tmp/cwd/test.c +do_test absolute-cwd 4 "/tmp/cwd/test.c" 1 { + "/tmp/cwd" +} { + {"test.c" 1} +} + +## ../other/test.c +do_test dot-dot-other 4 "../other/test.c" 1 { + "../other" +} { + {"test.c" 1} +} + +## /tmp/other/test.c +do_test absolute-other 4 "/tmp/other/test.c" 1 { + "/tmp/other" +} { + {"test.c" 1} +} diff --git a/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld238-dw5.exp b/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld238-dw5.exp new file mode 100644 index 0000000..98a278e --- /dev/null +++ b/gdb/testsuite/gdb.dwarf2/macro-source-path-gcc11-ld238-dw5.exp @@ -0,0 +1,81 @@ +# This testcase is part of GDB, the GNU debugger. + +# Copyright 2022-2025 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/>. + +# Generate binaries imitating different ways source file paths can be passed to +# compilers. Test printing macros from those binaries. + +# The do_test proc comes from macro-source-path.exp.tcl. +source $srcdir/$subdir/macro-source-path.exp.tcl + +# When adding a test here, please consider adding an equivalent case to +# `gdb.base/macro-source-path.exp`. + +# The following tests are based on the output of `gcc -gdwarf-5 -g3 <file>`, +# gcc 11 paired with gas from binutils 2.38. + +## test.c +do_test filename 5 "test.c" 1 { + "/tmp/cwd" +} { + {"test.c" 0} + {"test.c" 0} +} + +## ./test.c +do_test dot-filename 5 "./test.c" 1 { + "/tmp/cwd" + "." +} { + {"test.c" 1} + {"test.c" 1} +} + +## ../cwd/test.c +do_test dot-dot-cwd 5 "../cwd/test.c" 1 { + "/tmp/cwd" + "../cwd" +} { + {"test.c" 1} + {"test.c" 1} +} + +## /tmp/cwd/test.c +do_test absolute-cwd 5 "/tmp/cwd/test.c" 1 { + "/tmp/cwd" + "/tmp/cwd" +} { + {"test.c" 1} + {"test.c" 0} +} + +## ../other/test.c +do_test dot-dot-other 5 "../other/test.c" 1 { + "/tmp/cwd" + "../other" +} { + {"test.c" 1} + {"test.c" 1} +} + +## /tmp/other/test.c +do_test absolute-other 5 "/tmp/other/test.c" 1 { + "/tmp/cwd" + "/tmp/other" +} { + {"test.c" 1} + {"test.c" 1} +} diff --git a/gdb/testsuite/gdb.dwarf2/macro-source-path.exp b/gdb/testsuite/gdb.dwarf2/macro-source-path.exp.tcl index 1318f8e..ecaf685 100644 --- a/gdb/testsuite/gdb.dwarf2/macro-source-path.exp +++ b/gdb/testsuite/gdb.dwarf2/macro-source-path.exp.tcl @@ -17,12 +17,15 @@ # Generate binaries imitating different ways source file paths can be passed to # compilers. Test printing macros from those binaries. +# +# The entry points for this test are in the various +# gdb.dwarf2/macro-source-path-*.exp files. load_lib dwarf.exp require dwarf2_support -standard_testfile .c +standard_testfile macro-source-path.c lassign [function_range main $srcdir/$subdir/$srcfile] \ main_start main_len @@ -160,248 +163,3 @@ proc do_test { test_name lines_version DW_AT_name main_file_idx directories } } } - -# When adding a test here, please consider adding an equivalent case to the test -# of the same name in gdb.base. - -# The following tests are based on the output of `gcc -gdwarf-5 -g3 <file>`, -# gcc 11 paired with gas from binutils 2.38. - -## test.c -do_test gcc11-ld238-dw5-filename 5 "test.c" 1 { - "/tmp/cwd" -} { - {"test.c" 0} - {"test.c" 0} -} - -## ./test.c -do_test gcc11-ld238-dw5-dot-filename 5 "./test.c" 1 { - "/tmp/cwd" - "." -} { - {"test.c" 1} - {"test.c" 1} -} - -## ../cwd/test.c -do_test gcc11-ld238-dw5-dot-dot-cwd 5 "../cwd/test.c" 1 { - "/tmp/cwd" - "../cwd" -} { - {"test.c" 1} - {"test.c" 1} -} - -## /tmp/cwd/test.c -do_test gcc11-ld238-dw5-absolute-cwd 5 "/tmp/cwd/test.c" 1 { - "/tmp/cwd" - "/tmp/cwd" -} { - {"test.c" 1} - {"test.c" 0} -} - -## ../other/test.c -do_test gcc11-ld238-dw5-dot-dot-other 5 "../other/test.c" 1 { - "/tmp/cwd" - "../other" -} { - {"test.c" 1} - {"test.c" 1} -} - -## /tmp/other/test.c -do_test gcc11-ld238-dw5-absolute-other 5 "/tmp/other/test.c" 1 { - "/tmp/cwd" - "/tmp/other" -} { - {"test.c" 1} - {"test.c" 1} -} - -# The following tests are based on the output of `gcc -gdwarf-4 -g3 <file>`, -# gcc 11 paired with gas from binutils 2.38. With -gdwarf-4, gcc generates a -# v4 (pre-standard) .debug_macro section. - -## test.c -do_test gcc11-ld238-dw4-filename 4 "test.c" 1 { -} { - {"test.c" 0} -} - -## ./test.c -do_test gcc11-ld238-dw4-dot-filename 4 "./test.c" 1 { - "." -} { - {"test.c" 1} -} - -## ../cwd/test.c -do_test gcc11-ld238-dw4-dot-dot-cwd 4 "../cwd/test.c" 1 { - "../cwd" -} { - {"test.c" 1} -} - -## /tmp/cwd/test.c -do_test gcc11-ld238-dw4-absolute-cwd 4 "/tmp/cwd/test.c" 1 { - "/tmp/cwd" -} { - {"test.c" 1} -} - -## ../other/test.c -do_test gcc11-ld238-dw4-dot-dot-other 4 "../other/test.c" 1 { - "../other" -} { - {"test.c" 1} -} - -## /tmp/other/test.c -do_test gcc11-ld238-dw4-absolute-other 4 "/tmp/other/test.c" 1 { - "/tmp/other" -} { - {"test.c" 1} -} - -# The following tests are based on the output of `clang-14 -gdwarf-5 -# -fdebug-macro -g3 <file>` (using its built-in assembler) - -## test.c -do_test clang14-dw5-filename 5 "test.c" 0 { - "/tmp/cwd" -} { - {"test.c" 0} -} - -## ./test.c -do_test clang14-dw5-dot-filename 5 "test.c" 1 { - "/tmp/cwd" - "." -} { - {"test.c" 0} - {"test.c" 1} -} - -## ../cwd/test.c -do_test clang14-dw5-dot-dot-cwd 5 "../cwd/test.c" 0 { - "/tmp/cwd" -} { - {"../cwd/test.c" 0} -} - -## /tmp/cwd/test.c -do_test clang14-dw5-absolute-cwd 5 "/tmp/cwd/test.c" 1 { - "/tmp/cwd" -} { - {"/tmp/cwd/test.c" 0} - {"test.c" 0} -} - -## ../other/test.c -do_test clang14-dw5-dot-dot-other 5 "../other/test.c" 0 { - "/tmp/cwd" -} { - {"../other/test.c" 0} -} - -## /tmp/other/test.c -do_test clang14-dw5-absolute-other 5 "/tmp/other/test.c" 1 { - "/tmp/cwd" - "/tmp" -} { - {"/tmp/other/test.c" 0} - {"other/test.c" 1} -} - -# The following tests are based on the output of `clang-14 -gdwarf-4 -# -fdebug-macro -g3 <file>` (using its built-in assembler). With -gdwarf-4, -# clang produces a .debug_macinfo section, not a .debug_macro section. But -# this test still creates a .debug_macro section, that's good enough for what -# we want to test. - -## test.c -do_test clang14-dw4-filename 4 "test.c" 1 { -} { - {"test.c" 0} -} - -## ./test.c -do_test clang14-dw4-dot-filename 4 "test.c" 1 { - "." -} { - {"test.c" 1} -} - -## ../cwd/test.c -do_test clang14-dw4-dot-dot-cwd 4 "../cwd/test.c" 1 { - "../cwd" -} { - {"test.c" 1} -} - -## /tmp/cwd/test.c -do_test clang14-dw4-absolute-cwd 4 "/tmp/cwd/test.c" 1 { -} { - {"test.c" 0} -} - -## ../other/test.c -do_test clang14-dw4-dot-dot-other 4 "../other/test.c" 1 { - "../other" -} { - {"test.c" 1} -} - -## /tmp/other/test.c -do_test clang14-dw4-absolute-other 4 "/tmp/other/test.c" 1 { - "/tmp" -} { - {"other/test.c" 1} -} - -# The following tests are based on the output of `gcc -gdwarf-5 -g3 <file>`, -# gcc 11 paired with gas from binutils 2.34 (Ubuntu 20.04). It generates a v5 -# .debug_macro section, but a v3 .debug_line section. - -## test.c -do_test gcc11-ld234-dw5-filename 3 "test.c" 1 { -} { - {"test.c" 0} -} - -## ./test.c -do_test gcc11-ld234-dw5-dot-filename 3 "./test.c" 1 { - "." -} { - {"test.c" 1} -} - -## ../cwd/test.c -do_test gcc11-ld234-dw5-dot-dot-cwd 3 "../cwd/test.c" 1 { - "../cwd" -} { - {"test.c" 1} -} - -## /tmp/cwd/test.c -do_test gcc11-ld234-dw5-absolute-cwd 3 "/tmp/cwd/test.c" 1 { - "/tmp/cwd" -} { - {"test.c" 1} -} - -## ../other/test.c -do_test gcc11-ld234-dw5-dot-dot-other 3 "../other/test.c" 1 { - "../other" -} { - {"test.c" 1} -} - -## /tmp/other/test.c -do_test gcc11-ld234-dw5-absolute-other 3 "/tmp/other/test.c" 1 { - "/tmp/other" -} { - {"test.c" 1} -} diff --git a/gdb/testsuite/gdb.python/py-color.exp b/gdb/testsuite/gdb.python/py-color.exp index 1b8e0c5..99b4689 100644 --- a/gdb/testsuite/gdb.python/py-color.exp +++ b/gdb/testsuite/gdb.python/py-color.exp @@ -97,3 +97,36 @@ gdb_test [concat "python print (c_red.escape_sequence (True) + " \ "\033\\\[31m\033\\\[42mred on green\033\\\[49m red on default\033\\\[39m" \ "escape sequences" +gdb_test_multiline "Try to sub-class gdb.Color" \ + "python" "" \ + "class my_color(gdb.Color):" "" \ + " def __init__(self):" "" \ + " super().__init__('red')" "" \ + "end" \ + [multi_line \ + "Python Exception <class 'TypeError'>: type 'gdb\\.Color' is not an acceptable base type" \ + "Error occurred in Python: type 'gdb\\.Color' is not an acceptable base type"] + +gdb_test_multiline "Setup a color parameter and non gdb.Color object" \ + "python" "" \ + "class my_param(gdb.Parameter):" "" \ + " def __init__(self):" "" \ + " super().__init__('color-param', gdb.COMMAND_NONE, gdb.PARAM_COLOR)" "" \ + " self.value = gdb.Color('red')" "" \ + "color_param = my_param()" "" \ + " " "" \ + "class bad_type:" "" \ + " @property" "" \ + " def __class__(self):" "" \ + " raise RuntimeError('__class__ error for bad_type')" "" \ + "bad_obj = bad_type()" "" \ + "end" "" + +gdb_test_no_output "python color_param.value = gdb.Color('blue')" \ + "set color parameter to blue" + +gdb_test "python color_param.value = bad_obj" \ + [multi_line \ + "Python Exception <class 'RuntimeError'>: color argument must be a gdb\\.Color object\\." \ + "Error occurred in Python: color argument must be a gdb\\.Color object\\."] \ + "set color parameter to a non-color type" diff --git a/gdb/testsuite/gdb.python/py-disasm.exp.tcl b/gdb/testsuite/gdb.python/py-disasm.exp.tcl index 938326d..c5099ba 100644 --- a/gdb/testsuite/gdb.python/py-disasm.exp.tcl +++ b/gdb/testsuite/gdb.python/py-disasm.exp.tcl @@ -152,7 +152,10 @@ set test_plans \ "Buffer returned from read_memory is sized $decimal instead of the expected $decimal"]] \ [list "ResultOfWrongType" \ [make_exception_pattern "TypeError" \ - "Result is not a DisassemblerResult."]] \ + "Result from Disassembler must be gdb.DisassemblerResult, not Blah."]] \ + [list "ResultOfVeryWrongType" \ + [make_exception_pattern "TypeError" \ + "Result from Disassembler must be gdb.DisassemblerResult, not Blah."]] \ [list "ErrorCreatingTextPart_NoArgs" \ [make_exception_pattern "TypeError" \ [missing_arg_pattern "style" 1]]] \ diff --git a/gdb/testsuite/gdb.python/py-disasm.py b/gdb/testsuite/gdb.python/py-disasm.py index 32d6aa7..9761337 100644 --- a/gdb/testsuite/gdb.python/py-disasm.py +++ b/gdb/testsuite/gdb.python/py-disasm.py @@ -294,6 +294,24 @@ class ResultOfWrongType(TestDisassembler): return self.Blah(1, "ABC") +class ResultOfVeryWrongType(TestDisassembler): + """Return something that is not a DisassemblerResult from disassemble + method. The thing returned will raise an exception if used in an + isinstance() call, or in PyObject_IsInstance from C++. + """ + + class Blah: + def __init__(self): + pass + + @property + def __class__(self): + raise RuntimeError("error from __class__ in Blah") + + def disassemble(self, info): + return self.Blah() + + class TaggingDisassembler(TestDisassembler): """A simple disassembler that just tags the output.""" diff --git a/gdb/testsuite/gdb.python/py-frame.exp b/gdb/testsuite/gdb.python/py-frame.exp index 5668807..c1e3e33 100644 --- a/gdb/testsuite/gdb.python/py-frame.exp +++ b/gdb/testsuite/gdb.python/py-frame.exp @@ -188,6 +188,21 @@ gdb_test "python print(gdb.selected_frame().read_register(list()))" \ ".*Invalid type for register.*" \ "test Frame.read_register with list" +gdb_test_multiline "setup a bad object" \ + "python" "" \ + "class bad_type:" "" \ + " def __init__ (self):" "" \ + " pass" "" \ + " @property" "" \ + " def __class__(self):" "" \ + " raise RuntimeError('error from __class in bad_type')" "" \ + "bad_object = bad_type()" "" \ + "end" "" + +gdb_test "python print(gdb.selected_frame().read_register(bad_object))" \ + ".*Invalid type for register.*" \ + "test Frame.read_register with bad_type object" + # Compile again without debug info. gdb_exit if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile} {}] } { diff --git a/gdb/testsuite/gdb.python/py-unwind.exp b/gdb/testsuite/gdb.python/py-unwind.exp index 80eac28..b416c2f 100644 --- a/gdb/testsuite/gdb.python/py-unwind.exp +++ b/gdb/testsuite/gdb.python/py-unwind.exp @@ -245,6 +245,13 @@ with_test_prefix "frame-id 'pc' is invalid" { "Python Exception <class 'ValueError'>: invalid literal for int\\(\\) with base 10: 'xyz'\r\n.*" } +with_test_prefix "bad object unwinder" { + gdb_test_no_output "python obj = bad_object_unwinder(\"bad-object\")" + gdb_test_no_output "python gdb.unwinder.register_unwinder(None, obj, replace=True)" + gdb_test "backtrace" \ + "Python Exception <class 'gdb.error'>: an Unwinder should return gdb.UnwindInfo, not Blah\\.\r\n.*" +} + # Gather information about every frame. gdb_test_no_output "python capture_all_frame_information()" gdb_test_no_output "python gdb.newest_frame().select()" diff --git a/gdb/testsuite/gdb.python/py-unwind.py b/gdb/testsuite/gdb.python/py-unwind.py index 8e65a1a..0faccf2 100644 --- a/gdb/testsuite/gdb.python/py-unwind.py +++ b/gdb/testsuite/gdb.python/py-unwind.py @@ -267,4 +267,24 @@ class validating_unwinder(Unwinder): return None +class bad_object_unwinder(Unwinder): + def __init__(self, name): + super().__init__(name) + + def __call__(self, pending_frame): + + if pending_frame.level() != 1: + return None + + class Blah: + def __init__(self): + pass + + @property + def __class__(self): + raise RuntimeError("error in Blah.__class__") + + return Blah() + + print("Python script imported") diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp index c37cc89..ead14bd 100644 --- a/gdb/testsuite/lib/gdb.exp +++ b/gdb/testsuite/lib/gdb.exp @@ -3758,7 +3758,8 @@ proc supports_reverse {} { || [istarget "aarch64*-*-linux*"] || [istarget "loongarch*-*-linux*"] || [istarget "powerpc*-*-linux*"] - || [istarget "s390*-*-linux*"] } { + || [istarget "s390*-*-linux*"] + || [istarget "riscv*-*-*"] } { return 1 } diff --git a/ld/ldlang.c b/ld/ldlang.c index 0bb9e17..97fdb91 100644 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@ -5062,7 +5062,8 @@ print_input_section (asection *i, bool is_discarded) } print_spaces (SECTION_NAME_MAP_LENGTH - len); - if (i->output_section != NULL + if ((i->flags & SEC_EXCLUDE) == 0 + && i->output_section != NULL && i->output_section->owner == link_info.output_bfd) addr = i->output_section->vma + i->output_offset; else |