aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/configure.tgt2
-rw-r--r--gdb/loongarch-linux-tdep.c3
-rw-r--r--gdb/loongarch-tdep.c495
-rw-r--r--gdb/loongarch-tdep.h3
-rw-r--r--gdb/testsuite/lib/gdb.exp2
5 files changed, 504 insertions, 1 deletions
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 62df71b..2d78c11 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -363,7 +363,7 @@ lm32-*-*)
loongarch*-*-linux*)
# Target: LoongArch running Linux
gdb_target_obs="loongarch-linux-tdep.o glibc-tdep.o \
- linux-tdep.o solib-svr4.o"
+ linux-tdep.o solib-svr4.o linux-record.o"
;;
m32c-*-*)
diff --git a/gdb/loongarch-linux-tdep.c b/gdb/loongarch-linux-tdep.c
index 7157806..86e7ed8 100644
--- a/gdb/loongarch-linux-tdep.c
+++ b/gdb/loongarch-linux-tdep.c
@@ -602,6 +602,9 @@ loongarch_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
/* Get the syscall number from the arch's register. */
set_gdbarch_get_syscall_number (gdbarch, loongarch_linux_get_syscall_number);
+
+ /* Reversible debugging, process record. */
+ set_gdbarch_process_record (gdbarch, loongarch_process_record);
}
/* Initialize LoongArch Linux target support. */
diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c
index c50dd7f..c1b3a1f 100644
--- a/gdb/loongarch-tdep.c
+++ b/gdb/loongarch-tdep.c
@@ -18,12 +18,16 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "arch-utils.h"
+#include "arch/loongarch-insn.h"
#include "dwarf2/frame.h"
#include "elf-bfd.h"
#include "extract-store-integer.h"
#include "frame-unwind.h"
#include "gdbcore.h"
+#include "linux-record.h"
#include "loongarch-tdep.h"
+#include "record.h"
+#include "record-full.h"
#include "reggroups.h"
#include "target.h"
#include "target-descriptions.h"
@@ -1889,6 +1893,497 @@ loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
return gdbarch;
}
+/* LoongArch record/replay enumerations and structures. */
+
+enum loongarch_record_result
+{
+ LOONGARCH_RECORD_SUCCESS,
+ LOONGARCH_RECORD_UNSUPPORTED,
+ LOONGARCH_RECORD_UNKNOWN
+};
+
+struct loongarch_record_s
+{
+ struct gdbarch *gdbarch;
+ struct regcache *regcache;
+ CORE_ADDR this_addr; /* Addr of insn to be recorded. */
+ uint32_t insn; /* Insn to be recorded. */
+};
+
+/* Record handler for data processing instructions. */
+
+static int
+loongarch_record_data_proc_insn (loongarch_record_s *loongarch_record)
+{
+ int rd_num;
+ rd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, rd_num))
+ return -1;
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for read time instructions. */
+
+static int
+loongarch_record_read_time_insn (loongarch_record_s *loongarch_record)
+{
+ int rd_num, rj_num;
+ rd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
+ rj_num = loongarch_decode_imm ("5:5", loongarch_record->insn, 0);
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, rd_num))
+ return -1;
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, rj_num))
+ return -1;
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for branch instructions. */
+
+static int
+loongarch_record_branch_insn (loongarch_record_s *loongarch_record)
+{
+ if (is_jirl_insn (loongarch_record->insn))
+ {
+ int rd_num;
+ rd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, rd_num))
+ return -1;
+ }
+ else if (is_bl_insn (loongarch_record->insn))
+ {
+ if (record_full_arch_list_add_reg (loongarch_record->regcache,
+ LOONGARCH_RA_REGNUM))
+ return -1;
+ }
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for float data processing instructions. */
+
+static int
+loongarch_record_float_data_proc_insn (loongarch_record_s *loongarch_record)
+{
+ int fd_num;
+ fd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0)
+ + LOONGARCH_FIRST_FP_REGNUM;
+
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, fd_num))
+ return -1;
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for move gr to fcsr instructions. */
+
+static int
+loongarch_record_movgr2fcsr_insn (loongarch_record_s *loongarch_record)
+{
+ if (record_full_arch_list_add_reg (loongarch_record->regcache,
+ LOONGARCH_FCSR_REGNUM))
+ return -1;
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for move gr/fr to fcc instructions. */
+
+static int
+loongarch_record_mov2cf_insn (loongarch_record_s *loongarch_record)
+{
+ int cd;
+ cd = loongarch_decode_imm ("0:3", loongarch_record->insn, 0);
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, cd))
+ return -1;
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for float instructions. */
+
+static unsigned int
+loongarch_record_float_insn (loongarch_record_s *loongarch_record)
+{
+ if (is_movgr2fcsr_insn (loongarch_record->insn))
+ return loongarch_record_movgr2fcsr_insn (loongarch_record);
+ else if (is_mov2cf_insn (loongarch_record->insn))
+ return loongarch_record_mov2cf_insn (loongarch_record);
+ else
+ return loongarch_record_float_data_proc_insn (loongarch_record);
+}
+
+/* Record handler for store instructions. */
+
+static int
+loongarch_record_store_insn (loongarch_record_s *loongarch_record)
+{
+ enum store_types
+ {
+ STB, STH, STW, STD, STXB, STXH, STXW, STXD, STPTRW, STPTRD,
+ SCW, SCD, FSTS, FSTD, FSTXS, FSTXD, VST, XVST, NOT_STORE
+ };
+ int store_type, data_size, rj_num;
+ uint64_t address, rj_val;
+
+ store_type = is_st_b_insn (loongarch_record->insn) ? STB :
+ is_st_h_insn (loongarch_record->insn) ? STH :
+ is_st_w_insn (loongarch_record->insn) ? STW :
+ is_st_d_insn (loongarch_record->insn) ? STD :
+ is_stx_b_insn (loongarch_record->insn) ? STXB :
+ is_stx_h_insn (loongarch_record->insn) ? STXH :
+ is_stx_w_insn (loongarch_record->insn) ? STXW :
+ is_stx_d_insn (loongarch_record->insn) ? STXD :
+ is_stptr_w_insn (loongarch_record->insn) ? STPTRW :
+ is_stptr_d_insn (loongarch_record->insn) ? STPTRD :
+ is_sc_w_insn (loongarch_record->insn) ? SCW :
+ is_sc_d_insn (loongarch_record->insn) ? SCD :
+ is_fst_s_insn (loongarch_record->insn) ? FSTS :
+ is_fst_d_insn (loongarch_record->insn) ? FSTD :
+ is_fstx_s_insn (loongarch_record->insn) ? FSTXS :
+ is_fstx_d_insn (loongarch_record->insn) ? FSTXD :
+ is_vst_insn (loongarch_record->insn) ? VST :
+ is_xvst_insn (loongarch_record->insn) ? XVST :
+ NOT_STORE;
+ rj_num = loongarch_decode_imm ("5:5", loongarch_record->insn, 0);
+ regcache_raw_read_unsigned (loongarch_record->regcache, rj_num, &rj_val);
+
+ if (store_type == STB || store_type == STH || store_type == STW
+ || store_type == STD || store_type == FSTS || store_type == FSTD
+ || store_type == VST || store_type == XVST)
+ {
+ int imm;
+ imm = loongarch_decode_imm ("10:12", loongarch_record->insn, 1);
+ address = rj_val + imm;
+ switch (store_type)
+ {
+ case STB:
+ data_size = 1;
+ break;
+ case STH:
+ data_size = 2;
+ break;
+ case STW:
+ case FSTS:
+ data_size = 4;
+ break;
+ case STD:
+ case FSTD:
+ data_size = 8;
+ break;
+ case VST:
+ data_size = 16;
+ break;
+ case XVST:
+ data_size = 32;
+ break;
+ default:
+ data_size = 0;
+ break;
+ }
+
+ if (record_full_arch_list_add_mem (address, data_size))
+ return -1;
+ }
+ else if (store_type == STXB || store_type == STXH || store_type == STXW
+ || store_type == STXD || store_type == FSTXS || store_type == FSTXD)
+ {
+ int rk_num;
+ uint64_t rk_val;
+ rk_num = loongarch_decode_imm ("10:5", loongarch_record->insn, 0);
+ regcache_raw_read_unsigned (loongarch_record->regcache, rk_num, &rk_val);
+ address = rj_val + rk_val;
+ switch (store_type)
+ {
+ case STXB:
+ data_size = 1;
+ break;
+ case STXH:
+ data_size = 2;
+ break;
+ case STXW:
+ case FSTXS:
+ data_size = 4;
+ break;
+ case STXD:
+ case FSTXD:
+ data_size = 8;
+ break;
+ default:
+ data_size = 0;
+ break;
+ }
+
+ if (record_full_arch_list_add_mem (address, data_size))
+ return -1;
+ }
+ else if (store_type == STPTRW || store_type == STPTRD || store_type == SCW
+ || store_type == SCD)
+ {
+ int imm;
+ imm = loongarch_decode_imm ("10:14<<2", loongarch_record->insn, 1);
+ address = rj_val + imm;
+ switch (store_type)
+ {
+ case STPTRW:
+ case SCW:
+ data_size = 4;
+ break;
+ case STPTRD:
+ case SCD:
+ data_size = 8;
+ break;
+ default:
+ data_size = 0;
+ break;
+ }
+
+ if (record_full_arch_list_add_mem (address, data_size))
+ return -1;
+ }
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for atomic memory access instructions. */
+
+static int
+loongarch_record_atomic_access_insn (loongarch_record_s *loongarch_record)
+{
+ int rj_num, rd_num, rk_num, length;
+ int data_size;
+ uint64_t address;
+ rd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
+ rj_num = loongarch_decode_imm ("5:5", loongarch_record->insn, 0);
+ rk_num = loongarch_decode_imm ("10:5", loongarch_record->insn, 0);
+ regcache_raw_read_unsigned (loongarch_record->regcache, rj_num, &address);
+ if (is_basic_am_w_d_insn (loongarch_record->insn))
+ {
+ length = loongarch_decode_imm ("15:1", loongarch_record->insn, 0);
+ data_size = length == 1 ? 8 : 4;
+ if (record_full_arch_list_add_mem (address, data_size))
+ return -1;
+ }
+ if (is_am_b_h_insn (loongarch_record->insn))
+ {
+ length = loongarch_decode_imm ("15:1", loongarch_record->insn, 0);
+ data_size = length == 1 ? 2 : 1;
+ if (record_full_arch_list_add_mem (address, data_size))
+ return -1;
+ }
+ if (is_amcas_insn (loongarch_record->insn))
+ {
+ length = loongarch_decode_imm ("15:2", loongarch_record->insn, 0);
+ switch (length)
+ {
+ case 0x0:
+ data_size = 1;
+ break;
+ case 0x1:
+ data_size = 2;
+ break;
+ case 0x2:
+ data_size = 4;
+ break;
+ case 0x3:
+ data_size = 8;
+ break;
+ default:
+ data_size = 0;
+ break;
+ }
+ if (record_full_arch_list_add_mem (address, data_size))
+ return -1;
+ }
+
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, rd_num))
+ return -1;
+
+ if (is_amswap_insn (loongarch_record->insn))
+ {
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, rk_num))
+ return -1;
+ }
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for bound check load instructions. */
+
+static int
+loongarch_record_bound_check_load_insn (loongarch_record_s *loongarch_record)
+{
+ int rd_num, rj_num, rk_num, fd_num;
+ uint64_t rj_val, rk_val;
+ rd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
+ fd_num = loongarch_decode_imm ("0:5", loongarch_record->insn, 0);
+ rj_num = loongarch_decode_imm ("5:5", loongarch_record->insn, 0);
+ rk_num = loongarch_decode_imm ("10:5", loongarch_record->insn, 0);
+ regcache_raw_read_unsigned (loongarch_record->regcache, rj_num, &rj_val);
+ regcache_raw_read_unsigned (loongarch_record->regcache, rk_num, &rk_val);
+
+ if ((is_ldgt_insn (loongarch_record->insn) && (rj_val > rk_val))
+ || (is_ldle_insn (loongarch_record->insn) && (rj_val <= rk_val)))
+ {
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, rd_num))
+ return -1;
+ }
+ else if ((is_fldgt_insn (loongarch_record->insn) && (rj_val > rk_val))
+ || (is_fldle_insn (loongarch_record->insn) && (rj_val <= rk_val)))
+ {
+ if (record_full_arch_list_add_reg (loongarch_record->regcache, fd_num))
+ return -1;
+ }
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for bound check store instructions. */
+
+static int
+loongarch_record_bound_check_store_insn (loongarch_record_s *loongarch_record)
+{
+ int rj_num, rk_num;
+ int data_size;
+ uint64_t rj_val, rk_val;
+ uint32_t length_opcode;
+ rj_num = loongarch_decode_imm ("5:5", loongarch_record->insn, 0);
+ rk_num = loongarch_decode_imm ("10:5", loongarch_record->insn, 0);
+ regcache_raw_read_unsigned (loongarch_record->regcache, rj_num, &rj_val);
+ regcache_raw_read_unsigned (loongarch_record->regcache, rk_num, &rk_val);
+
+ if ((is_stgt_insn (loongarch_record->insn) && (rj_val > rk_val))
+ || (is_stle_insn (loongarch_record->insn) && (rj_val <= rk_val)))
+ {
+ length_opcode = loongarch_record->insn & 0x00018000;
+ switch (length_opcode)
+ {
+ case 0x00000000:
+ data_size = 1;
+ break;
+ case 0x00008000:
+ data_size = 2;
+ break;
+ case 0x00010000:
+ data_size = 4;
+ break;
+ case 0x00018000:
+ data_size = 8;
+ break;
+ default:
+ data_size = 0;
+ break;
+ }
+
+ if (record_full_arch_list_add_mem (rj_val, data_size))
+ return -1;
+ }
+ else if ((is_fstgt_insn (loongarch_record->insn) && (rj_val > rk_val))
+ || (is_fstle_insn (loongarch_record->insn) && (rj_val <= rk_val)))
+ {
+ length_opcode = loongarch_record->insn & 0x00008000;
+ switch (length_opcode)
+ {
+ case 0x00000000:
+ data_size = 4;
+ break;
+ case 0x00008000:
+ data_size = 8;
+ break;
+ default:
+ data_size = 0;
+ break;
+ }
+
+ if (record_full_arch_list_add_mem (rj_val, data_size))
+ return -1;
+ }
+
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Record handler for special instructions like privilege instructions,
+ barrier instructions and cache related instructions etc. */
+
+static int
+loongarch_record_special_insn (loongarch_record_s *loongarch_record)
+{
+ return LOONGARCH_RECORD_SUCCESS;
+}
+
+/* Decode insns type and invoke its record handler. */
+
+static int
+loongarch_record_decode_insn_handler (loongarch_record_s *loongarch_record)
+{
+ if (is_data_process_insn (loongarch_record->insn))
+ return loongarch_record_data_proc_insn (loongarch_record);
+ else if (is_branch_insn (loongarch_record->insn))
+ return loongarch_record_branch_insn (loongarch_record);
+ else if (is_store_insn (loongarch_record->insn))
+ return loongarch_record_store_insn (loongarch_record);
+ else if (is_read_time_insn (loongarch_record->insn))
+ return loongarch_record_read_time_insn (loongarch_record);
+ else if (is_float_insn (loongarch_record->insn))
+ return loongarch_record_float_insn (loongarch_record);
+ else if (is_special_insn (loongarch_record->insn))
+ return loongarch_record_special_insn (loongarch_record);
+ else if (is_atomic_access_insn (loongarch_record->insn))
+ return loongarch_record_atomic_access_insn (loongarch_record);
+ else if (is_bound_check_load_insn (loongarch_record->insn))
+ return loongarch_record_bound_check_load_insn (loongarch_record);
+ else if (is_bound_check_store_insn (loongarch_record->insn))
+ return loongarch_record_bound_check_store_insn (loongarch_record);
+
+ return LOONGARCH_RECORD_UNSUPPORTED;
+}
+
+/* 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
+loongarch_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+ CORE_ADDR insn_addr)
+{
+ int ret = 0;
+ loongarch_record_s loongarch_record;
+
+ /* reset the content of loongarch_record */
+ memset (&loongarch_record, 0, sizeof (loongarch_record_s));
+
+ /* write the loongarch_record */
+ loongarch_record.gdbarch = gdbarch;
+ loongarch_record.regcache = regcache;
+ loongarch_record.this_addr = insn_addr;
+
+ /* Get the current instruction */
+ loongarch_record.insn = (uint32_t) loongarch_fetch_instruction (insn_addr);
+ ret = loongarch_record_decode_insn_handler (&loongarch_record);
+ if (ret == LOONGARCH_RECORD_UNSUPPORTED)
+ {
+ gdb_printf (gdb_stderr,
+ _("Process record does not support instruction "
+ "0x%0x at address %s.\n"),
+ loongarch_record.insn,
+ paddress (gdbarch, insn_addr));
+ return -1;
+ }
+ if (ret == LOONGARCH_RECORD_SUCCESS)
+ {
+ /* Record PC registers. */
+ if (record_full_arch_list_add_reg (loongarch_record.regcache,
+ LOONGARCH_PC_REGNUM))
+ return -1;
+
+ if (record_full_arch_list_add_end ())
+ return -1;
+ }
+
+ return ret;
+}
+
void _initialize_loongarch_tdep ();
void
_initialize_loongarch_tdep ()
diff --git a/gdb/loongarch-tdep.h b/gdb/loongarch-tdep.h
index 5c81081..a148363 100644
--- a/gdb/loongarch-tdep.h
+++ b/gdb/loongarch-tdep.h
@@ -44,4 +44,7 @@ struct loongarch_gdbarch_tdep : gdbarch_tdep_base
CORE_ADDR (*syscall_next_pc) (const frame_info_ptr &frame) = nullptr;
};
+extern int loongarch_process_record (struct gdbarch *gdbarch,
+ struct regcache *regcache, CORE_ADDR addr);
+
#endif /* LOONGARCH_TDEP_H */
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 2b27d7f..127ab27 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -3700,6 +3700,7 @@ proc supports_process_record {} {
if { [istarget "arm*-*-linux*"] || [istarget "x86_64-*-linux*"]
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
+ || [istarget "loongarch*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
|| [istarget "s390*-*-linux*"] } {
return 1
@@ -3719,6 +3720,7 @@ proc supports_reverse {} {
if { [istarget "arm*-*-linux*"] || [istarget "x86_64-*-linux*"]
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
+ || [istarget "loongarch*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
|| [istarget "s390*-*-linux*"] } {
return 1