aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bfd/version.h2
-rw-r--r--gas/config/tc-aarch64.c2
-rw-r--r--gas/config/tc-aarch64.h2
-rw-r--r--gas/config/tc-i386.c2
-rw-r--r--gas/config/tc-i386.h2
-rw-r--r--gas/testsuite/gas/loongarch/div_zero.l4
-rw-r--r--gas/testsuite/gas/loongarch/div_zero.s2
-rw-r--r--gas/testsuite/gas/loongarch/loongarch.exp1
-rw-r--r--gdb/NEWS2
-rw-r--r--gdb/cli/cli-cmds.c2
-rwxr-xr-xgdb/configure2
-rw-r--r--gdb/configure.ac2
-rw-r--r--gdb/configure.tgt5
-rwxr-xr-xgdb/contrib/cc-with-tweaks.sh5
-rw-r--r--gdb/disasm-selftests.c87
-rw-r--r--gdb/disasm-selftests.h32
-rw-r--r--gdb/doc/gdb.texinfo6
-rw-r--r--gdb/doc/python.texi4
-rw-r--r--gdb/dwarf2/attribute.c5
-rw-r--r--gdb/dwarf2/attribute.h14
-rw-r--r--gdb/dwarf2/cooked-indexer.c4
-rw-r--r--gdb/dwarf2/die.c3
-rw-r--r--gdb/dwarf2/dwz.c193
-rw-r--r--gdb/dwarf2/dwz.h25
-rw-r--r--gdb/dwarf2/macro.c1
-rw-r--r--gdb/dwarf2/read.c19
-rw-r--r--gdb/dwarf2/read.h8
-rw-r--r--gdb/infrun.c27
-rw-r--r--gdb/infrun.h3
-rw-r--r--gdb/microblaze-linux-tdep.c3
-rw-r--r--gdb/pager.h1
-rw-r--r--gdb/printcmd.c2
-rw-r--r--gdb/python/py-breakpoint.c2
-rw-r--r--gdb/python/py-color.c35
-rw-r--r--gdb/remote.c2
-rw-r--r--gdb/riscv-canonicalize-syscall-gen.c342
-rw-r--r--gdb/riscv-linux-tdep.c256
-rw-r--r--gdb/riscv-linux-tdep.h29
-rw-r--r--gdb/riscv-tdep.c686
-rw-r--r--gdb/riscv-tdep.h16
-rw-r--r--gdb/s390-tdep.c52
-rwxr-xr-xgdb/syscalls/riscv-canonicalize-syscall-gen.py163
-rw-r--r--gdb/testsuite/boards/cc-with-dwz-5.exp28
-rw-r--r--gdb/testsuite/gdb.debuginfod/fetch_src_and_symbols.exp2
-rw-r--r--gdb/testsuite/gdb.dwarf2/dwzbuildid.exp159
-rw-r--r--gdb/testsuite/gdb.dwarf2/dwzbuildid.tcl184
-rw-r--r--gdb/testsuite/gdb.dwarf2/dwzbuildid5.exp17
-rw-r--r--gdb/testsuite/gdb.dwarf2/dwznolink.exp2
-rw-r--r--gdb/testsuite/gdb.dwarf2/no-gnu-debuglink.exp2
-rw-r--r--gdb/testsuite/gdb.python/gdb_leak_detector.py120
-rw-r--r--gdb/testsuite/gdb.python/py-breakpoint.exp2
-rw-r--r--gdb/testsuite/gdb.python/py-color-leak.exp28
-rw-r--r--gdb/testsuite/gdb.python/py-color-leak.py31
-rw-r--r--gdb/testsuite/gdb.python/py-color.exp3
-rw-r--r--gdb/testsuite/gdb.python/py-inferior-leak.exp14
-rw-r--r--gdb/testsuite/gdb.python/py-inferior-leak.py108
-rw-r--r--gdb/testsuite/gdb.python/py-read-memory-leak.exp14
-rw-r--r--gdb/testsuite/gdb.python/py-read-memory-leak.py84
-rw-r--r--gdb/testsuite/lib/dwarf.exp18
-rw-r--r--gdb/testsuite/lib/gdb-python.exp21
-rw-r--r--gdb/testsuite/lib/gdb.exp3
-rw-r--r--gdb/ui-file.c12
-rw-r--r--gdb/ui-file.h17
-rw-r--r--gdb/ui-style.c17
-rw-r--r--gdb/utils.c12
-rw-r--r--gprofng/doc/gprofng_ug.texi5
-rw-r--r--gprofng/src/CallStack.cc10
-rw-r--r--ld/ldlang.c3
-rw-r--r--ld/ldmisc.c10
69 files changed, 2450 insertions, 531 deletions
diff --git a/bfd/version.h b/bfd/version.h
index 5a40d14..177404d 100644
--- a/bfd/version.h
+++ b/bfd/version.h
@@ -16,7 +16,7 @@
In releases, the date is not included in either version strings or
sonames. */
-#define BFD_VERSION_DATE 20250422
+#define BFD_VERSION_DATE 20250423
#define BFD_VERSION @bfd_version@
#define BFD_VERSION_STRING @bfd_version_package@ @bfd_version_string@
#define REPORT_BUGS_TO @report_bugs_to@
diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c
index e071ad1..75aee15 100644
--- a/gas/config/tc-aarch64.c
+++ b/gas/config/tc-aarch64.c
@@ -9121,7 +9121,7 @@ aarch64_sframe_cfa_ra_offset (void)
return (offsetT) SFRAME_CFA_FIXED_RA_INVALID;
}
-/* Get the abi/arch indentifier for SFrame. */
+/* Get the abi/arch identifier for SFrame. */
unsigned char
aarch64_sframe_get_abi_arch (void)
diff --git a/gas/config/tc-aarch64.h b/gas/config/tc-aarch64.h
index acf1ce4..c2d0511 100644
--- a/gas/config/tc-aarch64.h
+++ b/gas/config/tc-aarch64.h
@@ -309,7 +309,7 @@ extern bool aarch64_sframe_ra_tracking_p (void);
extern offsetT aarch64_sframe_cfa_ra_offset (void);
#define sframe_cfa_ra_offset aarch64_sframe_cfa_ra_offset
-/* The abi/arch indentifier for SFrame. */
+/* The abi/arch identifier for SFrame. */
unsigned char aarch64_sframe_get_abi_arch (void);
#define sframe_get_abi_arch aarch64_sframe_get_abi_arch
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index 2c61353..3e7040a 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -11968,7 +11968,7 @@ x86_sframe_cfa_ra_offset (void)
return (offsetT) -8;
}
-/* The abi/arch indentifier for SFrame. */
+/* The abi/arch identifier for SFrame. */
unsigned char
x86_sframe_get_abi_arch (void)
{
diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h
index 3fb7920..19eb0bc 100644
--- a/gas/config/tc-i386.h
+++ b/gas/config/tc-i386.h
@@ -471,7 +471,7 @@ extern bool x86_sframe_ra_tracking_p (void);
extern offsetT x86_sframe_cfa_ra_offset (void);
#define sframe_cfa_ra_offset x86_sframe_cfa_ra_offset
-/* The abi/arch indentifier for SFrame. */
+/* The abi/arch identifier for SFrame. */
extern unsigned char x86_sframe_get_abi_arch (void);
#define sframe_get_abi_arch x86_sframe_get_abi_arch
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/NEWS b/gdb/NEWS
index 99ec392..4baed29 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -40,6 +40,8 @@
namespace into which the library was loaded, if more than one namespace
is active.
+ * Add record full support for rv64gc architectures
+
* New commands
maintenance check psymtabs
diff --git a/gdb/cli/cli-cmds.c b/gdb/cli/cli-cmds.c
index 7e13ef8..9a5021f 100644
--- a/gdb/cli/cli-cmds.c
+++ b/gdb/cli/cli-cmds.c
@@ -833,7 +833,7 @@ echo_command (const char *text, int from_tty)
gdb_printf ("%c", c);
}
- gdb_stdout->reset_style ();
+ gdb_stdout->emit_style_escape (ui_file_style ());
/* Force this output to appear now. */
gdb_flush (gdb_stdout);
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/contrib/cc-with-tweaks.sh b/gdb/contrib/cc-with-tweaks.sh
index de56018..930ba5c 100755
--- a/gdb/contrib/cc-with-tweaks.sh
+++ b/gdb/contrib/cc-with-tweaks.sh
@@ -42,6 +42,7 @@
# -Z invoke objcopy --compress-debug-sections
# -z compress using dwz
# -m compress using dwz -m
+# -5 compress using dwz -m -5
# -i make an index (.gdb_index)
# -c make an index (currently .gdb_index) in a cache dir
# -n make a dwarf5 index (.debug_names)
@@ -88,6 +89,7 @@ want_index=false
index_options=""
want_index_cache=false
want_dwz=false
+dwz_5flag=
want_multi=false
want_dwp=false
want_objcopy_compress=false
@@ -101,6 +103,7 @@ while [ $# -gt 0 ]; do
-n) want_index=true; index_options=-dwarf-5;;
-c) want_index_cache=true ;;
-m) want_multi=true ;;
+ -5) want_multi=true; dwz_5flag=-5 ;;
-p) want_dwp=true ;;
-l) want_gnu_debuglink=true ;;
*) break ;;
@@ -269,7 +272,7 @@ elif [ "$want_multi" = true ]; then
rm -f "$dwz_file"
cp "$output_file" "${output_file}.alt"
- $DWZ -m "$dwz_file" "$output_file" "${output_file}.alt" > /dev/null
+ $DWZ $dwz_5flag -m "$dwz_file" "$output_file" "${output_file}.alt" > /dev/null
rm -f "${output_file}.alt"
# Validate dwz's work by checking if the expected output file exists.
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..d54ad65 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.
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 50342bb..80f4c14 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -4554,8 +4554,8 @@ registered.
The help text for the new command is taken from the Python
documentation string for the command's class, if there is one. If no
-documentation string is provided, the default value ``This command is
-not documented.'' is used.
+documentation string is provided, the default value @samp{This command
+is not documented.} is used.
@end defun
@cindex don't repeat Python command
diff --git a/gdb/dwarf2/attribute.c b/gdb/dwarf2/attribute.c
index 1278f97..2d14ebd 100644
--- a/gdb/dwarf2/attribute.c
+++ b/gdb/dwarf2/attribute.c
@@ -73,7 +73,8 @@ attribute::form_is_string () const
|| form == DW_FORM_strx3
|| form == DW_FORM_strx4
|| form == DW_FORM_GNU_str_index
- || form == DW_FORM_GNU_strp_alt);
+ || form == DW_FORM_GNU_strp_alt
+ || form == DW_FORM_strp_sup);
}
/* See attribute.h. */
@@ -190,6 +191,8 @@ attribute::form_is_unsigned () const
{
return (form == DW_FORM_ref_addr
|| form == DW_FORM_GNU_ref_alt
+ || form == DW_FORM_ref_sup4
+ || form == DW_FORM_ref_sup8
|| form == DW_FORM_data2
|| form == DW_FORM_data4
|| form == DW_FORM_data8
diff --git a/gdb/dwarf2/attribute.h b/gdb/dwarf2/attribute.h
index 70edff3..ec4f3d8 100644
--- a/gdb/dwarf2/attribute.h
+++ b/gdb/dwarf2/attribute.h
@@ -144,7 +144,9 @@ struct attribute
|| form == DW_FORM_ref4
|| form == DW_FORM_ref8
|| form == DW_FORM_ref_udata
- || form == DW_FORM_GNU_ref_alt);
+ || form == DW_FORM_GNU_ref_alt
+ || form == DW_FORM_ref_sup4
+ || form == DW_FORM_ref_sup8);
}
/* Check if the attribute's form is a DW_FORM_block*
@@ -168,6 +170,16 @@ struct attribute
"reprocessing". */
bool form_requires_reprocessing () const;
+ /* Check if attribute's form refers to the separate "dwz" file.
+ This is only useful for references to the .debug_info section,
+ not to the supplementary .debug_str section. */
+ bool form_is_alt () const
+ {
+ return (form == DW_FORM_GNU_ref_alt
+ || form == DW_FORM_ref_sup4
+ || form == DW_FORM_ref_sup8);
+ }
+
/* Return DIE offset of this attribute. Return 0 with complaint if
the attribute is not of the required kind. */
diff --git a/gdb/dwarf2/cooked-indexer.c b/gdb/dwarf2/cooked-indexer.c
index 1f3a235..b8b66cf 100644
--- a/gdb/dwarf2/cooked-indexer.c
+++ b/gdb/dwarf2/cooked-indexer.c
@@ -222,7 +222,7 @@ cooked_indexer::scan_attributes (dwarf2_per_cu *scanning_per_cu,
case DW_AT_abstract_origin:
case DW_AT_extension:
origin_offset = attr.get_ref_die_offset ();
- origin_is_dwz = attr.form == DW_FORM_GNU_ref_alt;
+ origin_is_dwz = attr.form_is_alt ();
break;
case DW_AT_external:
@@ -423,7 +423,7 @@ cooked_indexer::index_imported_unit (cutu_reader *reader,
if (attr.name == DW_AT_import)
{
sect_off = attr.get_ref_die_offset ();
- is_dwz = (attr.form == DW_FORM_GNU_ref_alt
+ is_dwz = (attr.form_is_alt ()
|| reader->cu ()->per_cu->is_dwz);
}
}
diff --git a/gdb/dwarf2/die.c b/gdb/dwarf2/die.c
index 8c3d2ca..7ed18bf 100644
--- a/gdb/dwarf2/die.c
+++ b/gdb/dwarf2/die.c
@@ -90,6 +90,8 @@ dump_die_shallow (struct ui_file *f, int indent, struct die_info *die)
gdb_puts (hex_string (die->attrs[i].as_unsigned ()), f);
break;
case DW_FORM_GNU_ref_alt:
+ case DW_FORM_ref_sup4:
+ case DW_FORM_ref_sup8:
gdb_printf (f, "alt ref address: ");
gdb_puts (hex_string (die->attrs[i].as_unsigned ()), f);
break;
@@ -123,6 +125,7 @@ dump_die_shallow (struct ui_file *f, int indent, struct die_info *die)
case DW_FORM_strx:
case DW_FORM_GNU_str_index:
case DW_FORM_GNU_strp_alt:
+ case DW_FORM_strp_sup:
gdb_printf (f, "string: \"%s\" (%s canonicalized)",
die->attrs[i].as_string ()
? die->attrs[i].as_string () : "",
diff --git a/gdb/dwarf2/dwz.c b/gdb/dwarf2/dwz.c
index e5e18a1..583103b 100644
--- a/gdb/dwarf2/dwz.c
+++ b/gdb/dwarf2/dwz.c
@@ -21,6 +21,7 @@
#include "build-id.h"
#include "debuginfod-support.h"
+#include "dwarf2/leb.h"
#include "dwarf2/read.h"
#include "dwarf2/sect-names.h"
#include "filenames.h"
@@ -34,15 +35,17 @@
const char *
dwz_file::read_string (struct objfile *objfile, LONGEST str_offset)
{
- str.read (objfile);
+ /* This must be true because the sections are read in when the
+ dwz_file is created. */
+ gdb_assert (str.readin);
if (str.buffer == NULL)
- error (_("DW_FORM_GNU_strp_alt used without .debug_str "
+ error (_("supplementary DWARF file missing .debug_str "
"section [in module %s]"),
this->filename ());
if (str_offset >= str.size)
- error (_("DW_FORM_GNU_strp_alt pointing outside of "
- ".debug_str section [in module %s]"),
+ error (_("invalid string reference to supplementary DWARF file "
+ "[in module %s]"),
this->filename ());
gdb_assert (HOST_CHAR_BIT == 8);
if (str.buffer[str_offset] == '\0')
@@ -85,6 +88,139 @@ locate_dwz_sections (struct objfile *objfile, bfd *abfd, asection *sectp,
}
}
+/* Helper that throws an exception when reading the .debug_sup
+ section. */
+
+static void
+debug_sup_failure (const char *text, bfd *abfd)
+{
+ error (_("%s [in modules %s]"), text, bfd_get_filename (abfd));
+}
+
+/* Look for the .debug_sup section and read it. If the section does
+ not exist, this returns false. If the section does exist but fails
+ to parse for some reason, an exception is thrown. Otherwise, if
+ everything goes well, this returns true and fills in the out
+ parameters. */
+
+static bool
+get_debug_sup_info (bfd *abfd,
+ std::string *filename,
+ size_t *buildid_len,
+ gdb::unique_xmalloc_ptr<bfd_byte> *buildid)
+{
+ asection *sect = bfd_get_section_by_name (abfd, ".debug_sup");
+ if (sect == nullptr)
+ return false;
+
+ bfd_byte *contents;
+ if (!bfd_malloc_and_get_section (abfd, sect, &contents))
+ debug_sup_failure (_("could not read .debug_sup section"), abfd);
+
+ gdb::unique_xmalloc_ptr<bfd_byte> content_holder (contents);
+ bfd_size_type size = bfd_section_size (sect);
+
+ /* Version of this section. */
+ if (size < 4)
+ debug_sup_failure (_(".debug_sup section too short"), abfd);
+ unsigned int version = read_2_bytes (abfd, contents);
+ contents += 2;
+ size -= 2;
+ if (version != 5)
+ debug_sup_failure (_(".debug_sup has wrong version number"), abfd);
+
+ /* Skip the is_supplementary value. We already ensured there were
+ enough bytes, above. */
+ ++contents;
+ --size;
+
+ /* The spec says that in the supplementary file, this must be \0,
+ but it doesn't seem very important. */
+ const char *fname = (const char *) contents;
+ size_t len = strlen (fname) + 1;
+ if (filename != nullptr)
+ *filename = fname;
+ contents += len;
+ size -= len;
+
+ if (size == 0)
+ debug_sup_failure (_(".debug_sup section missing ID"), abfd);
+
+ unsigned int bytes_read;
+ *buildid_len = read_unsigned_leb128 (abfd, contents, &bytes_read);
+ contents += bytes_read;
+ size -= bytes_read;
+
+ if (size < *buildid_len)
+ debug_sup_failure (_("extra data after .debug_sup section ID"), abfd);
+
+ if (*buildid_len != 0)
+ buildid->reset ((bfd_byte *) xmemdup (contents, *buildid_len,
+ *buildid_len));
+
+ return true;
+}
+
+/* Validate that ABFD matches the given BUILDID. If DWARF5 is true,
+ then this is done by examining the .debug_sup data. */
+
+static bool
+verify_id (bfd *abfd, size_t len, const bfd_byte *buildid, bool dwarf5)
+{
+ if (!bfd_check_format (abfd, bfd_object))
+ return false;
+
+ if (dwarf5)
+ {
+ size_t new_len;
+ gdb::unique_xmalloc_ptr<bfd_byte> new_id;
+
+ if (!get_debug_sup_info (abfd, nullptr, &new_len, &new_id))
+ return false;
+ return (len == new_len
+ && memcmp (buildid, new_id.get (), len) == 0);
+ }
+ else
+ return build_id_verify (abfd, len, buildid);
+}
+
+/* Find either the .debug_sup or .gnu_debugaltlink section and return
+ its contents. Returns true on success and sets out parameters, or
+ false if nothing is found. */
+
+static bool
+read_alt_info (bfd *abfd, std::string *filename,
+ size_t *buildid_len,
+ gdb::unique_xmalloc_ptr<bfd_byte> *buildid,
+ bool *dwarf5)
+{
+ if (get_debug_sup_info (abfd, filename, buildid_len, buildid))
+ {
+ *dwarf5 = true;
+ return true;
+ }
+
+ bfd_size_type buildid_len_arg;
+ bfd_set_error (bfd_error_no_error);
+ bfd_byte *buildid_out;
+ gdb::unique_xmalloc_ptr<char> new_filename
+ (bfd_get_alt_debug_link_info (abfd, &buildid_len_arg,
+ &buildid_out));
+ if (new_filename == nullptr)
+ {
+ if (bfd_get_error () == bfd_error_no_error)
+ return false;
+ error (_("could not read '.gnu_debugaltlink' section: %s"),
+ bfd_errmsg (bfd_get_error ()));
+ }
+ *filename = new_filename.get ();
+
+ *buildid_len = buildid_len_arg;
+ buildid->reset (buildid_out);
+ *dwarf5 = false;
+ return true;
+}
+
/* Attempt to find a .dwz file (whose full path is represented by
FILENAME) in all of the specified debug file directories provided.
@@ -93,7 +229,7 @@ locate_dwz_sections (struct objfile *objfile, bfd *abfd, asection *sectp,
static gdb_bfd_ref_ptr
dwz_search_other_debugdirs (std::string &filename, bfd_byte *buildid,
- size_t buildid_len)
+ size_t buildid_len, bool dwarf5)
{
/* Let's assume that the path represented by FILENAME has the
"/.dwz/" subpath in it. This is what (most) GNU/Linux
@@ -161,7 +297,7 @@ dwz_search_other_debugdirs (std::string &filename, bfd_byte *buildid,
if (dwz_bfd == nullptr)
continue;
- if (!build_id_verify (dwz_bfd.get (), buildid_len, buildid))
+ if (!verify_id (dwz_bfd.get (), buildid_len, buildid, dwarf5))
{
dwz_bfd.reset (nullptr);
continue;
@@ -177,11 +313,8 @@ dwz_search_other_debugdirs (std::string &filename, bfd_byte *buildid,
/* See dwz.h. */
void
-dwarf2_read_dwz_file (dwarf2_per_objfile *per_objfile)
+dwz_file::read_dwz_file (dwarf2_per_objfile *per_objfile)
{
- bfd_size_type buildid_len_arg;
- size_t buildid_len;
- bfd_byte *buildid;
dwarf2_per_bfd *per_bfd = per_objfile->per_bfd;
/* This may query the user via the debuginfod support, so it may
@@ -193,24 +326,17 @@ dwarf2_read_dwz_file (dwarf2_per_objfile *per_objfile)
/* Set this early, so that on error it remains NULL. */
per_bfd->dwz_file.emplace (nullptr);
- bfd_set_error (bfd_error_no_error);
- gdb::unique_xmalloc_ptr<char> data
- (bfd_get_alt_debug_link_info (per_bfd->obfd,
- &buildid_len_arg, &buildid));
- if (data == NULL)
+ size_t buildid_len;
+ gdb::unique_xmalloc_ptr<bfd_byte> buildid;
+ std::string filename;
+ bool dwarf5;
+ if (!read_alt_info (per_bfd->obfd, &filename, &buildid_len, &buildid,
+ &dwarf5))
{
- if (bfd_get_error () == bfd_error_no_error)
- return;
- error (_("could not read '.gnu_debugaltlink' section: %s"),
- bfd_errmsg (bfd_get_error ()));
+ /* Nothing found, nothing to do. */
+ return;
}
- gdb::unique_xmalloc_ptr<bfd_byte> buildid_holder (buildid);
-
- buildid_len = (size_t) buildid_len_arg;
-
- std::string filename = data.get ();
-
if (!IS_ABSOLUTE_PATH (filename.c_str ()))
{
gdb::unique_xmalloc_ptr<char> abs = gdb_realpath (per_bfd->filename ());
@@ -223,25 +349,26 @@ dwarf2_read_dwz_file (dwarf2_per_objfile *per_objfile)
gdb_bfd_ref_ptr dwz_bfd (gdb_bfd_open (filename.c_str (), gnutarget));
if (dwz_bfd != NULL)
{
- if (!build_id_verify (dwz_bfd.get (), buildid_len, buildid))
+ if (!verify_id (dwz_bfd.get (), buildid_len, buildid.get (), dwarf5))
dwz_bfd.reset (nullptr);
}
if (dwz_bfd == NULL)
- dwz_bfd = build_id_to_debug_bfd (buildid_len, buildid);
+ dwz_bfd = build_id_to_debug_bfd (buildid_len, buildid.get ());
if (dwz_bfd == nullptr)
{
/* If the user has provided us with different
debug file directories, we can try them in order. */
- dwz_bfd = dwz_search_other_debugdirs (filename, buildid, buildid_len);
+ dwz_bfd = dwz_search_other_debugdirs (filename, buildid.get (),
+ buildid_len, dwarf5);
}
if (dwz_bfd == nullptr)
{
gdb::unique_xmalloc_ptr<char> alt_filename;
scoped_fd fd
- = debuginfod_debuginfo_query (buildid, buildid_len,
+ = debuginfod_debuginfo_query (buildid.get (), buildid_len,
per_bfd->filename (), &alt_filename);
if (fd.get () >= 0)
@@ -252,16 +379,18 @@ dwarf2_read_dwz_file (dwarf2_per_objfile *per_objfile)
if (dwz_bfd == nullptr)
warning (_("File \"%s\" from debuginfod cannot be opened as bfd"),
alt_filename.get ());
- else if (!build_id_verify (dwz_bfd.get (), buildid_len, buildid))
+ else if (!verify_id (dwz_bfd.get (), buildid_len, buildid.get (),
+ dwarf5))
dwz_bfd.reset (nullptr);
}
}
if (dwz_bfd == NULL)
- error (_("could not find '.gnu_debugaltlink' file for %s"),
+ error (_("could not find supplementary DWARF file (%s) for %s"),
+ filename.c_str (),
per_bfd->filename ());
- auto result = std::make_unique<dwz_file> (std::move (dwz_bfd));
+ dwz_file_up result (new dwz_file (std::move (dwz_bfd)));
for (asection *sec : gdb_bfd_sections (result->dwz_bfd))
locate_dwz_sections (per_objfile->objfile, result->dwz_bfd.get (),
diff --git a/gdb/dwarf2/dwz.h b/gdb/dwarf2/dwz.h
index 9f69123..372f7e6 100644
--- a/gdb/dwarf2/dwz.h
+++ b/gdb/dwarf2/dwz.h
@@ -31,10 +31,12 @@ struct dwarf2_per_objfile;
struct dwz_file
{
- dwz_file (gdb_bfd_ref_ptr &&bfd)
- : dwz_bfd (std::move (bfd))
- {
- }
+ /* Open the separate '.dwz' debug file, if needed. This will set
+ the appropriate field in the per-BFD structure. If the DWZ file
+ exists, the relevant sections are read in as well. Throws an
+ exception if the .gnu_debugaltlink or .debug_sup section exists
+ but is invalid or if the file cannot be found. */
+ static void read_dwz_file (dwarf2_per_objfile *per_objfile);
const char *filename () const
{
@@ -64,16 +66,15 @@ struct dwz_file
return a pointer to the string. */
const char *read_string (struct objfile *objfile, LONGEST str_offset);
-};
-using dwz_file_up = std::unique_ptr<dwz_file>;
+private:
-/* Open the separate '.dwz' debug file, if needed. This just sets the
- appropriate field in the per-BFD structure. If the DWZ file
- exists, the relevant sections are read in as well. Throws an error
- if the .gnu_debugaltlink section exists but the file cannot be
- found. */
+ explicit dwz_file (gdb_bfd_ref_ptr &&bfd)
+ : dwz_bfd (std::move (bfd))
+ {
+ }
+};
-extern void dwarf2_read_dwz_file (dwarf2_per_objfile *per_objfile);
+using dwz_file_up = std::unique_ptr<dwz_file>;
#endif /* GDB_DWARF2_DWZ_H */
diff --git a/gdb/dwarf2/macro.c b/gdb/dwarf2/macro.c
index 9b8f093..1dc3a9e 100644
--- a/gdb/dwarf2/macro.c
+++ b/gdb/dwarf2/macro.c
@@ -259,6 +259,7 @@ skip_form_bytes (bfd *abfd, const gdb_byte *bytes, const gdb_byte *buffer_end,
case DW_FORM_sec_offset:
case DW_FORM_strp:
case DW_FORM_GNU_strp_alt:
+ case DW_FORM_strp_sup:
bytes += offset_size;
break;
diff --git a/gdb/dwarf2/read.c b/gdb/dwarf2/read.c
index 55cf02f..7b019f9 100644
--- a/gdb/dwarf2/read.c
+++ b/gdb/dwarf2/read.c
@@ -1282,7 +1282,7 @@ dwarf2_has_info (struct objfile *objfile,
BFD, to avoid races. */
try
{
- dwarf2_read_dwz_file (per_objfile);
+ dwz_file::read_dwz_file (per_objfile);
}
catch (const gdb_exception_error &err)
{
@@ -3896,11 +3896,13 @@ cutu_reader::skip_one_attribute (dwarf_form form, const gdb_byte *info_ptr)
case DW_FORM_data4:
case DW_FORM_ref4:
case DW_FORM_strx4:
+ case DW_FORM_ref_sup4:
return info_ptr + 4;
case DW_FORM_data8:
case DW_FORM_ref8:
case DW_FORM_ref_sig8:
+ case DW_FORM_ref_sup8:
return info_ptr + 8;
case DW_FORM_data16:
@@ -3913,6 +3915,7 @@ cutu_reader::skip_one_attribute (dwarf_form form, const gdb_byte *info_ptr)
case DW_FORM_sec_offset:
case DW_FORM_strp:
case DW_FORM_GNU_strp_alt:
+ case DW_FORM_strp_sup:
return info_ptr + m_cu->header.offset_size;
case DW_FORM_exprloc:
@@ -5023,7 +5026,7 @@ process_imported_unit_die (struct die_info *die, struct dwarf2_cu *cu)
if (attr != NULL)
{
sect_offset sect_off = attr->get_ref_die_offset ();
- bool is_dwz = (attr->form == DW_FORM_GNU_ref_alt || cu->per_cu->is_dwz);
+ bool is_dwz = attr->form_is_alt () || cu->per_cu->is_dwz;
dwarf2_per_objfile *per_objfile = cu->per_objfile;
dwarf2_per_cu *per_cu
= dwarf2_find_containing_comp_unit (sect_off, is_dwz,
@@ -14914,10 +14917,12 @@ cutu_reader::read_attribute_value (attribute *attr, unsigned form,
info_ptr += 2;
break;
case DW_FORM_data4:
+ case DW_FORM_ref_sup4:
attr->set_unsigned (read_4_bytes (m_abfd, info_ptr));
info_ptr += 4;
break;
case DW_FORM_data8:
+ case DW_FORM_ref_sup8:
attr->set_unsigned (read_8_bytes (m_abfd, info_ptr));
info_ptr += 8;
break;
@@ -14969,6 +14974,7 @@ cutu_reader::read_attribute_value (attribute *attr, unsigned form,
}
[[fallthrough]];
case DW_FORM_GNU_strp_alt:
+ case DW_FORM_strp_sup:
{
dwz_file *dwz = per_objfile->per_bfd->get_dwz_file (true);
LONGEST str_offset
@@ -17251,6 +17257,7 @@ dwarf2_const_value_attr (const struct attribute *attr, struct type *type,
case DW_FORM_strx:
case DW_FORM_GNU_str_index:
case DW_FORM_GNU_strp_alt:
+ case DW_FORM_strp_sup:
/* The string is already allocated on the objfile obstack, point
directly to it. */
*bytes = (const gdb_byte *) attr->as_string ();
@@ -17457,7 +17464,7 @@ lookup_die_type (struct die_info *die, const struct attribute *attr,
/* First see if we have it cached. */
- if (attr->form == DW_FORM_GNU_ref_alt)
+ if (attr->form_is_alt ())
{
sect_offset sect_off = attr->get_ref_die_offset ();
dwarf2_per_cu *per_cu
@@ -18241,15 +18248,14 @@ follow_die_ref (struct die_info *src_die, const struct attribute *attr,
struct dwarf2_cu *cu = *ref_cu;
struct die_info *die;
- if (attr->form != DW_FORM_GNU_ref_alt && src_die->sect_off == sect_off)
+ if (!attr->form_is_alt () && src_die->sect_off == sect_off)
{
/* Self-reference, we're done. */
return src_die;
}
die = follow_die_offset (sect_off,
- (attr->form == DW_FORM_GNU_ref_alt
- || cu->per_cu->is_dwz),
+ attr->form_is_alt () || cu->per_cu->is_dwz,
ref_cu);
if (!die)
error (_(DWARF_ERROR_PREFIX
@@ -18460,6 +18466,7 @@ dwarf2_fetch_constant_bytes (sect_offset sect_off,
case DW_FORM_strx:
case DW_FORM_GNU_str_index:
case DW_FORM_GNU_strp_alt:
+ case DW_FORM_strp_sup:
/* The string is already allocated on the objfile obstack, point
directly to it. */
{
diff --git a/gdb/dwarf2/read.h b/gdb/dwarf2/read.h
index a9a2aa4..f3e043c 100644
--- a/gdb/dwarf2/read.h
+++ b/gdb/dwarf2/read.h
@@ -533,9 +533,9 @@ struct dwarf2_per_bfd
}
/* Return the separate '.dwz' debug file. If there is no
- .gnu_debugaltlink section in the file, then the result depends on
- REQUIRE: if REQUIRE is true, error out; if REQUIRE is false,
- return nullptr. */
+ .gnu_debugaltlink or .debug_sup section in the file, then the
+ result depends on REQUIRE: if REQUIRE is true, error out; if
+ REQUIRE is false, return nullptr. */
struct dwz_file *get_dwz_file (bool require = false)
{
gdb_assert (!require || this->dwz_file.has_value ());
@@ -546,7 +546,7 @@ struct dwarf2_per_bfd
{
result = this->dwz_file->get ();
if (require && result == nullptr)
- error (_("could not read '.gnu_debugaltlink' section"));
+ error (_("could not find supplementary DWARF file"));
}
return result;
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/microblaze-linux-tdep.c b/gdb/microblaze-linux-tdep.c
index 0f2f272..8dcbeaa 100644
--- a/gdb/microblaze-linux-tdep.c
+++ b/gdb/microblaze-linux-tdep.c
@@ -49,6 +49,9 @@ microblaze_linux_memory_remove_breakpoint (struct gdbarch *gdbarch,
/* Determine appropriate breakpoint contents and size for this address. */
bp = gdbarch_breakpoint_from_pc (gdbarch, &addr, &bplen);
+ /* Make sure we see the memory breakpoints. */
+ scoped_restore restore_memory
+ = make_scoped_restore_show_memory_breakpoints (1);
val = target_read_memory (addr, old_contents, bplen);
/* If our breakpoint is no longer at the address, this means that the
diff --git a/gdb/pager.h b/gdb/pager.h
index 3c4fcdf..052337d 100644
--- a/gdb/pager.h
+++ b/gdb/pager.h
@@ -50,7 +50,6 @@ public:
}
void emit_style_escape (const ui_file_style &style) override;
- void reset_style () override;
void flush () override;
diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index e8be470..2be5eaa 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -2883,7 +2883,7 @@ static void
printf_command (const char *arg, int from_tty)
{
ui_printf (arg, gdb_stdout);
- gdb_stdout->reset_style ();
+ gdb_stdout->emit_style_escape (ui_file_style ());
gdb_stdout->wrap_here (0);
gdb_stdout->flush ();
}
diff --git a/gdb/python/py-breakpoint.c b/gdb/python/py-breakpoint.c
index 861e9a3..58998f5 100644
--- a/gdb/python/py-breakpoint.c
+++ b/gdb/python/py-breakpoint.c
@@ -948,7 +948,7 @@ bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
else
{
PyErr_SetString (PyExc_RuntimeError,
- _("Line keyword should be an integer or a string. "));
+ _("Line keyword should be an integer or a string."));
return -1;
}
}
diff --git a/gdb/python/py-color.c b/gdb/python/py-color.c
index 91ad7ae..9e29ee2 100644
--- a/gdb/python/py-color.c
+++ b/gdb/python/py-color.c
@@ -80,33 +80,33 @@ gdbpy_get_color (PyObject *obj)
static PyObject *
get_attr (PyObject *obj, PyObject *attr_name)
{
- if (! PyUnicode_Check (attr_name))
+ if (!PyUnicode_Check (attr_name))
return PyObject_GenericGetAttr (obj, attr_name);
colorpy_object *self = (colorpy_object *) obj;
const ui_file_style::color &color = self->color;
- if (! PyUnicode_CompareWithASCIIString (attr_name, "colorspace"))
+ if (!PyUnicode_CompareWithASCIIString (attr_name, "colorspace"))
{
int value = static_cast<int> (color.colorspace ());
return gdb_py_object_from_longest (value).release ();
}
- if (! PyUnicode_CompareWithASCIIString (attr_name, "is_none"))
+ if (!PyUnicode_CompareWithASCIIString (attr_name, "is_none"))
return PyBool_FromLong (color.is_none ());
- if (! PyUnicode_CompareWithASCIIString (attr_name, "is_indexed"))
+ if (!PyUnicode_CompareWithASCIIString (attr_name, "is_indexed"))
return PyBool_FromLong (color.is_indexed ());
- if (! PyUnicode_CompareWithASCIIString (attr_name, "is_direct"))
+ if (!PyUnicode_CompareWithASCIIString (attr_name, "is_direct"))
return PyBool_FromLong (color.is_direct ());
if (color.is_indexed ()
- && ! PyUnicode_CompareWithASCIIString (attr_name, "index"))
+ && !PyUnicode_CompareWithASCIIString (attr_name, "index"))
return gdb_py_object_from_longest (color.get_value ()).release ();
if (color.is_direct ()
- && ! PyUnicode_CompareWithASCIIString (attr_name, "components"))
+ && !PyUnicode_CompareWithASCIIString (attr_name, "components"))
{
uint8_t rgb[3];
color.get_rgb (rgb);
@@ -144,7 +144,7 @@ colorpy_escape_sequence (PyObject *self, PyObject *is_fg_obj)
return nullptr;
}
- if (! PyBool_Check (is_fg_obj))
+ if (!PyBool_Check (is_fg_obj))
{
PyErr_SetString (PyExc_RuntimeError,
_("A boolean argument is required."));
@@ -175,17 +175,17 @@ colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
PyObject *colorspace_obj = nullptr;
color_space colorspace = color_space::MONOCHROME;
- if (! PyArg_ParseTuple (args, "|OO", &value_obj, &colorspace_obj))
+ if (!PyArg_ParseTuple (args, "|OO", &value_obj, &colorspace_obj))
return -1;
try
{
- if (colorspace_obj)
+ if (colorspace_obj != nullptr)
{
if (PyLong_Check (colorspace_obj))
{
long colorspace_id = -1;
- if (! gdb_py_int_as_long (colorspace_obj, &colorspace_id))
+ if (!gdb_py_int_as_long (colorspace_obj, &colorspace_id))
return -1;
if (!color_space_safe_cast (&colorspace, colorspace_id))
error (_("colorspace %ld is out of range."), colorspace_id);
@@ -201,11 +201,11 @@ colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
else if (PyLong_Check (value_obj))
{
long value = -1;
- if (! gdb_py_int_as_long (value_obj, &value))
+ if (!gdb_py_int_as_long (value_obj, &value))
return -1;
if (value < 0 || value > INT_MAX)
error (_("value %ld is out of range."), value);
- if (colorspace_obj)
+ if (colorspace_obj != nullptr)
obj->color = ui_file_style::color (colorspace, value);
else
obj->color = ui_file_style::color (value);
@@ -256,7 +256,6 @@ colorpy_init (PyObject *self, PyObject *args, PyObject *kwds)
return gdbpy_handle_gdb_exception (-1, except);
}
- Py_INCREF (self);
return 0;
}
@@ -272,10 +271,10 @@ colorpy_str (PyObject *self)
static int
gdbpy_initialize_color (void)
{
- for (auto & pair : colorspace_constants)
- if (PyModule_AddIntConstant (gdb_module, pair.name,
- static_cast<long> (pair.value)) < 0)
- return -1;
+ for (auto &pair : colorspace_constants)
+ if (PyModule_AddIntConstant (gdb_module, pair.name,
+ static_cast<long> (pair.value)) < 0)
+ return -1;
colorpy_object_type.tp_new = PyType_GenericNew;
return gdbpy_type_ready (&colorpy_object_type, gdb_module);
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..3749fc3
--- /dev/null
+++ b/gdb/riscv-canonicalize-syscall-gen.c
@@ -0,0 +1,342 @@
+/* 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_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; */
+ 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..0f481b1
--- /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 RISCV_LINUX_TDEP_H
+#define 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 /* 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,
+ [&regcache] (auto &&reg_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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
new file mode 100755
index 0000000..aa07ac1
--- /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 (riscv-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 (riscv-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/boards/cc-with-dwz-5.exp b/gdb/testsuite/boards/cc-with-dwz-5.exp
new file mode 100644
index 0000000..b254f91
--- /dev/null
+++ b/gdb/testsuite/boards/cc-with-dwz-5.exp
@@ -0,0 +1,28 @@
+# Copyright 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/>.
+
+# This file is a dejagnu "board file" and is used to run the testsuite
+# with contrib/cc-with-tweaks.sh -5.
+#
+# NOTE: We assume dwz is in $PATH.
+#
+# Example usage:
+# bash$ cd $objdir
+# bash$ make check-gdb \
+# RUNTESTFLAGS='--target_board=cc-with-dwz-5'
+#
+
+set CC_WITH_TWEAKS_FLAGS "-5"
+load_board_description "cc-with-tweaks"
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/dwzbuildid.exp b/gdb/testsuite/gdb.dwarf2/dwzbuildid.exp
index 055e69c..080e999 100644
--- a/gdb/testsuite/gdb.dwarf2/dwzbuildid.exp
+++ b/gdb/testsuite/gdb.dwarf2/dwzbuildid.exp
@@ -13,160 +13,5 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-load_lib dwarf.exp
-
-# This test can only be run on targets which support DWARF-2 and use gas.
-require dwarf2_support
-
-# No remote host testing either.
-require {!is_remote host}
-
-
-# Lots of source files since we test a few cases and make new files
-# for each.
-# The tests are:
-# ok - the main file refers to a dwz and the buildids match
-# mismatch - the buildids do not match
-# fallback - the buildids do not match but a match is found via buildid
-standard_testfile main.c \
- dwzbuildid-ok-base.S dwzbuildid-ok-sep.S \
- dwzbuildid-mismatch-base.S dwzbuildid-mismatch-sep.S \
- dwzbuildid-fallback-base.S dwzbuildid-fallback-sep.S \
- dwzbuildid-fallback-ok.S
-
-# Write some assembly that just has a .gnu_debugaltlink section.
-proc write_just_debugaltlink {filename dwzname buildid} {
- set asm_file [standard_output_file $filename]
-
- Dwarf::assemble $asm_file {
- upvar dwzname dwzname
- upvar buildid buildid
-
- gnu_debugaltlink $dwzname $buildid
-
- # Only the DWARF reader checks .gnu_debugaltlink, so make sure
- # there is a bit of DWARF in here.
- cu { label cu_start } {
- compile_unit {{language @DW_LANG_C}} {
- }
- }
- aranges {} cu_start {
- arange {} 0 0
- }
- }
-}
-
-# Write some DWARF that also sets the buildid.
-proc write_dwarf_file {filename buildid {value 99}} {
- set asm_file [standard_output_file $filename]
-
- Dwarf::assemble $asm_file {
- declare_labels int_label int_label2
-
- upvar buildid buildid
- upvar value value
-
- build_id $buildid
-
- cu { label cu_start } {
- compile_unit {{language @DW_LANG_C}} {
- int_label2: base_type {
- {name int}
- {byte_size 4 sdata}
- {encoding @DW_ATE_signed}
- }
-
- constant {
- {name the_int}
- {type :$int_label2}
- {const_value $value data1}
- }
- }
- }
-
- aranges {} cu_start {
- arange {} 0 0
- }
- }
-}
-
-if { [gdb_compile ${srcdir}/${subdir}/${srcfile} ${binfile}1.o \
- object {nodebug}] != "" } {
- return -1
-}
-
-# The values don't really matter, just whether they are equal.
-set ok_prefix 01
-set ok_suffix 02030405060708091011121314151617181920
-set ok_suffix2 020304050607080910111213141516171819ff
-set ok_buildid ${ok_prefix}${ok_suffix}
-set ok_buildid2 ${ok_prefix}${ok_suffix2}
-set bad_buildid [string repeat ff 20]
-
-set debugdir [standard_output_file {}]
-set basedir $debugdir/.build-id
-file mkdir $basedir $basedir/$ok_prefix
-
-# Test where the separate debuginfo's buildid matches.
-write_just_debugaltlink $srcfile2 ${binfile}3.o $ok_buildid
-write_dwarf_file $srcfile3 $ok_buildid
-
-# Test where the separate debuginfo's buildid does not match.
-write_just_debugaltlink $srcfile4 ${binfile}5.o $ok_buildid
-write_dwarf_file $srcfile5 $bad_buildid
-
-# Test where the separate debuginfo's buildid does not match, but then
-# we find a match in the .build-id directory.
-write_just_debugaltlink $srcfile6 ${binfile}7.o $ok_buildid2
-# Use 77 as the value so that if we load the bad debuginfo, we will
-# see the wrong result.
-write_dwarf_file $srcfile7 $bad_buildid 77
-write_dwarf_file $srcfile8 $ok_buildid2
-
-# Compile everything.
-for {set i 2} {$i <= 8} {incr i} {
- if {[gdb_compile [standard_output_file [set srcfile$i]] \
- ${binfile}$i.o object nodebug] != ""} {
- return -1
- }
-}
-
-# Copy a file into the .build-id place for the "fallback" test.
-file copy -force -- ${binfile}8.o $basedir/$ok_prefix/$ok_suffix2.debug
-
-proc do_test {} {
- clean_restart
-
- gdb_test_no_output "set debug-file-directory $::debugdir" \
- "set debug-file-directory"
-
- gdb_load ${::binfile}-${::testname}
-
- if {![runto_main]} {
- return
- }
-
- if {$::testname == "mismatch"} {
- gdb_test "print the_int" \
- "(No symbol table is loaded|No symbol \"the_int\" in current context).*"
- } else {
- gdb_test "print the_int" " = 99"
- }
-}
-
-foreach_with_prefix testname { ok mismatch fallback } {
- if { $testname == "ok" } {
- set objs [list ${binfile}1.o ${binfile}2.o]
- } elseif { $testname == "mismatch" } {
- set objs [list ${binfile}1.o ${binfile}4.o]
- } elseif { $testname == "fallback" } {
- set objs [list ${binfile}1.o ${binfile}6.o]
- }
-
- if {[gdb_compile $objs ${binfile}-$testname executable {quiet}] != ""} {
- unsupported "compilation failed"
- continue
- }
-
- do_test
-}
+set scenario gnu
+source $srcdir/$subdir/dwzbuildid.tcl
diff --git a/gdb/testsuite/gdb.dwarf2/dwzbuildid.tcl b/gdb/testsuite/gdb.dwarf2/dwzbuildid.tcl
new file mode 100644
index 0000000..a9077eb
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dwzbuildid.tcl
@@ -0,0 +1,184 @@
+# Copyright 2013-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/>.
+
+load_lib dwarf.exp
+
+# This test can only be run on targets which support DWARF-2 and use gas.
+require dwarf2_support
+
+# No remote host testing either.
+require {!is_remote host}
+
+
+# Lots of source files since we test a few cases and make new files
+# for each.
+# The tests are:
+# ok - the main file refers to a dwz and the buildids match
+# mismatch - the buildids do not match
+# fallback - the buildids do not match but a match is found via buildid
+standard_testfile main.c \
+ dwzbuildid-ok-base.S dwzbuildid-ok-sep.S \
+ dwzbuildid-mismatch-base.S dwzbuildid-mismatch-sep.S \
+ dwzbuildid-fallback-base.S dwzbuildid-fallback-sep.S \
+ dwzbuildid-fallback-ok.S
+
+# Write some assembly that just has a .gnu_debugaltlink section.
+proc write_just_debugaltlink {filename dwzname buildid} {
+ set asm_file [standard_output_file $filename]
+
+ Dwarf::assemble $asm_file {
+ upvar dwzname dwzname
+ upvar buildid buildid
+
+ if {$::scenario == "gnu"} {
+ gnu_debugaltlink $dwzname $buildid
+ } else {
+ debug_sup 0 $dwzname $buildid
+ }
+
+ # Only the DWARF reader checks .gnu_debugaltlink, so make sure
+ # there is a bit of DWARF in here.
+ cu { label cu_start } {
+ compile_unit {{language @DW_LANG_C}} {
+ }
+ }
+ aranges {} cu_start {
+ arange {} 0 0
+ }
+ }
+}
+
+# Write some DWARF that also sets the buildid.
+proc write_dwarf_file {filename buildid {value 99}} {
+ set asm_file [standard_output_file $filename]
+
+ Dwarf::assemble $asm_file {
+ declare_labels int_label int_label2
+
+ upvar buildid buildid
+ upvar value value
+
+ if {$::scenario == "gnu"} {
+ build_id $buildid
+ } else {
+ debug_sup 1 "" $buildid
+ }
+
+ cu { label cu_start } {
+ compile_unit {{language @DW_LANG_C}} {
+ int_label2: base_type {
+ {name int}
+ {byte_size 4 sdata}
+ {encoding @DW_ATE_signed}
+ }
+
+ constant {
+ {name the_int}
+ {type :$int_label2}
+ {const_value $value data1}
+ }
+ }
+ }
+
+ aranges {} cu_start {
+ arange {} 0 0
+ }
+ }
+}
+
+if { [gdb_compile ${srcdir}/${subdir}/${srcfile} ${binfile}1.o \
+ object {nodebug}] != "" } {
+ return -1
+}
+
+# The values don't really matter, just whether they are equal.
+set ok_prefix 01
+set ok_suffix 02030405060708091011121314151617181920
+set ok_suffix2 020304050607080910111213141516171819ff
+set ok_buildid ${ok_prefix}${ok_suffix}
+set ok_buildid2 ${ok_prefix}${ok_suffix2}
+set bad_buildid [string repeat ff 20]
+
+set debugdir [standard_output_file {}]
+set basedir $debugdir/.build-id
+file mkdir $basedir $basedir/$ok_prefix
+
+# Test where the separate debuginfo's buildid matches.
+write_just_debugaltlink $srcfile2 ${binfile}3.o $ok_buildid
+write_dwarf_file $srcfile3 $ok_buildid
+
+# Test where the separate debuginfo's buildid does not match.
+write_just_debugaltlink $srcfile4 ${binfile}5.o $ok_buildid
+write_dwarf_file $srcfile5 $bad_buildid
+
+# Test where the separate debuginfo's buildid does not match, but then
+# we find a match in the .build-id directory.
+write_just_debugaltlink $srcfile6 ${binfile}7.o $ok_buildid2
+# Use 77 as the value so that if we load the bad debuginfo, we will
+# see the wrong result.
+write_dwarf_file $srcfile7 $bad_buildid 77
+write_dwarf_file $srcfile8 $ok_buildid2
+
+# Compile everything.
+for {set i 2} {$i <= 8} {incr i} {
+ if {[gdb_compile [standard_output_file [set srcfile$i]] \
+ ${binfile}$i.o object nodebug] != ""} {
+ return -1
+ }
+}
+
+# Copy a file into the .build-id place for the "fallback" test.
+file copy -force -- ${binfile}8.o $basedir/$ok_prefix/$ok_suffix2.debug
+
+proc do_test {} {
+ clean_restart
+
+ gdb_test_no_output "set debug-file-directory $::debugdir" \
+ "set debug-file-directory"
+
+ gdb_load ${::binfile}-${::testname}
+
+ if {![runto_main]} {
+ return
+ }
+
+ if {$::testname == "mismatch"} {
+ gdb_test "print the_int" \
+ "(No symbol table is loaded|No symbol \"the_int\" in current context).*"
+ } else {
+ gdb_test "print the_int" " = 99"
+ }
+}
+
+set tests {ok mismatch}
+if {$scenario == "gnu"} {
+ lappend tests fallback
+}
+foreach_with_prefix testname $tests {
+ if { $testname == "ok" } {
+ set objs [list ${binfile}1.o ${binfile}2.o]
+ } elseif { $testname == "mismatch" } {
+ set objs [list ${binfile}1.o ${binfile}4.o]
+ } elseif { $testname == "fallback" } {
+ set objs [list ${binfile}1.o ${binfile}6.o]
+ }
+
+ if {[gdb_compile $objs ${binfile}-$testname executable {quiet}] != ""} {
+ unsupported "compilation failed"
+ continue
+ }
+
+ do_test
+}
diff --git a/gdb/testsuite/gdb.dwarf2/dwzbuildid5.exp b/gdb/testsuite/gdb.dwarf2/dwzbuildid5.exp
new file mode 100644
index 0000000..047626c
--- /dev/null
+++ b/gdb/testsuite/gdb.dwarf2/dwzbuildid5.exp
@@ -0,0 +1,17 @@
+# Copyright 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/>.
+
+set scenario dwarf5
+source $srcdir/$subdir/dwzbuildid.tcl
diff --git a/gdb/testsuite/gdb.dwarf2/dwznolink.exp b/gdb/testsuite/gdb.dwarf2/dwznolink.exp
index 91fe369..0c486ea 100644
--- a/gdb/testsuite/gdb.dwarf2/dwznolink.exp
+++ b/gdb/testsuite/gdb.dwarf2/dwznolink.exp
@@ -49,5 +49,5 @@ if {[build_executable $testfile.exp $testfile \
clean_restart
gdb_test "file -readnow $binfile" \
- "could not read '.gnu_debugaltlink' section" \
+ "could not find supplementary DWARF file" \
"file $testfile"
diff --git a/gdb/testsuite/gdb.dwarf2/no-gnu-debuglink.exp b/gdb/testsuite/gdb.dwarf2/no-gnu-debuglink.exp
index 7475d7a..05e625f 100644
--- a/gdb/testsuite/gdb.dwarf2/no-gnu-debuglink.exp
+++ b/gdb/testsuite/gdb.dwarf2/no-gnu-debuglink.exp
@@ -38,7 +38,7 @@ if { [build_executable $testfile.exp $testfile [list $srcfile $asm_file]] } {
clean_restart
gdb_test_no_output "maint set dwarf synchronous on"
-set msg "\r\nwarning: could not find '\.gnu_debugaltlink' file for \[^\r\n\]*"
+set msg "\r\nwarning: could not find supplementary DWARF file \[^\r\n\]*"
gdb_test "file $binfile" "$msg" "file command"
set question "Load new symbol table from .*\? .y or n. "
diff --git a/gdb/testsuite/gdb.python/gdb_leak_detector.py b/gdb/testsuite/gdb.python/gdb_leak_detector.py
new file mode 100644
index 0000000..b0f6d47
--- /dev/null
+++ b/gdb/testsuite/gdb.python/gdb_leak_detector.py
@@ -0,0 +1,120 @@
+# Copyright (C) 2021-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/>.
+
+# Defines a base class, which can be sub-classed, in order to run
+# memory leak tests on some aspects of GDB's Python API. See the
+# comments on the gdb_leak_detector class for more details.
+
+import os
+import tracemalloc
+import gdb
+
+
+# This class must be sub-classed to create a memory leak test. The
+# sub-classes __init__ method should call the parent classes __init__
+# method, and the sub-class should override allocate() and
+# deallocate(). See the comments on the various methods below for
+# more details of required arguments and expected usage.
+class gdb_leak_detector:
+
+ # Class initialisation. FILENAME is the file in which the
+ # sub-class is defined, usually passed as just '__file__'. This
+ # is used when looking for memory allocations; only allocations in
+ # FILENAME are considered.
+ def __init__(self, filename):
+ self.filters = [tracemalloc.Filter(True, "*" + os.path.basename(filename))]
+
+ # Internal helper function to actually run the test. Calls the
+ # allocate() method to allocate an object from GDB's Python API.
+ # When CLEAR is True the object will then be deallocated by
+ # calling deallocate(), otherwise, deallocate() is not called.
+ #
+ # Finally, this function checks for any memory allocatios
+ # originating from 'self.filename' that have not been freed, and
+ # returns the total (in bytes) of the memory that has been
+ # allocated, but not freed.
+ def _do_test(self, clear):
+ # Start tracing, and take a snapshot of the current allocations.
+ tracemalloc.start()
+ snapshot1 = tracemalloc.take_snapshot()
+
+ # Generate the GDB Python API object by calling the allocate
+ # method.
+ self.allocate()
+
+ # Possibly clear the reference to the allocated object.
+ if clear:
+ self.deallocate()
+
+ # Now grab a second snapshot of memory allocations, and stop
+ # tracing memory allocations.
+ snapshot2 = tracemalloc.take_snapshot()
+ tracemalloc.stop()
+
+ # Filter the snapshots; we only care about allocations originating
+ # from this file.
+ snapshot1 = snapshot1.filter_traces(self.filters)
+ snapshot2 = snapshot2.filter_traces(self.filters)
+
+ # Compare the snapshots, this leaves only things that were
+ # allocated, but not deallocated since the first snapshot.
+ stats = snapshot2.compare_to(snapshot1, "traceback")
+
+ # Total up all the allocated things.
+ total = 0
+ for stat in stats:
+ total += stat.size_diff
+ return total
+
+ # Run the memory leak test. Prints 'PASS' if successful,
+ # otherwise, raises an exception (of type GdbError).
+ def run(self):
+ # The first time we run this some global state will be allocated which
+ # shows up as memory that is allocated, but not released. So, run the
+ # test once and discard the result.
+ self._do_test(True)
+
+ # Now run the test twice, the first time we clear our global reference
+ # to the allocated object, which should allow Python to deallocate the
+ # object. The second time we hold onto the global reference, preventing
+ # Python from performing the deallocation.
+ bytes_with_clear = self._do_test(True)
+ bytes_without_clear = self._do_test(False)
+
+ # If there are any allocations left over when we cleared the reference
+ # (and expected deallocation) then this indicates a leak.
+ if bytes_with_clear > 0:
+ raise gdb.GdbError("memory leak when object reference was released")
+
+ # If there are no allocations showing when we hold onto a reference,
+ # then this likely indicates that the testing infrastructure is broken,
+ # and we're no longer spotting the allocations at all.
+ if bytes_without_clear == 0:
+ raise gdb.GdbError("object is unexpectedly not showing as allocated")
+
+ # Print a PASS message that the TCL script can see.
+ print("PASS")
+
+ # Sub-classes must override this method. Allocate an object (or
+ # multiple objects) from GDB's Python API. Store references to
+ # these objects within SELF.
+ def allocate(self):
+ raise NotImplementedError("allocate() not implemented")
+
+ # Sub-classes must override this method. Deallocate the object(s)
+ # allocated by the allocate() method. All that is required is for
+ # the references created in allocate() to be set to None.
+ def deallocate(self):
+ raise NotImplementedError("allocate() not implemented")
diff --git a/gdb/testsuite/gdb.python/py-breakpoint.exp b/gdb/testsuite/gdb.python/py-breakpoint.exp
index 1b9c05f..9a901a3 100644
--- a/gdb/testsuite/gdb.python/py-breakpoint.exp
+++ b/gdb/testsuite/gdb.python/py-breakpoint.exp
@@ -707,7 +707,7 @@ proc_with_prefix test_bkpt_explicit_loc {} {
delete_breakpoints
gdb_test "python bp1 = gdb.Breakpoint (line=bp1)" \
- "RuntimeError.*: Line keyword should be an integer or a string.*" \
+ "RuntimeError.*: Line keyword should be an integer or a string\\.\r\n.*" \
"set explicit breakpoint by invalid line type"
delete_breakpoints
diff --git a/gdb/testsuite/gdb.python/py-color-leak.exp b/gdb/testsuite/gdb.python/py-color-leak.exp
new file mode 100644
index 0000000..6d7fa7c
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-color-leak.exp
@@ -0,0 +1,28 @@
+# Copyright (C) 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/>.
+
+# This file is part of the GDB testsuite. It checks for memory leaks
+# associated with allocating gdb.Color objects.
+
+load_lib gdb-python.exp
+
+require allow_python_tests
+
+standard_testfile
+
+clean_restart
+
+gdb_py_run_memory_leak_test ${srcdir}/${subdir}/${testfile}.py \
+ "gdb.Color object deallocates correctly"
diff --git a/gdb/testsuite/gdb.python/py-color-leak.py b/gdb/testsuite/gdb.python/py-color-leak.py
new file mode 100644
index 0000000..50dc315
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-color-leak.py
@@ -0,0 +1,31 @@
+# Copyright (C) 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/>.
+
+import gdb_leak_detector
+
+
+class color_leak_detector(gdb_leak_detector.gdb_leak_detector):
+ def __init__(self):
+ super().__init__(__file__)
+ self.color = None
+
+ def allocate(self):
+ self.color = gdb.Color("red")
+
+ def deallocate(self):
+ self.color = None
+
+
+color_leak_detector().run()
diff --git a/gdb/testsuite/gdb.python/py-color.exp b/gdb/testsuite/gdb.python/py-color.exp
index c6f1041..1b8e0c5 100644
--- a/gdb/testsuite/gdb.python/py-color.exp
+++ b/gdb/testsuite/gdb.python/py-color.exp
@@ -13,8 +13,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-# This file is part of the GDB testsuite.
-# It tests gdb.parameter and gdb.Parameter.
+# This file is part of the GDB testsuite. It tests gdb.Color.
load_lib gdb-python.exp
diff --git a/gdb/testsuite/gdb.python/py-inferior-leak.exp b/gdb/testsuite/gdb.python/py-inferior-leak.exp
index 6710f59..15216ee 100644
--- a/gdb/testsuite/gdb.python/py-inferior-leak.exp
+++ b/gdb/testsuite/gdb.python/py-inferior-leak.exp
@@ -24,15 +24,5 @@ standard_testfile
clean_restart
-# Skip this test if the tracemalloc module is not available.
-if { ![gdb_py_module_available "tracemalloc"] } {
- unsupported "tracemalloc module not available"
- return
-}
-
-set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
-
-# Source the Python script, this runs the test (which is written
-# completely in Python), and either prints PASS, or throws an
-# exception.
-gdb_test "source ${pyfile}" "PASS" "source python script"
+gdb_py_run_memory_leak_test ${srcdir}/${subdir}/${testfile}.py \
+ "gdb.Inferior object deallocates correctly"
diff --git a/gdb/testsuite/gdb.python/py-inferior-leak.py b/gdb/testsuite/gdb.python/py-inferior-leak.py
index 97837dc..38f33c3 100644
--- a/gdb/testsuite/gdb.python/py-inferior-leak.py
+++ b/gdb/testsuite/gdb.python/py-inferior-leak.py
@@ -14,99 +14,31 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
-import tracemalloc
+import gdb_leak_detector
-import gdb
-# A global variable in which we store a reference to the gdb.Inferior
-# object sent to us in the new_inferior event.
-inf = None
+class inferior_leak_detector(gdb_leak_detector.gdb_leak_detector):
+ def __init__(self):
+ super().__init__(__file__)
+ self.inferior = None
+ self.__handler = lambda event: setattr(self, "inferior", event.inferior)
+ gdb.events.new_inferior.connect(self.__handler)
+ def __del__(self):
+ gdb.events.new_inferior.disconnect(self.__handler)
-# Register the new_inferior event handler.
-def new_inferior_handler(event):
- global inf
- inf = event.inferior
+ def allocate(self):
+ output = gdb.execute("add-inferior", False, True)
+ m = re.search(r"Added inferior (\d+)", output)
+ if m:
+ num = int(m.group(1))
+ else:
+ raise RuntimeError("no match")
+ gdb.execute("remove-inferiors %s" % num)
-gdb.events.new_inferior.connect(new_inferior_handler)
+ def deallocate(self):
+ self.inferior = None
-# A global filters list, we only care about memory allocations
-# originating from this script.
-filters = [tracemalloc.Filter(True, "*py-inferior-leak.py")]
-
-# Add a new inferior, and return the number of the new inferior.
-def add_inferior():
- output = gdb.execute("add-inferior", False, True)
- m = re.search(r"Added inferior (\d+)", output)
- if m:
- num = int(m.group(1))
- else:
- raise RuntimeError("no match")
- return num
-
-
-# Run the test. When CLEAR is True we clear the global INF variable
-# before comparing the before and after memory allocation traces.
-# When CLEAR is False we leave INF set to reference the gdb.Inferior
-# object, thus preventing the gdb.Inferior from being deallocated.
-def test(clear):
- global filters, inf
-
- # Start tracing, and take a snapshot of the current allocations.
- tracemalloc.start()
- snapshot1 = tracemalloc.take_snapshot()
-
- # Create an inferior, this triggers the new_inferior event, which
- # in turn holds a reference to the new gdb.Inferior object in the
- # global INF variable.
- num = add_inferior()
- gdb.execute("remove-inferiors %s" % num)
-
- # Possibly clear the global INF variable.
- if clear:
- inf = None
-
- # Now grab a second snapshot of memory allocations, and stop
- # tracing memory allocations.
- snapshot2 = tracemalloc.take_snapshot()
- tracemalloc.stop()
-
- # Filter the snapshots; we only care about allocations originating
- # from this file.
- snapshot1 = snapshot1.filter_traces(filters)
- snapshot2 = snapshot2.filter_traces(filters)
-
- # Compare the snapshots, this leaves only things that were
- # allocated, but not deallocated since the first snapshot.
- stats = snapshot2.compare_to(snapshot1, "traceback")
-
- # Total up all the deallocated things.
- total = 0
- for stat in stats:
- total += stat.size_diff
- return total
-
-
-# The first time we run this some global state will be allocated which
-# shows up as memory that is allocated, but not released. So, run the
-# test once and discard the result.
-test(True)
-
-# Now run the test twice, the first time we clear our global reference
-# to the gdb.Inferior object, which should allow Python to deallocate
-# the object. The second time we hold onto the global reference,
-# preventing Python from performing the deallocation.
-bytes_with_clear = test(True)
-bytes_without_clear = test(False)
-
-# The bug that used to exist in GDB was that even when we released the
-# global reference the gdb.Inferior object would not be deallocated.
-if bytes_with_clear > 0:
- raise gdb.GdbError("memory leak when gdb.Inferior should be released")
-if bytes_without_clear == 0:
- raise gdb.GdbError("gdb.Inferior object is no longer allocated")
-
-# Print a PASS message that the test script can see.
-print("PASS")
+inferior_leak_detector().run()
diff --git a/gdb/testsuite/gdb.python/py-read-memory-leak.exp b/gdb/testsuite/gdb.python/py-read-memory-leak.exp
index 0015a57..9ae5eb8 100644
--- a/gdb/testsuite/gdb.python/py-read-memory-leak.exp
+++ b/gdb/testsuite/gdb.python/py-read-memory-leak.exp
@@ -30,15 +30,5 @@ if ![runto_main] {
return -1
}
-# Skip this test if the tracemalloc module is not available.
-if { ![gdb_py_module_available "tracemalloc"] } {
- unsupported "tracemalloc module not available"
- return
-}
-
-set pyfile [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
-
-# Source the Python script, this runs the test (which is written
-# completely in Python), and either prints PASS, or throws an
-# exception.
-gdb_test "source ${pyfile}" "PASS" "source python script"
+gdb_py_run_memory_leak_test ${srcdir}/${subdir}/${testfile}.py \
+ "buffers returned by read_memory() deallocates correctly"
diff --git a/gdb/testsuite/gdb.python/py-read-memory-leak.py b/gdb/testsuite/gdb.python/py-read-memory-leak.py
index 348403d..71edf47 100644
--- a/gdb/testsuite/gdb.python/py-read-memory-leak.py
+++ b/gdb/testsuite/gdb.python/py-read-memory-leak.py
@@ -13,81 +13,21 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import os
-import tracemalloc
+import gdb_leak_detector
-import gdb
-# A global variable in which we store a reference to the memory buffer
-# returned from gdb.Inferior.read_memory().
-mem_buf = None
+class read_leak_detector(gdb_leak_detector.gdb_leak_detector):
+ def __init__(self):
+ super().__init__(__file__)
+ self.mem_buf = None
+ self.addr = gdb.parse_and_eval("px")
+ self.inf = gdb.inferiors()[0]
+ def allocate(self):
+ self.mem_buf = self.inf.read_memory(self.addr, 4096)
-# A global filters list, we only care about memory allocations
-# originating from this script.
-filters = [tracemalloc.Filter(True, "*" + os.path.basename(__file__))]
+ def deallocate(self):
+ self.mem_buf = None
-# Run the test. When CLEAR is True we clear the global INF variable
-# before comparing the before and after memory allocation traces.
-# When CLEAR is False we leave INF set to reference the gdb.Inferior
-# object, thus preventing the gdb.Inferior from being deallocated.
-def test(clear):
- global filters, mem_buf
-
- addr = gdb.parse_and_eval("px")
- inf = gdb.inferiors()[0]
-
- # Start tracing, and take a snapshot of the current allocations.
- tracemalloc.start()
- snapshot1 = tracemalloc.take_snapshot()
-
- # Read from the inferior, this allocate a memory buffer object.
- mem_buf = inf.read_memory(addr, 4096)
-
- # Possibly clear the global INF variable.
- if clear:
- mem_buf = None
-
- # Now grab a second snapshot of memory allocations, and stop
- # tracing memory allocations.
- snapshot2 = tracemalloc.take_snapshot()
- tracemalloc.stop()
-
- # Filter the snapshots; we only care about allocations originating
- # from this file.
- snapshot1 = snapshot1.filter_traces(filters)
- snapshot2 = snapshot2.filter_traces(filters)
-
- # Compare the snapshots, this leaves only things that were
- # allocated, but not deallocated since the first snapshot.
- stats = snapshot2.compare_to(snapshot1, "traceback")
-
- # Total up all the allocated things.
- total = 0
- for stat in stats:
- total += stat.size_diff
- return total
-
-
-# The first time we run this some global state will be allocated which
-# shows up as memory that is allocated, but not released. So, run the
-# test once and discard the result.
-test(True)
-
-# Now run the test twice, the first time we clear our global reference
-# to the memory buffer object, which should allow Python to deallocate
-# the object. The second time we hold onto the global reference,
-# preventing Python from performing the deallocation.
-bytes_with_clear = test(True)
-bytes_without_clear = test(False)
-
-# The bug that used to exist in GDB was that even when we released the
-# global reference the gdb.Inferior object would not be deallocated.
-if bytes_with_clear > 0:
- raise gdb.GdbError("memory leak when memory buffer should be released")
-if bytes_without_clear == 0:
- raise gdb.GdbError("memory buffer object is no longer allocated")
-
-# Print a PASS message that the test script can see.
-print("PASS")
+read_leak_detector().run()
diff --git a/gdb/testsuite/lib/dwarf.exp b/gdb/testsuite/lib/dwarf.exp
index 46b39a1..7e8778a 100644
--- a/gdb/testsuite/lib/dwarf.exp
+++ b/gdb/testsuite/lib/dwarf.exp
@@ -3068,6 +3068,24 @@ namespace eval Dwarf {
}
}
+ # Emit a .debug_sup section with the given file name and build-id.
+ # The buildid should be represented as a hexadecimal string, like
+ # "ffeeddcc".
+ proc debug_sup {is_sup filename buildid} {
+ _defer_output .debug_sup {
+ # The version.
+ _op .2byte 0x5
+ # Supplementary marker.
+ _op .byte $is_sup
+ _op .ascii [_quote $filename]
+ set len [expr {[string length $buildid] / 2}]
+ _op .uleb128 $len
+ foreach {a b} [split $buildid {}] {
+ _op .byte 0x$a$b
+ }
+ }
+ }
+
proc _note {type name hexdata} {
set namelen [expr [string length $name] + 1]
set datalen [expr [string length $hexdata] / 2]
diff --git a/gdb/testsuite/lib/gdb-python.exp b/gdb/testsuite/lib/gdb-python.exp
index b4eb40d..e026c1b 100644
--- a/gdb/testsuite/lib/gdb-python.exp
+++ b/gdb/testsuite/lib/gdb-python.exp
@@ -77,3 +77,24 @@ proc gdb_py_module_available { name } {
return ${available}
}
+
+# Run a memory leak test within the Python script FILENAME. This proc
+# checks that the required Python modules are available, sets up the
+# syspath so that the helper module can be found (in the same
+# directory as FILENAME), then loads FILENAME to run the test.
+proc gdb_py_run_memory_leak_test { filename testname } {
+ if { ![gdb_py_module_available "tracemalloc"] } {
+ unsupported "$testname (tracemalloc module not available)"
+ }
+
+ gdb_test_no_output -nopass "python import sys"
+ gdb_test_no_output -nopass \
+ "python sys.path.insert(0, \"[file dirname $filename]\")" \
+ "setup sys.path"
+
+ set pyfile [gdb_remote_download host ${filename}]
+
+ # Source the Python script, this runs the test, and either prints
+ # PASS, or throws an exception.
+ gdb_test "source ${pyfile}" "^PASS" $testname
+}
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/gdb/ui-file.c b/gdb/ui-file.c
index 09e8b0b..f86b6b1 100644
--- a/gdb/ui-file.c
+++ b/gdb/ui-file.c
@@ -88,18 +88,6 @@ ui_file::emit_style_escape (const ui_file_style &style)
/* See ui-file.h. */
void
-ui_file::reset_style ()
-{
- if (can_emit_style_escape ())
- {
- m_applied_style = ui_file_style ();
- this->puts (m_applied_style.to_ansi ().c_str ());
- }
-}
-
-/* See ui-file.h. */
-
-void
ui_file::printchar (int c, int quoter, bool async_safe)
{
char buf[4];
diff --git a/gdb/ui-file.h b/gdb/ui-file.h
index 351cf1f..3919e52 100644
--- a/gdb/ui-file.h
+++ b/gdb/ui-file.h
@@ -123,9 +123,6 @@ public:
/* Emit an ANSI style escape for STYLE. */
virtual void emit_style_escape (const ui_file_style &style);
- /* Rest the current output style to the empty style. */
- virtual void reset_style ();
-
/* Print STR, bypassing any paging that might be done by this
ui_file. Note that nearly no code should call this -- it's
intended for use by gdb_printf, but nothing else. */
@@ -353,12 +350,6 @@ public:
m_two->emit_style_escape (style);
}
- void reset_style () override
- {
- m_one->reset_style ();
- m_two->reset_style ();
- }
-
void puts_unfiltered (const char *str) override
{
m_one->puts_unfiltered (str);
@@ -389,10 +380,6 @@ public:
void emit_style_escape (const ui_file_style &style) override
{
}
-
- void reset_style () override
- {
- }
};
/* A base class for ui_file types that wrap another ui_file. */
@@ -419,10 +406,6 @@ public:
void emit_style_escape (const ui_file_style &style) override
{ m_stream->emit_style_escape (style); }
- /* Rest the current output style to the empty style. */
- void reset_style () override
- { m_stream->reset_style (); }
-
int fd () const override
{ return m_stream->fd (); }
diff --git a/gdb/ui-style.c b/gdb/ui-style.c
index b8321c5..b8d73ab 100644
--- a/gdb/ui-style.c
+++ b/gdb/ui-style.c
@@ -45,7 +45,8 @@ static const char ansi_regex_text[] =
/* The compiled form of ansi_regex_text. */
-static regex_t ansi_regex;
+static compiled_regex ansi_regex (ansi_regex_text, REG_EXTENDED,
+ _("Error in ANSI terminal escape sequences regex"));
/* This maps 8-color palette to RGB triples. The values come from
plain linux terminal. */
@@ -364,7 +365,7 @@ ui_file_style::parse (const char *buf, size_t *n_read)
{
regmatch_t subexps[NUM_SUBEXPRESSIONS];
- int match = regexec (&ansi_regex, buf, ARRAY_SIZE (subexps), subexps, 0);
+ int match = ansi_regex.exec (buf, ARRAY_SIZE (subexps), subexps, 0);
if (match == REG_NOMATCH)
{
*n_read = 0;
@@ -531,7 +532,7 @@ skip_ansi_escape (const char *buf, int *n_read)
{
regmatch_t subexps[NUM_SUBEXPRESSIONS];
- int match = regexec (&ansi_regex, buf, ARRAY_SIZE (subexps), subexps, 0);
+ int match = ansi_regex.exec (buf, ARRAY_SIZE (subexps), subexps, 0);
if (match == REG_NOMATCH || buf[subexps[FINAL_SUBEXP].rm_so] != 'm')
return false;
@@ -539,16 +540,6 @@ skip_ansi_escape (const char *buf, int *n_read)
return true;
}
-void _initialize_ui_style ();
-void
-_initialize_ui_style ()
-{
- int code = regcomp (&ansi_regex, ansi_regex_text, REG_EXTENDED);
- /* If the regular expression was incorrect, it was a programming
- error. */
- gdb_assert (code == 0);
-}
-
/* See ui-style.h. */
const std::vector<color_space> &
diff --git a/gdb/utils.c b/gdb/utils.c
index 986b906..ce3c26e 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -1407,18 +1407,6 @@ pager_file::emit_style_escape (const ui_file_style &style)
}
}
-/* See pager.h. */
-
-void
-pager_file::reset_style ()
-{
- if (can_emit_style_escape ())
- {
- m_applied_style = ui_file_style ();
- m_wrap_buffer.append (m_applied_style.to_ansi ());
- }
-}
-
/* Wait, so the user can read what's on the screen. Prompt the user
to continue by pressing RETURN. 'q' is also provided because
telling users what to do in the prompt is more user-friendly than
diff --git a/gprofng/doc/gprofng_ug.texi b/gprofng/doc/gprofng_ug.texi
index 7147090..f23f7b3 100644
--- a/gprofng/doc/gprofng_ug.texi
+++ b/gprofng/doc/gprofng_ug.texi
@@ -310,9 +310,8 @@ Since there is no need to recompile, existing executables can be used
and the profile measures the behaviour of exactly the same executable that is
used in production runs.
-With sampling, one inherently profiles a different executable, because
-the calls to the instrumentation library may affect the compiler optimizations
-and run time behaviour.
+With tracing, one inherently profiles a different executable, because
+the calls to the instrumentation library may affect runtime behaviour.
@item
With sampling, there are very few restrictions on what can be profiled and
diff --git a/gprofng/src/CallStack.cc b/gprofng/src/CallStack.cc
index 6df4112..9a3a15d 100644
--- a/gprofng/src/CallStack.cc
+++ b/gprofng/src/CallStack.cc
@@ -527,14 +527,10 @@ CallStackP::add_stack (DataDescriptor *dDscr, long idx, FramePacket *frp,
Vaddr va = frp->getFromStack (index);
DbeInstr *cur_instr = experiment->map_Vaddr_to_PC (va, tstamp);
-#if ARCH(Intel)// TBR? FIXUP_XXX_SPARC_LINUX: switch should be on experiment ARCH, not dbe ARCH
// We need to adjust return addresses on intel
- // in order to attribute inclusive metrics to
- // proper call instructions.
- if (experiment->exp_maj_version <= 9)
- if (!leaf && cur_instr->addr != 0)
- cur_instr = cur_instr->func->find_dbeinstr (0, cur_instr->addr - 1);
-#endif
+ // in order to attribute inclusive metrics to proper instructions.
+ if (experiment->platform == Intel && cur_instr->addr != 0)
+ cur_instr = cur_instr->func->find_dbeinstr (0, cur_instr->addr - 1);
// Skip PC's from PLT, update leaf and state accordingly
if ((cur_instr->func->flags & FUNC_FLAG_PLT)
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
diff --git a/ld/ldmisc.c b/ld/ldmisc.c
index 9ee0781..3f305fa 100644
--- a/ld/ldmisc.c
+++ b/ld/ldmisc.c
@@ -42,7 +42,6 @@
%C clever filename:linenumber with function
%D like %C, but no function name
%E current bfd error or errno
- %F error is fatal
%G like %D, but only function name
%H like %C but in addition emit section+offset
%P print program name
@@ -70,7 +69,6 @@
void
vfinfo (FILE *fp, const char *fmt, va_list ap, bool is_warning)
{
- bool isfatal = false;
const char *scan;
int arg_type;
unsigned int arg_count = 0;
@@ -280,11 +278,6 @@ vfinfo (FILE *fp, const char *fmt, va_list ap, bool is_warning)
}
break;
- case 'F':
- /* Error is fatal. */
- isfatal = true;
- break;
-
case 'P':
/* Print program name. */
fprintf (fp, "%s", program_name);
@@ -586,9 +579,6 @@ vfinfo (FILE *fp, const char *fmt, va_list ap, bool is_warning)
if (is_warning && config.fatal_warnings)
config.make_executable = false;
-
- if (isfatal)
- xexit (1);
}
/* Format info message and print on stdout. */