diff options
-rw-r--r-- | gdb/configure.tgt | 2 | ||||
-rw-r--r-- | gdb/loongarch-linux-tdep.c | 3 | ||||
-rw-r--r-- | gdb/loongarch-tdep.c | 495 | ||||
-rw-r--r-- | gdb/loongarch-tdep.h | 3 | ||||
-rw-r--r-- | gdb/testsuite/lib/gdb.exp | 2 |
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 |