* [PATCH] gdb/riscv: Add record support for rv64gc instructions
@ 2024-11-15 22:26 Timur
2024-11-18 19:46 ` Guinevere Larsen
0 siblings, 1 reply; 35+ messages in thread
From: Timur @ 2024-11-15 22:26 UTC (permalink / raw)
To: gdb-patches; +Cc: Timur
Timur Golubovich timurgol007@gmail.com
---
gdb/configure.tgt | 4 +-
gdb/riscv-canonicalize-syscall.c | 555 ++++++++++++++
gdb/riscv-linux-tdep.c | 261 +++++++
gdb/riscv-linux-tdep.h | 29 +
gdb/riscv-tdep.c | 703 +++++++++++++++++-
gdb/riscv-tdep.h | 15 +
.../riscv-canonicalize-syscall-gen.py | 126 ++++
gdb/testsuite/lib/gdb.exp | 3 +-
8 files changed, 1688 insertions(+), 8 deletions(-)
create mode 100644 gdb/riscv-canonicalize-syscall.c
create mode 100644 gdb/riscv-linux-tdep.h
create mode 100644 gdb/syscalls/riscv-canonicalize-syscall-gen.py
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 62df71b13fa..f965e03e84d 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -545,8 +545,8 @@ 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.o \
+ glibc-tdep.o linux-tdep.o solib-svr4.o symfile-mem.o linux-record.o"
;;
riscv*-*-*)
diff --git a/gdb/riscv-canonicalize-syscall.c b/gdb/riscv-canonicalize-syscall.c
new file mode 100644
index 00000000000..779bbd9335c
--- /dev/null
+++ b/gdb/riscv-canonicalize-syscall.c
@@ -0,0 +1,555 @@
+/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py */
+
+#include "defs.h"
+#include "riscv-linux-tdep.h"
+
+enum gdb_syscall
+riscv64_canonicalize_syscall (int syscall)
+{
+ switch (syscall)
+ {
+ case 0: // #define __NR_io_setup 0
+ return gdb_sys_io_setup;
+ case 1: // #define __NR_io_destroy 1
+ return gdb_sys_io_destroy;
+ case 2: // #define __NR_io_submit 2
+ return gdb_sys_io_submit;
+ case 3: // #define __NR_io_cancel 3
+ return gdb_sys_io_cancel;
+ case 4: // #define __NR_io_getevents 4
+ return gdb_sys_io_getevents;
+ case 5: // #define __NR_setxattr 5
+ return gdb_sys_setxattr;
+ case 6: // #define __NR_lsetxattr 6
+ return gdb_sys_lsetxattr;
+ case 7: // #define __NR_fsetxattr 7
+ return gdb_sys_fsetxattr;
+ case 8: // #define __NR_getxattr 8
+ return gdb_sys_getxattr;
+ case 9: // #define __NR_lgetxattr 9
+ return gdb_sys_lgetxattr;
+ case 10: // #define __NR_fgetxattr 10
+ return gdb_sys_fgetxattr;
+ case 11: // #define __NR_listxattr 11
+ return gdb_sys_listxattr;
+ case 12: // #define __NR_llistxattr 12
+ return gdb_sys_llistxattr;
+ case 13: // #define __NR_flistxattr 13
+ return gdb_sys_flistxattr;
+ case 14: // #define __NR_removexattr 14
+ return gdb_sys_removexattr;
+ case 15: // #define __NR_lremovexattr 15
+ return gdb_sys_lremovexattr;
+ case 16: // #define __NR_fremovexattr 16
+ return gdb_sys_fremovexattr;
+ case 17: // #define __NR_getcwd 17
+ return gdb_sys_getcwd;
+ case 18: // #define __NR_lookup_dcookie 18
+ return gdb_sys_lookup_dcookie;
+ case 19: // #define __NR_eventfd2 19
+ return gdb_sys_eventfd2;
+ case 20: // #define __NR_epoll_create1 20
+ return gdb_sys_epoll_create1;
+ case 21: // #define __NR_epoll_ctl 21
+ return gdb_sys_epoll_ctl;
+ case 22: // #define __NR_epoll_pwait 22
+ return gdb_sys_epoll_pwait;
+ case 23: // #define __NR_dup 23
+ return gdb_sys_dup;
+ case 24: // #define __NR_dup3 24
+ return gdb_sys_dup3;
+ case 25: // #define __NR_fcntl 25
+ return gdb_sys_fcntl;
+ case 26: // #define __NR_inotify_init1 26
+ return gdb_sys_inotify_init1;
+ case 27: // #define __NR_inotify_add_watch 27
+ return gdb_sys_inotify_add_watch;
+ case 28: // #define __NR_inotify_rm_watch 28
+ return gdb_sys_inotify_rm_watch;
+ case 29: // #define __NR_ioctl 29
+ return gdb_sys_ioctl;
+ case 30: // #define __NR_ioprio_set 30
+ return gdb_sys_ioprio_set;
+ case 31: // #define __NR_ioprio_get 31
+ return gdb_sys_ioprio_get;
+ case 32: // #define __NR_flock 32
+ return gdb_sys_flock;
+ case 33: // #define __NR_mknodat 33
+ return gdb_sys_mknodat;
+ case 34: // #define __NR_mkdirat 34
+ return gdb_sys_mkdirat;
+ case 35: // #define __NR_unlinkat 35
+ return gdb_sys_unlinkat;
+ case 36: // #define __NR_symlinkat 36
+ return gdb_sys_symlinkat;
+ case 37: // #define __NR_linkat 37
+ return gdb_sys_linkat;
+ // case 39: return gdb_sys_umount2;
+ case 40: // #define __NR_mount 40
+ return gdb_sys_mount;
+ case 41: // #define __NR_pivot_root 41
+ return gdb_sys_pivot_root;
+ case 42: // #define __NR_nfsservctl 42
+ return gdb_sys_nfsservctl;
+ case 43: // #define __NR_statfs 43
+ return gdb_sys_statfs;
+ case 44: // #define __NR_fstatfs 44
+ return gdb_sys_fstatfs;
+ case 45: // #define __NR_truncate 45
+ return gdb_sys_truncate;
+ case 46: // #define __NR_ftruncate 46
+ return gdb_sys_ftruncate;
+ case 47: // #define __NR_fallocate 47
+ return gdb_sys_fallocate;
+ case 48: // #define __NR_faccessat 48
+ return gdb_sys_faccessat;
+ case 49: // #define __NR_chdir 49
+ return gdb_sys_chdir;
+ case 50: // #define __NR_fchdir 50
+ return gdb_sys_fchdir;
+ case 51: // #define __NR_chroot 51
+ return gdb_sys_chroot;
+ case 52: // #define __NR_fchmod 52
+ return gdb_sys_fchmod;
+ case 53: // #define __NR_fchmodat 53
+ return gdb_sys_fchmodat;
+ case 54: // #define __NR_fchownat 54
+ return gdb_sys_fchownat;
+ case 55: // #define __NR_fchown 55
+ return gdb_sys_fchown;
+ case 56: // #define __NR_openat 56
+ return gdb_sys_openat;
+ case 57: // #define __NR_close 57
+ return gdb_sys_close;
+ case 58: // #define __NR_vhangup 58
+ return gdb_sys_vhangup;
+ case 59: // #define __NR_pipe2 59
+ return gdb_sys_pipe2;
+ case 60: // #define __NR_quotactl 60
+ return gdb_sys_quotactl;
+ case 61: // #define __NR_getdents64 61
+ return gdb_sys_getdents64;
+ case 62: // #define __NR_lseek 62
+ return gdb_sys_lseek;
+ case 63: // #define __NR_read 63
+ return gdb_sys_read;
+ case 64: // #define __NR_write 64
+ return gdb_sys_write;
+ case 65: // #define __NR_readv 65
+ return gdb_sys_readv;
+ case 66: // #define __NR_writev 66
+ return gdb_sys_writev;
+ case 67: // #define __NR_pread64 67
+ return gdb_sys_pread64;
+ case 68: // #define __NR_pwrite64 68
+ return gdb_sys_pwrite64;
+ // case 69: return gdb_sys_preadv;
+ // case 70: return gdb_sys_pwritev;
+ case 71: // #define __NR_sendfile 71
+ return gdb_sys_sendfile;
+ case 72: // #define __NR_pselect6 72
+ return gdb_sys_pselect6;
+ case 73: // #define __NR_ppoll 73
+ return gdb_sys_ppoll;
+ // case 74: return gdb_sys_signalfd4;
+ case 75: // #define __NR_vmsplice 75
+ return gdb_sys_vmsplice;
+ case 76: // #define __NR_splice 76
+ return gdb_sys_splice;
+ case 77: // #define __NR_tee 77
+ return gdb_sys_tee;
+ case 78: // #define __NR_readlinkat 78
+ return gdb_sys_readlinkat;
+ case 79: // #define __NR_newfstatat 79
+ return gdb_sys_newfstatat;
+ case 80: // #define __NR_fstat 80
+ return gdb_sys_fstat;
+ case 81: // #define __NR_sync 81
+ return gdb_sys_sync;
+ case 82: // #define __NR_fsync 82
+ return gdb_sys_fsync;
+ case 83: // #define __NR_fdatasync 83
+ return gdb_sys_fdatasync;
+ case 84: // #define __NR_sync_file_range 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: // #define __NR_acct 89
+ return gdb_sys_acct;
+ case 90: // #define __NR_capget 90
+ return gdb_sys_capget;
+ case 91: // #define __NR_capset 91
+ return gdb_sys_capset;
+ case 92: // #define __NR_personality 92
+ return gdb_sys_personality;
+ case 93: // #define __NR_exit 93
+ return gdb_sys_exit;
+ case 94: // #define __NR_exit_group 94
+ return gdb_sys_exit_group;
+ case 95: // #define __NR_waitid 95
+ return gdb_sys_waitid;
+ case 96: // #define __NR_set_tid_address 96
+ return gdb_sys_set_tid_address;
+ case 97: // #define __NR_unshare 97
+ return gdb_sys_unshare;
+ case 98: // #define __NR_futex 98
+ return gdb_sys_futex;
+ case 99: // #define __NR_set_robust_list 99
+ return gdb_sys_set_robust_list;
+ case 100: // #define __NR_get_robust_list 100
+ return gdb_sys_get_robust_list;
+ case 101: // #define __NR_nanosleep 101
+ return gdb_sys_nanosleep;
+ case 102: // #define __NR_getitimer 102
+ return gdb_sys_getitimer;
+ case 103: // #define __NR_setitimer 103
+ return gdb_sys_setitimer;
+ case 104: // #define __NR_kexec_load 104
+ return gdb_sys_kexec_load;
+ case 105: // #define __NR_init_module 105
+ return gdb_sys_init_module;
+ case 106: // #define __NR_delete_module 106
+ return gdb_sys_delete_module;
+ case 107: // #define __NR_timer_create 107
+ return gdb_sys_timer_create;
+ case 108: // #define __NR_timer_gettime 108
+ return gdb_sys_timer_gettime;
+ case 109: // #define __NR_timer_getoverrun 109
+ return gdb_sys_timer_getoverrun;
+ case 110: // #define __NR_timer_settime 110
+ return gdb_sys_timer_settime;
+ case 111: // #define __NR_timer_delete 111
+ return gdb_sys_timer_delete;
+ case 112: // #define __NR_clock_settime 112
+ return gdb_sys_clock_settime;
+ case 113: // #define __NR_clock_gettime 113
+ return gdb_sys_clock_gettime;
+ case 114: // #define __NR_clock_getres 114
+ return gdb_sys_clock_getres;
+ case 115: // #define __NR_clock_nanosleep 115
+ return gdb_sys_clock_nanosleep;
+ case 116: // #define __NR_syslog 116
+ return gdb_sys_syslog;
+ case 117: // #define __NR_ptrace 117
+ return gdb_sys_ptrace;
+ case 118: // #define __NR_sched_setparam 118
+ return gdb_sys_sched_setparam;
+ case 119: // #define __NR_sched_setscheduler 119
+ return gdb_sys_sched_setscheduler;
+ case 120: // #define __NR_sched_getscheduler 120
+ return gdb_sys_sched_getscheduler;
+ case 121: // #define __NR_sched_getparam 121
+ return gdb_sys_sched_getparam;
+ case 122: // #define __NR_sched_setaffinity 122
+ return gdb_sys_sched_setaffinity;
+ case 123: // #define __NR_sched_getaffinity 123
+ return gdb_sys_sched_getaffinity;
+ case 124: // #define __NR_sched_yield 124
+ return gdb_sys_sched_yield;
+ case 125: // #define __NR_sched_get_priority_max 125
+ return gdb_sys_sched_get_priority_max;
+ case 126: // #define __NR_sched_get_priority_min 126
+ return gdb_sys_sched_get_priority_min;
+ case 127: // #define __NR_sched_rr_get_interval 127
+ return gdb_sys_sched_rr_get_interval;
+ case 128: // #define __NR_restart_syscall 128
+ return gdb_sys_restart_syscall;
+ case 129: // #define __NR_kill 129
+ return gdb_sys_kill;
+ case 130: // #define __NR_tkill 130
+ return gdb_sys_tkill;
+ case 131: // #define __NR_tgkill 131
+ return gdb_sys_tgkill;
+ case 132: // #define __NR_sigaltstack 132
+ return gdb_sys_sigaltstack;
+ case 133: // #define __NR_rt_sigsuspend 133
+ return gdb_sys_rt_sigsuspend;
+ case 134: // #define __NR_rt_sigaction 134
+ return gdb_sys_rt_sigaction;
+ case 135: // #define __NR_rt_sigprocmask 135
+ return gdb_sys_rt_sigprocmask;
+ case 136: // #define __NR_rt_sigpending 136
+ return gdb_sys_rt_sigpending;
+ case 137: // #define __NR_rt_sigtimedwait 137
+ return gdb_sys_rt_sigtimedwait;
+ case 138: // #define __NR_rt_sigqueueinfo 138
+ return gdb_sys_rt_sigqueueinfo;
+ case 139: // #define __NR_rt_sigreturn 139
+ return gdb_sys_rt_sigreturn;
+ case 140: // #define __NR_setpriority 140
+ return gdb_sys_setpriority;
+ case 141: // #define __NR_getpriority 141
+ return gdb_sys_getpriority;
+ case 142: // #define __NR_reboot 142
+ return gdb_sys_reboot;
+ case 143: // #define __NR_setregid 143
+ return gdb_sys_setregid;
+ case 144: // #define __NR_setgid 144
+ return gdb_sys_setgid;
+ case 145: // #define __NR_setreuid 145
+ return gdb_sys_setreuid;
+ case 146: // #define __NR_setuid 146
+ return gdb_sys_setuid;
+ case 147: // #define __NR_setresuid 147
+ return gdb_sys_setresuid;
+ case 148: // #define __NR_getresuid 148
+ return gdb_sys_getresuid;
+ case 149: // #define __NR_setresgid 149
+ return gdb_sys_setresgid;
+ case 150: // #define __NR_getresgid 150
+ return gdb_sys_getresgid;
+ case 151: // #define __NR_setfsuid 151
+ return gdb_sys_setfsuid;
+ case 152: // #define __NR_setfsgid 152
+ return gdb_sys_setfsgid;
+ case 153: // #define __NR_times 153
+ return gdb_sys_times;
+ case 154: // #define __NR_setpgid 154
+ return gdb_sys_setpgid;
+ case 155: // #define __NR_getpgid 155
+ return gdb_sys_getpgid;
+ case 156: // #define __NR_getsid 156
+ return gdb_sys_getsid;
+ case 157: // #define __NR_setsid 157
+ return gdb_sys_setsid;
+ case 158: // #define __NR_getgroups 158
+ return gdb_sys_getgroups;
+ case 159: // #define __NR_setgroups 159
+ return gdb_sys_setgroups;
+ case 160: // #define __NR_uname 160
+ return gdb_sys_uname;
+ case 161: // #define __NR_sethostname 161
+ return gdb_sys_sethostname;
+ case 162: // #define __NR_setdomainname 162
+ return gdb_sys_setdomainname;
+ case 163: // #define __NR_getrlimit 163
+ return gdb_sys_getrlimit;
+ case 164: // #define __NR_setrlimit 164
+ return gdb_sys_setrlimit;
+ case 165: // #define __NR_getrusage 165
+ return gdb_sys_getrusage;
+ case 166: // #define __NR_umask 166
+ return gdb_sys_umask;
+ case 167: // #define __NR_prctl 167
+ return gdb_sys_prctl;
+ case 168: // #define __NR_getcpu 168
+ return gdb_sys_getcpu;
+ case 169: // #define __NR_gettimeofday 169
+ return gdb_sys_gettimeofday;
+ case 170: // #define __NR_settimeofday 170
+ return gdb_sys_settimeofday;
+ case 171: // #define __NR_adjtimex 171
+ return gdb_sys_adjtimex;
+ case 172: // #define __NR_getpid 172
+ return gdb_sys_getpid;
+ case 173: // #define __NR_getppid 173
+ return gdb_sys_getppid;
+ case 174: // #define __NR_getuid 174
+ return gdb_sys_getuid;
+ case 175: // #define __NR_geteuid 175
+ return gdb_sys_geteuid;
+ case 176: // #define __NR_getgid 176
+ return gdb_sys_getgid;
+ case 177: // #define __NR_getegid 177
+ return gdb_sys_getegid;
+ case 178: // #define __NR_gettid 178
+ return gdb_sys_gettid;
+ case 179: // #define __NR_sysinfo 179
+ return gdb_sys_sysinfo;
+ case 180: // #define __NR_mq_open 180
+ return gdb_sys_mq_open;
+ case 181: // #define __NR_mq_unlink 181
+ return gdb_sys_mq_unlink;
+ case 182: // #define __NR_mq_timedsend 182
+ return gdb_sys_mq_timedsend;
+ case 183: // #define __NR_mq_timedreceive 183
+ return gdb_sys_mq_timedreceive;
+ case 184: // #define __NR_mq_notify 184
+ return gdb_sys_mq_notify;
+ case 185: // #define __NR_mq_getsetattr 185
+ return gdb_sys_mq_getsetattr;
+ case 186: // #define __NR_msgget 186
+ return gdb_sys_msgget;
+ case 187: // #define __NR_msgctl 187
+ return gdb_sys_msgctl;
+ case 188: // #define __NR_msgrcv 188
+ return gdb_sys_msgrcv;
+ case 189: // #define __NR_msgsnd 189
+ return gdb_sys_msgsnd;
+ case 190: // #define __NR_semget 190
+ return gdb_sys_semget;
+ case 191: // #define __NR_semctl 191
+ return gdb_sys_semctl;
+ case 192: // #define __NR_semtimedop 192
+ return gdb_sys_semtimedop;
+ case 193: // #define __NR_semop 193
+ return gdb_sys_semop;
+ case 194: // #define __NR_shmget 194
+ return gdb_sys_shmget;
+ case 195: // #define __NR_shmctl 195
+ return gdb_sys_shmctl;
+ case 196: // #define __NR_shmat 196
+ return gdb_sys_shmat;
+ case 197: // #define __NR_shmdt 197
+ return gdb_sys_shmdt;
+ case 198: // #define __NR_socket 198
+ return gdb_sys_socket;
+ case 199: // #define __NR_socketpair 199
+ return gdb_sys_socketpair;
+ case 200: // #define __NR_bind 200
+ return gdb_sys_bind;
+ case 201: // #define __NR_listen 201
+ return gdb_sys_listen;
+ case 202: // #define __NR_accept 202
+ return gdb_sys_accept;
+ case 203: // #define __NR_connect 203
+ return gdb_sys_connect;
+ case 204: // #define __NR_getsockname 204
+ return gdb_sys_getsockname;
+ case 205: // #define __NR_getpeername 205
+ return gdb_sys_getpeername;
+ case 206: // #define __NR_sendto 206
+ return gdb_sys_sendto;
+ case 207: // #define __NR_recvfrom 207
+ return gdb_sys_recvfrom;
+ case 208: // #define __NR_setsockopt 208
+ return gdb_sys_setsockopt;
+ case 209: // #define __NR_getsockopt 209
+ return gdb_sys_getsockopt;
+ case 210: // #define __NR_shutdown 210
+ return gdb_sys_shutdown;
+ case 211: // #define __NR_sendmsg 211
+ return gdb_sys_sendmsg;
+ case 212: // #define __NR_recvmsg 212
+ return gdb_sys_recvmsg;
+ case 213: // #define __NR_readahead 213
+ return gdb_sys_readahead;
+ case 214: // #define __NR_brk 214
+ return gdb_sys_brk;
+ case 215: // #define __NR_munmap 215
+ return gdb_sys_munmap;
+ case 216: // #define __NR_mremap 216
+ return gdb_sys_mremap;
+ case 217: // #define __NR_add_key 217
+ return gdb_sys_add_key;
+ case 218: // #define __NR_request_key 218
+ return gdb_sys_request_key;
+ case 219: // #define __NR_keyctl 219
+ return gdb_sys_keyctl;
+ case 220: // #define __NR_clone 220
+ return gdb_sys_clone;
+ case 221: // #define __NR_execve 221
+ return gdb_sys_execve;
+ case 222:
+ return gdb_old_mmap;
+ case 223: // #define __NR_fadvise64 223
+ return gdb_sys_fadvise64;
+ case 224: // #define __NR_swapon 224
+ return gdb_sys_swapon;
+ case 225: // #define __NR_swapoff 225
+ return gdb_sys_swapoff;
+ case 226: // #define __NR_mprotect 226
+ return gdb_sys_mprotect;
+ case 227: // #define __NR_msync 227
+ return gdb_sys_msync;
+ case 228: // #define __NR_mlock 228
+ return gdb_sys_mlock;
+ case 229: // #define __NR_munlock 229
+ return gdb_sys_munlock;
+ case 230: // #define __NR_mlockall 230
+ return gdb_sys_mlockall;
+ case 231: // #define __NR_munlockall 231
+ return gdb_sys_munlockall;
+ case 232: // #define __NR_mincore 232
+ return gdb_sys_mincore;
+ case 233: // #define __NR_madvise 233
+ return gdb_sys_madvise;
+ case 234: // #define __NR_remap_file_pages 234
+ return gdb_sys_remap_file_pages;
+ case 235: // #define __NR_mbind 235
+ return gdb_sys_mbind;
+ case 236: // #define __NR_get_mempolicy 236
+ return gdb_sys_get_mempolicy;
+ case 237: // #define __NR_set_mempolicy 237
+ return gdb_sys_set_mempolicy;
+ case 238: // #define __NR_migrate_pages 238
+ return gdb_sys_migrate_pages;
+ case 239: // #define __NR_move_pages 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: // #define __NR_wait4 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: // #define __NR_getrandom 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: // #define __NR_statx 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 ff478cf4c28..5a56e9911f8 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,259 @@ 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;
+
+/* Record all registers but PC register for process-record. */
+
+using regnum_type = int;
+
+static bool
+save_registers (struct regcache *regcache, regnum_type fir, regnum_type last)
+{
+ for (regnum_type i = fir; i != last; ++i)
+ if (record_full_arch_list_add_reg (regcache, i))
+ return false;
+ return true;
+};
+
+static bool
+riscv_all_but_pc_registers_record (struct regcache *regcache)
+{
+ auto &&gdbarch = regcache->arch ();
+ auto &&tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto &&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;
+
+ // TODO: add saving vector registers
+
+ return true;
+}
+
+/* Handler for riscv system call instruction recording. */
+
+static int
+riscv_linux_syscall_record (struct regcache *regcache,
+ unsigned long svc_number)
+{
+ auto 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)
+ {
+ gdb_printf (gdb_stderr,
+ _ ("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;
+ }
+
+ auto 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. */
+/* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+static void
+riscv64_linux_record_tdep_init (
+ struct gdbarch *gdbarch, struct linux_record_tdep &riscv_linux_record_tdep)
+{
+ riscv_linux_record_tdep.size_pointer
+ = gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT;
+ riscv_linux_record_tdep.size__old_kernel_stat
+ = 48; // arch/powerpc/include/uapi/asm/stat.h: 2+2+2+2+2+2+2+2+8+8+8+8
+ 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; // include/uapi/linux/utsname.h:7:9+9+9+9+9
+ riscv_linux_record_tdep.size_ustat = 32;
+ riscv_linux_record_tdep.size_old_sigaction
+ = 32; // include/linux/signal_types.h:60:8+8+8+8
+ riscv_linux_record_tdep.size_old_sigset_t
+ = 8; // arch/powerpc/include/uapi/asm/signal.h:15
+ riscv_linux_record_tdep.size_rlimit = 16;
+ riscv_linux_record_tdep.size_rusage = 144;
+ riscv_linux_record_tdep.size_timeval = 8; // include/uapi/linux/time.h:17:8+8
+ riscv_linux_record_tdep.size_timezone = 8;
+ riscv_linux_record_tdep.size_old_gid_t
+ = 2; // arch/arm64/include/uapi/asm/posix_types.h:6
+ riscv_linux_record_tdep.size_old_uid_t
+ = 2; // arch/arm64/include/uapi/asm/posix_types.h:5
+ 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; // include/uapi/linux/time.h:27:8+8
+ riscv_linux_record_tdep.size_stat = 128;
+ riscv_linux_record_tdep.size_old_utsname
+ = 325; // include/uapi/linux/utsname.h:17:65+65+65+65+65
+ 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; // include/uapi/linux/timex.h:65:4+8+8+8+8+4+8+8+8+8+8+8+8+4+8+8+8+8+8+4+11*4
+ riscv_linux_record_tdep.size_mem_dqinfo = 72;
+ riscv_linux_record_tdep.size_if_dqblk
+ = 68; // include/uapi/linux/quota.h:111:8*8+4
+ riscv_linux_record_tdep.size_fs_quota_stat
+ = 64; // include/uapi/linux/dqblk_xfs.h:165:1+2+1+20+20+4+4+4+4+2+2
+ riscv_linux_record_tdep.size_timespec
+ = 16; // include/uapi/linux/time.h:11:8+8
+ riscv_linux_record_tdep.size_pollfd = 8;
+ riscv_linux_record_tdep.size_NFS_FHSIZE = 32; // include/uapi/linux/nfs.h:20
+ riscv_linux_record_tdep.size_knfsd_fh = 36; // fs/nfsd/nfsfh.h:47:4+32
+ 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; // include/uapi/asm-generic/stat.h:49:8+8+4+4+4+4+8+8+8+4+4+8+4+4+4+4+4+4+4+4
+ 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; // arch/x86/include/uapi/asm/ldt.h:21:4*9+1
+ 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; // tools/include/nolibc/std.h:34
+
+ 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;
+
+ 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;
+ riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
+}
+
/* Initialize RISC-V Linux ABI info. */
static void
@@ -205,6 +463,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 00000000000..5ee2c94387f
--- /dev/null
+++ b/gdb/riscv-linux-tdep.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2024 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/>. */
+
+#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 932708ca4e9..29013bc46c1 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
@@ -1655,6 +1658,11 @@ class riscv_insn
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. */
@@ -1800,11 +1808,6 @@ class riscv_insn
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;
@@ -4415,6 +4418,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);
@@ -4833,3 +4839,690 @@ this option can be used."),
&setriscvcmdlist,
&showriscvcmdlist);
}
+
+static bool
+try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
+{
+ gdb_assert (regcache);
+
+ if (regcache->raw_read<ULONGEST> (regnum, &addr)
+ != register_status::REG_VALID)
+ {
+ warning (_ ("Can not read at address %lx"), addr);
+ return false;
+ }
+ return true;
+}
+
+class riscv_recorded_insn final
+{
+public:
+ using regnum_type = int;
+ using memory_type = std::pair<CORE_ADDR, int>;
+
+ enum class record_type
+ {
+ UNKNOWN,
+ ORDINARY,
+
+ // corner cases
+ ECALL,
+ EBREAK,
+ };
+
+private:
+ using recorded_regs = std::vector<regnum_type>;
+ using recorded_mems = std::vector<memory_type>;
+
+ using mem_addr = decltype (std::declval<memory_type> ().first);
+ using mem_len = decltype (std::declval<memory_type> ().second);
+
+ record_type m_record_type = record_type::UNKNOWN;
+
+ bool m_error_occured = false;
+
+ recorded_regs m_regs;
+ recorded_mems m_mems;
+
+ /* 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;
+ }
+
+ bool
+ set_ordinary_record_type () noexcept
+ {
+ m_record_type = record_type::ORDINARY;
+ return true;
+ }
+
+ bool
+ set_error () noexcept
+ {
+ m_error_occured = true;
+ return false;
+ }
+
+ bool
+ has_error () const noexcept
+ {
+ return m_error_occured;
+ }
+
+ bool
+ read_reg (struct regcache *regcache, regnum_type reg,
+ ULONGEST &addr) noexcept
+ {
+ gdb_assert (regcache);
+
+ if (!try_read (regcache, reg, addr))
+ {
+ return set_error ();
+ }
+ return true;
+ }
+
+ bool
+ save_reg (regnum_type regnum) noexcept
+ {
+ try
+ {
+ m_regs.emplace_back (regnum);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ bool
+ save_mem (mem_addr addr, mem_len len) noexcept
+ {
+ try
+ {
+ m_mems.emplace_back (addr, len);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ 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 ();
+ }
+
+ 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_ld_insn (ival) || is_lbu_insn (ival)
+ || is_lhu_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
+ || is_lw_insn (ival) || is_ld_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_insn (ival) || is_srli_insn (ival)
+ || is_srai_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_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_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_d_w_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. Needs error checking after */
+ 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 ();
+ }
+
+ static bool
+ need_save_pc_fprd (ULONGEST ival) noexcept
+ {
+ return 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_flw_insn (ival) || is_fld_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_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_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. Needs error checking after */
+ 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 ();
+ }
+
+ 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. Needs error checking after */
+ 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 ();
+ }
+
+ /* If return value is > 0, than instruction needs saving memory len */
+ 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. Needs error checking after */
+ bool
+ try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache);
+
+ mem_addr addr = 0;
+ auto 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 ();
+ }
+
+ /* If return value is > 0, than instruction needs saving memory len */
+ static mem_len
+ need_save_pc_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_sc_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_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. Needs error checking after */
+ bool
+ try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache);
+
+ auto 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 ();
+ }
+
+ /* If return value is > 0, than instruction needs saving memory len */
+ static mem_len
+ need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_amoswap_w_insn (ival))
+ return 4;
+ if (is_amoswap_d_insn (ival))
+ return 8;
+ return 0;
+ }
+
+ /* Returns true if instruction is classified. Needs error checking after */
+ bool
+ try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache);
+
+ mem_addr addr = 0;
+ auto len = need_save_pc_rs2_rd_mem (ival);
+ if (len <= 0)
+ return false;
+
+ return !read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
+ || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
+ }
+
+ /* Returns true if instruction is successfully recorder.
+ Else returns false */
+ bool
+ record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache);
+
+ 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)
+ || try_save_pc_rs2_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 recorder.
+ Else returns false */
+ bool
+ record_insn_len2 (ULONGEST ival, struct regcache *regcache,
+ struct gdbarch *gdbarch) noexcept
+ {
+ gdb_assert (regcache);
+ gdb_assert (gdbarch);
+
+ mem_addr addr = 0;
+ auto xlen = riscv_isa_xlen (gdbarch);
+
+ /* 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)
+ || (xlen == 8 && is_c_ld_insn (ival)))
+ {
+ return !save_reg (decode_crs2_short (ival))
+ || set_ordinary_record_type ();
+ }
+
+ if (is_c_fld_insn (ival) || (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) || (xlen == 8 && is_c_sd_insn (ival)))
+ {
+ int offset = EXTRACT_CLTYPE_LD_IMM (ival);
+ return !read_reg (regcache, decode_crs1_short (ival), addr)
+ || !save_mem (addr + offset, 8) || set_ordinary_record_type ();
+ }
+
+ if ((xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
+ {
+ int offset = 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 (xlen == 4 && is_c_jal_insn (ival))
+ {
+ return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
+ }
+
+ if ((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) || (xlen == 8 && is_c_subw_insn (ival))
+ || (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) || (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) || (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) || (xlen == 8 && is_c_sdsp_insn (ival)))
+ {
+ int offset = 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) || (xlen == 4 && is_c_fswsp_insn (ival)))
+ {
+ int offset = 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:
+ using regs_iter = recorded_regs::const_iterator;
+ using mems_iter = recorded_mems::const_iterator;
+
+ bool
+ record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
+ {
+ int m_length = 0;
+ auto 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, gdbarch);
+
+ /* 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;
+ }
+
+ record_type
+ get_record_type () const noexcept
+ {
+ return m_record_type;
+ }
+
+ regs_iter
+ regs_begin () const noexcept
+ {
+ return m_regs.begin ();
+ }
+
+ regs_iter
+ regs_end () const noexcept
+ {
+ return m_regs.end ();
+ }
+
+ mems_iter
+ mems_begin () const noexcept
+ {
+ return m_mems.begin ();
+ }
+
+ mems_iter
+ mems_end () const noexcept
+ {
+ return m_mems.end ();
+ }
+};
+
+static int
+riscv_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
+ const riscv_recorded_insn &insn)
+{
+ gdb_assert (gdbarch && regcache);
+
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto regs_begin = insn.regs_begin ();
+ auto regs_end = insn.regs_end ();
+ if (std::any_of (regs_begin, regs_end, [®cache] (auto &®_it) {
+ return record_full_arch_list_add_reg (regcache, reg_it);
+ }))
+ return -1;
+
+ auto mems_begin = insn.mems_begin ();
+ auto mems_end = insn.mems_end ();
+ if (std::any_of (mems_begin, mems_end, [] (auto &&mem_it) {
+ return record_full_arch_list_add_mem (mem_it.first, mem_it.second);
+ }))
+ return -1;
+
+ switch (insn.get_record_type ())
+ {
+ case riscv_recorded_insn::record_type::ORDINARY:
+ break;
+
+ case riscv_recorded_insn::record_type::ECALL:
+ {
+ if (!tdep->riscv_syscall_record)
+ {
+ warning (_ ("Syscall record is not supported"));
+ return -1;
+ }
+ ULONGEST reg_val = 0;
+ 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;
+}
+
+int
+riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+ CORE_ADDR addr)
+{
+ gdb_assert (gdbarch && regcache);
+
+ riscv_recorded_insn insn;
+ if (!insn.record (gdbarch, regcache, addr))
+ {
+ record_full_arch_list_add_end ();
+ return -1;
+ }
+
+ auto ret_val = riscv_make_record_process (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 4bdc2e7a3d5..505371ba4f6 100644
--- a/gdb/riscv-tdep.h
+++ b/gdb/riscv-tdep.h
@@ -35,6 +35,10 @@ enum
RISCV_FP_REGNUM = 8, /* Frame Pointer. */
RISCV_A0_REGNUM = 10, /* First argument. */
RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
RISCV_PC_REGNUM = 32, /* Program Counter. */
@@ -113,6 +117,11 @@ 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 +186,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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
new file mode 100644
index 00000000000..235f858f5ff
--- /dev/null
+++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python3
+# pylint: disable=invalid-name
+
+# Copyright (C) 2024 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/>.
+
+import argparse
+import re
+import sys
+from pathlib import Path as _Path
+
+head = """\
+/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py */
+
+#include "defs.h"
+#include "riscv-linux-tdep.h"
+
+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:
+ canon_syscalls[syscall_num] = (
+ f" case {syscall_num}: // {line} return {gdb_syscall_name};\n"
+ )
+ # this is a place for corner cases
+ elif syscall_name == "mmap":
+ gdb_old_syscall_name = "gdb_old_mmap"
+ canon_syscalls[syscall_num] = f" case {syscall_num}:\n return {gdb_old_syscall_name};\n"
+ else:
+ canon_syscalls[syscall_num] = f" // case {syscall_num}: return {gdb_syscall_name};\n"
+ 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.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.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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 410f99e3350..b048ac7078b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
- || [istarget "s390*-*-linux*"] } {
+ || [istarget "s390*-*-linux*"]
+ || [istarget "riscv*-*-*"] } {
return 1
}
--
2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* Re: [PATCH] gdb/riscv: Add record support for rv64gc instructions
2024-11-15 22:26 [PATCH] gdb/riscv: Add record support for rv64gc instructions Timur
@ 2024-11-18 19:46 ` Guinevere Larsen
2024-11-26 14:17 ` [PATCH v2] " Timur
0 siblings, 1 reply; 35+ messages in thread
From: Guinevere Larsen @ 2024-11-18 19:46 UTC (permalink / raw)
To: Timur, gdb-patches
On 11/15/24 7:26 PM, Timur wrote:
> Timur Golubovich timurgol007@gmail.com
Hi!
In GDB we like to have descriptive commit messages, even if the changes
feel obvious to you. That way it is easier for us to review the changes,
as we understand the thinking behind your choices, and we just need to
confirm that the code does what the commit message says.
An example of something that would be good to explain would be, why did
you decide to make a python script and keep the riscv canonicalization
of syscalls as a generated script? These files tend to have few updates
once generated, and it is usually easier to do the updates by hand than
getting the list of syscalls and re-generating the full .c file,
especially considering that GDB likes to have long term compatibility,
so if a syscall ever gets deprecated and removed, we would take a lot
longer to remove it from our support. Plus, if a user of Risc-V ever
notices that the file is out of date it would be easy for any GDB
developer to add support to the missing syscall by hand than to download
glibc source files and re-run the script. So I (as the record-full
maintainer, but someone who doesn't understand risc-v) would prefer if
the file was not generated by a script.
I also have a few more comments inlined. I haven't thoroughly reviewed
all lines so there may be other coding style changes I missed. I
encourage you to read GDB's style in the wiki, here:
https://sourceware.org/gdb/wiki/Internals%20GDB-C-Coding-Standards
> ---
> gdb/configure.tgt | 4 +-
> gdb/riscv-canonicalize-syscall.c | 555 ++++++++++++++
> gdb/riscv-linux-tdep.c | 261 +++++++
> gdb/riscv-linux-tdep.h | 29 +
> gdb/riscv-tdep.c | 703 +++++++++++++++++-
> gdb/riscv-tdep.h | 15 +
> .../riscv-canonicalize-syscall-gen.py | 126 ++++
> gdb/testsuite/lib/gdb.exp | 3 +-
> 8 files changed, 1688 insertions(+), 8 deletions(-)
> create mode 100644 gdb/riscv-canonicalize-syscall.c
> create mode 100644 gdb/riscv-linux-tdep.h
> create mode 100644 gdb/syscalls/riscv-canonicalize-syscall-gen.py
>
> diff --git a/gdb/configure.tgt b/gdb/configure.tgt
> index 62df71b13fa..f965e03e84d 100644
> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt
> @@ -545,8 +545,8 @@ 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.o \
> + glibc-tdep.o linux-tdep.o solib-svr4.o symfile-mem.o linux-record.o"
Please keep the indentation as it was.
> ;;
>
> riscv*-*-*)
> diff --git a/gdb/riscv-canonicalize-syscall.c b/gdb/riscv-canonicalize-syscall.c
> new file mode 100644
> index 00000000000..779bbd9335c
> --- /dev/null
> +++ b/gdb/riscv-canonicalize-syscall.c
Generated files should end with the -gen suffix, so this should be
riscv-canonicalize-syscall-gen.c if the final version of this change
continues using the python script.
> @@ -0,0 +1,555 @@
> +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py */
Even for generated files, the copyright blurb should be added here.
> +
> +#include "defs.h"
> +#include "riscv-linux-tdep.h"
> +
> +enum gdb_syscall
> +riscv64_canonicalize_syscall (int syscall)
> +{
> + switch (syscall)
> + {
> + case 0: // #define __NR_io_setup 0
All comments should be in the form /* */, rather than //
I would also prefer if this followed what other architectures do,
defining an enum to relate syscall numbers in a RISCV system to their
names, and then this function returning the gdb_sys enum numbers,
however I don't expect to be working on RISCV in the near future, so
feel free to ignore this comment if the other RISCV maintainers are ok
with this style. This would make it so no #define comment is necessary
> + return gdb_sys_io_setup;
> + case 1: // #define __NR_io_destroy 1
> + return gdb_sys_io_destroy;
> + case 2: // #define __NR_io_submit 2
> + return gdb_sys_io_submit;
> + case 3: // #define __NR_io_cancel 3
> + return gdb_sys_io_cancel;
> + case 4: // #define __NR_io_getevents 4
> + return gdb_sys_io_getevents;
> + case 5: // #define __NR_setxattr 5
> + return gdb_sys_setxattr;
> + case 6: // #define __NR_lsetxattr 6
> + return gdb_sys_lsetxattr;
> + case 7: // #define __NR_fsetxattr 7
> + return gdb_sys_fsetxattr;
> + case 8: // #define __NR_getxattr 8
> + return gdb_sys_getxattr;
> + case 9: // #define __NR_lgetxattr 9
> + return gdb_sys_lgetxattr;
> + case 10: // #define __NR_fgetxattr 10
> + return gdb_sys_fgetxattr;
> + case 11: // #define __NR_listxattr 11
> + return gdb_sys_listxattr;
> + case 12: // #define __NR_llistxattr 12
> + return gdb_sys_llistxattr;
> + case 13: // #define __NR_flistxattr 13
> + return gdb_sys_flistxattr;
> + case 14: // #define __NR_removexattr 14
> + return gdb_sys_removexattr;
> + case 15: // #define __NR_lremovexattr 15
> + return gdb_sys_lremovexattr;
> + case 16: // #define __NR_fremovexattr 16
> + return gdb_sys_fremovexattr;
> + case 17: // #define __NR_getcwd 17
> + return gdb_sys_getcwd;
> + case 18: // #define __NR_lookup_dcookie 18
> + return gdb_sys_lookup_dcookie;
> + case 19: // #define __NR_eventfd2 19
> + return gdb_sys_eventfd2;
> + case 20: // #define __NR_epoll_create1 20
> + return gdb_sys_epoll_create1;
> + case 21: // #define __NR_epoll_ctl 21
> + return gdb_sys_epoll_ctl;
> + case 22: // #define __NR_epoll_pwait 22
> + return gdb_sys_epoll_pwait;
> + case 23: // #define __NR_dup 23
> + return gdb_sys_dup;
> + case 24: // #define __NR_dup3 24
> + return gdb_sys_dup3;
> + case 25: // #define __NR_fcntl 25
> + return gdb_sys_fcntl;
> + case 26: // #define __NR_inotify_init1 26
> + return gdb_sys_inotify_init1;
> + case 27: // #define __NR_inotify_add_watch 27
> + return gdb_sys_inotify_add_watch;
> + case 28: // #define __NR_inotify_rm_watch 28
> + return gdb_sys_inotify_rm_watch;
> + case 29: // #define __NR_ioctl 29
> + return gdb_sys_ioctl;
> + case 30: // #define __NR_ioprio_set 30
> + return gdb_sys_ioprio_set;
> + case 31: // #define __NR_ioprio_get 31
> + return gdb_sys_ioprio_get;
> + case 32: // #define __NR_flock 32
> + return gdb_sys_flock;
> + case 33: // #define __NR_mknodat 33
> + return gdb_sys_mknodat;
> + case 34: // #define __NR_mkdirat 34
> + return gdb_sys_mkdirat;
> + case 35: // #define __NR_unlinkat 35
> + return gdb_sys_unlinkat;
> + case 36: // #define __NR_symlinkat 36
> + return gdb_sys_symlinkat;
> + case 37: // #define __NR_linkat 37
> + return gdb_sys_linkat;
> + // case 39: return gdb_sys_umount2;
I don't think these provide much help, since syscall numbers are os+cpu
specific, and is the whole reason why we have this function
canonicalizing the syscall numbers. Again, take this as only a
suggestion, as I am not familiar with riscv code.
> + case 40: // #define __NR_mount 40
> + return gdb_sys_mount;
> + case 41: // #define __NR_pivot_root 41
> + return gdb_sys_pivot_root;
> + case 42: // #define __NR_nfsservctl 42
> + return gdb_sys_nfsservctl;
> + case 43: // #define __NR_statfs 43
> + return gdb_sys_statfs;
> + case 44: // #define __NR_fstatfs 44
> + return gdb_sys_fstatfs;
> + case 45: // #define __NR_truncate 45
> + return gdb_sys_truncate;
> + case 46: // #define __NR_ftruncate 46
> + return gdb_sys_ftruncate;
> + case 47: // #define __NR_fallocate 47
> + return gdb_sys_fallocate;
> + case 48: // #define __NR_faccessat 48
> + return gdb_sys_faccessat;
> + case 49: // #define __NR_chdir 49
> + return gdb_sys_chdir;
> + case 50: // #define __NR_fchdir 50
> + return gdb_sys_fchdir;
> + case 51: // #define __NR_chroot 51
> + return gdb_sys_chroot;
> + case 52: // #define __NR_fchmod 52
> + return gdb_sys_fchmod;
> + case 53: // #define __NR_fchmodat 53
> + return gdb_sys_fchmodat;
> + case 54: // #define __NR_fchownat 54
> + return gdb_sys_fchownat;
> + case 55: // #define __NR_fchown 55
> + return gdb_sys_fchown;
> + case 56: // #define __NR_openat 56
> + return gdb_sys_openat;
> + case 57: // #define __NR_close 57
> + return gdb_sys_close;
> + case 58: // #define __NR_vhangup 58
> + return gdb_sys_vhangup;
> + case 59: // #define __NR_pipe2 59
> + return gdb_sys_pipe2;
> + case 60: // #define __NR_quotactl 60
> + return gdb_sys_quotactl;
> + case 61: // #define __NR_getdents64 61
> + return gdb_sys_getdents64;
> + case 62: // #define __NR_lseek 62
> + return gdb_sys_lseek;
> + case 63: // #define __NR_read 63
> + return gdb_sys_read;
> + case 64: // #define __NR_write 64
> + return gdb_sys_write;
> + case 65: // #define __NR_readv 65
> + return gdb_sys_readv;
> + case 66: // #define __NR_writev 66
> + return gdb_sys_writev;
> + case 67: // #define __NR_pread64 67
> + return gdb_sys_pread64;
> + case 68: // #define __NR_pwrite64 68
> + return gdb_sys_pwrite64;
> + // case 69: return gdb_sys_preadv;
> + // case 70: return gdb_sys_pwritev;
> + case 71: // #define __NR_sendfile 71
> + return gdb_sys_sendfile;
> + case 72: // #define __NR_pselect6 72
> + return gdb_sys_pselect6;
> + case 73: // #define __NR_ppoll 73
> + return gdb_sys_ppoll;
> + // case 74: return gdb_sys_signalfd4;
> + case 75: // #define __NR_vmsplice 75
> + return gdb_sys_vmsplice;
> + case 76: // #define __NR_splice 76
> + return gdb_sys_splice;
> + case 77: // #define __NR_tee 77
> + return gdb_sys_tee;
> + case 78: // #define __NR_readlinkat 78
> + return gdb_sys_readlinkat;
> + case 79: // #define __NR_newfstatat 79
> + return gdb_sys_newfstatat;
> + case 80: // #define __NR_fstat 80
> + return gdb_sys_fstat;
> + case 81: // #define __NR_sync 81
> + return gdb_sys_sync;
> + case 82: // #define __NR_fsync 82
> + return gdb_sys_fsync;
> + case 83: // #define __NR_fdatasync 83
> + return gdb_sys_fdatasync;
> + case 84: // #define __NR_sync_file_range 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: // #define __NR_acct 89
> + return gdb_sys_acct;
> + case 90: // #define __NR_capget 90
> + return gdb_sys_capget;
> + case 91: // #define __NR_capset 91
> + return gdb_sys_capset;
> + case 92: // #define __NR_personality 92
> + return gdb_sys_personality;
> + case 93: // #define __NR_exit 93
> + return gdb_sys_exit;
> + case 94: // #define __NR_exit_group 94
> + return gdb_sys_exit_group;
> + case 95: // #define __NR_waitid 95
> + return gdb_sys_waitid;
> + case 96: // #define __NR_set_tid_address 96
> + return gdb_sys_set_tid_address;
> + case 97: // #define __NR_unshare 97
> + return gdb_sys_unshare;
> + case 98: // #define __NR_futex 98
> + return gdb_sys_futex;
> + case 99: // #define __NR_set_robust_list 99
> + return gdb_sys_set_robust_list;
> + case 100: // #define __NR_get_robust_list 100
> + return gdb_sys_get_robust_list;
> + case 101: // #define __NR_nanosleep 101
> + return gdb_sys_nanosleep;
> + case 102: // #define __NR_getitimer 102
> + return gdb_sys_getitimer;
> + case 103: // #define __NR_setitimer 103
> + return gdb_sys_setitimer;
> + case 104: // #define __NR_kexec_load 104
> + return gdb_sys_kexec_load;
> + case 105: // #define __NR_init_module 105
> + return gdb_sys_init_module;
> + case 106: // #define __NR_delete_module 106
> + return gdb_sys_delete_module;
> + case 107: // #define __NR_timer_create 107
> + return gdb_sys_timer_create;
> + case 108: // #define __NR_timer_gettime 108
> + return gdb_sys_timer_gettime;
> + case 109: // #define __NR_timer_getoverrun 109
> + return gdb_sys_timer_getoverrun;
> + case 110: // #define __NR_timer_settime 110
> + return gdb_sys_timer_settime;
> + case 111: // #define __NR_timer_delete 111
> + return gdb_sys_timer_delete;
> + case 112: // #define __NR_clock_settime 112
> + return gdb_sys_clock_settime;
> + case 113: // #define __NR_clock_gettime 113
> + return gdb_sys_clock_gettime;
> + case 114: // #define __NR_clock_getres 114
> + return gdb_sys_clock_getres;
> + case 115: // #define __NR_clock_nanosleep 115
> + return gdb_sys_clock_nanosleep;
> + case 116: // #define __NR_syslog 116
> + return gdb_sys_syslog;
> + case 117: // #define __NR_ptrace 117
> + return gdb_sys_ptrace;
> + case 118: // #define __NR_sched_setparam 118
> + return gdb_sys_sched_setparam;
> + case 119: // #define __NR_sched_setscheduler 119
> + return gdb_sys_sched_setscheduler;
> + case 120: // #define __NR_sched_getscheduler 120
> + return gdb_sys_sched_getscheduler;
> + case 121: // #define __NR_sched_getparam 121
> + return gdb_sys_sched_getparam;
> + case 122: // #define __NR_sched_setaffinity 122
> + return gdb_sys_sched_setaffinity;
> + case 123: // #define __NR_sched_getaffinity 123
> + return gdb_sys_sched_getaffinity;
> + case 124: // #define __NR_sched_yield 124
> + return gdb_sys_sched_yield;
> + case 125: // #define __NR_sched_get_priority_max 125
> + return gdb_sys_sched_get_priority_max;
> + case 126: // #define __NR_sched_get_priority_min 126
> + return gdb_sys_sched_get_priority_min;
> + case 127: // #define __NR_sched_rr_get_interval 127
> + return gdb_sys_sched_rr_get_interval;
> + case 128: // #define __NR_restart_syscall 128
> + return gdb_sys_restart_syscall;
> + case 129: // #define __NR_kill 129
> + return gdb_sys_kill;
> + case 130: // #define __NR_tkill 130
> + return gdb_sys_tkill;
> + case 131: // #define __NR_tgkill 131
> + return gdb_sys_tgkill;
> + case 132: // #define __NR_sigaltstack 132
> + return gdb_sys_sigaltstack;
> + case 133: // #define __NR_rt_sigsuspend 133
> + return gdb_sys_rt_sigsuspend;
> + case 134: // #define __NR_rt_sigaction 134
> + return gdb_sys_rt_sigaction;
> + case 135: // #define __NR_rt_sigprocmask 135
> + return gdb_sys_rt_sigprocmask;
> + case 136: // #define __NR_rt_sigpending 136
> + return gdb_sys_rt_sigpending;
> + case 137: // #define __NR_rt_sigtimedwait 137
> + return gdb_sys_rt_sigtimedwait;
> + case 138: // #define __NR_rt_sigqueueinfo 138
> + return gdb_sys_rt_sigqueueinfo;
> + case 139: // #define __NR_rt_sigreturn 139
> + return gdb_sys_rt_sigreturn;
> + case 140: // #define __NR_setpriority 140
> + return gdb_sys_setpriority;
> + case 141: // #define __NR_getpriority 141
> + return gdb_sys_getpriority;
> + case 142: // #define __NR_reboot 142
> + return gdb_sys_reboot;
> + case 143: // #define __NR_setregid 143
> + return gdb_sys_setregid;
> + case 144: // #define __NR_setgid 144
> + return gdb_sys_setgid;
> + case 145: // #define __NR_setreuid 145
> + return gdb_sys_setreuid;
> + case 146: // #define __NR_setuid 146
> + return gdb_sys_setuid;
> + case 147: // #define __NR_setresuid 147
> + return gdb_sys_setresuid;
> + case 148: // #define __NR_getresuid 148
> + return gdb_sys_getresuid;
> + case 149: // #define __NR_setresgid 149
> + return gdb_sys_setresgid;
> + case 150: // #define __NR_getresgid 150
> + return gdb_sys_getresgid;
> + case 151: // #define __NR_setfsuid 151
> + return gdb_sys_setfsuid;
> + case 152: // #define __NR_setfsgid 152
> + return gdb_sys_setfsgid;
> + case 153: // #define __NR_times 153
> + return gdb_sys_times;
> + case 154: // #define __NR_setpgid 154
> + return gdb_sys_setpgid;
> + case 155: // #define __NR_getpgid 155
> + return gdb_sys_getpgid;
> + case 156: // #define __NR_getsid 156
> + return gdb_sys_getsid;
> + case 157: // #define __NR_setsid 157
> + return gdb_sys_setsid;
> + case 158: // #define __NR_getgroups 158
> + return gdb_sys_getgroups;
> + case 159: // #define __NR_setgroups 159
> + return gdb_sys_setgroups;
> + case 160: // #define __NR_uname 160
> + return gdb_sys_uname;
> + case 161: // #define __NR_sethostname 161
> + return gdb_sys_sethostname;
> + case 162: // #define __NR_setdomainname 162
> + return gdb_sys_setdomainname;
> + case 163: // #define __NR_getrlimit 163
> + return gdb_sys_getrlimit;
> + case 164: // #define __NR_setrlimit 164
> + return gdb_sys_setrlimit;
> + case 165: // #define __NR_getrusage 165
> + return gdb_sys_getrusage;
> + case 166: // #define __NR_umask 166
> + return gdb_sys_umask;
> + case 167: // #define __NR_prctl 167
> + return gdb_sys_prctl;
> + case 168: // #define __NR_getcpu 168
> + return gdb_sys_getcpu;
> + case 169: // #define __NR_gettimeofday 169
> + return gdb_sys_gettimeofday;
> + case 170: // #define __NR_settimeofday 170
> + return gdb_sys_settimeofday;
> + case 171: // #define __NR_adjtimex 171
> + return gdb_sys_adjtimex;
> + case 172: // #define __NR_getpid 172
> + return gdb_sys_getpid;
> + case 173: // #define __NR_getppid 173
> + return gdb_sys_getppid;
> + case 174: // #define __NR_getuid 174
> + return gdb_sys_getuid;
> + case 175: // #define __NR_geteuid 175
> + return gdb_sys_geteuid;
> + case 176: // #define __NR_getgid 176
> + return gdb_sys_getgid;
> + case 177: // #define __NR_getegid 177
> + return gdb_sys_getegid;
> + case 178: // #define __NR_gettid 178
> + return gdb_sys_gettid;
> + case 179: // #define __NR_sysinfo 179
> + return gdb_sys_sysinfo;
> + case 180: // #define __NR_mq_open 180
> + return gdb_sys_mq_open;
> + case 181: // #define __NR_mq_unlink 181
> + return gdb_sys_mq_unlink;
> + case 182: // #define __NR_mq_timedsend 182
> + return gdb_sys_mq_timedsend;
> + case 183: // #define __NR_mq_timedreceive 183
> + return gdb_sys_mq_timedreceive;
> + case 184: // #define __NR_mq_notify 184
> + return gdb_sys_mq_notify;
> + case 185: // #define __NR_mq_getsetattr 185
> + return gdb_sys_mq_getsetattr;
> + case 186: // #define __NR_msgget 186
> + return gdb_sys_msgget;
> + case 187: // #define __NR_msgctl 187
> + return gdb_sys_msgctl;
> + case 188: // #define __NR_msgrcv 188
> + return gdb_sys_msgrcv;
> + case 189: // #define __NR_msgsnd 189
> + return gdb_sys_msgsnd;
> + case 190: // #define __NR_semget 190
> + return gdb_sys_semget;
> + case 191: // #define __NR_semctl 191
> + return gdb_sys_semctl;
> + case 192: // #define __NR_semtimedop 192
> + return gdb_sys_semtimedop;
> + case 193: // #define __NR_semop 193
> + return gdb_sys_semop;
> + case 194: // #define __NR_shmget 194
> + return gdb_sys_shmget;
> + case 195: // #define __NR_shmctl 195
> + return gdb_sys_shmctl;
> + case 196: // #define __NR_shmat 196
> + return gdb_sys_shmat;
> + case 197: // #define __NR_shmdt 197
> + return gdb_sys_shmdt;
> + case 198: // #define __NR_socket 198
> + return gdb_sys_socket;
> + case 199: // #define __NR_socketpair 199
> + return gdb_sys_socketpair;
> + case 200: // #define __NR_bind 200
> + return gdb_sys_bind;
> + case 201: // #define __NR_listen 201
> + return gdb_sys_listen;
> + case 202: // #define __NR_accept 202
> + return gdb_sys_accept;
> + case 203: // #define __NR_connect 203
> + return gdb_sys_connect;
> + case 204: // #define __NR_getsockname 204
> + return gdb_sys_getsockname;
> + case 205: // #define __NR_getpeername 205
> + return gdb_sys_getpeername;
> + case 206: // #define __NR_sendto 206
> + return gdb_sys_sendto;
> + case 207: // #define __NR_recvfrom 207
> + return gdb_sys_recvfrom;
> + case 208: // #define __NR_setsockopt 208
> + return gdb_sys_setsockopt;
> + case 209: // #define __NR_getsockopt 209
> + return gdb_sys_getsockopt;
> + case 210: // #define __NR_shutdown 210
> + return gdb_sys_shutdown;
> + case 211: // #define __NR_sendmsg 211
> + return gdb_sys_sendmsg;
> + case 212: // #define __NR_recvmsg 212
> + return gdb_sys_recvmsg;
> + case 213: // #define __NR_readahead 213
> + return gdb_sys_readahead;
> + case 214: // #define __NR_brk 214
> + return gdb_sys_brk;
> + case 215: // #define __NR_munmap 215
> + return gdb_sys_munmap;
> + case 216: // #define __NR_mremap 216
> + return gdb_sys_mremap;
> + case 217: // #define __NR_add_key 217
> + return gdb_sys_add_key;
> + case 218: // #define __NR_request_key 218
> + return gdb_sys_request_key;
> + case 219: // #define __NR_keyctl 219
> + return gdb_sys_keyctl;
> + case 220: // #define __NR_clone 220
> + return gdb_sys_clone;
> + case 221: // #define __NR_execve 221
> + return gdb_sys_execve;
> + case 222:
> + return gdb_old_mmap;
> + case 223: // #define __NR_fadvise64 223
> + return gdb_sys_fadvise64;
> + case 224: // #define __NR_swapon 224
> + return gdb_sys_swapon;
> + case 225: // #define __NR_swapoff 225
> + return gdb_sys_swapoff;
> + case 226: // #define __NR_mprotect 226
> + return gdb_sys_mprotect;
> + case 227: // #define __NR_msync 227
> + return gdb_sys_msync;
> + case 228: // #define __NR_mlock 228
> + return gdb_sys_mlock;
> + case 229: // #define __NR_munlock 229
> + return gdb_sys_munlock;
> + case 230: // #define __NR_mlockall 230
> + return gdb_sys_mlockall;
> + case 231: // #define __NR_munlockall 231
> + return gdb_sys_munlockall;
> + case 232: // #define __NR_mincore 232
> + return gdb_sys_mincore;
> + case 233: // #define __NR_madvise 233
> + return gdb_sys_madvise;
> + case 234: // #define __NR_remap_file_pages 234
> + return gdb_sys_remap_file_pages;
> + case 235: // #define __NR_mbind 235
> + return gdb_sys_mbind;
> + case 236: // #define __NR_get_mempolicy 236
> + return gdb_sys_get_mempolicy;
> + case 237: // #define __NR_set_mempolicy 237
> + return gdb_sys_set_mempolicy;
> + case 238: // #define __NR_migrate_pages 238
> + return gdb_sys_migrate_pages;
> + case 239: // #define __NR_move_pages 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: // #define __NR_wait4 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: // #define __NR_getrandom 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: // #define __NR_statx 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 ff478cf4c28..5a56e9911f8 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,259 @@ 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;
> +
> +/* Record all registers but PC register for process-record. */
> +
> +using regnum_type = int;
> +
> +static bool
> +save_registers (struct regcache *regcache, regnum_type fir, regnum_type last)
> +{
> + for (regnum_type i = fir; i != last; ++i)
> + if (record_full_arch_list_add_reg (regcache, i))
> + return false;
> + return true;
> +};
> +
> +static bool
> +riscv_all_but_pc_registers_record (struct regcache *regcache)
> +{
> + auto &&gdbarch = regcache->arch ();
> + auto &&tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> + auto &&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;
> +
> + // TODO: add saving vector registers
> +
> + return true;
> +}
> +
> +/* Handler for riscv system call instruction recording. */
> +
> +static int
> +riscv_linux_syscall_record (struct regcache *regcache,
> + unsigned long svc_number)
> +{
> + auto 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)
> + {
> + gdb_printf (gdb_stderr,
> + _ ("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;
> + }
> +
> + auto 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. */
> +/* These values are the size of the type that will be used in a system
> + call. They are obtained from Linux Kernel source. */
> +static void
> +riscv64_linux_record_tdep_init (
> + struct gdbarch *gdbarch, struct linux_record_tdep &riscv_linux_record_tdep)
> +{
> + riscv_linux_record_tdep.size_pointer
> + = gdbarch_ptr_bit (gdbarch) / TARGET_CHAR_BIT;
> + riscv_linux_record_tdep.size__old_kernel_stat
> + = 48; // arch/powerpc/include/uapi/asm/stat.h: 2+2+2+2+2+2+2+2+8+8+8+8
> + 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; // include/uapi/linux/utsname.h:7:9+9+9+9+9
> + riscv_linux_record_tdep.size_ustat = 32;
> + riscv_linux_record_tdep.size_old_sigaction
> + = 32; // include/linux/signal_types.h:60:8+8+8+8
> + riscv_linux_record_tdep.size_old_sigset_t
> + = 8; // arch/powerpc/include/uapi/asm/signal.h:15
> + riscv_linux_record_tdep.size_rlimit = 16;
> + riscv_linux_record_tdep.size_rusage = 144;
> + riscv_linux_record_tdep.size_timeval = 8; // include/uapi/linux/time.h:17:8+8
> + riscv_linux_record_tdep.size_timezone = 8;
> + riscv_linux_record_tdep.size_old_gid_t
> + = 2; // arch/arm64/include/uapi/asm/posix_types.h:6
> + riscv_linux_record_tdep.size_old_uid_t
> + = 2; // arch/arm64/include/uapi/asm/posix_types.h:5
> + 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; // include/uapi/linux/time.h:27:8+8
> + riscv_linux_record_tdep.size_stat = 128;
> + riscv_linux_record_tdep.size_old_utsname
> + = 325; // include/uapi/linux/utsname.h:17:65+65+65+65+65
> + 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; // include/uapi/linux/timex.h:65:4+8+8+8+8+4+8+8+8+8+8+8+8+4+8+8+8+8+8+4+11*4
> + riscv_linux_record_tdep.size_mem_dqinfo = 72;
> + riscv_linux_record_tdep.size_if_dqblk
> + = 68; // include/uapi/linux/quota.h:111:8*8+4
> + riscv_linux_record_tdep.size_fs_quota_stat
> + = 64; // include/uapi/linux/dqblk_xfs.h:165:1+2+1+20+20+4+4+4+4+2+2
> + riscv_linux_record_tdep.size_timespec
> + = 16; // include/uapi/linux/time.h:11:8+8
> + riscv_linux_record_tdep.size_pollfd = 8;
> + riscv_linux_record_tdep.size_NFS_FHSIZE = 32; // include/uapi/linux/nfs.h:20
> + riscv_linux_record_tdep.size_knfsd_fh = 36; // fs/nfsd/nfsfh.h:47:4+32
> + 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; // include/uapi/asm-generic/stat.h:49:8+8+4+4+4+4+8+8+8+4+4+8+4+4+4+4+4+4+4+4
> + 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; // arch/x86/include/uapi/asm/ldt.h:21:4*9+1
> + 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; // tools/include/nolibc/std.h:34
> +
> + 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;
> +
> + 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;
> + riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
> +}
> +
> /* Initialize RISC-V Linux ABI info. */
>
> static void
> @@ -205,6 +463,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 00000000000..5ee2c94387f
> --- /dev/null
> +++ b/gdb/riscv-linux-tdep.h
> @@ -0,0 +1,29 @@
> +/* Copyright (C) 2024 Free Software Foundation, Inc.
> + Contributed by Timur Golubovich
I think we don't use these "contributed by" lines anymore.
> +
> + 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 932708ca4e9..29013bc46c1 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
>
> @@ -1655,6 +1658,11 @@ class riscv_insn
> 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. */
> @@ -1800,11 +1808,6 @@ class riscv_insn
> 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;
>
> @@ -4415,6 +4418,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);
>
> @@ -4833,3 +4839,690 @@ this option can be used."),
> &setriscvcmdlist,
> &showriscvcmdlist);
> }
> +
> +static bool
> +try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
> +{
> + gdb_assert (regcache);
> +
> + if (regcache->raw_read<ULONGEST> (regnum, &addr)
> + != register_status::REG_VALID)
> + {
> + warning (_ ("Can not read at address %lx"), addr);
> + return false;
> + }
> + return true;
> +}
> +
> +class riscv_recorded_insn final
> +{
> +public:
> + using regnum_type = int;
> + using memory_type = std::pair<CORE_ADDR, int>;
> +
> + enum class record_type
> + {
> + UNKNOWN,
> + ORDINARY,
> +
> + // corner cases
> + ECALL,
> + EBREAK,
> + };
> +
> +private:
> + using recorded_regs = std::vector<regnum_type>;
> + using recorded_mems = std::vector<memory_type>;
> +
> + using mem_addr = decltype (std::declval<memory_type> ().first);
> + using mem_len = decltype (std::declval<memory_type> ().second);
> +
> + record_type m_record_type = record_type::UNKNOWN;
> +
> + bool m_error_occured = false;
> +
> + recorded_regs m_regs;
> + recorded_mems m_mems;
> +
> + /* 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;
> + }
> +
> + bool
> + set_ordinary_record_type () noexcept
> + {
> + m_record_type = record_type::ORDINARY;
> + return true;
> + }
> +
> + bool
> + set_error () noexcept
> + {
> + m_error_occured = true;
> + return false;
> + }
> +
> + bool
> + has_error () const noexcept
> + {
> + return m_error_occured;
> + }
> +
> + bool
> + read_reg (struct regcache *regcache, regnum_type reg,
> + ULONGEST &addr) noexcept
> + {
> + gdb_assert (regcache);
> +
> + if (!try_read (regcache, reg, addr))
> + {
> + return set_error ();
> + }
> + return true;
> + }
> +
> + bool
> + save_reg (regnum_type regnum) noexcept
> + {
> + try
> + {
> + m_regs.emplace_back (regnum);
> + return true;
> + }
> + catch (const std::bad_alloc &ex)
> + {
> + warning (_ ("Exception was caught during reverse execution: %s"),
> + ex.what ());
> + return set_error ();
> + }
> + }
> +
> + bool
> + save_mem (mem_addr addr, mem_len len) noexcept
> + {
> + try
> + {
> + m_mems.emplace_back (addr, len);
> + return true;
> + }
> + catch (const std::bad_alloc &ex)
> + {
> + warning (_ ("Exception was caught during reverse execution: %s"),
> + ex.what ());
> + return set_error ();
> + }
> + }
> +
> + 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 ();
> + }
> +
> + 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_ld_insn (ival) || is_lbu_insn (ival)
> + || is_lhu_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
> + || is_lw_insn (ival) || is_ld_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_insn (ival) || is_srli_insn (ival)
> + || is_srai_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_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_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_d_w_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. Needs error checking after */
Comments should end in a period and 2 spaces.
> + 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 ();
> + }
> +
> + static bool
> + need_save_pc_fprd (ULONGEST ival) noexcept
> + {
> + return 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_flw_insn (ival) || is_fld_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_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_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. Needs error checking after */
> + 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 ();
> + }
> +
> + 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. Needs error checking after */
> + 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 ();
> + }
> +
> + /* If return value is > 0, than instruction needs saving memory len */
> + 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. Needs error checking after */
> + bool
> + try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache);
> +
> + mem_addr addr = 0;
> + auto 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 ();
> + }
> +
> + /* If return value is > 0, than instruction needs saving memory len */
> + static mem_len
> + need_save_pc_rd_mem (ULONGEST ival) noexcept
> + {
> + if (is_sc_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_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. Needs error checking after */
> + bool
> + try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache);
> +
> + auto 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 ();
> + }
> +
> + /* If return value is > 0, than instruction needs saving memory len */
> + static mem_len
> + need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
> + {
> + if (is_amoswap_w_insn (ival))
> + return 4;
> + if (is_amoswap_d_insn (ival))
> + return 8;
> + return 0;
> + }
> +
> + /* Returns true if instruction is classified. Needs error checking after */
> + bool
> + try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache);
> +
> + mem_addr addr = 0;
> + auto len = need_save_pc_rs2_rd_mem (ival);
> + if (len <= 0)
> + return false;
> +
> + return !read_reg (regcache, decode_rs1 (ival), addr)
> + || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
> + || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
> + }
> +
> + /* Returns true if instruction is successfully recorder.
> + Else returns false */
> + bool
> + record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache);
> +
> + 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)
> + || try_save_pc_rs2_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 recorder.
> + Else returns false */
> + bool
> + record_insn_len2 (ULONGEST ival, struct regcache *regcache,
> + struct gdbarch *gdbarch) noexcept
> + {
> + gdb_assert (regcache);
> + gdb_assert (gdbarch);
> +
> + mem_addr addr = 0;
> + auto xlen = riscv_isa_xlen (gdbarch);
> +
> + /* 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)
> + || (xlen == 8 && is_c_ld_insn (ival)))
> + {
> + return !save_reg (decode_crs2_short (ival))
> + || set_ordinary_record_type ();
> + }
> +
> + if (is_c_fld_insn (ival) || (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) || (xlen == 8 && is_c_sd_insn (ival)))
> + {
> + int offset = EXTRACT_CLTYPE_LD_IMM (ival);
> + return !read_reg (regcache, decode_crs1_short (ival), addr)
> + || !save_mem (addr + offset, 8) || set_ordinary_record_type ();
> + }
> +
> + if ((xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
> + {
> + int offset = 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 (xlen == 4 && is_c_jal_insn (ival))
> + {
> + return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
> + }
> +
> + if ((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) || (xlen == 8 && is_c_subw_insn (ival))
> + || (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) || (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) || (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) || (xlen == 8 && is_c_sdsp_insn (ival)))
> + {
> + int offset = 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) || (xlen == 4 && is_c_fswsp_insn (ival)))
> + {
> + int offset = 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:
> + using regs_iter = recorded_regs::const_iterator;
> + using mems_iter = recorded_mems::const_iterator;
> +
> + bool
> + record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
> + {
> + int m_length = 0;
> + auto 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, gdbarch);
> +
> + /* 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. */
Again, the indentation here is incorrect. Lines should start in the same
column as the number 6.
--
Cheers,
Guinevere Larsen
She/Her/Hers
> + gdb_assert (m_length > 0 && m_length % 2 == 0);
> +
> + warning (_ ("Can not record unknown instruction (opcode = %lx)"), ival);
> + return false;
> + }
> +
> + record_type
> + get_record_type () const noexcept
> + {
> + return m_record_type;
> + }
> +
> + regs_iter
> + regs_begin () const noexcept
> + {
> + return m_regs.begin ();
> + }
> +
> + regs_iter
> + regs_end () const noexcept
> + {
> + return m_regs.end ();
> + }
> +
> + mems_iter
> + mems_begin () const noexcept
> + {
> + return m_mems.begin ();
> + }
> +
> + mems_iter
> + mems_end () const noexcept
> + {
> + return m_mems.end ();
> + }
> +};
> +
> +static int
> +riscv_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
> + const riscv_recorded_insn &insn)
> +{
> + gdb_assert (gdbarch && regcache);
> +
> + riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> + auto regs_begin = insn.regs_begin ();
> + auto regs_end = insn.regs_end ();
> + if (std::any_of (regs_begin, regs_end, [®cache] (auto &®_it) {
> + return record_full_arch_list_add_reg (regcache, reg_it);
> + }))
> + return -1;
> +
> + auto mems_begin = insn.mems_begin ();
> + auto mems_end = insn.mems_end ();
> + if (std::any_of (mems_begin, mems_end, [] (auto &&mem_it) {
> + return record_full_arch_list_add_mem (mem_it.first, mem_it.second);
> + }))
> + return -1;
> +
> + switch (insn.get_record_type ())
> + {
> + case riscv_recorded_insn::record_type::ORDINARY:
> + break;
> +
> + case riscv_recorded_insn::record_type::ECALL:
> + {
> + if (!tdep->riscv_syscall_record)
> + {
> + warning (_ ("Syscall record is not supported"));
> + return -1;
> + }
> + ULONGEST reg_val = 0;
> + 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;
> +}
> +
> +int
> +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
> + CORE_ADDR addr)
> +{
> + gdb_assert (gdbarch && regcache);
> +
> + riscv_recorded_insn insn;
> + if (!insn.record (gdbarch, regcache, addr))
> + {
> + record_full_arch_list_add_end ();
> + return -1;
> + }
> +
> + auto ret_val = riscv_make_record_process (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 4bdc2e7a3d5..505371ba4f6 100644
> --- a/gdb/riscv-tdep.h
> +++ b/gdb/riscv-tdep.h
> @@ -35,6 +35,10 @@ enum
> RISCV_FP_REGNUM = 8, /* Frame Pointer. */
> RISCV_A0_REGNUM = 10, /* First argument. */
> RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
> RISCV_PC_REGNUM = 32, /* Program Counter. */
>
> @@ -113,6 +117,11 @@ 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 +186,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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> new file mode 100644
> index 00000000000..235f858f5ff
> --- /dev/null
> +++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> @@ -0,0 +1,126 @@
> +#!/usr/bin/env python3
> +# pylint: disable=invalid-name
> +
> +# Copyright (C) 2024 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/>.
> +
> +import argparse
> +import re
> +import sys
> +from pathlib import Path as _Path
> +
> +head = """\
> +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py */
> +
> +#include "defs.h"
> +#include "riscv-linux-tdep.h"
> +
> +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:
> + canon_syscalls[syscall_num] = (
> + f" case {syscall_num}: // {line} return {gdb_syscall_name};\n"
> + )
> + # this is a place for corner cases
> + elif syscall_name == "mmap":
> + gdb_old_syscall_name = "gdb_old_mmap"
> + canon_syscalls[syscall_num] = f" case {syscall_num}:\n return {gdb_old_syscall_name};\n"
> + else:
> + canon_syscalls[syscall_num] = f" // case {syscall_num}: return {gdb_syscall_name};\n"
> + 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.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.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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 410f99e3350..b048ac7078b 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -3718,7 +3718,8 @@ proc supports_reverse {} {
> || [istarget "i\[34567\]86-*-linux*"]
> || [istarget "aarch64*-*-linux*"]
> || [istarget "powerpc*-*-linux*"]
> - || [istarget "s390*-*-linux*"] } {
> + || [istarget "s390*-*-linux*"]
> + || [istarget "riscv*-*-*"] } {
> return 1
> }
>
^ permalink raw reply [flat|nested] 35+ messages in thread* [PATCH v2] gdb/riscv: Add record support for rv64gc instructions
2024-11-18 19:46 ` Guinevere Larsen
@ 2024-11-26 14:17 ` Timur
2024-11-27 14:23 ` Guinevere Larsen
0 siblings, 1 reply; 35+ messages in thread
From: Timur @ 2024-11-26 14:17 UTC (permalink / raw)
To: gdb-patches; +Cc: Timur
Hello!
Thanks for review and sorry for such late answer.
> ---
> gdb/configure.tgt | 4 +-
> gdb/riscv-canonicalize-syscall.c | 555 ++++++++++++++
> gdb/riscv-linux-tdep.c | 261 +++++++
> gdb/riscv-linux-tdep.h | 29 +
> gdb/riscv-tdep.c | 703 +++++++++++++++++-
> gdb/riscv-tdep.h | 15 +
> .../riscv-canonicalize-syscall-gen.py | 126 ++++
> gdb/testsuite/lib/gdb.exp | 3 +-
> 8 files changed, 1688 insertions(+), 8 deletions(-)
> create mode 100644 gdb/riscv-canonicalize-syscall.c
> create mode 100644 gdb/riscv-linux-tdep.h
> create mode 100644 gdb/syscalls/riscv-canonicalize-syscall-gen.py
>
> diff --git a/gdb/configure.tgt b/gdb/configure.tgt
> index 62df71b13fa..f965e03e84d 100644
> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt
> @@ -545,8 +545,8 @@ 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.o \
> + glibc-tdep.o linux-tdep.o solib-svr4.o symfile-mem.o linux-record.o"
> Please keep the indentation as it was.
Addressed
> ;;
> riscv*-*-*)
> diff --git a/gdb/riscv-canonicalize-syscall.c b/gdb/riscv-canonicalize-syscall.c
> new file mode 100644
> index 00000000000..779bbd9335c
> --- /dev/null
> +++ b/gdb/riscv-canonicalize-syscall.c
> Generated files should end with the -gen suffix, so this should be
> riscv-canonicalize-syscall-gen.c if the final version of this change
> continues using the python script.
As for now I decided to leave it, changed it with suffix '-gen'
> @@ -0,0 +1,555 @@
> +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py */
> Even for generated files, the copyright blurb should be added here.
Addressed
> +
> +#include "defs.h"
> +#include "riscv-linux-tdep.h"
> +
> +enum gdb_syscall
> +riscv64_canonicalize_syscall (int syscall)
> +{
> + switch (syscall)
> + {
> + case 0: // #define __NR_io_setup 0
>
> All comments should be in the form /* */, rather than //
>
> I would also prefer if this followed what other architectures do,
> defining an enum to relate syscall numbers in a RISCV system to their
> names, and then this function returning the gdb_sys enum numbers,
> however I don't expect to be working on RISCV in the near future, so
> feel free to ignore this comment if the other RISCV maintainers are ok
> with this style. This would make it so no #define comment is necessary
Decided to make a script generating function that canonicalizes RISC-V syscall.
This solution will simplify adding record support for rv32 system. I also left
place for corner cases in generating script as they always may exist. Also
removed this #define comment from generating script.
> + case 35: // #define __NR_unlinkat 35
> + return gdb_sys_unlinkat;
> + case 36: // #define __NR_symlinkat 36
> + return gdb_sys_symlinkat;
> + case 37: // #define __NR_linkat 37
> + return gdb_sys_linkat;
> + // case 39: return gdb_sys_umount2;
>
> I don't think these provide much help, since syscall numbers are os+cpu
> specific, and is the whole reason why we have this function canonicalizing
> the syscall numbers. Again, take this as only a suggestion, as I am not
> familiar with riscv > code.
Deleted unnecessary comments.
> /* 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 00000000000..5ee2c94387f
> --- /dev/null
> +++ b/gdb/riscv-linux-tdep.h
> @@ -0,0 +1,29 @@
> +/* Copyright (C) 2024 Free Software Foundation, Inc.
> + Contributed by Timur Golubovich
> I think we don't use these "contributed by" lines anymore.
Addressed
> + || is_lr_w_insn (ival) || is_lr_d_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_d_w_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. Needs error checking after */
> Comments should end in a period and 2 spaces.
Addressed
> +
> + bool
> + record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
> + {
> + int m_length = 0;
> + auto 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, gdbarch);
> +
> + /* 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. */
> Again, the indentation here is incorrect. Lines should start in the same column as the number 6.
Addressed
Best wishes,
Timur Golubovich
Timur Golubovich timurgol007@gmail.com
---
gdb/configure.tgt | 4 +-
gdb/riscv-linux-tdep.c | 252 +++++++
gdb/riscv-linux-tdep.h | 28 +
gdb/riscv-tdep.c | 703 +++++++++++++++++-
gdb/riscv-tdep.h | 15 +
.../riscv-canonicalize-syscall-gen.py | 143 ++++
gdb/testsuite/lib/gdb.exp | 3 +-
7 files changed, 1140 insertions(+), 8 deletions(-)
create mode 100644 gdb/riscv-linux-tdep.h
create mode 100644 gdb/syscalls/riscv-canonicalize-syscall-gen.py
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 62df71b13fa..b350e1f6505 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -545,8 +545,8 @@ 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/riscv-linux-tdep.c b/gdb/riscv-linux-tdep.c
index ff478cf4c28..12cab06f276 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,250 @@ 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;
+
+/* Record all registers but PC register for process-record. */
+
+using regnum_type = int;
+
+static bool
+save_registers (struct regcache *regcache, regnum_type fir, regnum_type last)
+{
+ for (regnum_type i = fir; i != last; ++i)
+ if (record_full_arch_list_add_reg (regcache, i))
+ return false;
+ return true;
+};
+
+static bool
+riscv_all_but_pc_registers_record (struct regcache *regcache)
+{
+ auto &&gdbarch = regcache->arch ();
+ auto &&tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto &&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)
+{
+ auto 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)
+ {
+ gdb_printf (gdb_stderr,
+ _ ("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;
+ }
+
+ auto 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. */
+/* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+static void
+riscv64_linux_record_tdep_init (
+ struct gdbarch *gdbarch, struct linux_record_tdep &riscv_linux_record_tdep)
+{
+ /* Initialize the riscv_linux_record_tdep. */
+ /* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+ 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;
+ riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
+}
+
/* Initialize RISC-V Linux ABI info. */
static void
@@ -205,6 +454,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 00000000000..a0d67003a81
--- /dev/null
+++ b/gdb/riscv-linux-tdep.h
@@ -0,0 +1,28 @@
+/* Copyright (C) 2024 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 932708ca4e9..c6c7b7a63fd 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
@@ -1655,6 +1658,11 @@ class riscv_insn
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. */
@@ -1800,11 +1808,6 @@ class riscv_insn
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;
@@ -4415,6 +4418,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);
@@ -4833,3 +4839,690 @@ this option can be used."),
&setriscvcmdlist,
&showriscvcmdlist);
}
+
+static bool
+try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
+{
+ gdb_assert (regcache);
+
+ if (regcache->raw_read<ULONGEST> (regnum, &addr)
+ != register_status::REG_VALID)
+ {
+ warning (_ ("Can not read at address %lx"), addr);
+ return false;
+ }
+ return true;
+}
+
+class riscv_recorded_insn final
+{
+public:
+ using regnum_type = int;
+ using memory_type = std::pair<CORE_ADDR, int>;
+
+ enum class record_type
+ {
+ UNKNOWN,
+ ORDINARY,
+
+ /* Corner cases. */
+ ECALL,
+ EBREAK,
+ };
+
+private:
+ using recorded_regs = std::vector<regnum_type>;
+ using recorded_mems = std::vector<memory_type>;
+
+ using mem_addr = decltype (std::declval<memory_type> ().first);
+ using mem_len = decltype (std::declval<memory_type> ().second);
+
+ record_type m_record_type = record_type::UNKNOWN;
+
+ bool m_error_occured = false;
+
+ recorded_regs m_regs;
+ recorded_mems m_mems;
+
+ /* 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;
+ }
+
+ bool
+ set_ordinary_record_type () noexcept
+ {
+ m_record_type = record_type::ORDINARY;
+ return true;
+ }
+
+ bool
+ set_error () noexcept
+ {
+ m_error_occured = true;
+ return false;
+ }
+
+ bool
+ has_error () const noexcept
+ {
+ return m_error_occured;
+ }
+
+ bool
+ read_reg (struct regcache *regcache, regnum_type reg,
+ ULONGEST &addr) noexcept
+ {
+ gdb_assert (regcache);
+
+ if (!try_read (regcache, reg, addr))
+ {
+ return set_error ();
+ }
+ return true;
+ }
+
+ bool
+ save_reg (regnum_type regnum) noexcept
+ {
+ try
+ {
+ m_regs.emplace_back (regnum);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ bool
+ save_mem (mem_addr addr, mem_len len) noexcept
+ {
+ try
+ {
+ m_mems.emplace_back (addr, len);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ 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 ();
+ }
+
+ 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_ld_insn (ival) || is_lbu_insn (ival)
+ || is_lhu_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
+ || is_lw_insn (ival) || is_ld_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_insn (ival) || is_srli_insn (ival)
+ || is_srai_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_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_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_d_w_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. Needs error checking after. */
+ 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 ();
+ }
+
+ static bool
+ need_save_pc_fprd (ULONGEST ival) noexcept
+ {
+ return 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_flw_insn (ival) || is_fld_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_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_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. Needs error checking after. */
+ 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 ();
+ }
+
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, than instruction needs saving memory len. */
+ 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. Needs error checking after. */
+ bool
+ try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache);
+
+ mem_addr addr = 0;
+ auto 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 ();
+ }
+
+ /* If return value is > 0, than instruction needs saving memory len. */
+ static mem_len
+ need_save_pc_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_sc_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_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. Needs error checking after. */
+ bool
+ try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache);
+
+ auto 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 ();
+ }
+
+ /* If return value is > 0, than instruction needs saving memory len. */
+ static mem_len
+ need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_amoswap_w_insn (ival))
+ return 4;
+ if (is_amoswap_d_insn (ival))
+ return 8;
+ return 0;
+ }
+
+ /* Returns true if instruction is classified. Needs error checking after. */
+ bool
+ try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache);
+
+ mem_addr addr = 0;
+ auto len = need_save_pc_rs2_rd_mem (ival);
+ if (len <= 0)
+ return false;
+
+ return !read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
+ || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
+ }
+
+ /* Returns true if instruction is successfully recorder.
+ Else returns false. */
+ bool
+ record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache);
+
+ 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)
+ || try_save_pc_rs2_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 recorder.
+ Else returns false. */
+ bool
+ record_insn_len2 (ULONGEST ival, struct regcache *regcache,
+ struct gdbarch *gdbarch) noexcept
+ {
+ gdb_assert (regcache);
+ gdb_assert (gdbarch);
+
+ mem_addr addr = 0;
+ auto xlen = riscv_isa_xlen (gdbarch);
+
+ /* 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)
+ || (xlen == 8 && is_c_ld_insn (ival)))
+ {
+ return !save_reg (decode_crs2_short (ival))
+ || set_ordinary_record_type ();
+ }
+
+ if (is_c_fld_insn (ival) || (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) || (xlen == 8 && is_c_sd_insn (ival)))
+ {
+ int offset = EXTRACT_CLTYPE_LD_IMM (ival);
+ return !read_reg (regcache, decode_crs1_short (ival), addr)
+ || !save_mem (addr + offset, 8) || set_ordinary_record_type ();
+ }
+
+ if ((xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
+ {
+ int offset = 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 (xlen == 4 && is_c_jal_insn (ival))
+ {
+ return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
+ }
+
+ if ((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) || (xlen == 8 && is_c_subw_insn (ival))
+ || (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) || (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) || (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) || (xlen == 8 && is_c_sdsp_insn (ival)))
+ {
+ int offset = 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) || (xlen == 4 && is_c_fswsp_insn (ival)))
+ {
+ int offset = 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:
+ using regs_iter = recorded_regs::const_iterator;
+ using mems_iter = recorded_mems::const_iterator;
+
+ bool
+ record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
+ {
+ int m_length = 0;
+ auto 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, gdbarch);
+
+ /* 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;
+ }
+
+ record_type
+ get_record_type () const noexcept
+ {
+ return m_record_type;
+ }
+
+ regs_iter
+ regs_begin () const noexcept
+ {
+ return m_regs.begin ();
+ }
+
+ regs_iter
+ regs_end () const noexcept
+ {
+ return m_regs.end ();
+ }
+
+ mems_iter
+ mems_begin () const noexcept
+ {
+ return m_mems.begin ();
+ }
+
+ mems_iter
+ mems_end () const noexcept
+ {
+ return m_mems.end ();
+ }
+};
+
+static int
+riscv_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
+ const riscv_recorded_insn &insn)
+{
+ gdb_assert (gdbarch && regcache);
+
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto regs_begin = insn.regs_begin ();
+ auto regs_end = insn.regs_end ();
+ if (std::any_of (regs_begin, regs_end, [®cache] (auto &®_it) {
+ return record_full_arch_list_add_reg (regcache, reg_it);
+ }))
+ return -1;
+
+ auto mems_begin = insn.mems_begin ();
+ auto mems_end = insn.mems_end ();
+ if (std::any_of (mems_begin, mems_end, [] (auto &&mem_it) {
+ return record_full_arch_list_add_mem (mem_it.first, mem_it.second);
+ }))
+ return -1;
+
+ switch (insn.get_record_type ())
+ {
+ case riscv_recorded_insn::record_type::ORDINARY:
+ break;
+
+ case riscv_recorded_insn::record_type::ECALL:
+ {
+ if (!tdep->riscv_syscall_record)
+ {
+ warning (_ ("Syscall record is not supported"));
+ return -1;
+ }
+ ULONGEST reg_val = 0;
+ 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;
+}
+
+int
+riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+ CORE_ADDR addr)
+{
+ gdb_assert (gdbarch && regcache);
+
+ riscv_recorded_insn insn;
+ if (!insn.record (gdbarch, regcache, addr))
+ {
+ record_full_arch_list_add_end ();
+ return -1;
+ }
+
+ auto ret_val = riscv_make_record_process (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 4bdc2e7a3d5..505371ba4f6 100644
--- a/gdb/riscv-tdep.h
+++ b/gdb/riscv-tdep.h
@@ -35,6 +35,10 @@ enum
RISCV_FP_REGNUM = 8, /* Frame Pointer. */
RISCV_A0_REGNUM = 10, /* First argument. */
RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
RISCV_PC_REGNUM = 32, /* Program Counter. */
@@ -113,6 +117,11 @@ 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 +186,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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
new file mode 100644
index 00000000000..333e5d60c07
--- /dev/null
+++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python3
+# pylint: disable=invalid-name
+
+# Copyright (C) 2024 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/>.
+
+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 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"
+
+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:
+ canon_syscalls[syscall_num] = (
+ f" case {syscall_num}: return {gdb_syscall_name};\n"
+ )
+ # this is a place for corner cases
+ elif syscall_name == "mmap":
+ gdb_old_syscall_name = "gdb_old_mmap"
+ canon_syscalls[syscall_num] = f" case {syscall_num}: return {gdb_old_syscall_name};\n"
+ else:
+ canon_syscalls[syscall_num] = f" /* case {syscall_num}: return {gdb_syscall_name}; */\n"
+ 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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 410f99e3350..b048ac7078b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
- || [istarget "s390*-*-linux*"] } {
+ || [istarget "s390*-*-linux*"]
+ || [istarget "riscv*-*-*"] } {
return 1
}
--
2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* Re: [PATCH v2] gdb/riscv: Add record support for rv64gc instructions
2024-11-26 14:17 ` [PATCH v2] " Timur
@ 2024-11-27 14:23 ` Guinevere Larsen
2024-12-03 18:30 ` [PATCH v3] " Timur
0 siblings, 1 reply; 35+ messages in thread
From: Guinevere Larsen @ 2024-11-27 14:23 UTC (permalink / raw)
To: Timur, gdb-patches
On 11/26/24 11:17 AM, Timur wrote:
> Hello!
>
> Thanks for review and sorry for such late answer.
>
>> ---
>> gdb/configure.tgt | 4 +-
>> gdb/riscv-canonicalize-syscall.c | 555 ++++++++++++++
>> gdb/riscv-linux-tdep.c | 261 +++++++
>> gdb/riscv-linux-tdep.h | 29 +
>> gdb/riscv-tdep.c | 703 +++++++++++++++++-
>> gdb/riscv-tdep.h | 15 +
>> .../riscv-canonicalize-syscall-gen.py | 126 ++++
>> gdb/testsuite/lib/gdb.exp | 3 +-
>> 8 files changed, 1688 insertions(+), 8 deletions(-)
>> create mode 100644 gdb/riscv-canonicalize-syscall.c
>> create mode 100644 gdb/riscv-linux-tdep.h
>> create mode 100644 gdb/syscalls/riscv-canonicalize-syscall-gen.py
>>
>> diff --git a/gdb/configure.tgt b/gdb/configure.tgt
>> index 62df71b13fa..f965e03e84d 100644
>> --- a/gdb/configure.tgt
>> +++ b/gdb/configure.tgt
>> @@ -545,8 +545,8 @@ 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.o \
>> + glibc-tdep.o linux-tdep.o solib-svr4.o symfile-mem.o linux-record.o"
>> Please keep the indentation as it was.
> Addressed
>
>> ;;
>> riscv*-*-*)
>> diff --git a/gdb/riscv-canonicalize-syscall.c b/gdb/riscv-canonicalize-syscall.c
>> new file mode 100644
>> index 00000000000..779bbd9335c
>> --- /dev/null
>> +++ b/gdb/riscv-canonicalize-syscall.c
>> Generated files should end with the -gen suffix, so this should be
>> riscv-canonicalize-syscall-gen.c if the final version of this change
>> continues using the python script.
> As for now I decided to leave it, changed it with suffix '-gen'
>
>> @@ -0,0 +1,555 @@
>> +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py */
>> Even for generated files, the copyright blurb should be added here.
> Addressed
>
>> +
>> +#include "defs.h"
>> +#include "riscv-linux-tdep.h"
>> +
>> +enum gdb_syscall
>> +riscv64_canonicalize_syscall (int syscall)
>> +{
>> + switch (syscall)
>> + {
>> + case 0: // #define __NR_io_setup 0
>>
>> All comments should be in the form /* */, rather than //
>>
>> I would also prefer if this followed what other architectures do,
>> defining an enum to relate syscall numbers in a RISCV system to their
>> names, and then this function returning the gdb_sys enum numbers,
>> however I don't expect to be working on RISCV in the near future, so
>> feel free to ignore this comment if the other RISCV maintainers are ok
>> with this style. This would make it so no #define comment is necessary
> Decided to make a script generating function that canonicalizes RISC-V syscall.
> This solution will simplify adding record support for rv32 system. I also left
> place for corner cases in generating script as they always may exist. Also
> removed this #define comment from generating script.
I see. This makes sense.
I would like to see this explanation on the commit message, so that it
is recorded why Risc-V works different to all other arches.
>
>> + case 35: // #define __NR_unlinkat 35
>> + return gdb_sys_unlinkat;
>> + case 36: // #define __NR_symlinkat 36
>> + return gdb_sys_symlinkat;
>> + case 37: // #define __NR_linkat 37
>> + return gdb_sys_linkat;
>> + // case 39: return gdb_sys_umount2;
>>
>> I don't think these provide much help, since syscall numbers are os+cpu
>> specific, and is the whole reason why we have this function canonicalizing
>> the syscall numbers. Again, take this as only a suggestion, as I am not
>> familiar with riscv > code.
> Deleted unnecessary comments.
>
>> /* 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 00000000000..5ee2c94387f
>> --- /dev/null
>> +++ b/gdb/riscv-linux-tdep.h
>> @@ -0,0 +1,29 @@
>> +/* Copyright (C) 2024 Free Software Foundation, Inc.
>> + Contributed by Timur Golubovich
>> I think we don't use these "contributed by" lines anymore.
> Addressed
>
>> + || is_lr_w_insn (ival) || is_lr_d_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_d_w_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. Needs error checking after */
>> Comments should end in a period and 2 spaces.
> Addressed
>
>> +
>> + bool
>> + record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
>> + {
>> + int m_length = 0;
>> + auto 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, gdbarch);
>> +
>> + /* 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. */
>> Again, the indentation here is incorrect. Lines should start in the same column as the number 6.
> Addressed
>
> Best wishes,
> Timur Golubovich
>
> Timur Golubovich timurgol007@gmail.com
As I mentioned in the first review, please add some commit message
explaining a bit of what your patch is doing. I would recommend
explaining the inspiration for how riscv-tdep handles the recording (I
guess aarch64) and why you changed it; plus explaining the idea behind
the python script
> ---
> gdb/configure.tgt | 4 +-
> gdb/riscv-linux-tdep.c | 252 +++++++
> gdb/riscv-linux-tdep.h | 28 +
> gdb/riscv-tdep.c | 703 +++++++++++++++++-
> gdb/riscv-tdep.h | 15 +
> .../riscv-canonicalize-syscall-gen.py | 143 ++++
> gdb/testsuite/lib/gdb.exp | 3 +-
> 7 files changed, 1140 insertions(+), 8 deletions(-)
> create mode 100644 gdb/riscv-linux-tdep.h
> create mode 100644 gdb/syscalls/riscv-canonicalize-syscall-gen.py
>
> diff --git a/gdb/configure.tgt b/gdb/configure.tgt
> index 62df71b13fa..b350e1f6505 100644
> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt
> @@ -545,8 +545,8 @@ 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/riscv-linux-tdep.c b/gdb/riscv-linux-tdep.c
> index ff478cf4c28..12cab06f276 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,250 @@ 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;
> +
> +/* Record all registers but PC register for process-record. */
> +
> +using regnum_type = int;
> +
> +static bool
> +save_registers (struct regcache *regcache, regnum_type fir, regnum_type last)
Please fully type the variable names (fir -> first). Even if this makes
you need to use multiple lines for the function, I think this makes the
function easier to understand in the future.
Sorry about not mentioning this before, I missed it in my first pass.
> +{
> + for (regnum_type i = fir; i != last; ++i)
> + if (record_full_arch_list_add_reg (regcache, i))
> + return false;
> + return true;
> +};
> +
> +static bool
> +riscv_all_but_pc_registers_record (struct regcache *regcache)
> +{
> + auto &&gdbarch = regcache->arch ();
> + auto &&tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> + auto &&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)
> +{
> + auto 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)
> + {
> + gdb_printf (gdb_stderr,
> + _ ("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;
> + }
> +
> + auto 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. */
> +/* These values are the size of the type that will be used in a system
> + call. They are obtained from Linux Kernel source. */
> +static void
> +riscv64_linux_record_tdep_init (
> + struct gdbarch *gdbarch, struct linux_record_tdep &riscv_linux_record_tdep)
Oops, also missed this one on the first pass.
The preferred way to do multi-line arguments is:
static void
riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
struct
linux_record_tdep &riscv_linux_record_tdep)
{
(with the "struct" keywords being aligned)
> +{
> + /* Initialize the riscv_linux_record_tdep. */
> + /* These values are the size of the type that will be used in a system
> + call. They are obtained from Linux Kernel source. */
> + 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;
> + riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
> +}
> +
> /* Initialize RISC-V Linux ABI info. */
>
> static void
> @@ -205,6 +454,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 00000000000..a0d67003a81
> --- /dev/null
> +++ b/gdb/riscv-linux-tdep.h
> @@ -0,0 +1,28 @@
> +/* Copyright (C) 2024 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 932708ca4e9..c6c7b7a63fd 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
>
> @@ -1655,6 +1658,11 @@ class riscv_insn
> 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. */
> @@ -1800,11 +1808,6 @@ class riscv_insn
> 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;
>
> @@ -4415,6 +4418,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);
>
> @@ -4833,3 +4839,690 @@ this option can be used."),
> &setriscvcmdlist,
> &showriscvcmdlist);
> }
> +
> +static bool
> +try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
> +{
> + gdb_assert (regcache);
> +
> + if (regcache->raw_read<ULONGEST> (regnum, &addr)
> + != register_status::REG_VALID)
> + {
> + warning (_ ("Can not read at address %lx"), addr);
> + return false;
> + }
> + return true;
> +}
> +
> +class riscv_recorded_insn final
> +{
> +public:
> + using regnum_type = int;
> + using memory_type = std::pair<CORE_ADDR, int>;
> +
> + enum class record_type
> + {
> + UNKNOWN,
> + ORDINARY,
> +
> + /* Corner cases. */
> + ECALL,
> + EBREAK,
> + };
> +
> +private:
> + using recorded_regs = std::vector<regnum_type>;
> + using recorded_mems = std::vector<memory_type>;
> +
> + using mem_addr = decltype (std::declval<memory_type> ().first);
> + using mem_len = decltype (std::declval<memory_type> ().second);
> +
> + record_type m_record_type = record_type::UNKNOWN;
> +
> + bool m_error_occured = false;
> +
> + recorded_regs m_regs;
> + recorded_mems m_mems;
> +
> + /* 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;
> + }
> +
> + bool
> + set_ordinary_record_type () noexcept
> + {
> + m_record_type = record_type::ORDINARY;
> + return true;
> + }
> +
> + bool
> + set_error () noexcept
> + {
> + m_error_occured = true;
> + return false;
> + }
> +
> + bool
> + has_error () const noexcept
> + {
> + return m_error_occured;
> + }
> +
> + bool
> + read_reg (struct regcache *regcache, regnum_type reg,
> + ULONGEST &addr) noexcept
> + {
> + gdb_assert (regcache);
> +
> + if (!try_read (regcache, reg, addr))
> + {
> + return set_error ();
> + }
> + return true;
> + }
> +
> + bool
> + save_reg (regnum_type regnum) noexcept
> + {
> + try
> + {
> + m_regs.emplace_back (regnum);
Similar to my thoughts on Loongarch, I don't think this approach is the
best.
In short, recording is already considered very slow. The strategy this
patch uses is to first collect all the data to be recorded (which
demands dynamic allocations to std::vector), and then call
record_full_arch_list_add_reg or record_full_arch_list_add_mem, which
will do their own dynamic allocations. This seems wasteful to me.
I believe it would be better if, instead, the save_reg and save_mem
methods would directly call the correct record_full_arch_list_add
function, so that only one allocation is needed per unit of data that
needs recording.
Since aarch64 already uses the strategy in this patch I won't block
merging based on this feedback, if you're set on this idea, but I think
direct calls would be better.
> + return true;
> + }
> + catch (const std::bad_alloc &ex)
> + {
> + warning (_ ("Exception was caught during reverse execution: %s"),
> + ex.what ());
> + return set_error ();
> + }
> + }
> +
> + bool
> + save_mem (mem_addr addr, mem_len len) noexcept
> + {
> + try
> + {
> + m_mems.emplace_back (addr, len);
> + return true;
> + }
> + catch (const std::bad_alloc &ex)
> + {
> + warning (_ ("Exception was caught during reverse execution: %s"),
> + ex.what ());
> + return set_error ();
> + }
> + }
> +
> + 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 ();
> + }
> +
> + 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_ld_insn (ival) || is_lbu_insn (ival)
> + || is_lhu_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
> + || is_lw_insn (ival) || is_ld_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_insn (ival) || is_srli_insn (ival)
> + || is_srai_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_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_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_d_w_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. Needs error checking after. */
> + 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 ();
> + }
> +
> + static bool
> + need_save_pc_fprd (ULONGEST ival) noexcept
> + {
> + return 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_flw_insn (ival) || is_fld_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_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_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. Needs error checking after. */
> + 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 ();
> + }
> +
> + 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. Needs error checking after. */
> + 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 ();
> + }
> +
> + /* If return value is > 0, than instruction needs saving memory len. */
> + 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. Needs error checking after. */
> + bool
> + try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache);
> +
> + mem_addr addr = 0;
> + auto 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 ();
> + }
> +
> + /* If return value is > 0, than instruction needs saving memory len. */
> + static mem_len
> + need_save_pc_rd_mem (ULONGEST ival) noexcept
> + {
> + if (is_sc_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_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. Needs error checking after. */
> + bool
> + try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache);
> +
> + auto 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 ();
> + }
> +
> + /* If return value is > 0, than instruction needs saving memory len. */
> + static mem_len
> + need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
> + {
> + if (is_amoswap_w_insn (ival))
> + return 4;
> + if (is_amoswap_d_insn (ival))
> + return 8;
> + return 0;
> + }
> +
> + /* Returns true if instruction is classified. Needs error checking after. */
> + bool
> + try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache);
> +
> + mem_addr addr = 0;
> + auto len = need_save_pc_rs2_rd_mem (ival);
> + if (len <= 0)
> + return false;
> +
> + return !read_reg (regcache, decode_rs1 (ival), addr)
> + || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
> + || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
> + }
> +
> + /* Returns true if instruction is successfully recorder.
> + Else returns false. */
> + bool
> + record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache);
> +
> + 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)
> + || try_save_pc_rs2_rd_mem (ival, regcache))
> + {
> + return !has_error ();
> + }
Minor nit, but I noticed around here that this (and some lines later,
maybe some before) don't need the braces.
> +
> + warning (_ ("Currently this instruction with len 4(%lx) is unsupported"),
> + ival);
> + return false;
> + }
> +
> + /* Returns true if instruction is successfully recorder.
> + Else returns false. */
> + bool
> + record_insn_len2 (ULONGEST ival, struct regcache *regcache,
> + struct gdbarch *gdbarch) noexcept
> + {
> + gdb_assert (regcache);
> + gdb_assert (gdbarch);
> +
> + mem_addr addr = 0;
> + auto xlen = riscv_isa_xlen (gdbarch);
> +
> + /* 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)
> + || (xlen == 8 && is_c_ld_insn (ival)))
> + {
> + return !save_reg (decode_crs2_short (ival))
> + || set_ordinary_record_type ();
> + }
> +
> + if (is_c_fld_insn (ival) || (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) || (xlen == 8 && is_c_sd_insn (ival)))
> + {
> + int offset = EXTRACT_CLTYPE_LD_IMM (ival);
> + return !read_reg (regcache, decode_crs1_short (ival), addr)
> + || !save_mem (addr + offset, 8) || set_ordinary_record_type ();
> + }
> +
> + if ((xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
> + {
> + int offset = 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 (xlen == 4 && is_c_jal_insn (ival))
> + {
> + return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
> + }
> +
> + if ((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) || (xlen == 8 && is_c_subw_insn (ival))
> + || (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) || (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) || (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) || (xlen == 8 && is_c_sdsp_insn (ival)))
> + {
> + int offset = 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) || (xlen == 4 && is_c_fswsp_insn (ival)))
> + {
> + int offset = 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:
> + using regs_iter = recorded_regs::const_iterator;
> + using mems_iter = recorded_mems::const_iterator;
> +
> + bool
> + record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
> + {
> + int m_length = 0;
> + auto 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, gdbarch);
> +
> + /* 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;
> + }
> +
> + record_type
> + get_record_type () const noexcept
> + {
> + return m_record_type;
> + }
> +
> + regs_iter
> + regs_begin () const noexcept
> + {
> + return m_regs.begin ();
> + }
> +
> + regs_iter
> + regs_end () const noexcept
> + {
> + return m_regs.end ();
> + }
> +
> + mems_iter
> + mems_begin () const noexcept
> + {
> + return m_mems.begin ();
> + }
> +
> + mems_iter
> + mems_end () const noexcept
> + {
> + return m_mems.end ();
> + }
> +};
> +
> +static int
> +riscv_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
> + const riscv_recorded_insn &insn)
> +{
> + gdb_assert (gdbarch && regcache);
> +
> + riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> + auto regs_begin = insn.regs_begin ();
> + auto regs_end = insn.regs_end ();
> + if (std::any_of (regs_begin, regs_end, [®cache] (auto &®_it) {
> + return record_full_arch_list_add_reg (regcache, reg_it);
> + }))
> + return -1;
> +
> + auto mems_begin = insn.mems_begin ();
> + auto mems_end = insn.mems_end ();
> + if (std::any_of (mems_begin, mems_end, [] (auto &&mem_it) {
> + return record_full_arch_list_add_mem (mem_it.first, mem_it.second);
> + }))
> + return -1;
> +
> + switch (insn.get_record_type ())
> + {
> + case riscv_recorded_insn::record_type::ORDINARY:
> + break;
> +
> + case riscv_recorded_insn::record_type::ECALL:
> + {
> + if (!tdep->riscv_syscall_record)
> + {
> + warning (_ ("Syscall record is not supported"));
> + return -1;
> + }
> + ULONGEST reg_val = 0;
> + 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;
> +}
> +
> +int
> +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
> + CORE_ADDR addr)
> +{
> + gdb_assert (gdbarch && regcache);
null pointer checks should be explicit, ie, gdb_assert (gdbarch !=
nullptr && regcache != nullptr)
--
Cheers,
Guinevere Larsen
She/Her/Hers
> +
> + riscv_recorded_insn insn;
> + if (!insn.record (gdbarch, regcache, addr))
> + {
> + record_full_arch_list_add_end ();
> + return -1;
> + }
> +
> + auto ret_val = riscv_make_record_process (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 4bdc2e7a3d5..505371ba4f6 100644
> --- a/gdb/riscv-tdep.h
> +++ b/gdb/riscv-tdep.h
> @@ -35,6 +35,10 @@ enum
> RISCV_FP_REGNUM = 8, /* Frame Pointer. */
> RISCV_A0_REGNUM = 10, /* First argument. */
> RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
> RISCV_PC_REGNUM = 32, /* Program Counter. */
>
> @@ -113,6 +117,11 @@ 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 +186,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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> new file mode 100644
> index 00000000000..333e5d60c07
> --- /dev/null
> +++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> @@ -0,0 +1,143 @@
> +#!/usr/bin/env python3
> +# pylint: disable=invalid-name
> +
> +# Copyright (C) 2024 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/>.
> +
> +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 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"
> +
> +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:
> + canon_syscalls[syscall_num] = (
> + f" case {syscall_num}: return {gdb_syscall_name};\n"
> + )
> + # this is a place for corner cases
> + elif syscall_name == "mmap":
> + gdb_old_syscall_name = "gdb_old_mmap"
> + canon_syscalls[syscall_num] = f" case {syscall_num}: return {gdb_old_syscall_name};\n"
> + else:
> + canon_syscalls[syscall_num] = f" /* case {syscall_num}: return {gdb_syscall_name}; */\n"
> + 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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 410f99e3350..b048ac7078b 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -3718,7 +3718,8 @@ proc supports_reverse {} {
> || [istarget "i\[34567\]86-*-linux*"]
> || [istarget "aarch64*-*-linux*"]
> || [istarget "powerpc*-*-linux*"]
> - || [istarget "s390*-*-linux*"] } {
> + || [istarget "s390*-*-linux*"]
> + || [istarget "riscv*-*-*"] } {
> return 1
> }
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 35+ messages in thread* [PATCH v3] gdb/riscv: Add record support for rv64gc instructions
2024-11-27 14:23 ` Guinevere Larsen
@ 2024-12-03 18:30 ` Timur
2024-12-13 18:10 ` Guinevere Larsen
0 siblings, 1 reply; 35+ messages in thread
From: Timur @ 2024-12-03 18:30 UTC (permalink / raw)
To: gdb-patches; +Cc: Timur
This commit adds record full support for rv64gc instrcution.
It includes changes to the following files:
- gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
syscalls.
- gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
rv64gc instructions.
- gdb/configure.tgt: adds new files for compilation.
- gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
targets.
- gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate function
that canonicalizes RISC-V syscall. This script can simply support for syscalls
on rv32 system. To use this script you need to pass a path to a file with
syscalls description from riscv-glibc (example is in the help message). The
script produces a mapping from syscall names to gdb_syscall enum.
- gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
Timur Golubovich timurgol007@gmail.com
Hi!
> >> + switch (syscall)
> >> + {
> >> + case 0: // #define __NR_io_setup 0
> >>
> >> All comments should be in the form /* */, rather than //
> >>
>>> I would also prefer if this followed what other architectures do,
>>> defining an enum to relate syscall numbers in a RISCV system to their
>>> names, and then this function returning the gdb_sys enum numbers,
>>> however I don't expect to be working on RISCV in the near future, so
>>> feel free to ignore this comment if the other RISCV maintainers are ok
>>> with this style. This would make it so no #define comment is necessary
>> Decided to make a script generating function that canonicalizes RISC-V syscall.
>> This solution will simplify adding record support for rv32 system. I also left
>> place for corner cases in generating script as they always may exist. Also
>> removed this #define comment from generating script.
>
> I see. This makes sense.
>
> I would like to see this explanation on the commit message, so that it
> is recorded why Risc-V works different to all other arches.
Could you please double-check the commit message? I tried to make it more
detailed. Do you think I need to provide additional information?
>>> +
>>> + if (m_length == 2)
>>> + return record_insn_len2 (ival, regcache, gdbarch);
>>> +
>>> + /* 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. */
>>> Again, the indentation here is incorrect. Lines should start in the same column as the number 6.
>> Addressed
>>
>> Best wishes,
>> Timur Golubovich
>>
>> Timur Golubovich timurgol007@gmail.com
>
> As I mentioned in the first review, please add some commit message
> explaining a bit of what your patch is doing. I would recommend
> explaining the inspiration for how riscv-tdep handles the recording (I
> guess aarch64) and why you changed it; plus explaining the idea behind
> the python script.c
Added this information to the commit message.
>> /* The following value is derived from __NR_rt_sigreturn in
>> <include/uapi/asm-generic/unistd.h> from the Linux source tree. */
>> @@ -173,6 +178,250 @@ 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;
>> +
>> +/* Record all registers but PC register for process-record. */
>> +
>> +using regnum_type = int;
>> +
>> +static bool
>> +save_registers (struct regcache *regcache, regnum_type fir, regnum_type last)
>
> Please fully type the variable names (fir -> first). Even if this makes
> you need to use multiple lines for the function, I think this makes the
> function easier to understand in the future.
>
> Sorry about not mentioning this before, I missed it in my first pass.
Addressed
>> + if (syscall_gdb == gdb_sys_sigreturn || syscall_gdb == gdb_sys_rt_sigreturn)
>> + {
>> + if (!riscv_all_but_pc_registers_record (regcache))
>> + return -1;
>> + return 0;
>> + }
>> +
>> + auto 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. */
>> +/* These values are the size of the type that will be used in a system
>> + call. They are obtained from Linux Kernel source. */
>> +static void
>> +riscv64_linux_record_tdep_init (
>> + struct gdbarch *gdbarch, struct linux_record_tdep &riscv_linux_record_tdep)
>
> Oops, also missed this one on the first pass.
>
> The preferred way to do multi-line arguments is:
>
> static void
> riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
> struct
> linux_record_tdep &riscv_linux_record_tdep)
> {
>
> (with the "struct" keywords being aligned)
Addressed
>> + {
>> + gdb_assert (regcache);
>> +
>> + if (!try_read (regcache, reg, addr))
>> + {
>> + return set_error ();
>> + }
>> + return true;
>> + }
>> +
>> + bool
>> + save_reg (regnum_type regnum) noexcept
>> + {
>> + try
>> + {
>> + m_regs.emplace_back (regnum);
>
> Similar to my thoughts on Loongarch, I don't think this approach is the
> best.
>
> In short, recording is already considered very slow. The strategy this
> patch uses is to first collect all the data to be recorded (which
> demands dynamic allocations to std::vector), and then call
> record_full_arch_list_add_reg or record_full_arch_list_add_mem, which
> will do their own dynamic allocations. This seems wasteful to me.
>
> I believe it would be better if, instead, the save_reg and save_mem
> methods would directly call the correct record_full_arch_list_add
> function, so that only one allocation is needed per unit of data that
> needs recording.
>
> Since aarch64 already uses the strategy in this patch I won't block
> merging based on this feedback, if you're set on this idea, but I think
> direct calls would be better.
I think that this overhead is negligeble, since the number of allocation
because most instructions affect only 1 register. Moreover, I feel that the
suggested approach simplifies error handling, since I can decouple decoding
procedure from the actual error detection. If you don't mind, I would suggest
to leave it as it is.
>> + 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)
>> + || try_save_pc_rs2_rd_mem (ival, regcache))
>> + {
>> + return !has_error ();
>> + }
> Minor nit, but I noticed around here that this (and some lines later,
> maybe some before) don't need the braces.
Addressed
>> + 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;
>> +}
>> +
>> +int
>> +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
>> + CORE_ADDR addr)
>> +{
>> + gdb_assert (gdbarch && regcache);
> null pointer checks should be explicit, ie, gdb_assert (gdbarch !=
> nullptr && regcache != nullptr)
Addressed.
Best wishes,
Timur Golubovich
---
gdb/config.lt | 1484 ++++++++++++++++++++++++++
gdb/riscv-canonicalize-syscall-gen.c | 338 ++++++
gdb/riscv-linux-tdep.c | 22 +-
gdb/riscv-tdep.c | 141 +--
4 files changed, 1888 insertions(+), 97 deletions(-)
create mode 100755 gdb/config.lt
create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
diff --git a/gdb/config.lt b/gdb/config.lt
new file mode 100755
index 00000000000..96851a9bfaf
--- /dev/null
+++ b/gdb/config.lt
@@ -0,0 +1,1484 @@
+#! /bin/bash
+# Generated by configure.
+# Run this file to recreate a libtool stub with the current configuration.
+SHELL=${CONFIG_SHELL-/bin/bash}
+export SHELL
+## -------------------- ##
+## M4sh Initialization. ##
+## -------------------- ##
+
+# Be more Bourne compatible
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+ emulate sh
+ NULLCMD=:
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
+ # is contrary to our usage. Disable this feature.
+ alias -g '${1+"$@"}'='"$@"'
+ setopt NO_GLOB_SUBST
+else
+ case `(set -o) 2>/dev/null` in #(
+ *posix*) :
+ set -o posix ;; #(
+ *) :
+ ;;
+esac
+fi
+
+
+as_nl='
+'
+export as_nl
+# Printing a long string crashes Solaris 7 /usr/bin/printf.
+as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
+as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
+# Prefer a ksh shell builtin over an external printf program on Solaris,
+# but without wasting forks for bash or zsh.
+if test -z "$BASH_VERSION$ZSH_VERSION" \
+ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='print -r --'
+ as_echo_n='print -rn --'
+elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
+ as_echo='printf %s\n'
+ as_echo_n='printf %s'
+else
+ if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
+ as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
+ as_echo_n='/usr/ucb/echo -n'
+ else
+ as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
+ as_echo_n_body='eval
+ arg=$1;
+ case $arg in #(
+ *"$as_nl"*)
+ expr "X$arg" : "X\\(.*\\)$as_nl";
+ arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
+ esac;
+ expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
+ '
+ export as_echo_n_body
+ as_echo_n='sh -c $as_echo_n_body as_echo'
+ fi
+ export as_echo_body
+ as_echo='sh -c $as_echo_body as_echo'
+fi
+
+# The user is always right.
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
+
+
+# IFS
+# We need space, tab and new line, in precisely that order. Quoting is
+# there to prevent editors from complaining about space-tab.
+# (If _AS_PATH_WALK were called with IFS unset, it would disable word
+# splitting by setting IFS to empty value.)
+IFS=" "" $as_nl"
+
+# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
+case $0 in #((
+ *[\\/]* ) as_myself=$0 ;;
+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ done
+IFS=$as_save_IFS
+
+ ;;
+esac
+# We did not find ourselves, most probably we were run as `sh COMMAND'
+# in which case we are not to be found in the path.
+if test "x$as_myself" = x; then
+ as_myself=$0
+fi
+if test ! -f "$as_myself"; then
+ $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ exit 1
+fi
+
+# Unset variables that we do not need and which cause bugs (e.g. in
+# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
+# suppresses any "Segmentation fault" message there. '((' could
+# trigger a bug in pdksh 5.2.14.
+for as_var in BASH_ENV ENV MAIL MAILPATH
+do eval test x\${$as_var+set} = xset \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# NLS nuisances.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
+# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
+# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
+# script with STATUS, using 1 if that was 0.
+as_fn_error ()
+{
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ fi
+ $as_echo "$as_me: error: $2" >&2
+ as_fn_exit $as_status
+} # as_fn_error
+
+
+# as_fn_set_status STATUS
+# -----------------------
+# Set $? to STATUS, without forking.
+as_fn_set_status ()
+{
+ return $1
+} # as_fn_set_status
+
+# as_fn_exit STATUS
+# -----------------
+# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
+as_fn_exit ()
+{
+ set +e
+ as_fn_set_status $1
+ exit $1
+} # as_fn_exit
+
+# as_fn_unset VAR
+# ---------------
+# Portably unset VAR.
+as_fn_unset ()
+{
+ { eval $1=; unset $1;}
+}
+as_unset=as_fn_unset
+# as_fn_append VAR VALUE
+# ----------------------
+# Append the text in VALUE to the end of the definition contained in VAR. Take
+# advantage of any shell optimizations that allow amortized linear growth over
+# repeated appends, instead of the typical quadratic growth present in naive
+# implementations.
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+ eval 'as_fn_append ()
+ {
+ eval $1+=\$2
+ }'
+else
+ as_fn_append ()
+ {
+ eval $1=\$$1\$2
+ }
+fi # as_fn_append
+
+# as_fn_arith ARG...
+# ------------------
+# Perform arithmetic evaluation on the ARGs, and store the result in the
+# global $as_val. Take advantage of shells that can avoid forks. The arguments
+# must be portable across $(()) and expr.
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+ eval 'as_fn_arith ()
+ {
+ as_val=$(( $* ))
+ }'
+else
+ as_fn_arith ()
+ {
+ as_val=`expr "$@" || test $? -eq 1`
+ }
+fi # as_fn_arith
+
+
+if expr a : '\(a\)' >/dev/null 2>&1 &&
+ test "X`expr 00001 : '.*\(...\)'`" = X001; then
+ as_expr=expr
+else
+ as_expr=false
+fi
+
+if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
+ as_basename=basename
+else
+ as_basename=false
+fi
+
+if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
+ as_dirname=dirname
+else
+ as_dirname=false
+fi
+
+as_me=`$as_basename -- "$0" ||
+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
+ X"$0" : 'X\(//\)$' \| \
+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X/"$0" |
+ sed '/^.*\/\([^/][^/]*\)\/*$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\/\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+
+# Avoid depending upon Character Ranges.
+as_cr_letters='abcdefghijklmnopqrstuvwxyz'
+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
+as_cr_Letters=$as_cr_letters$as_cr_LETTERS
+as_cr_digits='0123456789'
+as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+ECHO_C= ECHO_N= ECHO_T=
+case `echo -n x` in #(((((
+-n*)
+ case `echo 'xy\c'` in
+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.
+ xy) ECHO_C='\c';;
+ *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
+ ECHO_T=' ';;
+ esac;;
+*)
+ ECHO_N='-n';;
+esac
+
+rm -f conf$$ conf$$.exe conf$$.file
+if test -d conf$$.dir; then
+ rm -f conf$$.dir/conf$$.file
+else
+ rm -f conf$$.dir
+ mkdir conf$$.dir 2>/dev/null
+fi
+if (echo >conf$$.file) 2>/dev/null; then
+ if ln -s conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s='ln -s'
+ # ... but there are two gotchas:
+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
+ # In both cases, we have to default to `cp -pR'.
+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
+ as_ln_s='cp -pR'
+ elif ln conf$$.file conf$$ 2>/dev/null; then
+ as_ln_s=ln
+ else
+ as_ln_s='cp -pR'
+ fi
+else
+ as_ln_s='cp -pR'
+fi
+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
+rmdir conf$$.dir 2>/dev/null
+
+
+# as_fn_mkdir_p
+# -------------
+# Create "$as_dir" as a directory, including parents if necessary.
+as_fn_mkdir_p ()
+{
+
+ case $as_dir in #(
+ -*) as_dir=./$as_dir;;
+ esac
+ test -d "$as_dir" || eval $as_mkdir_p || {
+ as_dirs=
+ while :; do
+ case $as_dir in #(
+ *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *) as_qdir=$as_dir;;
+ esac
+ as_dirs="'$as_qdir' $as_dirs"
+ as_dir=`$as_dirname -- "$as_dir" ||
+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
+ X"$as_dir" : 'X\(//\)[^/]' \| \
+ X"$as_dir" : 'X\(//\)$' \| \
+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
+$as_echo X"$as_dir" |
+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)[^/].*/{
+ s//\1/
+ q
+ }
+ /^X\(\/\/\)$/{
+ s//\1/
+ q
+ }
+ /^X\(\/\).*/{
+ s//\1/
+ q
+ }
+ s/.*/./; q'`
+ test -d "$as_dir" && break
+ done
+ test -z "$as_dirs" || eval "mkdir $as_dirs"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
+
+
+} # as_fn_mkdir_p
+if mkdir -p . 2>/dev/null; then
+ as_mkdir_p='mkdir -p "$as_dir"'
+else
+ test -d ./-p && rmdir ./-p
+ as_mkdir_p=false
+fi
+
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
+
+# Sed expression to map a string onto a valid CPP name.
+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+
+# Sed expression to map a string onto a valid variable name.
+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+
+
+exec 6>&1
+## --------------------------------- ##
+## Main body of "$CONFIG_LT" script. ##
+## --------------------------------- ##
+lt_cl_silent=false
+exec 5>>config.log
+{
+ echo
+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
+## Running $as_me. ##
+_ASBOX
+} >&5
+
+lt_cl_help="\
+\`$as_me' creates a local libtool stub from the current configuration,
+for use in further configure time tests before the real libtool is
+generated.
+
+Usage: $0 [OPTIONS]
+
+ -h, --help print this help, then exit
+ -V, --version print version number, then exit
+ -q, --quiet do not print progress messages
+ -d, --debug don't remove temporary files
+
+Report bugs to <bug-libtool@gnu.org>."
+
+lt_cl_version="\
+config.lt
+configured by $0, generated by GNU Autoconf 2.69.
+
+Copyright (C) 2009 Free Software Foundation, Inc.
+This config.lt script is free software; the Free Software Foundation
+gives unlimited permision to copy, distribute and modify it."
+
+while test $# != 0
+do
+ case $1 in
+ --version | --v* | -V )
+ echo "$lt_cl_version"; exit 0 ;;
+ --help | --h* | -h )
+ echo "$lt_cl_help"; exit 0 ;;
+ --debug | --d* | -d )
+ debug=: ;;
+ --quiet | --q* | --silent | --s* | -q )
+ lt_cl_silent=: ;;
+
+ -*) as_fn_error $? "unrecognized option: $1
+Try \`$0 --help' for more information." "$LINENO" 5 ;;
+
+ *) as_fn_error $? "unrecognized argument: $1
+Try \`$0 --help' for more information." "$LINENO" 5 ;;
+ esac
+ shift
+done
+
+if $lt_cl_silent; then
+ exec 6>/dev/null
+fi
+
+
+# The HP-UX ksh and POSIX shell print the target directory to stdout
+# if CDPATH is set.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+double_quote_subst='s/\(["`\\]\)/\\\1/g'
+delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
+macro_version='2.2.7a'
+macro_revision='1.3134'
+enable_shared='yes'
+enable_static='yes'
+pic_mode='default'
+enable_fast_install='needless'
+SHELL='/bin/bash'
+ECHO='printf %s\n'
+host_alias='x86_64-pc-linux-gnu'
+host='x86_64-pc-linux-gnu'
+host_os='linux-gnu'
+build_alias='x86_64-pc-linux-gnu'
+build='x86_64-pc-linux-gnu'
+build_os='linux-gnu'
+SED='/usr/bin/sed'
+Xsed='/usr/bin/sed -e 1s/^X//'
+GREP='/usr/bin/grep'
+EGREP='/usr/bin/grep -E'
+FGREP='/usr/bin/grep -F'
+LD='ld -m elf_x86_64'
+NM='/usr/bin/nm -B'
+LN_S='ln -s'
+max_cmd_len='1572864'
+ac_objext='o'
+exeext=''
+lt_unset='unset'
+lt_SP2NL='tr \040 \012'
+lt_NL2SP='tr \015\012 \040\040'
+reload_flag=' -r'
+reload_cmds='$LD$reload_flag -o $output$reload_objs'
+OBJDUMP='objdump'
+deplibs_check_method='pass_all'
+file_magic_cmd='$MAGIC_CMD'
+AR='ar --plugin /usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so --plugin /usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so'
+AR_FLAGS='rc'
+STRIP='strip'
+RANLIB='ranlib --plugin /usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so --plugin /usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so'
+old_postinstall_cmds='chmod 644 $oldlib~$RANLIB $oldlib'
+old_postuninstall_cmds=''
+old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs~$RANLIB $oldlib'
+lock_old_archive_extraction='no'
+CC='gcc'
+CFLAGS='-g -O2 '
+compiler='g++'
+GCC='yes'
+lt_cv_sys_global_symbol_pipe='sed -n -e '\''s/^.*[ ]\([ABCDGIRSTW][ABCDGIRSTW]*\)[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$/\1 \2 \2/p'\'''
+lt_cv_sys_global_symbol_to_cdecl='sed -n -e '\''s/^T .* \(.*\)$/extern int \1();/p'\'' -e '\''s/^[ABCDGIRSTW]* .* \(.*\)$/extern char \1;/p'\'''
+lt_cv_sys_global_symbol_to_c_name_address='sed -n -e '\''s/^: \([^ ]*\) $/ {\"\1\", (void *) 0},/p'\'' -e '\''s/^[ABCDGIRSTW]* \([^ ]*\) \([^ ]*\)$/ {"\2", (void *) \&\2},/p'\'''
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='sed -n -e '\''s/^: \([^ ]*\) $/ {\"\1\", (void *) 0},/p'\'' -e '\''s/^[ABCDGIRSTW]* \([^ ]*\) \(lib[^ ]*\)$/ {"\2", (void *) \&\2},/p'\'' -e '\''s/^[ABCDGIRSTW]* \([^ ]*\) \([^ ]*\)$/ {"lib\2", (void *) \&\2},/p'\'''
+objdir='.libs'
+MAGIC_CMD='file'
+lt_prog_compiler_no_builtin_flag=' -fno-builtin'
+lt_prog_compiler_wl='-Wl,'
+lt_prog_compiler_pic=' -fPIC -DPIC'
+lt_prog_compiler_static='-static'
+lt_cv_prog_compiler_c_o='yes'
+need_locks='no'
+DSYMUTIL='dsymutil'
+NMEDIT=''
+LIPO='lipo'
+OTOOL='otool'
+OTOOL64=''
+libext='a'
+shrext_cmds='.so'
+extract_expsyms_cmds=''
+archive_cmds_need_lc='no'
+enable_shared_with_static_runtimes='no'
+export_dynamic_flag_spec='${wl}--export-dynamic'
+whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+compiler_needs_object='no'
+old_archive_from_new_cmds=''
+old_archive_from_expsyms_cmds=''
+archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+module_cmds=''
+module_expsym_cmds=''
+with_gnu_ld='yes'
+allow_undefined_flag=''
+no_undefined_flag=''
+hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+hardcode_libdir_flag_spec_ld=''
+hardcode_libdir_separator=''
+hardcode_direct='no'
+hardcode_direct_absolute='no'
+hardcode_minus_L='no'
+hardcode_shlibpath_var='unsupported'
+hardcode_automatic='no'
+inherit_rpath='no'
+link_all_deplibs='unknown'
+fix_srcfile_path=''
+always_export_symbols='no'
+export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+include_expsyms=''
+prelink_cmds=''
+file_list_spec=''
+variables_saved_for_relink='PATH LD_LIBRARY_PATH LD_RUN_PATH GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH'
+need_lib_prefix='no'
+need_version='no'
+version_type='linux'
+runpath_var='LD_RUN_PATH'
+shlibpath_var='LD_LIBRARY_PATH'
+shlibpath_overrides_runpath='yes'
+libname_spec='lib$name'
+library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+soname_spec='${libname}${release}${shared_ext}$major'
+install_override_mode=''
+postinstall_cmds=''
+postuninstall_cmds=''
+finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
+finish_eval=''
+hardcode_into_libs='yes'
+sys_lib_search_path_spec='/usr/lib/gcc/x86_64-linux-gnu/11 /usr/lib/x86_64-linux-gnu /usr/lib /lib/x86_64-linux-gnu /lib '
+sys_lib_dlsearch_path_spec='/lib /usr/lib /usr/lib/x86_64-linux-gnu/libfakeroot /usr/local/lib /usr/local/lib/x86_64-linux-gnu /lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu /lib32 /usr/lib32 '
+hardcode_action='immediate'
+enable_dlopen='unknown'
+enable_dlopen_self='unknown'
+enable_dlopen_self_static='unknown'
+old_striplib='strip --strip-debug'
+striplib='strip --strip-unneeded'
+compiler_lib_search_dirs=''
+predep_objects=''
+postdep_objects=''
+predeps=''
+postdeps=''
+compiler_lib_search_path=''
+LD_CXX='ld -m elf_x86_64'
+reload_flag_CXX=' -r'
+reload_cmds_CXX='$LD$reload_flag -o $output$reload_objs'
+old_archive_cmds_CXX='$AR $AR_FLAGS $oldlib$oldobjs~$RANLIB $oldlib'
+compiler_CXX='g++'
+GCC_CXX='yes'
+lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin'
+lt_prog_compiler_wl_CXX='-Wl,'
+lt_prog_compiler_pic_CXX=' -fPIC -DPIC'
+lt_prog_compiler_static_CXX='-static'
+lt_cv_prog_compiler_c_o_CXX='yes'
+archive_cmds_need_lc_CXX='no'
+enable_shared_with_static_runtimes_CXX='no'
+export_dynamic_flag_spec_CXX='${wl}--export-dynamic'
+whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+compiler_needs_object_CXX='no'
+old_archive_from_new_cmds_CXX=''
+old_archive_from_expsyms_cmds_CXX=''
+archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
+archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+module_cmds_CXX=''
+module_expsym_cmds_CXX=''
+with_gnu_ld_CXX='yes'
+allow_undefined_flag_CXX=''
+no_undefined_flag_CXX=''
+hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir'
+hardcode_libdir_flag_spec_ld_CXX=''
+hardcode_libdir_separator_CXX=''
+hardcode_direct_CXX='no'
+hardcode_direct_absolute_CXX='no'
+hardcode_minus_L_CXX='no'
+hardcode_shlibpath_var_CXX='unsupported'
+hardcode_automatic_CXX='no'
+inherit_rpath_CXX='no'
+link_all_deplibs_CXX='unknown'
+fix_srcfile_path_CXX=''
+always_export_symbols_CXX='no'
+export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
+exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
+include_expsyms_CXX=''
+prelink_cmds_CXX=''
+file_list_spec_CXX=''
+hardcode_action_CXX='immediate'
+compiler_lib_search_dirs_CXX='/usr/lib/gcc/x86_64-linux-gnu/11 /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu /usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib /lib/x86_64-linux-gnu /lib/../lib /usr/lib/x86_64-linux-gnu /usr/lib/../lib /usr/lib/gcc/x86_64-linux-gnu/11/../../..'
+predep_objects_CXX='/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/11/crtbeginS.o'
+postdep_objects_CXX='/usr/lib/gcc/x86_64-linux-gnu/11/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crtn.o'
+predeps_CXX=''
+postdeps_CXX='-lstdc++ -lm -lgcc_s -lc -lgcc_s'
+compiler_lib_search_path_CXX='-L/usr/lib/gcc/x86_64-linux-gnu/11 -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/11/../../..'
+
+LTCC='gcc'
+LTCFLAGS='-g -O2 '
+compiler='gcc'
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+
+# Quote evaled strings.
+for var in SHELL ECHO SED GREP EGREP FGREP LD NM LN_S lt_SP2NL lt_NL2SP reload_flag OBJDUMP deplibs_check_method file_magic_cmd AR AR_FLAGS STRIP RANLIB CC CFLAGS compiler lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl lt_cv_sys_global_symbol_to_c_name_address lt_cv_sys_global_symbol_to_c_name_address_lib_prefix lt_prog_compiler_no_builtin_flag lt_prog_compiler_wl lt_prog_compiler_pic lt_prog_compiler_static lt_cv_prog_compiler_c_o need_locks DSYMUTIL NMEDIT LIPO OTOOL OTOOL64 shrext_cmds export_dynamic_flag_spec whole_archive_flag_spec compiler_needs_object with_gnu_ld allow_undefined_flag no_undefined_flag hardcode_libdir_flag_spec hardcode_libdir_flag_spec_ld hardcode_libdir_separator fix_srcfile_path exclude_expsyms include_expsyms file_list_spec variables_saved_for_relink libname_spec library_names_spec soname_spec install_override_mode finish_eval old_striplib striplib compiler_lib_search_dirs predep_objects postdep_objects predeps postdeps compiler_lib_search_path LD_CXX reload_flag_CXX compiler_CXX lt_prog_compiler_no_builtin_flag_CXX lt_prog_compiler_wl_CXX lt_prog_compiler_pic_CXX lt_prog_compiler_static_CXX lt_cv_prog_compiler_c_o_CXX export_dynamic_flag_spec_CXX whole_archive_flag_spec_CXX compiler_needs_object_CXX with_gnu_ld_CXX allow_undefined_flag_CXX no_undefined_flag_CXX hardcode_libdir_flag_spec_CXX hardcode_libdir_flag_spec_ld_CXX hardcode_libdir_separator_CXX fix_srcfile_path_CXX exclude_expsyms_CXX include_expsyms_CXX file_list_spec_CXX compiler_lib_search_dirs_CXX predep_objects_CXX postdep_objects_CXX predeps_CXX postdeps_CXX compiler_lib_search_path_CXX; do
+ case `eval \\$ECHO \\""\\$$var"\\"` in
+ *[\\\`\"\$]*)
+ eval "lt_$var=\\\"\`\$ECHO \"\$$var\" | \$SED \"\$sed_quote_subst\"\`\\\""
+ ;;
+ *)
+ eval "lt_$var=\\\"\$$var\\\""
+ ;;
+ esac
+done
+
+# Double-quote double-evaled strings.
+for var in reload_cmds old_postinstall_cmds old_postuninstall_cmds old_archive_cmds extract_expsyms_cmds old_archive_from_new_cmds old_archive_from_expsyms_cmds archive_cmds archive_expsym_cmds module_cmds module_expsym_cmds export_symbols_cmds prelink_cmds postinstall_cmds postuninstall_cmds finish_cmds sys_lib_search_path_spec sys_lib_dlsearch_path_spec reload_cmds_CXX old_archive_cmds_CXX old_archive_from_new_cmds_CXX old_archive_from_expsyms_cmds_CXX archive_cmds_CXX archive_expsym_cmds_CXX module_cmds_CXX module_expsym_cmds_CXX export_symbols_cmds_CXX prelink_cmds_CXX; do
+ case `eval \\$ECHO \\""\\$$var"\\"` in
+ *[\\\`\"\$]*)
+ eval "lt_$var=\\\"\`\$ECHO \"\$$var\" | \$SED -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\""
+ ;;
+ *)
+ eval "lt_$var=\\\"\$$var\\\""
+ ;;
+ esac
+done
+
+ac_aux_dir='..'
+xsi_shell='yes'
+lt_shell_append='yes'
+
+# See if we are running on zsh, and set the options which allow our
+# commands through without removal of \ escapes INIT.
+if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+fi
+
+
+ PACKAGE=''
+ VERSION=''
+ TIMESTAMP=''
+ RM='rm -f'
+ ofile='libtool'
+
+
+
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $ofile" >&5
+$as_echo "$as_me: creating $ofile" >&6;}
+
+
+ # See if we are running on zsh, and set the options which allow our
+ # commands through without removal of \ escapes.
+ if test -n "${ZSH_VERSION+set}" ; then
+ setopt NO_GLOB_SUBST
+ fi
+
+ cfgfile="${ofile}T"
+ trap "$RM \"$cfgfile\"; exit 1" 1 2 15
+ $RM "$cfgfile"
+
+ cat <<_LT_EOF >> "$cfgfile"
+#! $SHELL
+
+# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
+# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+#
+# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
+# 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+# Written by Gordon Matzigkeit, 1996
+#
+# This file is part of GNU Libtool.
+#
+# GNU Libtool 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 2 of
+# the License, or (at your option) any later version.
+#
+# As a special exception to the GNU General Public License,
+# if you distribute this file as part of a program or library that
+# is built using GNU Libtool, you may include this file under the
+# same distribution terms that you use for the rest of that program.
+#
+# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy
+# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
+# obtained by writing to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+# The names of the tagged configurations supported by this script.
+available_tags="CXX "
+
+# ### BEGIN LIBTOOL CONFIG
+
+# Which release of libtool.m4 was used?
+macro_version=$macro_version
+macro_revision=$macro_revision
+
+# Whether or not to build shared libraries.
+build_libtool_libs=$enable_shared
+
+# Whether or not to build static libraries.
+build_old_libs=$enable_static
+
+# What type of objects to build.
+pic_mode=$pic_mode
+
+# Whether or not to optimize for fast installation.
+fast_install=$enable_fast_install
+
+# Shell to use when invoking shell scripts.
+SHELL=$lt_SHELL
+
+# An echo program that protects backslashes.
+ECHO=$lt_ECHO
+
+# The host system.
+host_alias=$host_alias
+host=$host
+host_os=$host_os
+
+# The build system.
+build_alias=$build_alias
+build=$build
+build_os=$build_os
+
+# A sed program that does not truncate output.
+SED=$lt_SED
+
+# Sed that helps us avoid accidentally triggering echo(1) options like -n.
+Xsed="\$SED -e 1s/^X//"
+
+# A grep program that handles long lines.
+GREP=$lt_GREP
+
+# An ERE matcher.
+EGREP=$lt_EGREP
+
+# A literal string matcher.
+FGREP=$lt_FGREP
+
+# A BSD- or MS-compatible name lister.
+NM=$lt_NM
+
+# Whether we need soft or hard links.
+LN_S=$lt_LN_S
+
+# What is the maximum length of a command?
+max_cmd_len=$max_cmd_len
+
+# Object file suffix (normally "o").
+objext=$ac_objext
+
+# Executable file suffix (normally "").
+exeext=$exeext
+
+# whether the shell understands "unset".
+lt_unset=$lt_unset
+
+# turn spaces into newlines.
+SP2NL=$lt_lt_SP2NL
+
+# turn newlines into spaces.
+NL2SP=$lt_lt_NL2SP
+
+# An object symbol dumper.
+OBJDUMP=$lt_OBJDUMP
+
+# Method to check whether dependent libraries are shared objects.
+deplibs_check_method=$lt_deplibs_check_method
+
+# Command to use when deplibs_check_method == "file_magic".
+file_magic_cmd=$lt_file_magic_cmd
+
+# The archiver.
+AR=$lt_AR
+AR_FLAGS=$lt_AR_FLAGS
+
+# A symbol stripping program.
+STRIP=$lt_STRIP
+
+# Commands used to install an old-style archive.
+RANLIB=$lt_RANLIB
+old_postinstall_cmds=$lt_old_postinstall_cmds
+old_postuninstall_cmds=$lt_old_postuninstall_cmds
+
+# Whether to use a lock for old archive extraction.
+lock_old_archive_extraction=$lock_old_archive_extraction
+
+# A C compiler.
+LTCC=$lt_CC
+
+# LTCC compiler flags.
+LTCFLAGS=$lt_CFLAGS
+
+# Take the output of nm and produce a listing of raw symbols and C names.
+global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
+
+# Transform the output of nm in a proper C declaration.
+global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+
+# Transform the output of nm in a C name address pair.
+global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
+
+# Transform the output of nm in a C name address pair when lib prefix is needed.
+global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+
+# The name of the directory that contains temporary libtool files.
+objdir=$objdir
+
+# Used to examine libraries when file_magic_cmd begins with "file".
+MAGIC_CMD=$MAGIC_CMD
+
+# Must we lock files when doing compilation?
+need_locks=$lt_need_locks
+
+# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
+DSYMUTIL=$lt_DSYMUTIL
+
+# Tool to change global to local symbols on Mac OS X.
+NMEDIT=$lt_NMEDIT
+
+# Tool to manipulate fat objects and archives on Mac OS X.
+LIPO=$lt_LIPO
+
+# ldd/readelf like tool for Mach-O binaries on Mac OS X.
+OTOOL=$lt_OTOOL
+
+# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
+OTOOL64=$lt_OTOOL64
+
+# Old archive suffix (normally "a").
+libext=$libext
+
+# Shared library suffix (normally ".so").
+shrext_cmds=$lt_shrext_cmds
+
+# The commands to extract the exported symbol list from a shared archive.
+extract_expsyms_cmds=$lt_extract_expsyms_cmds
+
+# Variables whose values should be saved in libtool wrapper scripts and
+# restored at link time.
+variables_saved_for_relink=$lt_variables_saved_for_relink
+
+# Do we need the "lib" prefix for modules?
+need_lib_prefix=$need_lib_prefix
+
+# Do we need a version for libraries?
+need_version=$need_version
+
+# Library versioning type.
+version_type=$version_type
+
+# Shared library runtime path variable.
+runpath_var=$runpath_var
+
+# Shared library path variable.
+shlibpath_var=$shlibpath_var
+
+# Is shlibpath searched before the hard-coded library search path?
+shlibpath_overrides_runpath=$shlibpath_overrides_runpath
+
+# Format of library name prefix.
+libname_spec=$lt_libname_spec
+
+# List of archive names. First name is the real one, the rest are links.
+# The last name is the one that the linker finds with -lNAME
+library_names_spec=$lt_library_names_spec
+
+# The coded name of the library, if different from the real name.
+soname_spec=$lt_soname_spec
+
+# Permission mode override for installation of shared libraries.
+install_override_mode=$lt_install_override_mode
+
+# Command to use after installation of a shared archive.
+postinstall_cmds=$lt_postinstall_cmds
+
+# Command to use after uninstallation of a shared archive.
+postuninstall_cmds=$lt_postuninstall_cmds
+
+# Commands used to finish a libtool library installation in a directory.
+finish_cmds=$lt_finish_cmds
+
+# As "finish_cmds", except a single script fragment to be evaled but
+# not shown.
+finish_eval=$lt_finish_eval
+
+# Whether we should hardcode library paths into libraries.
+hardcode_into_libs=$hardcode_into_libs
+
+# Compile-time system search path for libraries.
+sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
+
+# Run-time system search path for libraries.
+sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+
+# Whether dlopen is supported.
+dlopen_support=$enable_dlopen
+
+# Whether dlopen of programs is supported.
+dlopen_self=$enable_dlopen_self
+
+# Whether dlopen of statically linked programs is supported.
+dlopen_self_static=$enable_dlopen_self_static
+
+# Commands to strip libraries.
+old_striplib=$lt_old_striplib
+striplib=$lt_striplib
+
+
+# The linker used to build libraries.
+LD=$lt_LD
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag
+reload_cmds=$lt_reload_cmds
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds
+
+# A language specific compiler.
+CC=$lt_compiler
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds
+archive_expsym_cmds=$lt_archive_expsym_cmds
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds
+module_expsym_cmds=$lt_module_expsym_cmds
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
+
+# If ld is used when linking, flag to hardcode \$libdir into a binary
+# during linking. This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path=$lt_fix_srcfile_path
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action
+
+# The directories searched by this compiler when creating a shared library.
+compiler_lib_search_dirs=$lt_compiler_lib_search_dirs
+
+# Dependencies to place before and after the objects being linked to
+# create a shared library.
+predep_objects=$lt_predep_objects
+postdep_objects=$lt_postdep_objects
+predeps=$lt_predeps
+postdeps=$lt_postdeps
+
+# The library search path used internally by the compiler when linking
+# a shared library.
+compiler_lib_search_path=$lt_compiler_lib_search_path
+
+# ### END LIBTOOL CONFIG
+
+_LT_EOF
+
+ case $host_os in
+ aix3*)
+ cat <<\_LT_EOF >> "$cfgfile"
+# AIX sometimes has problems with the GCC collect2 program. For some
+# reason, if we set the COLLECT_NAMES environment variable, the problems
+# vanish in a puff of smoke.
+if test "X${COLLECT_NAMES+set}" != Xset; then
+ COLLECT_NAMES=
+ export COLLECT_NAMES
+fi
+_LT_EOF
+ ;;
+ esac
+
+
+ltmain="$ac_aux_dir/ltmain.sh"
+
+
+ # We use sed instead of cat because bash on DJGPP gets confused if
+ # if finds mixed CR/LF and LF-only lines. Since sed operates in
+ # text mode, it properly converts lines to CR/LF. This bash problem
+ # is reportedly fixed, but why not run on old versions too?
+ sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ case $xsi_shell in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result="${1##*/}"
+}
+
+# func_dirname_and_basename file append nondir_replacement
+# perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# Implementation must be kept synchronized with func_dirname
+# and func_basename. For efficiency, we do not delegate to
+# those functions but instead duplicate the functionality here.
+func_dirname_and_basename ()
+{
+ case ${1} in
+ */*) func_dirname_result="${1%/*}${2}" ;;
+ * ) func_dirname_result="${3}" ;;
+ esac
+ func_basename_result="${1##*/}"
+}
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+func_stripname ()
+{
+ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+ # positional parameters, so assign one to ordinary parameter first.
+ func_stripname_result=${3}
+ func_stripname_result=${func_stripname_result#"${1}"}
+ func_stripname_result=${func_stripname_result%"${2}"}
+}
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=${1%%=*}
+ func_opt_split_arg=${1#*=}
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ case ${1} in
+ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
+ *) func_lo2o_result=${1} ;;
+ esac
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=${1%.*}.lo
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=$(( $* ))
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=${#1}
+}
+
+_LT_EOF
+ ;;
+ *) # Bourne compatible functions.
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_dirname file append nondir_replacement
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+func_dirname ()
+{
+ # Extract subdirectory from the argument.
+ func_dirname_result=`$ECHO "${1}" | $SED "$dirname"`
+ if test "X$func_dirname_result" = "X${1}"; then
+ func_dirname_result="${3}"
+ else
+ func_dirname_result="$func_dirname_result${2}"
+ fi
+}
+
+# func_basename file
+func_basename ()
+{
+ func_basename_result=`$ECHO "${1}" | $SED "$basename"`
+}
+
+
+# func_stripname prefix suffix name
+# strip PREFIX and SUFFIX off of NAME.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+# func_strip_suffix prefix name
+func_stripname ()
+{
+ case ${2} in
+ .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;;
+ *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;;
+ esac
+}
+
+# sed scripts:
+my_sed_long_opt='1s/^\(-[^=]*\)=.*/\1/;q'
+my_sed_long_arg='1s/^-[^=]*=//'
+
+# func_opt_split
+func_opt_split ()
+{
+ func_opt_split_opt=`$ECHO "${1}" | $SED "$my_sed_long_opt"`
+ func_opt_split_arg=`$ECHO "${1}" | $SED "$my_sed_long_arg"`
+}
+
+# func_lo2o object
+func_lo2o ()
+{
+ func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"`
+}
+
+# func_xform libobj-or-source
+func_xform ()
+{
+ func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'`
+}
+
+# func_arith arithmetic-term...
+func_arith ()
+{
+ func_arith_result=`expr "$@"`
+}
+
+# func_len string
+# STRING may not start with a hyphen.
+func_len ()
+{
+ func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len`
+}
+
+_LT_EOF
+esac
+
+case $lt_shell_append in
+ yes)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$1+=\$2"
+}
+_LT_EOF
+ ;;
+ *)
+ cat << \_LT_EOF >> "$cfgfile"
+
+# func_append var value
+# Append VALUE to the end of shell variable VAR.
+func_append ()
+{
+ eval "$1=\$$1\$2"
+}
+
+_LT_EOF
+ ;;
+ esac
+
+
+ sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \
+ || (rm -f "$cfgfile"; exit 1)
+
+ mv -f "$cfgfile" "$ofile" ||
+ (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
+ chmod +x "$ofile"
+
+
+ cat <<_LT_EOF >> "$ofile"
+
+# ### BEGIN LIBTOOL TAG CONFIG: CXX
+
+# The linker used to build libraries.
+LD=$lt_LD_CXX
+
+# How to create reloadable object files.
+reload_flag=$lt_reload_flag_CXX
+reload_cmds=$lt_reload_cmds_CXX
+
+# Commands used to build an old-style archive.
+old_archive_cmds=$lt_old_archive_cmds_CXX
+
+# A language specific compiler.
+CC=$lt_compiler_CXX
+
+# Is the compiler the GNU compiler?
+with_gcc=$GCC_CXX
+
+# Compiler flag to turn off builtin functions.
+no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX
+
+# How to pass a linker flag through the compiler.
+wl=$lt_lt_prog_compiler_wl_CXX
+
+# Additional compiler flags for building library objects.
+pic_flag=$lt_lt_prog_compiler_pic_CXX
+
+# Compiler flag to prevent dynamic linking.
+link_static_flag=$lt_lt_prog_compiler_static_CXX
+
+# Does compiler simultaneously support -c and -o options?
+compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX
+
+# Whether or not to add -lc for building shared libraries.
+build_libtool_need_lc=$archive_cmds_need_lc_CXX
+
+# Whether or not to disallow shared libs when runtime libs are static.
+allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX
+
+# Compiler flag to allow reflexive dlopens.
+export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX
+
+# Compiler flag to generate shared objects directly from archives.
+whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX
+
+# Whether the compiler copes with passing no objects directly.
+compiler_needs_object=$lt_compiler_needs_object_CXX
+
+# Create an old-style archive from a shared archive.
+old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX
+
+# Create a temporary old-style archive to link instead of a shared archive.
+old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX
+
+# Commands used to build a shared archive.
+archive_cmds=$lt_archive_cmds_CXX
+archive_expsym_cmds=$lt_archive_expsym_cmds_CXX
+
+# Commands used to build a loadable module if different from building
+# a shared archive.
+module_cmds=$lt_module_cmds_CXX
+module_expsym_cmds=$lt_module_expsym_cmds_CXX
+
+# Whether we are building with GNU ld or not.
+with_gnu_ld=$lt_with_gnu_ld_CXX
+
+# Flag that allows shared libraries with undefined symbols to be built.
+allow_undefined_flag=$lt_allow_undefined_flag_CXX
+
+# Flag that enforces no undefined symbols.
+no_undefined_flag=$lt_no_undefined_flag_CXX
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist
+hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX
+
+# If ld is used when linking, flag to hardcode \$libdir into a binary
+# during linking. This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_CXX
+
+# Whether we need a single "-rpath" flag with a separated argument.
+hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary.
+hardcode_direct=$hardcode_direct_CXX
+
+# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# DIR into the resulting binary and the resulting library dependency is
+# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
+# library is relocated.
+hardcode_direct_absolute=$hardcode_direct_absolute_CXX
+
+# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
+# into the resulting binary.
+hardcode_minus_L=$hardcode_minus_L_CXX
+
+# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
+# into the resulting binary.
+hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX
+
+# Set to "yes" if building a shared library automatically hardcodes DIR
+# into the library and all subsequent libraries and executables linked
+# against it.
+hardcode_automatic=$hardcode_automatic_CXX
+
+# Set to yes if linker adds runtime paths of dependent libraries
+# to runtime path list.
+inherit_rpath=$inherit_rpath_CXX
+
+# Whether libtool must link a program against all its dependency libraries.
+link_all_deplibs=$link_all_deplibs_CXX
+
+# Fix the shell variable \$srcfile for the compiler.
+fix_srcfile_path=$lt_fix_srcfile_path_CXX
+
+# Set to "yes" if exported symbols are required.
+always_export_symbols=$always_export_symbols_CXX
+
+# The commands to list exported symbols.
+export_symbols_cmds=$lt_export_symbols_cmds_CXX
+
+# Symbols that should not be listed in the preloaded symbols.
+exclude_expsyms=$lt_exclude_expsyms_CXX
+
+# Symbols that must always be exported.
+include_expsyms=$lt_include_expsyms_CXX
+
+# Commands necessary for linking programs (against libraries) with templates.
+prelink_cmds=$lt_prelink_cmds_CXX
+
+# Specify filename containing input files.
+file_list_spec=$lt_file_list_spec_CXX
+
+# How to hardcode a shared library path into an executable.
+hardcode_action=$hardcode_action_CXX
+
+# The directories searched by this compiler when creating a shared library.
+compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX
+
+# Dependencies to place before and after the objects being linked to
+# create a shared library.
+predep_objects=$lt_predep_objects_CXX
+postdep_objects=$lt_postdep_objects_CXX
+predeps=$lt_predeps_CXX
+postdeps=$lt_postdeps_CXX
+
+# The library search path used internally by the compiler when linking
+# a shared library.
+compiler_lib_search_path=$lt_compiler_lib_search_path_CXX
+
+# ### END LIBTOOL TAG CONFIG: CXX
+_LT_EOF
+
+
+as_fn_exit 0
diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
new file mode 100644
index 00000000000..60d80aae0a6
--- /dev/null
+++ b/gdb/riscv-canonicalize-syscall-gen.c
@@ -0,0 +1,338 @@
+/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py
+
+ Copyright (C) 2024 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"
+
+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 12cab06f276..ebaed9b4508 100644
--- a/gdb/riscv-linux-tdep.c
+++ b/gdb/riscv-linux-tdep.c
@@ -187,9 +187,11 @@ static linux_record_tdep riscv_linux_record_tdep;
using regnum_type = int;
static bool
-save_registers (struct regcache *regcache, regnum_type fir, regnum_type last)
+save_registers (struct regcache *regcache, regnum_type first, regnum_type last)
{
- for (regnum_type i = fir; i != last; ++i)
+ 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;
@@ -198,6 +200,8 @@ save_registers (struct regcache *regcache, regnum_type fir, regnum_type last)
static bool
riscv_all_but_pc_registers_record (struct regcache *regcache)
{
+ gdb_assert (regcache != nullptr);
+
auto &&gdbarch = regcache->arch ();
auto &&tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
auto &&features = tdep->isa_features;
@@ -219,12 +223,12 @@ static int
riscv_linux_syscall_record (struct regcache *regcache,
unsigned long svc_number)
{
+ gdb_assert (regcache != nullptr);
+
auto syscall_gdb = riscv64_canonicalize_syscall (svc_number);
if (record_debug > 1)
- {
- gdb_printf (gdb_stdlog, "Made syscall %s.\n", plongest (svc_number));
- }
+ gdb_printf (gdb_stdlog, "Made syscall %s.\n", plongest (svc_number));
if (syscall_gdb == gdb_sys_no_syscall)
{
@@ -258,9 +262,11 @@ riscv_linux_syscall_record (struct regcache *regcache,
/* These values are the size of the type that will be used in a system
call. They are obtained from Linux Kernel source. */
static void
-riscv64_linux_record_tdep_init (
- struct gdbarch *gdbarch, struct linux_record_tdep &riscv_linux_record_tdep)
+riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
+ struct linux_record_tdep &riscv_linux_record_tdep)
{
+ gdb_assert (gdbarch != nullptr);
+
/* Initialize the riscv_linux_record_tdep. */
/* These values are the size of the type that will be used in a system
call. They are obtained from Linux Kernel source. */
@@ -427,7 +433,7 @@ riscv64_linux_record_tdep_init (
static void
riscv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
- riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
linux_init_abi (info, gdbarch, 0);
diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
index c6c7b7a63fd..37ec30fff91 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -4843,7 +4843,7 @@ this option can be used."),
static bool
try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
{
- gdb_assert (regcache);
+ gdb_assert (regcache != nullptr);
if (regcache->raw_read<ULONGEST> (regnum, &addr)
!= register_status::REG_VALID)
@@ -4964,12 +4964,10 @@ class riscv_recorded_insn final
read_reg (struct regcache *regcache, regnum_type reg,
ULONGEST &addr) noexcept
{
- gdb_assert (regcache);
+ gdb_assert (regcache != nullptr);
if (!try_read (regcache, reg, addr))
- {
- return set_error ();
- }
+ return set_error ();
return true;
}
@@ -5143,14 +5141,14 @@ class riscv_recorded_insn final
bool
try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
{
- gdb_assert (regcache);
+ gdb_assert (regcache != nullptr);
- mem_addr addr = 0;
+ auto addr = mem_addr{};
auto len = need_save_pc_mem (ival);
if (len <= 0)
return false;
- mem_len offset = EXTRACT_STYPE_IMM (ival);
+ auto offset = mem_len{EXTRACT_STYPE_IMM (ival)};
return !read_reg (regcache, decode_rs1 (ival), addr)
|| !save_mem (addr + offset, len) || set_ordinary_record_type ();
}
@@ -5178,10 +5176,10 @@ class riscv_recorded_insn final
bool
try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
{
- gdb_assert (regcache);
+ gdb_assert (regcache != nullptr);
auto len = need_save_pc_rd_mem (ival);
- mem_addr addr = 0;
+ auto addr = mem_addr{};
if (len <= 0)
return false;
@@ -5205,9 +5203,9 @@ class riscv_recorded_insn final
bool
try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
{
- gdb_assert (regcache);
+ gdb_assert (regcache != nullptr);
- mem_addr addr = 0;
+ auto addr = mem_addr{};
auto len = need_save_pc_rs2_rd_mem (ival);
if (len <= 0)
return false;
@@ -5222,7 +5220,7 @@ class riscv_recorded_insn final
bool
record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept
{
- gdb_assert (regcache);
+ gdb_assert (regcache != nullptr);
if (is_ecall_insn (ival))
{
@@ -5240,9 +5238,7 @@ class riscv_recorded_insn final
|| try_save_pc_rd_csr (ival) || try_save_pc_mem (ival, regcache)
|| try_save_pc_rd_mem (ival, regcache)
|| try_save_pc_rs2_rd_mem (ival, regcache))
- {
- return !has_error ();
- }
+ return !has_error ();
warning (_ ("Currently this instruction with len 4(%lx) is unsupported"),
ival);
@@ -5255,10 +5251,10 @@ class riscv_recorded_insn final
record_insn_len2 (ULONGEST ival, struct regcache *regcache,
struct gdbarch *gdbarch) noexcept
{
- gdb_assert (regcache);
- gdb_assert (gdbarch);
+ gdb_assert (regcache != nullptr);
+ gdb_assert (gdbarch != nullptr);
- mem_addr addr = 0;
+ auto addr = mem_addr{};
auto xlen = riscv_isa_xlen (gdbarch);
/* The order here is very important, because
@@ -5266,100 +5262,70 @@ class riscv_recorded_insn final
if (is_c_addi4spn_insn (ival) || is_c_lw_insn (ival)
|| (xlen == 8 && is_c_ld_insn (ival)))
- {
- return !save_reg (decode_crs2_short (ival))
- || set_ordinary_record_type ();
- }
+ return !save_reg (decode_crs2_short (ival))
+ || set_ordinary_record_type ();
if (is_c_fld_insn (ival) || (xlen == 4 && is_c_flw_insn (ival)))
- {
- return !save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival))
- || set_ordinary_record_type ();
- }
+ return !save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival))
+ || set_ordinary_record_type ();
if (is_c_fsd_insn (ival) || (xlen == 8 && is_c_sd_insn (ival)))
{
- int offset = EXTRACT_CLTYPE_LD_IMM (ival);
+ auto offset = int{EXTRACT_CLTYPE_LD_IMM (ival)};
return !read_reg (regcache, decode_crs1_short (ival), addr)
|| !save_mem (addr + offset, 8) || set_ordinary_record_type ();
}
if ((xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
{
- int offset = EXTRACT_CLTYPE_LW_IMM (ival);
+ auto offset = int{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 ();
- }
+ return set_ordinary_record_type ();
if (is_c_addi_insn (ival))
- {
- return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
- }
+ return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
if (xlen == 4 && is_c_jal_insn (ival))
- {
- return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
- }
+ return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
if ((xlen == 8 && is_c_addiw_insn (ival)) || is_c_li_insn (ival))
- {
- return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
- }
+ 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 ();
- }
+ 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 ();
- }
+ 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) || (xlen == 8 && is_c_subw_insn (ival))
|| (xlen == 8 && is_c_addw_insn (ival)))
- {
- return !save_reg (decode_crs1_short (ival))
- || set_ordinary_record_type ();
- }
+ 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 ();
- }
+ return set_ordinary_record_type ();
if (is_c_slli_insn (ival))
- {
- return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
- }
+ return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
if (is_c_fldsp_insn (ival) || (xlen == 4 && is_c_flwsp_insn (ival)))
- {
- return !save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival))
- || set_ordinary_record_type ();
- }
+ return !save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival))
+ || set_ordinary_record_type ();
if (is_c_lwsp_insn (ival) || (xlen == 8 && is_c_ldsp_insn (ival)))
- {
- return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
- }
+ return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
if (is_c_jr_insn (ival))
- {
- return set_ordinary_record_type ();
- }
+ return set_ordinary_record_type ();
if (is_c_mv_insn (ival))
- {
- return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
- }
+ return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
if (is_c_ebreak_insn (ival))
{
@@ -5368,25 +5334,21 @@ class riscv_recorded_insn final
}
if (is_c_jalr_insn (ival))
- {
- return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
- }
+ 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 ();
- }
+ return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
if (is_c_fsdsp_insn (ival) || (xlen == 8 && is_c_sdsp_insn (ival)))
{
- int offset = EXTRACT_CSSTYPE_SDSP_IMM (ival);
+ auto offset = int{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) || (xlen == 4 && is_c_fswsp_insn (ival)))
{
- int offset = EXTRACT_CSSTYPE_SWSP_IMM (ival);
+ auto offset = int{EXTRACT_CSSTYPE_SWSP_IMM (ival)};
return !read_reg (regcache, RISCV_SP_REGNUM, addr)
|| !save_mem (addr + offset, 4) || set_ordinary_record_type ();
}
@@ -5403,7 +5365,10 @@ class riscv_recorded_insn final
bool
record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
{
- int m_length = 0;
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ auto m_length = int{};
auto ival = riscv_insn::fetch_instruction (gdbarch, addr, &m_length);
if (!save_reg (RISCV_PC_REGNUM))
return false;
@@ -5458,9 +5423,10 @@ static int
riscv_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
const riscv_recorded_insn &insn)
{
- gdb_assert (gdbarch && regcache);
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
- riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
auto regs_begin = insn.regs_begin ();
auto regs_end = insn.regs_end ();
if (std::any_of (regs_begin, regs_end, [®cache] (auto &®_it) {
@@ -5487,11 +5453,9 @@ riscv_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
warning (_ ("Syscall record is not supported"));
return -1;
}
- ULONGEST reg_val = 0;
+ auto reg_val = ULONGEST{};
if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
- {
- return -1;
- }
+ return -1;
return tdep->riscv_syscall_record (regcache, reg_val);
}
@@ -5508,7 +5472,8 @@ int
riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
CORE_ADDR addr)
{
- gdb_assert (gdbarch && regcache);
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
riscv_recorded_insn insn;
if (!insn.record (gdbarch, regcache, addr))
@@ -5520,9 +5485,7 @@ riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
auto ret_val = riscv_make_record_process (gdbarch, regcache, insn);
if (record_full_arch_list_add_end ())
- {
- return -1;
- }
+ return -1;
return ret_val;
}
--
2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* Re: [PATCH v3] gdb/riscv: Add record support for rv64gc instructions
2024-12-03 18:30 ` [PATCH v3] " Timur
@ 2024-12-13 18:10 ` Guinevere Larsen
2024-12-17 0:10 ` [PATCH v4] This commit adds record full support for rv64gc instruction set Timur
0 siblings, 1 reply; 35+ messages in thread
From: Guinevere Larsen @ 2024-12-13 18:10 UTC (permalink / raw)
To: Timur, gdb-patches
Sorry about the delay reviewing, this escaped my notice when it first
came to the list.
On 12/3/24 3:30 PM, Timur wrote:
> This commit adds record full support for rv64gc instrcution.
>
> It includes changes to the following files:
> - gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
> syscalls.
> - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
> rv64gc instructions.
> - gdb/configure.tgt: adds new files for compilation.
> - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
> targets.
> - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate function
> that canonicalizes RISC-V syscall. This script can simply support for syscalls
> on rv32 system. To use this script you need to pass a path to a file with
One small nit, but I would word it as "This script can simplify support
for syscalls on rv32 and rv64 systems". The current wording sounds to me
like the script is only for rv32, which I know isn't the case.
> syscalls description from riscv-glibc (example is in the help message). The
> script produces a mapping from syscall names to gdb_syscall enum.
> - gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
The GDB style for commit message is more free form text, instead of a
changelog. However, this change is simple enough that I think this
should be fine.
>
> Timur Golubovich timurgol007@gmail.com
>
> Hi!
>
>>>> + switch (syscall)
>>>> + {
>>>> + case 0: // #define __NR_io_setup 0
>>>>
>>>> All comments should be in the form /* */, rather than //
>>>>
>>>> I would also prefer if this followed what other architectures do,
>>>> defining an enum to relate syscall numbers in a RISCV system to their
>>>> names, and then this function returning the gdb_sys enum numbers,
>>>> however I don't expect to be working on RISCV in the near future, so
>>>> feel free to ignore this comment if the other RISCV maintainers are ok
>>>> with this style. This would make it so no #define comment is necessary
>>> Decided to make a script generating function that canonicalizes RISC-V syscall.
>>> This solution will simplify adding record support for rv32 system. I also left
>>> place for corner cases in generating script as they always may exist. Also
>>> removed this #define comment from generating script.
>> I see. This makes sense.
>>
>> I would like to see this explanation on the commit message, so that it
>> is recorded why Risc-V works different to all other arches.
> Could you please double-check the commit message? I tried to make it more
> detailed. Do you think I need to provide additional information?
>
>>>> +
>>>> + if (m_length == 2)
>>>> + return record_insn_len2 (ival, regcache, gdbarch);
>>>> +
>>>> + /* 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. */
>>>> Again, the indentation here is incorrect. Lines should start in the same column as the number 6.
>>> Addressed
>>>
>>> Best wishes,
>>> Timur Golubovich
>>>
>>> Timur Golubovich timurgol007@gmail.com
>> As I mentioned in the first review, please add some commit message
>> explaining a bit of what your patch is doing. I would recommend
>> explaining the inspiration for how riscv-tdep handles the recording (I
>> guess aarch64) and why you changed it; plus explaining the idea behind
>> the python script.c
> Added this information to the commit message.
>
>>> /* The following value is derived from __NR_rt_sigreturn in
>>> <include/uapi/asm-generic/unistd.h> from the Linux source tree. */
>>> @@ -173,6 +178,250 @@ 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;
>>> +
>>> +/* Record all registers but PC register for process-record. */
>>> +
>>> +using regnum_type = int;
>>> +
>>> +static bool
>>> +save_registers (struct regcache *regcache, regnum_type fir, regnum_type last)
>> Please fully type the variable names (fir -> first). Even if this makes
>> you need to use multiple lines for the function, I think this makes the
>> function easier to understand in the future.
>>
>> Sorry about not mentioning this before, I missed it in my first pass.
> Addressed
>
>>> + if (syscall_gdb == gdb_sys_sigreturn || syscall_gdb == gdb_sys_rt_sigreturn)
>>> + {
>>> + if (!riscv_all_but_pc_registers_record (regcache))
>>> + return -1;
>>> + return 0;
>>> + }
>>> +
>>> + auto 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. */
>>> +/* These values are the size of the type that will be used in a system
>>> + call. They are obtained from Linux Kernel source. */
>>> +static void
>>> +riscv64_linux_record_tdep_init (
>>> + struct gdbarch *gdbarch, struct linux_record_tdep &riscv_linux_record_tdep)
>> Oops, also missed this one on the first pass.
>>
>> The preferred way to do multi-line arguments is:
>>
>> static void
>> riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
>> struct
>> linux_record_tdep &riscv_linux_record_tdep)
>> {
>>
>> (with the "struct" keywords being aligned)
> Addressed
>
>>> + {
>>> + gdb_assert (regcache);
>>> +
>>> + if (!try_read (regcache, reg, addr))
>>> + {
>>> + return set_error ();
>>> + }
>>> + return true;
>>> + }
>>> +
>>> + bool
>>> + save_reg (regnum_type regnum) noexcept
>>> + {
>>> + try
>>> + {
>>> + m_regs.emplace_back (regnum);
>> Similar to my thoughts on Loongarch, I don't think this approach is the
>> best.
>>
>> In short, recording is already considered very slow. The strategy this
>> patch uses is to first collect all the data to be recorded (which
>> demands dynamic allocations to std::vector), and then call
>> record_full_arch_list_add_reg or record_full_arch_list_add_mem, which
>> will do their own dynamic allocations. This seems wasteful to me.
>>
>> I believe it would be better if, instead, the save_reg and save_mem
>> methods would directly call the correct record_full_arch_list_add
>> function, so that only one allocation is needed per unit of data that
>> needs recording.
>>
>> Since aarch64 already uses the strategy in this patch I won't block
>> merging based on this feedback, if you're set on this idea, but I think
>> direct calls would be better.
> I think that this overhead is negligeble, since the number of allocation
> because most instructions affect only 1 register. Moreover, I feel that the
> suggested approach simplifies error handling, since I can decouple decoding
> procedure from the actual error detection. If you don't mind, I would suggest
> to leave it as it is.
>
>>> + 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)
>>> + || try_save_pc_rs2_rd_mem (ival, regcache))
>>> + {
>>> + return !has_error ();
>>> + }
>> Minor nit, but I noticed around here that this (and some lines later,
>> maybe some before) don't need the braces.
> Addressed
>
>>> + 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;
>>> +}
>>> +
>>> +int
>>> +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
>>> + CORE_ADDR addr)
>>> +{
>>> + gdb_assert (gdbarch && regcache);
>> null pointer checks should be explicit, ie, gdb_assert (gdbarch !=
>> nullptr && regcache != nullptr)
> Addressed.
>
> Best wishes,
> Timur Golubovich
>
> ---
> gdb/config.lt | 1484 ++++++++++++++++++++++++++
> gdb/riscv-canonicalize-syscall-gen.c | 338 ++++++
> gdb/riscv-linux-tdep.c | 22 +-
> gdb/riscv-tdep.c | 141 +--
> 4 files changed, 1888 insertions(+), 97 deletions(-)
> create mode 100755 gdb/config.lt
> create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
Are you sure you sent the correct diff? A couple files are missing
(riscv-linux-tdep.h, configure.tgt, gdb.exp and
riscv-canonicalize-syscall.c), and there's no mention of config.lt in
the commit message.
--
Cheers,
Guinevere Larsen
She/Her/Hers
>
> diff --git a/gdb/config.lt b/gdb/config.lt
> new file mode 100755
> index 00000000000..96851a9bfaf
> --- /dev/null
> +++ b/gdb/config.lt
> @@ -0,0 +1,1484 @@
> +#! /bin/bash
> +# Generated by configure.
> +# Run this file to recreate a libtool stub with the current configuration.
> +SHELL=${CONFIG_SHELL-/bin/bash}
> +export SHELL
> +## -------------------- ##
> +## M4sh Initialization. ##
> +## -------------------- ##
> +
> +# Be more Bourne compatible
> +DUALCASE=1; export DUALCASE # for MKS sh
> +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
> + emulate sh
> + NULLCMD=:
> + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
> + # is contrary to our usage. Disable this feature.
> + alias -g '${1+"$@"}'='"$@"'
> + setopt NO_GLOB_SUBST
> +else
> + case `(set -o) 2>/dev/null` in #(
> + *posix*) :
> + set -o posix ;; #(
> + *) :
> + ;;
> +esac
> +fi
> +
> +
> +as_nl='
> +'
> +export as_nl
> +# Printing a long string crashes Solaris 7 /usr/bin/printf.
> +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
> +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
> +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
> +# Prefer a ksh shell builtin over an external printf program on Solaris,
> +# but without wasting forks for bash or zsh.
> +if test -z "$BASH_VERSION$ZSH_VERSION" \
> + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
> + as_echo='print -r --'
> + as_echo_n='print -rn --'
> +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
> + as_echo='printf %s\n'
> + as_echo_n='printf %s'
> +else
> + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
> + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
> + as_echo_n='/usr/ucb/echo -n'
> + else
> + as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
> + as_echo_n_body='eval
> + arg=$1;
> + case $arg in #(
> + *"$as_nl"*)
> + expr "X$arg" : "X\\(.*\\)$as_nl";
> + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
> + esac;
> + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
> + '
> + export as_echo_n_body
> + as_echo_n='sh -c $as_echo_n_body as_echo'
> + fi
> + export as_echo_body
> + as_echo='sh -c $as_echo_body as_echo'
> +fi
> +
> +# The user is always right.
> +if test "${PATH_SEPARATOR+set}" != set; then
> + PATH_SEPARATOR=:
> + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
> + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
> + PATH_SEPARATOR=';'
> + }
> +fi
> +
> +
> +# IFS
> +# We need space, tab and new line, in precisely that order. Quoting is
> +# there to prevent editors from complaining about space-tab.
> +# (If _AS_PATH_WALK were called with IFS unset, it would disable word
> +# splitting by setting IFS to empty value.)
> +IFS=" "" $as_nl"
> +
> +# Find who we are. Look in the path if we contain no directory separator.
> +as_myself=
> +case $0 in #((
> + *[\\/]* ) as_myself=$0 ;;
> + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
> +for as_dir in $PATH
> +do
> + IFS=$as_save_IFS
> + test -z "$as_dir" && as_dir=.
> + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
> + done
> +IFS=$as_save_IFS
> +
> + ;;
> +esac
> +# We did not find ourselves, most probably we were run as `sh COMMAND'
> +# in which case we are not to be found in the path.
> +if test "x$as_myself" = x; then
> + as_myself=$0
> +fi
> +if test ! -f "$as_myself"; then
> + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
> + exit 1
> +fi
> +
> +# Unset variables that we do not need and which cause bugs (e.g. in
> +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
> +# suppresses any "Segmentation fault" message there. '((' could
> +# trigger a bug in pdksh 5.2.14.
> +for as_var in BASH_ENV ENV MAIL MAILPATH
> +do eval test x\${$as_var+set} = xset \
> + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
> +done
> +PS1='$ '
> +PS2='> '
> +PS4='+ '
> +
> +# NLS nuisances.
> +LC_ALL=C
> +export LC_ALL
> +LANGUAGE=C
> +export LANGUAGE
> +
> +# CDPATH.
> +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
> +
> +
> +# as_fn_error STATUS ERROR [LINENO LOG_FD]
> +# ----------------------------------------
> +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
> +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
> +# script with STATUS, using 1 if that was 0.
> +as_fn_error ()
> +{
> + as_status=$1; test $as_status -eq 0 && as_status=1
> + if test "$4"; then
> + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
> + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
> + fi
> + $as_echo "$as_me: error: $2" >&2
> + as_fn_exit $as_status
> +} # as_fn_error
> +
> +
> +# as_fn_set_status STATUS
> +# -----------------------
> +# Set $? to STATUS, without forking.
> +as_fn_set_status ()
> +{
> + return $1
> +} # as_fn_set_status
> +
> +# as_fn_exit STATUS
> +# -----------------
> +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context.
> +as_fn_exit ()
> +{
> + set +e
> + as_fn_set_status $1
> + exit $1
> +} # as_fn_exit
> +
> +# as_fn_unset VAR
> +# ---------------
> +# Portably unset VAR.
> +as_fn_unset ()
> +{
> + { eval $1=; unset $1;}
> +}
> +as_unset=as_fn_unset
> +# as_fn_append VAR VALUE
> +# ----------------------
> +# Append the text in VALUE to the end of the definition contained in VAR. Take
> +# advantage of any shell optimizations that allow amortized linear growth over
> +# repeated appends, instead of the typical quadratic growth present in naive
> +# implementations.
> +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
> + eval 'as_fn_append ()
> + {
> + eval $1+=\$2
> + }'
> +else
> + as_fn_append ()
> + {
> + eval $1=\$$1\$2
> + }
> +fi # as_fn_append
> +
> +# as_fn_arith ARG...
> +# ------------------
> +# Perform arithmetic evaluation on the ARGs, and store the result in the
> +# global $as_val. Take advantage of shells that can avoid forks. The arguments
> +# must be portable across $(()) and expr.
> +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
> + eval 'as_fn_arith ()
> + {
> + as_val=$(( $* ))
> + }'
> +else
> + as_fn_arith ()
> + {
> + as_val=`expr "$@" || test $? -eq 1`
> + }
> +fi # as_fn_arith
> +
> +
> +if expr a : '\(a\)' >/dev/null 2>&1 &&
> + test "X`expr 00001 : '.*\(...\)'`" = X001; then
> + as_expr=expr
> +else
> + as_expr=false
> +fi
> +
> +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then
> + as_basename=basename
> +else
> + as_basename=false
> +fi
> +
> +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then
> + as_dirname=dirname
> +else
> + as_dirname=false
> +fi
> +
> +as_me=`$as_basename -- "$0" ||
> +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
> + X"$0" : 'X\(//\)$' \| \
> + X"$0" : 'X\(/\)' \| . 2>/dev/null ||
> +$as_echo X/"$0" |
> + sed '/^.*\/\([^/][^/]*\)\/*$/{
> + s//\1/
> + q
> + }
> + /^X\/\(\/\/\)$/{
> + s//\1/
> + q
> + }
> + /^X\/\(\/\).*/{
> + s//\1/
> + q
> + }
> + s/.*/./; q'`
> +
> +# Avoid depending upon Character Ranges.
> +as_cr_letters='abcdefghijklmnopqrstuvwxyz'
> +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
> +as_cr_Letters=$as_cr_letters$as_cr_LETTERS
> +as_cr_digits='0123456789'
> +as_cr_alnum=$as_cr_Letters$as_cr_digits
> +
> +ECHO_C= ECHO_N= ECHO_T=
> +case `echo -n x` in #(((((
> +-n*)
> + case `echo 'xy\c'` in
> + *c*) ECHO_T=' ';; # ECHO_T is single tab character.
> + xy) ECHO_C='\c';;
> + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null
> + ECHO_T=' ';;
> + esac;;
> +*)
> + ECHO_N='-n';;
> +esac
> +
> +rm -f conf$$ conf$$.exe conf$$.file
> +if test -d conf$$.dir; then
> + rm -f conf$$.dir/conf$$.file
> +else
> + rm -f conf$$.dir
> + mkdir conf$$.dir 2>/dev/null
> +fi
> +if (echo >conf$$.file) 2>/dev/null; then
> + if ln -s conf$$.file conf$$ 2>/dev/null; then
> + as_ln_s='ln -s'
> + # ... but there are two gotchas:
> + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
> + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
> + # In both cases, we have to default to `cp -pR'.
> + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
> + as_ln_s='cp -pR'
> + elif ln conf$$.file conf$$ 2>/dev/null; then
> + as_ln_s=ln
> + else
> + as_ln_s='cp -pR'
> + fi
> +else
> + as_ln_s='cp -pR'
> +fi
> +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
> +rmdir conf$$.dir 2>/dev/null
> +
> +
> +# as_fn_mkdir_p
> +# -------------
> +# Create "$as_dir" as a directory, including parents if necessary.
> +as_fn_mkdir_p ()
> +{
> +
> + case $as_dir in #(
> + -*) as_dir=./$as_dir;;
> + esac
> + test -d "$as_dir" || eval $as_mkdir_p || {
> + as_dirs=
> + while :; do
> + case $as_dir in #(
> + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
> + *) as_qdir=$as_dir;;
> + esac
> + as_dirs="'$as_qdir' $as_dirs"
> + as_dir=`$as_dirname -- "$as_dir" ||
> +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
> + X"$as_dir" : 'X\(//\)[^/]' \| \
> + X"$as_dir" : 'X\(//\)$' \| \
> + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
> +$as_echo X"$as_dir" |
> + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
> + s//\1/
> + q
> + }
> + /^X\(\/\/\)[^/].*/{
> + s//\1/
> + q
> + }
> + /^X\(\/\/\)$/{
> + s//\1/
> + q
> + }
> + /^X\(\/\).*/{
> + s//\1/
> + q
> + }
> + s/.*/./; q'`
> + test -d "$as_dir" && break
> + done
> + test -z "$as_dirs" || eval "mkdir $as_dirs"
> + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
> +
> +
> +} # as_fn_mkdir_p
> +if mkdir -p . 2>/dev/null; then
> + as_mkdir_p='mkdir -p "$as_dir"'
> +else
> + test -d ./-p && rmdir ./-p
> + as_mkdir_p=false
> +fi
> +
> +
> +# as_fn_executable_p FILE
> +# -----------------------
> +# Test if FILE is an executable regular file.
> +as_fn_executable_p ()
> +{
> + test -f "$1" && test -x "$1"
> +} # as_fn_executable_p
> +as_test_x='test -x'
> +as_executable_p=as_fn_executable_p
> +
> +# Sed expression to map a string onto a valid CPP name.
> +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
> +
> +# Sed expression to map a string onto a valid variable name.
> +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
> +
> +
> +exec 6>&1
> +## --------------------------------- ##
> +## Main body of "$CONFIG_LT" script. ##
> +## --------------------------------- ##
> +lt_cl_silent=false
> +exec 5>>config.log
> +{
> + echo
> + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
> +## Running $as_me. ##
> +_ASBOX
> +} >&5
> +
> +lt_cl_help="\
> +\`$as_me' creates a local libtool stub from the current configuration,
> +for use in further configure time tests before the real libtool is
> +generated.
> +
> +Usage: $0 [OPTIONS]
> +
> + -h, --help print this help, then exit
> + -V, --version print version number, then exit
> + -q, --quiet do not print progress messages
> + -d, --debug don't remove temporary files
> +
> +Report bugs to <bug-libtool@gnu.org>."
> +
> +lt_cl_version="\
> +config.lt
> +configured by $0, generated by GNU Autoconf 2.69.
> +
> +Copyright (C) 2009 Free Software Foundation, Inc.
> +This config.lt script is free software; the Free Software Foundation
> +gives unlimited permision to copy, distribute and modify it."
> +
> +while test $# != 0
> +do
> + case $1 in
> + --version | --v* | -V )
> + echo "$lt_cl_version"; exit 0 ;;
> + --help | --h* | -h )
> + echo "$lt_cl_help"; exit 0 ;;
> + --debug | --d* | -d )
> + debug=: ;;
> + --quiet | --q* | --silent | --s* | -q )
> + lt_cl_silent=: ;;
> +
> + -*) as_fn_error $? "unrecognized option: $1
> +Try \`$0 --help' for more information." "$LINENO" 5 ;;
> +
> + *) as_fn_error $? "unrecognized argument: $1
> +Try \`$0 --help' for more information." "$LINENO" 5 ;;
> + esac
> + shift
> +done
> +
> +if $lt_cl_silent; then
> + exec 6>/dev/null
> +fi
> +
> +
> +# The HP-UX ksh and POSIX shell print the target directory to stdout
> +# if CDPATH is set.
> +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
> +
> +sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
> +double_quote_subst='s/\(["`\\]\)/\\\1/g'
> +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g'
> +macro_version='2.2.7a'
> +macro_revision='1.3134'
> +enable_shared='yes'
> +enable_static='yes'
> +pic_mode='default'
> +enable_fast_install='needless'
> +SHELL='/bin/bash'
> +ECHO='printf %s\n'
> +host_alias='x86_64-pc-linux-gnu'
> +host='x86_64-pc-linux-gnu'
> +host_os='linux-gnu'
> +build_alias='x86_64-pc-linux-gnu'
> +build='x86_64-pc-linux-gnu'
> +build_os='linux-gnu'
> +SED='/usr/bin/sed'
> +Xsed='/usr/bin/sed -e 1s/^X//'
> +GREP='/usr/bin/grep'
> +EGREP='/usr/bin/grep -E'
> +FGREP='/usr/bin/grep -F'
> +LD='ld -m elf_x86_64'
> +NM='/usr/bin/nm -B'
> +LN_S='ln -s'
> +max_cmd_len='1572864'
> +ac_objext='o'
> +exeext=''
> +lt_unset='unset'
> +lt_SP2NL='tr \040 \012'
> +lt_NL2SP='tr \015\012 \040\040'
> +reload_flag=' -r'
> +reload_cmds='$LD$reload_flag -o $output$reload_objs'
> +OBJDUMP='objdump'
> +deplibs_check_method='pass_all'
> +file_magic_cmd='$MAGIC_CMD'
> +AR='ar --plugin /usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so --plugin /usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so'
> +AR_FLAGS='rc'
> +STRIP='strip'
> +RANLIB='ranlib --plugin /usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so --plugin /usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so'
> +old_postinstall_cmds='chmod 644 $oldlib~$RANLIB $oldlib'
> +old_postuninstall_cmds=''
> +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs~$RANLIB $oldlib'
> +lock_old_archive_extraction='no'
> +CC='gcc'
> +CFLAGS='-g -O2 '
> +compiler='g++'
> +GCC='yes'
> +lt_cv_sys_global_symbol_pipe='sed -n -e '\''s/^.*[ ]\([ABCDGIRSTW][ABCDGIRSTW]*\)[ ][ ]*\([_A-Za-z][_A-Za-z0-9]*\)$/\1 \2 \2/p'\'''
> +lt_cv_sys_global_symbol_to_cdecl='sed -n -e '\''s/^T .* \(.*\)$/extern int \1();/p'\'' -e '\''s/^[ABCDGIRSTW]* .* \(.*\)$/extern char \1;/p'\'''
> +lt_cv_sys_global_symbol_to_c_name_address='sed -n -e '\''s/^: \([^ ]*\) $/ {\"\1\", (void *) 0},/p'\'' -e '\''s/^[ABCDGIRSTW]* \([^ ]*\) \([^ ]*\)$/ {"\2", (void *) \&\2},/p'\'''
> +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='sed -n -e '\''s/^: \([^ ]*\) $/ {\"\1\", (void *) 0},/p'\'' -e '\''s/^[ABCDGIRSTW]* \([^ ]*\) \(lib[^ ]*\)$/ {"\2", (void *) \&\2},/p'\'' -e '\''s/^[ABCDGIRSTW]* \([^ ]*\) \([^ ]*\)$/ {"lib\2", (void *) \&\2},/p'\'''
> +objdir='.libs'
> +MAGIC_CMD='file'
> +lt_prog_compiler_no_builtin_flag=' -fno-builtin'
> +lt_prog_compiler_wl='-Wl,'
> +lt_prog_compiler_pic=' -fPIC -DPIC'
> +lt_prog_compiler_static='-static'
> +lt_cv_prog_compiler_c_o='yes'
> +need_locks='no'
> +DSYMUTIL='dsymutil'
> +NMEDIT=''
> +LIPO='lipo'
> +OTOOL='otool'
> +OTOOL64=''
> +libext='a'
> +shrext_cmds='.so'
> +extract_expsyms_cmds=''
> +archive_cmds_need_lc='no'
> +enable_shared_with_static_runtimes='no'
> +export_dynamic_flag_spec='${wl}--export-dynamic'
> +whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
> +compiler_needs_object='no'
> +old_archive_from_new_cmds=''
> +old_archive_from_expsyms_cmds=''
> +archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
> +archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
> + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
> + echo "local: *; };" >> $output_objdir/$libname.ver~
> + $CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
> +module_cmds=''
> +module_expsym_cmds=''
> +with_gnu_ld='yes'
> +allow_undefined_flag=''
> +no_undefined_flag=''
> +hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
> +hardcode_libdir_flag_spec_ld=''
> +hardcode_libdir_separator=''
> +hardcode_direct='no'
> +hardcode_direct_absolute='no'
> +hardcode_minus_L='no'
> +hardcode_shlibpath_var='unsupported'
> +hardcode_automatic='no'
> +inherit_rpath='no'
> +link_all_deplibs='unknown'
> +fix_srcfile_path=''
> +always_export_symbols='no'
> +export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
> +exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
> +include_expsyms=''
> +prelink_cmds=''
> +file_list_spec=''
> +variables_saved_for_relink='PATH LD_LIBRARY_PATH LD_RUN_PATH GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH'
> +need_lib_prefix='no'
> +need_version='no'
> +version_type='linux'
> +runpath_var='LD_RUN_PATH'
> +shlibpath_var='LD_LIBRARY_PATH'
> +shlibpath_overrides_runpath='yes'
> +libname_spec='lib$name'
> +library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
> +soname_spec='${libname}${release}${shared_ext}$major'
> +install_override_mode=''
> +postinstall_cmds=''
> +postuninstall_cmds=''
> +finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
> +finish_eval=''
> +hardcode_into_libs='yes'
> +sys_lib_search_path_spec='/usr/lib/gcc/x86_64-linux-gnu/11 /usr/lib/x86_64-linux-gnu /usr/lib /lib/x86_64-linux-gnu /lib '
> +sys_lib_dlsearch_path_spec='/lib /usr/lib /usr/lib/x86_64-linux-gnu/libfakeroot /usr/local/lib /usr/local/lib/x86_64-linux-gnu /lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu /lib32 /usr/lib32 '
> +hardcode_action='immediate'
> +enable_dlopen='unknown'
> +enable_dlopen_self='unknown'
> +enable_dlopen_self_static='unknown'
> +old_striplib='strip --strip-debug'
> +striplib='strip --strip-unneeded'
> +compiler_lib_search_dirs=''
> +predep_objects=''
> +postdep_objects=''
> +predeps=''
> +postdeps=''
> +compiler_lib_search_path=''
> +LD_CXX='ld -m elf_x86_64'
> +reload_flag_CXX=' -r'
> +reload_cmds_CXX='$LD$reload_flag -o $output$reload_objs'
> +old_archive_cmds_CXX='$AR $AR_FLAGS $oldlib$oldobjs~$RANLIB $oldlib'
> +compiler_CXX='g++'
> +GCC_CXX='yes'
> +lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin'
> +lt_prog_compiler_wl_CXX='-Wl,'
> +lt_prog_compiler_pic_CXX=' -fPIC -DPIC'
> +lt_prog_compiler_static_CXX='-static'
> +lt_cv_prog_compiler_c_o_CXX='yes'
> +archive_cmds_need_lc_CXX='no'
> +enable_shared_with_static_runtimes_CXX='no'
> +export_dynamic_flag_spec_CXX='${wl}--export-dynamic'
> +whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
> +compiler_needs_object_CXX='no'
> +old_archive_from_new_cmds_CXX=''
> +old_archive_from_expsyms_cmds_CXX=''
> +archive_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
> +archive_expsym_cmds_CXX='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
> +module_cmds_CXX=''
> +module_expsym_cmds_CXX=''
> +with_gnu_ld_CXX='yes'
> +allow_undefined_flag_CXX=''
> +no_undefined_flag_CXX=''
> +hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir'
> +hardcode_libdir_flag_spec_ld_CXX=''
> +hardcode_libdir_separator_CXX=''
> +hardcode_direct_CXX='no'
> +hardcode_direct_absolute_CXX='no'
> +hardcode_minus_L_CXX='no'
> +hardcode_shlibpath_var_CXX='unsupported'
> +hardcode_automatic_CXX='no'
> +inherit_rpath_CXX='no'
> +link_all_deplibs_CXX='unknown'
> +fix_srcfile_path_CXX=''
> +always_export_symbols_CXX='no'
> +export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols'
> +exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
> +include_expsyms_CXX=''
> +prelink_cmds_CXX=''
> +file_list_spec_CXX=''
> +hardcode_action_CXX='immediate'
> +compiler_lib_search_dirs_CXX='/usr/lib/gcc/x86_64-linux-gnu/11 /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu /usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib /lib/x86_64-linux-gnu /lib/../lib /usr/lib/x86_64-linux-gnu /usr/lib/../lib /usr/lib/gcc/x86_64-linux-gnu/11/../../..'
> +predep_objects_CXX='/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/11/crtbeginS.o'
> +postdep_objects_CXX='/usr/lib/gcc/x86_64-linux-gnu/11/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crtn.o'
> +predeps_CXX=''
> +postdeps_CXX='-lstdc++ -lm -lgcc_s -lc -lgcc_s'
> +compiler_lib_search_path_CXX='-L/usr/lib/gcc/x86_64-linux-gnu/11 -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/11/../../..'
> +
> +LTCC='gcc'
> +LTCFLAGS='-g -O2 '
> +compiler='gcc'
> +
> +# A function that is used when there is no print builtin or printf.
> +func_fallback_echo ()
> +{
> + eval 'cat <<_LTECHO_EOF
> +$1
> +_LTECHO_EOF'
> +}
> +
> +# Quote evaled strings.
> +for var in SHELL ECHO SED GREP EGREP FGREP LD NM LN_S lt_SP2NL lt_NL2SP reload_flag OBJDUMP deplibs_check_method file_magic_cmd AR AR_FLAGS STRIP RANLIB CC CFLAGS compiler lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl lt_cv_sys_global_symbol_to_c_name_address lt_cv_sys_global_symbol_to_c_name_address_lib_prefix lt_prog_compiler_no_builtin_flag lt_prog_compiler_wl lt_prog_compiler_pic lt_prog_compiler_static lt_cv_prog_compiler_c_o need_locks DSYMUTIL NMEDIT LIPO OTOOL OTOOL64 shrext_cmds export_dynamic_flag_spec whole_archive_flag_spec compiler_needs_object with_gnu_ld allow_undefined_flag no_undefined_flag hardcode_libdir_flag_spec hardcode_libdir_flag_spec_ld hardcode_libdir_separator fix_srcfile_path exclude_expsyms include_expsyms file_list_spec variables_saved_for_relink libname_spec library_names_spec soname_spec install_override_mode finish_eval old_striplib striplib compiler_lib_search_dirs predep_objects postdep_objects predeps postdeps compiler_lib_search_path LD_CXX reload_flag_CXX compiler_CXX lt_prog_compiler_no_builtin_flag_CXX lt_prog_compiler_wl_CXX lt_prog_compiler_pic_CXX lt_prog_compiler_static_CXX lt_cv_prog_compiler_c_o_CXX export_dynamic_flag_spec_CXX whole_archive_flag_spec_CXX compiler_needs_object_CXX with_gnu_ld_CXX allow_undefined_flag_CXX no_undefined_flag_CXX hardcode_libdir_flag_spec_CXX hardcode_libdir_flag_spec_ld_CXX hardcode_libdir_separator_CXX fix_srcfile_path_CXX exclude_expsyms_CXX include_expsyms_CXX file_list_spec_CXX compiler_lib_search_dirs_CXX predep_objects_CXX postdep_objects_CXX predeps_CXX postdeps_CXX compiler_lib_search_path_CXX; do
> + case `eval \\$ECHO \\""\\$$var"\\"` in
> + *[\\\`\"\$]*)
> + eval "lt_$var=\\\"\`\$ECHO \"\$$var\" | \$SED \"\$sed_quote_subst\"\`\\\""
> + ;;
> + *)
> + eval "lt_$var=\\\"\$$var\\\""
> + ;;
> + esac
> +done
> +
> +# Double-quote double-evaled strings.
> +for var in reload_cmds old_postinstall_cmds old_postuninstall_cmds old_archive_cmds extract_expsyms_cmds old_archive_from_new_cmds old_archive_from_expsyms_cmds archive_cmds archive_expsym_cmds module_cmds module_expsym_cmds export_symbols_cmds prelink_cmds postinstall_cmds postuninstall_cmds finish_cmds sys_lib_search_path_spec sys_lib_dlsearch_path_spec reload_cmds_CXX old_archive_cmds_CXX old_archive_from_new_cmds_CXX old_archive_from_expsyms_cmds_CXX archive_cmds_CXX archive_expsym_cmds_CXX module_cmds_CXX module_expsym_cmds_CXX export_symbols_cmds_CXX prelink_cmds_CXX; do
> + case `eval \\$ECHO \\""\\$$var"\\"` in
> + *[\\\`\"\$]*)
> + eval "lt_$var=\\\"\`\$ECHO \"\$$var\" | \$SED -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\""
> + ;;
> + *)
> + eval "lt_$var=\\\"\$$var\\\""
> + ;;
> + esac
> +done
> +
> +ac_aux_dir='..'
> +xsi_shell='yes'
> +lt_shell_append='yes'
> +
> +# See if we are running on zsh, and set the options which allow our
> +# commands through without removal of \ escapes INIT.
> +if test -n "${ZSH_VERSION+set}" ; then
> + setopt NO_GLOB_SUBST
> +fi
> +
> +
> + PACKAGE=''
> + VERSION=''
> + TIMESTAMP=''
> + RM='rm -f'
> + ofile='libtool'
> +
> +
> +
> +
> +
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $ofile" >&5
> +$as_echo "$as_me: creating $ofile" >&6;}
> +
> +
> + # See if we are running on zsh, and set the options which allow our
> + # commands through without removal of \ escapes.
> + if test -n "${ZSH_VERSION+set}" ; then
> + setopt NO_GLOB_SUBST
> + fi
> +
> + cfgfile="${ofile}T"
> + trap "$RM \"$cfgfile\"; exit 1" 1 2 15
> + $RM "$cfgfile"
> +
> + cat <<_LT_EOF >> "$cfgfile"
> +#! $SHELL
> +
> +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
> +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
> +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
> +# NOTE: Changes made to this file will be lost: look at ltmain.sh.
> +#
> +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
> +# 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
> +# Written by Gordon Matzigkeit, 1996
> +#
> +# This file is part of GNU Libtool.
> +#
> +# GNU Libtool 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 2 of
> +# the License, or (at your option) any later version.
> +#
> +# As a special exception to the GNU General Public License,
> +# if you distribute this file as part of a program or library that
> +# is built using GNU Libtool, you may include this file under the
> +# same distribution terms that you use for the rest of that program.
> +#
> +# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy
> +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
> +# obtained by writing to the Free Software Foundation, Inc.,
> +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
> +
> +
> +# The names of the tagged configurations supported by this script.
> +available_tags="CXX "
> +
> +# ### BEGIN LIBTOOL CONFIG
> +
> +# Which release of libtool.m4 was used?
> +macro_version=$macro_version
> +macro_revision=$macro_revision
> +
> +# Whether or not to build shared libraries.
> +build_libtool_libs=$enable_shared
> +
> +# Whether or not to build static libraries.
> +build_old_libs=$enable_static
> +
> +# What type of objects to build.
> +pic_mode=$pic_mode
> +
> +# Whether or not to optimize for fast installation.
> +fast_install=$enable_fast_install
> +
> +# Shell to use when invoking shell scripts.
> +SHELL=$lt_SHELL
> +
> +# An echo program that protects backslashes.
> +ECHO=$lt_ECHO
> +
> +# The host system.
> +host_alias=$host_alias
> +host=$host
> +host_os=$host_os
> +
> +# The build system.
> +build_alias=$build_alias
> +build=$build
> +build_os=$build_os
> +
> +# A sed program that does not truncate output.
> +SED=$lt_SED
> +
> +# Sed that helps us avoid accidentally triggering echo(1) options like -n.
> +Xsed="\$SED -e 1s/^X//"
> +
> +# A grep program that handles long lines.
> +GREP=$lt_GREP
> +
> +# An ERE matcher.
> +EGREP=$lt_EGREP
> +
> +# A literal string matcher.
> +FGREP=$lt_FGREP
> +
> +# A BSD- or MS-compatible name lister.
> +NM=$lt_NM
> +
> +# Whether we need soft or hard links.
> +LN_S=$lt_LN_S
> +
> +# What is the maximum length of a command?
> +max_cmd_len=$max_cmd_len
> +
> +# Object file suffix (normally "o").
> +objext=$ac_objext
> +
> +# Executable file suffix (normally "").
> +exeext=$exeext
> +
> +# whether the shell understands "unset".
> +lt_unset=$lt_unset
> +
> +# turn spaces into newlines.
> +SP2NL=$lt_lt_SP2NL
> +
> +# turn newlines into spaces.
> +NL2SP=$lt_lt_NL2SP
> +
> +# An object symbol dumper.
> +OBJDUMP=$lt_OBJDUMP
> +
> +# Method to check whether dependent libraries are shared objects.
> +deplibs_check_method=$lt_deplibs_check_method
> +
> +# Command to use when deplibs_check_method == "file_magic".
> +file_magic_cmd=$lt_file_magic_cmd
> +
> +# The archiver.
> +AR=$lt_AR
> +AR_FLAGS=$lt_AR_FLAGS
> +
> +# A symbol stripping program.
> +STRIP=$lt_STRIP
> +
> +# Commands used to install an old-style archive.
> +RANLIB=$lt_RANLIB
> +old_postinstall_cmds=$lt_old_postinstall_cmds
> +old_postuninstall_cmds=$lt_old_postuninstall_cmds
> +
> +# Whether to use a lock for old archive extraction.
> +lock_old_archive_extraction=$lock_old_archive_extraction
> +
> +# A C compiler.
> +LTCC=$lt_CC
> +
> +# LTCC compiler flags.
> +LTCFLAGS=$lt_CFLAGS
> +
> +# Take the output of nm and produce a listing of raw symbols and C names.
> +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
> +
> +# Transform the output of nm in a proper C declaration.
> +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
> +
> +# Transform the output of nm in a C name address pair.
> +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
> +
> +# Transform the output of nm in a C name address pair when lib prefix is needed.
> +global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
> +
> +# The name of the directory that contains temporary libtool files.
> +objdir=$objdir
> +
> +# Used to examine libraries when file_magic_cmd begins with "file".
> +MAGIC_CMD=$MAGIC_CMD
> +
> +# Must we lock files when doing compilation?
> +need_locks=$lt_need_locks
> +
> +# Tool to manipulate archived DWARF debug symbol files on Mac OS X.
> +DSYMUTIL=$lt_DSYMUTIL
> +
> +# Tool to change global to local symbols on Mac OS X.
> +NMEDIT=$lt_NMEDIT
> +
> +# Tool to manipulate fat objects and archives on Mac OS X.
> +LIPO=$lt_LIPO
> +
> +# ldd/readelf like tool for Mach-O binaries on Mac OS X.
> +OTOOL=$lt_OTOOL
> +
> +# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4.
> +OTOOL64=$lt_OTOOL64
> +
> +# Old archive suffix (normally "a").
> +libext=$libext
> +
> +# Shared library suffix (normally ".so").
> +shrext_cmds=$lt_shrext_cmds
> +
> +# The commands to extract the exported symbol list from a shared archive.
> +extract_expsyms_cmds=$lt_extract_expsyms_cmds
> +
> +# Variables whose values should be saved in libtool wrapper scripts and
> +# restored at link time.
> +variables_saved_for_relink=$lt_variables_saved_for_relink
> +
> +# Do we need the "lib" prefix for modules?
> +need_lib_prefix=$need_lib_prefix
> +
> +# Do we need a version for libraries?
> +need_version=$need_version
> +
> +# Library versioning type.
> +version_type=$version_type
> +
> +# Shared library runtime path variable.
> +runpath_var=$runpath_var
> +
> +# Shared library path variable.
> +shlibpath_var=$shlibpath_var
> +
> +# Is shlibpath searched before the hard-coded library search path?
> +shlibpath_overrides_runpath=$shlibpath_overrides_runpath
> +
> +# Format of library name prefix.
> +libname_spec=$lt_libname_spec
> +
> +# List of archive names. First name is the real one, the rest are links.
> +# The last name is the one that the linker finds with -lNAME
> +library_names_spec=$lt_library_names_spec
> +
> +# The coded name of the library, if different from the real name.
> +soname_spec=$lt_soname_spec
> +
> +# Permission mode override for installation of shared libraries.
> +install_override_mode=$lt_install_override_mode
> +
> +# Command to use after installation of a shared archive.
> +postinstall_cmds=$lt_postinstall_cmds
> +
> +# Command to use after uninstallation of a shared archive.
> +postuninstall_cmds=$lt_postuninstall_cmds
> +
> +# Commands used to finish a libtool library installation in a directory.
> +finish_cmds=$lt_finish_cmds
> +
> +# As "finish_cmds", except a single script fragment to be evaled but
> +# not shown.
> +finish_eval=$lt_finish_eval
> +
> +# Whether we should hardcode library paths into libraries.
> +hardcode_into_libs=$hardcode_into_libs
> +
> +# Compile-time system search path for libraries.
> +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
> +
> +# Run-time system search path for libraries.
> +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
> +
> +# Whether dlopen is supported.
> +dlopen_support=$enable_dlopen
> +
> +# Whether dlopen of programs is supported.
> +dlopen_self=$enable_dlopen_self
> +
> +# Whether dlopen of statically linked programs is supported.
> +dlopen_self_static=$enable_dlopen_self_static
> +
> +# Commands to strip libraries.
> +old_striplib=$lt_old_striplib
> +striplib=$lt_striplib
> +
> +
> +# The linker used to build libraries.
> +LD=$lt_LD
> +
> +# How to create reloadable object files.
> +reload_flag=$lt_reload_flag
> +reload_cmds=$lt_reload_cmds
> +
> +# Commands used to build an old-style archive.
> +old_archive_cmds=$lt_old_archive_cmds
> +
> +# A language specific compiler.
> +CC=$lt_compiler
> +
> +# Is the compiler the GNU compiler?
> +with_gcc=$GCC
> +
> +# Compiler flag to turn off builtin functions.
> +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag
> +
> +# How to pass a linker flag through the compiler.
> +wl=$lt_lt_prog_compiler_wl
> +
> +# Additional compiler flags for building library objects.
> +pic_flag=$lt_lt_prog_compiler_pic
> +
> +# Compiler flag to prevent dynamic linking.
> +link_static_flag=$lt_lt_prog_compiler_static
> +
> +# Does compiler simultaneously support -c and -o options?
> +compiler_c_o=$lt_lt_cv_prog_compiler_c_o
> +
> +# Whether or not to add -lc for building shared libraries.
> +build_libtool_need_lc=$archive_cmds_need_lc
> +
> +# Whether or not to disallow shared libs when runtime libs are static.
> +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes
> +
> +# Compiler flag to allow reflexive dlopens.
> +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec
> +
> +# Compiler flag to generate shared objects directly from archives.
> +whole_archive_flag_spec=$lt_whole_archive_flag_spec
> +
> +# Whether the compiler copes with passing no objects directly.
> +compiler_needs_object=$lt_compiler_needs_object
> +
> +# Create an old-style archive from a shared archive.
> +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds
> +
> +# Create a temporary old-style archive to link instead of a shared archive.
> +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds
> +
> +# Commands used to build a shared archive.
> +archive_cmds=$lt_archive_cmds
> +archive_expsym_cmds=$lt_archive_expsym_cmds
> +
> +# Commands used to build a loadable module if different from building
> +# a shared archive.
> +module_cmds=$lt_module_cmds
> +module_expsym_cmds=$lt_module_expsym_cmds
> +
> +# Whether we are building with GNU ld or not.
> +with_gnu_ld=$lt_with_gnu_ld
> +
> +# Flag that allows shared libraries with undefined symbols to be built.
> +allow_undefined_flag=$lt_allow_undefined_flag
> +
> +# Flag that enforces no undefined symbols.
> +no_undefined_flag=$lt_no_undefined_flag
> +
> +# Flag to hardcode \$libdir into a binary during linking.
> +# This must work even if \$libdir does not exist
> +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
> +
> +# If ld is used when linking, flag to hardcode \$libdir into a binary
> +# during linking. This must work even if \$libdir does not exist.
> +hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld
> +
> +# Whether we need a single "-rpath" flag with a separated argument.
> +hardcode_libdir_separator=$lt_hardcode_libdir_separator
> +
> +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
> +# DIR into the resulting binary.
> +hardcode_direct=$hardcode_direct
> +
> +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
> +# DIR into the resulting binary and the resulting library dependency is
> +# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
> +# library is relocated.
> +hardcode_direct_absolute=$hardcode_direct_absolute
> +
> +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
> +# into the resulting binary.
> +hardcode_minus_L=$hardcode_minus_L
> +
> +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
> +# into the resulting binary.
> +hardcode_shlibpath_var=$hardcode_shlibpath_var
> +
> +# Set to "yes" if building a shared library automatically hardcodes DIR
> +# into the library and all subsequent libraries and executables linked
> +# against it.
> +hardcode_automatic=$hardcode_automatic
> +
> +# Set to yes if linker adds runtime paths of dependent libraries
> +# to runtime path list.
> +inherit_rpath=$inherit_rpath
> +
> +# Whether libtool must link a program against all its dependency libraries.
> +link_all_deplibs=$link_all_deplibs
> +
> +# Fix the shell variable \$srcfile for the compiler.
> +fix_srcfile_path=$lt_fix_srcfile_path
> +
> +# Set to "yes" if exported symbols are required.
> +always_export_symbols=$always_export_symbols
> +
> +# The commands to list exported symbols.
> +export_symbols_cmds=$lt_export_symbols_cmds
> +
> +# Symbols that should not be listed in the preloaded symbols.
> +exclude_expsyms=$lt_exclude_expsyms
> +
> +# Symbols that must always be exported.
> +include_expsyms=$lt_include_expsyms
> +
> +# Commands necessary for linking programs (against libraries) with templates.
> +prelink_cmds=$lt_prelink_cmds
> +
> +# Specify filename containing input files.
> +file_list_spec=$lt_file_list_spec
> +
> +# How to hardcode a shared library path into an executable.
> +hardcode_action=$hardcode_action
> +
> +# The directories searched by this compiler when creating a shared library.
> +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs
> +
> +# Dependencies to place before and after the objects being linked to
> +# create a shared library.
> +predep_objects=$lt_predep_objects
> +postdep_objects=$lt_postdep_objects
> +predeps=$lt_predeps
> +postdeps=$lt_postdeps
> +
> +# The library search path used internally by the compiler when linking
> +# a shared library.
> +compiler_lib_search_path=$lt_compiler_lib_search_path
> +
> +# ### END LIBTOOL CONFIG
> +
> +_LT_EOF
> +
> + case $host_os in
> + aix3*)
> + cat <<\_LT_EOF >> "$cfgfile"
> +# AIX sometimes has problems with the GCC collect2 program. For some
> +# reason, if we set the COLLECT_NAMES environment variable, the problems
> +# vanish in a puff of smoke.
> +if test "X${COLLECT_NAMES+set}" != Xset; then
> + COLLECT_NAMES=
> + export COLLECT_NAMES
> +fi
> +_LT_EOF
> + ;;
> + esac
> +
> +
> +ltmain="$ac_aux_dir/ltmain.sh"
> +
> +
> + # We use sed instead of cat because bash on DJGPP gets confused if
> + # if finds mixed CR/LF and LF-only lines. Since sed operates in
> + # text mode, it properly converts lines to CR/LF. This bash problem
> + # is reportedly fixed, but why not run on old versions too?
> + sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \
> + || (rm -f "$cfgfile"; exit 1)
> +
> + case $xsi_shell in
> + yes)
> + cat << \_LT_EOF >> "$cfgfile"
> +
> +# func_dirname file append nondir_replacement
> +# Compute the dirname of FILE. If nonempty, add APPEND to the result,
> +# otherwise set result to NONDIR_REPLACEMENT.
> +func_dirname ()
> +{
> + case ${1} in
> + */*) func_dirname_result="${1%/*}${2}" ;;
> + * ) func_dirname_result="${3}" ;;
> + esac
> +}
> +
> +# func_basename file
> +func_basename ()
> +{
> + func_basename_result="${1##*/}"
> +}
> +
> +# func_dirname_and_basename file append nondir_replacement
> +# perform func_basename and func_dirname in a single function
> +# call:
> +# dirname: Compute the dirname of FILE. If nonempty,
> +# add APPEND to the result, otherwise set result
> +# to NONDIR_REPLACEMENT.
> +# value returned in "$func_dirname_result"
> +# basename: Compute filename of FILE.
> +# value retuned in "$func_basename_result"
> +# Implementation must be kept synchronized with func_dirname
> +# and func_basename. For efficiency, we do not delegate to
> +# those functions but instead duplicate the functionality here.
> +func_dirname_and_basename ()
> +{
> + case ${1} in
> + */*) func_dirname_result="${1%/*}${2}" ;;
> + * ) func_dirname_result="${3}" ;;
> + esac
> + func_basename_result="${1##*/}"
> +}
> +
> +# func_stripname prefix suffix name
> +# strip PREFIX and SUFFIX off of NAME.
> +# PREFIX and SUFFIX must not contain globbing or regex special
> +# characters, hashes, percent signs, but SUFFIX may contain a leading
> +# dot (in which case that matches only a dot).
> +func_stripname ()
> +{
> + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
> + # positional parameters, so assign one to ordinary parameter first.
> + func_stripname_result=${3}
> + func_stripname_result=${func_stripname_result#"${1}"}
> + func_stripname_result=${func_stripname_result%"${2}"}
> +}
> +
> +# func_opt_split
> +func_opt_split ()
> +{
> + func_opt_split_opt=${1%%=*}
> + func_opt_split_arg=${1#*=}
> +}
> +
> +# func_lo2o object
> +func_lo2o ()
> +{
> + case ${1} in
> + *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
> + *) func_lo2o_result=${1} ;;
> + esac
> +}
> +
> +# func_xform libobj-or-source
> +func_xform ()
> +{
> + func_xform_result=${1%.*}.lo
> +}
> +
> +# func_arith arithmetic-term...
> +func_arith ()
> +{
> + func_arith_result=$(( $* ))
> +}
> +
> +# func_len string
> +# STRING may not start with a hyphen.
> +func_len ()
> +{
> + func_len_result=${#1}
> +}
> +
> +_LT_EOF
> + ;;
> + *) # Bourne compatible functions.
> + cat << \_LT_EOF >> "$cfgfile"
> +
> +# func_dirname file append nondir_replacement
> +# Compute the dirname of FILE. If nonempty, add APPEND to the result,
> +# otherwise set result to NONDIR_REPLACEMENT.
> +func_dirname ()
> +{
> + # Extract subdirectory from the argument.
> + func_dirname_result=`$ECHO "${1}" | $SED "$dirname"`
> + if test "X$func_dirname_result" = "X${1}"; then
> + func_dirname_result="${3}"
> + else
> + func_dirname_result="$func_dirname_result${2}"
> + fi
> +}
> +
> +# func_basename file
> +func_basename ()
> +{
> + func_basename_result=`$ECHO "${1}" | $SED "$basename"`
> +}
> +
> +
> +# func_stripname prefix suffix name
> +# strip PREFIX and SUFFIX off of NAME.
> +# PREFIX and SUFFIX must not contain globbing or regex special
> +# characters, hashes, percent signs, but SUFFIX may contain a leading
> +# dot (in which case that matches only a dot).
> +# func_strip_suffix prefix name
> +func_stripname ()
> +{
> + case ${2} in
> + .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;;
> + *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;;
> + esac
> +}
> +
> +# sed scripts:
> +my_sed_long_opt='1s/^\(-[^=]*\)=.*/\1/;q'
> +my_sed_long_arg='1s/^-[^=]*=//'
> +
> +# func_opt_split
> +func_opt_split ()
> +{
> + func_opt_split_opt=`$ECHO "${1}" | $SED "$my_sed_long_opt"`
> + func_opt_split_arg=`$ECHO "${1}" | $SED "$my_sed_long_arg"`
> +}
> +
> +# func_lo2o object
> +func_lo2o ()
> +{
> + func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"`
> +}
> +
> +# func_xform libobj-or-source
> +func_xform ()
> +{
> + func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'`
> +}
> +
> +# func_arith arithmetic-term...
> +func_arith ()
> +{
> + func_arith_result=`expr "$@"`
> +}
> +
> +# func_len string
> +# STRING may not start with a hyphen.
> +func_len ()
> +{
> + func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len`
> +}
> +
> +_LT_EOF
> +esac
> +
> +case $lt_shell_append in
> + yes)
> + cat << \_LT_EOF >> "$cfgfile"
> +
> +# func_append var value
> +# Append VALUE to the end of shell variable VAR.
> +func_append ()
> +{
> + eval "$1+=\$2"
> +}
> +_LT_EOF
> + ;;
> + *)
> + cat << \_LT_EOF >> "$cfgfile"
> +
> +# func_append var value
> +# Append VALUE to the end of shell variable VAR.
> +func_append ()
> +{
> + eval "$1=\$$1\$2"
> +}
> +
> +_LT_EOF
> + ;;
> + esac
> +
> +
> + sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \
> + || (rm -f "$cfgfile"; exit 1)
> +
> + mv -f "$cfgfile" "$ofile" ||
> + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
> + chmod +x "$ofile"
> +
> +
> + cat <<_LT_EOF >> "$ofile"
> +
> +# ### BEGIN LIBTOOL TAG CONFIG: CXX
> +
> +# The linker used to build libraries.
> +LD=$lt_LD_CXX
> +
> +# How to create reloadable object files.
> +reload_flag=$lt_reload_flag_CXX
> +reload_cmds=$lt_reload_cmds_CXX
> +
> +# Commands used to build an old-style archive.
> +old_archive_cmds=$lt_old_archive_cmds_CXX
> +
> +# A language specific compiler.
> +CC=$lt_compiler_CXX
> +
> +# Is the compiler the GNU compiler?
> +with_gcc=$GCC_CXX
> +
> +# Compiler flag to turn off builtin functions.
> +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX
> +
> +# How to pass a linker flag through the compiler.
> +wl=$lt_lt_prog_compiler_wl_CXX
> +
> +# Additional compiler flags for building library objects.
> +pic_flag=$lt_lt_prog_compiler_pic_CXX
> +
> +# Compiler flag to prevent dynamic linking.
> +link_static_flag=$lt_lt_prog_compiler_static_CXX
> +
> +# Does compiler simultaneously support -c and -o options?
> +compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX
> +
> +# Whether or not to add -lc for building shared libraries.
> +build_libtool_need_lc=$archive_cmds_need_lc_CXX
> +
> +# Whether or not to disallow shared libs when runtime libs are static.
> +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX
> +
> +# Compiler flag to allow reflexive dlopens.
> +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX
> +
> +# Compiler flag to generate shared objects directly from archives.
> +whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX
> +
> +# Whether the compiler copes with passing no objects directly.
> +compiler_needs_object=$lt_compiler_needs_object_CXX
> +
> +# Create an old-style archive from a shared archive.
> +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX
> +
> +# Create a temporary old-style archive to link instead of a shared archive.
> +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX
> +
> +# Commands used to build a shared archive.
> +archive_cmds=$lt_archive_cmds_CXX
> +archive_expsym_cmds=$lt_archive_expsym_cmds_CXX
> +
> +# Commands used to build a loadable module if different from building
> +# a shared archive.
> +module_cmds=$lt_module_cmds_CXX
> +module_expsym_cmds=$lt_module_expsym_cmds_CXX
> +
> +# Whether we are building with GNU ld or not.
> +with_gnu_ld=$lt_with_gnu_ld_CXX
> +
> +# Flag that allows shared libraries with undefined symbols to be built.
> +allow_undefined_flag=$lt_allow_undefined_flag_CXX
> +
> +# Flag that enforces no undefined symbols.
> +no_undefined_flag=$lt_no_undefined_flag_CXX
> +
> +# Flag to hardcode \$libdir into a binary during linking.
> +# This must work even if \$libdir does not exist
> +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX
> +
> +# If ld is used when linking, flag to hardcode \$libdir into a binary
> +# during linking. This must work even if \$libdir does not exist.
> +hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_CXX
> +
> +# Whether we need a single "-rpath" flag with a separated argument.
> +hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX
> +
> +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
> +# DIR into the resulting binary.
> +hardcode_direct=$hardcode_direct_CXX
> +
> +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
> +# DIR into the resulting binary and the resulting library dependency is
> +# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
> +# library is relocated.
> +hardcode_direct_absolute=$hardcode_direct_absolute_CXX
> +
> +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR
> +# into the resulting binary.
> +hardcode_minus_L=$hardcode_minus_L_CXX
> +
> +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR
> +# into the resulting binary.
> +hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX
> +
> +# Set to "yes" if building a shared library automatically hardcodes DIR
> +# into the library and all subsequent libraries and executables linked
> +# against it.
> +hardcode_automatic=$hardcode_automatic_CXX
> +
> +# Set to yes if linker adds runtime paths of dependent libraries
> +# to runtime path list.
> +inherit_rpath=$inherit_rpath_CXX
> +
> +# Whether libtool must link a program against all its dependency libraries.
> +link_all_deplibs=$link_all_deplibs_CXX
> +
> +# Fix the shell variable \$srcfile for the compiler.
> +fix_srcfile_path=$lt_fix_srcfile_path_CXX
> +
> +# Set to "yes" if exported symbols are required.
> +always_export_symbols=$always_export_symbols_CXX
> +
> +# The commands to list exported symbols.
> +export_symbols_cmds=$lt_export_symbols_cmds_CXX
> +
> +# Symbols that should not be listed in the preloaded symbols.
> +exclude_expsyms=$lt_exclude_expsyms_CXX
> +
> +# Symbols that must always be exported.
> +include_expsyms=$lt_include_expsyms_CXX
> +
> +# Commands necessary for linking programs (against libraries) with templates.
> +prelink_cmds=$lt_prelink_cmds_CXX
> +
> +# Specify filename containing input files.
> +file_list_spec=$lt_file_list_spec_CXX
> +
> +# How to hardcode a shared library path into an executable.
> +hardcode_action=$hardcode_action_CXX
> +
> +# The directories searched by this compiler when creating a shared library.
> +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX
> +
> +# Dependencies to place before and after the objects being linked to
> +# create a shared library.
> +predep_objects=$lt_predep_objects_CXX
> +postdep_objects=$lt_postdep_objects_CXX
> +predeps=$lt_predeps_CXX
> +postdeps=$lt_postdeps_CXX
> +
> +# The library search path used internally by the compiler when linking
> +# a shared library.
> +compiler_lib_search_path=$lt_compiler_lib_search_path_CXX
> +
> +# ### END LIBTOOL TAG CONFIG: CXX
> +_LT_EOF
> +
> +
> +as_fn_exit 0
> diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
> new file mode 100644
> index 00000000000..60d80aae0a6
> --- /dev/null
> +++ b/gdb/riscv-canonicalize-syscall-gen.c
> @@ -0,0 +1,338 @@
> +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py
> +
> + Copyright (C) 2024 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"
> +
> +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 12cab06f276..ebaed9b4508 100644
> --- a/gdb/riscv-linux-tdep.c
> +++ b/gdb/riscv-linux-tdep.c
> @@ -187,9 +187,11 @@ static linux_record_tdep riscv_linux_record_tdep;
> using regnum_type = int;
>
> static bool
> -save_registers (struct regcache *regcache, regnum_type fir, regnum_type last)
> +save_registers (struct regcache *regcache, regnum_type first, regnum_type last)
> {
> - for (regnum_type i = fir; i != last; ++i)
> + 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;
> @@ -198,6 +200,8 @@ save_registers (struct regcache *regcache, regnum_type fir, regnum_type last)
> static bool
> riscv_all_but_pc_registers_record (struct regcache *regcache)
> {
> + gdb_assert (regcache != nullptr);
> +
> auto &&gdbarch = regcache->arch ();
> auto &&tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> auto &&features = tdep->isa_features;
> @@ -219,12 +223,12 @@ static int
> riscv_linux_syscall_record (struct regcache *regcache,
> unsigned long svc_number)
> {
> + gdb_assert (regcache != nullptr);
> +
> auto syscall_gdb = riscv64_canonicalize_syscall (svc_number);
>
> if (record_debug > 1)
> - {
> - gdb_printf (gdb_stdlog, "Made syscall %s.\n", plongest (svc_number));
> - }
> + gdb_printf (gdb_stdlog, "Made syscall %s.\n", plongest (svc_number));
>
> if (syscall_gdb == gdb_sys_no_syscall)
> {
> @@ -258,9 +262,11 @@ riscv_linux_syscall_record (struct regcache *regcache,
> /* These values are the size of the type that will be used in a system
> call. They are obtained from Linux Kernel source. */
> static void
> -riscv64_linux_record_tdep_init (
> - struct gdbarch *gdbarch, struct linux_record_tdep &riscv_linux_record_tdep)
> +riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
> + struct linux_record_tdep &riscv_linux_record_tdep)
> {
> + gdb_assert (gdbarch != nullptr);
> +
> /* Initialize the riscv_linux_record_tdep. */
> /* These values are the size of the type that will be used in a system
> call. They are obtained from Linux Kernel source. */
> @@ -427,7 +433,7 @@ riscv64_linux_record_tdep_init (
> static void
> riscv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
> {
> - riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> + auto tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
>
> linux_init_abi (info, gdbarch, 0);
>
> diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
> index c6c7b7a63fd..37ec30fff91 100644
> --- a/gdb/riscv-tdep.c
> +++ b/gdb/riscv-tdep.c
> @@ -4843,7 +4843,7 @@ this option can be used."),
> static bool
> try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
> {
> - gdb_assert (regcache);
> + gdb_assert (regcache != nullptr);
>
> if (regcache->raw_read<ULONGEST> (regnum, &addr)
> != register_status::REG_VALID)
> @@ -4964,12 +4964,10 @@ class riscv_recorded_insn final
> read_reg (struct regcache *regcache, regnum_type reg,
> ULONGEST &addr) noexcept
> {
> - gdb_assert (regcache);
> + gdb_assert (regcache != nullptr);
>
> if (!try_read (regcache, reg, addr))
> - {
> - return set_error ();
> - }
> + return set_error ();
> return true;
> }
>
> @@ -5143,14 +5141,14 @@ class riscv_recorded_insn final
> bool
> try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
> {
> - gdb_assert (regcache);
> + gdb_assert (regcache != nullptr);
>
> - mem_addr addr = 0;
> + auto addr = mem_addr{};
> auto len = need_save_pc_mem (ival);
> if (len <= 0)
> return false;
>
> - mem_len offset = EXTRACT_STYPE_IMM (ival);
> + auto offset = mem_len{EXTRACT_STYPE_IMM (ival)};
> return !read_reg (regcache, decode_rs1 (ival), addr)
> || !save_mem (addr + offset, len) || set_ordinary_record_type ();
> }
> @@ -5178,10 +5176,10 @@ class riscv_recorded_insn final
> bool
> try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
> {
> - gdb_assert (regcache);
> + gdb_assert (regcache != nullptr);
>
> auto len = need_save_pc_rd_mem (ival);
> - mem_addr addr = 0;
> + auto addr = mem_addr{};
> if (len <= 0)
> return false;
>
> @@ -5205,9 +5203,9 @@ class riscv_recorded_insn final
> bool
> try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
> {
> - gdb_assert (regcache);
> + gdb_assert (regcache != nullptr);
>
> - mem_addr addr = 0;
> + auto addr = mem_addr{};
> auto len = need_save_pc_rs2_rd_mem (ival);
> if (len <= 0)
> return false;
> @@ -5222,7 +5220,7 @@ class riscv_recorded_insn final
> bool
> record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept
> {
> - gdb_assert (regcache);
> + gdb_assert (regcache != nullptr);
>
> if (is_ecall_insn (ival))
> {
> @@ -5240,9 +5238,7 @@ class riscv_recorded_insn final
> || try_save_pc_rd_csr (ival) || try_save_pc_mem (ival, regcache)
> || try_save_pc_rd_mem (ival, regcache)
> || try_save_pc_rs2_rd_mem (ival, regcache))
> - {
> - return !has_error ();
> - }
> + return !has_error ();
>
> warning (_ ("Currently this instruction with len 4(%lx) is unsupported"),
> ival);
> @@ -5255,10 +5251,10 @@ class riscv_recorded_insn final
> record_insn_len2 (ULONGEST ival, struct regcache *regcache,
> struct gdbarch *gdbarch) noexcept
> {
> - gdb_assert (regcache);
> - gdb_assert (gdbarch);
> + gdb_assert (regcache != nullptr);
> + gdb_assert (gdbarch != nullptr);
>
> - mem_addr addr = 0;
> + auto addr = mem_addr{};
> auto xlen = riscv_isa_xlen (gdbarch);
>
> /* The order here is very important, because
> @@ -5266,100 +5262,70 @@ class riscv_recorded_insn final
>
> if (is_c_addi4spn_insn (ival) || is_c_lw_insn (ival)
> || (xlen == 8 && is_c_ld_insn (ival)))
> - {
> - return !save_reg (decode_crs2_short (ival))
> - || set_ordinary_record_type ();
> - }
> + return !save_reg (decode_crs2_short (ival))
> + || set_ordinary_record_type ();
>
> if (is_c_fld_insn (ival) || (xlen == 4 && is_c_flw_insn (ival)))
> - {
> - return !save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival))
> - || set_ordinary_record_type ();
> - }
> + return !save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival))
> + || set_ordinary_record_type ();
>
> if (is_c_fsd_insn (ival) || (xlen == 8 && is_c_sd_insn (ival)))
> {
> - int offset = EXTRACT_CLTYPE_LD_IMM (ival);
> + auto offset = int{EXTRACT_CLTYPE_LD_IMM (ival)};
> return !read_reg (regcache, decode_crs1_short (ival), addr)
> || !save_mem (addr + offset, 8) || set_ordinary_record_type ();
> }
>
> if ((xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
> {
> - int offset = EXTRACT_CLTYPE_LW_IMM (ival);
> + auto offset = int{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 ();
> - }
> + return set_ordinary_record_type ();
>
> if (is_c_addi_insn (ival))
> - {
> - return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
> - }
> + return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
>
> if (xlen == 4 && is_c_jal_insn (ival))
> - {
> - return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
> - }
> + return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
>
> if ((xlen == 8 && is_c_addiw_insn (ival)) || is_c_li_insn (ival))
> - {
> - return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
> - }
> + 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 ();
> - }
> + 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 ();
> - }
> + 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) || (xlen == 8 && is_c_subw_insn (ival))
> || (xlen == 8 && is_c_addw_insn (ival)))
> - {
> - return !save_reg (decode_crs1_short (ival))
> - || set_ordinary_record_type ();
> - }
> + 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 ();
> - }
> + return set_ordinary_record_type ();
>
> if (is_c_slli_insn (ival))
> - {
> - return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
> - }
> + return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
>
> if (is_c_fldsp_insn (ival) || (xlen == 4 && is_c_flwsp_insn (ival)))
> - {
> - return !save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival))
> - || set_ordinary_record_type ();
> - }
> + return !save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival))
> + || set_ordinary_record_type ();
>
> if (is_c_lwsp_insn (ival) || (xlen == 8 && is_c_ldsp_insn (ival)))
> - {
> - return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
> - }
> + return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
>
> if (is_c_jr_insn (ival))
> - {
> - return set_ordinary_record_type ();
> - }
> + return set_ordinary_record_type ();
>
> if (is_c_mv_insn (ival))
> - {
> - return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
> - }
> + return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
>
> if (is_c_ebreak_insn (ival))
> {
> @@ -5368,25 +5334,21 @@ class riscv_recorded_insn final
> }
>
> if (is_c_jalr_insn (ival))
> - {
> - return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
> - }
> + 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 ();
> - }
> + return !save_reg (decode_crs1 (ival)) || set_ordinary_record_type ();
>
> if (is_c_fsdsp_insn (ival) || (xlen == 8 && is_c_sdsp_insn (ival)))
> {
> - int offset = EXTRACT_CSSTYPE_SDSP_IMM (ival);
> + auto offset = int{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) || (xlen == 4 && is_c_fswsp_insn (ival)))
> {
> - int offset = EXTRACT_CSSTYPE_SWSP_IMM (ival);
> + auto offset = int{EXTRACT_CSSTYPE_SWSP_IMM (ival)};
> return !read_reg (regcache, RISCV_SP_REGNUM, addr)
> || !save_mem (addr + offset, 4) || set_ordinary_record_type ();
> }
> @@ -5403,7 +5365,10 @@ class riscv_recorded_insn final
> bool
> record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
> {
> - int m_length = 0;
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + auto m_length = int{};
> auto ival = riscv_insn::fetch_instruction (gdbarch, addr, &m_length);
> if (!save_reg (RISCV_PC_REGNUM))
> return false;
> @@ -5458,9 +5423,10 @@ static int
> riscv_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
> const riscv_recorded_insn &insn)
> {
> - gdb_assert (gdbarch && regcache);
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
>
> - riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> + auto tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> auto regs_begin = insn.regs_begin ();
> auto regs_end = insn.regs_end ();
> if (std::any_of (regs_begin, regs_end, [®cache] (auto &®_it) {
> @@ -5487,11 +5453,9 @@ riscv_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
> warning (_ ("Syscall record is not supported"));
> return -1;
> }
> - ULONGEST reg_val = 0;
> + auto reg_val = ULONGEST{};
> if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
> - {
> - return -1;
> - }
> + return -1;
> return tdep->riscv_syscall_record (regcache, reg_val);
> }
>
> @@ -5508,7 +5472,8 @@ int
> riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
> CORE_ADDR addr)
> {
> - gdb_assert (gdbarch && regcache);
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
>
> riscv_recorded_insn insn;
> if (!insn.record (gdbarch, regcache, addr))
> @@ -5520,9 +5485,7 @@ riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
> auto ret_val = riscv_make_record_process (gdbarch, regcache, insn);
>
> if (record_full_arch_list_add_end ())
> - {
> - return -1;
> - }
> + return -1;
>
> return ret_val;
> }
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 35+ messages in thread* [PATCH v4] This commit adds record full support for rv64gc instruction set.
2024-12-13 18:10 ` Guinevere Larsen
@ 2024-12-17 0:10 ` Timur
2024-12-17 14:23 ` Guinevere Larsen
0 siblings, 1 reply; 35+ messages in thread
From: Timur @ 2024-12-17 0:10 UTC (permalink / raw)
To: gdb-patches; +Cc: Timur
It includes changes to the following files:
- gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
syscalls.
- gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
rv64gc instructions.
- gdb/configure.tgt: adds new files for compilation.
- gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
targets.
- gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate function
that canonicalizes RISC-V syscall. This script can simplify support for syscalls
on rv32 system. To use this script you need to pass a path to a file with
syscalls description from riscv-glibc (example is in the help message). The
script produces a mapping from syscall names to gdb_syscall enum.
- gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
Timur Golubovich timurgol007@gmail.com
Hi! I am sorry, I did incorrect previos time (made tab so as `config.lt` was
added). So I will duplicate my answers from the previous letter.
> >> + switch (syscall)
> >> + {
> >> + case 0: // #define __NR_io_setup 0
> >>
> >> All comments should be in the form /* */, rather than //
> >>
>>> I would also prefer if this followed what other architectures do,
>>> defining an enum to relate syscall numbers in a RISCV system to their
>>> names, and then this function returning the gdb_sys enum numbers,
>>> however I don't expect to be working on RISCV in the near future, so
>>> feel free to ignore this comment if the other RISCV maintainers are ok
>>> with this style. This would make it so no #define comment is necessary
>> Decided to make a script generating function that canonicalizes RISC-V syscall.
>> This solution will simplify adding record support for rv32 system. I also left
>> place for corner cases in generating script as they always may exist. Also
>> removed this #define comment from generating script.
>
> I see. This makes sense.
>
> I would like to see this explanation on the commit message, so that it
> is recorded why Risc-V works different to all other arches.
Could you please double-check the commit message? I tried to make it more
detailed. Do you think I need to provide additional information?
>>> +
>>> + if (m_length == 2)
>>> + return record_insn_len2 (ival, regcache, gdbarch);
>>> +
>>> + /* 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. */
>>> Again, the indentation here is incorrect. Lines should start in the same column as the number 6.
>> Addressed
>>
>> Best wishes,
>> Timur Golubovich
>>
>> Timur Golubovich timurgol007@gmail.com
>
> As I mentioned in the first review, please add some commit message
> explaining a bit of what your patch is doing. I would recommend
> explaining the inspiration for how riscv-tdep handles the recording (I
> guess aarch64) and why you changed it; plus explaining the idea behind
> the python script.c
Added this information to the commit message.
>> /* The following value is derived from __NR_rt_sigreturn in
>> <include/uapi/asm-generic/unistd.h> from the Linux source tree. */
>> @@ -173,6 +178,250 @@ 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;
>> +
>> +/* Record all registers but PC register for process-record. */
>> +
>> +using regnum_type = int;
>> +
>> +static bool
>> +save_registers (struct regcache *regcache, regnum_type fir, regnum_type last)
>
> Please fully type the variable names (fir -> first). Even if this makes
> you need to use multiple lines for the function, I think this makes the
> function easier to understand in the future.
>
> Sorry about not mentioning this before, I missed it in my first pass.
Addressed
>> + if (syscall_gdb == gdb_sys_sigreturn || syscall_gdb == gdb_sys_rt_sigreturn)
>> + {
>> + if (!riscv_all_but_pc_registers_record (regcache))
>> + return -1;
>> + return 0;
>> + }
>> +
>> + auto 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. */
>> +/* These values are the size of the type that will be used in a system
>> + call. They are obtained from Linux Kernel source. */
>> +static void
>> +riscv64_linux_record_tdep_init (
>> + struct gdbarch *gdbarch, struct linux_record_tdep &riscv_linux_record_tdep)
>
> Oops, also missed this one on the first pass.
>
> The preferred way to do multi-line arguments is:
>
> static void
> riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
> struct
> linux_record_tdep &riscv_linux_record_tdep)
> {
>
> (with the "struct" keywords being aligned)
Addressed
>> + {
>> + gdb_assert (regcache);
>> +
>> + if (!try_read (regcache, reg, addr))
>> + {
>> + return set_error ();
>> + }
>> + return true;
>> + }
>> +
>> + bool
>> + save_reg (regnum_type regnum) noexcept
>> + {
>> + try
>> + {
>> + m_regs.emplace_back (regnum);
>
> Similar to my thoughts on Loongarch, I don't think this approach is the
> best.
>
> In short, recording is already considered very slow. The strategy this
> patch uses is to first collect all the data to be recorded (which
> demands dynamic allocations to std::vector), and then call
> record_full_arch_list_add_reg or record_full_arch_list_add_mem, which
> will do their own dynamic allocations. This seems wasteful to me.
>
> I believe it would be better if, instead, the save_reg and save_mem
> methods would directly call the correct record_full_arch_list_add
> function, so that only one allocation is needed per unit of data that
> needs recording.
>
> Since aarch64 already uses the strategy in this patch I won't block
> merging based on this feedback, if you're set on this idea, but I think
> direct calls would be better.
I think that this overhead is negligeble, since the number of allocation
because most instructions affect only 1 register. Moreover, I feel that the
suggested approach simplifies error handling, since I can decouple decoding
procedure from the actual error detection. If you don't mind, I would suggest
to leave it as it is.
>> + 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)
>> + || try_save_pc_rs2_rd_mem (ival, regcache))
>> + {
>> + return !has_error ();
>> + }
> Minor nit, but I noticed around here that this (and some lines later,
> maybe some before) don't need the braces.
Addressed
>> + 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;
>> +}
>> +
>> +int
>> +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
>> + CORE_ADDR addr)
>> +{
>> + gdb_assert (gdbarch && regcache);
> null pointer checks should be explicit, ie, gdb_assert (gdbarch !=
> nullptr && regcache != nullptr)
Addressed.
Best wishes,
Timur Golubovich
---
gdb/configure.tgt | 4 +-
gdb/riscv-canonicalize-syscall-gen.c | 338 +++++++++
gdb/riscv-linux-tdep.c | 260 ++++++-
gdb/riscv-linux-tdep.h | 28 +
gdb/riscv-tdep.c | 666 +++++++++++++++++-
gdb/riscv-tdep.h | 15 +
.../riscv-canonicalize-syscall-gen.py | 143 ++++
gdb/testsuite/lib/gdb.exp | 3 +-
8 files changed, 1448 insertions(+), 9 deletions(-)
create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
create mode 100644 gdb/riscv-linux-tdep.h
create mode 100644 gdb/syscalls/riscv-canonicalize-syscall-gen.py
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 62df71b13fa..b350e1f6505 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -545,8 +545,8 @@ 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/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
new file mode 100644
index 00000000000..60d80aae0a6
--- /dev/null
+++ b/gdb/riscv-canonicalize-syscall-gen.c
@@ -0,0 +1,338 @@
+/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py
+
+ Copyright (C) 2024 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"
+
+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 ff478cf4c28..ebaed9b4508 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,12 +178,262 @@ 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;
+
+/* Record all registers but PC register for process-record. */
+
+using regnum_type = int;
+
+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;
+};
+
+static bool
+riscv_all_but_pc_registers_record (struct regcache *regcache)
+{
+ gdb_assert (regcache != nullptr);
+
+ auto &&gdbarch = regcache->arch ();
+ auto &&tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto &&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);
+
+ auto 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)
+ {
+ gdb_printf (gdb_stderr,
+ _ ("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;
+ }
+
+ auto 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. */
+/* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+static void
+riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
+ struct linux_record_tdep &riscv_linux_record_tdep)
+{
+ gdb_assert (gdbarch != nullptr);
+
+ /* Initialize the riscv_linux_record_tdep. */
+ /* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+ 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;
+ riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
+}
+
/* Initialize RISC-V Linux ABI info. */
static void
riscv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
- riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
linux_init_abi (info, gdbarch, 0);
@@ -205,6 +460,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 00000000000..a0d67003a81
--- /dev/null
+++ b/gdb/riscv-linux-tdep.h
@@ -0,0 +1,28 @@
+/* Copyright (C) 2024 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 932708ca4e9..37ec30fff91 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
@@ -1655,6 +1658,11 @@ class riscv_insn
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. */
@@ -1800,11 +1808,6 @@ class riscv_insn
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;
@@ -4415,6 +4418,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);
@@ -4833,3 +4839,653 @@ this option can be used."),
&setriscvcmdlist,
&showriscvcmdlist);
}
+
+static bool
+try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
+{
+ gdb_assert (regcache != nullptr);
+
+ if (regcache->raw_read<ULONGEST> (regnum, &addr)
+ != register_status::REG_VALID)
+ {
+ warning (_ ("Can not read at address %lx"), addr);
+ return false;
+ }
+ return true;
+}
+
+class riscv_recorded_insn final
+{
+public:
+ using regnum_type = int;
+ using memory_type = std::pair<CORE_ADDR, int>;
+
+ enum class record_type
+ {
+ UNKNOWN,
+ ORDINARY,
+
+ /* Corner cases. */
+ ECALL,
+ EBREAK,
+ };
+
+private:
+ using recorded_regs = std::vector<regnum_type>;
+ using recorded_mems = std::vector<memory_type>;
+
+ using mem_addr = decltype (std::declval<memory_type> ().first);
+ using mem_len = decltype (std::declval<memory_type> ().second);
+
+ record_type m_record_type = record_type::UNKNOWN;
+
+ bool m_error_occured = false;
+
+ recorded_regs m_regs;
+ recorded_mems m_mems;
+
+ /* 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;
+ }
+
+ bool
+ set_ordinary_record_type () noexcept
+ {
+ m_record_type = record_type::ORDINARY;
+ return true;
+ }
+
+ bool
+ set_error () noexcept
+ {
+ m_error_occured = true;
+ return false;
+ }
+
+ bool
+ has_error () const noexcept
+ {
+ return m_error_occured;
+ }
+
+ 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;
+ }
+
+ bool
+ save_reg (regnum_type regnum) noexcept
+ {
+ try
+ {
+ m_regs.emplace_back (regnum);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ bool
+ save_mem (mem_addr addr, mem_len len) noexcept
+ {
+ try
+ {
+ m_mems.emplace_back (addr, len);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ 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 ();
+ }
+
+ 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_ld_insn (ival) || is_lbu_insn (ival)
+ || is_lhu_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
+ || is_lw_insn (ival) || is_ld_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_insn (ival) || is_srli_insn (ival)
+ || is_srai_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_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_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_d_w_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. Needs error checking after. */
+ 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 ();
+ }
+
+ static bool
+ need_save_pc_fprd (ULONGEST ival) noexcept
+ {
+ return 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_flw_insn (ival) || is_fld_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_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_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. Needs error checking after. */
+ 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 ();
+ }
+
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, than instruction needs saving memory len. */
+ 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. Needs error checking after. */
+ bool
+ try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ auto addr = mem_addr{};
+ auto len = need_save_pc_mem (ival);
+ if (len <= 0)
+ return false;
+
+ auto offset = mem_len{EXTRACT_STYPE_IMM (ival)};
+ return !read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr + offset, len) || set_ordinary_record_type ();
+ }
+
+ /* If return value is > 0, than instruction needs saving memory len. */
+ static mem_len
+ need_save_pc_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_sc_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_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. Needs error checking after. */
+ bool
+ try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ auto len = need_save_pc_rd_mem (ival);
+ auto addr = mem_addr{};
+ 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 ();
+ }
+
+ /* If return value is > 0, than instruction needs saving memory len. */
+ static mem_len
+ need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_amoswap_w_insn (ival))
+ return 4;
+ if (is_amoswap_d_insn (ival))
+ return 8;
+ return 0;
+ }
+
+ /* Returns true if instruction is classified. Needs error checking after. */
+ bool
+ try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ auto addr = mem_addr{};
+ auto len = need_save_pc_rs2_rd_mem (ival);
+ if (len <= 0)
+ return false;
+
+ return !read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
+ || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
+ }
+
+ /* Returns true if instruction is successfully recorder.
+ Else returns false. */
+ 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)
+ || try_save_pc_rs2_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 recorder.
+ Else returns false. */
+ bool
+ record_insn_len2 (ULONGEST ival, struct regcache *regcache,
+ struct gdbarch *gdbarch) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+ gdb_assert (gdbarch != nullptr);
+
+ auto addr = mem_addr{};
+ auto xlen = riscv_isa_xlen (gdbarch);
+
+ /* 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)
+ || (xlen == 8 && is_c_ld_insn (ival)))
+ return !save_reg (decode_crs2_short (ival))
+ || set_ordinary_record_type ();
+
+ if (is_c_fld_insn (ival) || (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) || (xlen == 8 && is_c_sd_insn (ival)))
+ {
+ auto offset = int{EXTRACT_CLTYPE_LD_IMM (ival)};
+ return !read_reg (regcache, decode_crs1_short (ival), addr)
+ || !save_mem (addr + offset, 8) || set_ordinary_record_type ();
+ }
+
+ if ((xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
+ {
+ auto offset = int{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 (xlen == 4 && is_c_jal_insn (ival))
+ return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
+
+ if ((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) || (xlen == 8 && is_c_subw_insn (ival))
+ || (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) || (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) || (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) || (xlen == 8 && is_c_sdsp_insn (ival)))
+ {
+ auto offset = int{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) || (xlen == 4 && is_c_fswsp_insn (ival)))
+ {
+ auto offset = int{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:
+ using regs_iter = recorded_regs::const_iterator;
+ using mems_iter = recorded_mems::const_iterator;
+
+ bool
+ record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
+ {
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ auto m_length = int{};
+ auto 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, gdbarch);
+
+ /* 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;
+ }
+
+ record_type
+ get_record_type () const noexcept
+ {
+ return m_record_type;
+ }
+
+ regs_iter
+ regs_begin () const noexcept
+ {
+ return m_regs.begin ();
+ }
+
+ regs_iter
+ regs_end () const noexcept
+ {
+ return m_regs.end ();
+ }
+
+ mems_iter
+ mems_begin () const noexcept
+ {
+ return m_mems.begin ();
+ }
+
+ mems_iter
+ mems_end () const noexcept
+ {
+ return m_mems.end ();
+ }
+};
+
+static int
+riscv_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
+ const riscv_recorded_insn &insn)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ auto tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto regs_begin = insn.regs_begin ();
+ auto regs_end = insn.regs_end ();
+ if (std::any_of (regs_begin, regs_end, [®cache] (auto &®_it) {
+ return record_full_arch_list_add_reg (regcache, reg_it);
+ }))
+ return -1;
+
+ auto mems_begin = insn.mems_begin ();
+ auto mems_end = insn.mems_end ();
+ if (std::any_of (mems_begin, mems_end, [] (auto &&mem_it) {
+ return record_full_arch_list_add_mem (mem_it.first, mem_it.second);
+ }))
+ return -1;
+
+ switch (insn.get_record_type ())
+ {
+ case riscv_recorded_insn::record_type::ORDINARY:
+ break;
+
+ case riscv_recorded_insn::record_type::ECALL:
+ {
+ if (!tdep->riscv_syscall_record)
+ {
+ warning (_ ("Syscall record is not supported"));
+ return -1;
+ }
+ auto 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;
+}
+
+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;
+ }
+
+ auto ret_val = riscv_make_record_process (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 4bdc2e7a3d5..505371ba4f6 100644
--- a/gdb/riscv-tdep.h
+++ b/gdb/riscv-tdep.h
@@ -35,6 +35,10 @@ enum
RISCV_FP_REGNUM = 8, /* Frame Pointer. */
RISCV_A0_REGNUM = 10, /* First argument. */
RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
RISCV_PC_REGNUM = 32, /* Program Counter. */
@@ -113,6 +117,11 @@ 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 +186,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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
new file mode 100644
index 00000000000..333e5d60c07
--- /dev/null
+++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python3
+# pylint: disable=invalid-name
+
+# Copyright (C) 2024 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/>.
+
+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 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"
+
+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:
+ canon_syscalls[syscall_num] = (
+ f" case {syscall_num}: return {gdb_syscall_name};\n"
+ )
+ # this is a place for corner cases
+ elif syscall_name == "mmap":
+ gdb_old_syscall_name = "gdb_old_mmap"
+ canon_syscalls[syscall_num] = f" case {syscall_num}: return {gdb_old_syscall_name};\n"
+ else:
+ canon_syscalls[syscall_num] = f" /* case {syscall_num}: return {gdb_syscall_name}; */\n"
+ 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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 410f99e3350..b048ac7078b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
- || [istarget "s390*-*-linux*"] } {
+ || [istarget "s390*-*-linux*"]
+ || [istarget "riscv*-*-*"] } {
return 1
}
--
2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* Re: [PATCH v4] This commit adds record full support for rv64gc instruction set.
2024-12-17 0:10 ` [PATCH v4] This commit adds record full support for rv64gc instruction set Timur
@ 2024-12-17 14:23 ` Guinevere Larsen
2024-12-23 15:12 ` [PATCH v5] " Timur
0 siblings, 1 reply; 35+ messages in thread
From: Guinevere Larsen @ 2024-12-17 14:23 UTC (permalink / raw)
To: Timur, gdb-patches
On 12/16/24 9:10 PM, Timur wrote:
> It includes changes to the following files:
> - gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
> syscalls.
> - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
> rv64gc instructions.
> - gdb/configure.tgt: adds new files for compilation.
> - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
> targets.
> - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate function
> that canonicalizes RISC-V syscall. This script can simplify support for syscalls
> on rv32 system. To use this script you need to pass a path to a file with
As I mentioned in the previous email, please mention both rv32 and
rv64, as the current wording doesn't make it clear that it helps both
> syscalls description from riscv-glibc (example is in the help message). The
> script produces a mapping from syscall names to gdb_syscall enum.
> - gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
>
> Timur Golubovich timurgol007@gmail.com
>
> Hi! I am sorry, I did incorrect previos time (made tab so as `config.lt` was
> added). So I will duplicate my answers from the previous letter.
Hi!
Thanks for sending the updated version. Commit message is ok apart from
the one nitpick, though I think we tend to do more paragraph style
messages, rather than changelog style.
I have some comments on the code, but with those fixed I'll be happy to
add my +1 to this patch, but I'll ask that you wait for a risc-v or
global maintainer to approve it, as I can only review stuff related to
recording itself.
Additionally, since this patch seems to be close to being approved, I
checked and I can't see your name in past contributions to GDB. Do you
have a copyright assignment in place? If not, you might want to start
the process soon, since it can take a little while. Take a look at the
gdb wiki for more information:
https://sourceware.org/gdb/wiki/ContributionChecklist#FSF_copyright_Assignment
>
>>>> + switch (syscall)
>>>> + {
>>>> + case 0: // #define __NR_io_setup 0
>>>>
>>>> All comments should be in the form /* */, rather than //
>>>>
>>>> I would also prefer if this followed what other architectures do,
>>>> defining an enum to relate syscall numbers in a RISCV system to their
>>>> names, and then this function returning the gdb_sys enum numbers,
>>>> however I don't expect to be working on RISCV in the near future, so
>>>> feel free to ignore this comment if the other RISCV maintainers are ok
>>>> with this style. This would make it so no #define comment is necessary
>>> Decided to make a script generating function that canonicalizes RISC-V syscall.
>>> This solution will simplify adding record support for rv32 system. I also left
>>> place for corner cases in generating script as they always may exist. Also
>>> removed this #define comment from generating script.
>> I see. This makes sense.
>>
>> I would like to see this explanation on the commit message, so that it
>> is recorded why Risc-V works different to all other arches.
> Could you please double-check the commit message? I tried to make it more
> detailed. Do you think I need to provide additional information?
>
>>>> +
>>>> + if (m_length == 2)
>>>> + return record_insn_len2 (ival, regcache, gdbarch);
>>>> +
>>>> + /* 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. */
>>>> Again, the indentation here is incorrect. Lines should start in the same column as the number 6.
>>> Addressed
>>>
>>> Best wishes,
>>> Timur Golubovich
>>>
>>> Timur Golubovich timurgol007@gmail.com
>> As I mentioned in the first review, please add some commit message
>> explaining a bit of what your patch is doing. I would recommend
>> explaining the inspiration for how riscv-tdep handles the recording (I
>> guess aarch64) and why you changed it; plus explaining the idea behind
>> the python script.c
> Added this information to the commit message.
>
>>> /* The following value is derived from __NR_rt_sigreturn in
>>> <include/uapi/asm-generic/unistd.h> from the Linux source tree. */
>>> @@ -173,6 +178,250 @@ 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;
>>> +
>>> +/* Record all registers but PC register for process-record. */
>>> +
>>> +using regnum_type = int;
>>> +
>>> +static bool
>>> +save_registers (struct regcache *regcache, regnum_type fir, regnum_type last)
>> Please fully type the variable names (fir -> first). Even if this makes
>> you need to use multiple lines for the function, I think this makes the
>> function easier to understand in the future.
>>
>> Sorry about not mentioning this before, I missed it in my first pass.
> Addressed
>
>>> + if (syscall_gdb == gdb_sys_sigreturn || syscall_gdb == gdb_sys_rt_sigreturn)
>>> + {
>>> + if (!riscv_all_but_pc_registers_record (regcache))
>>> + return -1;
>>> + return 0;
>>> + }
>>> +
>>> + auto 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. */
>>> +/* These values are the size of the type that will be used in a system
>>> + call. They are obtained from Linux Kernel source. */
>>> +static void
>>> +riscv64_linux_record_tdep_init (
>>> + struct gdbarch *gdbarch, struct linux_record_tdep &riscv_linux_record_tdep)
>> Oops, also missed this one on the first pass.
>>
>> The preferred way to do multi-line arguments is:
>>
>> static void
>> riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
>> struct
>> linux_record_tdep &riscv_linux_record_tdep)
>> {
>>
>> (with the "struct" keywords being aligned)
> Addressed
>
>>> + {
>>> + gdb_assert (regcache);
>>> +
>>> + if (!try_read (regcache, reg, addr))
>>> + {
>>> + return set_error ();
>>> + }
>>> + return true;
>>> + }
>>> +
>>> + bool
>>> + save_reg (regnum_type regnum) noexcept
>>> + {
>>> + try
>>> + {
>>> + m_regs.emplace_back (regnum);
>> Similar to my thoughts on Loongarch, I don't think this approach is the
>> best.
>>
>> In short, recording is already considered very slow. The strategy this
>> patch uses is to first collect all the data to be recorded (which
>> demands dynamic allocations to std::vector), and then call
>> record_full_arch_list_add_reg or record_full_arch_list_add_mem, which
>> will do their own dynamic allocations. This seems wasteful to me.
>>
>> I believe it would be better if, instead, the save_reg and save_mem
>> methods would directly call the correct record_full_arch_list_add
>> function, so that only one allocation is needed per unit of data that
>> needs recording.
>>
>> Since aarch64 already uses the strategy in this patch I won't block
>> merging based on this feedback, if you're set on this idea, but I think
>> direct calls would be better.
> I think that this overhead is negligeble, since the number of allocation
> because most instructions affect only 1 register. Moreover, I feel that the
> suggested approach simplifies error handling, since I can decouple decoding
> procedure from the actual error detection. If you don't mind, I would suggest
> to leave it as it is.
>
>>> + 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)
>>> + || try_save_pc_rs2_rd_mem (ival, regcache))
>>> + {
>>> + return !has_error ();
>>> + }
>> Minor nit, but I noticed around here that this (and some lines later,
>> maybe some before) don't need the braces.
> Addressed
>
>>> + 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;
>>> +}
>>> +
>>> +int
>>> +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
>>> + CORE_ADDR addr)
>>> +{
>>> + gdb_assert (gdbarch && regcache);
>> null pointer checks should be explicit, ie, gdb_assert (gdbarch !=
>> nullptr && regcache != nullptr)
> Addressed.
>
> Best wishes,
> Timur Golubovich
>
> ---
> gdb/configure.tgt | 4 +-
> gdb/riscv-canonicalize-syscall-gen.c | 338 +++++++++
> gdb/riscv-linux-tdep.c | 260 ++++++-
> gdb/riscv-linux-tdep.h | 28 +
> gdb/riscv-tdep.c | 666 +++++++++++++++++-
> gdb/riscv-tdep.h | 15 +
> .../riscv-canonicalize-syscall-gen.py | 143 ++++
> gdb/testsuite/lib/gdb.exp | 3 +-
> 8 files changed, 1448 insertions(+), 9 deletions(-)
> create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
> create mode 100644 gdb/riscv-linux-tdep.h
> create mode 100644 gdb/syscalls/riscv-canonicalize-syscall-gen.py
>
> diff --git a/gdb/configure.tgt b/gdb/configure.tgt
> index 62df71b13fa..b350e1f6505 100644
> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt
> @@ -545,8 +545,8 @@ 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/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
> new file mode 100644
> index 00000000000..60d80aae0a6
> --- /dev/null
> +++ b/gdb/riscv-canonicalize-syscall-gen.c
> @@ -0,0 +1,338 @@
> +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py
> +
> + Copyright (C) 2024 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"
> +
> +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 ff478cf4c28..ebaed9b4508 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,12 +178,262 @@ 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;
> +
> +/* Record all registers but PC register for process-record. */
> +
> +using regnum_type = int;
> +
> +static bool
> +save_registers (struct regcache *regcache, regnum_type first, regnum_type last)
I also missed this before, but all functions should have a comment
explaining what they do. It can be short, but it should be there.
There's a couple more instances where it is needed below.
> +{
> + 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;
> +};
> +
> +static bool
> +riscv_all_but_pc_registers_record (struct regcache *regcache)
> +{
> + gdb_assert (regcache != nullptr);
> +
> + auto &&gdbarch = regcache->arch ();
> + auto &&tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> + auto &&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))
When indentation reaches 8 spaces, it should be changed to a tab
instead. There are a couple more instances in the patch as well
> + 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);
> +
> + auto 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)
> + {
> + gdb_printf (gdb_stderr,
> + _ ("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;
> + }
> +
> + auto 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. */
> +/* These values are the size of the type that will be used in a system
> + call. They are obtained from Linux Kernel source. */
> +static void
> +riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
> + struct linux_record_tdep &riscv_linux_record_tdep)
> +{
> + gdb_assert (gdbarch != nullptr);
> +
> + /* Initialize the riscv_linux_record_tdep. */
> + /* These values are the size of the type that will be used in a system
> + call. They are obtained from Linux Kernel source. */
> + 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;
> + riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
> +}
> +
> /* Initialize RISC-V Linux ABI info. */
>
> static void
> riscv_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
> {
> - riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> + auto tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
>
> linux_init_abi (info, gdbarch, 0);
>
> @@ -205,6 +460,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 00000000000..a0d67003a81
> --- /dev/null
> +++ b/gdb/riscv-linux-tdep.h
> @@ -0,0 +1,28 @@
> +/* Copyright (C) 2024 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 932708ca4e9..37ec30fff91 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
>
> @@ -1655,6 +1658,11 @@ class riscv_insn
> 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. */
> @@ -1800,11 +1808,6 @@ class riscv_insn
> 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;
>
> @@ -4415,6 +4418,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);
>
> @@ -4833,3 +4839,653 @@ this option can be used."),
> &setriscvcmdlist,
> &showriscvcmdlist);
> }
> +
> +static bool
> +try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
> +{
> + gdb_assert (regcache != nullptr);
> +
> + if (regcache->raw_read<ULONGEST> (regnum, &addr)
> + != register_status::REG_VALID)
> + {
> + warning (_ ("Can not read at address %lx"), addr);
> + return false;
> + }
> + return true;
> +}
> +
> +class riscv_recorded_insn final
> +{
> +public:
> + using regnum_type = int;
> + using memory_type = std::pair<CORE_ADDR, int>;
> +
> + enum class record_type
> + {
> + UNKNOWN,
> + ORDINARY,
> +
> + /* Corner cases. */
> + ECALL,
> + EBREAK,
> + };
> +
> +private:
> + using recorded_regs = std::vector<regnum_type>;
> + using recorded_mems = std::vector<memory_type>;
> +
> + using mem_addr = decltype (std::declval<memory_type> ().first);
> + using mem_len = decltype (std::declval<memory_type> ().second);
> +
> + record_type m_record_type = record_type::UNKNOWN;
> +
> + bool m_error_occured = false;
> +
> + recorded_regs m_regs;
> + recorded_mems m_mems;
> +
> + /* 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;
> + }
> +
> + bool
> + set_ordinary_record_type () noexcept
> + {
> + m_record_type = record_type::ORDINARY;
> + return true;
> + }
> +
> + bool
> + set_error () noexcept
> + {
> + m_error_occured = true;
> + return false;
> + }
> +
> + bool
> + has_error () const noexcept
> + {
> + return m_error_occured;
> + }
> +
> + 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;
> + }
> +
> + bool
> + save_reg (regnum_type regnum) noexcept
> + {
> + try
> + {
> + m_regs.emplace_back (regnum);
> + return true;
> + }
> + catch (const std::bad_alloc &ex)
> + {
> + warning (_ ("Exception was caught during reverse execution: %s"),
> + ex.what ());
> + return set_error ();
> + }
> + }
> +
> + bool
> + save_mem (mem_addr addr, mem_len len) noexcept
> + {
> + try
> + {
> + m_mems.emplace_back (addr, len);
> + return true;
> + }
> + catch (const std::bad_alloc &ex)
> + {
> + warning (_ ("Exception was caught during reverse execution: %s"),
> + ex.what ());
> + return set_error ();
> + }
> + }
> +
> + 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 ();
> + }
> +
> + 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_ld_insn (ival) || is_lbu_insn (ival)
> + || is_lhu_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
> + || is_lw_insn (ival) || is_ld_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_insn (ival) || is_srli_insn (ival)
> + || is_srai_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_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_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_d_w_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. Needs error checking after. */
> + 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 ();
> + }
> +
> + static bool
> + need_save_pc_fprd (ULONGEST ival) noexcept
> + {
> + return 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_flw_insn (ival) || is_fld_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_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_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. Needs error checking after. */
> + 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 ();
> + }
> +
> + 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. Needs error checking after. */
> + 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 ();
> + }
> +
> + /* If return value is > 0, than instruction needs saving memory len. */
> + 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. Needs error checking after. */
> + bool
> + try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + auto addr = mem_addr{};
> + auto len = need_save_pc_mem (ival);
> + if (len <= 0)
> + return false;
> +
> + auto offset = mem_len{EXTRACT_STYPE_IMM (ival)};
> + return !read_reg (regcache, decode_rs1 (ival), addr)
> + || !save_mem (addr + offset, len) || set_ordinary_record_type ();
> + }
> +
> + /* If return value is > 0, than instruction needs saving memory len. */
> + static mem_len
> + need_save_pc_rd_mem (ULONGEST ival) noexcept
> + {
> + if (is_sc_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_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. Needs error checking after. */
> + bool
> + try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + auto len = need_save_pc_rd_mem (ival);
> + auto addr = mem_addr{};
> + 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 ();
> + }
> +
> + /* If return value is > 0, than instruction needs saving memory len. */
> + static mem_len
> + need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
> + {
> + if (is_amoswap_w_insn (ival))
> + return 4;
> + if (is_amoswap_d_insn (ival))
> + return 8;
> + return 0;
> + }
> +
> + /* Returns true if instruction is classified. Needs error checking after. */
> + bool
> + try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + auto addr = mem_addr{};
> + auto len = need_save_pc_rs2_rd_mem (ival);
> + if (len <= 0)
> + return false;
> +
> + return !read_reg (regcache, decode_rs1 (ival), addr)
> + || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
> + || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
> + }
> +
> + /* Returns true if instruction is successfully recorder.
> + Else returns false. */
> + 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)
> + || try_save_pc_rs2_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 recorder.
> + Else returns false. */
> + bool
> + record_insn_len2 (ULONGEST ival, struct regcache *regcache,
> + struct gdbarch *gdbarch) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> + gdb_assert (gdbarch != nullptr);
> +
> + auto addr = mem_addr{};
> + auto xlen = riscv_isa_xlen (gdbarch);
> +
> + /* 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)
> + || (xlen == 8 && is_c_ld_insn (ival)))
> + return !save_reg (decode_crs2_short (ival))
> + || set_ordinary_record_type ();
> +
> + if (is_c_fld_insn (ival) || (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) || (xlen == 8 && is_c_sd_insn (ival)))
> + {
> + auto offset = int{EXTRACT_CLTYPE_LD_IMM (ival)};
> + return !read_reg (regcache, decode_crs1_short (ival), addr)
> + || !save_mem (addr + offset, 8) || set_ordinary_record_type ();
> + }
> +
> + if ((xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
> + {
> + auto offset = int{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 (xlen == 4 && is_c_jal_insn (ival))
> + return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
> +
> + if ((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) || (xlen == 8 && is_c_subw_insn (ival))
> + || (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) || (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) || (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) || (xlen == 8 && is_c_sdsp_insn (ival)))
> + {
> + auto offset = int{EXTRACT_CSSTYPE_SDSP_IMM (ival)};
When trying to compile this patch, this line caused compilation errors,
due to "narrowing conversion". It should use ULONGEST instead of int.
Also, we try to restrict the usage of "auto", so that's it's easier to
understand things line-by-line instead of needing to double check with
something like a variable or function declaration. Did you have any
issues with using:
ULONGEST offset = EXTRACT_CSSTYPE_SDSP_IMM (ival);
?
> + return !read_reg (regcache, RISCV_SP_REGNUM, addr)
> + || !save_mem (addr + offset, 8) || set_ordinary_record_type ();
I'm not sure I like the idea of using conditional execution due to
logical operands. It feels much more bash-y than C, I'd prefer if you
used if conditions, or exceptions.
However, this is personal preference, and I don't think I'll be touching
this file often, so if a RISC-V or global maintainer is ok with it, feel
free to ignore this comment.
> + }
> +
> + if (is_c_swsp_insn (ival) || (xlen == 4 && is_c_fswsp_insn (ival)))
> + {
> + auto offset = int{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:
> + using regs_iter = recorded_regs::const_iterator;
> + using mems_iter = recorded_mems::const_iterator;
> +
> + bool
> + record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
> + {
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + auto m_length = int{};
> + auto 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, gdbarch);
> +
> + /* 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;
> + }
> +
> + record_type
> + get_record_type () const noexcept
> + {
> + return m_record_type;
> + }
> +
> + regs_iter
> + regs_begin () const noexcept
> + {
> + return m_regs.begin ();
> + }
> +
> + regs_iter
> + regs_end () const noexcept
> + {
> + return m_regs.end ();
> + }
> +
> + mems_iter
> + mems_begin () const noexcept
> + {
> + return m_mems.begin ();
> + }
> +
> + mems_iter
> + mems_end () const noexcept
> + {
> + return m_mems.end ();
> + }
> +};
> +
> +static int
> +riscv_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
> + const riscv_recorded_insn &insn)
> +{
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + auto tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> + auto regs_begin = insn.regs_begin ();
> + auto regs_end = insn.regs_end ();
> + if (std::any_of (regs_begin, regs_end, [®cache] (auto &®_it) {
> + return record_full_arch_list_add_reg (regcache, reg_it);
> + }))
> + return -1;
> +
> + auto mems_begin = insn.mems_begin ();
> + auto mems_end = insn.mems_end ();
> + if (std::any_of (mems_begin, mems_end, [] (auto &&mem_it) {
> + return record_full_arch_list_add_mem (mem_it.first, mem_it.second);
> + }))
> + return -1;
> +
> + switch (insn.get_record_type ())
> + {
> + case riscv_recorded_insn::record_type::ORDINARY:
> + break;
> +
> + case riscv_recorded_insn::record_type::ECALL:
> + {
> + if (!tdep->riscv_syscall_record)
> + {
> + warning (_ ("Syscall record is not supported"));
> + return -1;
> + }
> + auto 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;
> +}
> +
> +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;
> + }
> +
> + auto ret_val = riscv_make_record_process (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 4bdc2e7a3d5..505371ba4f6 100644
> --- a/gdb/riscv-tdep.h
> +++ b/gdb/riscv-tdep.h
> @@ -35,6 +35,10 @@ enum
> RISCV_FP_REGNUM = 8, /* Frame Pointer. */
> RISCV_A0_REGNUM = 10, /* First argument. */
> RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
> RISCV_PC_REGNUM = 32, /* Program Counter. */
>
> @@ -113,6 +117,11 @@ 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 +186,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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> new file mode 100644
> index 00000000000..333e5d60c07
> --- /dev/null
> +++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> @@ -0,0 +1,143 @@
> +#!/usr/bin/env python3
> +# pylint: disable=invalid-name
> +
> +# Copyright (C) 2024 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/>.
> +
> +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 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"
> +
> +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:
> + canon_syscalls[syscall_num] = (
> + f" case {syscall_num}: return {gdb_syscall_name};\n"
> + )
> + # this is a place for corner cases
> + elif syscall_name == "mmap":
> + gdb_old_syscall_name = "gdb_old_mmap"
> + canon_syscalls[syscall_num] = f" case {syscall_num}: return {gdb_old_syscall_name};\n"
> + else:
> + canon_syscalls[syscall_num] = f" /* case {syscall_num}: return {gdb_syscall_name}; */\n"
> + 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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 410f99e3350..b048ac7078b 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -3718,7 +3718,8 @@ proc supports_reverse {} {
> || [istarget "i\[34567\]86-*-linux*"]
> || [istarget "aarch64*-*-linux*"]
> || [istarget "powerpc*-*-linux*"]
> - || [istarget "s390*-*-linux*"] } {
> + || [istarget "s390*-*-linux*"]
> + || [istarget "riscv*-*-*"] } {
> return 1
> }
>
> --
> 2.34.1
>
--
Cheers,
Guinevere Larsen
She/Her/Hers
^ permalink raw reply [flat|nested] 35+ messages in thread* [PATCH v5] This commit adds record full support for rv64gc instruction set.
2024-12-17 14:23 ` Guinevere Larsen
@ 2024-12-23 15:12 ` Timur
2024-12-23 18:55 ` Guinevere Larsen
0 siblings, 1 reply; 35+ messages in thread
From: Timur @ 2024-12-23 15:12 UTC (permalink / raw)
To: gdb-patches; +Cc: Timur
It includes changes to the following files:
- gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
syscalls.
- gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
rv64gc instructions.
- gdb/configure.tgt: adds new files for compilation.
- gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
targets.
- gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate function
that canonicalizes RISC-V syscall. This script can simplify support for
syscalls on rv32 and rv64 system (currently support only for rv64). To use
this script you need to pass a path to a file with syscalls description from
riscv-glibc (example is in the help message). The script produces a mapping
from syscall names to gdb_syscall enum.
- gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
Timur Golubovich timurgol007@gmail.com
Hi! Sorry for answering late, I was taking an exam.
>> It includes changes to the following files:
>> - gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
>> syscalls.
>> - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
>> rv64gc instructions.
>> - gdb/configure.tgt: adds new files for compilation.
>> - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
>> targets.
>> - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate function
>> that canonicalizes RISC-V syscall. This script can simplify support for syscalls
>> on rv32 system. To use this script you need to pass a path to a file with
> As I mentioned in the previous email, please mention both rv32 and
> rv64, as the current wording doesn't make it clear that it helps both
Addressed
> Hi!
>
> Thanks for sending the updated version. Commit message is ok apart from
> the one nitpick, though I think we tend to do more paragraph style
> messages, rather than changelog style.
>
> I have some comments on the code, but with those fixed I'll be happy to
> add my +1 to this patch, but I'll ask that you wait for a risc-v or
> global maintainer to approve it, as I can only review stuff related to
> recording itself.
>
> Additionally, since this patch seems to be close to being approved, I
> checked and I can't see your name in past contributions to GDB. Do you
> have a copyright assignment in place? If not, you might want to start
> the process soon, since it can take a little while. Take a look at the
> gdb wiki for more information:
> https://sourceware.org/gdb/wiki/ContributionChecklist#FSF_copyright_Assignment
As I understood, I need to send filled form `request-assign.changes` to
assign@gnu.org to get my copyright. Please correct me if I'm wrong.
>>
>> +/* RISC-V process record-replay constructs: syscall, signal etc. */
>> +
>> +static linux_record_tdep riscv_linux_record_tdep;
>> +
>> +/* Record all registers but PC register for process-record. */
>> +
>> +using regnum_type = int;
>> +
>> +static bool
>> +save_registers (struct regcache *regcache, regnum_type first, regnum_type last)
> I also missed this before, but all functions should have a comment
> explaining what they do. It can be short, but it should be there.
>
> There's a couple more instances where it is needed below.
Addressed
>> +{
>> + 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;
>> +};
>> +
>> +static bool
>> +riscv_all_but_pc_registers_record (struct regcache *regcache)
>> +{
>> + gdb_assert (regcache != nullptr);
>> +
>> + auto &&gdbarch = regcache->arch ();
>> + auto &&tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
>> + auto &&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))
> When indentation reaches 8 spaces, it should be changed to a tab
> instead. There are a couple more instances in the patch as well
Addressed, but I didn't find more places with such identation
>> + 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) || (xlen == 8 && is_c_sdsp_insn (ival)))
>> + {
>> + auto offset = int{EXTRACT_CSSTYPE_SDSP_IMM (ival)};
> When trying to compile this patch, this line caused compilation errors,
> due to "narrowing conversion". It should use ULONGEST instead of int.
Oh, my bad, I am sorry. Addressed
> Also, we try to restrict the usage of "auto", so that's it's easier to
> understand things line-by-line instead of needing to double check with
> something like a variable or function declaration. Did you have any
> issues with using:
>
> ULONGEST offset = EXTRACT_CSSTYPE_SDSP_IMM (ival);
>
> ?
Removed all auto's in such context. Addressed
>> + return !read_reg (regcache, RISCV_SP_REGNUM, addr)
>> + || !save_mem (addr + offset, 8) || set_ordinary_record_type ();
> I'm not sure I like the idea of using conditional execution due to
> logical operands. It feels much more bash-y than C, I'd prefer if you
> used if conditions, or exceptions.
> However, this is personal preference, and I don't think I'll be touching
> this file often, so if a RISC-V or global maintainer is ok with it, feel
> free to ignore this comment.
In first version of this patch there were these multiple conditions and it
didn't look nice. Also the patch size was much larger, so I decided to do
this way.
---
gdb/configure.tgt | 4 +-
gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
gdb/riscv-linux-tdep.c | 259 +++++++
gdb/riscv-linux-tdep.h | 29 +
gdb/riscv-tdep.c | 700 +++++++++++++++++-
gdb/riscv-tdep.h | 15 +
.../riscv-canonicalize-syscall-gen.py | 147 ++++
gdb/testsuite/lib/gdb.exp | 3 +-
8 files changed, 1491 insertions(+), 8 deletions(-)
create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
create mode 100644 gdb/riscv-linux-tdep.h
create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 62df71b13fa..b350e1f6505 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -545,8 +545,8 @@ 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/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
new file mode 100644
index 00000000000..c0afe8affe7
--- /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 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 ff478cf4c28..04a6f5dd4aa 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,257 @@ 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. */
+/* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+
+static void
+riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
+ struct linux_record_tdep &riscv_linux_record_tdep)
+{
+ gdb_assert (gdbarch != nullptr);
+
+ /* Initialize the riscv_linux_record_tdep. */
+ /* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+ 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;
+ riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
+}
+
/* Initialize RISC-V Linux ABI info. */
static void
@@ -205,6 +461,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 00000000000..8e48ab8e1fd
--- /dev/null
+++ b/gdb/riscv-linux-tdep.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2024 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 932708ca4e9..88ed4852e8f 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
@@ -1655,6 +1658,11 @@ class riscv_insn
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. */
@@ -1800,11 +1808,6 @@ class riscv_insn
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;
@@ -4415,6 +4418,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);
@@ -4833,3 +4839,687 @@ this option can be used."),
&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<ULONGEST> (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:
+ using regnum_type = int;
+ using memory_type = std::pair<CORE_ADDR, int>;
+
+ enum class record_type
+ {
+ UNKNOWN,
+ ORDINARY,
+
+ /* Corner cases. */
+ ECALL,
+ EBREAK,
+ };
+
+private:
+ using recorded_regs = std::vector<regnum_type>;
+ using recorded_mems = std::vector<memory_type>;
+
+ using mem_addr = decltype (std::declval<memory_type> ().first);
+ using mem_len = decltype (std::declval<memory_type> ().second);
+
+ record_type m_record_type = record_type::UNKNOWN;
+
+ bool m_error_occured = false;
+
+ recorded_regs m_regs;
+ recorded_mems m_mems;
+
+ /* 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. Sets error and returns false if error happened. */
+ bool
+ save_reg (regnum_type regnum) noexcept
+ {
+ try
+ {
+ m_regs.emplace_back (regnum);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ /* Save memory chunk. Sets error and returns false if error happened. */
+ bool
+ save_mem (mem_addr addr, mem_len len) noexcept
+ {
+ try
+ {
+ m_mems.emplace_back (addr, len);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ /* 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_ld_insn (ival) || is_lbu_insn (ival)
+ || is_lhu_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
+ || is_lw_insn (ival) || is_ld_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_insn (ival) || is_srli_insn (ival)
+ || is_srai_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_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_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_d_w_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. Needs error checking after. */
+ 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_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_flw_insn (ival) || is_fld_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_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_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. Needs error checking after. */
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc and
+ memory chunk of appropriate length. */
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc, rd and
+ memory chunk of appropriate length. */
+ static mem_len
+ need_save_pc_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_sc_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_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. Needs error checking after. */
+ 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 = mem_addr{};
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc, rs2, rd and
+ memory chunk of appropriate length. */
+ static mem_len
+ need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_amoswap_w_insn (ival))
+ return 4;
+ if (is_amoswap_d_insn (ival))
+ return 8;
+ return 0;
+ }
+
+ /* Returns true if instruction is classified. Needs error checking after. */
+ bool
+ try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ mem_addr addr = mem_addr{};
+ mem_len len = need_save_pc_rs2_rd_mem (ival);
+ if (len <= 0)
+ return false;
+
+ return !read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
+ || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
+ }
+
+ /* Returns true if instruction is successfully recorder. */
+ 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)
+ || try_save_pc_rs2_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 recorder.
+ Else returns false. */
+ bool
+ record_insn_len2 (ULONGEST ival, struct regcache *regcache,
+ struct gdbarch *gdbarch) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+ gdb_assert (gdbarch != nullptr);
+
+ mem_addr addr = mem_addr{};
+ int xlen = riscv_isa_xlen (gdbarch);
+
+ /* 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)
+ || (xlen == 8 && is_c_ld_insn (ival)))
+ return !save_reg (decode_crs2_short (ival))
+ || set_ordinary_record_type ();
+
+ if (is_c_fld_insn (ival) || (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) || (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 ((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 (xlen == 4 && is_c_jal_insn (ival))
+ return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
+
+ if ((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) || (xlen == 8 && is_c_subw_insn (ival))
+ || (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) || (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) || (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) || (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) || (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:
+ using regs_iter = recorded_regs::const_iterator;
+ 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 = int{};
+ 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, gdbarch);
+
+ /* 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_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
+ const riscv_recorded_insn &insn)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto regs_begin = insn.regs_begin ();
+ auto regs_end = insn.regs_end ();
+ if (std::any_of (regs_begin, regs_end, [®cache] (auto &®_it) {
+ return record_full_arch_list_add_reg (regcache, reg_it);
+ }))
+ return -1;
+
+ auto mems_begin = insn.mems_begin ();
+ auto mems_end = insn.mems_end ();
+ if (std::any_of (mems_begin, mems_end, [] (auto &&mem_it) {
+ return record_full_arch_list_add_mem (mem_it.first, mem_it.second);
+ }))
+ return -1;
+
+ switch (insn.get_record_type ())
+ {
+ case riscv_recorded_insn::record_type::ORDINARY:
+ break;
+
+ case riscv_recorded_insn::record_type::ECALL:
+ {
+ if (!tdep->riscv_syscall_record)
+ {
+ warning (_ ("Syscall record is not supported"));
+ return -1;
+ }
+ ULONGEST reg_val = ULONGEST{};
+ if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
+ return -1;
+ return tdep->riscv_syscall_record (regcache, reg_val);
+ }
+
+ case riscv_recorded_insn::record_type::EBREAK:
+ break;
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* Parse the current instruction and record the values of the registers and
+ memory that will be changed in current instruction to record_arch_list.
+ Return -1 if something is wrong. */
+
+int
+riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+ CORE_ADDR addr)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_recorded_insn insn;
+ if (!insn.record (gdbarch, regcache, addr))
+ {
+ record_full_arch_list_add_end ();
+ return -1;
+ }
+
+ int ret_val = riscv_make_record_process (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 4bdc2e7a3d5..505371ba4f6 100644
--- a/gdb/riscv-tdep.h
+++ b/gdb/riscv-tdep.h
@@ -35,6 +35,10 @@ enum
RISCV_FP_REGNUM = 8, /* Frame Pointer. */
RISCV_A0_REGNUM = 10, /* First argument. */
RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
RISCV_PC_REGNUM = 32, /* Program Counter. */
@@ -113,6 +117,11 @@ 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 +186,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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
new file mode 100755
index 00000000000..3a6176e6cf4
--- /dev/null
+++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python3
+# pylint: disable=invalid-name
+
+# Copyright (C) 2024 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/>.
+
+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 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:
+ canon_syscalls[syscall_num] = (
+ f" case {syscall_num}: return {gdb_syscall_name};\n"
+ )
+ # this is a place for corner cases
+ elif syscall_name == "mmap":
+ gdb_old_syscall_name = "gdb_old_mmap"
+ canon_syscalls[syscall_num] = f" case {syscall_num}: return {gdb_old_syscall_name};\n"
+ else:
+ canon_syscalls[syscall_num] = f" /* case {syscall_num}: return {gdb_syscall_name}; */\n"
+ 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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 410f99e3350..b048ac7078b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
- || [istarget "s390*-*-linux*"] } {
+ || [istarget "s390*-*-linux*"]
+ || [istarget "riscv*-*-*"] } {
return 1
}
--
2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* Re: [PATCH v5] This commit adds record full support for rv64gc instruction set.
2024-12-23 15:12 ` [PATCH v5] " Timur
@ 2024-12-23 18:55 ` Guinevere Larsen
2024-12-31 16:09 ` [PATCH v6] " Timur
0 siblings, 1 reply; 35+ messages in thread
From: Guinevere Larsen @ 2024-12-23 18:55 UTC (permalink / raw)
To: Timur, gdb-patches
On 12/23/24 12:12 PM, Timur wrote:
> It includes changes to the following files:
> - gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
> syscalls.
> - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
> rv64gc instructions.
> - gdb/configure.tgt: adds new files for compilation.
> - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
> targets.
> - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate function
> that canonicalizes RISC-V syscall. This script can simplify support for
> syscalls on rv32 and rv64 system (currently support only for rv64). To use
> this script you need to pass a path to a file with syscalls description from
> riscv-glibc (example is in the help message). The script produces a mapping
> from syscall names to gdb_syscall enum.
> - gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
>
> Timur Golubovich timurgol007@gmail.com
>
> Hi! Sorry for answering late, I was taking an exam.
I hope the exam went well!
As a heads up for you, today is my last day before a christmas break,
and I think many other GDB engineers as well, so things might slow down
a bit
>
>>> It includes changes to the following files:
>>> - gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
>>> syscalls.
>>> - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
>>> rv64gc instructions.
>>> - gdb/configure.tgt: adds new files for compilation.
>>> - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
>>> targets.
>>> - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate function
>>> that canonicalizes RISC-V syscall. This script can simplify support for syscalls
>>> on rv32 system. To use this script you need to pass a path to a file with
>> As I mentioned in the previous email, please mention both rv32 and
>> rv64, as the current wording doesn't make it clear that it helps both
> Addressed
>
>> Hi!
>>
>> Thanks for sending the updated version. Commit message is ok apart from
>> the one nitpick, though I think we tend to do more paragraph style
>> messages, rather than changelog style.
>>
>> I have some comments on the code, but with those fixed I'll be happy to
>> add my +1 to this patch, but I'll ask that you wait for a risc-v or
>> global maintainer to approve it, as I can only review stuff related to
>> recording itself.
>>
>> Additionally, since this patch seems to be close to being approved, I
>> checked and I can't see your name in past contributions to GDB. Do you
>> have a copyright assignment in place? If not, you might want to start
>> the process soon, since it can take a little while. Take a look at the
>> gdb wiki for more information:
>> https://sourceware.org/gdb/wiki/ContributionChecklist#FSF_copyright_Assignment
> As I understood, I need to send filled form `request-assign.changes` to
> assign@gnu.org to get my copyright. Please correct me if I'm wrong.
I believe that is the case, yes. If you'd like some more help with this
part, Eli Zaretskii (eliz <at> gnu <dot> org) has previously volunteered
to help explain the process for newcomers.
Speaking of Eli, I was just reminded that this patch should include a
gdb/NEWS entry (could be a single line, like "Add record full support
rv64gc architectures", or similar) and an update to the supported
architectures in the gdb/doc/gdb.texinfo.
>
>>> +/* RISC-V process record-replay constructs: syscall, signal etc. */
>>> +
>>> +static linux_record_tdep riscv_linux_record_tdep;
>>> +
>>> +/* Record all registers but PC register for process-record. */
>>> +
>>> +using regnum_type = int;
>>> +
>>> +static bool
>>> +save_registers (struct regcache *regcache, regnum_type first, regnum_type last)
>> I also missed this before, but all functions should have a comment
>> explaining what they do. It can be short, but it should be there.
>>
>> There's a couple more instances where it is needed below.
> Addressed
>
>>> +{
>>> + 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;
>>> +};
>>> +
>>> +static bool
>>> +riscv_all_but_pc_registers_record (struct regcache *regcache)
>>> +{
>>> + gdb_assert (regcache != nullptr);
>>> +
>>> + auto &&gdbarch = regcache->arch ();
>>> + auto &&tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
>>> + auto &&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))
>> When indentation reaches 8 spaces, it should be changed to a tab
>> instead. There are a couple more instances in the patch as well
> Addressed, but I didn't find more places with such identation
This might be your email client changing things then... the new version
you sent still only has spaces, not tabs. If you try to save the email
and use `git am <patch>`, it'll warn you of the issues. If you're sure
the whitespace issue is due to the email client, and with adding the
documentation stuff I mentioned above, you can add my review tag to the
next version of this patch: Reviewed-By: Guinevere Larsen
<guinevere@redhat.com>. I hope some risc-v or global maintainer approves
the new version quickly, as I like this change :)
On a side-note, the best way to make sure an email client doesn't get in
the way is to use `git send-email`, if you're able (here's a convenience
link, in case you had issues setting it up and didn't find this one:
https://stackoverflow.com/questions/68238912/how-to-configure-and-use-git-send-email-to-work-with-gmail-to-email-patches-to).
--
Cheers,
Guinevere Larsen
She/Her/Hers
>
>>> + 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) || (xlen == 8 && is_c_sdsp_insn (ival)))
>>> + {
>>> + auto offset = int{EXTRACT_CSSTYPE_SDSP_IMM (ival)};
>> When trying to compile this patch, this line caused compilation errors,
>> due to "narrowing conversion". It should use ULONGEST instead of int.
> Oh, my bad, I am sorry. Addressed
>
>> Also, we try to restrict the usage of "auto", so that's it's easier to
>> understand things line-by-line instead of needing to double check with
>> something like a variable or function declaration. Did you have any
>> issues with using:
>>
>> ULONGEST offset = EXTRACT_CSSTYPE_SDSP_IMM (ival);
>>
>> ?
> Removed all auto's in such context. Addressed
>
>>> + return !read_reg (regcache, RISCV_SP_REGNUM, addr)
>>> + || !save_mem (addr + offset, 8) || set_ordinary_record_type ();
>> I'm not sure I like the idea of using conditional execution due to
>> logical operands. It feels much more bash-y than C, I'd prefer if you
>> used if conditions, or exceptions.
>> However, this is personal preference, and I don't think I'll be touching
>> this file often, so if a RISC-V or global maintainer is ok with it, feel
>> free to ignore this comment.
> In first version of this patch there were these multiple conditions and it
> didn't look nice. Also the patch size was much larger, so I decided to do
> this way.
>
> ---
> gdb/configure.tgt | 4 +-
> gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
> gdb/riscv-linux-tdep.c | 259 +++++++
> gdb/riscv-linux-tdep.h | 29 +
> gdb/riscv-tdep.c | 700 +++++++++++++++++-
> gdb/riscv-tdep.h | 15 +
> .../riscv-canonicalize-syscall-gen.py | 147 ++++
> gdb/testsuite/lib/gdb.exp | 3 +-
> 8 files changed, 1491 insertions(+), 8 deletions(-)
> create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
> create mode 100644 gdb/riscv-linux-tdep.h
> create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
>
> diff --git a/gdb/configure.tgt b/gdb/configure.tgt
> index 62df71b13fa..b350e1f6505 100644
> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt
> @@ -545,8 +545,8 @@ 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/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
> new file mode 100644
> index 00000000000..c0afe8affe7
> --- /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 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 ff478cf4c28..04a6f5dd4aa 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,257 @@ 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. */
> +/* These values are the size of the type that will be used in a system
> + call. They are obtained from Linux Kernel source. */
> +
> +static void
> +riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
> + struct linux_record_tdep &riscv_linux_record_tdep)
> +{
> + gdb_assert (gdbarch != nullptr);
> +
> + /* Initialize the riscv_linux_record_tdep. */
> + /* These values are the size of the type that will be used in a system
> + call. They are obtained from Linux Kernel source. */
> + 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;
> + riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
> +}
> +
> /* Initialize RISC-V Linux ABI info. */
>
> static void
> @@ -205,6 +461,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 00000000000..8e48ab8e1fd
> --- /dev/null
> +++ b/gdb/riscv-linux-tdep.h
> @@ -0,0 +1,29 @@
> +/* Copyright (C) 2024 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 932708ca4e9..88ed4852e8f 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
>
> @@ -1655,6 +1658,11 @@ class riscv_insn
> 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. */
> @@ -1800,11 +1808,6 @@ class riscv_insn
> 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;
>
> @@ -4415,6 +4418,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);
>
> @@ -4833,3 +4839,687 @@ this option can be used."),
> &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<ULONGEST> (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:
> + using regnum_type = int;
> + using memory_type = std::pair<CORE_ADDR, int>;
> +
> + enum class record_type
> + {
> + UNKNOWN,
> + ORDINARY,
> +
> + /* Corner cases. */
> + ECALL,
> + EBREAK,
> + };
> +
> +private:
> + using recorded_regs = std::vector<regnum_type>;
> + using recorded_mems = std::vector<memory_type>;
> +
> + using mem_addr = decltype (std::declval<memory_type> ().first);
> + using mem_len = decltype (std::declval<memory_type> ().second);
> +
> + record_type m_record_type = record_type::UNKNOWN;
> +
> + bool m_error_occured = false;
> +
> + recorded_regs m_regs;
> + recorded_mems m_mems;
> +
> + /* 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. Sets error and returns false if error happened. */
> + bool
> + save_reg (regnum_type regnum) noexcept
> + {
> + try
> + {
> + m_regs.emplace_back (regnum);
> + return true;
> + }
> + catch (const std::bad_alloc &ex)
> + {
> + warning (_ ("Exception was caught during reverse execution: %s"),
> + ex.what ());
> + return set_error ();
> + }
> + }
> +
> + /* Save memory chunk. Sets error and returns false if error happened. */
> + bool
> + save_mem (mem_addr addr, mem_len len) noexcept
> + {
> + try
> + {
> + m_mems.emplace_back (addr, len);
> + return true;
> + }
> + catch (const std::bad_alloc &ex)
> + {
> + warning (_ ("Exception was caught during reverse execution: %s"),
> + ex.what ());
> + return set_error ();
> + }
> + }
> +
> + /* 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_ld_insn (ival) || is_lbu_insn (ival)
> + || is_lhu_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
> + || is_lw_insn (ival) || is_ld_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_insn (ival) || is_srli_insn (ival)
> + || is_srai_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_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_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_d_w_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. Needs error checking after. */
> + 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_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_flw_insn (ival) || is_fld_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_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_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. Needs error checking after. */
> + 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. Needs error checking after. */
> + 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 ();
> + }
> +
> + /* If return value is > 0, instruction needs only saving pc and
> + memory chunk of appropriate length. */
> + 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. Needs error checking after. */
> + 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 ();
> + }
> +
> + /* If return value is > 0, instruction needs only saving pc, rd and
> + memory chunk of appropriate length. */
> + static mem_len
> + need_save_pc_rd_mem (ULONGEST ival) noexcept
> + {
> + if (is_sc_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_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. Needs error checking after. */
> + 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 = mem_addr{};
> + 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 ();
> + }
> +
> + /* If return value is > 0, instruction needs only saving pc, rs2, rd and
> + memory chunk of appropriate length. */
> + static mem_len
> + need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
> + {
> + if (is_amoswap_w_insn (ival))
> + return 4;
> + if (is_amoswap_d_insn (ival))
> + return 8;
> + return 0;
> + }
> +
> + /* Returns true if instruction is classified. Needs error checking after. */
> + bool
> + try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + mem_addr addr = mem_addr{};
> + mem_len len = need_save_pc_rs2_rd_mem (ival);
> + if (len <= 0)
> + return false;
> +
> + return !read_reg (regcache, decode_rs1 (ival), addr)
> + || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
> + || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
> + }
> +
> + /* Returns true if instruction is successfully recorder. */
> + 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)
> + || try_save_pc_rs2_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 recorder.
> + Else returns false. */
> + bool
> + record_insn_len2 (ULONGEST ival, struct regcache *regcache,
> + struct gdbarch *gdbarch) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> + gdb_assert (gdbarch != nullptr);
> +
> + mem_addr addr = mem_addr{};
> + int xlen = riscv_isa_xlen (gdbarch);
> +
> + /* 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)
> + || (xlen == 8 && is_c_ld_insn (ival)))
> + return !save_reg (decode_crs2_short (ival))
> + || set_ordinary_record_type ();
> +
> + if (is_c_fld_insn (ival) || (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) || (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 ((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 (xlen == 4 && is_c_jal_insn (ival))
> + return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
> +
> + if ((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) || (xlen == 8 && is_c_subw_insn (ival))
> + || (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) || (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) || (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) || (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) || (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:
> + using regs_iter = recorded_regs::const_iterator;
> + 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 = int{};
> + 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, gdbarch);
> +
> + /* 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_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
> + const riscv_recorded_insn &insn)
> +{
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> + auto regs_begin = insn.regs_begin ();
> + auto regs_end = insn.regs_end ();
> + if (std::any_of (regs_begin, regs_end, [®cache] (auto &®_it) {
> + return record_full_arch_list_add_reg (regcache, reg_it);
> + }))
> + return -1;
> +
> + auto mems_begin = insn.mems_begin ();
> + auto mems_end = insn.mems_end ();
> + if (std::any_of (mems_begin, mems_end, [] (auto &&mem_it) {
> + return record_full_arch_list_add_mem (mem_it.first, mem_it.second);
> + }))
> + return -1;
> +
> + switch (insn.get_record_type ())
> + {
> + case riscv_recorded_insn::record_type::ORDINARY:
> + break;
> +
> + case riscv_recorded_insn::record_type::ECALL:
> + {
> + if (!tdep->riscv_syscall_record)
> + {
> + warning (_ ("Syscall record is not supported"));
> + return -1;
> + }
> + ULONGEST reg_val = ULONGEST{};
> + if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
> + return -1;
> + return tdep->riscv_syscall_record (regcache, reg_val);
> + }
> +
> + case riscv_recorded_insn::record_type::EBREAK:
> + break;
> +
> + default:
> + return -1;
> + }
> + return 0;
> +}
> +
> +/* Parse the current instruction and record the values of the registers and
> + memory that will be changed in current instruction to record_arch_list.
> + Return -1 if something is wrong. */
> +
> +int
> +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
> + CORE_ADDR addr)
> +{
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + riscv_recorded_insn insn;
> + if (!insn.record (gdbarch, regcache, addr))
> + {
> + record_full_arch_list_add_end ();
> + return -1;
> + }
> +
> + int ret_val = riscv_make_record_process (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 4bdc2e7a3d5..505371ba4f6 100644
> --- a/gdb/riscv-tdep.h
> +++ b/gdb/riscv-tdep.h
> @@ -35,6 +35,10 @@ enum
> RISCV_FP_REGNUM = 8, /* Frame Pointer. */
> RISCV_A0_REGNUM = 10, /* First argument. */
> RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
> RISCV_PC_REGNUM = 32, /* Program Counter. */
>
> @@ -113,6 +117,11 @@ 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 +186,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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> new file mode 100755
> index 00000000000..3a6176e6cf4
> --- /dev/null
> +++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> @@ -0,0 +1,147 @@
> +#!/usr/bin/env python3
> +# pylint: disable=invalid-name
> +
> +# Copyright (C) 2024 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/>.
> +
> +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 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:
> + canon_syscalls[syscall_num] = (
> + f" case {syscall_num}: return {gdb_syscall_name};\n"
> + )
> + # this is a place for corner cases
> + elif syscall_name == "mmap":
> + gdb_old_syscall_name = "gdb_old_mmap"
> + canon_syscalls[syscall_num] = f" case {syscall_num}: return {gdb_old_syscall_name};\n"
> + else:
> + canon_syscalls[syscall_num] = f" /* case {syscall_num}: return {gdb_syscall_name}; */\n"
> + 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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 410f99e3350..b048ac7078b 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -3718,7 +3718,8 @@ proc supports_reverse {} {
> || [istarget "i\[34567\]86-*-linux*"]
> || [istarget "aarch64*-*-linux*"]
> || [istarget "powerpc*-*-linux*"]
> - || [istarget "s390*-*-linux*"] } {
> + || [istarget "s390*-*-linux*"]
> + || [istarget "riscv*-*-*"] } {
> return 1
> }
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 35+ messages in thread* [PATCH v6] This commit adds record full support for rv64gc instruction set.
2024-12-23 18:55 ` Guinevere Larsen
@ 2024-12-31 16:09 ` Timur
2025-01-10 18:12 ` Guinevere Larsen
0 siblings, 1 reply; 35+ messages in thread
From: Timur @ 2024-12-31 16:09 UTC (permalink / raw)
To: gdb-patches; +Cc: Timur
It includes changes to the following files:
- gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
syscalls.
- gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
rv64gc instructions.
- gdb/configure.tgt: adds new files for compilation.
- gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
targets.
- gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate function
that canonicalizes RISC-V syscall. This script can simplify support for
syscalls on rv32 and rv64 system (currently support only for rv64). To use
this script you need to pass a path to a file with syscalls description from
riscv-glibc (example is in the help message). The script produces a mapping
from syscall names to gdb_syscall enum.
- gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
- gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
- gdb/NEWS: notification of new functionality.
Timur Golubovich timurgol007@gmail.com
Hi! Congratulations on the past holidays.
>>>> - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
>>>> rv64gc instructions.
>>>> - gdb/configure.tgt: adds new files for compilation.
>>>> - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
>>>> targets.
>>>> - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate function
>>>> that canonicalizes RISC-V syscall. This script can simplify support for syscalls
>>>> on rv32 system. To use this script you need to pass a path to a file with
>>> As I mentioned in the previous email, please mention both rv32 and
>>> rv64, as the current wording doesn't make it clear that it helps both
>> Addressed
>>
>>> Hi!
>>>
>>> Thanks for sending the updated version. Commit message is ok apart from
>>> the one nitpick, though I think we tend to do more paragraph style
>>> messages, rather than changelog style.
>>>
>>> I have some comments on the code, but with those fixed I'll be happy to
>>> add my +1 to this patch, but I'll ask that you wait for a risc-v or
>>> global maintainer to approve it, as I can only review stuff related to
>>> recording itself.
>>>
>>> Additionally, since this patch seems to be close to being approved, I
>>> checked and I can't see your name in past contributions to GDB. Do you
>>> have a copyright assignment in place? If not, you might want to start
>>> the process soon, since it can take a little while. Take a look at the
>>> gdb wiki for more information:
>>> https://sourceware.org/gdb/wiki/ContributionChecklist#FSF_copyright_Assignment
>> As I understood, I need to send filled form `request-assign.changes` to
>> assign@gnu.org to get my copyright. Please correct me if I'm wrong.
>
> I believe that is the case, yes. If you'd like some more help with this
> part, Eli Zaretskii (eliz <at> gnu <dot> org) has previously volunteered
> to help explain the process for newcomers.
>
> Speaking of Eli, I was just reminded that this patch should include a
> gdb/NEWS entry (could be a single line, like "Add record full support
> rv64gc architectures", or similar) and an update to the supported
> architectures in the gdb/doc/gdb.texinfo.
I send this form and wait for the answer. Also updated files you proposed.
>>>> + 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))
>>> When indentation reaches 8 spaces, it should be changed to a tab
>>> instead. There are a couple more instances in the patch as well
>> Addressed, but I didn't find more places with such identation
>
> This might be your email client changing things then... the new version
> you sent still only has spaces, not tabs. If you try to save the email
> and use `git am <patch>`, it'll warn you of the issues. If you're sure
> the whitespace issue is due to the email client, and with adding the
> documentation stuff I mentioned above, you can add my review tag to the
> next version of this patch: Reviewed-By: Guinevere Larsen
> <guinevere@redhat.com>. I hope some risc-v or global maintainer approves
> the new version quickly, as I like this change :)
>
> On a side-note, the best way to make sure an email client doesn't get in
> the way is to use `git send-email`, if you're able (here's a convenience
> link, in case you had issues setting it up and didn't find this one:
> https://stackoverflow.com/questions/68238912/how-to-configure-and-use-git-send-email-to-work-with-gmail-to-email-patches-to).
Well, I noticed that my IDE turned tabs into spaces before saving. Addressed.
---
gdb/NEWS | 2 +
gdb/configure.tgt | 5 +-
gdb/doc/gdb.texinfo | 2 +-
gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
gdb/riscv-linux-tdep.c | 259 +++++++
gdb/riscv-linux-tdep.h | 29 +
gdb/riscv-tdep.c | 707 +++++++++++++++++-
gdb/riscv-tdep.h | 15 +
.../riscv-canonicalize-syscall-gen.py | 148 ++++
gdb/testsuite/lib/gdb.exp | 3 +-
10 files changed, 1503 insertions(+), 9 deletions(-)
create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
create mode 100644 gdb/riscv-linux-tdep.h
create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
diff --git a/gdb/NEWS b/gdb/NEWS
index 964183910dc..20fdb571047 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -9581,3 +9581,5 @@ GDB now handles cross debugging. If you are remotely debugging between
two different machines, type ``./configure host -target=targ''.
Host is the machine where GDB will run; targ is the machine
where the program that you are debugging will run.
+
+ * Add record full support for rv64gc architectures
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 62df71b13fa..0cd5c509695 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -545,8 +545,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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 99720f1206e..d5b989d681e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7759,7 +7759,7 @@ 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,
-Moxie, PowerPC, PowerPC64, S/390, and x86 (i386/amd64) running
+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}.
diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
new file mode 100644
index 00000000000..a33f7d88b6a
--- /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 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 ff478cf4c28..2f7d911f1dd 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,257 @@ 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. */
+/* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+
+static void
+riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
+ struct linux_record_tdep &riscv_linux_record_tdep)
+{
+ gdb_assert (gdbarch != nullptr);
+
+ /* Initialize the riscv_linux_record_tdep. */
+ /* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+ 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;
+ riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
+}
+
/* Initialize RISC-V Linux ABI info. */
static void
@@ -205,6 +461,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 00000000000..8e48ab8e1fd
--- /dev/null
+++ b/gdb/riscv-linux-tdep.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2024 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 932708ca4e9..a0d059e386d 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
@@ -1655,6 +1658,11 @@ class riscv_insn
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. */
@@ -1800,11 +1808,6 @@ class riscv_insn
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;
@@ -4415,6 +4418,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);
@@ -4833,3 +4839,694 @@ this option can be used."),
&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<ULONGEST> (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:
+ using regnum_type = int;
+ using memory_type = std::pair<CORE_ADDR, int>;
+
+ enum class record_type
+ {
+ UNKNOWN,
+ ORDINARY,
+
+ /* Corner cases. */
+ ECALL,
+ EBREAK,
+ };
+
+private:
+ using recorded_regs = std::vector<regnum_type>;
+ using recorded_mems = std::vector<memory_type>;
+
+ using mem_addr = decltype (std::declval<memory_type> ().first);
+ using mem_len = decltype (std::declval<memory_type> ().second);
+
+ record_type m_record_type = record_type::UNKNOWN;
+
+ bool m_error_occured = false;
+
+ recorded_regs m_regs;
+ recorded_mems m_mems;
+
+ /* 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. Sets error and returns false if error happened. */
+ bool
+ save_reg (regnum_type regnum) noexcept
+ {
+ try
+ {
+ m_regs.emplace_back (regnum);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ /* Save memory chunk. Sets error and returns false if error happened. */
+ bool
+ save_mem (mem_addr addr, mem_len len) noexcept
+ {
+ try
+ {
+ m_mems.emplace_back (addr, len);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ /* 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_ld_insn (ival) || is_lbu_insn (ival)
+ || is_lhu_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
+ || is_lw_insn (ival) || is_ld_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_insn (ival) || is_srli_insn (ival)
+ || is_srai_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_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_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_d_w_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. Needs error checking after. */
+ 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_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_flw_insn (ival) || is_fld_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_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_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. Needs error checking after. */
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc and
+ memory chunk of appropriate length. */
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc, rd and
+ memory chunk of appropriate length. */
+ static mem_len
+ need_save_pc_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_sc_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_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. Needs error checking after. */
+ 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 = mem_addr{};
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc, rs2, rd and
+ memory chunk of appropriate length. */
+ static mem_len
+ need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_amoswap_w_insn (ival))
+ return 4;
+ if (is_amoswap_d_insn (ival))
+ return 8;
+ return 0;
+ }
+
+ /* Returns true if instruction is classified. Needs error checking after. */
+ bool
+ try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ mem_addr addr = mem_addr{};
+ mem_len len = need_save_pc_rs2_rd_mem (ival);
+ if (len <= 0)
+ return false;
+
+ return !read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
+ || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
+ }
+
+ /* Returns true if instruction is successfully recorder. */
+ 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)
+ || try_save_pc_rs2_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 recorder.
+ Else returns false. */
+ bool
+ record_insn_len2 (ULONGEST ival, struct regcache *regcache,
+ struct gdbarch *gdbarch) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+ gdb_assert (gdbarch != nullptr);
+
+ mem_addr addr = mem_addr{};
+ int xlen = riscv_isa_xlen (gdbarch);
+
+ /* 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)
+ || (xlen == 8 && is_c_ld_insn (ival)))
+ return !save_reg (decode_crs2_short (ival))
+ || set_ordinary_record_type ();
+
+ if (is_c_fld_insn (ival) || (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) || (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 ((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 (xlen == 4 && is_c_jal_insn (ival))
+ return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
+
+ if ((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) || (xlen == 8 && is_c_subw_insn (ival))
+ || (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) || (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) || (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) || (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) || (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:
+ using regs_iter = recorded_regs::const_iterator;
+ 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 = int{};
+ 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, gdbarch);
+
+ /* 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_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
+ const riscv_recorded_insn &insn)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto regs_begin = insn.regs_begin ();
+ auto regs_end = insn.regs_end ();
+ if (std::any_of (regs_begin,
+ regs_end,
+ [®cache] (auto &®_it)
+ {
+ return record_full_arch_list_add_reg (regcache, reg_it);
+ }))
+ return -1;
+
+ auto mems_begin = insn.mems_begin ();
+ auto mems_end = insn.mems_end ();
+ if (std::any_of (mems_begin,
+ mems_end,
+ [] (auto &&mem_it)
+ {
+ return record_full_arch_list_add_mem (mem_it.first,
+ mem_it.second);
+ }))
+ return -1;
+
+ switch (insn.get_record_type ())
+ {
+ case riscv_recorded_insn::record_type::ORDINARY:
+ break;
+
+ case riscv_recorded_insn::record_type::ECALL:
+ {
+ if (!tdep->riscv_syscall_record)
+ {
+ warning (_ ("Syscall record is not supported"));
+ return -1;
+ }
+ ULONGEST reg_val = ULONGEST{};
+ if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
+ return -1;
+ return tdep->riscv_syscall_record (regcache, reg_val);
+ }
+
+ case riscv_recorded_insn::record_type::EBREAK:
+ break;
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* Parse the current instruction and record the values of the registers and
+ memory that will be changed in current instruction to record_arch_list.
+ Return -1 if something is wrong. */
+
+int
+riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+ CORE_ADDR addr)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_recorded_insn insn;
+ if (!insn.record (gdbarch, regcache, addr))
+ {
+ record_full_arch_list_add_end ();
+ return -1;
+ }
+
+ int ret_val = riscv_make_record_process (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 4bdc2e7a3d5..34cd32d5575 100644
--- a/gdb/riscv-tdep.h
+++ b/gdb/riscv-tdep.h
@@ -35,6 +35,10 @@ enum
RISCV_FP_REGNUM = 8, /* Frame Pointer. */
RISCV_A0_REGNUM = 10, /* First argument. */
RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
RISCV_PC_REGNUM = 32, /* Program Counter. */
@@ -113,6 +117,11 @@ 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 +186,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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
new file mode 100755
index 00000000000..e553d380924
--- /dev/null
+++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+# pylint: disable=invalid-name
+
+# Copyright (C) 2024 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/>.
+
+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 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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 410f99e3350..b048ac7078b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
- || [istarget "s390*-*-linux*"] } {
+ || [istarget "s390*-*-linux*"]
+ || [istarget "riscv*-*-*"] } {
return 1
}
--
2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* Re: [PATCH v6] This commit adds record full support for rv64gc instruction set.
2024-12-31 16:09 ` [PATCH v6] " Timur
@ 2025-01-10 18:12 ` Guinevere Larsen
2025-01-16 15:48 ` [PATCH v7] " Timur
0 siblings, 1 reply; 35+ messages in thread
From: Guinevere Larsen @ 2025-01-10 18:12 UTC (permalink / raw)
To: Timur, gdb-patches
On 12/31/24 1:09 PM, Timur wrote:
> It includes changes to the following files:
> - gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
> syscalls.
> - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
> rv64gc instructions.
> - gdb/configure.tgt: adds new files for compilation.
> - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
> targets.
> - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate function
> that canonicalizes RISC-V syscall. This script can simplify support for
> syscalls on rv32 and rv64 system (currently support only for rv64). To use
> this script you need to pass a path to a file with syscalls description from
> riscv-glibc (example is in the help message). The script produces a mapping
> from syscall names to gdb_syscall enum.
> - gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
> - gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
> - gdb/NEWS: notification of new functionality.
>
> Timur Golubovich timurgol007@gmail.com
>
> Hi! Congratulations on the past holidays.
Hi!
Thank you! for you as well :)
From a reverse debugging point of view, I think this patch is ok to go
in, feel free to add my tag: Approved-By: Guinevere Larsen
<guinevere@redhat.com> (record-full)
Please wait for some risc-v or global maintainer to also review this
patch before pushing.
--
Cheers,
Guinevere Larsen
She/Her/Hers
>
>>>>> - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
>>>>> rv64gc instructions.
>>>>> - gdb/configure.tgt: adds new files for compilation.
>>>>> - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
>>>>> targets.
>>>>> - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate function
>>>>> that canonicalizes RISC-V syscall. This script can simplify support for syscalls
>>>>> on rv32 system. To use this script you need to pass a path to a file with
>>>> As I mentioned in the previous email, please mention both rv32 and
>>>> rv64, as the current wording doesn't make it clear that it helps both
>>> Addressed
>>>
>>>> Hi!
>>>>
>>>> Thanks for sending the updated version. Commit message is ok apart from
>>>> the one nitpick, though I think we tend to do more paragraph style
>>>> messages, rather than changelog style.
>>>>
>>>> I have some comments on the code, but with those fixed I'll be happy to
>>>> add my +1 to this patch, but I'll ask that you wait for a risc-v or
>>>> global maintainer to approve it, as I can only review stuff related to
>>>> recording itself.
>>>>
>>>> Additionally, since this patch seems to be close to being approved, I
>>>> checked and I can't see your name in past contributions to GDB. Do you
>>>> have a copyright assignment in place? If not, you might want to start
>>>> the process soon, since it can take a little while. Take a look at the
>>>> gdb wiki for more information:
>>>> https://sourceware.org/gdb/wiki/ContributionChecklist#FSF_copyright_Assignment
>>> As I understood, I need to send filled form `request-assign.changes` to
>>> assign@gnu.org to get my copyright. Please correct me if I'm wrong.
>> I believe that is the case, yes. If you'd like some more help with this
>> part, Eli Zaretskii (eliz <at> gnu <dot> org) has previously volunteered
>> to help explain the process for newcomers.
>>
>> Speaking of Eli, I was just reminded that this patch should include a
>> gdb/NEWS entry (could be a single line, like "Add record full support
>> rv64gc architectures", or similar) and an update to the supported
>> architectures in the gdb/doc/gdb.texinfo.
> I send this form and wait for the answer. Also updated files you proposed.
>
>>>>> + 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))
>>>> When indentation reaches 8 spaces, it should be changed to a tab
>>>> instead. There are a couple more instances in the patch as well
>>> Addressed, but I didn't find more places with such identation
>> This might be your email client changing things then... the new version
>> you sent still only has spaces, not tabs. If you try to save the email
>> and use `git am <patch>`, it'll warn you of the issues. If you're sure
>> the whitespace issue is due to the email client, and with adding the
>> documentation stuff I mentioned above, you can add my review tag to the
>> next version of this patch: Reviewed-By: Guinevere Larsen
>> <guinevere@redhat.com>. I hope some risc-v or global maintainer approves
>> the new version quickly, as I like this change :)
>>
>> On a side-note, the best way to make sure an email client doesn't get in
>> the way is to use `git send-email`, if you're able (here's a convenience
>> link, in case you had issues setting it up and didn't find this one:
>> https://stackoverflow.com/questions/68238912/how-to-configure-and-use-git-send-email-to-work-with-gmail-to-email-patches-to).
> Well, I noticed that my IDE turned tabs into spaces before saving. Addressed.
>
> ---
> gdb/NEWS | 2 +
> gdb/configure.tgt | 5 +-
> gdb/doc/gdb.texinfo | 2 +-
> gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
> gdb/riscv-linux-tdep.c | 259 +++++++
> gdb/riscv-linux-tdep.h | 29 +
> gdb/riscv-tdep.c | 707 +++++++++++++++++-
> gdb/riscv-tdep.h | 15 +
> .../riscv-canonicalize-syscall-gen.py | 148 ++++
> gdb/testsuite/lib/gdb.exp | 3 +-
> 10 files changed, 1503 insertions(+), 9 deletions(-)
> create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
> create mode 100644 gdb/riscv-linux-tdep.h
> create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 964183910dc..20fdb571047 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -9581,3 +9581,5 @@ GDB now handles cross debugging. If you are remotely debugging between
> two different machines, type ``./configure host -target=targ''.
> Host is the machine where GDB will run; targ is the machine
> where the program that you are debugging will run.
> +
> + * Add record full support for rv64gc architectures
> diff --git a/gdb/configure.tgt b/gdb/configure.tgt
> index 62df71b13fa..0cd5c509695 100644
> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt
> @@ -545,8 +545,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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 99720f1206e..d5b989d681e 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -7759,7 +7759,7 @@ 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,
> -Moxie, PowerPC, PowerPC64, S/390, and x86 (i386/amd64) running
> +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}.
>
> diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
> new file mode 100644
> index 00000000000..a33f7d88b6a
> --- /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 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 ff478cf4c28..2f7d911f1dd 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,257 @@ 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. */
> +/* These values are the size of the type that will be used in a system
> + call. They are obtained from Linux Kernel source. */
> +
> +static void
> +riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
> + struct linux_record_tdep &riscv_linux_record_tdep)
> +{
> + gdb_assert (gdbarch != nullptr);
> +
> + /* Initialize the riscv_linux_record_tdep. */
> + /* These values are the size of the type that will be used in a system
> + call. They are obtained from Linux Kernel source. */
> + 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;
> + riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
> +}
> +
> /* Initialize RISC-V Linux ABI info. */
>
> static void
> @@ -205,6 +461,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 00000000000..8e48ab8e1fd
> --- /dev/null
> +++ b/gdb/riscv-linux-tdep.h
> @@ -0,0 +1,29 @@
> +/* Copyright (C) 2024 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 932708ca4e9..a0d059e386d 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
>
> @@ -1655,6 +1658,11 @@ class riscv_insn
> 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. */
> @@ -1800,11 +1808,6 @@ class riscv_insn
> 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;
>
> @@ -4415,6 +4418,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);
>
> @@ -4833,3 +4839,694 @@ this option can be used."),
> &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<ULONGEST> (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:
> + using regnum_type = int;
> + using memory_type = std::pair<CORE_ADDR, int>;
> +
> + enum class record_type
> + {
> + UNKNOWN,
> + ORDINARY,
> +
> + /* Corner cases. */
> + ECALL,
> + EBREAK,
> + };
> +
> +private:
> + using recorded_regs = std::vector<regnum_type>;
> + using recorded_mems = std::vector<memory_type>;
> +
> + using mem_addr = decltype (std::declval<memory_type> ().first);
> + using mem_len = decltype (std::declval<memory_type> ().second);
> +
> + record_type m_record_type = record_type::UNKNOWN;
> +
> + bool m_error_occured = false;
> +
> + recorded_regs m_regs;
> + recorded_mems m_mems;
> +
> + /* 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. Sets error and returns false if error happened. */
> + bool
> + save_reg (regnum_type regnum) noexcept
> + {
> + try
> + {
> + m_regs.emplace_back (regnum);
> + return true;
> + }
> + catch (const std::bad_alloc &ex)
> + {
> + warning (_ ("Exception was caught during reverse execution: %s"),
> + ex.what ());
> + return set_error ();
> + }
> + }
> +
> + /* Save memory chunk. Sets error and returns false if error happened. */
> + bool
> + save_mem (mem_addr addr, mem_len len) noexcept
> + {
> + try
> + {
> + m_mems.emplace_back (addr, len);
> + return true;
> + }
> + catch (const std::bad_alloc &ex)
> + {
> + warning (_ ("Exception was caught during reverse execution: %s"),
> + ex.what ());
> + return set_error ();
> + }
> + }
> +
> + /* 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_ld_insn (ival) || is_lbu_insn (ival)
> + || is_lhu_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
> + || is_lw_insn (ival) || is_ld_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_insn (ival) || is_srli_insn (ival)
> + || is_srai_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_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_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_d_w_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. Needs error checking after. */
> + 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_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_flw_insn (ival) || is_fld_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_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_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. Needs error checking after. */
> + 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. Needs error checking after. */
> + 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 ();
> + }
> +
> + /* If return value is > 0, instruction needs only saving pc and
> + memory chunk of appropriate length. */
> + 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. Needs error checking after. */
> + 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 ();
> + }
> +
> + /* If return value is > 0, instruction needs only saving pc, rd and
> + memory chunk of appropriate length. */
> + static mem_len
> + need_save_pc_rd_mem (ULONGEST ival) noexcept
> + {
> + if (is_sc_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_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. Needs error checking after. */
> + 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 = mem_addr{};
> + 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 ();
> + }
> +
> + /* If return value is > 0, instruction needs only saving pc, rs2, rd and
> + memory chunk of appropriate length. */
> + static mem_len
> + need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
> + {
> + if (is_amoswap_w_insn (ival))
> + return 4;
> + if (is_amoswap_d_insn (ival))
> + return 8;
> + return 0;
> + }
> +
> + /* Returns true if instruction is classified. Needs error checking after. */
> + bool
> + try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + mem_addr addr = mem_addr{};
> + mem_len len = need_save_pc_rs2_rd_mem (ival);
> + if (len <= 0)
> + return false;
> +
> + return !read_reg (regcache, decode_rs1 (ival), addr)
> + || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
> + || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
> + }
> +
> + /* Returns true if instruction is successfully recorder. */
> + 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)
> + || try_save_pc_rs2_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 recorder.
> + Else returns false. */
> + bool
> + record_insn_len2 (ULONGEST ival, struct regcache *regcache,
> + struct gdbarch *gdbarch) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> + gdb_assert (gdbarch != nullptr);
> +
> + mem_addr addr = mem_addr{};
> + int xlen = riscv_isa_xlen (gdbarch);
> +
> + /* 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)
> + || (xlen == 8 && is_c_ld_insn (ival)))
> + return !save_reg (decode_crs2_short (ival))
> + || set_ordinary_record_type ();
> +
> + if (is_c_fld_insn (ival) || (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) || (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 ((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 (xlen == 4 && is_c_jal_insn (ival))
> + return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
> +
> + if ((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) || (xlen == 8 && is_c_subw_insn (ival))
> + || (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) || (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) || (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) || (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) || (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:
> + using regs_iter = recorded_regs::const_iterator;
> + 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 = int{};
> + 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, gdbarch);
> +
> + /* 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_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
> + const riscv_recorded_insn &insn)
> +{
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> + auto regs_begin = insn.regs_begin ();
> + auto regs_end = insn.regs_end ();
> + if (std::any_of (regs_begin,
> + regs_end,
> + [®cache] (auto &®_it)
> + {
> + return record_full_arch_list_add_reg (regcache, reg_it);
> + }))
> + return -1;
> +
> + auto mems_begin = insn.mems_begin ();
> + auto mems_end = insn.mems_end ();
> + if (std::any_of (mems_begin,
> + mems_end,
> + [] (auto &&mem_it)
> + {
> + return record_full_arch_list_add_mem (mem_it.first,
> + mem_it.second);
> + }))
> + return -1;
> +
> + switch (insn.get_record_type ())
> + {
> + case riscv_recorded_insn::record_type::ORDINARY:
> + break;
> +
> + case riscv_recorded_insn::record_type::ECALL:
> + {
> + if (!tdep->riscv_syscall_record)
> + {
> + warning (_ ("Syscall record is not supported"));
> + return -1;
> + }
> + ULONGEST reg_val = ULONGEST{};
> + if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
> + return -1;
> + return tdep->riscv_syscall_record (regcache, reg_val);
> + }
> +
> + case riscv_recorded_insn::record_type::EBREAK:
> + break;
> +
> + default:
> + return -1;
> + }
> + return 0;
> +}
> +
> +/* Parse the current instruction and record the values of the registers and
> + memory that will be changed in current instruction to record_arch_list.
> + Return -1 if something is wrong. */
> +
> +int
> +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
> + CORE_ADDR addr)
> +{
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + riscv_recorded_insn insn;
> + if (!insn.record (gdbarch, regcache, addr))
> + {
> + record_full_arch_list_add_end ();
> + return -1;
> + }
> +
> + int ret_val = riscv_make_record_process (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 4bdc2e7a3d5..34cd32d5575 100644
> --- a/gdb/riscv-tdep.h
> +++ b/gdb/riscv-tdep.h
> @@ -35,6 +35,10 @@ enum
> RISCV_FP_REGNUM = 8, /* Frame Pointer. */
> RISCV_A0_REGNUM = 10, /* First argument. */
> RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
> RISCV_PC_REGNUM = 32, /* Program Counter. */
>
> @@ -113,6 +117,11 @@ 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 +186,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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> new file mode 100755
> index 00000000000..e553d380924
> --- /dev/null
> +++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> @@ -0,0 +1,148 @@
> +#!/usr/bin/env python3
> +# pylint: disable=invalid-name
> +
> +# Copyright (C) 2024 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/>.
> +
> +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 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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 410f99e3350..b048ac7078b 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -3718,7 +3718,8 @@ proc supports_reverse {} {
> || [istarget "i\[34567\]86-*-linux*"]
> || [istarget "aarch64*-*-linux*"]
> || [istarget "powerpc*-*-linux*"]
> - || [istarget "s390*-*-linux*"] } {
> + || [istarget "s390*-*-linux*"]
> + || [istarget "riscv*-*-*"] } {
> return 1
> }
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 35+ messages in thread* [PATCH v7] This commit adds record full support for rv64gc instruction set
2025-01-10 18:12 ` Guinevere Larsen
@ 2025-01-16 15:48 ` Timur
2025-02-10 18:07 ` [PING][PATCH v8] " Timur
0 siblings, 1 reply; 35+ messages in thread
From: Timur @ 2025-01-16 15:48 UTC (permalink / raw)
To: gdb-patches, aburgess, palmer; +Cc: Timur, Guinevere Larsen
It includes changes to the following files:
- gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
syscalls.
- gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
rv64gc instructions.
- gdb/configure.tgt: adds new files for compilation.
- gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
targets.
- gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
function that canonicalizes RISC-V syscall. This script can simplify support
for syscalls on rv32 and rv64 system (currently support only for rv64). To
use this script you need to pass a path to a file with syscalls description
from riscv-glibc (example is in the help message). The script produces a
mapping from syscall names to gdb_syscall enum.
- gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
- gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
- gdb/NEWS: notification of new functionality.
Approved-By: Guinevere Larsen <guinevere@redhat.com>
Hi!
> Please wait for some risc-v or global maintainer to also review this
> patch before pushing.
As I see in gdb/MAINTAINERS, Andrew Burgess and Palmer Dabbelt are the only
riscv maintainers. I added their email addresses in this letter.
Andrew, Palmer would you kindly take a look at this patch?
---
gdb/NEWS | 2 +
gdb/configure.tgt | 5 +-
gdb/doc/gdb.texinfo | 2 +-
gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
gdb/riscv-linux-tdep.c | 260 +++++++
gdb/riscv-linux-tdep.h | 29 +
gdb/riscv-tdep.c | 707 +++++++++++++++++-
gdb/riscv-tdep.h | 14 +
.../riscv-canonicalize-syscall-gen.py | 148 ++++
gdb/testsuite/lib/gdb.exp | 3 +-
10 files changed, 1503 insertions(+), 9 deletions(-)
create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
create mode 100644 gdb/riscv-linux-tdep.h
create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
diff --git a/gdb/NEWS b/gdb/NEWS
index 964183910dc..20fdb571047 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -9581,3 +9581,5 @@ GDB now handles cross debugging. If you are remotely debugging between
two different machines, type ``./configure host -target=targ''.
Host is the machine where GDB will run; targ is the machine
where the program that you are debugging will run.
+
+ * Add record full support for rv64gc architectures
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 62df71b13fa..0cd5c509695 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -545,8 +545,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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 99720f1206e..d5b989d681e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7759,7 +7759,7 @@ 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,
-Moxie, PowerPC, PowerPC64, S/390, and x86 (i386/amd64) running
+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}.
diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
new file mode 100644
index 00000000000..a33f7d88b6a
--- /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 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 ff478cf4c28..d08302d8f34 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,258 @@ 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. */
+/* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+
+static void
+riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
+ struct linux_record_tdep &
+ riscv_linux_record_tdep)
+{
+ gdb_assert (gdbarch != nullptr);
+
+ /* Initialize the riscv_linux_record_tdep. */
+ /* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+ 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;
+ riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
+}
+
/* Initialize RISC-V Linux ABI info. */
static void
@@ -205,6 +462,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 00000000000..8e48ab8e1fd
--- /dev/null
+++ b/gdb/riscv-linux-tdep.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2024 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 932708ca4e9..908cfd21409 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
@@ -1655,6 +1658,11 @@ class riscv_insn
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. */
@@ -1800,11 +1808,6 @@ class riscv_insn
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;
@@ -4415,6 +4418,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);
@@ -4833,3 +4839,694 @@ this option can be used."),
&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<ULONGEST> (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:
+ using regnum_type = int;
+ using memory_type = std::pair<CORE_ADDR, int>;
+
+ enum class record_type
+ {
+ UNKNOWN,
+ ORDINARY,
+
+ /* Corner cases. */
+ ECALL,
+ EBREAK,
+ };
+
+private:
+ using recorded_regs = std::vector<regnum_type>;
+ using recorded_mems = std::vector<memory_type>;
+
+ using mem_addr = decltype (std::declval<memory_type> ().first);
+ using mem_len = decltype (std::declval<memory_type> ().second);
+
+ record_type m_record_type = record_type::UNKNOWN;
+
+ bool m_error_occured = false;
+
+ recorded_regs m_regs;
+ recorded_mems m_mems;
+
+ /* 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. Sets error and returns false if error happened. */
+ bool
+ save_reg (regnum_type regnum) noexcept
+ {
+ try
+ {
+ m_regs.emplace_back (regnum);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ /* Save memory chunk. Sets error and returns false if error happened. */
+ bool
+ save_mem (mem_addr addr, mem_len len) noexcept
+ {
+ try
+ {
+ m_mems.emplace_back (addr, len);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ /* 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_ld_insn (ival) || is_lbu_insn (ival)
+ || is_lhu_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
+ || is_lw_insn (ival) || is_ld_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_insn (ival) || is_srli_insn (ival)
+ || is_srai_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_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_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_d_w_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. Needs error checking after. */
+ 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_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_flw_insn (ival) || is_fld_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_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_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. Needs error checking after. */
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc and
+ memory chunk of appropriate length. */
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc, rd and
+ memory chunk of appropriate length. */
+ static mem_len
+ need_save_pc_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_sc_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_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. Needs error checking after. */
+ 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 = mem_addr{};
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc, rs2, rd and
+ memory chunk of appropriate length. */
+ static mem_len
+ need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_amoswap_w_insn (ival))
+ return 4;
+ if (is_amoswap_d_insn (ival))
+ return 8;
+ return 0;
+ }
+
+ /* Returns true if instruction is classified. Needs error checking after. */
+ bool
+ try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ mem_addr addr = mem_addr{};
+ mem_len len = need_save_pc_rs2_rd_mem (ival);
+ if (len <= 0)
+ return false;
+
+ return !read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
+ || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
+ }
+
+ /* Returns true if instruction is successfully recorder. */
+ 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)
+ || try_save_pc_rs2_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 recorder.
+ Else returns false. */
+ bool
+ record_insn_len2 (ULONGEST ival, struct regcache *regcache,
+ struct gdbarch *gdbarch) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+ gdb_assert (gdbarch != nullptr);
+
+ mem_addr addr = mem_addr{};
+ int xlen = riscv_isa_xlen (gdbarch);
+
+ /* 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)
+ || (xlen == 8 && is_c_ld_insn (ival)))
+ return !save_reg (decode_crs2_short (ival))
+ || set_ordinary_record_type ();
+
+ if (is_c_fld_insn (ival) || (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) || (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 ((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 (xlen == 4 && is_c_jal_insn (ival))
+ return !save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ();
+
+ if ((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) || (xlen == 8 && is_c_subw_insn (ival))
+ || (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) || (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) || (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) || (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) || (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:
+ using regs_iter = recorded_regs::const_iterator;
+ 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 = int{};
+ 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, gdbarch);
+
+ /* 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_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
+ const riscv_recorded_insn &insn)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto regs_begin = insn.regs_begin ();
+ auto regs_end = insn.regs_end ();
+ if (std::any_of (regs_begin,
+ regs_end,
+ [®cache] (auto &®_it)
+ {
+ return record_full_arch_list_add_reg (regcache, reg_it);
+ }))
+ return -1;
+
+ auto mems_begin = insn.mems_begin ();
+ auto mems_end = insn.mems_end ();
+ if (std::any_of (mems_begin,
+ mems_end,
+ [] (auto &&mem_it)
+ {
+ return record_full_arch_list_add_mem (mem_it.first,
+ mem_it.second);
+ }))
+ return -1;
+
+ switch (insn.get_record_type ())
+ {
+ case riscv_recorded_insn::record_type::ORDINARY:
+ break;
+
+ case riscv_recorded_insn::record_type::ECALL:
+ {
+ if (!tdep->riscv_syscall_record)
+ {
+ warning (_ ("Syscall record is not supported"));
+ return -1;
+ }
+ ULONGEST reg_val = ULONGEST{};
+ if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
+ return -1;
+ return tdep->riscv_syscall_record (regcache, reg_val);
+ }
+
+ case riscv_recorded_insn::record_type::EBREAK:
+ break;
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* Parse the current instruction and record the values of the registers and
+ memory that will be changed in current instruction to record_arch_list.
+ Return -1 if something is wrong. */
+
+int
+riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+ CORE_ADDR addr)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_recorded_insn insn;
+ if (!insn.record (gdbarch, regcache, addr))
+ {
+ record_full_arch_list_add_end ();
+ return -1;
+ }
+
+ int ret_val = riscv_make_record_process (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 4bdc2e7a3d5..52b2306c35d 100644
--- a/gdb/riscv-tdep.h
+++ b/gdb/riscv-tdep.h
@@ -35,6 +35,10 @@ enum
RISCV_FP_REGNUM = 8, /* Frame Pointer. */
RISCV_A0_REGNUM = 10, /* First argument. */
RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
RISCV_PC_REGNUM = 32, /* Program Counter. */
@@ -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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
new file mode 100755
index 00000000000..e553d380924
--- /dev/null
+++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+# pylint: disable=invalid-name
+
+# Copyright (C) 2024 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/>.
+
+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 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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 410f99e3350..b048ac7078b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
- || [istarget "s390*-*-linux*"] } {
+ || [istarget "s390*-*-linux*"]
+ || [istarget "riscv*-*-*"] } {
return 1
}
--
2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* [PING][PATCH v8] This commit adds record full support for rv64gc instruction set
2025-01-16 15:48 ` [PATCH v7] " Timur
@ 2025-02-10 18:07 ` Timur
2025-02-10 19:30 ` Eli Zaretskii
0 siblings, 1 reply; 35+ messages in thread
From: Timur @ 2025-02-10 18:07 UTC (permalink / raw)
To: gdb-patches, eliz; +Cc: Timur, Guinevere Larsen
It includes changes to the following files:
- gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
syscalls.
- gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
rv64gc instructions.
- gdb/configure.tgt: adds new files for compilation.
- gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
targets.
- gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
function that canonicalizes RISC-V syscall. This script can simplify support
for syscalls on rv32 and rv64 system (currently support only for rv64). To
use this script you need to pass a path to a file with syscalls description
from riscv-glibc (example is in the help message). The script produces a
mapping from syscall names to gdb_syscall enum.
- gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
- gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
- gdb/NEWS: notification of new functionality.
Approved-By: Guinevere Larsen <guinevere@redhat.com>
Hi!
It's been 3 weeks since my last message, so I'm pinging.
I received the FSF form with my changes, so it looks like everything is ready.
Can you please look at the patch from the architecture RISC-V side?
---
gdb/NEWS | 2 +
gdb/configure.tgt | 5 +-
gdb/doc/gdb.texinfo | 2 +-
gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
gdb/riscv-linux-tdep.c | 260 +++++++
gdb/riscv-linux-tdep.h | 29 +
gdb/riscv-tdep.c | 710 +++++++++++++++++-
gdb/riscv-tdep.h | 14 +
.../riscv-canonicalize-syscall-gen.py | 148 ++++
gdb/testsuite/lib/gdb.exp | 3 +-
10 files changed, 1506 insertions(+), 9 deletions(-)
create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
create mode 100644 gdb/riscv-linux-tdep.h
create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
diff --git a/gdb/NEWS b/gdb/NEWS
index 964183910dc..20fdb571047 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -9581,3 +9581,5 @@ GDB now handles cross debugging. If you are remotely debugging between
two different machines, type ``./configure host -target=targ''.
Host is the machine where GDB will run; targ is the machine
where the program that you are debugging will run.
+
+ * Add record full support for rv64gc architectures
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 62df71b13fa..0cd5c509695 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -545,8 +545,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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 99720f1206e..d5b989d681e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7759,7 +7759,7 @@ 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,
-Moxie, PowerPC, PowerPC64, S/390, and x86 (i386/amd64) running
+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}.
diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
new file mode 100644
index 00000000000..a33f7d88b6a
--- /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 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 ff478cf4c28..d08302d8f34 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,258 @@ 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. */
+/* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+
+static void
+riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
+ struct linux_record_tdep &
+ riscv_linux_record_tdep)
+{
+ gdb_assert (gdbarch != nullptr);
+
+ /* Initialize the riscv_linux_record_tdep. */
+ /* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+ 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;
+ riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
+}
+
/* Initialize RISC-V Linux ABI info. */
static void
@@ -205,6 +462,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 00000000000..8e48ab8e1fd
--- /dev/null
+++ b/gdb/riscv-linux-tdep.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2024 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 932708ca4e9..c171fb05b3b 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
@@ -1655,6 +1658,11 @@ class riscv_insn
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. */
@@ -1800,11 +1808,6 @@ class riscv_insn
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;
@@ -4415,6 +4418,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);
@@ -4833,3 +4839,697 @@ this option can be used."),
&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<ULONGEST> (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:
+ using regnum_type = int;
+ using memory_type = std::pair<CORE_ADDR, int>;
+
+ enum class record_type
+ {
+ UNKNOWN,
+ ORDINARY,
+
+ /* Corner cases. */
+ ECALL,
+ EBREAK,
+ };
+
+private:
+ using recorded_regs = std::vector<regnum_type>;
+ using recorded_mems = std::vector<memory_type>;
+
+ using mem_addr = decltype (std::declval<memory_type> ().first);
+ using mem_len = decltype (std::declval<memory_type> ().second);
+
+ record_type m_record_type = record_type::UNKNOWN;
+
+ bool m_error_occured = false;
+
+ recorded_regs m_regs;
+ recorded_mems m_mems;
+
+ int m_xlen = int{};
+
+ /* 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. Sets error and returns false if error happened. */
+ bool
+ save_reg (regnum_type regnum) noexcept
+ {
+ try
+ {
+ m_regs.emplace_back (regnum);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ /* Save memory chunk. Sets error and returns false if error happened. */
+ bool
+ save_mem (mem_addr addr, mem_len len) noexcept
+ {
+ try
+ {
+ m_mems.emplace_back (addr, len);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ /* 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. Needs error checking after. */
+ 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. Needs error checking after. */
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc and
+ memory chunk of appropriate length. */
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc, rd and
+ memory chunk of appropriate length. */
+ static mem_len
+ need_save_pc_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_sc_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_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. Needs error checking after. */
+ 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 = mem_addr{};
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc, rs2, rd and
+ memory chunk of appropriate length. */
+ static mem_len
+ need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_amoswap_w_insn (ival))
+ return 4;
+ if (is_amoswap_d_insn (ival))
+ return 8;
+ return 0;
+ }
+
+ /* Returns true if instruction is classified. Needs error checking after. */
+ bool
+ try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ mem_addr addr = mem_addr{};
+ mem_len len = need_save_pc_rs2_rd_mem (ival);
+ if (len <= 0)
+ return false;
+
+ return !read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
+ || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
+ }
+
+ /* Returns true if instruction is successfully recorder. */
+ 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)
+ || try_save_pc_rs2_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 recorder.
+ Else returns false. */
+ 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:
+ using regs_iter = recorded_regs::const_iterator;
+ 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 = int{};
+ 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_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
+ const riscv_recorded_insn &insn)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto regs_begin = insn.regs_begin ();
+ auto regs_end = insn.regs_end ();
+ if (std::any_of (regs_begin,
+ regs_end,
+ [®cache] (auto &®_it)
+ {
+ return record_full_arch_list_add_reg (regcache, reg_it);
+ }))
+ return -1;
+
+ auto mems_begin = insn.mems_begin ();
+ auto mems_end = insn.mems_end ();
+ if (std::any_of (mems_begin,
+ mems_end,
+ [] (auto &&mem_it)
+ {
+ return record_full_arch_list_add_mem (mem_it.first,
+ mem_it.second);
+ }))
+ return -1;
+
+ switch (insn.get_record_type ())
+ {
+ case riscv_recorded_insn::record_type::ORDINARY:
+ break;
+
+ case riscv_recorded_insn::record_type::ECALL:
+ {
+ if (!tdep->riscv_syscall_record)
+ {
+ warning (_ ("Syscall record is not supported"));
+ return -1;
+ }
+ ULONGEST reg_val = ULONGEST{};
+ if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
+ return -1;
+ return tdep->riscv_syscall_record (regcache, reg_val);
+ }
+
+ case riscv_recorded_insn::record_type::EBREAK:
+ break;
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* Parse the current instruction and record the values of the registers and
+ memory that will be changed in current instruction to record_arch_list.
+ Return -1 if something is wrong. */
+
+int
+riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+ CORE_ADDR addr)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_recorded_insn insn;
+ if (!insn.record (gdbarch, regcache, addr))
+ {
+ record_full_arch_list_add_end ();
+ return -1;
+ }
+
+ int ret_val = riscv_make_record_process (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 4bdc2e7a3d5..52b2306c35d 100644
--- a/gdb/riscv-tdep.h
+++ b/gdb/riscv-tdep.h
@@ -35,6 +35,10 @@ enum
RISCV_FP_REGNUM = 8, /* Frame Pointer. */
RISCV_A0_REGNUM = 10, /* First argument. */
RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
RISCV_PC_REGNUM = 32, /* Program Counter. */
@@ -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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
new file mode 100755
index 00000000000..e553d380924
--- /dev/null
+++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+# pylint: disable=invalid-name
+
+# Copyright (C) 2024 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/>.
+
+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 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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 410f99e3350..b048ac7078b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
- || [istarget "s390*-*-linux*"] } {
+ || [istarget "s390*-*-linux*"]
+ || [istarget "riscv*-*-*"] } {
return 1
}
--
2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* Re: [PING][PATCH v8] This commit adds record full support for rv64gc instruction set
2025-02-10 18:07 ` [PING][PATCH v8] " Timur
@ 2025-02-10 19:30 ` Eli Zaretskii
2025-02-11 22:06 ` [PATCH] " Timur
0 siblings, 1 reply; 35+ messages in thread
From: Eli Zaretskii @ 2025-02-10 19:30 UTC (permalink / raw)
To: Timur; +Cc: gdb-patches, guinevere
> From: Timur <timurgol007@gmail.com>
> Cc: Timur <timurgol007@gmail.com>,
> Guinevere Larsen <guinevere@redhat.com>
> Date: Mon, 10 Feb 2025 21:07:03 +0300
>
> gdb/NEWS | 2 +
> gdb/configure.tgt | 5 +-
> gdb/doc/gdb.texinfo | 2 +-
> gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
> gdb/riscv-linux-tdep.c | 260 +++++++
> gdb/riscv-linux-tdep.h | 29 +
> gdb/riscv-tdep.c | 710 +++++++++++++++++-
> gdb/riscv-tdep.h | 14 +
> .../riscv-canonicalize-syscall-gen.py | 148 ++++
> gdb/testsuite/lib/gdb.exp | 3 +-
> 10 files changed, 1506 insertions(+), 9 deletions(-)
> create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
> create mode 100644 gdb/riscv-linux-tdep.h
> create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
Thanks, the changes for the documentation are okay.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
^ permalink raw reply [flat|nested] 35+ messages in thread
* [PATCH] This commit adds record full support for rv64gc instruction set
2025-02-10 19:30 ` Eli Zaretskii
@ 2025-02-11 22:06 ` Timur
2025-02-26 11:14 ` [PATCH][PING] " Timur
2025-03-13 15:03 ` [PATCH] " Andrew Burgess
0 siblings, 2 replies; 35+ messages in thread
From: Timur @ 2025-02-11 22:06 UTC (permalink / raw)
To: gdb-patches, guinevere; +Cc: Timur, Eli Zaretskii
It includes changes to the following files:
- gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
syscalls.
- gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
rv64gc instructions.
- gdb/configure.tgt: adds new files for compilation.
- gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
targets.
- gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
function that canonicalizes RISC-V syscall. This script can simplify support
for syscalls on rv32 and rv64 system (currently support only for rv64). To
use this script you need to pass a path to a file with syscalls description
from riscv-glibc (example is in the help message). The script produces a
mapping from syscall names to gdb_syscall enum.
- gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
- gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
- gdb/NEWS: notification of new functionality.
Approved-By: Guinevere Larsen <guinevere@redhat.com>
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Hi!
This patch got Reviewed-By: Eli Zaretskii <eliz@gnu.org> and
Approved-By: Guinevere Larsen <guinevere@redhat.com>. I got a FSF form with my
changes. This is my first commit in GDB and I don't know how can I merge this
patch. Can someone please help me do it, or do I need something more?
---
gdb/NEWS | 2 +
gdb/configure.tgt | 5 +-
gdb/doc/gdb.texinfo | 2 +-
gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
gdb/riscv-linux-tdep.c | 260 +++++++
gdb/riscv-linux-tdep.h | 29 +
gdb/riscv-tdep.c | 710 +++++++++++++++++-
gdb/riscv-tdep.h | 14 +
.../riscv-canonicalize-syscall-gen.py | 148 ++++
gdb/testsuite/lib/gdb.exp | 3 +-
10 files changed, 1506 insertions(+), 9 deletions(-)
create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
create mode 100644 gdb/riscv-linux-tdep.h
create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
diff --git a/gdb/NEWS b/gdb/NEWS
index 964183910dc..20fdb571047 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -9581,3 +9581,5 @@ GDB now handles cross debugging. If you are remotely debugging between
two different machines, type ``./configure host -target=targ''.
Host is the machine where GDB will run; targ is the machine
where the program that you are debugging will run.
+
+ * Add record full support for rv64gc architectures
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 62df71b13fa..0cd5c509695 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -545,8 +545,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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 99720f1206e..d5b989d681e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7759,7 +7759,7 @@ 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,
-Moxie, PowerPC, PowerPC64, S/390, and x86 (i386/amd64) running
+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}.
diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
new file mode 100644
index 00000000000..a33f7d88b6a
--- /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 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 ff478cf4c28..d08302d8f34 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,258 @@ 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. */
+/* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+
+static void
+riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
+ struct linux_record_tdep &
+ riscv_linux_record_tdep)
+{
+ gdb_assert (gdbarch != nullptr);
+
+ /* Initialize the riscv_linux_record_tdep. */
+ /* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+ 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;
+ riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
+}
+
/* Initialize RISC-V Linux ABI info. */
static void
@@ -205,6 +462,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 00000000000..8e48ab8e1fd
--- /dev/null
+++ b/gdb/riscv-linux-tdep.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2024 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 932708ca4e9..c171fb05b3b 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
@@ -1655,6 +1658,11 @@ class riscv_insn
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. */
@@ -1800,11 +1808,6 @@ class riscv_insn
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;
@@ -4415,6 +4418,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);
@@ -4833,3 +4839,697 @@ this option can be used."),
&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<ULONGEST> (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:
+ using regnum_type = int;
+ using memory_type = std::pair<CORE_ADDR, int>;
+
+ enum class record_type
+ {
+ UNKNOWN,
+ ORDINARY,
+
+ /* Corner cases. */
+ ECALL,
+ EBREAK,
+ };
+
+private:
+ using recorded_regs = std::vector<regnum_type>;
+ using recorded_mems = std::vector<memory_type>;
+
+ using mem_addr = decltype (std::declval<memory_type> ().first);
+ using mem_len = decltype (std::declval<memory_type> ().second);
+
+ record_type m_record_type = record_type::UNKNOWN;
+
+ bool m_error_occured = false;
+
+ recorded_regs m_regs;
+ recorded_mems m_mems;
+
+ int m_xlen = int{};
+
+ /* 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. Sets error and returns false if error happened. */
+ bool
+ save_reg (regnum_type regnum) noexcept
+ {
+ try
+ {
+ m_regs.emplace_back (regnum);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ /* Save memory chunk. Sets error and returns false if error happened. */
+ bool
+ save_mem (mem_addr addr, mem_len len) noexcept
+ {
+ try
+ {
+ m_mems.emplace_back (addr, len);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ /* 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. Needs error checking after. */
+ 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. Needs error checking after. */
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc and
+ memory chunk of appropriate length. */
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc, rd and
+ memory chunk of appropriate length. */
+ static mem_len
+ need_save_pc_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_sc_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_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. Needs error checking after. */
+ 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 = mem_addr{};
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc, rs2, rd and
+ memory chunk of appropriate length. */
+ static mem_len
+ need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_amoswap_w_insn (ival))
+ return 4;
+ if (is_amoswap_d_insn (ival))
+ return 8;
+ return 0;
+ }
+
+ /* Returns true if instruction is classified. Needs error checking after. */
+ bool
+ try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ mem_addr addr = mem_addr{};
+ mem_len len = need_save_pc_rs2_rd_mem (ival);
+ if (len <= 0)
+ return false;
+
+ return !read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
+ || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
+ }
+
+ /* Returns true if instruction is successfully recorder. */
+ 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)
+ || try_save_pc_rs2_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 recorder.
+ Else returns false. */
+ 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:
+ using regs_iter = recorded_regs::const_iterator;
+ 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 = int{};
+ 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_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
+ const riscv_recorded_insn &insn)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto regs_begin = insn.regs_begin ();
+ auto regs_end = insn.regs_end ();
+ if (std::any_of (regs_begin,
+ regs_end,
+ [®cache] (auto &®_it)
+ {
+ return record_full_arch_list_add_reg (regcache, reg_it);
+ }))
+ return -1;
+
+ auto mems_begin = insn.mems_begin ();
+ auto mems_end = insn.mems_end ();
+ if (std::any_of (mems_begin,
+ mems_end,
+ [] (auto &&mem_it)
+ {
+ return record_full_arch_list_add_mem (mem_it.first,
+ mem_it.second);
+ }))
+ return -1;
+
+ switch (insn.get_record_type ())
+ {
+ case riscv_recorded_insn::record_type::ORDINARY:
+ break;
+
+ case riscv_recorded_insn::record_type::ECALL:
+ {
+ if (!tdep->riscv_syscall_record)
+ {
+ warning (_ ("Syscall record is not supported"));
+ return -1;
+ }
+ ULONGEST reg_val = ULONGEST{};
+ if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
+ return -1;
+ return tdep->riscv_syscall_record (regcache, reg_val);
+ }
+
+ case riscv_recorded_insn::record_type::EBREAK:
+ break;
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* Parse the current instruction and record the values of the registers and
+ memory that will be changed in current instruction to record_arch_list.
+ Return -1 if something is wrong. */
+
+int
+riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+ CORE_ADDR addr)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_recorded_insn insn;
+ if (!insn.record (gdbarch, regcache, addr))
+ {
+ record_full_arch_list_add_end ();
+ return -1;
+ }
+
+ int ret_val = riscv_make_record_process (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 4bdc2e7a3d5..52b2306c35d 100644
--- a/gdb/riscv-tdep.h
+++ b/gdb/riscv-tdep.h
@@ -35,6 +35,10 @@ enum
RISCV_FP_REGNUM = 8, /* Frame Pointer. */
RISCV_A0_REGNUM = 10, /* First argument. */
RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
RISCV_PC_REGNUM = 32, /* Program Counter. */
@@ -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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
new file mode 100755
index 00000000000..e553d380924
--- /dev/null
+++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+# pylint: disable=invalid-name
+
+# Copyright (C) 2024 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/>.
+
+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 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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 410f99e3350..b048ac7078b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
- || [istarget "s390*-*-linux*"] } {
+ || [istarget "s390*-*-linux*"]
+ || [istarget "riscv*-*-*"] } {
return 1
}
--
2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* [PATCH][PING] This commit adds record full support for rv64gc instruction set
2025-02-11 22:06 ` [PATCH] " Timur
@ 2025-02-26 11:14 ` Timur
2025-03-13 10:38 ` Andrew Burgess
2025-03-13 15:03 ` [PATCH] " Andrew Burgess
1 sibling, 1 reply; 35+ messages in thread
From: Timur @ 2025-02-26 11:14 UTC (permalink / raw)
To: gdb-patches, guinevere; +Cc: Timur, Eli Zaretskii
It includes changes to the following files:
- gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
syscalls.
- gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
rv64gc instructions.
- gdb/configure.tgt: adds new files for compilation.
- gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
targets.
- gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
function that canonicalizes RISC-V syscall. This script can simplify support
for syscalls on rv32 and rv64 system (currently support only for rv64). To
use this script you need to pass a path to a file with syscalls description
from riscv-glibc (example is in the help message). The script produces a
mapping from syscall names to gdb_syscall enum.
- gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
- gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
- gdb/NEWS: notification of new functionality.
Approved-By: Guinevere Larsen <guinevere@redhat.com>
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Hi!
It's been 2 weeks since my last message, so I'm pinging.
> This patch got Reviewed-By: Eli Zaretskii <eliz@gnu.org> and
> Approved-By: Guinevere Larsen <guinevere@redhat.com>. I got a FSF form with
> my changes. This is my first commit in GDB and I don't know how can I merge
> this patch. Can someone please help me do it, or do I need something more?
---
gdb/NEWS | 2 +
gdb/configure.tgt | 5 +-
gdb/doc/gdb.texinfo | 2 +-
gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
gdb/riscv-linux-tdep.c | 260 +++++++
gdb/riscv-linux-tdep.h | 29 +
gdb/riscv-tdep.c | 710 +++++++++++++++++-
gdb/riscv-tdep.h | 14 +
.../riscv-canonicalize-syscall-gen.py | 148 ++++
gdb/testsuite/lib/gdb.exp | 3 +-
10 files changed, 1506 insertions(+), 9 deletions(-)
create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
create mode 100644 gdb/riscv-linux-tdep.h
create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
diff --git a/gdb/NEWS b/gdb/NEWS
index 964183910dc..20fdb571047 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -9581,3 +9581,5 @@ GDB now handles cross debugging. If you are remotely debugging between
two different machines, type ``./configure host -target=targ''.
Host is the machine where GDB will run; targ is the machine
where the program that you are debugging will run.
+
+ * Add record full support for rv64gc architectures
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 62df71b13fa..0cd5c509695 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -545,8 +545,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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 99720f1206e..d5b989d681e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7759,7 +7759,7 @@ 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,
-Moxie, PowerPC, PowerPC64, S/390, and x86 (i386/amd64) running
+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}.
diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
new file mode 100644
index 00000000000..a33f7d88b6a
--- /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 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 ff478cf4c28..d08302d8f34 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,258 @@ 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. */
+/* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+
+static void
+riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
+ struct linux_record_tdep &
+ riscv_linux_record_tdep)
+{
+ gdb_assert (gdbarch != nullptr);
+
+ /* Initialize the riscv_linux_record_tdep. */
+ /* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+ 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;
+ riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
+}
+
/* Initialize RISC-V Linux ABI info. */
static void
@@ -205,6 +462,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 00000000000..8e48ab8e1fd
--- /dev/null
+++ b/gdb/riscv-linux-tdep.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2024 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 932708ca4e9..c171fb05b3b 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
@@ -1655,6 +1658,11 @@ class riscv_insn
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. */
@@ -1800,11 +1808,6 @@ class riscv_insn
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;
@@ -4415,6 +4418,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);
@@ -4833,3 +4839,697 @@ this option can be used."),
&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<ULONGEST> (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:
+ using regnum_type = int;
+ using memory_type = std::pair<CORE_ADDR, int>;
+
+ enum class record_type
+ {
+ UNKNOWN,
+ ORDINARY,
+
+ /* Corner cases. */
+ ECALL,
+ EBREAK,
+ };
+
+private:
+ using recorded_regs = std::vector<regnum_type>;
+ using recorded_mems = std::vector<memory_type>;
+
+ using mem_addr = decltype (std::declval<memory_type> ().first);
+ using mem_len = decltype (std::declval<memory_type> ().second);
+
+ record_type m_record_type = record_type::UNKNOWN;
+
+ bool m_error_occured = false;
+
+ recorded_regs m_regs;
+ recorded_mems m_mems;
+
+ int m_xlen = int{};
+
+ /* 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. Sets error and returns false if error happened. */
+ bool
+ save_reg (regnum_type regnum) noexcept
+ {
+ try
+ {
+ m_regs.emplace_back (regnum);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ /* Save memory chunk. Sets error and returns false if error happened. */
+ bool
+ save_mem (mem_addr addr, mem_len len) noexcept
+ {
+ try
+ {
+ m_mems.emplace_back (addr, len);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ /* 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. Needs error checking after. */
+ 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. Needs error checking after. */
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc and
+ memory chunk of appropriate length. */
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc, rd and
+ memory chunk of appropriate length. */
+ static mem_len
+ need_save_pc_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_sc_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_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. Needs error checking after. */
+ 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 = mem_addr{};
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc, rs2, rd and
+ memory chunk of appropriate length. */
+ static mem_len
+ need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_amoswap_w_insn (ival))
+ return 4;
+ if (is_amoswap_d_insn (ival))
+ return 8;
+ return 0;
+ }
+
+ /* Returns true if instruction is classified. Needs error checking after. */
+ bool
+ try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ mem_addr addr = mem_addr{};
+ mem_len len = need_save_pc_rs2_rd_mem (ival);
+ if (len <= 0)
+ return false;
+
+ return !read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
+ || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
+ }
+
+ /* Returns true if instruction is successfully recorder. */
+ 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)
+ || try_save_pc_rs2_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 recorder.
+ Else returns false. */
+ 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:
+ using regs_iter = recorded_regs::const_iterator;
+ 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 = int{};
+ 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_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
+ const riscv_recorded_insn &insn)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto regs_begin = insn.regs_begin ();
+ auto regs_end = insn.regs_end ();
+ if (std::any_of (regs_begin,
+ regs_end,
+ [®cache] (auto &®_it)
+ {
+ return record_full_arch_list_add_reg (regcache, reg_it);
+ }))
+ return -1;
+
+ auto mems_begin = insn.mems_begin ();
+ auto mems_end = insn.mems_end ();
+ if (std::any_of (mems_begin,
+ mems_end,
+ [] (auto &&mem_it)
+ {
+ return record_full_arch_list_add_mem (mem_it.first,
+ mem_it.second);
+ }))
+ return -1;
+
+ switch (insn.get_record_type ())
+ {
+ case riscv_recorded_insn::record_type::ORDINARY:
+ break;
+
+ case riscv_recorded_insn::record_type::ECALL:
+ {
+ if (!tdep->riscv_syscall_record)
+ {
+ warning (_ ("Syscall record is not supported"));
+ return -1;
+ }
+ ULONGEST reg_val = ULONGEST{};
+ if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
+ return -1;
+ return tdep->riscv_syscall_record (regcache, reg_val);
+ }
+
+ case riscv_recorded_insn::record_type::EBREAK:
+ break;
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* Parse the current instruction and record the values of the registers and
+ memory that will be changed in current instruction to record_arch_list.
+ Return -1 if something is wrong. */
+
+int
+riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+ CORE_ADDR addr)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_recorded_insn insn;
+ if (!insn.record (gdbarch, regcache, addr))
+ {
+ record_full_arch_list_add_end ();
+ return -1;
+ }
+
+ int ret_val = riscv_make_record_process (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 4bdc2e7a3d5..52b2306c35d 100644
--- a/gdb/riscv-tdep.h
+++ b/gdb/riscv-tdep.h
@@ -35,6 +35,10 @@ enum
RISCV_FP_REGNUM = 8, /* Frame Pointer. */
RISCV_A0_REGNUM = 10, /* First argument. */
RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
RISCV_PC_REGNUM = 32, /* Program Counter. */
@@ -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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
new file mode 100755
index 00000000000..e553d380924
--- /dev/null
+++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+# pylint: disable=invalid-name
+
+# Copyright (C) 2024 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/>.
+
+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 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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 410f99e3350..b048ac7078b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
- || [istarget "s390*-*-linux*"] } {
+ || [istarget "s390*-*-linux*"]
+ || [istarget "riscv*-*-*"] } {
return 1
}
--
2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* Re: [PATCH][PING] This commit adds record full support for rv64gc instruction set
2025-02-26 11:14 ` [PATCH][PING] " Timur
@ 2025-03-13 10:38 ` Andrew Burgess
0 siblings, 0 replies; 35+ messages in thread
From: Andrew Burgess @ 2025-03-13 10:38 UTC (permalink / raw)
To: Timur, gdb-patches, guinevere; +Cc: Timur, Eli Zaretskii
Timur <timurgol007@gmail.com> writes:
> It includes changes to the following files:
> - gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
> syscalls.
> - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
> rv64gc instructions.
> - gdb/configure.tgt: adds new files for compilation.
> - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
> targets.
> - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
> function that canonicalizes RISC-V syscall. This script can simplify support
> for syscalls on rv32 and rv64 system (currently support only for rv64). To
> use this script you need to pass a path to a file with syscalls description
> from riscv-glibc (example is in the help message). The script produces a
> mapping from syscall names to gdb_syscall enum.
> - gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
> - gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
> - gdb/NEWS: notification of new functionality.
>
> Approved-By: Guinevere Larsen <guinevere@redhat.com>
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
>
> Hi!
> It's been 2 weeks since my last message, so I'm pinging.
I'll take a look through this today/tomorrow.
Sorry for the delay.
Thanks,
Andrew
>
>> This patch got Reviewed-By: Eli Zaretskii <eliz@gnu.org> and
>> Approved-By: Guinevere Larsen <guinevere@redhat.com>. I got a FSF form with
>> my changes. This is my first commit in GDB and I don't know how can I merge
>> this patch. Can someone please help me do it, or do I need something more?
>
> ---
> gdb/NEWS | 2 +
> gdb/configure.tgt | 5 +-
> gdb/doc/gdb.texinfo | 2 +-
> gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
> gdb/riscv-linux-tdep.c | 260 +++++++
> gdb/riscv-linux-tdep.h | 29 +
> gdb/riscv-tdep.c | 710 +++++++++++++++++-
> gdb/riscv-tdep.h | 14 +
> .../riscv-canonicalize-syscall-gen.py | 148 ++++
> gdb/testsuite/lib/gdb.exp | 3 +-
> 10 files changed, 1506 insertions(+), 9 deletions(-)
> create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
> create mode 100644 gdb/riscv-linux-tdep.h
> create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 964183910dc..20fdb571047 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -9581,3 +9581,5 @@ GDB now handles cross debugging. If you are remotely debugging between
> two different machines, type ``./configure host -target=targ''.
> Host is the machine where GDB will run; targ is the machine
> where the program that you are debugging will run.
> +
> + * Add record full support for rv64gc architectures
> diff --git a/gdb/configure.tgt b/gdb/configure.tgt
> index 62df71b13fa..0cd5c509695 100644
> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt
> @@ -545,8 +545,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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 99720f1206e..d5b989d681e 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -7759,7 +7759,7 @@ 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,
> -Moxie, PowerPC, PowerPC64, S/390, and x86 (i386/amd64) running
> +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}.
>
> diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
> new file mode 100644
> index 00000000000..a33f7d88b6a
> --- /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 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 ff478cf4c28..d08302d8f34 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,258 @@ 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. */
> +/* These values are the size of the type that will be used in a system
> + call. They are obtained from Linux Kernel source. */
> +
> +static void
> +riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
> + struct linux_record_tdep &
> + riscv_linux_record_tdep)
> +{
> + gdb_assert (gdbarch != nullptr);
> +
> + /* Initialize the riscv_linux_record_tdep. */
> + /* These values are the size of the type that will be used in a system
> + call. They are obtained from Linux Kernel source. */
> + 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;
> + riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
> +}
> +
> /* Initialize RISC-V Linux ABI info. */
>
> static void
> @@ -205,6 +462,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 00000000000..8e48ab8e1fd
> --- /dev/null
> +++ b/gdb/riscv-linux-tdep.h
> @@ -0,0 +1,29 @@
> +/* Copyright (C) 2024 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 932708ca4e9..c171fb05b3b 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
>
> @@ -1655,6 +1658,11 @@ class riscv_insn
> 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. */
> @@ -1800,11 +1808,6 @@ class riscv_insn
> 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;
>
> @@ -4415,6 +4418,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);
>
> @@ -4833,3 +4839,697 @@ this option can be used."),
> &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<ULONGEST> (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:
> + using regnum_type = int;
> + using memory_type = std::pair<CORE_ADDR, int>;
> +
> + enum class record_type
> + {
> + UNKNOWN,
> + ORDINARY,
> +
> + /* Corner cases. */
> + ECALL,
> + EBREAK,
> + };
> +
> +private:
> + using recorded_regs = std::vector<regnum_type>;
> + using recorded_mems = std::vector<memory_type>;
> +
> + using mem_addr = decltype (std::declval<memory_type> ().first);
> + using mem_len = decltype (std::declval<memory_type> ().second);
> +
> + record_type m_record_type = record_type::UNKNOWN;
> +
> + bool m_error_occured = false;
> +
> + recorded_regs m_regs;
> + recorded_mems m_mems;
> +
> + int m_xlen = int{};
> +
> + /* 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. Sets error and returns false if error happened. */
> + bool
> + save_reg (regnum_type regnum) noexcept
> + {
> + try
> + {
> + m_regs.emplace_back (regnum);
> + return true;
> + }
> + catch (const std::bad_alloc &ex)
> + {
> + warning (_ ("Exception was caught during reverse execution: %s"),
> + ex.what ());
> + return set_error ();
> + }
> + }
> +
> + /* Save memory chunk. Sets error and returns false if error happened. */
> + bool
> + save_mem (mem_addr addr, mem_len len) noexcept
> + {
> + try
> + {
> + m_mems.emplace_back (addr, len);
> + return true;
> + }
> + catch (const std::bad_alloc &ex)
> + {
> + warning (_ ("Exception was caught during reverse execution: %s"),
> + ex.what ());
> + return set_error ();
> + }
> + }
> +
> + /* 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. Needs error checking after. */
> + 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. Needs error checking after. */
> + 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. Needs error checking after. */
> + 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 ();
> + }
> +
> + /* If return value is > 0, instruction needs only saving pc and
> + memory chunk of appropriate length. */
> + 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. Needs error checking after. */
> + 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 ();
> + }
> +
> + /* If return value is > 0, instruction needs only saving pc, rd and
> + memory chunk of appropriate length. */
> + static mem_len
> + need_save_pc_rd_mem (ULONGEST ival) noexcept
> + {
> + if (is_sc_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_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. Needs error checking after. */
> + 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 = mem_addr{};
> + 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 ();
> + }
> +
> + /* If return value is > 0, instruction needs only saving pc, rs2, rd and
> + memory chunk of appropriate length. */
> + static mem_len
> + need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
> + {
> + if (is_amoswap_w_insn (ival))
> + return 4;
> + if (is_amoswap_d_insn (ival))
> + return 8;
> + return 0;
> + }
> +
> + /* Returns true if instruction is classified. Needs error checking after. */
> + bool
> + try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + mem_addr addr = mem_addr{};
> + mem_len len = need_save_pc_rs2_rd_mem (ival);
> + if (len <= 0)
> + return false;
> +
> + return !read_reg (regcache, decode_rs1 (ival), addr)
> + || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
> + || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
> + }
> +
> + /* Returns true if instruction is successfully recorder. */
> + 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)
> + || try_save_pc_rs2_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 recorder.
> + Else returns false. */
> + 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:
> + using regs_iter = recorded_regs::const_iterator;
> + 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 = int{};
> + 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_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
> + const riscv_recorded_insn &insn)
> +{
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> + auto regs_begin = insn.regs_begin ();
> + auto regs_end = insn.regs_end ();
> + if (std::any_of (regs_begin,
> + regs_end,
> + [®cache] (auto &®_it)
> + {
> + return record_full_arch_list_add_reg (regcache, reg_it);
> + }))
> + return -1;
> +
> + auto mems_begin = insn.mems_begin ();
> + auto mems_end = insn.mems_end ();
> + if (std::any_of (mems_begin,
> + mems_end,
> + [] (auto &&mem_it)
> + {
> + return record_full_arch_list_add_mem (mem_it.first,
> + mem_it.second);
> + }))
> + return -1;
> +
> + switch (insn.get_record_type ())
> + {
> + case riscv_recorded_insn::record_type::ORDINARY:
> + break;
> +
> + case riscv_recorded_insn::record_type::ECALL:
> + {
> + if (!tdep->riscv_syscall_record)
> + {
> + warning (_ ("Syscall record is not supported"));
> + return -1;
> + }
> + ULONGEST reg_val = ULONGEST{};
> + if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
> + return -1;
> + return tdep->riscv_syscall_record (regcache, reg_val);
> + }
> +
> + case riscv_recorded_insn::record_type::EBREAK:
> + break;
> +
> + default:
> + return -1;
> + }
> + return 0;
> +}
> +
> +/* Parse the current instruction and record the values of the registers and
> + memory that will be changed in current instruction to record_arch_list.
> + Return -1 if something is wrong. */
> +
> +int
> +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
> + CORE_ADDR addr)
> +{
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + riscv_recorded_insn insn;
> + if (!insn.record (gdbarch, regcache, addr))
> + {
> + record_full_arch_list_add_end ();
> + return -1;
> + }
> +
> + int ret_val = riscv_make_record_process (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 4bdc2e7a3d5..52b2306c35d 100644
> --- a/gdb/riscv-tdep.h
> +++ b/gdb/riscv-tdep.h
> @@ -35,6 +35,10 @@ enum
> RISCV_FP_REGNUM = 8, /* Frame Pointer. */
> RISCV_A0_REGNUM = 10, /* First argument. */
> RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
> RISCV_PC_REGNUM = 32, /* Program Counter. */
>
> @@ -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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> new file mode 100755
> index 00000000000..e553d380924
> --- /dev/null
> +++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> @@ -0,0 +1,148 @@
> +#!/usr/bin/env python3
> +# pylint: disable=invalid-name
> +
> +# Copyright (C) 2024 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/>.
> +
> +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 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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 410f99e3350..b048ac7078b 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -3718,7 +3718,8 @@ proc supports_reverse {} {
> || [istarget "i\[34567\]86-*-linux*"]
> || [istarget "aarch64*-*-linux*"]
> || [istarget "powerpc*-*-linux*"]
> - || [istarget "s390*-*-linux*"] } {
> + || [istarget "s390*-*-linux*"]
> + || [istarget "riscv*-*-*"] } {
> return 1
> }
>
> --
> 2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH] This commit adds record full support for rv64gc instruction set
2025-02-11 22:06 ` [PATCH] " Timur
2025-02-26 11:14 ` [PATCH][PING] " Timur
@ 2025-03-13 15:03 ` Andrew Burgess
2025-03-18 18:49 ` [PATCH v8] " Timur
1 sibling, 1 reply; 35+ messages in thread
From: Andrew Burgess @ 2025-03-13 15:03 UTC (permalink / raw)
To: Timur, gdb-patches, guinevere; +Cc: Timur, Eli Zaretskii
Timur <timurgol007@gmail.com> writes:
> It includes changes to the following files:
> - gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
> syscalls.
> - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
> rv64gc instructions.
> - gdb/configure.tgt: adds new files for compilation.
> - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
> targets.
> - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
> function that canonicalizes RISC-V syscall. This script can simplify support
> for syscalls on rv32 and rv64 system (currently support only for rv64). To
> use this script you need to pass a path to a file with syscalls description
> from riscv-glibc (example is in the help message). The script produces a
> mapping from syscall names to gdb_syscall enum.
> - gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
> - gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
> - gdb/NEWS: notification of new functionality.
>
> Approved-By: Guinevere Larsen <guinevere@redhat.com>
> Reviewed-By: Eli Zaretskii <eliz@gnu.org>
>
> Hi!
> This patch got Reviewed-By: Eli Zaretskii <eliz@gnu.org> and
> Approved-By: Guinevere Larsen <guinevere@redhat.com>. I got a FSF form with my
> changes. This is my first commit in GDB and I don't know how can I merge this
> patch. Can someone please help me do it, or do I need something more?
Thanks for working on this. Sorry I didn't get round to reviewing it
earlier.
I do have some feedback. I think it's mostly minor stuff though, style
issues, so hopefully shouldn't take too long to fix up.
>
> ---
> gdb/NEWS | 2 +
> gdb/configure.tgt | 5 +-
> gdb/doc/gdb.texinfo | 2 +-
> gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
> gdb/riscv-linux-tdep.c | 260 +++++++
> gdb/riscv-linux-tdep.h | 29 +
> gdb/riscv-tdep.c | 710 +++++++++++++++++-
> gdb/riscv-tdep.h | 14 +
> .../riscv-canonicalize-syscall-gen.py | 148 ++++
> gdb/testsuite/lib/gdb.exp | 3 +-
> 10 files changed, 1506 insertions(+), 9 deletions(-)
> create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
> create mode 100644 gdb/riscv-linux-tdep.h
> create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 964183910dc..20fdb571047 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -9581,3 +9581,5 @@ GDB now handles cross debugging. If you are remotely debugging between
> two different machines, type ``./configure host -target=targ''.
> Host is the machine where GDB will run; targ is the machine
> where the program that you are debugging will run.
> +
> + * Add record full support for rv64gc architectures
> diff --git a/gdb/configure.tgt b/gdb/configure.tgt
> index 62df71b13fa..0cd5c509695 100644
> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt
> @@ -545,8 +545,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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 99720f1206e..d5b989d681e 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -7759,7 +7759,7 @@ 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,
> -Moxie, PowerPC, PowerPC64, S/390, and x86 (i386/amd64) running
> +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}.
>
> diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
> new file mode 100644
> index 00000000000..a33f7d88b6a
> --- /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 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 ff478cf4c28..d08302d8f34 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,258 @@ 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 "
Unfortunately, the '_' function is the one function which breaks the
space before '(' rule. All uses should be just '_('. There are a
couple of incorrect uses of '_ (' in the code, but the majority don't
include the space.
> + "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. */
> +/* These values are the size of the type that will be used in a system
> + call. They are obtained from Linux Kernel source. */
There comments should be merged into one please. Or maybe just drop the
second part? It is repeated in the function body, and, honestly, the
comment above the function confused me initially, I thought "These
values..." was referring to the function arguments. In the function
body it is much clearer what you are referencing.
> +
> +static void
> +riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
> + struct linux_record_tdep &
> + riscv_linux_record_tdep)
> +{
> + gdb_assert (gdbarch != nullptr);
> +
> + /* Initialize the riscv_linux_record_tdep. */
> + /* These values are the size of the type that will be used in a system
> + call. They are obtained from Linux Kernel source. */
I've not checked any of these values. I figure bugs will be ironed out
in the future.
> + 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;
> + riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
This really puzzled me, I even went and checked the ABI documentation.
But, doesn't the 7th argument go into a6? I have a comment related to
this on the definition of RISCV_A7_REGNUM.
> +}
> +
> /* Initialize RISC-V Linux ABI info. */
>
> static void
> @@ -205,6 +462,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 00000000000..8e48ab8e1fd
> --- /dev/null
> +++ b/gdb/riscv-linux-tdep.h
> @@ -0,0 +1,29 @@
> +/* Copyright (C) 2024 Free Software Foundation, Inc.
When you update, remember to update all the dates to 2024-2025.
> +
> + 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 932708ca4e9..c171fb05b3b 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
>
> @@ -1655,6 +1658,11 @@ class riscv_insn
> 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. */
> @@ -1800,11 +1808,6 @@ class riscv_insn
> 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;
>
> @@ -4415,6 +4418,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);
>
> @@ -4833,3 +4839,697 @@ this option can be used."),
> &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<ULONGEST> (regnum, &addr)
Is the type name needed here? This is the only raw_read call in GDB
that needs the '<ULONGEST>', other uses seem to get by with type
inference.
> + != 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:
> + using regnum_type = int;
> + using memory_type = std::pair<CORE_ADDR, int>;
Ideally all member types and variables should have a comment. That rule
is (I think, unfortunately) not strictly enforced, but for
e.g. memory_type, a comment would be useful. I can guess that the 'int'
is a length, but it would be nice to have this spelled out.
> +
> + enum class record_type
> + {
> + UNKNOWN,
> + ORDINARY,
> +
> + /* Corner cases. */
> + ECALL,
> + EBREAK,
> + };
> +
> +private:
> + using recorded_regs = std::vector<regnum_type>;
> + using recorded_mems = std::vector<memory_type>;
> +
> + using mem_addr = decltype (std::declval<memory_type> ().first);
> + using mem_len = decltype (std::declval<memory_type> ().second);
> +
> + record_type m_record_type = record_type::UNKNOWN;
> +
> + bool m_error_occured = false;
> +
> + recorded_regs m_regs;
> + recorded_mems m_mems;
As with the types, comments on these to indicate their intended usage
would be great.
> +
> + int m_xlen = int{};
I know that the '{}' style is recommended in some places, but its use is
not common in GDB, and (personally) the more explicit 'int m_xlen = 0;'
seems far clearer. Please could you change the two uses of '{}' to use
value initialisation.
> +
> + /* Helper for decode 16-bit instruction RS1. */
Unfortunately, GNU style dictates two spaces after a '.', both at the
end of a comment, and at the end of a sentence within a comment. I
understand this is going to be a pain to fix up, but consistent style
helps make the code base easier to read.
> + 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. Sets error and returns false if error happened. */
> + bool
> + save_reg (regnum_type regnum) noexcept
> + {
> + try
> + {
> + m_regs.emplace_back (regnum);
> + return true;
> + }
> + catch (const std::bad_alloc &ex)
> + {
> + warning (_ ("Exception was caught during reverse execution: %s"),
> + ex.what ());
> + return set_error ();
> + }
Maybe I'm wrong, but I'm pretty sure that `warning` also tries to
allocate memory, so might throw an exception. I think my point is that,
if trying to allocate such a small memory block triggers an exception,
then we're pretty much dead anyway.
As such, I wonder if we might as well just drop the try/catch, and rely
on the noexcept triggering an abort() if an exception is thrown?
I also notice that this appears to be the only place in GDB where we are
catching std::bad_alloc, which makes me a little wonder if this is
necessary or not.
> + }
> +
> + /* Save memory chunk. Sets error and returns false if error happened. */
> + bool
> + save_mem (mem_addr addr, mem_len len) noexcept
> + {
> + try
> + {
> + m_mems.emplace_back (addr, len);
> + return true;
> + }
> + catch (const std::bad_alloc &ex)
> + {
> + warning (_ ("Exception was caught during reverse execution: %s"),
> + ex.what ());
> + return set_error ();
> + }
> + }
> +
> + /* 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);
GNU style is that multi-line return expressions should be wrapped
with ( ... ), which helps some editors better align the code.
> + }
> +
> + /* 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. Needs error checking after. */
> + 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. Needs error checking after. */
> + 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. Needs error checking after. */
> + 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 ();
> + }
> +
> + /* If return value is > 0, instruction needs only saving pc and
> + memory chunk of appropriate length. */
Can you reword this comment please. You should explain that the return
value indicates the size of memory block that needs saving. There are a
couple of places where the comments above a function are a bit vague,
not really explaining all possible return values, or what happens when
an error occurs.
> + 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. Needs error checking after. */
What does 'Needs error checking after' mean? I guess it means a call to
has_error()? I think the comment should say something like 'This
function can set m_error_occured.' But also, what is the return value
if an error is set?
> + 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 ();
> + }
> +
> + /* If return value is > 0, instruction needs only saving pc, rd and
> + memory chunk of appropriate length. */
> + static mem_len
> + need_save_pc_rd_mem (ULONGEST ival) noexcept
> + {
> + if (is_sc_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_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. Needs error checking after. */
> + 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 = mem_addr{};
> + 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 ();
> + }
> +
> + /* If return value is > 0, instruction needs only saving pc, rs2, rd and
> + memory chunk of appropriate length. */
> + static mem_len
> + need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
> + {
> + if (is_amoswap_w_insn (ival))
> + return 4;
> + if (is_amoswap_d_insn (ival))
> + return 8;
> + return 0;
> + }
> +
> + /* Returns true if instruction is classified. Needs error checking after. */
> + bool
> + try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + mem_addr addr = mem_addr{};
> + mem_len len = need_save_pc_rs2_rd_mem (ival);
> + if (len <= 0)
> + return false;
> +
> + return !read_reg (regcache, decode_rs1 (ival), addr)
> + || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
> + || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
> + }
> +
> + /* Returns true if instruction is successfully recorder. */
> + 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)
> + || try_save_pc_rs2_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 recorder.
> + Else returns false. */
Comments on functions like this should explain the prerequisites,
e.g. in this case, the instruction must be of length 2. At least, I
assume that is a prerequisite, based on how the function is used.
> + 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:
> + using regs_iter = recorded_regs::const_iterator;
> + 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 = int{};
> + 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_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
> + const riscv_recorded_insn &insn)
Just a little thing, but this function name doesn't make sense to me.
Is this really making a record process? It seems like a better name
might be `riscv_record_insn_details` or some such, isn't that what it's
doing?
> +{
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> + auto regs_begin = insn.regs_begin ();
> + auto regs_end = insn.regs_end ();
> + if (std::any_of (regs_begin,
> + regs_end,
> + [®cache] (auto &®_it)
> + {
> + return record_full_arch_list_add_reg (regcache, reg_it);
> + }))
> + return -1;
> +
> + auto mems_begin = insn.mems_begin ();
> + auto mems_end = insn.mems_end ();
> + if (std::any_of (mems_begin,
> + mems_end,
> + [] (auto &&mem_it)
> + {
> + return record_full_arch_list_add_mem (mem_it.first,
> + mem_it.second);
> + }))
> + return -1;
> +
> + switch (insn.get_record_type ())
> + {
> + case riscv_recorded_insn::record_type::ORDINARY:
> + break;
> +
> + case riscv_recorded_insn::record_type::ECALL:
> + {
> + if (!tdep->riscv_syscall_record)
> + {
> + warning (_ ("Syscall record is not supported"));
> + return -1;
> + }
> + ULONGEST reg_val = ULONGEST{};
> + if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
> + return -1;
> + return tdep->riscv_syscall_record (regcache, reg_val);
> + }
> +
> + case riscv_recorded_insn::record_type::EBREAK:
> + break;
> +
> + default:
> + return -1;
> + }
> + return 0;
> +}
> +
> +/* Parse the current instruction and record the values of the registers and
> + memory that will be changed in current instruction to record_arch_list.
> + Return -1 if something is wrong. */
> +
> +int
> +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
> + CORE_ADDR addr)
> +{
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + riscv_recorded_insn insn;
> + if (!insn.record (gdbarch, regcache, addr))
> + {
> + record_full_arch_list_add_end ();
> + return -1;
> + }
> +
> + int ret_val = riscv_make_record_process (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 4bdc2e7a3d5..52b2306c35d 100644
> --- a/gdb/riscv-tdep.h
> +++ b/gdb/riscv-tdep.h
> @@ -35,6 +35,10 @@ enum
> RISCV_FP_REGNUM = 8, /* Frame Pointer. */
> RISCV_A0_REGNUM = 10, /* First argument. */
> RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
Is the comment on RISCV_A7_REGNUM correct? I suspect this was a mistake
in the commit e843807b2df9f99b8172bfaf4daa3a42461cdbfa.
> RISCV_PC_REGNUM = 32, /* Program Counter. */
>
> @@ -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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> new file mode 100755
> index 00000000000..e553d380924
> --- /dev/null
> +++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> @@ -0,0 +1,148 @@
> +#!/usr/bin/env python3
> +# pylint: disable=invalid-name
> +
> +# Copyright (C) 2024 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/>.
> +
I know there's a brief help message included with this script, but even
reading that, it's not clear to me how I'd use this script.
But, most importantly, I think you should have a comment here, after the
copyright, but before the code, that explains (ideally) how to use the
script, or, at a minimum, says, "Run the script with the --help option
for usage instructions.", or similar.
And ideally, whatever those instructions are, they should be simple
enough that someone who knows nothing about this corner of GDB can
successfully run the script.
Once again, thank you for working on this. This seems like a great
change, and all the points I've raised are, I think, pretty simple.
Thanks,
Andrew
> +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 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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 410f99e3350..b048ac7078b 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -3718,7 +3718,8 @@ proc supports_reverse {} {
> || [istarget "i\[34567\]86-*-linux*"]
> || [istarget "aarch64*-*-linux*"]
> || [istarget "powerpc*-*-linux*"]
> - || [istarget "s390*-*-linux*"] } {
> + || [istarget "s390*-*-linux*"]
> + || [istarget "riscv*-*-*"] } {
> return 1
> }
>
> --
> 2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* [PATCH v8] This commit adds record full support for rv64gc instruction set
2025-03-13 15:03 ` [PATCH] " Andrew Burgess
@ 2025-03-18 18:49 ` Timur
2025-03-31 17:50 ` [PING][PATCH] " Timur
2025-04-10 9:16 ` [PATCH v8] " Andrew Burgess
0 siblings, 2 replies; 35+ messages in thread
From: Timur @ 2025-03-18 18:49 UTC (permalink / raw)
To: gdb-patches, aburgess; +Cc: guinevere, Timur
It includes changes to the following files:
- gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
syscalls.
- gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
rv64gc instructions.
- gdb/configure.tgt: adds new files for compilation.
- gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
targets.
- gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
function that canonicalizes RISC-V syscall. This script can simplify support
for syscalls on rv32 and rv64 system (currently support only for rv64). To
use this script you need to pass a path to a file with syscalls description
from riscv-glibc (example is in the help message). The script produces a
mapping from syscall names to gdb_syscall enum.
- gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
- gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
- gdb/NEWS: notification of new functionality.
Approved-By: Guinevere Larsen <guinevere@redhat.com>
Hi!
First, big gratitude for review!
>> + {
>> + warning (_ ("Process record and replay target doesn't "
>
> Unfortunately, the '_' function is the one function which breaks the
> space before '(' rule. All uses should be just '_('. There are a
> couple of incorrect uses of '_ (' in the code, but the majority don't
> include the space.
Addressed
>> +/* Initialize the riscv64_linux_record_tdep. */
>> +/* These values are the size of the type that will be used in a system
>> + call. They are obtained from Linux Kernel source. */
>
> There comments should be merged into one please. Or maybe just drop the
> second part? It is repeated in the function body, and, honestly, the
> comment above the function confused me initially, I thought "These
> values..." was referring to the function arguments. In the function
> body it is much clearer what you are referencing.
Addressed
>> +
>> + /* Initialize the riscv_linux_record_tdep. */
>> + /* These values are the size of the type that will be used in a system
>> + call. They are obtained from Linux Kernel source. */
>
> I've not checked any of these values. I figure bugs will be ironed out
> in the future.
I agree with you. I checked the part that was possible by running Linux under
gdb and using the command "print sizeof()"
>> + 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;
>> + riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
>
> This really puzzled me, I even went and checked the ABI documentation.
> But, doesn't the 7th argument go into a6? I have a comment related to
> this on the definition of RISCV_A7_REGNUM.
Addressed. As in RISC-V ecall there can be maximum 6 arguments,
I deleted the seventh.
>> index 00000000000..8e48ab8e1fd
>> --- /dev/null
>> +++ b/gdb/riscv-linux-tdep.h
>> @@ -0,0 +1,29 @@
>> +/* Copyright (C) 2024 Free Software Foundation, Inc.
>
> When you update, remember to update all the dates to 2024-2025.
>> +static bool
>> +try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
>> +{
>> + gdb_assert (regcache != nullptr);
>> +
>> + if (regcache->raw_read<ULONGEST> (regnum, &addr)
>
> Is the type name needed here? This is the only raw_read call in GDB
> that needs the '<ULONGEST>', other uses seem to get by with type
> inference.
Addressed
>> +/* Helper class to record instruction. */
>> +
>> +class riscv_recorded_insn final
>> +{
>> +public:
>> + using regnum_type = int;
>> + using memory_type = std::pair<CORE_ADDR, int>;
>
> Ideally all member types and variables should have a comment. That rule
> is (I think, unfortunately) not strictly enforced, but for
> e.g. memory_type, a comment would be useful. I can guess that the 'int'
> is a length, but it would be nice to have this spelled out.
Addressed
>> + using mem_addr = decltype (std::declval<memory_type> ().first);
>> + using mem_len = decltype (std::declval<memory_type> ().second);
>> +
>> + record_type m_record_type = record_type::UNKNOWN;
>> +
>> + bool m_error_occured = false;
>> +
>> + recorded_regs m_regs;
>> + recorded_mems m_mems;
>
> As with the types, comments on these to indicate their intended usage
> would be great.
Addressed
>> +
>> + int m_xlen = int{};
>
> I know that the '{}' style is recommended in some places, but its use is
> not common in GDB, and (personally) the more explicit 'int m_xlen = 0;'
> seems far clearer. Please could you change the two uses of '{}' to use
> value initialisation.
Addressed
>> +
>> + /* Helper for decode 16-bit instruction RS1. */
>
> Unfortunately, GNU style dictates two spaces after a '.', both at the
> end of a comment, and at the end of a sentence within a comment. I
> understand this is going to be a pain to fix up, but consistent style
> helps make the code base easier to read.
Addressed
>> + bool
>> + save_reg (regnum_type regnum) noexcept
>> + {
>> + try
>> + {
>> + m_regs.emplace_back (regnum);
>> + return true;
>> + }
>> + catch (const std::bad_alloc &ex)
>> + {
>> + warning (_ ("Exception was caught during reverse execution: %s"),
>> + ex.what ());
>> + return set_error ();
>> + }
>
> Maybe I'm wrong, but I'm pretty sure that `warning` also tries to
> allocate memory, so might throw an exception. I think my point is that,
> if trying to allocate such a small memory block triggers an exception,
> then we're pretty much dead anyway.
>
> As such, I wonder if we might as well just drop the try/catch, and rely
> on the noexcept triggering an abort() if an exception is thrown?
>
> I also notice that this appears to be the only place in GDB where we are
> catching std::bad_alloc, which makes me a little wonder if this is
> necessary or not.
Addressed
>> + /* 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);
>
> GNU style is that multi-line return expressions should be wrapped
> with ( ... ), which helps some editors better align the code.
Addressed
>> + return !save_reg (decode_rd (ival))
>> + || !save_reg (RISCV_FIRST_CSR_REGNUM + decode_csr (ival))
>> + || set_ordinary_record_type ();
>> + }
>> +
>> + /* If return value is > 0, instruction needs only saving pc and
>> + memory chunk of appropriate length. */
>
> Can you reword this comment please. You should explain that the return
> value indicates the size of memory block that needs saving. There are a
> couple of places where the comments above a function are a bit vague,
> not really explaining all possible return values, or what happens when
> an error occurs.
Addressed
>> + 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. Needs error checking after. */
>
> What does 'Needs error checking after' mean? I guess it means a call to
> has_error()? I think the comment should say something like 'This
> function can set m_error_occured.' But also, what is the return value
> if an error is set?
Yes, you were right. Error does not influence on the return value. Addressed
>> +
>> + warning (_ ("Currently this instruction with len 4(%lx) is unsupported"),
>> + ival);
>> + return false;
>> + }
>> +
>> + /* Returns true if instruction is successfully recorder.
>> + Else returns false. */
>
> Comments on functions like this should explain the prerequisites,
> e.g. in this case, the instruction must be of length 2. At least, I
> assume that is a prerequisite, based on how the function is used.
Addressed
>> +
>> +/* A helper function to record instruction using record API. */
>> +
>> +static int
>> +riscv_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
>> + const riscv_recorded_insn &insn)
>
> Just a little thing, but this function name doesn't make sense to me.
> Is this really making a record process? It seems like a better name
> might be `riscv_record_insn_details` or some such, isn't that what it's
> doing?
Yes, this is internal function. The idea was that regardless the place where
an error occurred, we will be able to call record_full_arch_list_add_end at the
end of the recording. Without creating confusing blocks of if's. Addressed
>> RISCV_FP_REGNUM = 8, /* Frame Pointer. */
>> RISCV_A0_REGNUM = 10, /* First argument. */
>> RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
>
> Is the comment on RISCV_A7_REGNUM correct? I suspect this was a mistake
> in the commit e843807b2df9f99b8172bfaf4daa3a42461cdbfa.
Changed comment as this register is used to pass number of syscall. Addressed
>> +# 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/>.
>> +
>
> I know there's a brief help message included with this script, but even
> reading that, it's not clear to me how I'd use this script.
>
> But, most importantly, I think you should have a comment here, after the
> copyright, but before the code, that explains (ideally) how to use the
> script, or, at a minimum, says, "Run the script with the --help option
> for usage instructions.", or similar.
Addressed
Thanks for the review again, waiting for your reply.
---
gdb/NEWS | 2 +
gdb/configure.tgt | 5 +-
gdb/doc/gdb.texinfo | 2 +-
gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
gdb/riscv-linux-tdep.c | 258 ++++++-
gdb/riscv-linux-tdep.h | 29 +
gdb/riscv-tdep.c | 688 +++++++++++++++++-
gdb/riscv-tdep.h | 16 +-
.../riscv-canonicalize-syscall-gen.py | 163 +++++
gdb/testsuite/lib/gdb.exp | 3 +-
10 files changed, 1496 insertions(+), 12 deletions(-)
create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
create mode 100644 gdb/riscv-linux-tdep.h
create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
diff --git a/gdb/NEWS b/gdb/NEWS
index 964183910dc..20fdb571047 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -9581,3 +9581,5 @@ GDB now handles cross debugging. If you are remotely debugging between
two different machines, type ``./configure host -target=targ''.
Host is the machine where GDB will run; targ is the machine
where the program that you are debugging will run.
+
+ * Add record full support for rv64gc architectures
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 62df71b13fa..0cd5c509695 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -545,8 +545,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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 99720f1206e..d5b989d681e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7759,7 +7759,7 @@ 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,
-Moxie, PowerPC, PowerPC64, S/390, and x86 (i386/amd64) running
+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}.
diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
new file mode 100644
index 00000000000..3749fc32f33
--- /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 ff478cf4c28..f21039a613c 100644
--- a/gdb/riscv-linux-tdep.c
+++ b/gdb/riscv-linux-tdep.c
@@ -1,5 +1,5 @@
/* Target-dependent code for GNU/Linux on RISC-V processors.
- Copyright (C) 2018-2024 Free Software Foundation, Inc.
+ Copyright (C) 2018-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -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 00000000000..0f481b168d1
--- /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 932708ca4e9..cf1023a70c2 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -1,6 +1,6 @@
/* Target-dependent code for the RISC-V architecture, for GDB.
- Copyright (C) 2018-2024 Free Software Foundation, Inc.
+ Copyright (C) 2018-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -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
@@ -1655,6 +1658,11 @@ class riscv_insn
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. */
@@ -1800,11 +1808,6 @@ class riscv_insn
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;
@@ -4415,6 +4418,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);
@@ -4833,3 +4839,673 @@ this option can be used."),
&setriscvcmdlist,
&showriscvcmdlist);
}
+
+/* A wrapper to read register under number regnum to address addr.
+ Returns false if error happened and makes warning. */
+
+static bool
+try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
+{
+ gdb_assert (regcache != nullptr);
+
+ if (regcache->raw_read (regnum, &addr)
+ != register_status::REG_VALID)
+ {
+ warning (_("Can not read at address %lx"), addr);
+ return false;
+ }
+ return true;
+}
+
+/* Helper class to record instruction. */
+
+class riscv_recorded_insn final
+{
+public:
+ /* Type for saved register. */
+ using regnum_type = int;
+ /* Type for saved memory. First is address, second is length. */
+ using memory_type = std::pair<CORE_ADDR, int>;
+
+ /* Enum class that represents which type does recording belong to. */
+ enum class record_type
+ {
+ UNKNOWN,
+ ORDINARY,
+
+ /* Corner cases. */
+ ECALL,
+ EBREAK,
+ };
+
+private:
+ /* Type for set of registers that need to be saved. */
+ using recorded_regs = std::vector<regnum_type>;
+ /* Type for set of memory records that need to be saved. */
+ using recorded_mems = std::vector<memory_type>;
+
+ /* Type for memory address, extracted from memory_type. */
+ using mem_addr = decltype (std::declval<memory_type> ().first);
+ /* Type for memory length, extracted from memory_type. */
+ using mem_len = decltype (std::declval<memory_type> ().second);
+
+ /* Record type of current instruction. */
+ record_type m_record_type = record_type::UNKNOWN;
+
+ /* Flag that represents was there an error in current recording. */
+ bool m_error_occured = false;
+
+ /* Set of registers that need to be recorded. */
+ recorded_regs m_regs;
+ /* Set of memory chunks that need to be recorded. */
+ recorded_mems m_mems;
+
+ /* Width in bytes of the general purpose registers for GDBARCH,
+ where recording is happening. */
+ int m_xlen = 0;
+
+ /* Helper for decode 16-bit instruction RS1. */
+ static regnum_type
+ decode_crs1_short (ULONGEST opcode) noexcept
+ {
+ return ((opcode >> OP_SH_CRS1S) & OP_MASK_CRS1S) + 8;
+ }
+
+ /* Helper for decode 16-bit instruction RS2. */
+ static regnum_type
+ decode_crs2_short (ULONGEST opcode) noexcept
+ {
+ return ((opcode >> OP_SH_CRS2S) & OP_MASK_CRS2S) + 8;
+ }
+
+ /* Helper for decode 16-bit instruction CRS1. */
+ static regnum_type
+ decode_crs1 (ULONGEST opcode) noexcept
+ {
+ return ((opcode >> OP_SH_RD) & OP_MASK_RD);
+ }
+
+ /* Helper for decode 16-bit instruction CRS2. */
+ static regnum_type
+ decode_crs2 (ULONGEST opcode) noexcept
+ {
+ return ((opcode >> OP_SH_CRS2) & OP_MASK_CRS2);
+ }
+
+ /* Helper for decode 32-bit instruction RD. */
+ static regnum_type
+ decode_rd (ULONGEST ival) noexcept
+ {
+ return (ival >> OP_SH_RD) & OP_MASK_RD;
+ }
+
+ /* Helper for decode 32-bit instruction RS1. */
+ static regnum_type
+ decode_rs1 (ULONGEST ival) noexcept
+ {
+ return (ival >> OP_SH_RS1) & OP_MASK_RS1;
+ }
+
+ /* Helper for decode 32-bit instruction RS2. */
+ static regnum_type
+ decode_rs2 (ULONGEST ival) noexcept
+ {
+ return (ival >> OP_SH_RS2) & OP_MASK_RS2;
+ }
+
+ /* Helper for decode 32-bit instruction CSR. */
+ static regnum_type
+ decode_csr (ULONGEST ival) noexcept
+ {
+ return (ival >> OP_SH_CSR) & OP_MASK_CSR;
+ }
+
+ /* Set ordinary record type. Always returns true. */
+ bool
+ set_ordinary_record_type () noexcept
+ {
+ m_record_type = record_type::ORDINARY;
+ return true;
+ }
+
+ /* Set error happened. Always returns false. */
+ bool
+ set_error () noexcept
+ {
+ m_error_occured = true;
+ return false;
+ }
+
+ /* Check if current recording has an error. */
+ bool
+ has_error () const noexcept
+ {
+ return m_error_occured;
+ }
+
+ /* Reads register. Sets error and returns false if error happened. */
+ bool
+ read_reg (struct regcache *regcache, regnum_type reg,
+ ULONGEST &addr) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ if (!try_read (regcache, reg, addr))
+ return set_error ();
+ return true;
+ }
+
+ /* Save register. Returns true or aborts if exception happened. */
+ bool
+ save_reg (regnum_type regnum) noexcept
+ {
+ m_regs.emplace_back (regnum);
+ return true;
+ }
+
+ /* Save memory chunk. Returns true or aborts if exception happened. */
+ bool
+ save_mem (mem_addr addr, mem_len len) noexcept
+ {
+ m_mems.emplace_back (addr, len);
+ return true;
+ }
+
+ /* Returns true if instruction needs only saving pc. */
+ static bool
+ need_save_pc (ULONGEST ival) noexcept
+ {
+ return (is_beq_insn (ival) || is_bne_insn (ival) || is_blt_insn (ival)
+ || is_bge_insn (ival) || is_bltu_insn (ival) || is_bgeu_insn (ival)
+ || is_fence_insn (ival) || is_pause_insn (ival)
+ || is_fence_i_insn (ival));
+ }
+
+ /* Returns true if instruction is classified. */
+ bool
+ try_save_pc (ULONGEST ival) noexcept
+ {
+ if (!need_save_pc (ival))
+ return false;
+
+ return set_ordinary_record_type ();
+ }
+
+ /* Returns true if instruction needs only saving pc and rd. */
+ static bool
+ need_save_pc_rd (ULONGEST ival) noexcept
+ {
+ return (is_lui_insn (ival) || is_auipc_insn (ival) || is_jal_insn (ival)
+ || is_jalr_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
+ || is_lw_insn (ival) || is_lbu_insn (ival) || is_lhu_insn (ival)
+ || is_addi_insn (ival) || is_slti_insn (ival)
+ || is_sltiu_insn (ival) || is_xori_insn (ival) || is_ori_insn (ival)
+ || is_andi_insn (ival) || is_slli_rv32_insn (ival)
+ || is_srli_rv32_insn (ival) || is_srai_rv32_insn (ival)
+ || is_add_insn (ival) || is_sub_insn (ival) || is_sll_insn (ival)
+ || is_slt_insn (ival) || is_sltu_insn (ival) || is_xor_insn (ival)
+ || is_srl_insn (ival) || is_sra_insn (ival) || is_or_insn (ival)
+ || is_and_insn (ival) || is_lwu_insn (ival) || is_ld_insn (ival)
+ || is_slli_insn (ival) || is_srli_insn (ival) || is_srai_insn (ival)
+ || is_addiw_insn (ival) || is_slliw_insn (ival)
+ || is_srliw_insn (ival) || is_sraiw_insn (ival)
+ || is_addw_insn (ival) || is_subw_insn (ival) || is_sllw_insn (ival)
+ || is_srlw_insn (ival) || is_sraw_insn (ival) || is_mul_insn (ival)
+ || is_mulh_insn (ival) || is_mulhsu_insn (ival)
+ || is_mulhu_insn (ival) || is_div_insn (ival) || is_divu_insn (ival)
+ || is_rem_insn (ival) || is_remu_insn (ival) || is_mulw_insn (ival)
+ || is_divw_insn (ival) || is_divuw_insn (ival)
+ || is_remw_insn (ival) || is_remuw_insn (ival)
+ || is_lr_w_insn (ival) || is_lr_d_insn (ival)
+ || is_fcvt_w_s_insn (ival) || is_fcvt_wu_s_insn (ival)
+ || is_fmv_x_s_insn (ival) || is_feq_s_insn (ival)
+ || is_flt_s_insn (ival) || is_fle_s_insn (ival)
+ || is_fclass_s_insn (ival) || is_fcvt_l_s_insn (ival)
+ || is_fcvt_lu_s_insn (ival) || is_feq_d_insn (ival)
+ || is_flt_d_insn (ival) || is_fle_d_insn (ival)
+ || is_fclass_d_insn (ival) || is_fcvt_w_d_insn (ival)
+ || is_fcvt_wu_d_insn (ival) || is_fcvt_l_d_insn (ival)
+ || is_fcvt_lu_d_insn (ival) || is_fmv_x_d_insn (ival));
+ }
+
+ /* Returns true if instruction is classified. This function can set
+ m_error_occured. */
+ bool
+ try_save_pc_rd (ULONGEST ival) noexcept
+ {
+ if (!need_save_pc_rd (ival))
+ return false;
+
+ return (!save_reg (decode_rd (ival)) || set_ordinary_record_type ());
+ }
+
+ /* Returns true if instruction needs only saving pc and
+ floating point rd. */
+ static bool
+ need_save_pc_fprd (ULONGEST ival) noexcept
+ {
+ return (is_flw_insn (ival) || is_fmadd_s_insn (ival)
+ || is_fmsub_s_insn (ival) || is_fnmsub_s_insn (ival)
+ || is_fnmadd_s_insn (ival) || is_fadd_s_insn (ival)
+ || is_fsub_s_insn (ival) || is_fmul_s_insn (ival)
+ || is_fdiv_s_insn (ival) || is_fsqrt_s_insn (ival)
+ || is_fsgnj_s_insn (ival) || is_fsgnjn_s_insn (ival)
+ || is_fsgnjx_s_insn (ival) || is_fmin_s_insn (ival)
+ || is_fmax_s_insn (ival) || is_fcvt_s_w_insn (ival)
+ || is_fcvt_s_wu_insn (ival) || is_fmv_s_x_insn (ival)
+ || is_fcvt_s_l_insn (ival) || is_fcvt_s_lu_insn (ival)
+ || is_fld_insn (ival) || is_fmadd_d_insn (ival)
+ || is_fmsub_d_insn (ival) || is_fnmsub_d_insn (ival)
+ || is_fnmadd_d_insn (ival) || is_fadd_d_insn (ival)
+ || is_fsub_d_insn (ival) || is_fmul_d_insn (ival)
+ || is_fdiv_d_insn (ival) || is_fsqrt_d_insn (ival)
+ || is_fsgnj_d_insn (ival) || is_fsgnjn_d_insn (ival)
+ || is_fsgnjx_d_insn (ival) || is_fmin_d_insn (ival)
+ || is_fmax_d_insn (ival) || is_fcvt_s_d_insn (ival)
+ || is_fcvt_d_s_insn (ival) || is_fcvt_d_w_insn (ival)
+ || is_fcvt_d_wu_insn (ival) || is_fcvt_d_l_insn (ival)
+ || is_fcvt_d_lu_insn (ival) || is_fmv_d_x_insn (ival));
+ }
+
+ /* Returns true if instruction is classified. This function can set
+ m_error_occured. */
+ bool
+ try_save_pc_fprd (ULONGEST ival) noexcept
+ {
+ if (!need_save_pc_fprd (ival))
+ return false;
+
+ return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_rd (ival))
+ || set_ordinary_record_type ());
+ }
+
+ /* Returns true if instruction needs only saving pc, rd and csr. */
+ static bool
+ need_save_pc_rd_csr (ULONGEST ival) noexcept
+ {
+ return (is_csrrw_insn (ival) || is_csrrs_insn (ival) || is_csrrc_insn (ival)
+ || is_csrrwi_insn (ival) || is_csrrsi_insn (ival)
+ || is_csrrc_insn (ival));
+ }
+
+ /* Returns true if instruction is classified. This function can set
+ m_error_occured. */
+ bool
+ try_save_pc_rd_csr (ULONGEST ival) noexcept
+ {
+ if (!need_save_pc_rd_csr (ival))
+ return false;
+
+ return (!save_reg (decode_rd (ival))
+ || !save_reg (RISCV_FIRST_CSR_REGNUM + decode_csr (ival))
+ || set_ordinary_record_type ());
+ }
+
+ /* Returns the size of the memory chunk that needs to be saved if the
+ instruction belongs to the group that needs only saving pc and memory.
+ Otherwise returns 0. */
+ static mem_len
+ need_save_pc_mem (ULONGEST ival) noexcept
+ {
+ if (is_sb_insn (ival))
+ return 1;
+ if (is_sh_insn (ival))
+ return 2;
+ if (is_sw_insn (ival) || is_fsw_insn (ival))
+ return 4;
+ if (is_sd_insn (ival) || is_fsd_insn (ival))
+ return 8;
+ return 0;
+ }
+
+ /* Returns true if instruction is classified. This function can set
+ m_error_occured. */
+ bool
+ try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ mem_addr addr = mem_addr{};
+ mem_len len = need_save_pc_mem (ival);
+ if (len <= 0)
+ return false;
+
+ mem_len offset = EXTRACT_STYPE_IMM (ival);
+ return (!read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr + offset, len) || set_ordinary_record_type ());
+ }
+
+ /* Returns the size of the memory chunk that needs to be saved if the
+ instruction belongs to the group that needs only saving pc, rd and memory.
+ Otherwise returns 0. */
+ static mem_len
+ need_save_pc_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_sc_w_insn (ival) || is_amoswap_w_insn (ival)
+ || is_amoadd_w_insn (ival) || is_amoxor_w_insn (ival)
+ || is_amoand_w_insn (ival) || is_amoor_w_insn (ival)
+ || is_amomin_w_insn (ival) || is_amomax_w_insn (ival)
+ || is_amominu_w_insn (ival) || is_amomaxu_w_insn (ival))
+ return 4;
+ if (is_sc_d_insn (ival) || is_amoswap_d_insn (ival)
+ || is_amoadd_d_insn (ival) || is_amoxor_d_insn (ival)
+ || is_amoand_d_insn (ival) || is_amoor_d_insn (ival)
+ || is_amomin_d_insn (ival) || is_amomax_d_insn (ival)
+ || is_amominu_d_insn (ival) || is_amomaxu_d_insn (ival))
+ return 8;
+ return 0;
+ }
+
+ /* Returns true if instruction is classified. This function can set
+ m_error_occured. */
+ bool
+ try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ mem_len len = need_save_pc_rd_mem (ival);
+ mem_addr addr = 0;
+ if (len <= 0)
+ return false;
+
+ return (!read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr, len) || !save_reg (decode_rd (ival))
+ || set_ordinary_record_type ());
+ }
+
+ /* Returns true if instruction is successfully recordered. The length of
+ the instruction must be equal 4 bytes. */
+ bool
+ record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ if (is_ecall_insn (ival))
+ {
+ m_record_type = record_type::ECALL;
+ return true;
+ }
+
+ if (is_ebreak_insn (ival))
+ {
+ m_record_type = record_type::EBREAK;
+ return true;
+ }
+
+ if (try_save_pc (ival) || try_save_pc_rd (ival) || try_save_pc_fprd (ival)
+ || try_save_pc_rd_csr (ival) || try_save_pc_mem (ival, regcache)
+ || try_save_pc_rd_mem (ival, regcache))
+ return !has_error ();
+
+ warning (_("Currently this instruction with len 4(%lx) is unsupported"),
+ ival);
+ return false;
+ }
+
+ /* Returns true if instruction is successfully recordered. The length of
+ the instruction must be equal 2 bytes. */
+ bool
+ record_insn_len2 (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ mem_addr addr = mem_addr{};
+
+ /* The order here is very important, because
+ opcodes of some instructions may be the same. */
+
+ if (is_c_addi4spn_insn (ival) || is_c_lw_insn (ival)
+ || (m_xlen == 8 && is_c_ld_insn (ival)))
+ return (!save_reg (decode_crs2_short (ival))
+ || set_ordinary_record_type ());
+
+ if (is_c_fld_insn (ival) || (m_xlen == 4 && is_c_flw_insn (ival)))
+ return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival))
+ || set_ordinary_record_type ());
+
+ if (is_c_fsd_insn (ival) || (m_xlen == 8 && is_c_sd_insn (ival)))
+ {
+ ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LD_IMM (ival)};
+ return (!read_reg (regcache, decode_crs1_short (ival), addr)
+ || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
+ }
+
+ if ((m_xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
+ {
+ ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LW_IMM (ival)};
+ return (!read_reg (regcache, decode_crs1_short (ival), addr)
+ || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
+ }
+
+ if (is_c_nop_insn (ival))
+ return set_ordinary_record_type ();
+
+ if (is_c_addi_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (m_xlen == 4 && is_c_jal_insn (ival))
+ return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
+
+ if ((m_xlen == 8 && is_c_addiw_insn (ival)) || is_c_li_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_addi16sp_insn (ival))
+ return (!save_reg (RISCV_SP_REGNUM) || set_ordinary_record_type ());
+
+ if (is_c_lui_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_srli_insn (ival) || is_c_srai_insn (ival) || is_c_andi_insn (ival)
+ || is_c_sub_insn (ival) || is_c_xor_insn (ival) || is_c_or_insn (ival)
+ || is_c_and_insn (ival) || (m_xlen == 8 && is_c_subw_insn (ival))
+ || (m_xlen == 8 && is_c_addw_insn (ival)))
+ return (!save_reg (decode_crs1_short (ival))
+ || set_ordinary_record_type ());
+
+ if (is_c_j_insn (ival) || is_c_beqz_insn (ival) || is_c_bnez_insn (ival))
+ return set_ordinary_record_type ();
+
+ if (is_c_slli_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_fldsp_insn (ival) || (m_xlen == 4 && is_c_flwsp_insn (ival)))
+ return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival))
+ || set_ordinary_record_type ());
+
+ if (is_c_lwsp_insn (ival) || (m_xlen == 8 && is_c_ldsp_insn (ival)))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_jr_insn (ival))
+ return set_ordinary_record_type ();
+
+ if (is_c_mv_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_ebreak_insn (ival))
+ {
+ m_record_type = record_type::EBREAK;
+ return true;
+ }
+
+ if (is_c_jalr_insn (ival))
+ return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
+
+ if (is_c_add_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_fsdsp_insn (ival) || (m_xlen == 8 && is_c_sdsp_insn (ival)))
+ {
+ ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SDSP_IMM (ival)};
+ return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
+ || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
+ }
+
+ if (is_c_swsp_insn (ival) || (m_xlen == 4 && is_c_fswsp_insn (ival)))
+ {
+ ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SWSP_IMM (ival)};
+ return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
+ || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
+ }
+
+ warning (_("Currently this instruction with len 2(%lx) is unsupported"),
+ ival);
+ return false;
+ }
+
+public:
+ /* Iterator for registers that need to be recorded. */
+ using regs_iter = recorded_regs::const_iterator;
+ /* Iterator for memory chunks that need to be recorded. */
+ using mems_iter = recorded_mems::const_iterator;
+
+ /* Record instruction at address addr. Returns false if error happened. */
+ bool
+ record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
+ {
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ int m_length = 0;
+ m_xlen = riscv_isa_xlen (gdbarch);
+ ULONGEST ival = riscv_insn::fetch_instruction (gdbarch, addr, &m_length);
+ if (!save_reg (RISCV_PC_REGNUM))
+ return false;
+
+ if (m_length == 4)
+ return record_insn_len4 (ival, regcache);
+
+ if (m_length == 2)
+ return record_insn_len2 (ival, regcache);
+
+ /* 6 bytes or more. If the instruction is longer than 8 bytes, we don't
+ have full instruction bits in ival. At least, such long instructions
+ are not defined yet, so just ignore it. */
+ gdb_assert (m_length > 0 && m_length % 2 == 0);
+
+ warning (_("Can not record unknown instruction (opcode = %lx)"), ival);
+ return false;
+ }
+
+ /* Get record type of instruction. */
+ record_type
+ get_record_type () const noexcept
+ {
+ return m_record_type;
+ }
+
+ /* Returns an iterator to the beginning of the registers that need
+ to be saved. */
+ regs_iter
+ regs_begin () const noexcept
+ {
+ return m_regs.begin ();
+ }
+
+ /* Returns an iterator to the end of the registers that need
+ to be saved. */
+ regs_iter
+ regs_end () const noexcept
+ {
+ return m_regs.end ();
+ }
+
+ /* Returns an iterator to the beginning of the memory chunks that need
+ to be saved. */
+ mems_iter
+ mems_begin () const noexcept
+ {
+ return m_mems.begin ();
+ }
+
+ /* Returns an iterator to the end of the memory chunks that need
+ to be saved. */
+ mems_iter
+ mems_end () const noexcept
+ {
+ return m_mems.end ();
+ }
+};
+
+/* A helper function to record instruction using record API. */
+
+static int
+riscv_record_insn_details (struct gdbarch *gdbarch, struct regcache *regcache,
+ const riscv_recorded_insn &insn)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto regs_begin = insn.regs_begin ();
+ auto regs_end = insn.regs_end ();
+ if (std::any_of (regs_begin,
+ regs_end,
+ [®cache] (auto &®_it)
+ {
+ return record_full_arch_list_add_reg (regcache, reg_it);
+ }))
+ return -1;
+
+ auto mems_begin = insn.mems_begin ();
+ auto mems_end = insn.mems_end ();
+ if (std::any_of (mems_begin,
+ mems_end,
+ [] (auto &&mem_it)
+ {
+ return record_full_arch_list_add_mem (mem_it.first,
+ mem_it.second);
+ }))
+ return -1;
+
+ switch (insn.get_record_type ())
+ {
+ case riscv_recorded_insn::record_type::ORDINARY:
+ break;
+
+ case riscv_recorded_insn::record_type::ECALL:
+ {
+ if (!tdep->riscv_syscall_record)
+ {
+ warning (_("Syscall record is not supported"));
+ return -1;
+ }
+ ULONGEST reg_val = ULONGEST{};
+ if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
+ return -1;
+ return tdep->riscv_syscall_record (regcache, reg_val);
+ }
+
+ case riscv_recorded_insn::record_type::EBREAK:
+ break;
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* Parse the current instruction and record the values of the registers and
+ memory that will be changed in current instruction to record_arch_list.
+ Return -1 if something is wrong. */
+
+int
+riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+ CORE_ADDR addr)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_recorded_insn insn;
+ if (!insn.record (gdbarch, regcache, addr))
+ {
+ record_full_arch_list_add_end ();
+ return -1;
+ }
+
+ int ret_val = riscv_record_insn_details (gdbarch, regcache, insn);
+
+ if (record_full_arch_list_add_end ())
+ return -1;
+
+ return ret_val;
+}
diff --git a/gdb/riscv-tdep.h b/gdb/riscv-tdep.h
index 4bdc2e7a3d5..726f197af02 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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
new file mode 100755
index 00000000000..aa07ac13145
--- /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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 410f99e3350..b048ac7078b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
- || [istarget "s390*-*-linux*"] } {
+ || [istarget "s390*-*-linux*"]
+ || [istarget "riscv*-*-*"] } {
return 1
}
--
2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* [PING][PATCH] This commit adds record full support for rv64gc instruction set
2025-03-18 18:49 ` [PATCH v8] " Timur
@ 2025-03-31 17:50 ` Timur
2025-04-08 12:33 ` Timur Golubovich
2025-04-10 9:16 ` [PATCH v8] " Andrew Burgess
1 sibling, 1 reply; 35+ messages in thread
From: Timur @ 2025-03-31 17:50 UTC (permalink / raw)
To: gdb-patches; +Cc: guinevere, aburgess, Timur
It includes changes to the following files:
- gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
syscalls.
- gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
rv64gc instructions.
- gdb/configure.tgt: adds new files for compilation.
- gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
targets.
- gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
function that canonicalizes RISC-V syscall. This script can simplify support
for syscalls on rv32 and rv64 system (currently support only for rv64). To
use this script you need to pass a path to a file with syscalls description
from riscv-glibc (example is in the help message). The script produces a
mapping from syscall names to gdb_syscall enum.
- gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
- gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
- gdb/NEWS: notification of new functionality.
Approved-By: Guinevere Larsen <guinevere@redhat.com>
Hi! It's been some time since I last received some feedback from you, can you
please take a look once again? Is there something I can do to help push
these changes forward?
---
gdb/NEWS | 2 +
gdb/configure.tgt | 5 +-
gdb/doc/gdb.texinfo | 2 +-
gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
gdb/riscv-linux-tdep.c | 258 ++++++-
gdb/riscv-linux-tdep.h | 29 +
gdb/riscv-tdep.c | 688 +++++++++++++++++-
gdb/riscv-tdep.h | 16 +-
.../riscv-canonicalize-syscall-gen.py | 163 +++++
gdb/testsuite/lib/gdb.exp | 3 +-
10 files changed, 1496 insertions(+), 12 deletions(-)
create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
create mode 100644 gdb/riscv-linux-tdep.h
create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
diff --git a/gdb/NEWS b/gdb/NEWS
index 964183910dc..20fdb571047 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -9581,3 +9581,5 @@ GDB now handles cross debugging. If you are remotely debugging between
two different machines, type ``./configure host -target=targ''.
Host is the machine where GDB will run; targ is the machine
where the program that you are debugging will run.
+
+ * Add record full support for rv64gc architectures
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 62df71b13fa..0cd5c509695 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -545,8 +545,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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 99720f1206e..d5b989d681e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7759,7 +7759,7 @@ 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,
-Moxie, PowerPC, PowerPC64, S/390, and x86 (i386/amd64) running
+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}.
diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
new file mode 100644
index 00000000000..3749fc32f33
--- /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 ff478cf4c28..f21039a613c 100644
--- a/gdb/riscv-linux-tdep.c
+++ b/gdb/riscv-linux-tdep.c
@@ -1,5 +1,5 @@
/* Target-dependent code for GNU/Linux on RISC-V processors.
- Copyright (C) 2018-2024 Free Software Foundation, Inc.
+ Copyright (C) 2018-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -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 00000000000..0f481b168d1
--- /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 932708ca4e9..cf1023a70c2 100644
--- a/gdb/riscv-tdep.c
+++ b/gdb/riscv-tdep.c
@@ -1,6 +1,6 @@
/* Target-dependent code for the RISC-V architecture, for GDB.
- Copyright (C) 2018-2024 Free Software Foundation, Inc.
+ Copyright (C) 2018-2025 Free Software Foundation, Inc.
This file is part of GDB.
@@ -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
@@ -1655,6 +1658,11 @@ class riscv_insn
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. */
@@ -1800,11 +1808,6 @@ class riscv_insn
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;
@@ -4415,6 +4418,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);
@@ -4833,3 +4839,673 @@ this option can be used."),
&setriscvcmdlist,
&showriscvcmdlist);
}
+
+/* A wrapper to read register under number regnum to address addr.
+ Returns false if error happened and makes warning. */
+
+static bool
+try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
+{
+ gdb_assert (regcache != nullptr);
+
+ if (regcache->raw_read (regnum, &addr)
+ != register_status::REG_VALID)
+ {
+ warning (_("Can not read at address %lx"), addr);
+ return false;
+ }
+ return true;
+}
+
+/* Helper class to record instruction. */
+
+class riscv_recorded_insn final
+{
+public:
+ /* Type for saved register. */
+ using regnum_type = int;
+ /* Type for saved memory. First is address, second is length. */
+ using memory_type = std::pair<CORE_ADDR, int>;
+
+ /* Enum class that represents which type does recording belong to. */
+ enum class record_type
+ {
+ UNKNOWN,
+ ORDINARY,
+
+ /* Corner cases. */
+ ECALL,
+ EBREAK,
+ };
+
+private:
+ /* Type for set of registers that need to be saved. */
+ using recorded_regs = std::vector<regnum_type>;
+ /* Type for set of memory records that need to be saved. */
+ using recorded_mems = std::vector<memory_type>;
+
+ /* Type for memory address, extracted from memory_type. */
+ using mem_addr = decltype (std::declval<memory_type> ().first);
+ /* Type for memory length, extracted from memory_type. */
+ using mem_len = decltype (std::declval<memory_type> ().second);
+
+ /* Record type of current instruction. */
+ record_type m_record_type = record_type::UNKNOWN;
+
+ /* Flag that represents was there an error in current recording. */
+ bool m_error_occured = false;
+
+ /* Set of registers that need to be recorded. */
+ recorded_regs m_regs;
+ /* Set of memory chunks that need to be recorded. */
+ recorded_mems m_mems;
+
+ /* Width in bytes of the general purpose registers for GDBARCH,
+ where recording is happening. */
+ int m_xlen = 0;
+
+ /* Helper for decode 16-bit instruction RS1. */
+ static regnum_type
+ decode_crs1_short (ULONGEST opcode) noexcept
+ {
+ return ((opcode >> OP_SH_CRS1S) & OP_MASK_CRS1S) + 8;
+ }
+
+ /* Helper for decode 16-bit instruction RS2. */
+ static regnum_type
+ decode_crs2_short (ULONGEST opcode) noexcept
+ {
+ return ((opcode >> OP_SH_CRS2S) & OP_MASK_CRS2S) + 8;
+ }
+
+ /* Helper for decode 16-bit instruction CRS1. */
+ static regnum_type
+ decode_crs1 (ULONGEST opcode) noexcept
+ {
+ return ((opcode >> OP_SH_RD) & OP_MASK_RD);
+ }
+
+ /* Helper for decode 16-bit instruction CRS2. */
+ static regnum_type
+ decode_crs2 (ULONGEST opcode) noexcept
+ {
+ return ((opcode >> OP_SH_CRS2) & OP_MASK_CRS2);
+ }
+
+ /* Helper for decode 32-bit instruction RD. */
+ static regnum_type
+ decode_rd (ULONGEST ival) noexcept
+ {
+ return (ival >> OP_SH_RD) & OP_MASK_RD;
+ }
+
+ /* Helper for decode 32-bit instruction RS1. */
+ static regnum_type
+ decode_rs1 (ULONGEST ival) noexcept
+ {
+ return (ival >> OP_SH_RS1) & OP_MASK_RS1;
+ }
+
+ /* Helper for decode 32-bit instruction RS2. */
+ static regnum_type
+ decode_rs2 (ULONGEST ival) noexcept
+ {
+ return (ival >> OP_SH_RS2) & OP_MASK_RS2;
+ }
+
+ /* Helper for decode 32-bit instruction CSR. */
+ static regnum_type
+ decode_csr (ULONGEST ival) noexcept
+ {
+ return (ival >> OP_SH_CSR) & OP_MASK_CSR;
+ }
+
+ /* Set ordinary record type. Always returns true. */
+ bool
+ set_ordinary_record_type () noexcept
+ {
+ m_record_type = record_type::ORDINARY;
+ return true;
+ }
+
+ /* Set error happened. Always returns false. */
+ bool
+ set_error () noexcept
+ {
+ m_error_occured = true;
+ return false;
+ }
+
+ /* Check if current recording has an error. */
+ bool
+ has_error () const noexcept
+ {
+ return m_error_occured;
+ }
+
+ /* Reads register. Sets error and returns false if error happened. */
+ bool
+ read_reg (struct regcache *regcache, regnum_type reg,
+ ULONGEST &addr) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ if (!try_read (regcache, reg, addr))
+ return set_error ();
+ return true;
+ }
+
+ /* Save register. Returns true or aborts if exception happened. */
+ bool
+ save_reg (regnum_type regnum) noexcept
+ {
+ m_regs.emplace_back (regnum);
+ return true;
+ }
+
+ /* Save memory chunk. Returns true or aborts if exception happened. */
+ bool
+ save_mem (mem_addr addr, mem_len len) noexcept
+ {
+ m_mems.emplace_back (addr, len);
+ return true;
+ }
+
+ /* Returns true if instruction needs only saving pc. */
+ static bool
+ need_save_pc (ULONGEST ival) noexcept
+ {
+ return (is_beq_insn (ival) || is_bne_insn (ival) || is_blt_insn (ival)
+ || is_bge_insn (ival) || is_bltu_insn (ival) || is_bgeu_insn (ival)
+ || is_fence_insn (ival) || is_pause_insn (ival)
+ || is_fence_i_insn (ival));
+ }
+
+ /* Returns true if instruction is classified. */
+ bool
+ try_save_pc (ULONGEST ival) noexcept
+ {
+ if (!need_save_pc (ival))
+ return false;
+
+ return set_ordinary_record_type ();
+ }
+
+ /* Returns true if instruction needs only saving pc and rd. */
+ static bool
+ need_save_pc_rd (ULONGEST ival) noexcept
+ {
+ return (is_lui_insn (ival) || is_auipc_insn (ival) || is_jal_insn (ival)
+ || is_jalr_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
+ || is_lw_insn (ival) || is_lbu_insn (ival) || is_lhu_insn (ival)
+ || is_addi_insn (ival) || is_slti_insn (ival)
+ || is_sltiu_insn (ival) || is_xori_insn (ival) || is_ori_insn (ival)
+ || is_andi_insn (ival) || is_slli_rv32_insn (ival)
+ || is_srli_rv32_insn (ival) || is_srai_rv32_insn (ival)
+ || is_add_insn (ival) || is_sub_insn (ival) || is_sll_insn (ival)
+ || is_slt_insn (ival) || is_sltu_insn (ival) || is_xor_insn (ival)
+ || is_srl_insn (ival) || is_sra_insn (ival) || is_or_insn (ival)
+ || is_and_insn (ival) || is_lwu_insn (ival) || is_ld_insn (ival)
+ || is_slli_insn (ival) || is_srli_insn (ival) || is_srai_insn (ival)
+ || is_addiw_insn (ival) || is_slliw_insn (ival)
+ || is_srliw_insn (ival) || is_sraiw_insn (ival)
+ || is_addw_insn (ival) || is_subw_insn (ival) || is_sllw_insn (ival)
+ || is_srlw_insn (ival) || is_sraw_insn (ival) || is_mul_insn (ival)
+ || is_mulh_insn (ival) || is_mulhsu_insn (ival)
+ || is_mulhu_insn (ival) || is_div_insn (ival) || is_divu_insn (ival)
+ || is_rem_insn (ival) || is_remu_insn (ival) || is_mulw_insn (ival)
+ || is_divw_insn (ival) || is_divuw_insn (ival)
+ || is_remw_insn (ival) || is_remuw_insn (ival)
+ || is_lr_w_insn (ival) || is_lr_d_insn (ival)
+ || is_fcvt_w_s_insn (ival) || is_fcvt_wu_s_insn (ival)
+ || is_fmv_x_s_insn (ival) || is_feq_s_insn (ival)
+ || is_flt_s_insn (ival) || is_fle_s_insn (ival)
+ || is_fclass_s_insn (ival) || is_fcvt_l_s_insn (ival)
+ || is_fcvt_lu_s_insn (ival) || is_feq_d_insn (ival)
+ || is_flt_d_insn (ival) || is_fle_d_insn (ival)
+ || is_fclass_d_insn (ival) || is_fcvt_w_d_insn (ival)
+ || is_fcvt_wu_d_insn (ival) || is_fcvt_l_d_insn (ival)
+ || is_fcvt_lu_d_insn (ival) || is_fmv_x_d_insn (ival));
+ }
+
+ /* Returns true if instruction is classified. This function can set
+ m_error_occured. */
+ bool
+ try_save_pc_rd (ULONGEST ival) noexcept
+ {
+ if (!need_save_pc_rd (ival))
+ return false;
+
+ return (!save_reg (decode_rd (ival)) || set_ordinary_record_type ());
+ }
+
+ /* Returns true if instruction needs only saving pc and
+ floating point rd. */
+ static bool
+ need_save_pc_fprd (ULONGEST ival) noexcept
+ {
+ return (is_flw_insn (ival) || is_fmadd_s_insn (ival)
+ || is_fmsub_s_insn (ival) || is_fnmsub_s_insn (ival)
+ || is_fnmadd_s_insn (ival) || is_fadd_s_insn (ival)
+ || is_fsub_s_insn (ival) || is_fmul_s_insn (ival)
+ || is_fdiv_s_insn (ival) || is_fsqrt_s_insn (ival)
+ || is_fsgnj_s_insn (ival) || is_fsgnjn_s_insn (ival)
+ || is_fsgnjx_s_insn (ival) || is_fmin_s_insn (ival)
+ || is_fmax_s_insn (ival) || is_fcvt_s_w_insn (ival)
+ || is_fcvt_s_wu_insn (ival) || is_fmv_s_x_insn (ival)
+ || is_fcvt_s_l_insn (ival) || is_fcvt_s_lu_insn (ival)
+ || is_fld_insn (ival) || is_fmadd_d_insn (ival)
+ || is_fmsub_d_insn (ival) || is_fnmsub_d_insn (ival)
+ || is_fnmadd_d_insn (ival) || is_fadd_d_insn (ival)
+ || is_fsub_d_insn (ival) || is_fmul_d_insn (ival)
+ || is_fdiv_d_insn (ival) || is_fsqrt_d_insn (ival)
+ || is_fsgnj_d_insn (ival) || is_fsgnjn_d_insn (ival)
+ || is_fsgnjx_d_insn (ival) || is_fmin_d_insn (ival)
+ || is_fmax_d_insn (ival) || is_fcvt_s_d_insn (ival)
+ || is_fcvt_d_s_insn (ival) || is_fcvt_d_w_insn (ival)
+ || is_fcvt_d_wu_insn (ival) || is_fcvt_d_l_insn (ival)
+ || is_fcvt_d_lu_insn (ival) || is_fmv_d_x_insn (ival));
+ }
+
+ /* Returns true if instruction is classified. This function can set
+ m_error_occured. */
+ bool
+ try_save_pc_fprd (ULONGEST ival) noexcept
+ {
+ if (!need_save_pc_fprd (ival))
+ return false;
+
+ return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_rd (ival))
+ || set_ordinary_record_type ());
+ }
+
+ /* Returns true if instruction needs only saving pc, rd and csr. */
+ static bool
+ need_save_pc_rd_csr (ULONGEST ival) noexcept
+ {
+ return (is_csrrw_insn (ival) || is_csrrs_insn (ival) || is_csrrc_insn (ival)
+ || is_csrrwi_insn (ival) || is_csrrsi_insn (ival)
+ || is_csrrc_insn (ival));
+ }
+
+ /* Returns true if instruction is classified. This function can set
+ m_error_occured. */
+ bool
+ try_save_pc_rd_csr (ULONGEST ival) noexcept
+ {
+ if (!need_save_pc_rd_csr (ival))
+ return false;
+
+ return (!save_reg (decode_rd (ival))
+ || !save_reg (RISCV_FIRST_CSR_REGNUM + decode_csr (ival))
+ || set_ordinary_record_type ());
+ }
+
+ /* Returns the size of the memory chunk that needs to be saved if the
+ instruction belongs to the group that needs only saving pc and memory.
+ Otherwise returns 0. */
+ static mem_len
+ need_save_pc_mem (ULONGEST ival) noexcept
+ {
+ if (is_sb_insn (ival))
+ return 1;
+ if (is_sh_insn (ival))
+ return 2;
+ if (is_sw_insn (ival) || is_fsw_insn (ival))
+ return 4;
+ if (is_sd_insn (ival) || is_fsd_insn (ival))
+ return 8;
+ return 0;
+ }
+
+ /* Returns true if instruction is classified. This function can set
+ m_error_occured. */
+ bool
+ try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ mem_addr addr = mem_addr{};
+ mem_len len = need_save_pc_mem (ival);
+ if (len <= 0)
+ return false;
+
+ mem_len offset = EXTRACT_STYPE_IMM (ival);
+ return (!read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr + offset, len) || set_ordinary_record_type ());
+ }
+
+ /* Returns the size of the memory chunk that needs to be saved if the
+ instruction belongs to the group that needs only saving pc, rd and memory.
+ Otherwise returns 0. */
+ static mem_len
+ need_save_pc_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_sc_w_insn (ival) || is_amoswap_w_insn (ival)
+ || is_amoadd_w_insn (ival) || is_amoxor_w_insn (ival)
+ || is_amoand_w_insn (ival) || is_amoor_w_insn (ival)
+ || is_amomin_w_insn (ival) || is_amomax_w_insn (ival)
+ || is_amominu_w_insn (ival) || is_amomaxu_w_insn (ival))
+ return 4;
+ if (is_sc_d_insn (ival) || is_amoswap_d_insn (ival)
+ || is_amoadd_d_insn (ival) || is_amoxor_d_insn (ival)
+ || is_amoand_d_insn (ival) || is_amoor_d_insn (ival)
+ || is_amomin_d_insn (ival) || is_amomax_d_insn (ival)
+ || is_amominu_d_insn (ival) || is_amomaxu_d_insn (ival))
+ return 8;
+ return 0;
+ }
+
+ /* Returns true if instruction is classified. This function can set
+ m_error_occured. */
+ bool
+ try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ mem_len len = need_save_pc_rd_mem (ival);
+ mem_addr addr = 0;
+ if (len <= 0)
+ return false;
+
+ return (!read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr, len) || !save_reg (decode_rd (ival))
+ || set_ordinary_record_type ());
+ }
+
+ /* Returns true if instruction is successfully recordered. The length of
+ the instruction must be equal 4 bytes. */
+ bool
+ record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ if (is_ecall_insn (ival))
+ {
+ m_record_type = record_type::ECALL;
+ return true;
+ }
+
+ if (is_ebreak_insn (ival))
+ {
+ m_record_type = record_type::EBREAK;
+ return true;
+ }
+
+ if (try_save_pc (ival) || try_save_pc_rd (ival) || try_save_pc_fprd (ival)
+ || try_save_pc_rd_csr (ival) || try_save_pc_mem (ival, regcache)
+ || try_save_pc_rd_mem (ival, regcache))
+ return !has_error ();
+
+ warning (_("Currently this instruction with len 4(%lx) is unsupported"),
+ ival);
+ return false;
+ }
+
+ /* Returns true if instruction is successfully recordered. The length of
+ the instruction must be equal 2 bytes. */
+ bool
+ record_insn_len2 (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ mem_addr addr = mem_addr{};
+
+ /* The order here is very important, because
+ opcodes of some instructions may be the same. */
+
+ if (is_c_addi4spn_insn (ival) || is_c_lw_insn (ival)
+ || (m_xlen == 8 && is_c_ld_insn (ival)))
+ return (!save_reg (decode_crs2_short (ival))
+ || set_ordinary_record_type ());
+
+ if (is_c_fld_insn (ival) || (m_xlen == 4 && is_c_flw_insn (ival)))
+ return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival))
+ || set_ordinary_record_type ());
+
+ if (is_c_fsd_insn (ival) || (m_xlen == 8 && is_c_sd_insn (ival)))
+ {
+ ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LD_IMM (ival)};
+ return (!read_reg (regcache, decode_crs1_short (ival), addr)
+ || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
+ }
+
+ if ((m_xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
+ {
+ ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LW_IMM (ival)};
+ return (!read_reg (regcache, decode_crs1_short (ival), addr)
+ || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
+ }
+
+ if (is_c_nop_insn (ival))
+ return set_ordinary_record_type ();
+
+ if (is_c_addi_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (m_xlen == 4 && is_c_jal_insn (ival))
+ return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
+
+ if ((m_xlen == 8 && is_c_addiw_insn (ival)) || is_c_li_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_addi16sp_insn (ival))
+ return (!save_reg (RISCV_SP_REGNUM) || set_ordinary_record_type ());
+
+ if (is_c_lui_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_srli_insn (ival) || is_c_srai_insn (ival) || is_c_andi_insn (ival)
+ || is_c_sub_insn (ival) || is_c_xor_insn (ival) || is_c_or_insn (ival)
+ || is_c_and_insn (ival) || (m_xlen == 8 && is_c_subw_insn (ival))
+ || (m_xlen == 8 && is_c_addw_insn (ival)))
+ return (!save_reg (decode_crs1_short (ival))
+ || set_ordinary_record_type ());
+
+ if (is_c_j_insn (ival) || is_c_beqz_insn (ival) || is_c_bnez_insn (ival))
+ return set_ordinary_record_type ();
+
+ if (is_c_slli_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_fldsp_insn (ival) || (m_xlen == 4 && is_c_flwsp_insn (ival)))
+ return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival))
+ || set_ordinary_record_type ());
+
+ if (is_c_lwsp_insn (ival) || (m_xlen == 8 && is_c_ldsp_insn (ival)))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_jr_insn (ival))
+ return set_ordinary_record_type ();
+
+ if (is_c_mv_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_ebreak_insn (ival))
+ {
+ m_record_type = record_type::EBREAK;
+ return true;
+ }
+
+ if (is_c_jalr_insn (ival))
+ return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
+
+ if (is_c_add_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_fsdsp_insn (ival) || (m_xlen == 8 && is_c_sdsp_insn (ival)))
+ {
+ ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SDSP_IMM (ival)};
+ return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
+ || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
+ }
+
+ if (is_c_swsp_insn (ival) || (m_xlen == 4 && is_c_fswsp_insn (ival)))
+ {
+ ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SWSP_IMM (ival)};
+ return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
+ || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
+ }
+
+ warning (_("Currently this instruction with len 2(%lx) is unsupported"),
+ ival);
+ return false;
+ }
+
+public:
+ /* Iterator for registers that need to be recorded. */
+ using regs_iter = recorded_regs::const_iterator;
+ /* Iterator for memory chunks that need to be recorded. */
+ using mems_iter = recorded_mems::const_iterator;
+
+ /* Record instruction at address addr. Returns false if error happened. */
+ bool
+ record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
+ {
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ int m_length = 0;
+ m_xlen = riscv_isa_xlen (gdbarch);
+ ULONGEST ival = riscv_insn::fetch_instruction (gdbarch, addr, &m_length);
+ if (!save_reg (RISCV_PC_REGNUM))
+ return false;
+
+ if (m_length == 4)
+ return record_insn_len4 (ival, regcache);
+
+ if (m_length == 2)
+ return record_insn_len2 (ival, regcache);
+
+ /* 6 bytes or more. If the instruction is longer than 8 bytes, we don't
+ have full instruction bits in ival. At least, such long instructions
+ are not defined yet, so just ignore it. */
+ gdb_assert (m_length > 0 && m_length % 2 == 0);
+
+ warning (_("Can not record unknown instruction (opcode = %lx)"), ival);
+ return false;
+ }
+
+ /* Get record type of instruction. */
+ record_type
+ get_record_type () const noexcept
+ {
+ return m_record_type;
+ }
+
+ /* Returns an iterator to the beginning of the registers that need
+ to be saved. */
+ regs_iter
+ regs_begin () const noexcept
+ {
+ return m_regs.begin ();
+ }
+
+ /* Returns an iterator to the end of the registers that need
+ to be saved. */
+ regs_iter
+ regs_end () const noexcept
+ {
+ return m_regs.end ();
+ }
+
+ /* Returns an iterator to the beginning of the memory chunks that need
+ to be saved. */
+ mems_iter
+ mems_begin () const noexcept
+ {
+ return m_mems.begin ();
+ }
+
+ /* Returns an iterator to the end of the memory chunks that need
+ to be saved. */
+ mems_iter
+ mems_end () const noexcept
+ {
+ return m_mems.end ();
+ }
+};
+
+/* A helper function to record instruction using record API. */
+
+static int
+riscv_record_insn_details (struct gdbarch *gdbarch, struct regcache *regcache,
+ const riscv_recorded_insn &insn)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto regs_begin = insn.regs_begin ();
+ auto regs_end = insn.regs_end ();
+ if (std::any_of (regs_begin,
+ regs_end,
+ [®cache] (auto &®_it)
+ {
+ return record_full_arch_list_add_reg (regcache, reg_it);
+ }))
+ return -1;
+
+ auto mems_begin = insn.mems_begin ();
+ auto mems_end = insn.mems_end ();
+ if (std::any_of (mems_begin,
+ mems_end,
+ [] (auto &&mem_it)
+ {
+ return record_full_arch_list_add_mem (mem_it.first,
+ mem_it.second);
+ }))
+ return -1;
+
+ switch (insn.get_record_type ())
+ {
+ case riscv_recorded_insn::record_type::ORDINARY:
+ break;
+
+ case riscv_recorded_insn::record_type::ECALL:
+ {
+ if (!tdep->riscv_syscall_record)
+ {
+ warning (_("Syscall record is not supported"));
+ return -1;
+ }
+ ULONGEST reg_val = ULONGEST{};
+ if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
+ return -1;
+ return tdep->riscv_syscall_record (regcache, reg_val);
+ }
+
+ case riscv_recorded_insn::record_type::EBREAK:
+ break;
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* Parse the current instruction and record the values of the registers and
+ memory that will be changed in current instruction to record_arch_list.
+ Return -1 if something is wrong. */
+
+int
+riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+ CORE_ADDR addr)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_recorded_insn insn;
+ if (!insn.record (gdbarch, regcache, addr))
+ {
+ record_full_arch_list_add_end ();
+ return -1;
+ }
+
+ int ret_val = riscv_record_insn_details (gdbarch, regcache, insn);
+
+ if (record_full_arch_list_add_end ())
+ return -1;
+
+ return ret_val;
+}
diff --git a/gdb/riscv-tdep.h b/gdb/riscv-tdep.h
index 4bdc2e7a3d5..726f197af02 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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
new file mode 100755
index 00000000000..aa07ac13145
--- /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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 410f99e3350..b048ac7078b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
- || [istarget "s390*-*-linux*"] } {
+ || [istarget "s390*-*-linux*"]
+ || [istarget "riscv*-*-*"] } {
return 1
}
--
2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* Re: [PING][PATCH] This commit adds record full support for rv64gc instruction set
2025-03-31 17:50 ` [PING][PATCH] " Timur
@ 2025-04-08 12:33 ` Timur Golubovich
2025-04-10 9:16 ` Andrew Burgess
0 siblings, 1 reply; 35+ messages in thread
From: Timur Golubovich @ 2025-04-08 12:33 UTC (permalink / raw)
To: gdb-patches, aburgess
Hi! One more week has passed, so I am pinging.
пн, 31 мар. 2025 г. в 20:50, Timur <timurgol007@gmail.com>:
>
> It includes changes to the following files:
> - gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
> syscalls.
> - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
> rv64gc instructions.
> - gdb/configure.tgt: adds new files for compilation.
> - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
> targets.
> - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
> function that canonicalizes RISC-V syscall. This script can simplify support
> for syscalls on rv32 and rv64 system (currently support only for rv64). To
> use this script you need to pass a path to a file with syscalls description
> from riscv-glibc (example is in the help message). The script produces a
> mapping from syscall names to gdb_syscall enum.
> - gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
> - gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
> - gdb/NEWS: notification of new functionality.
>
> Approved-By: Guinevere Larsen <guinevere@redhat.com>
>
> Hi! It's been some time since I last received some feedback from you, can you
> please take a look once again? Is there something I can do to help push
> these changes forward?
>
> ---
> gdb/NEWS | 2 +
> gdb/configure.tgt | 5 +-
> gdb/doc/gdb.texinfo | 2 +-
> gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
> gdb/riscv-linux-tdep.c | 258 ++++++-
> gdb/riscv-linux-tdep.h | 29 +
> gdb/riscv-tdep.c | 688 +++++++++++++++++-
> gdb/riscv-tdep.h | 16 +-
> .../riscv-canonicalize-syscall-gen.py | 163 +++++
> gdb/testsuite/lib/gdb.exp | 3 +-
> 10 files changed, 1496 insertions(+), 12 deletions(-)
> create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
> create mode 100644 gdb/riscv-linux-tdep.h
> create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 964183910dc..20fdb571047 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -9581,3 +9581,5 @@ GDB now handles cross debugging. If you are remotely debugging between
> two different machines, type ``./configure host -target=targ''.
> Host is the machine where GDB will run; targ is the machine
> where the program that you are debugging will run.
> +
> + * Add record full support for rv64gc architectures
> diff --git a/gdb/configure.tgt b/gdb/configure.tgt
> index 62df71b13fa..0cd5c509695 100644
> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt
> @@ -545,8 +545,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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 99720f1206e..d5b989d681e 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -7759,7 +7759,7 @@ 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,
> -Moxie, PowerPC, PowerPC64, S/390, and x86 (i386/amd64) running
> +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}.
>
> diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
> new file mode 100644
> index 00000000000..3749fc32f33
> --- /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 ff478cf4c28..f21039a613c 100644
> --- a/gdb/riscv-linux-tdep.c
> +++ b/gdb/riscv-linux-tdep.c
> @@ -1,5 +1,5 @@
> /* Target-dependent code for GNU/Linux on RISC-V processors.
> - Copyright (C) 2018-2024 Free Software Foundation, Inc.
> + Copyright (C) 2018-2025 Free Software Foundation, Inc.
>
> This file is part of GDB.
>
> @@ -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 00000000000..0f481b168d1
> --- /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 932708ca4e9..cf1023a70c2 100644
> --- a/gdb/riscv-tdep.c
> +++ b/gdb/riscv-tdep.c
> @@ -1,6 +1,6 @@
> /* Target-dependent code for the RISC-V architecture, for GDB.
>
> - Copyright (C) 2018-2024 Free Software Foundation, Inc.
> + Copyright (C) 2018-2025 Free Software Foundation, Inc.
>
> This file is part of GDB.
>
> @@ -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
>
> @@ -1655,6 +1658,11 @@ class riscv_insn
> 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. */
> @@ -1800,11 +1808,6 @@ class riscv_insn
> 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;
>
> @@ -4415,6 +4418,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);
>
> @@ -4833,3 +4839,673 @@ this option can be used."),
> &setriscvcmdlist,
> &showriscvcmdlist);
> }
> +
> +/* A wrapper to read register under number regnum to address addr.
> + Returns false if error happened and makes warning. */
> +
> +static bool
> +try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
> +{
> + gdb_assert (regcache != nullptr);
> +
> + if (regcache->raw_read (regnum, &addr)
> + != register_status::REG_VALID)
> + {
> + warning (_("Can not read at address %lx"), addr);
> + return false;
> + }
> + return true;
> +}
> +
> +/* Helper class to record instruction. */
> +
> +class riscv_recorded_insn final
> +{
> +public:
> + /* Type for saved register. */
> + using regnum_type = int;
> + /* Type for saved memory. First is address, second is length. */
> + using memory_type = std::pair<CORE_ADDR, int>;
> +
> + /* Enum class that represents which type does recording belong to. */
> + enum class record_type
> + {
> + UNKNOWN,
> + ORDINARY,
> +
> + /* Corner cases. */
> + ECALL,
> + EBREAK,
> + };
> +
> +private:
> + /* Type for set of registers that need to be saved. */
> + using recorded_regs = std::vector<regnum_type>;
> + /* Type for set of memory records that need to be saved. */
> + using recorded_mems = std::vector<memory_type>;
> +
> + /* Type for memory address, extracted from memory_type. */
> + using mem_addr = decltype (std::declval<memory_type> ().first);
> + /* Type for memory length, extracted from memory_type. */
> + using mem_len = decltype (std::declval<memory_type> ().second);
> +
> + /* Record type of current instruction. */
> + record_type m_record_type = record_type::UNKNOWN;
> +
> + /* Flag that represents was there an error in current recording. */
> + bool m_error_occured = false;
> +
> + /* Set of registers that need to be recorded. */
> + recorded_regs m_regs;
> + /* Set of memory chunks that need to be recorded. */
> + recorded_mems m_mems;
> +
> + /* Width in bytes of the general purpose registers for GDBARCH,
> + where recording is happening. */
> + int m_xlen = 0;
> +
> + /* Helper for decode 16-bit instruction RS1. */
> + static regnum_type
> + decode_crs1_short (ULONGEST opcode) noexcept
> + {
> + return ((opcode >> OP_SH_CRS1S) & OP_MASK_CRS1S) + 8;
> + }
> +
> + /* Helper for decode 16-bit instruction RS2. */
> + static regnum_type
> + decode_crs2_short (ULONGEST opcode) noexcept
> + {
> + return ((opcode >> OP_SH_CRS2S) & OP_MASK_CRS2S) + 8;
> + }
> +
> + /* Helper for decode 16-bit instruction CRS1. */
> + static regnum_type
> + decode_crs1 (ULONGEST opcode) noexcept
> + {
> + return ((opcode >> OP_SH_RD) & OP_MASK_RD);
> + }
> +
> + /* Helper for decode 16-bit instruction CRS2. */
> + static regnum_type
> + decode_crs2 (ULONGEST opcode) noexcept
> + {
> + return ((opcode >> OP_SH_CRS2) & OP_MASK_CRS2);
> + }
> +
> + /* Helper for decode 32-bit instruction RD. */
> + static regnum_type
> + decode_rd (ULONGEST ival) noexcept
> + {
> + return (ival >> OP_SH_RD) & OP_MASK_RD;
> + }
> +
> + /* Helper for decode 32-bit instruction RS1. */
> + static regnum_type
> + decode_rs1 (ULONGEST ival) noexcept
> + {
> + return (ival >> OP_SH_RS1) & OP_MASK_RS1;
> + }
> +
> + /* Helper for decode 32-bit instruction RS2. */
> + static regnum_type
> + decode_rs2 (ULONGEST ival) noexcept
> + {
> + return (ival >> OP_SH_RS2) & OP_MASK_RS2;
> + }
> +
> + /* Helper for decode 32-bit instruction CSR. */
> + static regnum_type
> + decode_csr (ULONGEST ival) noexcept
> + {
> + return (ival >> OP_SH_CSR) & OP_MASK_CSR;
> + }
> +
> + /* Set ordinary record type. Always returns true. */
> + bool
> + set_ordinary_record_type () noexcept
> + {
> + m_record_type = record_type::ORDINARY;
> + return true;
> + }
> +
> + /* Set error happened. Always returns false. */
> + bool
> + set_error () noexcept
> + {
> + m_error_occured = true;
> + return false;
> + }
> +
> + /* Check if current recording has an error. */
> + bool
> + has_error () const noexcept
> + {
> + return m_error_occured;
> + }
> +
> + /* Reads register. Sets error and returns false if error happened. */
> + bool
> + read_reg (struct regcache *regcache, regnum_type reg,
> + ULONGEST &addr) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + if (!try_read (regcache, reg, addr))
> + return set_error ();
> + return true;
> + }
> +
> + /* Save register. Returns true or aborts if exception happened. */
> + bool
> + save_reg (regnum_type regnum) noexcept
> + {
> + m_regs.emplace_back (regnum);
> + return true;
> + }
> +
> + /* Save memory chunk. Returns true or aborts if exception happened. */
> + bool
> + save_mem (mem_addr addr, mem_len len) noexcept
> + {
> + m_mems.emplace_back (addr, len);
> + return true;
> + }
> +
> + /* Returns true if instruction needs only saving pc. */
> + static bool
> + need_save_pc (ULONGEST ival) noexcept
> + {
> + return (is_beq_insn (ival) || is_bne_insn (ival) || is_blt_insn (ival)
> + || is_bge_insn (ival) || is_bltu_insn (ival) || is_bgeu_insn (ival)
> + || is_fence_insn (ival) || is_pause_insn (ival)
> + || is_fence_i_insn (ival));
> + }
> +
> + /* Returns true if instruction is classified. */
> + bool
> + try_save_pc (ULONGEST ival) noexcept
> + {
> + if (!need_save_pc (ival))
> + return false;
> +
> + return set_ordinary_record_type ();
> + }
> +
> + /* Returns true if instruction needs only saving pc and rd. */
> + static bool
> + need_save_pc_rd (ULONGEST ival) noexcept
> + {
> + return (is_lui_insn (ival) || is_auipc_insn (ival) || is_jal_insn (ival)
> + || is_jalr_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
> + || is_lw_insn (ival) || is_lbu_insn (ival) || is_lhu_insn (ival)
> + || is_addi_insn (ival) || is_slti_insn (ival)
> + || is_sltiu_insn (ival) || is_xori_insn (ival) || is_ori_insn (ival)
> + || is_andi_insn (ival) || is_slli_rv32_insn (ival)
> + || is_srli_rv32_insn (ival) || is_srai_rv32_insn (ival)
> + || is_add_insn (ival) || is_sub_insn (ival) || is_sll_insn (ival)
> + || is_slt_insn (ival) || is_sltu_insn (ival) || is_xor_insn (ival)
> + || is_srl_insn (ival) || is_sra_insn (ival) || is_or_insn (ival)
> + || is_and_insn (ival) || is_lwu_insn (ival) || is_ld_insn (ival)
> + || is_slli_insn (ival) || is_srli_insn (ival) || is_srai_insn (ival)
> + || is_addiw_insn (ival) || is_slliw_insn (ival)
> + || is_srliw_insn (ival) || is_sraiw_insn (ival)
> + || is_addw_insn (ival) || is_subw_insn (ival) || is_sllw_insn (ival)
> + || is_srlw_insn (ival) || is_sraw_insn (ival) || is_mul_insn (ival)
> + || is_mulh_insn (ival) || is_mulhsu_insn (ival)
> + || is_mulhu_insn (ival) || is_div_insn (ival) || is_divu_insn (ival)
> + || is_rem_insn (ival) || is_remu_insn (ival) || is_mulw_insn (ival)
> + || is_divw_insn (ival) || is_divuw_insn (ival)
> + || is_remw_insn (ival) || is_remuw_insn (ival)
> + || is_lr_w_insn (ival) || is_lr_d_insn (ival)
> + || is_fcvt_w_s_insn (ival) || is_fcvt_wu_s_insn (ival)
> + || is_fmv_x_s_insn (ival) || is_feq_s_insn (ival)
> + || is_flt_s_insn (ival) || is_fle_s_insn (ival)
> + || is_fclass_s_insn (ival) || is_fcvt_l_s_insn (ival)
> + || is_fcvt_lu_s_insn (ival) || is_feq_d_insn (ival)
> + || is_flt_d_insn (ival) || is_fle_d_insn (ival)
> + || is_fclass_d_insn (ival) || is_fcvt_w_d_insn (ival)
> + || is_fcvt_wu_d_insn (ival) || is_fcvt_l_d_insn (ival)
> + || is_fcvt_lu_d_insn (ival) || is_fmv_x_d_insn (ival));
> + }
> +
> + /* Returns true if instruction is classified. This function can set
> + m_error_occured. */
> + bool
> + try_save_pc_rd (ULONGEST ival) noexcept
> + {
> + if (!need_save_pc_rd (ival))
> + return false;
> +
> + return (!save_reg (decode_rd (ival)) || set_ordinary_record_type ());
> + }
> +
> + /* Returns true if instruction needs only saving pc and
> + floating point rd. */
> + static bool
> + need_save_pc_fprd (ULONGEST ival) noexcept
> + {
> + return (is_flw_insn (ival) || is_fmadd_s_insn (ival)
> + || is_fmsub_s_insn (ival) || is_fnmsub_s_insn (ival)
> + || is_fnmadd_s_insn (ival) || is_fadd_s_insn (ival)
> + || is_fsub_s_insn (ival) || is_fmul_s_insn (ival)
> + || is_fdiv_s_insn (ival) || is_fsqrt_s_insn (ival)
> + || is_fsgnj_s_insn (ival) || is_fsgnjn_s_insn (ival)
> + || is_fsgnjx_s_insn (ival) || is_fmin_s_insn (ival)
> + || is_fmax_s_insn (ival) || is_fcvt_s_w_insn (ival)
> + || is_fcvt_s_wu_insn (ival) || is_fmv_s_x_insn (ival)
> + || is_fcvt_s_l_insn (ival) || is_fcvt_s_lu_insn (ival)
> + || is_fld_insn (ival) || is_fmadd_d_insn (ival)
> + || is_fmsub_d_insn (ival) || is_fnmsub_d_insn (ival)
> + || is_fnmadd_d_insn (ival) || is_fadd_d_insn (ival)
> + || is_fsub_d_insn (ival) || is_fmul_d_insn (ival)
> + || is_fdiv_d_insn (ival) || is_fsqrt_d_insn (ival)
> + || is_fsgnj_d_insn (ival) || is_fsgnjn_d_insn (ival)
> + || is_fsgnjx_d_insn (ival) || is_fmin_d_insn (ival)
> + || is_fmax_d_insn (ival) || is_fcvt_s_d_insn (ival)
> + || is_fcvt_d_s_insn (ival) || is_fcvt_d_w_insn (ival)
> + || is_fcvt_d_wu_insn (ival) || is_fcvt_d_l_insn (ival)
> + || is_fcvt_d_lu_insn (ival) || is_fmv_d_x_insn (ival));
> + }
> +
> + /* Returns true if instruction is classified. This function can set
> + m_error_occured. */
> + bool
> + try_save_pc_fprd (ULONGEST ival) noexcept
> + {
> + if (!need_save_pc_fprd (ival))
> + return false;
> +
> + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_rd (ival))
> + || set_ordinary_record_type ());
> + }
> +
> + /* Returns true if instruction needs only saving pc, rd and csr. */
> + static bool
> + need_save_pc_rd_csr (ULONGEST ival) noexcept
> + {
> + return (is_csrrw_insn (ival) || is_csrrs_insn (ival) || is_csrrc_insn (ival)
> + || is_csrrwi_insn (ival) || is_csrrsi_insn (ival)
> + || is_csrrc_insn (ival));
> + }
> +
> + /* Returns true if instruction is classified. This function can set
> + m_error_occured. */
> + bool
> + try_save_pc_rd_csr (ULONGEST ival) noexcept
> + {
> + if (!need_save_pc_rd_csr (ival))
> + return false;
> +
> + return (!save_reg (decode_rd (ival))
> + || !save_reg (RISCV_FIRST_CSR_REGNUM + decode_csr (ival))
> + || set_ordinary_record_type ());
> + }
> +
> + /* Returns the size of the memory chunk that needs to be saved if the
> + instruction belongs to the group that needs only saving pc and memory.
> + Otherwise returns 0. */
> + static mem_len
> + need_save_pc_mem (ULONGEST ival) noexcept
> + {
> + if (is_sb_insn (ival))
> + return 1;
> + if (is_sh_insn (ival))
> + return 2;
> + if (is_sw_insn (ival) || is_fsw_insn (ival))
> + return 4;
> + if (is_sd_insn (ival) || is_fsd_insn (ival))
> + return 8;
> + return 0;
> + }
> +
> + /* Returns true if instruction is classified. This function can set
> + m_error_occured. */
> + bool
> + try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + mem_addr addr = mem_addr{};
> + mem_len len = need_save_pc_mem (ival);
> + if (len <= 0)
> + return false;
> +
> + mem_len offset = EXTRACT_STYPE_IMM (ival);
> + return (!read_reg (regcache, decode_rs1 (ival), addr)
> + || !save_mem (addr + offset, len) || set_ordinary_record_type ());
> + }
> +
> + /* Returns the size of the memory chunk that needs to be saved if the
> + instruction belongs to the group that needs only saving pc, rd and memory.
> + Otherwise returns 0. */
> + static mem_len
> + need_save_pc_rd_mem (ULONGEST ival) noexcept
> + {
> + if (is_sc_w_insn (ival) || is_amoswap_w_insn (ival)
> + || is_amoadd_w_insn (ival) || is_amoxor_w_insn (ival)
> + || is_amoand_w_insn (ival) || is_amoor_w_insn (ival)
> + || is_amomin_w_insn (ival) || is_amomax_w_insn (ival)
> + || is_amominu_w_insn (ival) || is_amomaxu_w_insn (ival))
> + return 4;
> + if (is_sc_d_insn (ival) || is_amoswap_d_insn (ival)
> + || is_amoadd_d_insn (ival) || is_amoxor_d_insn (ival)
> + || is_amoand_d_insn (ival) || is_amoor_d_insn (ival)
> + || is_amomin_d_insn (ival) || is_amomax_d_insn (ival)
> + || is_amominu_d_insn (ival) || is_amomaxu_d_insn (ival))
> + return 8;
> + return 0;
> + }
> +
> + /* Returns true if instruction is classified. This function can set
> + m_error_occured. */
> + bool
> + try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + mem_len len = need_save_pc_rd_mem (ival);
> + mem_addr addr = 0;
> + if (len <= 0)
> + return false;
> +
> + return (!read_reg (regcache, decode_rs1 (ival), addr)
> + || !save_mem (addr, len) || !save_reg (decode_rd (ival))
> + || set_ordinary_record_type ());
> + }
> +
> + /* Returns true if instruction is successfully recordered. The length of
> + the instruction must be equal 4 bytes. */
> + bool
> + record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + if (is_ecall_insn (ival))
> + {
> + m_record_type = record_type::ECALL;
> + return true;
> + }
> +
> + if (is_ebreak_insn (ival))
> + {
> + m_record_type = record_type::EBREAK;
> + return true;
> + }
> +
> + if (try_save_pc (ival) || try_save_pc_rd (ival) || try_save_pc_fprd (ival)
> + || try_save_pc_rd_csr (ival) || try_save_pc_mem (ival, regcache)
> + || try_save_pc_rd_mem (ival, regcache))
> + return !has_error ();
> +
> + warning (_("Currently this instruction with len 4(%lx) is unsupported"),
> + ival);
> + return false;
> + }
> +
> + /* Returns true if instruction is successfully recordered. The length of
> + the instruction must be equal 2 bytes. */
> + bool
> + record_insn_len2 (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + mem_addr addr = mem_addr{};
> +
> + /* The order here is very important, because
> + opcodes of some instructions may be the same. */
> +
> + if (is_c_addi4spn_insn (ival) || is_c_lw_insn (ival)
> + || (m_xlen == 8 && is_c_ld_insn (ival)))
> + return (!save_reg (decode_crs2_short (ival))
> + || set_ordinary_record_type ());
> +
> + if (is_c_fld_insn (ival) || (m_xlen == 4 && is_c_flw_insn (ival)))
> + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival))
> + || set_ordinary_record_type ());
> +
> + if (is_c_fsd_insn (ival) || (m_xlen == 8 && is_c_sd_insn (ival)))
> + {
> + ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LD_IMM (ival)};
> + return (!read_reg (regcache, decode_crs1_short (ival), addr)
> + || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
> + }
> +
> + if ((m_xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
> + {
> + ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LW_IMM (ival)};
> + return (!read_reg (regcache, decode_crs1_short (ival), addr)
> + || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
> + }
> +
> + if (is_c_nop_insn (ival))
> + return set_ordinary_record_type ();
> +
> + if (is_c_addi_insn (ival))
> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> +
> + if (m_xlen == 4 && is_c_jal_insn (ival))
> + return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
> +
> + if ((m_xlen == 8 && is_c_addiw_insn (ival)) || is_c_li_insn (ival))
> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> +
> + if (is_c_addi16sp_insn (ival))
> + return (!save_reg (RISCV_SP_REGNUM) || set_ordinary_record_type ());
> +
> + if (is_c_lui_insn (ival))
> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> +
> + if (is_c_srli_insn (ival) || is_c_srai_insn (ival) || is_c_andi_insn (ival)
> + || is_c_sub_insn (ival) || is_c_xor_insn (ival) || is_c_or_insn (ival)
> + || is_c_and_insn (ival) || (m_xlen == 8 && is_c_subw_insn (ival))
> + || (m_xlen == 8 && is_c_addw_insn (ival)))
> + return (!save_reg (decode_crs1_short (ival))
> + || set_ordinary_record_type ());
> +
> + if (is_c_j_insn (ival) || is_c_beqz_insn (ival) || is_c_bnez_insn (ival))
> + return set_ordinary_record_type ();
> +
> + if (is_c_slli_insn (ival))
> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> +
> + if (is_c_fldsp_insn (ival) || (m_xlen == 4 && is_c_flwsp_insn (ival)))
> + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival))
> + || set_ordinary_record_type ());
> +
> + if (is_c_lwsp_insn (ival) || (m_xlen == 8 && is_c_ldsp_insn (ival)))
> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> +
> + if (is_c_jr_insn (ival))
> + return set_ordinary_record_type ();
> +
> + if (is_c_mv_insn (ival))
> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> +
> + if (is_c_ebreak_insn (ival))
> + {
> + m_record_type = record_type::EBREAK;
> + return true;
> + }
> +
> + if (is_c_jalr_insn (ival))
> + return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
> +
> + if (is_c_add_insn (ival))
> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> +
> + if (is_c_fsdsp_insn (ival) || (m_xlen == 8 && is_c_sdsp_insn (ival)))
> + {
> + ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SDSP_IMM (ival)};
> + return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
> + || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
> + }
> +
> + if (is_c_swsp_insn (ival) || (m_xlen == 4 && is_c_fswsp_insn (ival)))
> + {
> + ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SWSP_IMM (ival)};
> + return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
> + || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
> + }
> +
> + warning (_("Currently this instruction with len 2(%lx) is unsupported"),
> + ival);
> + return false;
> + }
> +
> +public:
> + /* Iterator for registers that need to be recorded. */
> + using regs_iter = recorded_regs::const_iterator;
> + /* Iterator for memory chunks that need to be recorded. */
> + using mems_iter = recorded_mems::const_iterator;
> +
> + /* Record instruction at address addr. Returns false if error happened. */
> + bool
> + record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
> + {
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + int m_length = 0;
> + m_xlen = riscv_isa_xlen (gdbarch);
> + ULONGEST ival = riscv_insn::fetch_instruction (gdbarch, addr, &m_length);
> + if (!save_reg (RISCV_PC_REGNUM))
> + return false;
> +
> + if (m_length == 4)
> + return record_insn_len4 (ival, regcache);
> +
> + if (m_length == 2)
> + return record_insn_len2 (ival, regcache);
> +
> + /* 6 bytes or more. If the instruction is longer than 8 bytes, we don't
> + have full instruction bits in ival. At least, such long instructions
> + are not defined yet, so just ignore it. */
> + gdb_assert (m_length > 0 && m_length % 2 == 0);
> +
> + warning (_("Can not record unknown instruction (opcode = %lx)"), ival);
> + return false;
> + }
> +
> + /* Get record type of instruction. */
> + record_type
> + get_record_type () const noexcept
> + {
> + return m_record_type;
> + }
> +
> + /* Returns an iterator to the beginning of the registers that need
> + to be saved. */
> + regs_iter
> + regs_begin () const noexcept
> + {
> + return m_regs.begin ();
> + }
> +
> + /* Returns an iterator to the end of the registers that need
> + to be saved. */
> + regs_iter
> + regs_end () const noexcept
> + {
> + return m_regs.end ();
> + }
> +
> + /* Returns an iterator to the beginning of the memory chunks that need
> + to be saved. */
> + mems_iter
> + mems_begin () const noexcept
> + {
> + return m_mems.begin ();
> + }
> +
> + /* Returns an iterator to the end of the memory chunks that need
> + to be saved. */
> + mems_iter
> + mems_end () const noexcept
> + {
> + return m_mems.end ();
> + }
> +};
> +
> +/* A helper function to record instruction using record API. */
> +
> +static int
> +riscv_record_insn_details (struct gdbarch *gdbarch, struct regcache *regcache,
> + const riscv_recorded_insn &insn)
> +{
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> + auto regs_begin = insn.regs_begin ();
> + auto regs_end = insn.regs_end ();
> + if (std::any_of (regs_begin,
> + regs_end,
> + [®cache] (auto &®_it)
> + {
> + return record_full_arch_list_add_reg (regcache, reg_it);
> + }))
> + return -1;
> +
> + auto mems_begin = insn.mems_begin ();
> + auto mems_end = insn.mems_end ();
> + if (std::any_of (mems_begin,
> + mems_end,
> + [] (auto &&mem_it)
> + {
> + return record_full_arch_list_add_mem (mem_it.first,
> + mem_it.second);
> + }))
> + return -1;
> +
> + switch (insn.get_record_type ())
> + {
> + case riscv_recorded_insn::record_type::ORDINARY:
> + break;
> +
> + case riscv_recorded_insn::record_type::ECALL:
> + {
> + if (!tdep->riscv_syscall_record)
> + {
> + warning (_("Syscall record is not supported"));
> + return -1;
> + }
> + ULONGEST reg_val = ULONGEST{};
> + if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
> + return -1;
> + return tdep->riscv_syscall_record (regcache, reg_val);
> + }
> +
> + case riscv_recorded_insn::record_type::EBREAK:
> + break;
> +
> + default:
> + return -1;
> + }
> + return 0;
> +}
> +
> +/* Parse the current instruction and record the values of the registers and
> + memory that will be changed in current instruction to record_arch_list.
> + Return -1 if something is wrong. */
> +
> +int
> +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
> + CORE_ADDR addr)
> +{
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + riscv_recorded_insn insn;
> + if (!insn.record (gdbarch, regcache, addr))
> + {
> + record_full_arch_list_add_end ();
> + return -1;
> + }
> +
> + int ret_val = riscv_record_insn_details (gdbarch, regcache, insn);
> +
> + if (record_full_arch_list_add_end ())
> + return -1;
> +
> + return ret_val;
> +}
> diff --git a/gdb/riscv-tdep.h b/gdb/riscv-tdep.h
> index 4bdc2e7a3d5..726f197af02 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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> new file mode 100755
> index 00000000000..aa07ac13145
> --- /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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 410f99e3350..b048ac7078b 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -3718,7 +3718,8 @@ proc supports_reverse {} {
> || [istarget "i\[34567\]86-*-linux*"]
> || [istarget "aarch64*-*-linux*"]
> || [istarget "powerpc*-*-linux*"]
> - || [istarget "s390*-*-linux*"] } {
> + || [istarget "s390*-*-linux*"]
> + || [istarget "riscv*-*-*"] } {
> return 1
> }
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 35+ messages in thread* Re: [PING][PATCH] This commit adds record full support for rv64gc instruction set
2025-04-08 12:33 ` Timur Golubovich
@ 2025-04-10 9:16 ` Andrew Burgess
0 siblings, 0 replies; 35+ messages in thread
From: Andrew Burgess @ 2025-04-10 9:16 UTC (permalink / raw)
To: Timur Golubovich, gdb-patches
Timur Golubovich <timurgol007@gmail.com> writes:
> Hi! One more week has passed, so I am pinging.
Sorry for the delay, I sent some feedback.
Thanks,
Andrew
>
> пн, 31 мар. 2025 г. в 20:50, Timur <timurgol007@gmail.com>:
>>
>> It includes changes to the following files:
>> - gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
>> syscalls.
>> - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
>> rv64gc instructions.
>> - gdb/configure.tgt: adds new files for compilation.
>> - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
>> targets.
>> - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
>> function that canonicalizes RISC-V syscall. This script can simplify support
>> for syscalls on rv32 and rv64 system (currently support only for rv64). To
>> use this script you need to pass a path to a file with syscalls description
>> from riscv-glibc (example is in the help message). The script produces a
>> mapping from syscall names to gdb_syscall enum.
>> - gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
>> - gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
>> - gdb/NEWS: notification of new functionality.
>>
>> Approved-By: Guinevere Larsen <guinevere@redhat.com>
>>
>> Hi! It's been some time since I last received some feedback from you, can you
>> please take a look once again? Is there something I can do to help push
>> these changes forward?
>>
>> ---
>> gdb/NEWS | 2 +
>> gdb/configure.tgt | 5 +-
>> gdb/doc/gdb.texinfo | 2 +-
>> gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
>> gdb/riscv-linux-tdep.c | 258 ++++++-
>> gdb/riscv-linux-tdep.h | 29 +
>> gdb/riscv-tdep.c | 688 +++++++++++++++++-
>> gdb/riscv-tdep.h | 16 +-
>> .../riscv-canonicalize-syscall-gen.py | 163 +++++
>> gdb/testsuite/lib/gdb.exp | 3 +-
>> 10 files changed, 1496 insertions(+), 12 deletions(-)
>> create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
>> create mode 100644 gdb/riscv-linux-tdep.h
>> create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
>>
>> diff --git a/gdb/NEWS b/gdb/NEWS
>> index 964183910dc..20fdb571047 100644
>> --- a/gdb/NEWS
>> +++ b/gdb/NEWS
>> @@ -9581,3 +9581,5 @@ GDB now handles cross debugging. If you are remotely debugging between
>> two different machines, type ``./configure host -target=targ''.
>> Host is the machine where GDB will run; targ is the machine
>> where the program that you are debugging will run.
>> +
>> + * Add record full support for rv64gc architectures
>> diff --git a/gdb/configure.tgt b/gdb/configure.tgt
>> index 62df71b13fa..0cd5c509695 100644
>> --- a/gdb/configure.tgt
>> +++ b/gdb/configure.tgt
>> @@ -545,8 +545,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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
>> index 99720f1206e..d5b989d681e 100644
>> --- a/gdb/doc/gdb.texinfo
>> +++ b/gdb/doc/gdb.texinfo
>> @@ -7759,7 +7759,7 @@ 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,
>> -Moxie, PowerPC, PowerPC64, S/390, and x86 (i386/amd64) running
>> +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}.
>>
>> diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
>> new file mode 100644
>> index 00000000000..3749fc32f33
>> --- /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 ff478cf4c28..f21039a613c 100644
>> --- a/gdb/riscv-linux-tdep.c
>> +++ b/gdb/riscv-linux-tdep.c
>> @@ -1,5 +1,5 @@
>> /* Target-dependent code for GNU/Linux on RISC-V processors.
>> - Copyright (C) 2018-2024 Free Software Foundation, Inc.
>> + Copyright (C) 2018-2025 Free Software Foundation, Inc.
>>
>> This file is part of GDB.
>>
>> @@ -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 00000000000..0f481b168d1
>> --- /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 932708ca4e9..cf1023a70c2 100644
>> --- a/gdb/riscv-tdep.c
>> +++ b/gdb/riscv-tdep.c
>> @@ -1,6 +1,6 @@
>> /* Target-dependent code for the RISC-V architecture, for GDB.
>>
>> - Copyright (C) 2018-2024 Free Software Foundation, Inc.
>> + Copyright (C) 2018-2025 Free Software Foundation, Inc.
>>
>> This file is part of GDB.
>>
>> @@ -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
>>
>> @@ -1655,6 +1658,11 @@ class riscv_insn
>> 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. */
>> @@ -1800,11 +1808,6 @@ class riscv_insn
>> 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;
>>
>> @@ -4415,6 +4418,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);
>>
>> @@ -4833,3 +4839,673 @@ this option can be used."),
>> &setriscvcmdlist,
>> &showriscvcmdlist);
>> }
>> +
>> +/* A wrapper to read register under number regnum to address addr.
>> + Returns false if error happened and makes warning. */
>> +
>> +static bool
>> +try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
>> +{
>> + gdb_assert (regcache != nullptr);
>> +
>> + if (regcache->raw_read (regnum, &addr)
>> + != register_status::REG_VALID)
>> + {
>> + warning (_("Can not read at address %lx"), addr);
>> + return false;
>> + }
>> + return true;
>> +}
>> +
>> +/* Helper class to record instruction. */
>> +
>> +class riscv_recorded_insn final
>> +{
>> +public:
>> + /* Type for saved register. */
>> + using regnum_type = int;
>> + /* Type for saved memory. First is address, second is length. */
>> + using memory_type = std::pair<CORE_ADDR, int>;
>> +
>> + /* Enum class that represents which type does recording belong to. */
>> + enum class record_type
>> + {
>> + UNKNOWN,
>> + ORDINARY,
>> +
>> + /* Corner cases. */
>> + ECALL,
>> + EBREAK,
>> + };
>> +
>> +private:
>> + /* Type for set of registers that need to be saved. */
>> + using recorded_regs = std::vector<regnum_type>;
>> + /* Type for set of memory records that need to be saved. */
>> + using recorded_mems = std::vector<memory_type>;
>> +
>> + /* Type for memory address, extracted from memory_type. */
>> + using mem_addr = decltype (std::declval<memory_type> ().first);
>> + /* Type for memory length, extracted from memory_type. */
>> + using mem_len = decltype (std::declval<memory_type> ().second);
>> +
>> + /* Record type of current instruction. */
>> + record_type m_record_type = record_type::UNKNOWN;
>> +
>> + /* Flag that represents was there an error in current recording. */
>> + bool m_error_occured = false;
>> +
>> + /* Set of registers that need to be recorded. */
>> + recorded_regs m_regs;
>> + /* Set of memory chunks that need to be recorded. */
>> + recorded_mems m_mems;
>> +
>> + /* Width in bytes of the general purpose registers for GDBARCH,
>> + where recording is happening. */
>> + int m_xlen = 0;
>> +
>> + /* Helper for decode 16-bit instruction RS1. */
>> + static regnum_type
>> + decode_crs1_short (ULONGEST opcode) noexcept
>> + {
>> + return ((opcode >> OP_SH_CRS1S) & OP_MASK_CRS1S) + 8;
>> + }
>> +
>> + /* Helper for decode 16-bit instruction RS2. */
>> + static regnum_type
>> + decode_crs2_short (ULONGEST opcode) noexcept
>> + {
>> + return ((opcode >> OP_SH_CRS2S) & OP_MASK_CRS2S) + 8;
>> + }
>> +
>> + /* Helper for decode 16-bit instruction CRS1. */
>> + static regnum_type
>> + decode_crs1 (ULONGEST opcode) noexcept
>> + {
>> + return ((opcode >> OP_SH_RD) & OP_MASK_RD);
>> + }
>> +
>> + /* Helper for decode 16-bit instruction CRS2. */
>> + static regnum_type
>> + decode_crs2 (ULONGEST opcode) noexcept
>> + {
>> + return ((opcode >> OP_SH_CRS2) & OP_MASK_CRS2);
>> + }
>> +
>> + /* Helper for decode 32-bit instruction RD. */
>> + static regnum_type
>> + decode_rd (ULONGEST ival) noexcept
>> + {
>> + return (ival >> OP_SH_RD) & OP_MASK_RD;
>> + }
>> +
>> + /* Helper for decode 32-bit instruction RS1. */
>> + static regnum_type
>> + decode_rs1 (ULONGEST ival) noexcept
>> + {
>> + return (ival >> OP_SH_RS1) & OP_MASK_RS1;
>> + }
>> +
>> + /* Helper for decode 32-bit instruction RS2. */
>> + static regnum_type
>> + decode_rs2 (ULONGEST ival) noexcept
>> + {
>> + return (ival >> OP_SH_RS2) & OP_MASK_RS2;
>> + }
>> +
>> + /* Helper for decode 32-bit instruction CSR. */
>> + static regnum_type
>> + decode_csr (ULONGEST ival) noexcept
>> + {
>> + return (ival >> OP_SH_CSR) & OP_MASK_CSR;
>> + }
>> +
>> + /* Set ordinary record type. Always returns true. */
>> + bool
>> + set_ordinary_record_type () noexcept
>> + {
>> + m_record_type = record_type::ORDINARY;
>> + return true;
>> + }
>> +
>> + /* Set error happened. Always returns false. */
>> + bool
>> + set_error () noexcept
>> + {
>> + m_error_occured = true;
>> + return false;
>> + }
>> +
>> + /* Check if current recording has an error. */
>> + bool
>> + has_error () const noexcept
>> + {
>> + return m_error_occured;
>> + }
>> +
>> + /* Reads register. Sets error and returns false if error happened. */
>> + bool
>> + read_reg (struct regcache *regcache, regnum_type reg,
>> + ULONGEST &addr) noexcept
>> + {
>> + gdb_assert (regcache != nullptr);
>> +
>> + if (!try_read (regcache, reg, addr))
>> + return set_error ();
>> + return true;
>> + }
>> +
>> + /* Save register. Returns true or aborts if exception happened. */
>> + bool
>> + save_reg (regnum_type regnum) noexcept
>> + {
>> + m_regs.emplace_back (regnum);
>> + return true;
>> + }
>> +
>> + /* Save memory chunk. Returns true or aborts if exception happened. */
>> + bool
>> + save_mem (mem_addr addr, mem_len len) noexcept
>> + {
>> + m_mems.emplace_back (addr, len);
>> + return true;
>> + }
>> +
>> + /* Returns true if instruction needs only saving pc. */
>> + static bool
>> + need_save_pc (ULONGEST ival) noexcept
>> + {
>> + return (is_beq_insn (ival) || is_bne_insn (ival) || is_blt_insn (ival)
>> + || is_bge_insn (ival) || is_bltu_insn (ival) || is_bgeu_insn (ival)
>> + || is_fence_insn (ival) || is_pause_insn (ival)
>> + || is_fence_i_insn (ival));
>> + }
>> +
>> + /* Returns true if instruction is classified. */
>> + bool
>> + try_save_pc (ULONGEST ival) noexcept
>> + {
>> + if (!need_save_pc (ival))
>> + return false;
>> +
>> + return set_ordinary_record_type ();
>> + }
>> +
>> + /* Returns true if instruction needs only saving pc and rd. */
>> + static bool
>> + need_save_pc_rd (ULONGEST ival) noexcept
>> + {
>> + return (is_lui_insn (ival) || is_auipc_insn (ival) || is_jal_insn (ival)
>> + || is_jalr_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
>> + || is_lw_insn (ival) || is_lbu_insn (ival) || is_lhu_insn (ival)
>> + || is_addi_insn (ival) || is_slti_insn (ival)
>> + || is_sltiu_insn (ival) || is_xori_insn (ival) || is_ori_insn (ival)
>> + || is_andi_insn (ival) || is_slli_rv32_insn (ival)
>> + || is_srli_rv32_insn (ival) || is_srai_rv32_insn (ival)
>> + || is_add_insn (ival) || is_sub_insn (ival) || is_sll_insn (ival)
>> + || is_slt_insn (ival) || is_sltu_insn (ival) || is_xor_insn (ival)
>> + || is_srl_insn (ival) || is_sra_insn (ival) || is_or_insn (ival)
>> + || is_and_insn (ival) || is_lwu_insn (ival) || is_ld_insn (ival)
>> + || is_slli_insn (ival) || is_srli_insn (ival) || is_srai_insn (ival)
>> + || is_addiw_insn (ival) || is_slliw_insn (ival)
>> + || is_srliw_insn (ival) || is_sraiw_insn (ival)
>> + || is_addw_insn (ival) || is_subw_insn (ival) || is_sllw_insn (ival)
>> + || is_srlw_insn (ival) || is_sraw_insn (ival) || is_mul_insn (ival)
>> + || is_mulh_insn (ival) || is_mulhsu_insn (ival)
>> + || is_mulhu_insn (ival) || is_div_insn (ival) || is_divu_insn (ival)
>> + || is_rem_insn (ival) || is_remu_insn (ival) || is_mulw_insn (ival)
>> + || is_divw_insn (ival) || is_divuw_insn (ival)
>> + || is_remw_insn (ival) || is_remuw_insn (ival)
>> + || is_lr_w_insn (ival) || is_lr_d_insn (ival)
>> + || is_fcvt_w_s_insn (ival) || is_fcvt_wu_s_insn (ival)
>> + || is_fmv_x_s_insn (ival) || is_feq_s_insn (ival)
>> + || is_flt_s_insn (ival) || is_fle_s_insn (ival)
>> + || is_fclass_s_insn (ival) || is_fcvt_l_s_insn (ival)
>> + || is_fcvt_lu_s_insn (ival) || is_feq_d_insn (ival)
>> + || is_flt_d_insn (ival) || is_fle_d_insn (ival)
>> + || is_fclass_d_insn (ival) || is_fcvt_w_d_insn (ival)
>> + || is_fcvt_wu_d_insn (ival) || is_fcvt_l_d_insn (ival)
>> + || is_fcvt_lu_d_insn (ival) || is_fmv_x_d_insn (ival));
>> + }
>> +
>> + /* Returns true if instruction is classified. This function can set
>> + m_error_occured. */
>> + bool
>> + try_save_pc_rd (ULONGEST ival) noexcept
>> + {
>> + if (!need_save_pc_rd (ival))
>> + return false;
>> +
>> + return (!save_reg (decode_rd (ival)) || set_ordinary_record_type ());
>> + }
>> +
>> + /* Returns true if instruction needs only saving pc and
>> + floating point rd. */
>> + static bool
>> + need_save_pc_fprd (ULONGEST ival) noexcept
>> + {
>> + return (is_flw_insn (ival) || is_fmadd_s_insn (ival)
>> + || is_fmsub_s_insn (ival) || is_fnmsub_s_insn (ival)
>> + || is_fnmadd_s_insn (ival) || is_fadd_s_insn (ival)
>> + || is_fsub_s_insn (ival) || is_fmul_s_insn (ival)
>> + || is_fdiv_s_insn (ival) || is_fsqrt_s_insn (ival)
>> + || is_fsgnj_s_insn (ival) || is_fsgnjn_s_insn (ival)
>> + || is_fsgnjx_s_insn (ival) || is_fmin_s_insn (ival)
>> + || is_fmax_s_insn (ival) || is_fcvt_s_w_insn (ival)
>> + || is_fcvt_s_wu_insn (ival) || is_fmv_s_x_insn (ival)
>> + || is_fcvt_s_l_insn (ival) || is_fcvt_s_lu_insn (ival)
>> + || is_fld_insn (ival) || is_fmadd_d_insn (ival)
>> + || is_fmsub_d_insn (ival) || is_fnmsub_d_insn (ival)
>> + || is_fnmadd_d_insn (ival) || is_fadd_d_insn (ival)
>> + || is_fsub_d_insn (ival) || is_fmul_d_insn (ival)
>> + || is_fdiv_d_insn (ival) || is_fsqrt_d_insn (ival)
>> + || is_fsgnj_d_insn (ival) || is_fsgnjn_d_insn (ival)
>> + || is_fsgnjx_d_insn (ival) || is_fmin_d_insn (ival)
>> + || is_fmax_d_insn (ival) || is_fcvt_s_d_insn (ival)
>> + || is_fcvt_d_s_insn (ival) || is_fcvt_d_w_insn (ival)
>> + || is_fcvt_d_wu_insn (ival) || is_fcvt_d_l_insn (ival)
>> + || is_fcvt_d_lu_insn (ival) || is_fmv_d_x_insn (ival));
>> + }
>> +
>> + /* Returns true if instruction is classified. This function can set
>> + m_error_occured. */
>> + bool
>> + try_save_pc_fprd (ULONGEST ival) noexcept
>> + {
>> + if (!need_save_pc_fprd (ival))
>> + return false;
>> +
>> + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_rd (ival))
>> + || set_ordinary_record_type ());
>> + }
>> +
>> + /* Returns true if instruction needs only saving pc, rd and csr. */
>> + static bool
>> + need_save_pc_rd_csr (ULONGEST ival) noexcept
>> + {
>> + return (is_csrrw_insn (ival) || is_csrrs_insn (ival) || is_csrrc_insn (ival)
>> + || is_csrrwi_insn (ival) || is_csrrsi_insn (ival)
>> + || is_csrrc_insn (ival));
>> + }
>> +
>> + /* Returns true if instruction is classified. This function can set
>> + m_error_occured. */
>> + bool
>> + try_save_pc_rd_csr (ULONGEST ival) noexcept
>> + {
>> + if (!need_save_pc_rd_csr (ival))
>> + return false;
>> +
>> + return (!save_reg (decode_rd (ival))
>> + || !save_reg (RISCV_FIRST_CSR_REGNUM + decode_csr (ival))
>> + || set_ordinary_record_type ());
>> + }
>> +
>> + /* Returns the size of the memory chunk that needs to be saved if the
>> + instruction belongs to the group that needs only saving pc and memory.
>> + Otherwise returns 0. */
>> + static mem_len
>> + need_save_pc_mem (ULONGEST ival) noexcept
>> + {
>> + if (is_sb_insn (ival))
>> + return 1;
>> + if (is_sh_insn (ival))
>> + return 2;
>> + if (is_sw_insn (ival) || is_fsw_insn (ival))
>> + return 4;
>> + if (is_sd_insn (ival) || is_fsd_insn (ival))
>> + return 8;
>> + return 0;
>> + }
>> +
>> + /* Returns true if instruction is classified. This function can set
>> + m_error_occured. */
>> + bool
>> + try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
>> + {
>> + gdb_assert (regcache != nullptr);
>> +
>> + mem_addr addr = mem_addr{};
>> + mem_len len = need_save_pc_mem (ival);
>> + if (len <= 0)
>> + return false;
>> +
>> + mem_len offset = EXTRACT_STYPE_IMM (ival);
>> + return (!read_reg (regcache, decode_rs1 (ival), addr)
>> + || !save_mem (addr + offset, len) || set_ordinary_record_type ());
>> + }
>> +
>> + /* Returns the size of the memory chunk that needs to be saved if the
>> + instruction belongs to the group that needs only saving pc, rd and memory.
>> + Otherwise returns 0. */
>> + static mem_len
>> + need_save_pc_rd_mem (ULONGEST ival) noexcept
>> + {
>> + if (is_sc_w_insn (ival) || is_amoswap_w_insn (ival)
>> + || is_amoadd_w_insn (ival) || is_amoxor_w_insn (ival)
>> + || is_amoand_w_insn (ival) || is_amoor_w_insn (ival)
>> + || is_amomin_w_insn (ival) || is_amomax_w_insn (ival)
>> + || is_amominu_w_insn (ival) || is_amomaxu_w_insn (ival))
>> + return 4;
>> + if (is_sc_d_insn (ival) || is_amoswap_d_insn (ival)
>> + || is_amoadd_d_insn (ival) || is_amoxor_d_insn (ival)
>> + || is_amoand_d_insn (ival) || is_amoor_d_insn (ival)
>> + || is_amomin_d_insn (ival) || is_amomax_d_insn (ival)
>> + || is_amominu_d_insn (ival) || is_amomaxu_d_insn (ival))
>> + return 8;
>> + return 0;
>> + }
>> +
>> + /* Returns true if instruction is classified. This function can set
>> + m_error_occured. */
>> + bool
>> + try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
>> + {
>> + gdb_assert (regcache != nullptr);
>> +
>> + mem_len len = need_save_pc_rd_mem (ival);
>> + mem_addr addr = 0;
>> + if (len <= 0)
>> + return false;
>> +
>> + return (!read_reg (regcache, decode_rs1 (ival), addr)
>> + || !save_mem (addr, len) || !save_reg (decode_rd (ival))
>> + || set_ordinary_record_type ());
>> + }
>> +
>> + /* Returns true if instruction is successfully recordered. The length of
>> + the instruction must be equal 4 bytes. */
>> + bool
>> + record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept
>> + {
>> + gdb_assert (regcache != nullptr);
>> +
>> + if (is_ecall_insn (ival))
>> + {
>> + m_record_type = record_type::ECALL;
>> + return true;
>> + }
>> +
>> + if (is_ebreak_insn (ival))
>> + {
>> + m_record_type = record_type::EBREAK;
>> + return true;
>> + }
>> +
>> + if (try_save_pc (ival) || try_save_pc_rd (ival) || try_save_pc_fprd (ival)
>> + || try_save_pc_rd_csr (ival) || try_save_pc_mem (ival, regcache)
>> + || try_save_pc_rd_mem (ival, regcache))
>> + return !has_error ();
>> +
>> + warning (_("Currently this instruction with len 4(%lx) is unsupported"),
>> + ival);
>> + return false;
>> + }
>> +
>> + /* Returns true if instruction is successfully recordered. The length of
>> + the instruction must be equal 2 bytes. */
>> + bool
>> + record_insn_len2 (ULONGEST ival, struct regcache *regcache) noexcept
>> + {
>> + gdb_assert (regcache != nullptr);
>> +
>> + mem_addr addr = mem_addr{};
>> +
>> + /* The order here is very important, because
>> + opcodes of some instructions may be the same. */
>> +
>> + if (is_c_addi4spn_insn (ival) || is_c_lw_insn (ival)
>> + || (m_xlen == 8 && is_c_ld_insn (ival)))
>> + return (!save_reg (decode_crs2_short (ival))
>> + || set_ordinary_record_type ());
>> +
>> + if (is_c_fld_insn (ival) || (m_xlen == 4 && is_c_flw_insn (ival)))
>> + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival))
>> + || set_ordinary_record_type ());
>> +
>> + if (is_c_fsd_insn (ival) || (m_xlen == 8 && is_c_sd_insn (ival)))
>> + {
>> + ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LD_IMM (ival)};
>> + return (!read_reg (regcache, decode_crs1_short (ival), addr)
>> + || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
>> + }
>> +
>> + if ((m_xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
>> + {
>> + ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LW_IMM (ival)};
>> + return (!read_reg (regcache, decode_crs1_short (ival), addr)
>> + || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
>> + }
>> +
>> + if (is_c_nop_insn (ival))
>> + return set_ordinary_record_type ();
>> +
>> + if (is_c_addi_insn (ival))
>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>> +
>> + if (m_xlen == 4 && is_c_jal_insn (ival))
>> + return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
>> +
>> + if ((m_xlen == 8 && is_c_addiw_insn (ival)) || is_c_li_insn (ival))
>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>> +
>> + if (is_c_addi16sp_insn (ival))
>> + return (!save_reg (RISCV_SP_REGNUM) || set_ordinary_record_type ());
>> +
>> + if (is_c_lui_insn (ival))
>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>> +
>> + if (is_c_srli_insn (ival) || is_c_srai_insn (ival) || is_c_andi_insn (ival)
>> + || is_c_sub_insn (ival) || is_c_xor_insn (ival) || is_c_or_insn (ival)
>> + || is_c_and_insn (ival) || (m_xlen == 8 && is_c_subw_insn (ival))
>> + || (m_xlen == 8 && is_c_addw_insn (ival)))
>> + return (!save_reg (decode_crs1_short (ival))
>> + || set_ordinary_record_type ());
>> +
>> + if (is_c_j_insn (ival) || is_c_beqz_insn (ival) || is_c_bnez_insn (ival))
>> + return set_ordinary_record_type ();
>> +
>> + if (is_c_slli_insn (ival))
>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>> +
>> + if (is_c_fldsp_insn (ival) || (m_xlen == 4 && is_c_flwsp_insn (ival)))
>> + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival))
>> + || set_ordinary_record_type ());
>> +
>> + if (is_c_lwsp_insn (ival) || (m_xlen == 8 && is_c_ldsp_insn (ival)))
>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>> +
>> + if (is_c_jr_insn (ival))
>> + return set_ordinary_record_type ();
>> +
>> + if (is_c_mv_insn (ival))
>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>> +
>> + if (is_c_ebreak_insn (ival))
>> + {
>> + m_record_type = record_type::EBREAK;
>> + return true;
>> + }
>> +
>> + if (is_c_jalr_insn (ival))
>> + return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
>> +
>> + if (is_c_add_insn (ival))
>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>> +
>> + if (is_c_fsdsp_insn (ival) || (m_xlen == 8 && is_c_sdsp_insn (ival)))
>> + {
>> + ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SDSP_IMM (ival)};
>> + return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
>> + || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
>> + }
>> +
>> + if (is_c_swsp_insn (ival) || (m_xlen == 4 && is_c_fswsp_insn (ival)))
>> + {
>> + ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SWSP_IMM (ival)};
>> + return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
>> + || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
>> + }
>> +
>> + warning (_("Currently this instruction with len 2(%lx) is unsupported"),
>> + ival);
>> + return false;
>> + }
>> +
>> +public:
>> + /* Iterator for registers that need to be recorded. */
>> + using regs_iter = recorded_regs::const_iterator;
>> + /* Iterator for memory chunks that need to be recorded. */
>> + using mems_iter = recorded_mems::const_iterator;
>> +
>> + /* Record instruction at address addr. Returns false if error happened. */
>> + bool
>> + record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
>> + {
>> + gdb_assert (gdbarch != nullptr);
>> + gdb_assert (regcache != nullptr);
>> +
>> + int m_length = 0;
>> + m_xlen = riscv_isa_xlen (gdbarch);
>> + ULONGEST ival = riscv_insn::fetch_instruction (gdbarch, addr, &m_length);
>> + if (!save_reg (RISCV_PC_REGNUM))
>> + return false;
>> +
>> + if (m_length == 4)
>> + return record_insn_len4 (ival, regcache);
>> +
>> + if (m_length == 2)
>> + return record_insn_len2 (ival, regcache);
>> +
>> + /* 6 bytes or more. If the instruction is longer than 8 bytes, we don't
>> + have full instruction bits in ival. At least, such long instructions
>> + are not defined yet, so just ignore it. */
>> + gdb_assert (m_length > 0 && m_length % 2 == 0);
>> +
>> + warning (_("Can not record unknown instruction (opcode = %lx)"), ival);
>> + return false;
>> + }
>> +
>> + /* Get record type of instruction. */
>> + record_type
>> + get_record_type () const noexcept
>> + {
>> + return m_record_type;
>> + }
>> +
>> + /* Returns an iterator to the beginning of the registers that need
>> + to be saved. */
>> + regs_iter
>> + regs_begin () const noexcept
>> + {
>> + return m_regs.begin ();
>> + }
>> +
>> + /* Returns an iterator to the end of the registers that need
>> + to be saved. */
>> + regs_iter
>> + regs_end () const noexcept
>> + {
>> + return m_regs.end ();
>> + }
>> +
>> + /* Returns an iterator to the beginning of the memory chunks that need
>> + to be saved. */
>> + mems_iter
>> + mems_begin () const noexcept
>> + {
>> + return m_mems.begin ();
>> + }
>> +
>> + /* Returns an iterator to the end of the memory chunks that need
>> + to be saved. */
>> + mems_iter
>> + mems_end () const noexcept
>> + {
>> + return m_mems.end ();
>> + }
>> +};
>> +
>> +/* A helper function to record instruction using record API. */
>> +
>> +static int
>> +riscv_record_insn_details (struct gdbarch *gdbarch, struct regcache *regcache,
>> + const riscv_recorded_insn &insn)
>> +{
>> + gdb_assert (gdbarch != nullptr);
>> + gdb_assert (regcache != nullptr);
>> +
>> + riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
>> + auto regs_begin = insn.regs_begin ();
>> + auto regs_end = insn.regs_end ();
>> + if (std::any_of (regs_begin,
>> + regs_end,
>> + [®cache] (auto &®_it)
>> + {
>> + return record_full_arch_list_add_reg (regcache, reg_it);
>> + }))
>> + return -1;
>> +
>> + auto mems_begin = insn.mems_begin ();
>> + auto mems_end = insn.mems_end ();
>> + if (std::any_of (mems_begin,
>> + mems_end,
>> + [] (auto &&mem_it)
>> + {
>> + return record_full_arch_list_add_mem (mem_it.first,
>> + mem_it.second);
>> + }))
>> + return -1;
>> +
>> + switch (insn.get_record_type ())
>> + {
>> + case riscv_recorded_insn::record_type::ORDINARY:
>> + break;
>> +
>> + case riscv_recorded_insn::record_type::ECALL:
>> + {
>> + if (!tdep->riscv_syscall_record)
>> + {
>> + warning (_("Syscall record is not supported"));
>> + return -1;
>> + }
>> + ULONGEST reg_val = ULONGEST{};
>> + if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
>> + return -1;
>> + return tdep->riscv_syscall_record (regcache, reg_val);
>> + }
>> +
>> + case riscv_recorded_insn::record_type::EBREAK:
>> + break;
>> +
>> + default:
>> + return -1;
>> + }
>> + return 0;
>> +}
>> +
>> +/* Parse the current instruction and record the values of the registers and
>> + memory that will be changed in current instruction to record_arch_list.
>> + Return -1 if something is wrong. */
>> +
>> +int
>> +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
>> + CORE_ADDR addr)
>> +{
>> + gdb_assert (gdbarch != nullptr);
>> + gdb_assert (regcache != nullptr);
>> +
>> + riscv_recorded_insn insn;
>> + if (!insn.record (gdbarch, regcache, addr))
>> + {
>> + record_full_arch_list_add_end ();
>> + return -1;
>> + }
>> +
>> + int ret_val = riscv_record_insn_details (gdbarch, regcache, insn);
>> +
>> + if (record_full_arch_list_add_end ())
>> + return -1;
>> +
>> + return ret_val;
>> +}
>> diff --git a/gdb/riscv-tdep.h b/gdb/riscv-tdep.h
>> index 4bdc2e7a3d5..726f197af02 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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
>> new file mode 100755
>> index 00000000000..aa07ac13145
>> --- /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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
>> index 410f99e3350..b048ac7078b 100644
>> --- a/gdb/testsuite/lib/gdb.exp
>> +++ b/gdb/testsuite/lib/gdb.exp
>> @@ -3718,7 +3718,8 @@ proc supports_reverse {} {
>> || [istarget "i\[34567\]86-*-linux*"]
>> || [istarget "aarch64*-*-linux*"]
>> || [istarget "powerpc*-*-linux*"]
>> - || [istarget "s390*-*-linux*"] } {
>> + || [istarget "s390*-*-linux*"]
>> + || [istarget "riscv*-*-*"] } {
>> return 1
>> }
>>
>> --
>> 2.34.1
>>
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v8] This commit adds record full support for rv64gc instruction set
2025-03-18 18:49 ` [PATCH v8] " Timur
2025-03-31 17:50 ` [PING][PATCH] " Timur
@ 2025-04-10 9:16 ` Andrew Burgess
2025-04-10 11:55 ` Timur
1 sibling, 1 reply; 35+ messages in thread
From: Andrew Burgess @ 2025-04-10 9:16 UTC (permalink / raw)
To: Timur, gdb-patches; +Cc: guinevere, Timur
Hi Timur,
I think this is looking great now. I do have a couple of minor follow
on questions/points, but I think this is almost done. Thanks for all
your work getting this ready for merging. Notes are inline below...
Timur <timurgol007@gmail.com> writes:
> It includes changes to the following files:
> - gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
> syscalls.
> - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
> rv64gc instructions.
> - gdb/configure.tgt: adds new files for compilation.
> - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
> targets.
> - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
> function that canonicalizes RISC-V syscall. This script can simplify support
> for syscalls on rv32 and rv64 system (currently support only for rv64). To
> use this script you need to pass a path to a file with syscalls description
> from riscv-glibc (example is in the help message). The script produces a
I guess riscv-glibc is a fork of glibc that is running ahead of the
upstream glibc project?
Is there any reason that this cannot work with the official glibc
project? That would be preferable I think unless there's a compelling
reason, which ideally we'd document.
I tried running the script using current HEAD of glibc, the only
significant change is:
diff --git i/gdb/riscv-canonicalize-syscall-gen.c w/gdb/riscv-canonicalize-syscall-gen.c
index 3749fc32f33..03579984f4a 100644
--- i/gdb/riscv-canonicalize-syscall-gen.c
+++ w/gdb/riscv-canonicalize-syscall-gen.c
@@ -270,7 +270,7 @@ riscv64_canonicalize_syscall (int syscall)
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 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; */
There was also a bunch of new syscalls that are added, but commented
out. I haven't tried to figure out if the change above is a bad thing
or not though, maybe you could advise.
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 964183910dc..20fdb571047 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -9581,3 +9581,5 @@ GDB now handles cross debugging. If you are remotely debugging between
> two different machines, type ``./configure host -target=targ''.
> Host is the machine where GDB will run; targ is the machine
> where the program that you are debugging will run.
> +
> + * Add record full support for rv64gc architectures
This entry needs to move to the correct section of the NEWS file. If
you look for the lines starting '***' you'll see that the first header
is '*** Changes since GDB 16', this is the section where you should
place your entry, probably just before the '* New commands' line.
> diff --git a/gdb/riscv-linux-tdep.h b/gdb/riscv-linux-tdep.h
> new file mode 100644
> index 00000000000..0f481b168d1
> --- /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
These include guards need to be renamed to GDB_RISCV_LINUX_TDEP_H. You
can do this automatically if you like with:
./gdb/check-include-guards.py --update gdb/*.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/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 410f99e3350..b048ac7078b 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -3718,7 +3718,8 @@ proc supports_reverse {} {
> || [istarget "i\[34567\]86-*-linux*"]
> || [istarget "aarch64*-*-linux*"]
> || [istarget "powerpc*-*-linux*"]
> - || [istarget "s390*-*-linux*"] } {
> + || [istarget "s390*-*-linux*"]
> + || [istarget "riscv*-*-*"] } {
I notice that every other entry in this list restricts the match to
Linux based targets. Is RISC-V really different here? Or should RISC-V
be similarly restricted?
Thanks,
Andrew
^ permalink raw reply [flat|nested] 35+ messages in thread* [PATCH v8] This commit adds record full support for rv64gc instruction set
2025-04-10 9:16 ` [PATCH v8] " Andrew Burgess
@ 2025-04-10 11:55 ` Timur
2025-04-14 7:40 ` Andrew Burgess
0 siblings, 1 reply; 35+ messages in thread
From: Timur @ 2025-04-10 11:55 UTC (permalink / raw)
To: gdb-patches, aburgess; +Cc: Timur, Guinevere Larsen
It includes changes to the following files:
- gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
syscalls.
- gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
rv64gc instructions.
- gdb/configure.tgt: adds new files for compilation.
- gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
targets.
- gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
function that canonicalizes RISC-V syscall. This script can simplify support
for syscalls on rv32 and rv64 system (currently support only for rv64). To
use this script you need to pass a path to a file with syscalls description
from riscv-glibc (example is in the help message). The script produces a
mapping from syscall names to gdb_syscall enum.
- gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
- gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
- gdb/NEWS: notification of new functionality.
Approved-By: Guinevere Larsen <guinevere@redhat.com>
Hi Andrew! Thank you for review.
> I guess riscv-glibc is a fork of glibc that is running ahead of the
> upstream glibc project?
>
> Is there any reason that this cannot work with the official glibc
> project? That would be preferable I think unless there's a compelling
> reason, which ideally we'd document.
>
> I tried running the script using current HEAD of glibc, the only
> significant change is:
>
> diff --git i/gdb/riscv-canonicalize-syscall-gen.c w/gdb/riscv-canonicalize-syscall-gen.c
> index 3749fc32f33..03579984f4a 100644
> --- i/gdb/riscv-canonicalize-syscall-gen.c
> +++ w/gdb/riscv-canonicalize-syscall-gen.c
> @@ -270,7 +270,7 @@ riscv64_canonicalize_syscall (int syscall)
> 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 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; */
>
> There was also a bunch of new syscalls that are added, but commented
> out. I haven't tried to figure out if the change above is a bad thing
> or not though, maybe you could advise.
Yes, I agree with you, I just used riscv-glibc as I had aldready downloaded it
and unfortunately the version was pretty old (I started working on this patch
almost half a year ago).
> This entry needs to move to the correct section of the NEWS file. If
> you look for the lines starting '***' you'll see that the first header
> is '*** Changes since GDB 16', this is the section where you should
> place your entry, probably just before the '* New commands' line.
> These include guards need to be renamed to GDB_RISCV_LINUX_TDEP_H. You
> can do this automatically if you like with:
>
> ./gdb/check-include-guards.py --update gdb/*.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 */
Addressed
>@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
>> || [istarget "i\[34567\]86-*-linux*"]
>> || [istarget "aarch64*-*-linux*"]
>> || [istarget "powerpc*-*-linux*"]
>> - || [istarget "s390*-*-linux*"] } {
>> + || [istarget "s390*-*-linux*"]
>> + || [istarget "riscv*-*-*"] } {
>
> I notice that every other entry in this list restricts the match to
> Linux based targets. Is RISC-V really different here? Or should RISC-V
> be similarly restricted?
Addressed, this was just my misprint.
Hope that now everything is okay, wait for your feedback.
---
gdb/NEWS | 2 +
gdb/configure.tgt | 5 +-
gdb/doc/gdb.texinfo | 2 +-
gdb/riscv-canonicalize-syscall-gen.c | 358 +++++++++
gdb/riscv-linux-tdep.c | 256 +++++++
gdb/riscv-linux-tdep.h | 29 +
gdb/riscv-tdep.c | 686 +++++++++++++++++-
gdb/riscv-tdep.h | 16 +-
.../riscv-canonicalize-syscall-gen.py | 163 +++++
gdb/testsuite/lib/gdb.exp | 3 +-
10 files changed, 1510 insertions(+), 10 deletions(-)
create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
create mode 100644 gdb/riscv-linux-tdep.h
create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
diff --git a/gdb/NEWS b/gdb/NEWS
index 99ec392d4c4..1a05a9f9251 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/configure.tgt b/gdb/configure.tgt
index 18a15c032c3..44da2257be8 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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b251c8e1228..73f3b14cf74 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7807,7 +7807,7 @@ 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
+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}.
diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
new file mode 100644
index 00000000000..03579984f4a
--- /dev/null
+++ b/gdb/riscv-canonicalize-syscall-gen.c
@@ -0,0 +1,358 @@
+/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py
+
+ Copyright (C) 2024-2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "riscv-linux-tdep.h"
+
+/* riscv64_canonicalize_syscall maps from the native riscv 64 Linux set
+ of syscall ids into a canonical set of syscall ids used by
+ process record. */
+
+enum gdb_syscall
+riscv64_canonicalize_syscall (int syscall)
+{
+ switch (syscall)
+ {
+ case 0: return gdb_sys_io_setup;
+ case 1: return gdb_sys_io_destroy;
+ case 2: return gdb_sys_io_submit;
+ case 3: return gdb_sys_io_cancel;
+ case 4: return gdb_sys_io_getevents;
+ case 5: return gdb_sys_setxattr;
+ case 6: return gdb_sys_lsetxattr;
+ case 7: return gdb_sys_fsetxattr;
+ case 8: return gdb_sys_getxattr;
+ case 9: return gdb_sys_lgetxattr;
+ case 10: return gdb_sys_fgetxattr;
+ case 11: return gdb_sys_listxattr;
+ case 12: return gdb_sys_llistxattr;
+ case 13: return gdb_sys_flistxattr;
+ case 14: return gdb_sys_removexattr;
+ case 15: return gdb_sys_lremovexattr;
+ case 16: return gdb_sys_fremovexattr;
+ case 17: return gdb_sys_getcwd;
+ case 18: return gdb_sys_lookup_dcookie;
+ case 19: return gdb_sys_eventfd2;
+ case 20: return gdb_sys_epoll_create1;
+ case 21: return gdb_sys_epoll_ctl;
+ case 22: return gdb_sys_epoll_pwait;
+ case 23: return gdb_sys_dup;
+ case 24: return gdb_sys_dup3;
+ case 25: return gdb_sys_fcntl;
+ case 26: return gdb_sys_inotify_init1;
+ case 27: return gdb_sys_inotify_add_watch;
+ case 28: return gdb_sys_inotify_rm_watch;
+ case 29: return gdb_sys_ioctl;
+ case 30: return gdb_sys_ioprio_set;
+ case 31: return gdb_sys_ioprio_get;
+ case 32: return gdb_sys_flock;
+ case 33: return gdb_sys_mknodat;
+ case 34: return gdb_sys_mkdirat;
+ case 35: return gdb_sys_unlinkat;
+ case 36: return gdb_sys_symlinkat;
+ case 37: return gdb_sys_linkat;
+ /* case 39: return gdb_sys_umount2; */
+ case 40: return gdb_sys_mount;
+ case 41: return gdb_sys_pivot_root;
+ case 42: return gdb_sys_nfsservctl;
+ case 43: return gdb_sys_statfs;
+ case 44: return gdb_sys_fstatfs;
+ case 45: return gdb_sys_truncate;
+ case 46: return gdb_sys_ftruncate;
+ case 47: return gdb_sys_fallocate;
+ case 48: return gdb_sys_faccessat;
+ case 49: return gdb_sys_chdir;
+ case 50: return gdb_sys_fchdir;
+ case 51: return gdb_sys_chroot;
+ case 52: return gdb_sys_fchmod;
+ case 53: return gdb_sys_fchmodat;
+ case 54: return gdb_sys_fchownat;
+ case 55: return gdb_sys_fchown;
+ case 56: return gdb_sys_openat;
+ case 57: return gdb_sys_close;
+ case 58: return gdb_sys_vhangup;
+ case 59: return gdb_sys_pipe2;
+ case 60: return gdb_sys_quotactl;
+ case 61: return gdb_sys_getdents64;
+ case 62: return gdb_sys_lseek;
+ case 63: return gdb_sys_read;
+ case 64: return gdb_sys_write;
+ case 65: return gdb_sys_readv;
+ case 66: return gdb_sys_writev;
+ case 67: return gdb_sys_pread64;
+ case 68: return gdb_sys_pwrite64;
+ /* case 69: return gdb_sys_preadv; */
+ /* case 70: return gdb_sys_pwritev; */
+ case 71: return gdb_sys_sendfile;
+ case 72: return gdb_sys_pselect6;
+ case 73: return gdb_sys_ppoll;
+ /* case 74: return gdb_sys_signalfd4; */
+ case 75: return gdb_sys_vmsplice;
+ case 76: return gdb_sys_splice;
+ case 77: return gdb_sys_tee;
+ case 78: return gdb_sys_readlinkat;
+ case 79: return gdb_sys_newfstatat;
+ case 80: return gdb_sys_fstat;
+ case 81: return gdb_sys_sync;
+ case 82: return gdb_sys_fsync;
+ case 83: return gdb_sys_fdatasync;
+ case 84: return gdb_sys_sync_file_range;
+ /* case 85: return gdb_sys_timerfd_create; */
+ /* case 86: return gdb_sys_timerfd_settime; */
+ /* case 87: return gdb_sys_timerfd_gettime; */
+ /* case 88: return gdb_sys_utimensat; */
+ case 89: return gdb_sys_acct;
+ case 90: return gdb_sys_capget;
+ case 91: return gdb_sys_capset;
+ case 92: return gdb_sys_personality;
+ case 93: return gdb_sys_exit;
+ case 94: return gdb_sys_exit_group;
+ case 95: return gdb_sys_waitid;
+ case 96: return gdb_sys_set_tid_address;
+ case 97: return gdb_sys_unshare;
+ case 98: return gdb_sys_futex;
+ case 99: return gdb_sys_set_robust_list;
+ case 100: return gdb_sys_get_robust_list;
+ case 101: return gdb_sys_nanosleep;
+ case 102: return gdb_sys_getitimer;
+ case 103: return gdb_sys_setitimer;
+ case 104: return gdb_sys_kexec_load;
+ case 105: return gdb_sys_init_module;
+ case 106: return gdb_sys_delete_module;
+ case 107: return gdb_sys_timer_create;
+ case 108: return gdb_sys_timer_gettime;
+ case 109: return gdb_sys_timer_getoverrun;
+ case 110: return gdb_sys_timer_settime;
+ case 111: return gdb_sys_timer_delete;
+ case 112: return gdb_sys_clock_settime;
+ case 113: return gdb_sys_clock_gettime;
+ case 114: return gdb_sys_clock_getres;
+ case 115: return gdb_sys_clock_nanosleep;
+ case 116: return gdb_sys_syslog;
+ case 117: return gdb_sys_ptrace;
+ case 118: return gdb_sys_sched_setparam;
+ case 119: return gdb_sys_sched_setscheduler;
+ case 120: return gdb_sys_sched_getscheduler;
+ case 121: return gdb_sys_sched_getparam;
+ case 122: return gdb_sys_sched_setaffinity;
+ case 123: return gdb_sys_sched_getaffinity;
+ case 124: return gdb_sys_sched_yield;
+ case 125: return gdb_sys_sched_get_priority_max;
+ case 126: return gdb_sys_sched_get_priority_min;
+ case 127: return gdb_sys_sched_rr_get_interval;
+ case 128: return gdb_sys_restart_syscall;
+ case 129: return gdb_sys_kill;
+ case 130: return gdb_sys_tkill;
+ case 131: return gdb_sys_tgkill;
+ case 132: return gdb_sys_sigaltstack;
+ case 133: return gdb_sys_rt_sigsuspend;
+ case 134: return gdb_sys_rt_sigaction;
+ case 135: return gdb_sys_rt_sigprocmask;
+ case 136: return gdb_sys_rt_sigpending;
+ case 137: return gdb_sys_rt_sigtimedwait;
+ case 138: return gdb_sys_rt_sigqueueinfo;
+ case 139: return gdb_sys_rt_sigreturn;
+ case 140: return gdb_sys_setpriority;
+ case 141: return gdb_sys_getpriority;
+ case 142: return gdb_sys_reboot;
+ case 143: return gdb_sys_setregid;
+ case 144: return gdb_sys_setgid;
+ case 145: return gdb_sys_setreuid;
+ case 146: return gdb_sys_setuid;
+ case 147: return gdb_sys_setresuid;
+ case 148: return gdb_sys_getresuid;
+ case 149: return gdb_sys_setresgid;
+ case 150: return gdb_sys_getresgid;
+ case 151: return gdb_sys_setfsuid;
+ case 152: return gdb_sys_setfsgid;
+ case 153: return gdb_sys_times;
+ case 154: return gdb_sys_setpgid;
+ case 155: return gdb_sys_getpgid;
+ case 156: return gdb_sys_getsid;
+ case 157: return gdb_sys_setsid;
+ case 158: return gdb_sys_getgroups;
+ case 159: return gdb_sys_setgroups;
+ case 160: return gdb_sys_uname;
+ case 161: return gdb_sys_sethostname;
+ case 162: return gdb_sys_setdomainname;
+ case 163: return gdb_sys_getrlimit;
+ case 164: return gdb_sys_setrlimit;
+ case 165: return gdb_sys_getrusage;
+ case 166: return gdb_sys_umask;
+ case 167: return gdb_sys_prctl;
+ case 168: return gdb_sys_getcpu;
+ case 169: return gdb_sys_gettimeofday;
+ case 170: return gdb_sys_settimeofday;
+ case 171: return gdb_sys_adjtimex;
+ case 172: return gdb_sys_getpid;
+ case 173: return gdb_sys_getppid;
+ case 174: return gdb_sys_getuid;
+ case 175: return gdb_sys_geteuid;
+ case 176: return gdb_sys_getgid;
+ case 177: return gdb_sys_getegid;
+ case 178: return gdb_sys_gettid;
+ case 179: return gdb_sys_sysinfo;
+ case 180: return gdb_sys_mq_open;
+ case 181: return gdb_sys_mq_unlink;
+ case 182: return gdb_sys_mq_timedsend;
+ case 183: return gdb_sys_mq_timedreceive;
+ case 184: return gdb_sys_mq_notify;
+ case 185: return gdb_sys_mq_getsetattr;
+ case 186: return gdb_sys_msgget;
+ case 187: return gdb_sys_msgctl;
+ case 188: return gdb_sys_msgrcv;
+ case 189: return gdb_sys_msgsnd;
+ case 190: return gdb_sys_semget;
+ case 191: return gdb_sys_semctl;
+ case 192: return gdb_sys_semtimedop;
+ case 193: return gdb_sys_semop;
+ case 194: return gdb_sys_shmget;
+ case 195: return gdb_sys_shmctl;
+ case 196: return gdb_sys_shmat;
+ case 197: return gdb_sys_shmdt;
+ case 198: return gdb_sys_socket;
+ case 199: return gdb_sys_socketpair;
+ case 200: return gdb_sys_bind;
+ case 201: return gdb_sys_listen;
+ case 202: return gdb_sys_accept;
+ case 203: return gdb_sys_connect;
+ case 204: return gdb_sys_getsockname;
+ case 205: return gdb_sys_getpeername;
+ case 206: return gdb_sys_sendto;
+ case 207: return gdb_sys_recvfrom;
+ case 208: return gdb_sys_setsockopt;
+ case 209: return gdb_sys_getsockopt;
+ case 210: return gdb_sys_shutdown;
+ case 211: return gdb_sys_sendmsg;
+ case 212: return gdb_sys_recvmsg;
+ case 213: return gdb_sys_readahead;
+ case 214: return gdb_sys_brk;
+ case 215: return gdb_sys_munmap;
+ case 216: return gdb_sys_mremap;
+ case 217: return gdb_sys_add_key;
+ case 218: return gdb_sys_request_key;
+ case 219: return gdb_sys_keyctl;
+ case 220: return gdb_sys_clone;
+ case 221: return gdb_sys_execve;
+ case 222: return gdb_old_mmap;
+ case 223: return gdb_sys_fadvise64;
+ case 224: return gdb_sys_swapon;
+ case 225: return gdb_sys_swapoff;
+ case 226: return gdb_sys_mprotect;
+ case 227: return gdb_sys_msync;
+ case 228: return gdb_sys_mlock;
+ case 229: return gdb_sys_munlock;
+ case 230: return gdb_sys_mlockall;
+ case 231: return gdb_sys_munlockall;
+ case 232: return gdb_sys_mincore;
+ case 233: return gdb_sys_madvise;
+ case 234: return gdb_sys_remap_file_pages;
+ case 235: return gdb_sys_mbind;
+ case 236: return gdb_sys_get_mempolicy;
+ case 237: return gdb_sys_set_mempolicy;
+ case 238: return gdb_sys_migrate_pages;
+ case 239: return gdb_sys_move_pages;
+ /* case 240: return gdb_sys_rt_tgsigqueueinfo; */
+ /* case 241: return gdb_sys_perf_event_open; */
+ case 242: return gdb_sys_accept4;
+ /* case 243: return gdb_sys_recvmmsg; */
+ /* case 258: return gdb_sys_riscv_hwprobe; */
+ /* case 259: return gdb_sys_riscv_flush_icache; */
+ case 260: return gdb_sys_wait4;
+ /* case 261: return gdb_sys_prlimit64; */
+ /* case 262: return gdb_sys_fanotify_init; */
+ /* case 263: return gdb_sys_fanotify_mark; */
+ /* case 264: return gdb_sys_name_to_handle_at; */
+ /* case 265: return gdb_sys_open_by_handle_at; */
+ /* case 266: return gdb_sys_clock_adjtime; */
+ /* case 267: return gdb_sys_syncfs; */
+ /* case 268: return gdb_sys_setns; */
+ /* case 269: return gdb_sys_sendmmsg; */
+ /* case 270: return gdb_sys_process_vm_readv; */
+ /* case 271: return gdb_sys_process_vm_writev; */
+ /* case 272: return gdb_sys_kcmp; */
+ /* case 273: return gdb_sys_finit_module; */
+ /* case 274: return gdb_sys_sched_setattr; */
+ /* case 275: return gdb_sys_sched_getattr; */
+ /* case 276: return gdb_sys_renameat2; */
+ /* case 277: return gdb_sys_seccomp; */
+ case 278: return gdb_sys_getrandom;
+ /* case 279: return gdb_sys_memfd_create; */
+ /* case 280: return gdb_sys_bpf; */
+ /* case 281: return gdb_sys_execveat; */
+ /* case 282: return gdb_sys_userfaultfd; */
+ /* case 283: return gdb_sys_membarrier; */
+ /* case 284: return gdb_sys_mlock2; */
+ /* case 285: return gdb_sys_copy_file_range; */
+ /* case 286: return gdb_sys_preadv2; */
+ /* case 287: return gdb_sys_pwritev2; */
+ /* case 288: return gdb_sys_pkey_mprotect; */
+ /* case 289: return gdb_sys_pkey_alloc; */
+ /* case 290: return gdb_sys_pkey_free; */
+ case 291: return gdb_sys_statx;
+ /* case 292: return gdb_sys_io_pgetevents; */
+ /* case 293: return gdb_sys_rseq; */
+ /* case 294: return gdb_sys_kexec_file_load; */
+ /* case 424: return gdb_sys_pidfd_send_signal; */
+ /* case 425: return gdb_sys_io_uring_setup; */
+ /* case 426: return gdb_sys_io_uring_enter; */
+ /* case 427: return gdb_sys_io_uring_register; */
+ /* case 428: return gdb_sys_open_tree; */
+ /* case 429: return gdb_sys_move_mount; */
+ /* case 430: return gdb_sys_fsopen; */
+ /* case 431: return gdb_sys_fsconfig; */
+ /* case 432: return gdb_sys_fsmount; */
+ /* case 433: return gdb_sys_fspick; */
+ /* case 434: return gdb_sys_pidfd_open; */
+ /* case 435: return gdb_sys_clone3; */
+ /* case 436: return gdb_sys_close_range; */
+ /* case 437: return gdb_sys_openat2; */
+ /* case 438: return gdb_sys_pidfd_getfd; */
+ /* case 439: return gdb_sys_faccessat2; */
+ /* case 440: return gdb_sys_process_madvise; */
+ /* case 441: return gdb_sys_epoll_pwait2; */
+ /* case 442: return gdb_sys_mount_setattr; */
+ /* case 443: return gdb_sys_quotactl_fd; */
+ /* case 444: return gdb_sys_landlock_create_ruleset; */
+ /* case 445: return gdb_sys_landlock_add_rule; */
+ /* case 446: return gdb_sys_landlock_restrict_self; */
+ /* case 447: return gdb_sys_memfd_secret; */
+ /* case 448: return gdb_sys_process_mrelease; */
+ /* case 449: return gdb_sys_futex_waitv; */
+ /* case 450: return gdb_sys_set_mempolicy_home_node; */
+ /* case 451: return gdb_sys_cachestat; */
+ /* case 452: return gdb_sys_fchmodat2; */
+ /* case 453: return gdb_sys_map_shadow_stack; */
+ /* case 454: return gdb_sys_futex_wake; */
+ /* case 455: return gdb_sys_futex_wait; */
+ /* case 456: return gdb_sys_futex_requeue; */
+ /* case 457: return gdb_sys_statmount; */
+ /* case 458: return gdb_sys_listmount; */
+ /* case 459: return gdb_sys_lsm_get_self_attr; */
+ /* case 460: return gdb_sys_lsm_set_self_attr; */
+ /* case 461: return gdb_sys_lsm_list_modules; */
+ /* case 462: return gdb_sys_mseal; */
+ /* case 463: return gdb_sys_setxattrat; */
+ /* case 464: return gdb_sys_getxattrat; */
+ /* case 465: return gdb_sys_listxattrat; */
+ /* case 466: return gdb_sys_removexattrat; */
+ default:
+ return gdb_sys_no_syscall;
+ }
+}
diff --git a/gdb/riscv-linux-tdep.c b/gdb/riscv-linux-tdep.c
index 4c0c65c1457..f21039a613c 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 00000000000..9dd9e373caa
--- /dev/null
+++ b/gdb/riscv-linux-tdep.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2024-2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef GDB_RISCV_LINUX_TDEP_H
+#define GDB_RISCV_LINUX_TDEP_H
+
+#include "linux-record.h"
+
+/* riscv64_canonicalize_syscall maps from the native riscv Linux set
+ of syscall ids into a canonical set of syscall ids used by
+ process record. */
+
+extern enum gdb_syscall riscv64_canonicalize_syscall (int syscall);
+
+#endif /* GDB_RISCV_LINUX_TDEP_H */
diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
index 91f6dffebe1..19367ff55ba 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 @@ class riscv_insn
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 @@ class riscv_insn
m_rs2 = decode_register_index_short (ival, OP_SH_CRS2S);
}
- /* Fetch instruction from target memory at ADDR, return the content of
- the instruction, and update LEN with the instruction length. */
- static ULONGEST fetch_instruction (struct gdbarch *gdbarch,
- CORE_ADDR addr, int *len);
-
/* The length of the instruction in bytes. Should be 2 or 4. */
int m_length;
@@ -4433,6 +4436,9 @@ riscv_gdbarch_init (struct gdbarch_info info,
set_gdbarch_stap_register_indirection_suffixes
(gdbarch, stap_register_indirection_suffixes);
+ /* Process record-replay */
+ set_gdbarch_process_record (gdbarch, riscv_process_record);
+
/* Hook in OS ABI-specific overrides, if they have been registered. */
gdbarch_init_osabi (info, gdbarch);
@@ -4866,3 +4872,673 @@ equivalent change in the disassembler output."),
&setriscvcmdlist,
&showriscvcmdlist);
}
+
+/* A wrapper to read register under number regnum to address addr.
+ Returns false if error happened and makes warning. */
+
+static bool
+try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
+{
+ gdb_assert (regcache != nullptr);
+
+ if (regcache->raw_read (regnum, &addr)
+ != register_status::REG_VALID)
+ {
+ warning (_("Can not read at address %lx"), addr);
+ return false;
+ }
+ return true;
+}
+
+/* Helper class to record instruction. */
+
+class riscv_recorded_insn final
+{
+public:
+ /* Type for saved register. */
+ using regnum_type = int;
+ /* Type for saved memory. First is address, second is length. */
+ using memory_type = std::pair<CORE_ADDR, int>;
+
+ /* Enum class that represents which type does recording belong to. */
+ enum class record_type
+ {
+ UNKNOWN,
+ ORDINARY,
+
+ /* Corner cases. */
+ ECALL,
+ EBREAK,
+ };
+
+private:
+ /* Type for set of registers that need to be saved. */
+ using recorded_regs = std::vector<regnum_type>;
+ /* Type for set of memory records that need to be saved. */
+ using recorded_mems = std::vector<memory_type>;
+
+ /* Type for memory address, extracted from memory_type. */
+ using mem_addr = decltype (std::declval<memory_type> ().first);
+ /* Type for memory length, extracted from memory_type. */
+ using mem_len = decltype (std::declval<memory_type> ().second);
+
+ /* Record type of current instruction. */
+ record_type m_record_type = record_type::UNKNOWN;
+
+ /* Flag that represents was there an error in current recording. */
+ bool m_error_occured = false;
+
+ /* Set of registers that need to be recorded. */
+ recorded_regs m_regs;
+ /* Set of memory chunks that need to be recorded. */
+ recorded_mems m_mems;
+
+ /* Width in bytes of the general purpose registers for GDBARCH,
+ where recording is happening. */
+ int m_xlen = 0;
+
+ /* Helper for decode 16-bit instruction RS1. */
+ static regnum_type
+ decode_crs1_short (ULONGEST opcode) noexcept
+ {
+ return ((opcode >> OP_SH_CRS1S) & OP_MASK_CRS1S) + 8;
+ }
+
+ /* Helper for decode 16-bit instruction RS2. */
+ static regnum_type
+ decode_crs2_short (ULONGEST opcode) noexcept
+ {
+ return ((opcode >> OP_SH_CRS2S) & OP_MASK_CRS2S) + 8;
+ }
+
+ /* Helper for decode 16-bit instruction CRS1. */
+ static regnum_type
+ decode_crs1 (ULONGEST opcode) noexcept
+ {
+ return ((opcode >> OP_SH_RD) & OP_MASK_RD);
+ }
+
+ /* Helper for decode 16-bit instruction CRS2. */
+ static regnum_type
+ decode_crs2 (ULONGEST opcode) noexcept
+ {
+ return ((opcode >> OP_SH_CRS2) & OP_MASK_CRS2);
+ }
+
+ /* Helper for decode 32-bit instruction RD. */
+ static regnum_type
+ decode_rd (ULONGEST ival) noexcept
+ {
+ return (ival >> OP_SH_RD) & OP_MASK_RD;
+ }
+
+ /* Helper for decode 32-bit instruction RS1. */
+ static regnum_type
+ decode_rs1 (ULONGEST ival) noexcept
+ {
+ return (ival >> OP_SH_RS1) & OP_MASK_RS1;
+ }
+
+ /* Helper for decode 32-bit instruction RS2. */
+ static regnum_type
+ decode_rs2 (ULONGEST ival) noexcept
+ {
+ return (ival >> OP_SH_RS2) & OP_MASK_RS2;
+ }
+
+ /* Helper for decode 32-bit instruction CSR. */
+ static regnum_type
+ decode_csr (ULONGEST ival) noexcept
+ {
+ return (ival >> OP_SH_CSR) & OP_MASK_CSR;
+ }
+
+ /* Set ordinary record type. Always returns true. */
+ bool
+ set_ordinary_record_type () noexcept
+ {
+ m_record_type = record_type::ORDINARY;
+ return true;
+ }
+
+ /* Set error happened. Always returns false. */
+ bool
+ set_error () noexcept
+ {
+ m_error_occured = true;
+ return false;
+ }
+
+ /* Check if current recording has an error. */
+ bool
+ has_error () const noexcept
+ {
+ return m_error_occured;
+ }
+
+ /* Reads register. Sets error and returns false if error happened. */
+ bool
+ read_reg (struct regcache *regcache, regnum_type reg,
+ ULONGEST &addr) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ if (!try_read (regcache, reg, addr))
+ return set_error ();
+ return true;
+ }
+
+ /* Save register. Returns true or aborts if exception happened. */
+ bool
+ save_reg (regnum_type regnum) noexcept
+ {
+ m_regs.emplace_back (regnum);
+ return true;
+ }
+
+ /* Save memory chunk. Returns true or aborts if exception happened. */
+ bool
+ save_mem (mem_addr addr, mem_len len) noexcept
+ {
+ m_mems.emplace_back (addr, len);
+ return true;
+ }
+
+ /* Returns true if instruction needs only saving pc. */
+ static bool
+ need_save_pc (ULONGEST ival) noexcept
+ {
+ return (is_beq_insn (ival) || is_bne_insn (ival) || is_blt_insn (ival)
+ || is_bge_insn (ival) || is_bltu_insn (ival) || is_bgeu_insn (ival)
+ || is_fence_insn (ival) || is_pause_insn (ival)
+ || is_fence_i_insn (ival));
+ }
+
+ /* Returns true if instruction is classified. */
+ bool
+ try_save_pc (ULONGEST ival) noexcept
+ {
+ if (!need_save_pc (ival))
+ return false;
+
+ return set_ordinary_record_type ();
+ }
+
+ /* Returns true if instruction needs only saving pc and rd. */
+ static bool
+ need_save_pc_rd (ULONGEST ival) noexcept
+ {
+ return (is_lui_insn (ival) || is_auipc_insn (ival) || is_jal_insn (ival)
+ || is_jalr_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
+ || is_lw_insn (ival) || is_lbu_insn (ival) || is_lhu_insn (ival)
+ || is_addi_insn (ival) || is_slti_insn (ival)
+ || is_sltiu_insn (ival) || is_xori_insn (ival) || is_ori_insn (ival)
+ || is_andi_insn (ival) || is_slli_rv32_insn (ival)
+ || is_srli_rv32_insn (ival) || is_srai_rv32_insn (ival)
+ || is_add_insn (ival) || is_sub_insn (ival) || is_sll_insn (ival)
+ || is_slt_insn (ival) || is_sltu_insn (ival) || is_xor_insn (ival)
+ || is_srl_insn (ival) || is_sra_insn (ival) || is_or_insn (ival)
+ || is_and_insn (ival) || is_lwu_insn (ival) || is_ld_insn (ival)
+ || is_slli_insn (ival) || is_srli_insn (ival) || is_srai_insn (ival)
+ || is_addiw_insn (ival) || is_slliw_insn (ival)
+ || is_srliw_insn (ival) || is_sraiw_insn (ival)
+ || is_addw_insn (ival) || is_subw_insn (ival) || is_sllw_insn (ival)
+ || is_srlw_insn (ival) || is_sraw_insn (ival) || is_mul_insn (ival)
+ || is_mulh_insn (ival) || is_mulhsu_insn (ival)
+ || is_mulhu_insn (ival) || is_div_insn (ival) || is_divu_insn (ival)
+ || is_rem_insn (ival) || is_remu_insn (ival) || is_mulw_insn (ival)
+ || is_divw_insn (ival) || is_divuw_insn (ival)
+ || is_remw_insn (ival) || is_remuw_insn (ival)
+ || is_lr_w_insn (ival) || is_lr_d_insn (ival)
+ || is_fcvt_w_s_insn (ival) || is_fcvt_wu_s_insn (ival)
+ || is_fmv_x_s_insn (ival) || is_feq_s_insn (ival)
+ || is_flt_s_insn (ival) || is_fle_s_insn (ival)
+ || is_fclass_s_insn (ival) || is_fcvt_l_s_insn (ival)
+ || is_fcvt_lu_s_insn (ival) || is_feq_d_insn (ival)
+ || is_flt_d_insn (ival) || is_fle_d_insn (ival)
+ || is_fclass_d_insn (ival) || is_fcvt_w_d_insn (ival)
+ || is_fcvt_wu_d_insn (ival) || is_fcvt_l_d_insn (ival)
+ || is_fcvt_lu_d_insn (ival) || is_fmv_x_d_insn (ival));
+ }
+
+ /* Returns true if instruction is classified. This function can set
+ m_error_occured. */
+ bool
+ try_save_pc_rd (ULONGEST ival) noexcept
+ {
+ if (!need_save_pc_rd (ival))
+ return false;
+
+ return (!save_reg (decode_rd (ival)) || set_ordinary_record_type ());
+ }
+
+ /* Returns true if instruction needs only saving pc and
+ floating point rd. */
+ static bool
+ need_save_pc_fprd (ULONGEST ival) noexcept
+ {
+ return (is_flw_insn (ival) || is_fmadd_s_insn (ival)
+ || is_fmsub_s_insn (ival) || is_fnmsub_s_insn (ival)
+ || is_fnmadd_s_insn (ival) || is_fadd_s_insn (ival)
+ || is_fsub_s_insn (ival) || is_fmul_s_insn (ival)
+ || is_fdiv_s_insn (ival) || is_fsqrt_s_insn (ival)
+ || is_fsgnj_s_insn (ival) || is_fsgnjn_s_insn (ival)
+ || is_fsgnjx_s_insn (ival) || is_fmin_s_insn (ival)
+ || is_fmax_s_insn (ival) || is_fcvt_s_w_insn (ival)
+ || is_fcvt_s_wu_insn (ival) || is_fmv_s_x_insn (ival)
+ || is_fcvt_s_l_insn (ival) || is_fcvt_s_lu_insn (ival)
+ || is_fld_insn (ival) || is_fmadd_d_insn (ival)
+ || is_fmsub_d_insn (ival) || is_fnmsub_d_insn (ival)
+ || is_fnmadd_d_insn (ival) || is_fadd_d_insn (ival)
+ || is_fsub_d_insn (ival) || is_fmul_d_insn (ival)
+ || is_fdiv_d_insn (ival) || is_fsqrt_d_insn (ival)
+ || is_fsgnj_d_insn (ival) || is_fsgnjn_d_insn (ival)
+ || is_fsgnjx_d_insn (ival) || is_fmin_d_insn (ival)
+ || is_fmax_d_insn (ival) || is_fcvt_s_d_insn (ival)
+ || is_fcvt_d_s_insn (ival) || is_fcvt_d_w_insn (ival)
+ || is_fcvt_d_wu_insn (ival) || is_fcvt_d_l_insn (ival)
+ || is_fcvt_d_lu_insn (ival) || is_fmv_d_x_insn (ival));
+ }
+
+ /* Returns true if instruction is classified. This function can set
+ m_error_occured. */
+ bool
+ try_save_pc_fprd (ULONGEST ival) noexcept
+ {
+ if (!need_save_pc_fprd (ival))
+ return false;
+
+ return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_rd (ival))
+ || set_ordinary_record_type ());
+ }
+
+ /* Returns true if instruction needs only saving pc, rd and csr. */
+ static bool
+ need_save_pc_rd_csr (ULONGEST ival) noexcept
+ {
+ return (is_csrrw_insn (ival) || is_csrrs_insn (ival) || is_csrrc_insn (ival)
+ || is_csrrwi_insn (ival) || is_csrrsi_insn (ival)
+ || is_csrrc_insn (ival));
+ }
+
+ /* Returns true if instruction is classified. This function can set
+ m_error_occured. */
+ bool
+ try_save_pc_rd_csr (ULONGEST ival) noexcept
+ {
+ if (!need_save_pc_rd_csr (ival))
+ return false;
+
+ return (!save_reg (decode_rd (ival))
+ || !save_reg (RISCV_FIRST_CSR_REGNUM + decode_csr (ival))
+ || set_ordinary_record_type ());
+ }
+
+ /* Returns the size of the memory chunk that needs to be saved if the
+ instruction belongs to the group that needs only saving pc and memory.
+ Otherwise returns 0. */
+ static mem_len
+ need_save_pc_mem (ULONGEST ival) noexcept
+ {
+ if (is_sb_insn (ival))
+ return 1;
+ if (is_sh_insn (ival))
+ return 2;
+ if (is_sw_insn (ival) || is_fsw_insn (ival))
+ return 4;
+ if (is_sd_insn (ival) || is_fsd_insn (ival))
+ return 8;
+ return 0;
+ }
+
+ /* Returns true if instruction is classified. This function can set
+ m_error_occured. */
+ bool
+ try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ mem_addr addr = mem_addr{};
+ mem_len len = need_save_pc_mem (ival);
+ if (len <= 0)
+ return false;
+
+ mem_len offset = EXTRACT_STYPE_IMM (ival);
+ return (!read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr + offset, len) || set_ordinary_record_type ());
+ }
+
+ /* Returns the size of the memory chunk that needs to be saved if the
+ instruction belongs to the group that needs only saving pc, rd and memory.
+ Otherwise returns 0. */
+ static mem_len
+ need_save_pc_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_sc_w_insn (ival) || is_amoswap_w_insn (ival)
+ || is_amoadd_w_insn (ival) || is_amoxor_w_insn (ival)
+ || is_amoand_w_insn (ival) || is_amoor_w_insn (ival)
+ || is_amomin_w_insn (ival) || is_amomax_w_insn (ival)
+ || is_amominu_w_insn (ival) || is_amomaxu_w_insn (ival))
+ return 4;
+ if (is_sc_d_insn (ival) || is_amoswap_d_insn (ival)
+ || is_amoadd_d_insn (ival) || is_amoxor_d_insn (ival)
+ || is_amoand_d_insn (ival) || is_amoor_d_insn (ival)
+ || is_amomin_d_insn (ival) || is_amomax_d_insn (ival)
+ || is_amominu_d_insn (ival) || is_amomaxu_d_insn (ival))
+ return 8;
+ return 0;
+ }
+
+ /* Returns true if instruction is classified. This function can set
+ m_error_occured. */
+ bool
+ try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ mem_len len = need_save_pc_rd_mem (ival);
+ mem_addr addr = 0;
+ if (len <= 0)
+ return false;
+
+ return (!read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr, len) || !save_reg (decode_rd (ival))
+ || set_ordinary_record_type ());
+ }
+
+ /* Returns true if instruction is successfully recordered. The length of
+ the instruction must be equal 4 bytes. */
+ bool
+ record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ if (is_ecall_insn (ival))
+ {
+ m_record_type = record_type::ECALL;
+ return true;
+ }
+
+ if (is_ebreak_insn (ival))
+ {
+ m_record_type = record_type::EBREAK;
+ return true;
+ }
+
+ if (try_save_pc (ival) || try_save_pc_rd (ival) || try_save_pc_fprd (ival)
+ || try_save_pc_rd_csr (ival) || try_save_pc_mem (ival, regcache)
+ || try_save_pc_rd_mem (ival, regcache))
+ return !has_error ();
+
+ warning (_("Currently this instruction with len 4(%lx) is unsupported"),
+ ival);
+ return false;
+ }
+
+ /* Returns true if instruction is successfully recordered. The length of
+ the instruction must be equal 2 bytes. */
+ bool
+ record_insn_len2 (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ mem_addr addr = mem_addr{};
+
+ /* The order here is very important, because
+ opcodes of some instructions may be the same. */
+
+ if (is_c_addi4spn_insn (ival) || is_c_lw_insn (ival)
+ || (m_xlen == 8 && is_c_ld_insn (ival)))
+ return (!save_reg (decode_crs2_short (ival))
+ || set_ordinary_record_type ());
+
+ if (is_c_fld_insn (ival) || (m_xlen == 4 && is_c_flw_insn (ival)))
+ return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival))
+ || set_ordinary_record_type ());
+
+ if (is_c_fsd_insn (ival) || (m_xlen == 8 && is_c_sd_insn (ival)))
+ {
+ ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LD_IMM (ival)};
+ return (!read_reg (regcache, decode_crs1_short (ival), addr)
+ || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
+ }
+
+ if ((m_xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
+ {
+ ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LW_IMM (ival)};
+ return (!read_reg (regcache, decode_crs1_short (ival), addr)
+ || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
+ }
+
+ if (is_c_nop_insn (ival))
+ return set_ordinary_record_type ();
+
+ if (is_c_addi_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (m_xlen == 4 && is_c_jal_insn (ival))
+ return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
+
+ if ((m_xlen == 8 && is_c_addiw_insn (ival)) || is_c_li_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_addi16sp_insn (ival))
+ return (!save_reg (RISCV_SP_REGNUM) || set_ordinary_record_type ());
+
+ if (is_c_lui_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_srli_insn (ival) || is_c_srai_insn (ival) || is_c_andi_insn (ival)
+ || is_c_sub_insn (ival) || is_c_xor_insn (ival) || is_c_or_insn (ival)
+ || is_c_and_insn (ival) || (m_xlen == 8 && is_c_subw_insn (ival))
+ || (m_xlen == 8 && is_c_addw_insn (ival)))
+ return (!save_reg (decode_crs1_short (ival))
+ || set_ordinary_record_type ());
+
+ if (is_c_j_insn (ival) || is_c_beqz_insn (ival) || is_c_bnez_insn (ival))
+ return set_ordinary_record_type ();
+
+ if (is_c_slli_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_fldsp_insn (ival) || (m_xlen == 4 && is_c_flwsp_insn (ival)))
+ return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival))
+ || set_ordinary_record_type ());
+
+ if (is_c_lwsp_insn (ival) || (m_xlen == 8 && is_c_ldsp_insn (ival)))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_jr_insn (ival))
+ return set_ordinary_record_type ();
+
+ if (is_c_mv_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_ebreak_insn (ival))
+ {
+ m_record_type = record_type::EBREAK;
+ return true;
+ }
+
+ if (is_c_jalr_insn (ival))
+ return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
+
+ if (is_c_add_insn (ival))
+ return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
+
+ if (is_c_fsdsp_insn (ival) || (m_xlen == 8 && is_c_sdsp_insn (ival)))
+ {
+ ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SDSP_IMM (ival)};
+ return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
+ || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
+ }
+
+ if (is_c_swsp_insn (ival) || (m_xlen == 4 && is_c_fswsp_insn (ival)))
+ {
+ ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SWSP_IMM (ival)};
+ return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
+ || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
+ }
+
+ warning (_("Currently this instruction with len 2(%lx) is unsupported"),
+ ival);
+ return false;
+ }
+
+public:
+ /* Iterator for registers that need to be recorded. */
+ using regs_iter = recorded_regs::const_iterator;
+ /* Iterator for memory chunks that need to be recorded. */
+ using mems_iter = recorded_mems::const_iterator;
+
+ /* Record instruction at address addr. Returns false if error happened. */
+ bool
+ record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
+ {
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ int m_length = 0;
+ m_xlen = riscv_isa_xlen (gdbarch);
+ ULONGEST ival = riscv_insn::fetch_instruction (gdbarch, addr, &m_length);
+ if (!save_reg (RISCV_PC_REGNUM))
+ return false;
+
+ if (m_length == 4)
+ return record_insn_len4 (ival, regcache);
+
+ if (m_length == 2)
+ return record_insn_len2 (ival, regcache);
+
+ /* 6 bytes or more. If the instruction is longer than 8 bytes, we don't
+ have full instruction bits in ival. At least, such long instructions
+ are not defined yet, so just ignore it. */
+ gdb_assert (m_length > 0 && m_length % 2 == 0);
+
+ warning (_("Can not record unknown instruction (opcode = %lx)"), ival);
+ return false;
+ }
+
+ /* Get record type of instruction. */
+ record_type
+ get_record_type () const noexcept
+ {
+ return m_record_type;
+ }
+
+ /* Returns an iterator to the beginning of the registers that need
+ to be saved. */
+ regs_iter
+ regs_begin () const noexcept
+ {
+ return m_regs.begin ();
+ }
+
+ /* Returns an iterator to the end of the registers that need
+ to be saved. */
+ regs_iter
+ regs_end () const noexcept
+ {
+ return m_regs.end ();
+ }
+
+ /* Returns an iterator to the beginning of the memory chunks that need
+ to be saved. */
+ mems_iter
+ mems_begin () const noexcept
+ {
+ return m_mems.begin ();
+ }
+
+ /* Returns an iterator to the end of the memory chunks that need
+ to be saved. */
+ mems_iter
+ mems_end () const noexcept
+ {
+ return m_mems.end ();
+ }
+};
+
+/* A helper function to record instruction using record API. */
+
+static int
+riscv_record_insn_details (struct gdbarch *gdbarch, struct regcache *regcache,
+ const riscv_recorded_insn &insn)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto regs_begin = insn.regs_begin ();
+ auto regs_end = insn.regs_end ();
+ if (std::any_of (regs_begin,
+ regs_end,
+ [®cache] (auto &®_it)
+ {
+ return record_full_arch_list_add_reg (regcache, reg_it);
+ }))
+ return -1;
+
+ auto mems_begin = insn.mems_begin ();
+ auto mems_end = insn.mems_end ();
+ if (std::any_of (mems_begin,
+ mems_end,
+ [] (auto &&mem_it)
+ {
+ return record_full_arch_list_add_mem (mem_it.first,
+ mem_it.second);
+ }))
+ return -1;
+
+ switch (insn.get_record_type ())
+ {
+ case riscv_recorded_insn::record_type::ORDINARY:
+ break;
+
+ case riscv_recorded_insn::record_type::ECALL:
+ {
+ if (!tdep->riscv_syscall_record)
+ {
+ warning (_("Syscall record is not supported"));
+ return -1;
+ }
+ ULONGEST reg_val = ULONGEST{};
+ if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
+ return -1;
+ return tdep->riscv_syscall_record (regcache, reg_val);
+ }
+
+ case riscv_recorded_insn::record_type::EBREAK:
+ break;
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* Parse the current instruction and record the values of the registers and
+ memory that will be changed in current instruction to record_arch_list.
+ Return -1 if something is wrong. */
+
+int
+riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+ CORE_ADDR addr)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_recorded_insn insn;
+ if (!insn.record (gdbarch, regcache, addr))
+ {
+ record_full_arch_list_add_end ();
+ return -1;
+ }
+
+ int ret_val = riscv_record_insn_details (gdbarch, regcache, insn);
+
+ if (record_full_arch_list_add_end ())
+ return -1;
+
+ return ret_val;
+}
diff --git a/gdb/riscv-tdep.h b/gdb/riscv-tdep.h
index ad1e9596b83..2903aefd007 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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
new file mode 100755
index 00000000000..30e52b7e472
--- /dev/null
+++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
@@ -0,0 +1,163 @@
+#!/usr/bin/env python3
+# pylint: disable=invalid-name
+
+# Copyright (C) 2024-2025 Free Software Foundation, Inc.
+# Contributed by Timur Golubovich
+
+# This file is part of GDB.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# To get help message for this script, run:
+# ./gdb/syscalls/riscv-canonicalize-syscall-gen.py --help
+
+# Execution result:
+
+# usage: riscv-canonicalize-syscall-gen.py [-h] -i INPUT
+#
+# Generate file gdb/riscv-canonicalize-syscall-gen.c from path to riscv linux syscalls.
+#
+# options:
+# -h, --help show this help message and exit
+# -i INPUT, --input INPUT
+# path to riscv linux syscalls (glibc/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h)
+
+import argparse
+import re
+import sys
+from pathlib import Path as _Path
+
+head = """\
+/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py
+
+ Copyright (C) 2024-2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "riscv-linux-tdep.h"
+
+/* riscv64_canonicalize_syscall maps from the native riscv 64 Linux set
+ of syscall ids into a canonical set of syscall ids used by
+ process record. */
+
+enum gdb_syscall
+riscv64_canonicalize_syscall (int syscall)
+{
+ switch (syscall)
+ {
+"""
+
+tail = """\
+ default:
+ return gdb_sys_no_syscall;
+ }
+}
+"""
+
+
+class Generator:
+ def _get_gdb_syscalls(self, gdb_syscalls_path: _Path) -> list[str]:
+ gdb_syscalls: list[str] = []
+ with open(gdb_syscalls_path, "r", encoding="UTF-8") as file:
+ lines = file.readlines()
+ for line in lines:
+ match = re.search(r"\s*(?P<name>gdb_sys_[^S]+)\S*=", line)
+ if match:
+ gdb_syscalls.append(match.group("name").strip())
+ return gdb_syscalls
+
+ def _get_canon_syscalls_lines(self, syscalls_path: _Path, gdb_syscalls: list[str]) -> list[str]:
+ canon_syscalls: dict[int, str] = {}
+ with open(syscalls_path, "r", encoding="UTF-8") as file:
+ lines = file.readlines()
+ for line in lines:
+ match = re.match(r"#define\s+__NR_(?P<name>[^\s]+)\s+(?P<number>\d+)", line)
+ if match:
+ syscall_name = match.group("name")
+ syscall_num = int(match.group("number"))
+ gdb_syscall_name = f"gdb_sys_{syscall_name}"
+ if gdb_syscall_name in gdb_syscalls:
+ value = f" case {syscall_num}: return {gdb_syscall_name};\n"
+ canon_syscalls[syscall_num] = value
+ # this is a place for corner cases
+ elif syscall_name == "mmap":
+ gdb_old_syscall_name = "gdb_old_mmap"
+ value = f" case {syscall_num}: return {gdb_old_syscall_name};\n"
+ canon_syscalls[syscall_num] = value
+ else:
+ value = f" /* case {syscall_num}: return {gdb_syscall_name}; */\n"
+ canon_syscalls[syscall_num] = value
+ return [canon_syscalls[syscall_num] for syscall_num in sorted(canon_syscalls)]
+
+ def generate(self, syscalls_path: _Path) -> None:
+ repo_path = _Path(__file__).parent.parent.parent
+ gdb_syscalls_path = repo_path / "gdb" / "linux-record.h"
+ canon_syscalls_path = repo_path / "gdb" / "riscv-canonicalize-syscall-gen.c"
+
+ gdb_syscalls = self._get_gdb_syscalls(gdb_syscalls_path)
+ canon_syscalls_lines = self._get_canon_syscalls_lines(syscalls_path, gdb_syscalls)
+
+ with open(canon_syscalls_path, "w", encoding="UTF-8") as file:
+ file.writelines(head)
+ file.writelines(canon_syscalls_lines)
+ file.writelines(tail)
+
+
+help_message = """\
+Generate file gdb/riscv-canonicalize-syscall-gen.c
+from path to riscv linux syscalls.
+"""
+
+
+def setup_parser() -> argparse.ArgumentParser:
+ parser = argparse.ArgumentParser(description=help_message)
+ parser.add_argument(
+ "-i",
+ "--input",
+ type=_Path,
+ required=True,
+ help="path to riscv linux syscalls (glibc/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h)",
+ )
+ return parser
+
+
+def main(argv: list[str]) -> int:
+ try:
+ parser = setup_parser()
+ args = parser.parse_args(argv)
+ generator = Generator()
+ generator.generate(args.input)
+ return 0
+ except RuntimeError as e:
+ print(str(e))
+ return -1
+
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv[1:]))
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index c37cc8974e2..0149125e60a 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*-*-linux*"] } {
return 1
}
--
2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* Re: [PATCH v8] This commit adds record full support for rv64gc instruction set
2025-04-10 11:55 ` Timur
@ 2025-04-14 7:40 ` Andrew Burgess
2025-04-14 8:44 ` Timur Golubovich
0 siblings, 1 reply; 35+ messages in thread
From: Andrew Burgess @ 2025-04-14 7:40 UTC (permalink / raw)
To: Timur, gdb-patches; +Cc: Timur, Guinevere Larsen
Timur <timurgol007@gmail.com> writes:
> It includes changes to the following files:
> - gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
> syscalls.
> - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
> rv64gc instructions.
> - gdb/configure.tgt: adds new files for compilation.
> - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
> targets.
> - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
> function that canonicalizes RISC-V syscall. This script can simplify support
> for syscalls on rv32 and rv64 system (currently support only for rv64). To
> use this script you need to pass a path to a file with syscalls description
> from riscv-glibc (example is in the help message). The script produces a
> mapping from syscall names to gdb_syscall enum.
> - gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
> - gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
> - gdb/NEWS: notification of new functionality.
>
> Approved-By: Guinevere Larsen <guinevere@redhat.com>
>
> Hi Andrew! Thank you for review.
>
>> I guess riscv-glibc is a fork of glibc that is running ahead of the
>> upstream glibc project?
>>
>> Is there any reason that this cannot work with the official glibc
>> project? That would be preferable I think unless there's a compelling
>> reason, which ideally we'd document.
>>
>> I tried running the script using current HEAD of glibc, the only
>> significant change is:
>>
>> diff --git i/gdb/riscv-canonicalize-syscall-gen.c w/gdb/riscv-canonicalize-syscall-gen.c
>> index 3749fc32f33..03579984f4a 100644
>> --- i/gdb/riscv-canonicalize-syscall-gen.c
>> +++ w/gdb/riscv-canonicalize-syscall-gen.c
>> @@ -270,7 +270,7 @@ riscv64_canonicalize_syscall (int syscall)
>> 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 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; */
>>
>> There was also a bunch of new syscalls that are added, but commented
>> out. I haven't tried to figure out if the change above is a bad thing
>> or not though, maybe you could advise.
>
> Yes, I agree with you, I just used riscv-glibc as I had aldready downloaded it
> and unfortunately the version was pretty old (I started working on this patch
> almost half a year ago).
>
>> This entry needs to move to the correct section of the NEWS file. If
>> you look for the lines starting '***' you'll see that the first header
>> is '*** Changes since GDB 16', this is the section where you should
>> place your entry, probably just before the '* New commands' line.
>> These include guards need to be renamed to GDB_RISCV_LINUX_TDEP_H. You
>> can do this automatically if you like with:
>>
>> ./gdb/check-include-guards.py --update gdb/*.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 */
>
> Addressed
>
>>@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
>>> || [istarget "i\[34567\]86-*-linux*"]
>>> || [istarget "aarch64*-*-linux*"]
>>> || [istarget "powerpc*-*-linux*"]
>>> - || [istarget "s390*-*-linux*"] } {
>>> + || [istarget "s390*-*-linux*"]
>>> + || [istarget "riscv*-*-*"] } {
>>
>> I notice that every other entry in this list restricts the match to
>> Linux based targets. Is RISC-V really different here? Or should RISC-V
>> be similarly restricted?
>
> Addressed, this was just my misprint.
>
> Hope that now everything is okay, wait for your feedback.
I scanned the fixes, they look good. I haven't looked at the rest of
the patch in detail, assuming it hasn't changed from the previous
version:
Approved-By: Andrew Burgess <aburgess@redhat.com>
Thanks,
Andrew
>
> ---
> gdb/NEWS | 2 +
> gdb/configure.tgt | 5 +-
> gdb/doc/gdb.texinfo | 2 +-
> gdb/riscv-canonicalize-syscall-gen.c | 358 +++++++++
> gdb/riscv-linux-tdep.c | 256 +++++++
> gdb/riscv-linux-tdep.h | 29 +
> gdb/riscv-tdep.c | 686 +++++++++++++++++-
> gdb/riscv-tdep.h | 16 +-
> .../riscv-canonicalize-syscall-gen.py | 163 +++++
> gdb/testsuite/lib/gdb.exp | 3 +-
> 10 files changed, 1510 insertions(+), 10 deletions(-)
> create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
> create mode 100644 gdb/riscv-linux-tdep.h
> create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index 99ec392d4c4..1a05a9f9251 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/configure.tgt b/gdb/configure.tgt
> index 18a15c032c3..44da2257be8 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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index b251c8e1228..73f3b14cf74 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -7807,7 +7807,7 @@ 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
> +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}.
>
> diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
> new file mode 100644
> index 00000000000..03579984f4a
> --- /dev/null
> +++ b/gdb/riscv-canonicalize-syscall-gen.c
> @@ -0,0 +1,358 @@
> +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py
> +
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> +
> + This file is part of GDB.
> +
> + This program is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License as published by
> + the Free Software Foundation; either version 3 of the License, or
> + (at your option) any later version.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +#include "defs.h"
> +#include "riscv-linux-tdep.h"
> +
> +/* riscv64_canonicalize_syscall maps from the native riscv 64 Linux set
> + of syscall ids into a canonical set of syscall ids used by
> + process record. */
> +
> +enum gdb_syscall
> +riscv64_canonicalize_syscall (int syscall)
> +{
> + switch (syscall)
> + {
> + case 0: return gdb_sys_io_setup;
> + case 1: return gdb_sys_io_destroy;
> + case 2: return gdb_sys_io_submit;
> + case 3: return gdb_sys_io_cancel;
> + case 4: return gdb_sys_io_getevents;
> + case 5: return gdb_sys_setxattr;
> + case 6: return gdb_sys_lsetxattr;
> + case 7: return gdb_sys_fsetxattr;
> + case 8: return gdb_sys_getxattr;
> + case 9: return gdb_sys_lgetxattr;
> + case 10: return gdb_sys_fgetxattr;
> + case 11: return gdb_sys_listxattr;
> + case 12: return gdb_sys_llistxattr;
> + case 13: return gdb_sys_flistxattr;
> + case 14: return gdb_sys_removexattr;
> + case 15: return gdb_sys_lremovexattr;
> + case 16: return gdb_sys_fremovexattr;
> + case 17: return gdb_sys_getcwd;
> + case 18: return gdb_sys_lookup_dcookie;
> + case 19: return gdb_sys_eventfd2;
> + case 20: return gdb_sys_epoll_create1;
> + case 21: return gdb_sys_epoll_ctl;
> + case 22: return gdb_sys_epoll_pwait;
> + case 23: return gdb_sys_dup;
> + case 24: return gdb_sys_dup3;
> + case 25: return gdb_sys_fcntl;
> + case 26: return gdb_sys_inotify_init1;
> + case 27: return gdb_sys_inotify_add_watch;
> + case 28: return gdb_sys_inotify_rm_watch;
> + case 29: return gdb_sys_ioctl;
> + case 30: return gdb_sys_ioprio_set;
> + case 31: return gdb_sys_ioprio_get;
> + case 32: return gdb_sys_flock;
> + case 33: return gdb_sys_mknodat;
> + case 34: return gdb_sys_mkdirat;
> + case 35: return gdb_sys_unlinkat;
> + case 36: return gdb_sys_symlinkat;
> + case 37: return gdb_sys_linkat;
> + /* case 39: return gdb_sys_umount2; */
> + case 40: return gdb_sys_mount;
> + case 41: return gdb_sys_pivot_root;
> + case 42: return gdb_sys_nfsservctl;
> + case 43: return gdb_sys_statfs;
> + case 44: return gdb_sys_fstatfs;
> + case 45: return gdb_sys_truncate;
> + case 46: return gdb_sys_ftruncate;
> + case 47: return gdb_sys_fallocate;
> + case 48: return gdb_sys_faccessat;
> + case 49: return gdb_sys_chdir;
> + case 50: return gdb_sys_fchdir;
> + case 51: return gdb_sys_chroot;
> + case 52: return gdb_sys_fchmod;
> + case 53: return gdb_sys_fchmodat;
> + case 54: return gdb_sys_fchownat;
> + case 55: return gdb_sys_fchown;
> + case 56: return gdb_sys_openat;
> + case 57: return gdb_sys_close;
> + case 58: return gdb_sys_vhangup;
> + case 59: return gdb_sys_pipe2;
> + case 60: return gdb_sys_quotactl;
> + case 61: return gdb_sys_getdents64;
> + case 62: return gdb_sys_lseek;
> + case 63: return gdb_sys_read;
> + case 64: return gdb_sys_write;
> + case 65: return gdb_sys_readv;
> + case 66: return gdb_sys_writev;
> + case 67: return gdb_sys_pread64;
> + case 68: return gdb_sys_pwrite64;
> + /* case 69: return gdb_sys_preadv; */
> + /* case 70: return gdb_sys_pwritev; */
> + case 71: return gdb_sys_sendfile;
> + case 72: return gdb_sys_pselect6;
> + case 73: return gdb_sys_ppoll;
> + /* case 74: return gdb_sys_signalfd4; */
> + case 75: return gdb_sys_vmsplice;
> + case 76: return gdb_sys_splice;
> + case 77: return gdb_sys_tee;
> + case 78: return gdb_sys_readlinkat;
> + case 79: return gdb_sys_newfstatat;
> + case 80: return gdb_sys_fstat;
> + case 81: return gdb_sys_sync;
> + case 82: return gdb_sys_fsync;
> + case 83: return gdb_sys_fdatasync;
> + case 84: return gdb_sys_sync_file_range;
> + /* case 85: return gdb_sys_timerfd_create; */
> + /* case 86: return gdb_sys_timerfd_settime; */
> + /* case 87: return gdb_sys_timerfd_gettime; */
> + /* case 88: return gdb_sys_utimensat; */
> + case 89: return gdb_sys_acct;
> + case 90: return gdb_sys_capget;
> + case 91: return gdb_sys_capset;
> + case 92: return gdb_sys_personality;
> + case 93: return gdb_sys_exit;
> + case 94: return gdb_sys_exit_group;
> + case 95: return gdb_sys_waitid;
> + case 96: return gdb_sys_set_tid_address;
> + case 97: return gdb_sys_unshare;
> + case 98: return gdb_sys_futex;
> + case 99: return gdb_sys_set_robust_list;
> + case 100: return gdb_sys_get_robust_list;
> + case 101: return gdb_sys_nanosleep;
> + case 102: return gdb_sys_getitimer;
> + case 103: return gdb_sys_setitimer;
> + case 104: return gdb_sys_kexec_load;
> + case 105: return gdb_sys_init_module;
> + case 106: return gdb_sys_delete_module;
> + case 107: return gdb_sys_timer_create;
> + case 108: return gdb_sys_timer_gettime;
> + case 109: return gdb_sys_timer_getoverrun;
> + case 110: return gdb_sys_timer_settime;
> + case 111: return gdb_sys_timer_delete;
> + case 112: return gdb_sys_clock_settime;
> + case 113: return gdb_sys_clock_gettime;
> + case 114: return gdb_sys_clock_getres;
> + case 115: return gdb_sys_clock_nanosleep;
> + case 116: return gdb_sys_syslog;
> + case 117: return gdb_sys_ptrace;
> + case 118: return gdb_sys_sched_setparam;
> + case 119: return gdb_sys_sched_setscheduler;
> + case 120: return gdb_sys_sched_getscheduler;
> + case 121: return gdb_sys_sched_getparam;
> + case 122: return gdb_sys_sched_setaffinity;
> + case 123: return gdb_sys_sched_getaffinity;
> + case 124: return gdb_sys_sched_yield;
> + case 125: return gdb_sys_sched_get_priority_max;
> + case 126: return gdb_sys_sched_get_priority_min;
> + case 127: return gdb_sys_sched_rr_get_interval;
> + case 128: return gdb_sys_restart_syscall;
> + case 129: return gdb_sys_kill;
> + case 130: return gdb_sys_tkill;
> + case 131: return gdb_sys_tgkill;
> + case 132: return gdb_sys_sigaltstack;
> + case 133: return gdb_sys_rt_sigsuspend;
> + case 134: return gdb_sys_rt_sigaction;
> + case 135: return gdb_sys_rt_sigprocmask;
> + case 136: return gdb_sys_rt_sigpending;
> + case 137: return gdb_sys_rt_sigtimedwait;
> + case 138: return gdb_sys_rt_sigqueueinfo;
> + case 139: return gdb_sys_rt_sigreturn;
> + case 140: return gdb_sys_setpriority;
> + case 141: return gdb_sys_getpriority;
> + case 142: return gdb_sys_reboot;
> + case 143: return gdb_sys_setregid;
> + case 144: return gdb_sys_setgid;
> + case 145: return gdb_sys_setreuid;
> + case 146: return gdb_sys_setuid;
> + case 147: return gdb_sys_setresuid;
> + case 148: return gdb_sys_getresuid;
> + case 149: return gdb_sys_setresgid;
> + case 150: return gdb_sys_getresgid;
> + case 151: return gdb_sys_setfsuid;
> + case 152: return gdb_sys_setfsgid;
> + case 153: return gdb_sys_times;
> + case 154: return gdb_sys_setpgid;
> + case 155: return gdb_sys_getpgid;
> + case 156: return gdb_sys_getsid;
> + case 157: return gdb_sys_setsid;
> + case 158: return gdb_sys_getgroups;
> + case 159: return gdb_sys_setgroups;
> + case 160: return gdb_sys_uname;
> + case 161: return gdb_sys_sethostname;
> + case 162: return gdb_sys_setdomainname;
> + case 163: return gdb_sys_getrlimit;
> + case 164: return gdb_sys_setrlimit;
> + case 165: return gdb_sys_getrusage;
> + case 166: return gdb_sys_umask;
> + case 167: return gdb_sys_prctl;
> + case 168: return gdb_sys_getcpu;
> + case 169: return gdb_sys_gettimeofday;
> + case 170: return gdb_sys_settimeofday;
> + case 171: return gdb_sys_adjtimex;
> + case 172: return gdb_sys_getpid;
> + case 173: return gdb_sys_getppid;
> + case 174: return gdb_sys_getuid;
> + case 175: return gdb_sys_geteuid;
> + case 176: return gdb_sys_getgid;
> + case 177: return gdb_sys_getegid;
> + case 178: return gdb_sys_gettid;
> + case 179: return gdb_sys_sysinfo;
> + case 180: return gdb_sys_mq_open;
> + case 181: return gdb_sys_mq_unlink;
> + case 182: return gdb_sys_mq_timedsend;
> + case 183: return gdb_sys_mq_timedreceive;
> + case 184: return gdb_sys_mq_notify;
> + case 185: return gdb_sys_mq_getsetattr;
> + case 186: return gdb_sys_msgget;
> + case 187: return gdb_sys_msgctl;
> + case 188: return gdb_sys_msgrcv;
> + case 189: return gdb_sys_msgsnd;
> + case 190: return gdb_sys_semget;
> + case 191: return gdb_sys_semctl;
> + case 192: return gdb_sys_semtimedop;
> + case 193: return gdb_sys_semop;
> + case 194: return gdb_sys_shmget;
> + case 195: return gdb_sys_shmctl;
> + case 196: return gdb_sys_shmat;
> + case 197: return gdb_sys_shmdt;
> + case 198: return gdb_sys_socket;
> + case 199: return gdb_sys_socketpair;
> + case 200: return gdb_sys_bind;
> + case 201: return gdb_sys_listen;
> + case 202: return gdb_sys_accept;
> + case 203: return gdb_sys_connect;
> + case 204: return gdb_sys_getsockname;
> + case 205: return gdb_sys_getpeername;
> + case 206: return gdb_sys_sendto;
> + case 207: return gdb_sys_recvfrom;
> + case 208: return gdb_sys_setsockopt;
> + case 209: return gdb_sys_getsockopt;
> + case 210: return gdb_sys_shutdown;
> + case 211: return gdb_sys_sendmsg;
> + case 212: return gdb_sys_recvmsg;
> + case 213: return gdb_sys_readahead;
> + case 214: return gdb_sys_brk;
> + case 215: return gdb_sys_munmap;
> + case 216: return gdb_sys_mremap;
> + case 217: return gdb_sys_add_key;
> + case 218: return gdb_sys_request_key;
> + case 219: return gdb_sys_keyctl;
> + case 220: return gdb_sys_clone;
> + case 221: return gdb_sys_execve;
> + case 222: return gdb_old_mmap;
> + case 223: return gdb_sys_fadvise64;
> + case 224: return gdb_sys_swapon;
> + case 225: return gdb_sys_swapoff;
> + case 226: return gdb_sys_mprotect;
> + case 227: return gdb_sys_msync;
> + case 228: return gdb_sys_mlock;
> + case 229: return gdb_sys_munlock;
> + case 230: return gdb_sys_mlockall;
> + case 231: return gdb_sys_munlockall;
> + case 232: return gdb_sys_mincore;
> + case 233: return gdb_sys_madvise;
> + case 234: return gdb_sys_remap_file_pages;
> + case 235: return gdb_sys_mbind;
> + case 236: return gdb_sys_get_mempolicy;
> + case 237: return gdb_sys_set_mempolicy;
> + case 238: return gdb_sys_migrate_pages;
> + case 239: return gdb_sys_move_pages;
> + /* case 240: return gdb_sys_rt_tgsigqueueinfo; */
> + /* case 241: return gdb_sys_perf_event_open; */
> + case 242: return gdb_sys_accept4;
> + /* case 243: return gdb_sys_recvmmsg; */
> + /* case 258: return gdb_sys_riscv_hwprobe; */
> + /* case 259: return gdb_sys_riscv_flush_icache; */
> + case 260: return gdb_sys_wait4;
> + /* case 261: return gdb_sys_prlimit64; */
> + /* case 262: return gdb_sys_fanotify_init; */
> + /* case 263: return gdb_sys_fanotify_mark; */
> + /* case 264: return gdb_sys_name_to_handle_at; */
> + /* case 265: return gdb_sys_open_by_handle_at; */
> + /* case 266: return gdb_sys_clock_adjtime; */
> + /* case 267: return gdb_sys_syncfs; */
> + /* case 268: return gdb_sys_setns; */
> + /* case 269: return gdb_sys_sendmmsg; */
> + /* case 270: return gdb_sys_process_vm_readv; */
> + /* case 271: return gdb_sys_process_vm_writev; */
> + /* case 272: return gdb_sys_kcmp; */
> + /* case 273: return gdb_sys_finit_module; */
> + /* case 274: return gdb_sys_sched_setattr; */
> + /* case 275: return gdb_sys_sched_getattr; */
> + /* case 276: return gdb_sys_renameat2; */
> + /* case 277: return gdb_sys_seccomp; */
> + case 278: return gdb_sys_getrandom;
> + /* case 279: return gdb_sys_memfd_create; */
> + /* case 280: return gdb_sys_bpf; */
> + /* case 281: return gdb_sys_execveat; */
> + /* case 282: return gdb_sys_userfaultfd; */
> + /* case 283: return gdb_sys_membarrier; */
> + /* case 284: return gdb_sys_mlock2; */
> + /* case 285: return gdb_sys_copy_file_range; */
> + /* case 286: return gdb_sys_preadv2; */
> + /* case 287: return gdb_sys_pwritev2; */
> + /* case 288: return gdb_sys_pkey_mprotect; */
> + /* case 289: return gdb_sys_pkey_alloc; */
> + /* case 290: return gdb_sys_pkey_free; */
> + case 291: return gdb_sys_statx;
> + /* case 292: return gdb_sys_io_pgetevents; */
> + /* case 293: return gdb_sys_rseq; */
> + /* case 294: return gdb_sys_kexec_file_load; */
> + /* case 424: return gdb_sys_pidfd_send_signal; */
> + /* case 425: return gdb_sys_io_uring_setup; */
> + /* case 426: return gdb_sys_io_uring_enter; */
> + /* case 427: return gdb_sys_io_uring_register; */
> + /* case 428: return gdb_sys_open_tree; */
> + /* case 429: return gdb_sys_move_mount; */
> + /* case 430: return gdb_sys_fsopen; */
> + /* case 431: return gdb_sys_fsconfig; */
> + /* case 432: return gdb_sys_fsmount; */
> + /* case 433: return gdb_sys_fspick; */
> + /* case 434: return gdb_sys_pidfd_open; */
> + /* case 435: return gdb_sys_clone3; */
> + /* case 436: return gdb_sys_close_range; */
> + /* case 437: return gdb_sys_openat2; */
> + /* case 438: return gdb_sys_pidfd_getfd; */
> + /* case 439: return gdb_sys_faccessat2; */
> + /* case 440: return gdb_sys_process_madvise; */
> + /* case 441: return gdb_sys_epoll_pwait2; */
> + /* case 442: return gdb_sys_mount_setattr; */
> + /* case 443: return gdb_sys_quotactl_fd; */
> + /* case 444: return gdb_sys_landlock_create_ruleset; */
> + /* case 445: return gdb_sys_landlock_add_rule; */
> + /* case 446: return gdb_sys_landlock_restrict_self; */
> + /* case 447: return gdb_sys_memfd_secret; */
> + /* case 448: return gdb_sys_process_mrelease; */
> + /* case 449: return gdb_sys_futex_waitv; */
> + /* case 450: return gdb_sys_set_mempolicy_home_node; */
> + /* case 451: return gdb_sys_cachestat; */
> + /* case 452: return gdb_sys_fchmodat2; */
> + /* case 453: return gdb_sys_map_shadow_stack; */
> + /* case 454: return gdb_sys_futex_wake; */
> + /* case 455: return gdb_sys_futex_wait; */
> + /* case 456: return gdb_sys_futex_requeue; */
> + /* case 457: return gdb_sys_statmount; */
> + /* case 458: return gdb_sys_listmount; */
> + /* case 459: return gdb_sys_lsm_get_self_attr; */
> + /* case 460: return gdb_sys_lsm_set_self_attr; */
> + /* case 461: return gdb_sys_lsm_list_modules; */
> + /* case 462: return gdb_sys_mseal; */
> + /* case 463: return gdb_sys_setxattrat; */
> + /* case 464: return gdb_sys_getxattrat; */
> + /* case 465: return gdb_sys_listxattrat; */
> + /* case 466: return gdb_sys_removexattrat; */
> + default:
> + return gdb_sys_no_syscall;
> + }
> +}
> diff --git a/gdb/riscv-linux-tdep.c b/gdb/riscv-linux-tdep.c
> index 4c0c65c1457..f21039a613c 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 00000000000..9dd9e373caa
> --- /dev/null
> +++ b/gdb/riscv-linux-tdep.h
> @@ -0,0 +1,29 @@
> +/* Copyright (C) 2024-2025 Free Software Foundation, Inc.
> +
> + This file is part of GDB.
> +
> + This program is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License as published by
> + the Free Software Foundation; either version 3 of the License, or
> + (at your option) any later version.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +#ifndef GDB_RISCV_LINUX_TDEP_H
> +#define GDB_RISCV_LINUX_TDEP_H
> +
> +#include "linux-record.h"
> +
> +/* riscv64_canonicalize_syscall maps from the native riscv Linux set
> + of syscall ids into a canonical set of syscall ids used by
> + process record. */
> +
> +extern enum gdb_syscall riscv64_canonicalize_syscall (int syscall);
> +
> +#endif /* GDB_RISCV_LINUX_TDEP_H */
> diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
> index 91f6dffebe1..19367ff55ba 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 @@ class riscv_insn
> 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 @@ class riscv_insn
> m_rs2 = decode_register_index_short (ival, OP_SH_CRS2S);
> }
>
> - /* Fetch instruction from target memory at ADDR, return the content of
> - the instruction, and update LEN with the instruction length. */
> - static ULONGEST fetch_instruction (struct gdbarch *gdbarch,
> - CORE_ADDR addr, int *len);
> -
> /* The length of the instruction in bytes. Should be 2 or 4. */
> int m_length;
>
> @@ -4433,6 +4436,9 @@ riscv_gdbarch_init (struct gdbarch_info info,
> set_gdbarch_stap_register_indirection_suffixes
> (gdbarch, stap_register_indirection_suffixes);
>
> + /* Process record-replay */
> + set_gdbarch_process_record (gdbarch, riscv_process_record);
> +
> /* Hook in OS ABI-specific overrides, if they have been registered. */
> gdbarch_init_osabi (info, gdbarch);
>
> @@ -4866,3 +4872,673 @@ equivalent change in the disassembler output."),
> &setriscvcmdlist,
> &showriscvcmdlist);
> }
> +
> +/* A wrapper to read register under number regnum to address addr.
> + Returns false if error happened and makes warning. */
> +
> +static bool
> +try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
> +{
> + gdb_assert (regcache != nullptr);
> +
> + if (regcache->raw_read (regnum, &addr)
> + != register_status::REG_VALID)
> + {
> + warning (_("Can not read at address %lx"), addr);
> + return false;
> + }
> + return true;
> +}
> +
> +/* Helper class to record instruction. */
> +
> +class riscv_recorded_insn final
> +{
> +public:
> + /* Type for saved register. */
> + using regnum_type = int;
> + /* Type for saved memory. First is address, second is length. */
> + using memory_type = std::pair<CORE_ADDR, int>;
> +
> + /* Enum class that represents which type does recording belong to. */
> + enum class record_type
> + {
> + UNKNOWN,
> + ORDINARY,
> +
> + /* Corner cases. */
> + ECALL,
> + EBREAK,
> + };
> +
> +private:
> + /* Type for set of registers that need to be saved. */
> + using recorded_regs = std::vector<regnum_type>;
> + /* Type for set of memory records that need to be saved. */
> + using recorded_mems = std::vector<memory_type>;
> +
> + /* Type for memory address, extracted from memory_type. */
> + using mem_addr = decltype (std::declval<memory_type> ().first);
> + /* Type for memory length, extracted from memory_type. */
> + using mem_len = decltype (std::declval<memory_type> ().second);
> +
> + /* Record type of current instruction. */
> + record_type m_record_type = record_type::UNKNOWN;
> +
> + /* Flag that represents was there an error in current recording. */
> + bool m_error_occured = false;
> +
> + /* Set of registers that need to be recorded. */
> + recorded_regs m_regs;
> + /* Set of memory chunks that need to be recorded. */
> + recorded_mems m_mems;
> +
> + /* Width in bytes of the general purpose registers for GDBARCH,
> + where recording is happening. */
> + int m_xlen = 0;
> +
> + /* Helper for decode 16-bit instruction RS1. */
> + static regnum_type
> + decode_crs1_short (ULONGEST opcode) noexcept
> + {
> + return ((opcode >> OP_SH_CRS1S) & OP_MASK_CRS1S) + 8;
> + }
> +
> + /* Helper for decode 16-bit instruction RS2. */
> + static regnum_type
> + decode_crs2_short (ULONGEST opcode) noexcept
> + {
> + return ((opcode >> OP_SH_CRS2S) & OP_MASK_CRS2S) + 8;
> + }
> +
> + /* Helper for decode 16-bit instruction CRS1. */
> + static regnum_type
> + decode_crs1 (ULONGEST opcode) noexcept
> + {
> + return ((opcode >> OP_SH_RD) & OP_MASK_RD);
> + }
> +
> + /* Helper for decode 16-bit instruction CRS2. */
> + static regnum_type
> + decode_crs2 (ULONGEST opcode) noexcept
> + {
> + return ((opcode >> OP_SH_CRS2) & OP_MASK_CRS2);
> + }
> +
> + /* Helper for decode 32-bit instruction RD. */
> + static regnum_type
> + decode_rd (ULONGEST ival) noexcept
> + {
> + return (ival >> OP_SH_RD) & OP_MASK_RD;
> + }
> +
> + /* Helper for decode 32-bit instruction RS1. */
> + static regnum_type
> + decode_rs1 (ULONGEST ival) noexcept
> + {
> + return (ival >> OP_SH_RS1) & OP_MASK_RS1;
> + }
> +
> + /* Helper for decode 32-bit instruction RS2. */
> + static regnum_type
> + decode_rs2 (ULONGEST ival) noexcept
> + {
> + return (ival >> OP_SH_RS2) & OP_MASK_RS2;
> + }
> +
> + /* Helper for decode 32-bit instruction CSR. */
> + static regnum_type
> + decode_csr (ULONGEST ival) noexcept
> + {
> + return (ival >> OP_SH_CSR) & OP_MASK_CSR;
> + }
> +
> + /* Set ordinary record type. Always returns true. */
> + bool
> + set_ordinary_record_type () noexcept
> + {
> + m_record_type = record_type::ORDINARY;
> + return true;
> + }
> +
> + /* Set error happened. Always returns false. */
> + bool
> + set_error () noexcept
> + {
> + m_error_occured = true;
> + return false;
> + }
> +
> + /* Check if current recording has an error. */
> + bool
> + has_error () const noexcept
> + {
> + return m_error_occured;
> + }
> +
> + /* Reads register. Sets error and returns false if error happened. */
> + bool
> + read_reg (struct regcache *regcache, regnum_type reg,
> + ULONGEST &addr) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + if (!try_read (regcache, reg, addr))
> + return set_error ();
> + return true;
> + }
> +
> + /* Save register. Returns true or aborts if exception happened. */
> + bool
> + save_reg (regnum_type regnum) noexcept
> + {
> + m_regs.emplace_back (regnum);
> + return true;
> + }
> +
> + /* Save memory chunk. Returns true or aborts if exception happened. */
> + bool
> + save_mem (mem_addr addr, mem_len len) noexcept
> + {
> + m_mems.emplace_back (addr, len);
> + return true;
> + }
> +
> + /* Returns true if instruction needs only saving pc. */
> + static bool
> + need_save_pc (ULONGEST ival) noexcept
> + {
> + return (is_beq_insn (ival) || is_bne_insn (ival) || is_blt_insn (ival)
> + || is_bge_insn (ival) || is_bltu_insn (ival) || is_bgeu_insn (ival)
> + || is_fence_insn (ival) || is_pause_insn (ival)
> + || is_fence_i_insn (ival));
> + }
> +
> + /* Returns true if instruction is classified. */
> + bool
> + try_save_pc (ULONGEST ival) noexcept
> + {
> + if (!need_save_pc (ival))
> + return false;
> +
> + return set_ordinary_record_type ();
> + }
> +
> + /* Returns true if instruction needs only saving pc and rd. */
> + static bool
> + need_save_pc_rd (ULONGEST ival) noexcept
> + {
> + return (is_lui_insn (ival) || is_auipc_insn (ival) || is_jal_insn (ival)
> + || is_jalr_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
> + || is_lw_insn (ival) || is_lbu_insn (ival) || is_lhu_insn (ival)
> + || is_addi_insn (ival) || is_slti_insn (ival)
> + || is_sltiu_insn (ival) || is_xori_insn (ival) || is_ori_insn (ival)
> + || is_andi_insn (ival) || is_slli_rv32_insn (ival)
> + || is_srli_rv32_insn (ival) || is_srai_rv32_insn (ival)
> + || is_add_insn (ival) || is_sub_insn (ival) || is_sll_insn (ival)
> + || is_slt_insn (ival) || is_sltu_insn (ival) || is_xor_insn (ival)
> + || is_srl_insn (ival) || is_sra_insn (ival) || is_or_insn (ival)
> + || is_and_insn (ival) || is_lwu_insn (ival) || is_ld_insn (ival)
> + || is_slli_insn (ival) || is_srli_insn (ival) || is_srai_insn (ival)
> + || is_addiw_insn (ival) || is_slliw_insn (ival)
> + || is_srliw_insn (ival) || is_sraiw_insn (ival)
> + || is_addw_insn (ival) || is_subw_insn (ival) || is_sllw_insn (ival)
> + || is_srlw_insn (ival) || is_sraw_insn (ival) || is_mul_insn (ival)
> + || is_mulh_insn (ival) || is_mulhsu_insn (ival)
> + || is_mulhu_insn (ival) || is_div_insn (ival) || is_divu_insn (ival)
> + || is_rem_insn (ival) || is_remu_insn (ival) || is_mulw_insn (ival)
> + || is_divw_insn (ival) || is_divuw_insn (ival)
> + || is_remw_insn (ival) || is_remuw_insn (ival)
> + || is_lr_w_insn (ival) || is_lr_d_insn (ival)
> + || is_fcvt_w_s_insn (ival) || is_fcvt_wu_s_insn (ival)
> + || is_fmv_x_s_insn (ival) || is_feq_s_insn (ival)
> + || is_flt_s_insn (ival) || is_fle_s_insn (ival)
> + || is_fclass_s_insn (ival) || is_fcvt_l_s_insn (ival)
> + || is_fcvt_lu_s_insn (ival) || is_feq_d_insn (ival)
> + || is_flt_d_insn (ival) || is_fle_d_insn (ival)
> + || is_fclass_d_insn (ival) || is_fcvt_w_d_insn (ival)
> + || is_fcvt_wu_d_insn (ival) || is_fcvt_l_d_insn (ival)
> + || is_fcvt_lu_d_insn (ival) || is_fmv_x_d_insn (ival));
> + }
> +
> + /* Returns true if instruction is classified. This function can set
> + m_error_occured. */
> + bool
> + try_save_pc_rd (ULONGEST ival) noexcept
> + {
> + if (!need_save_pc_rd (ival))
> + return false;
> +
> + return (!save_reg (decode_rd (ival)) || set_ordinary_record_type ());
> + }
> +
> + /* Returns true if instruction needs only saving pc and
> + floating point rd. */
> + static bool
> + need_save_pc_fprd (ULONGEST ival) noexcept
> + {
> + return (is_flw_insn (ival) || is_fmadd_s_insn (ival)
> + || is_fmsub_s_insn (ival) || is_fnmsub_s_insn (ival)
> + || is_fnmadd_s_insn (ival) || is_fadd_s_insn (ival)
> + || is_fsub_s_insn (ival) || is_fmul_s_insn (ival)
> + || is_fdiv_s_insn (ival) || is_fsqrt_s_insn (ival)
> + || is_fsgnj_s_insn (ival) || is_fsgnjn_s_insn (ival)
> + || is_fsgnjx_s_insn (ival) || is_fmin_s_insn (ival)
> + || is_fmax_s_insn (ival) || is_fcvt_s_w_insn (ival)
> + || is_fcvt_s_wu_insn (ival) || is_fmv_s_x_insn (ival)
> + || is_fcvt_s_l_insn (ival) || is_fcvt_s_lu_insn (ival)
> + || is_fld_insn (ival) || is_fmadd_d_insn (ival)
> + || is_fmsub_d_insn (ival) || is_fnmsub_d_insn (ival)
> + || is_fnmadd_d_insn (ival) || is_fadd_d_insn (ival)
> + || is_fsub_d_insn (ival) || is_fmul_d_insn (ival)
> + || is_fdiv_d_insn (ival) || is_fsqrt_d_insn (ival)
> + || is_fsgnj_d_insn (ival) || is_fsgnjn_d_insn (ival)
> + || is_fsgnjx_d_insn (ival) || is_fmin_d_insn (ival)
> + || is_fmax_d_insn (ival) || is_fcvt_s_d_insn (ival)
> + || is_fcvt_d_s_insn (ival) || is_fcvt_d_w_insn (ival)
> + || is_fcvt_d_wu_insn (ival) || is_fcvt_d_l_insn (ival)
> + || is_fcvt_d_lu_insn (ival) || is_fmv_d_x_insn (ival));
> + }
> +
> + /* Returns true if instruction is classified. This function can set
> + m_error_occured. */
> + bool
> + try_save_pc_fprd (ULONGEST ival) noexcept
> + {
> + if (!need_save_pc_fprd (ival))
> + return false;
> +
> + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_rd (ival))
> + || set_ordinary_record_type ());
> + }
> +
> + /* Returns true if instruction needs only saving pc, rd and csr. */
> + static bool
> + need_save_pc_rd_csr (ULONGEST ival) noexcept
> + {
> + return (is_csrrw_insn (ival) || is_csrrs_insn (ival) || is_csrrc_insn (ival)
> + || is_csrrwi_insn (ival) || is_csrrsi_insn (ival)
> + || is_csrrc_insn (ival));
> + }
> +
> + /* Returns true if instruction is classified. This function can set
> + m_error_occured. */
> + bool
> + try_save_pc_rd_csr (ULONGEST ival) noexcept
> + {
> + if (!need_save_pc_rd_csr (ival))
> + return false;
> +
> + return (!save_reg (decode_rd (ival))
> + || !save_reg (RISCV_FIRST_CSR_REGNUM + decode_csr (ival))
> + || set_ordinary_record_type ());
> + }
> +
> + /* Returns the size of the memory chunk that needs to be saved if the
> + instruction belongs to the group that needs only saving pc and memory.
> + Otherwise returns 0. */
> + static mem_len
> + need_save_pc_mem (ULONGEST ival) noexcept
> + {
> + if (is_sb_insn (ival))
> + return 1;
> + if (is_sh_insn (ival))
> + return 2;
> + if (is_sw_insn (ival) || is_fsw_insn (ival))
> + return 4;
> + if (is_sd_insn (ival) || is_fsd_insn (ival))
> + return 8;
> + return 0;
> + }
> +
> + /* Returns true if instruction is classified. This function can set
> + m_error_occured. */
> + bool
> + try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + mem_addr addr = mem_addr{};
> + mem_len len = need_save_pc_mem (ival);
> + if (len <= 0)
> + return false;
> +
> + mem_len offset = EXTRACT_STYPE_IMM (ival);
> + return (!read_reg (regcache, decode_rs1 (ival), addr)
> + || !save_mem (addr + offset, len) || set_ordinary_record_type ());
> + }
> +
> + /* Returns the size of the memory chunk that needs to be saved if the
> + instruction belongs to the group that needs only saving pc, rd and memory.
> + Otherwise returns 0. */
> + static mem_len
> + need_save_pc_rd_mem (ULONGEST ival) noexcept
> + {
> + if (is_sc_w_insn (ival) || is_amoswap_w_insn (ival)
> + || is_amoadd_w_insn (ival) || is_amoxor_w_insn (ival)
> + || is_amoand_w_insn (ival) || is_amoor_w_insn (ival)
> + || is_amomin_w_insn (ival) || is_amomax_w_insn (ival)
> + || is_amominu_w_insn (ival) || is_amomaxu_w_insn (ival))
> + return 4;
> + if (is_sc_d_insn (ival) || is_amoswap_d_insn (ival)
> + || is_amoadd_d_insn (ival) || is_amoxor_d_insn (ival)
> + || is_amoand_d_insn (ival) || is_amoor_d_insn (ival)
> + || is_amomin_d_insn (ival) || is_amomax_d_insn (ival)
> + || is_amominu_d_insn (ival) || is_amomaxu_d_insn (ival))
> + return 8;
> + return 0;
> + }
> +
> + /* Returns true if instruction is classified. This function can set
> + m_error_occured. */
> + bool
> + try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + mem_len len = need_save_pc_rd_mem (ival);
> + mem_addr addr = 0;
> + if (len <= 0)
> + return false;
> +
> + return (!read_reg (regcache, decode_rs1 (ival), addr)
> + || !save_mem (addr, len) || !save_reg (decode_rd (ival))
> + || set_ordinary_record_type ());
> + }
> +
> + /* Returns true if instruction is successfully recordered. The length of
> + the instruction must be equal 4 bytes. */
> + bool
> + record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + if (is_ecall_insn (ival))
> + {
> + m_record_type = record_type::ECALL;
> + return true;
> + }
> +
> + if (is_ebreak_insn (ival))
> + {
> + m_record_type = record_type::EBREAK;
> + return true;
> + }
> +
> + if (try_save_pc (ival) || try_save_pc_rd (ival) || try_save_pc_fprd (ival)
> + || try_save_pc_rd_csr (ival) || try_save_pc_mem (ival, regcache)
> + || try_save_pc_rd_mem (ival, regcache))
> + return !has_error ();
> +
> + warning (_("Currently this instruction with len 4(%lx) is unsupported"),
> + ival);
> + return false;
> + }
> +
> + /* Returns true if instruction is successfully recordered. The length of
> + the instruction must be equal 2 bytes. */
> + bool
> + record_insn_len2 (ULONGEST ival, struct regcache *regcache) noexcept
> + {
> + gdb_assert (regcache != nullptr);
> +
> + mem_addr addr = mem_addr{};
> +
> + /* The order here is very important, because
> + opcodes of some instructions may be the same. */
> +
> + if (is_c_addi4spn_insn (ival) || is_c_lw_insn (ival)
> + || (m_xlen == 8 && is_c_ld_insn (ival)))
> + return (!save_reg (decode_crs2_short (ival))
> + || set_ordinary_record_type ());
> +
> + if (is_c_fld_insn (ival) || (m_xlen == 4 && is_c_flw_insn (ival)))
> + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival))
> + || set_ordinary_record_type ());
> +
> + if (is_c_fsd_insn (ival) || (m_xlen == 8 && is_c_sd_insn (ival)))
> + {
> + ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LD_IMM (ival)};
> + return (!read_reg (regcache, decode_crs1_short (ival), addr)
> + || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
> + }
> +
> + if ((m_xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
> + {
> + ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LW_IMM (ival)};
> + return (!read_reg (regcache, decode_crs1_short (ival), addr)
> + || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
> + }
> +
> + if (is_c_nop_insn (ival))
> + return set_ordinary_record_type ();
> +
> + if (is_c_addi_insn (ival))
> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> +
> + if (m_xlen == 4 && is_c_jal_insn (ival))
> + return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
> +
> + if ((m_xlen == 8 && is_c_addiw_insn (ival)) || is_c_li_insn (ival))
> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> +
> + if (is_c_addi16sp_insn (ival))
> + return (!save_reg (RISCV_SP_REGNUM) || set_ordinary_record_type ());
> +
> + if (is_c_lui_insn (ival))
> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> +
> + if (is_c_srli_insn (ival) || is_c_srai_insn (ival) || is_c_andi_insn (ival)
> + || is_c_sub_insn (ival) || is_c_xor_insn (ival) || is_c_or_insn (ival)
> + || is_c_and_insn (ival) || (m_xlen == 8 && is_c_subw_insn (ival))
> + || (m_xlen == 8 && is_c_addw_insn (ival)))
> + return (!save_reg (decode_crs1_short (ival))
> + || set_ordinary_record_type ());
> +
> + if (is_c_j_insn (ival) || is_c_beqz_insn (ival) || is_c_bnez_insn (ival))
> + return set_ordinary_record_type ();
> +
> + if (is_c_slli_insn (ival))
> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> +
> + if (is_c_fldsp_insn (ival) || (m_xlen == 4 && is_c_flwsp_insn (ival)))
> + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival))
> + || set_ordinary_record_type ());
> +
> + if (is_c_lwsp_insn (ival) || (m_xlen == 8 && is_c_ldsp_insn (ival)))
> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> +
> + if (is_c_jr_insn (ival))
> + return set_ordinary_record_type ();
> +
> + if (is_c_mv_insn (ival))
> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> +
> + if (is_c_ebreak_insn (ival))
> + {
> + m_record_type = record_type::EBREAK;
> + return true;
> + }
> +
> + if (is_c_jalr_insn (ival))
> + return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
> +
> + if (is_c_add_insn (ival))
> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> +
> + if (is_c_fsdsp_insn (ival) || (m_xlen == 8 && is_c_sdsp_insn (ival)))
> + {
> + ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SDSP_IMM (ival)};
> + return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
> + || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
> + }
> +
> + if (is_c_swsp_insn (ival) || (m_xlen == 4 && is_c_fswsp_insn (ival)))
> + {
> + ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SWSP_IMM (ival)};
> + return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
> + || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
> + }
> +
> + warning (_("Currently this instruction with len 2(%lx) is unsupported"),
> + ival);
> + return false;
> + }
> +
> +public:
> + /* Iterator for registers that need to be recorded. */
> + using regs_iter = recorded_regs::const_iterator;
> + /* Iterator for memory chunks that need to be recorded. */
> + using mems_iter = recorded_mems::const_iterator;
> +
> + /* Record instruction at address addr. Returns false if error happened. */
> + bool
> + record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
> + {
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + int m_length = 0;
> + m_xlen = riscv_isa_xlen (gdbarch);
> + ULONGEST ival = riscv_insn::fetch_instruction (gdbarch, addr, &m_length);
> + if (!save_reg (RISCV_PC_REGNUM))
> + return false;
> +
> + if (m_length == 4)
> + return record_insn_len4 (ival, regcache);
> +
> + if (m_length == 2)
> + return record_insn_len2 (ival, regcache);
> +
> + /* 6 bytes or more. If the instruction is longer than 8 bytes, we don't
> + have full instruction bits in ival. At least, such long instructions
> + are not defined yet, so just ignore it. */
> + gdb_assert (m_length > 0 && m_length % 2 == 0);
> +
> + warning (_("Can not record unknown instruction (opcode = %lx)"), ival);
> + return false;
> + }
> +
> + /* Get record type of instruction. */
> + record_type
> + get_record_type () const noexcept
> + {
> + return m_record_type;
> + }
> +
> + /* Returns an iterator to the beginning of the registers that need
> + to be saved. */
> + regs_iter
> + regs_begin () const noexcept
> + {
> + return m_regs.begin ();
> + }
> +
> + /* Returns an iterator to the end of the registers that need
> + to be saved. */
> + regs_iter
> + regs_end () const noexcept
> + {
> + return m_regs.end ();
> + }
> +
> + /* Returns an iterator to the beginning of the memory chunks that need
> + to be saved. */
> + mems_iter
> + mems_begin () const noexcept
> + {
> + return m_mems.begin ();
> + }
> +
> + /* Returns an iterator to the end of the memory chunks that need
> + to be saved. */
> + mems_iter
> + mems_end () const noexcept
> + {
> + return m_mems.end ();
> + }
> +};
> +
> +/* A helper function to record instruction using record API. */
> +
> +static int
> +riscv_record_insn_details (struct gdbarch *gdbarch, struct regcache *regcache,
> + const riscv_recorded_insn &insn)
> +{
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> + auto regs_begin = insn.regs_begin ();
> + auto regs_end = insn.regs_end ();
> + if (std::any_of (regs_begin,
> + regs_end,
> + [®cache] (auto &®_it)
> + {
> + return record_full_arch_list_add_reg (regcache, reg_it);
> + }))
> + return -1;
> +
> + auto mems_begin = insn.mems_begin ();
> + auto mems_end = insn.mems_end ();
> + if (std::any_of (mems_begin,
> + mems_end,
> + [] (auto &&mem_it)
> + {
> + return record_full_arch_list_add_mem (mem_it.first,
> + mem_it.second);
> + }))
> + return -1;
> +
> + switch (insn.get_record_type ())
> + {
> + case riscv_recorded_insn::record_type::ORDINARY:
> + break;
> +
> + case riscv_recorded_insn::record_type::ECALL:
> + {
> + if (!tdep->riscv_syscall_record)
> + {
> + warning (_("Syscall record is not supported"));
> + return -1;
> + }
> + ULONGEST reg_val = ULONGEST{};
> + if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
> + return -1;
> + return tdep->riscv_syscall_record (regcache, reg_val);
> + }
> +
> + case riscv_recorded_insn::record_type::EBREAK:
> + break;
> +
> + default:
> + return -1;
> + }
> + return 0;
> +}
> +
> +/* Parse the current instruction and record the values of the registers and
> + memory that will be changed in current instruction to record_arch_list.
> + Return -1 if something is wrong. */
> +
> +int
> +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
> + CORE_ADDR addr)
> +{
> + gdb_assert (gdbarch != nullptr);
> + gdb_assert (regcache != nullptr);
> +
> + riscv_recorded_insn insn;
> + if (!insn.record (gdbarch, regcache, addr))
> + {
> + record_full_arch_list_add_end ();
> + return -1;
> + }
> +
> + int ret_val = riscv_record_insn_details (gdbarch, regcache, insn);
> +
> + if (record_full_arch_list_add_end ())
> + return -1;
> +
> + return ret_val;
> +}
> diff --git a/gdb/riscv-tdep.h b/gdb/riscv-tdep.h
> index ad1e9596b83..2903aefd007 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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> new file mode 100755
> index 00000000000..30e52b7e472
> --- /dev/null
> +++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> @@ -0,0 +1,163 @@
> +#!/usr/bin/env python3
> +# pylint: disable=invalid-name
> +
> +# Copyright (C) 2024-2025 Free Software Foundation, Inc.
> +# Contributed by Timur Golubovich
> +
> +# This file is part of GDB.
> +
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; either version 3 of the License, or
> +# (at your option) any later version.
> +
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU General Public License for more details.
> +
> +# You should have received a copy of the GNU General Public License
> +# along with this program. If not, see <http://www.gnu.org/licenses/>.
> +
> +
> +# To get help message for this script, run:
> +# ./gdb/syscalls/riscv-canonicalize-syscall-gen.py --help
> +
> +# Execution result:
> +
> +# usage: riscv-canonicalize-syscall-gen.py [-h] -i INPUT
> +#
> +# Generate file gdb/riscv-canonicalize-syscall-gen.c from path to riscv linux syscalls.
> +#
> +# options:
> +# -h, --help show this help message and exit
> +# -i INPUT, --input INPUT
> +# path to riscv linux syscalls (glibc/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h)
> +
> +import argparse
> +import re
> +import sys
> +from pathlib import Path as _Path
> +
> +head = """\
> +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py
> +
> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> +
> + This file is part of GDB.
> +
> + This program is free software; you can redistribute it and/or modify
> + it under the terms of the GNU General Public License as published by
> + the Free Software Foundation; either version 3 of the License, or
> + (at your option) any later version.
> +
> + This program is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + GNU General Public License for more details.
> +
> + You should have received a copy of the GNU General Public License
> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> +
> +#include "defs.h"
> +#include "riscv-linux-tdep.h"
> +
> +/* riscv64_canonicalize_syscall maps from the native riscv 64 Linux set
> + of syscall ids into a canonical set of syscall ids used by
> + process record. */
> +
> +enum gdb_syscall
> +riscv64_canonicalize_syscall (int syscall)
> +{
> + switch (syscall)
> + {
> +"""
> +
> +tail = """\
> + default:
> + return gdb_sys_no_syscall;
> + }
> +}
> +"""
> +
> +
> +class Generator:
> + def _get_gdb_syscalls(self, gdb_syscalls_path: _Path) -> list[str]:
> + gdb_syscalls: list[str] = []
> + with open(gdb_syscalls_path, "r", encoding="UTF-8") as file:
> + lines = file.readlines()
> + for line in lines:
> + match = re.search(r"\s*(?P<name>gdb_sys_[^S]+)\S*=", line)
> + if match:
> + gdb_syscalls.append(match.group("name").strip())
> + return gdb_syscalls
> +
> + def _get_canon_syscalls_lines(self, syscalls_path: _Path, gdb_syscalls: list[str]) -> list[str]:
> + canon_syscalls: dict[int, str] = {}
> + with open(syscalls_path, "r", encoding="UTF-8") as file:
> + lines = file.readlines()
> + for line in lines:
> + match = re.match(r"#define\s+__NR_(?P<name>[^\s]+)\s+(?P<number>\d+)", line)
> + if match:
> + syscall_name = match.group("name")
> + syscall_num = int(match.group("number"))
> + gdb_syscall_name = f"gdb_sys_{syscall_name}"
> + if gdb_syscall_name in gdb_syscalls:
> + value = f" case {syscall_num}: return {gdb_syscall_name};\n"
> + canon_syscalls[syscall_num] = value
> + # this is a place for corner cases
> + elif syscall_name == "mmap":
> + gdb_old_syscall_name = "gdb_old_mmap"
> + value = f" case {syscall_num}: return {gdb_old_syscall_name};\n"
> + canon_syscalls[syscall_num] = value
> + else:
> + value = f" /* case {syscall_num}: return {gdb_syscall_name}; */\n"
> + canon_syscalls[syscall_num] = value
> + return [canon_syscalls[syscall_num] for syscall_num in sorted(canon_syscalls)]
> +
> + def generate(self, syscalls_path: _Path) -> None:
> + repo_path = _Path(__file__).parent.parent.parent
> + gdb_syscalls_path = repo_path / "gdb" / "linux-record.h"
> + canon_syscalls_path = repo_path / "gdb" / "riscv-canonicalize-syscall-gen.c"
> +
> + gdb_syscalls = self._get_gdb_syscalls(gdb_syscalls_path)
> + canon_syscalls_lines = self._get_canon_syscalls_lines(syscalls_path, gdb_syscalls)
> +
> + with open(canon_syscalls_path, "w", encoding="UTF-8") as file:
> + file.writelines(head)
> + file.writelines(canon_syscalls_lines)
> + file.writelines(tail)
> +
> +
> +help_message = """\
> +Generate file gdb/riscv-canonicalize-syscall-gen.c
> +from path to riscv linux syscalls.
> +"""
> +
> +
> +def setup_parser() -> argparse.ArgumentParser:
> + parser = argparse.ArgumentParser(description=help_message)
> + parser.add_argument(
> + "-i",
> + "--input",
> + type=_Path,
> + required=True,
> + help="path to riscv linux syscalls (glibc/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h)",
> + )
> + return parser
> +
> +
> +def main(argv: list[str]) -> int:
> + try:
> + parser = setup_parser()
> + args = parser.parse_args(argv)
> + generator = Generator()
> + generator.generate(args.input)
> + return 0
> + except RuntimeError as e:
> + print(str(e))
> + return -1
> +
> +
> +if __name__ == "__main__":
> + sys.exit(main(sys.argv[1:]))
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index c37cc8974e2..0149125e60a 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*-*-linux*"] } {
> return 1
> }
>
> --
> 2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* Re: [PATCH v8] This commit adds record full support for rv64gc instruction set
2025-04-14 7:40 ` Andrew Burgess
@ 2025-04-14 8:44 ` Timur Golubovich
2025-04-14 12:03 ` Guinevere Larsen
2025-04-22 17:36 ` Timur Golubovich
0 siblings, 2 replies; 35+ messages in thread
From: Timur Golubovich @ 2025-04-14 8:44 UTC (permalink / raw)
To: Andrew Burgess; +Cc: gdb-patches, Guinevere Larsen
Hi!
Thank you Andrew for watching this.
I think that now we can merge this patch?
пн, 14 апр. 2025 г. в 10:41, Andrew Burgess <aburgess@redhat.com>:
>
> Timur <timurgol007@gmail.com> writes:
>
> > It includes changes to the following files:
> > - gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
> > syscalls.
> > - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
> > rv64gc instructions.
> > - gdb/configure.tgt: adds new files for compilation.
> > - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
> > targets.
> > - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
> > function that canonicalizes RISC-V syscall. This script can simplify support
> > for syscalls on rv32 and rv64 system (currently support only for rv64). To
> > use this script you need to pass a path to a file with syscalls description
> > from riscv-glibc (example is in the help message). The script produces a
> > mapping from syscall names to gdb_syscall enum.
> > - gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
> > - gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
> > - gdb/NEWS: notification of new functionality.
> >
> > Approved-By: Guinevere Larsen <guinevere@redhat.com>
> >
> > Hi Andrew! Thank you for review.
> >
> >> I guess riscv-glibc is a fork of glibc that is running ahead of the
> >> upstream glibc project?
> >>
> >> Is there any reason that this cannot work with the official glibc
> >> project? That would be preferable I think unless there's a compelling
> >> reason, which ideally we'd document.
> >>
> >> I tried running the script using current HEAD of glibc, the only
> >> significant change is:
> >>
> >> diff --git i/gdb/riscv-canonicalize-syscall-gen.c w/gdb/riscv-canonicalize-syscall-gen.c
> >> index 3749fc32f33..03579984f4a 100644
> >> --- i/gdb/riscv-canonicalize-syscall-gen.c
> >> +++ w/gdb/riscv-canonicalize-syscall-gen.c
> >> @@ -270,7 +270,7 @@ riscv64_canonicalize_syscall (int syscall)
> >> 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 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; */
> >>
> >> There was also a bunch of new syscalls that are added, but commented
> >> out. I haven't tried to figure out if the change above is a bad thing
> >> or not though, maybe you could advise.
> >
> > Yes, I agree with you, I just used riscv-glibc as I had aldready downloaded it
> > and unfortunately the version was pretty old (I started working on this patch
> > almost half a year ago).
> >
> >> This entry needs to move to the correct section of the NEWS file. If
> >> you look for the lines starting '***' you'll see that the first header
> >> is '*** Changes since GDB 16', this is the section where you should
> >> place your entry, probably just before the '* New commands' line.
> >> These include guards need to be renamed to GDB_RISCV_LINUX_TDEP_H. You
> >> can do this automatically if you like with:
> >>
> >> ./gdb/check-include-guards.py --update gdb/*.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 */
> >
> > Addressed
> >
> >>@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
> >>> || [istarget "i\[34567\]86-*-linux*"]
> >>> || [istarget "aarch64*-*-linux*"]
> >>> || [istarget "powerpc*-*-linux*"]
> >>> - || [istarget "s390*-*-linux*"] } {
> >>> + || [istarget "s390*-*-linux*"]
> >>> + || [istarget "riscv*-*-*"] } {
> >>
> >> I notice that every other entry in this list restricts the match to
> >> Linux based targets. Is RISC-V really different here? Or should RISC-V
> >> be similarly restricted?
> >
> > Addressed, this was just my misprint.
> >
> > Hope that now everything is okay, wait for your feedback.
>
> I scanned the fixes, they look good. I haven't looked at the rest of
> the patch in detail, assuming it hasn't changed from the previous
> version:
>
> Approved-By: Andrew Burgess <aburgess@redhat.com>
>
> Thanks,
> Andrew
>
>
>
> >
> > ---
> > gdb/NEWS | 2 +
> > gdb/configure.tgt | 5 +-
> > gdb/doc/gdb.texinfo | 2 +-
> > gdb/riscv-canonicalize-syscall-gen.c | 358 +++++++++
> > gdb/riscv-linux-tdep.c | 256 +++++++
> > gdb/riscv-linux-tdep.h | 29 +
> > gdb/riscv-tdep.c | 686 +++++++++++++++++-
> > gdb/riscv-tdep.h | 16 +-
> > .../riscv-canonicalize-syscall-gen.py | 163 +++++
> > gdb/testsuite/lib/gdb.exp | 3 +-
> > 10 files changed, 1510 insertions(+), 10 deletions(-)
> > create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
> > create mode 100644 gdb/riscv-linux-tdep.h
> > create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
> >
> > diff --git a/gdb/NEWS b/gdb/NEWS
> > index 99ec392d4c4..1a05a9f9251 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/configure.tgt b/gdb/configure.tgt
> > index 18a15c032c3..44da2257be8 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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> > index b251c8e1228..73f3b14cf74 100644
> > --- a/gdb/doc/gdb.texinfo
> > +++ b/gdb/doc/gdb.texinfo
> > @@ -7807,7 +7807,7 @@ 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
> > +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}.
> >
> > diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
> > new file mode 100644
> > index 00000000000..03579984f4a
> > --- /dev/null
> > +++ b/gdb/riscv-canonicalize-syscall-gen.c
> > @@ -0,0 +1,358 @@
> > +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py
> > +
> > + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> > +
> > + This file is part of GDB.
> > +
> > + This program is free software; you can redistribute it and/or modify
> > + it under the terms of the GNU General Public License as published by
> > + the Free Software Foundation; either version 3 of the License, or
> > + (at your option) any later version.
> > +
> > + This program is distributed in the hope that it will be useful,
> > + but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + GNU General Public License for more details.
> > +
> > + You should have received a copy of the GNU General Public License
> > + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> > +
> > +#include "defs.h"
> > +#include "riscv-linux-tdep.h"
> > +
> > +/* riscv64_canonicalize_syscall maps from the native riscv 64 Linux set
> > + of syscall ids into a canonical set of syscall ids used by
> > + process record. */
> > +
> > +enum gdb_syscall
> > +riscv64_canonicalize_syscall (int syscall)
> > +{
> > + switch (syscall)
> > + {
> > + case 0: return gdb_sys_io_setup;
> > + case 1: return gdb_sys_io_destroy;
> > + case 2: return gdb_sys_io_submit;
> > + case 3: return gdb_sys_io_cancel;
> > + case 4: return gdb_sys_io_getevents;
> > + case 5: return gdb_sys_setxattr;
> > + case 6: return gdb_sys_lsetxattr;
> > + case 7: return gdb_sys_fsetxattr;
> > + case 8: return gdb_sys_getxattr;
> > + case 9: return gdb_sys_lgetxattr;
> > + case 10: return gdb_sys_fgetxattr;
> > + case 11: return gdb_sys_listxattr;
> > + case 12: return gdb_sys_llistxattr;
> > + case 13: return gdb_sys_flistxattr;
> > + case 14: return gdb_sys_removexattr;
> > + case 15: return gdb_sys_lremovexattr;
> > + case 16: return gdb_sys_fremovexattr;
> > + case 17: return gdb_sys_getcwd;
> > + case 18: return gdb_sys_lookup_dcookie;
> > + case 19: return gdb_sys_eventfd2;
> > + case 20: return gdb_sys_epoll_create1;
> > + case 21: return gdb_sys_epoll_ctl;
> > + case 22: return gdb_sys_epoll_pwait;
> > + case 23: return gdb_sys_dup;
> > + case 24: return gdb_sys_dup3;
> > + case 25: return gdb_sys_fcntl;
> > + case 26: return gdb_sys_inotify_init1;
> > + case 27: return gdb_sys_inotify_add_watch;
> > + case 28: return gdb_sys_inotify_rm_watch;
> > + case 29: return gdb_sys_ioctl;
> > + case 30: return gdb_sys_ioprio_set;
> > + case 31: return gdb_sys_ioprio_get;
> > + case 32: return gdb_sys_flock;
> > + case 33: return gdb_sys_mknodat;
> > + case 34: return gdb_sys_mkdirat;
> > + case 35: return gdb_sys_unlinkat;
> > + case 36: return gdb_sys_symlinkat;
> > + case 37: return gdb_sys_linkat;
> > + /* case 39: return gdb_sys_umount2; */
> > + case 40: return gdb_sys_mount;
> > + case 41: return gdb_sys_pivot_root;
> > + case 42: return gdb_sys_nfsservctl;
> > + case 43: return gdb_sys_statfs;
> > + case 44: return gdb_sys_fstatfs;
> > + case 45: return gdb_sys_truncate;
> > + case 46: return gdb_sys_ftruncate;
> > + case 47: return gdb_sys_fallocate;
> > + case 48: return gdb_sys_faccessat;
> > + case 49: return gdb_sys_chdir;
> > + case 50: return gdb_sys_fchdir;
> > + case 51: return gdb_sys_chroot;
> > + case 52: return gdb_sys_fchmod;
> > + case 53: return gdb_sys_fchmodat;
> > + case 54: return gdb_sys_fchownat;
> > + case 55: return gdb_sys_fchown;
> > + case 56: return gdb_sys_openat;
> > + case 57: return gdb_sys_close;
> > + case 58: return gdb_sys_vhangup;
> > + case 59: return gdb_sys_pipe2;
> > + case 60: return gdb_sys_quotactl;
> > + case 61: return gdb_sys_getdents64;
> > + case 62: return gdb_sys_lseek;
> > + case 63: return gdb_sys_read;
> > + case 64: return gdb_sys_write;
> > + case 65: return gdb_sys_readv;
> > + case 66: return gdb_sys_writev;
> > + case 67: return gdb_sys_pread64;
> > + case 68: return gdb_sys_pwrite64;
> > + /* case 69: return gdb_sys_preadv; */
> > + /* case 70: return gdb_sys_pwritev; */
> > + case 71: return gdb_sys_sendfile;
> > + case 72: return gdb_sys_pselect6;
> > + case 73: return gdb_sys_ppoll;
> > + /* case 74: return gdb_sys_signalfd4; */
> > + case 75: return gdb_sys_vmsplice;
> > + case 76: return gdb_sys_splice;
> > + case 77: return gdb_sys_tee;
> > + case 78: return gdb_sys_readlinkat;
> > + case 79: return gdb_sys_newfstatat;
> > + case 80: return gdb_sys_fstat;
> > + case 81: return gdb_sys_sync;
> > + case 82: return gdb_sys_fsync;
> > + case 83: return gdb_sys_fdatasync;
> > + case 84: return gdb_sys_sync_file_range;
> > + /* case 85: return gdb_sys_timerfd_create; */
> > + /* case 86: return gdb_sys_timerfd_settime; */
> > + /* case 87: return gdb_sys_timerfd_gettime; */
> > + /* case 88: return gdb_sys_utimensat; */
> > + case 89: return gdb_sys_acct;
> > + case 90: return gdb_sys_capget;
> > + case 91: return gdb_sys_capset;
> > + case 92: return gdb_sys_personality;
> > + case 93: return gdb_sys_exit;
> > + case 94: return gdb_sys_exit_group;
> > + case 95: return gdb_sys_waitid;
> > + case 96: return gdb_sys_set_tid_address;
> > + case 97: return gdb_sys_unshare;
> > + case 98: return gdb_sys_futex;
> > + case 99: return gdb_sys_set_robust_list;
> > + case 100: return gdb_sys_get_robust_list;
> > + case 101: return gdb_sys_nanosleep;
> > + case 102: return gdb_sys_getitimer;
> > + case 103: return gdb_sys_setitimer;
> > + case 104: return gdb_sys_kexec_load;
> > + case 105: return gdb_sys_init_module;
> > + case 106: return gdb_sys_delete_module;
> > + case 107: return gdb_sys_timer_create;
> > + case 108: return gdb_sys_timer_gettime;
> > + case 109: return gdb_sys_timer_getoverrun;
> > + case 110: return gdb_sys_timer_settime;
> > + case 111: return gdb_sys_timer_delete;
> > + case 112: return gdb_sys_clock_settime;
> > + case 113: return gdb_sys_clock_gettime;
> > + case 114: return gdb_sys_clock_getres;
> > + case 115: return gdb_sys_clock_nanosleep;
> > + case 116: return gdb_sys_syslog;
> > + case 117: return gdb_sys_ptrace;
> > + case 118: return gdb_sys_sched_setparam;
> > + case 119: return gdb_sys_sched_setscheduler;
> > + case 120: return gdb_sys_sched_getscheduler;
> > + case 121: return gdb_sys_sched_getparam;
> > + case 122: return gdb_sys_sched_setaffinity;
> > + case 123: return gdb_sys_sched_getaffinity;
> > + case 124: return gdb_sys_sched_yield;
> > + case 125: return gdb_sys_sched_get_priority_max;
> > + case 126: return gdb_sys_sched_get_priority_min;
> > + case 127: return gdb_sys_sched_rr_get_interval;
> > + case 128: return gdb_sys_restart_syscall;
> > + case 129: return gdb_sys_kill;
> > + case 130: return gdb_sys_tkill;
> > + case 131: return gdb_sys_tgkill;
> > + case 132: return gdb_sys_sigaltstack;
> > + case 133: return gdb_sys_rt_sigsuspend;
> > + case 134: return gdb_sys_rt_sigaction;
> > + case 135: return gdb_sys_rt_sigprocmask;
> > + case 136: return gdb_sys_rt_sigpending;
> > + case 137: return gdb_sys_rt_sigtimedwait;
> > + case 138: return gdb_sys_rt_sigqueueinfo;
> > + case 139: return gdb_sys_rt_sigreturn;
> > + case 140: return gdb_sys_setpriority;
> > + case 141: return gdb_sys_getpriority;
> > + case 142: return gdb_sys_reboot;
> > + case 143: return gdb_sys_setregid;
> > + case 144: return gdb_sys_setgid;
> > + case 145: return gdb_sys_setreuid;
> > + case 146: return gdb_sys_setuid;
> > + case 147: return gdb_sys_setresuid;
> > + case 148: return gdb_sys_getresuid;
> > + case 149: return gdb_sys_setresgid;
> > + case 150: return gdb_sys_getresgid;
> > + case 151: return gdb_sys_setfsuid;
> > + case 152: return gdb_sys_setfsgid;
> > + case 153: return gdb_sys_times;
> > + case 154: return gdb_sys_setpgid;
> > + case 155: return gdb_sys_getpgid;
> > + case 156: return gdb_sys_getsid;
> > + case 157: return gdb_sys_setsid;
> > + case 158: return gdb_sys_getgroups;
> > + case 159: return gdb_sys_setgroups;
> > + case 160: return gdb_sys_uname;
> > + case 161: return gdb_sys_sethostname;
> > + case 162: return gdb_sys_setdomainname;
> > + case 163: return gdb_sys_getrlimit;
> > + case 164: return gdb_sys_setrlimit;
> > + case 165: return gdb_sys_getrusage;
> > + case 166: return gdb_sys_umask;
> > + case 167: return gdb_sys_prctl;
> > + case 168: return gdb_sys_getcpu;
> > + case 169: return gdb_sys_gettimeofday;
> > + case 170: return gdb_sys_settimeofday;
> > + case 171: return gdb_sys_adjtimex;
> > + case 172: return gdb_sys_getpid;
> > + case 173: return gdb_sys_getppid;
> > + case 174: return gdb_sys_getuid;
> > + case 175: return gdb_sys_geteuid;
> > + case 176: return gdb_sys_getgid;
> > + case 177: return gdb_sys_getegid;
> > + case 178: return gdb_sys_gettid;
> > + case 179: return gdb_sys_sysinfo;
> > + case 180: return gdb_sys_mq_open;
> > + case 181: return gdb_sys_mq_unlink;
> > + case 182: return gdb_sys_mq_timedsend;
> > + case 183: return gdb_sys_mq_timedreceive;
> > + case 184: return gdb_sys_mq_notify;
> > + case 185: return gdb_sys_mq_getsetattr;
> > + case 186: return gdb_sys_msgget;
> > + case 187: return gdb_sys_msgctl;
> > + case 188: return gdb_sys_msgrcv;
> > + case 189: return gdb_sys_msgsnd;
> > + case 190: return gdb_sys_semget;
> > + case 191: return gdb_sys_semctl;
> > + case 192: return gdb_sys_semtimedop;
> > + case 193: return gdb_sys_semop;
> > + case 194: return gdb_sys_shmget;
> > + case 195: return gdb_sys_shmctl;
> > + case 196: return gdb_sys_shmat;
> > + case 197: return gdb_sys_shmdt;
> > + case 198: return gdb_sys_socket;
> > + case 199: return gdb_sys_socketpair;
> > + case 200: return gdb_sys_bind;
> > + case 201: return gdb_sys_listen;
> > + case 202: return gdb_sys_accept;
> > + case 203: return gdb_sys_connect;
> > + case 204: return gdb_sys_getsockname;
> > + case 205: return gdb_sys_getpeername;
> > + case 206: return gdb_sys_sendto;
> > + case 207: return gdb_sys_recvfrom;
> > + case 208: return gdb_sys_setsockopt;
> > + case 209: return gdb_sys_getsockopt;
> > + case 210: return gdb_sys_shutdown;
> > + case 211: return gdb_sys_sendmsg;
> > + case 212: return gdb_sys_recvmsg;
> > + case 213: return gdb_sys_readahead;
> > + case 214: return gdb_sys_brk;
> > + case 215: return gdb_sys_munmap;
> > + case 216: return gdb_sys_mremap;
> > + case 217: return gdb_sys_add_key;
> > + case 218: return gdb_sys_request_key;
> > + case 219: return gdb_sys_keyctl;
> > + case 220: return gdb_sys_clone;
> > + case 221: return gdb_sys_execve;
> > + case 222: return gdb_old_mmap;
> > + case 223: return gdb_sys_fadvise64;
> > + case 224: return gdb_sys_swapon;
> > + case 225: return gdb_sys_swapoff;
> > + case 226: return gdb_sys_mprotect;
> > + case 227: return gdb_sys_msync;
> > + case 228: return gdb_sys_mlock;
> > + case 229: return gdb_sys_munlock;
> > + case 230: return gdb_sys_mlockall;
> > + case 231: return gdb_sys_munlockall;
> > + case 232: return gdb_sys_mincore;
> > + case 233: return gdb_sys_madvise;
> > + case 234: return gdb_sys_remap_file_pages;
> > + case 235: return gdb_sys_mbind;
> > + case 236: return gdb_sys_get_mempolicy;
> > + case 237: return gdb_sys_set_mempolicy;
> > + case 238: return gdb_sys_migrate_pages;
> > + case 239: return gdb_sys_move_pages;
> > + /* case 240: return gdb_sys_rt_tgsigqueueinfo; */
> > + /* case 241: return gdb_sys_perf_event_open; */
> > + case 242: return gdb_sys_accept4;
> > + /* case 243: return gdb_sys_recvmmsg; */
> > + /* case 258: return gdb_sys_riscv_hwprobe; */
> > + /* case 259: return gdb_sys_riscv_flush_icache; */
> > + case 260: return gdb_sys_wait4;
> > + /* case 261: return gdb_sys_prlimit64; */
> > + /* case 262: return gdb_sys_fanotify_init; */
> > + /* case 263: return gdb_sys_fanotify_mark; */
> > + /* case 264: return gdb_sys_name_to_handle_at; */
> > + /* case 265: return gdb_sys_open_by_handle_at; */
> > + /* case 266: return gdb_sys_clock_adjtime; */
> > + /* case 267: return gdb_sys_syncfs; */
> > + /* case 268: return gdb_sys_setns; */
> > + /* case 269: return gdb_sys_sendmmsg; */
> > + /* case 270: return gdb_sys_process_vm_readv; */
> > + /* case 271: return gdb_sys_process_vm_writev; */
> > + /* case 272: return gdb_sys_kcmp; */
> > + /* case 273: return gdb_sys_finit_module; */
> > + /* case 274: return gdb_sys_sched_setattr; */
> > + /* case 275: return gdb_sys_sched_getattr; */
> > + /* case 276: return gdb_sys_renameat2; */
> > + /* case 277: return gdb_sys_seccomp; */
> > + case 278: return gdb_sys_getrandom;
> > + /* case 279: return gdb_sys_memfd_create; */
> > + /* case 280: return gdb_sys_bpf; */
> > + /* case 281: return gdb_sys_execveat; */
> > + /* case 282: return gdb_sys_userfaultfd; */
> > + /* case 283: return gdb_sys_membarrier; */
> > + /* case 284: return gdb_sys_mlock2; */
> > + /* case 285: return gdb_sys_copy_file_range; */
> > + /* case 286: return gdb_sys_preadv2; */
> > + /* case 287: return gdb_sys_pwritev2; */
> > + /* case 288: return gdb_sys_pkey_mprotect; */
> > + /* case 289: return gdb_sys_pkey_alloc; */
> > + /* case 290: return gdb_sys_pkey_free; */
> > + case 291: return gdb_sys_statx;
> > + /* case 292: return gdb_sys_io_pgetevents; */
> > + /* case 293: return gdb_sys_rseq; */
> > + /* case 294: return gdb_sys_kexec_file_load; */
> > + /* case 424: return gdb_sys_pidfd_send_signal; */
> > + /* case 425: return gdb_sys_io_uring_setup; */
> > + /* case 426: return gdb_sys_io_uring_enter; */
> > + /* case 427: return gdb_sys_io_uring_register; */
> > + /* case 428: return gdb_sys_open_tree; */
> > + /* case 429: return gdb_sys_move_mount; */
> > + /* case 430: return gdb_sys_fsopen; */
> > + /* case 431: return gdb_sys_fsconfig; */
> > + /* case 432: return gdb_sys_fsmount; */
> > + /* case 433: return gdb_sys_fspick; */
> > + /* case 434: return gdb_sys_pidfd_open; */
> > + /* case 435: return gdb_sys_clone3; */
> > + /* case 436: return gdb_sys_close_range; */
> > + /* case 437: return gdb_sys_openat2; */
> > + /* case 438: return gdb_sys_pidfd_getfd; */
> > + /* case 439: return gdb_sys_faccessat2; */
> > + /* case 440: return gdb_sys_process_madvise; */
> > + /* case 441: return gdb_sys_epoll_pwait2; */
> > + /* case 442: return gdb_sys_mount_setattr; */
> > + /* case 443: return gdb_sys_quotactl_fd; */
> > + /* case 444: return gdb_sys_landlock_create_ruleset; */
> > + /* case 445: return gdb_sys_landlock_add_rule; */
> > + /* case 446: return gdb_sys_landlock_restrict_self; */
> > + /* case 447: return gdb_sys_memfd_secret; */
> > + /* case 448: return gdb_sys_process_mrelease; */
> > + /* case 449: return gdb_sys_futex_waitv; */
> > + /* case 450: return gdb_sys_set_mempolicy_home_node; */
> > + /* case 451: return gdb_sys_cachestat; */
> > + /* case 452: return gdb_sys_fchmodat2; */
> > + /* case 453: return gdb_sys_map_shadow_stack; */
> > + /* case 454: return gdb_sys_futex_wake; */
> > + /* case 455: return gdb_sys_futex_wait; */
> > + /* case 456: return gdb_sys_futex_requeue; */
> > + /* case 457: return gdb_sys_statmount; */
> > + /* case 458: return gdb_sys_listmount; */
> > + /* case 459: return gdb_sys_lsm_get_self_attr; */
> > + /* case 460: return gdb_sys_lsm_set_self_attr; */
> > + /* case 461: return gdb_sys_lsm_list_modules; */
> > + /* case 462: return gdb_sys_mseal; */
> > + /* case 463: return gdb_sys_setxattrat; */
> > + /* case 464: return gdb_sys_getxattrat; */
> > + /* case 465: return gdb_sys_listxattrat; */
> > + /* case 466: return gdb_sys_removexattrat; */
> > + default:
> > + return gdb_sys_no_syscall;
> > + }
> > +}
> > diff --git a/gdb/riscv-linux-tdep.c b/gdb/riscv-linux-tdep.c
> > index 4c0c65c1457..f21039a613c 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 00000000000..9dd9e373caa
> > --- /dev/null
> > +++ b/gdb/riscv-linux-tdep.h
> > @@ -0,0 +1,29 @@
> > +/* Copyright (C) 2024-2025 Free Software Foundation, Inc.
> > +
> > + This file is part of GDB.
> > +
> > + This program is free software; you can redistribute it and/or modify
> > + it under the terms of the GNU General Public License as published by
> > + the Free Software Foundation; either version 3 of the License, or
> > + (at your option) any later version.
> > +
> > + This program is distributed in the hope that it will be useful,
> > + but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + GNU General Public License for more details.
> > +
> > + You should have received a copy of the GNU General Public License
> > + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> > +
> > +#ifndef GDB_RISCV_LINUX_TDEP_H
> > +#define GDB_RISCV_LINUX_TDEP_H
> > +
> > +#include "linux-record.h"
> > +
> > +/* riscv64_canonicalize_syscall maps from the native riscv Linux set
> > + of syscall ids into a canonical set of syscall ids used by
> > + process record. */
> > +
> > +extern enum gdb_syscall riscv64_canonicalize_syscall (int syscall);
> > +
> > +#endif /* GDB_RISCV_LINUX_TDEP_H */
> > diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
> > index 91f6dffebe1..19367ff55ba 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 @@ class riscv_insn
> > 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 @@ class riscv_insn
> > m_rs2 = decode_register_index_short (ival, OP_SH_CRS2S);
> > }
> >
> > - /* Fetch instruction from target memory at ADDR, return the content of
> > - the instruction, and update LEN with the instruction length. */
> > - static ULONGEST fetch_instruction (struct gdbarch *gdbarch,
> > - CORE_ADDR addr, int *len);
> > -
> > /* The length of the instruction in bytes. Should be 2 or 4. */
> > int m_length;
> >
> > @@ -4433,6 +4436,9 @@ riscv_gdbarch_init (struct gdbarch_info info,
> > set_gdbarch_stap_register_indirection_suffixes
> > (gdbarch, stap_register_indirection_suffixes);
> >
> > + /* Process record-replay */
> > + set_gdbarch_process_record (gdbarch, riscv_process_record);
> > +
> > /* Hook in OS ABI-specific overrides, if they have been registered. */
> > gdbarch_init_osabi (info, gdbarch);
> >
> > @@ -4866,3 +4872,673 @@ equivalent change in the disassembler output."),
> > &setriscvcmdlist,
> > &showriscvcmdlist);
> > }
> > +
> > +/* A wrapper to read register under number regnum to address addr.
> > + Returns false if error happened and makes warning. */
> > +
> > +static bool
> > +try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
> > +{
> > + gdb_assert (regcache != nullptr);
> > +
> > + if (regcache->raw_read (regnum, &addr)
> > + != register_status::REG_VALID)
> > + {
> > + warning (_("Can not read at address %lx"), addr);
> > + return false;
> > + }
> > + return true;
> > +}
> > +
> > +/* Helper class to record instruction. */
> > +
> > +class riscv_recorded_insn final
> > +{
> > +public:
> > + /* Type for saved register. */
> > + using regnum_type = int;
> > + /* Type for saved memory. First is address, second is length. */
> > + using memory_type = std::pair<CORE_ADDR, int>;
> > +
> > + /* Enum class that represents which type does recording belong to. */
> > + enum class record_type
> > + {
> > + UNKNOWN,
> > + ORDINARY,
> > +
> > + /* Corner cases. */
> > + ECALL,
> > + EBREAK,
> > + };
> > +
> > +private:
> > + /* Type for set of registers that need to be saved. */
> > + using recorded_regs = std::vector<regnum_type>;
> > + /* Type for set of memory records that need to be saved. */
> > + using recorded_mems = std::vector<memory_type>;
> > +
> > + /* Type for memory address, extracted from memory_type. */
> > + using mem_addr = decltype (std::declval<memory_type> ().first);
> > + /* Type for memory length, extracted from memory_type. */
> > + using mem_len = decltype (std::declval<memory_type> ().second);
> > +
> > + /* Record type of current instruction. */
> > + record_type m_record_type = record_type::UNKNOWN;
> > +
> > + /* Flag that represents was there an error in current recording. */
> > + bool m_error_occured = false;
> > +
> > + /* Set of registers that need to be recorded. */
> > + recorded_regs m_regs;
> > + /* Set of memory chunks that need to be recorded. */
> > + recorded_mems m_mems;
> > +
> > + /* Width in bytes of the general purpose registers for GDBARCH,
> > + where recording is happening. */
> > + int m_xlen = 0;
> > +
> > + /* Helper for decode 16-bit instruction RS1. */
> > + static regnum_type
> > + decode_crs1_short (ULONGEST opcode) noexcept
> > + {
> > + return ((opcode >> OP_SH_CRS1S) & OP_MASK_CRS1S) + 8;
> > + }
> > +
> > + /* Helper for decode 16-bit instruction RS2. */
> > + static regnum_type
> > + decode_crs2_short (ULONGEST opcode) noexcept
> > + {
> > + return ((opcode >> OP_SH_CRS2S) & OP_MASK_CRS2S) + 8;
> > + }
> > +
> > + /* Helper for decode 16-bit instruction CRS1. */
> > + static regnum_type
> > + decode_crs1 (ULONGEST opcode) noexcept
> > + {
> > + return ((opcode >> OP_SH_RD) & OP_MASK_RD);
> > + }
> > +
> > + /* Helper for decode 16-bit instruction CRS2. */
> > + static regnum_type
> > + decode_crs2 (ULONGEST opcode) noexcept
> > + {
> > + return ((opcode >> OP_SH_CRS2) & OP_MASK_CRS2);
> > + }
> > +
> > + /* Helper for decode 32-bit instruction RD. */
> > + static regnum_type
> > + decode_rd (ULONGEST ival) noexcept
> > + {
> > + return (ival >> OP_SH_RD) & OP_MASK_RD;
> > + }
> > +
> > + /* Helper for decode 32-bit instruction RS1. */
> > + static regnum_type
> > + decode_rs1 (ULONGEST ival) noexcept
> > + {
> > + return (ival >> OP_SH_RS1) & OP_MASK_RS1;
> > + }
> > +
> > + /* Helper for decode 32-bit instruction RS2. */
> > + static regnum_type
> > + decode_rs2 (ULONGEST ival) noexcept
> > + {
> > + return (ival >> OP_SH_RS2) & OP_MASK_RS2;
> > + }
> > +
> > + /* Helper for decode 32-bit instruction CSR. */
> > + static regnum_type
> > + decode_csr (ULONGEST ival) noexcept
> > + {
> > + return (ival >> OP_SH_CSR) & OP_MASK_CSR;
> > + }
> > +
> > + /* Set ordinary record type. Always returns true. */
> > + bool
> > + set_ordinary_record_type () noexcept
> > + {
> > + m_record_type = record_type::ORDINARY;
> > + return true;
> > + }
> > +
> > + /* Set error happened. Always returns false. */
> > + bool
> > + set_error () noexcept
> > + {
> > + m_error_occured = true;
> > + return false;
> > + }
> > +
> > + /* Check if current recording has an error. */
> > + bool
> > + has_error () const noexcept
> > + {
> > + return m_error_occured;
> > + }
> > +
> > + /* Reads register. Sets error and returns false if error happened. */
> > + bool
> > + read_reg (struct regcache *regcache, regnum_type reg,
> > + ULONGEST &addr) noexcept
> > + {
> > + gdb_assert (regcache != nullptr);
> > +
> > + if (!try_read (regcache, reg, addr))
> > + return set_error ();
> > + return true;
> > + }
> > +
> > + /* Save register. Returns true or aborts if exception happened. */
> > + bool
> > + save_reg (regnum_type regnum) noexcept
> > + {
> > + m_regs.emplace_back (regnum);
> > + return true;
> > + }
> > +
> > + /* Save memory chunk. Returns true or aborts if exception happened. */
> > + bool
> > + save_mem (mem_addr addr, mem_len len) noexcept
> > + {
> > + m_mems.emplace_back (addr, len);
> > + return true;
> > + }
> > +
> > + /* Returns true if instruction needs only saving pc. */
> > + static bool
> > + need_save_pc (ULONGEST ival) noexcept
> > + {
> > + return (is_beq_insn (ival) || is_bne_insn (ival) || is_blt_insn (ival)
> > + || is_bge_insn (ival) || is_bltu_insn (ival) || is_bgeu_insn (ival)
> > + || is_fence_insn (ival) || is_pause_insn (ival)
> > + || is_fence_i_insn (ival));
> > + }
> > +
> > + /* Returns true if instruction is classified. */
> > + bool
> > + try_save_pc (ULONGEST ival) noexcept
> > + {
> > + if (!need_save_pc (ival))
> > + return false;
> > +
> > + return set_ordinary_record_type ();
> > + }
> > +
> > + /* Returns true if instruction needs only saving pc and rd. */
> > + static bool
> > + need_save_pc_rd (ULONGEST ival) noexcept
> > + {
> > + return (is_lui_insn (ival) || is_auipc_insn (ival) || is_jal_insn (ival)
> > + || is_jalr_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
> > + || is_lw_insn (ival) || is_lbu_insn (ival) || is_lhu_insn (ival)
> > + || is_addi_insn (ival) || is_slti_insn (ival)
> > + || is_sltiu_insn (ival) || is_xori_insn (ival) || is_ori_insn (ival)
> > + || is_andi_insn (ival) || is_slli_rv32_insn (ival)
> > + || is_srli_rv32_insn (ival) || is_srai_rv32_insn (ival)
> > + || is_add_insn (ival) || is_sub_insn (ival) || is_sll_insn (ival)
> > + || is_slt_insn (ival) || is_sltu_insn (ival) || is_xor_insn (ival)
> > + || is_srl_insn (ival) || is_sra_insn (ival) || is_or_insn (ival)
> > + || is_and_insn (ival) || is_lwu_insn (ival) || is_ld_insn (ival)
> > + || is_slli_insn (ival) || is_srli_insn (ival) || is_srai_insn (ival)
> > + || is_addiw_insn (ival) || is_slliw_insn (ival)
> > + || is_srliw_insn (ival) || is_sraiw_insn (ival)
> > + || is_addw_insn (ival) || is_subw_insn (ival) || is_sllw_insn (ival)
> > + || is_srlw_insn (ival) || is_sraw_insn (ival) || is_mul_insn (ival)
> > + || is_mulh_insn (ival) || is_mulhsu_insn (ival)
> > + || is_mulhu_insn (ival) || is_div_insn (ival) || is_divu_insn (ival)
> > + || is_rem_insn (ival) || is_remu_insn (ival) || is_mulw_insn (ival)
> > + || is_divw_insn (ival) || is_divuw_insn (ival)
> > + || is_remw_insn (ival) || is_remuw_insn (ival)
> > + || is_lr_w_insn (ival) || is_lr_d_insn (ival)
> > + || is_fcvt_w_s_insn (ival) || is_fcvt_wu_s_insn (ival)
> > + || is_fmv_x_s_insn (ival) || is_feq_s_insn (ival)
> > + || is_flt_s_insn (ival) || is_fle_s_insn (ival)
> > + || is_fclass_s_insn (ival) || is_fcvt_l_s_insn (ival)
> > + || is_fcvt_lu_s_insn (ival) || is_feq_d_insn (ival)
> > + || is_flt_d_insn (ival) || is_fle_d_insn (ival)
> > + || is_fclass_d_insn (ival) || is_fcvt_w_d_insn (ival)
> > + || is_fcvt_wu_d_insn (ival) || is_fcvt_l_d_insn (ival)
> > + || is_fcvt_lu_d_insn (ival) || is_fmv_x_d_insn (ival));
> > + }
> > +
> > + /* Returns true if instruction is classified. This function can set
> > + m_error_occured. */
> > + bool
> > + try_save_pc_rd (ULONGEST ival) noexcept
> > + {
> > + if (!need_save_pc_rd (ival))
> > + return false;
> > +
> > + return (!save_reg (decode_rd (ival)) || set_ordinary_record_type ());
> > + }
> > +
> > + /* Returns true if instruction needs only saving pc and
> > + floating point rd. */
> > + static bool
> > + need_save_pc_fprd (ULONGEST ival) noexcept
> > + {
> > + return (is_flw_insn (ival) || is_fmadd_s_insn (ival)
> > + || is_fmsub_s_insn (ival) || is_fnmsub_s_insn (ival)
> > + || is_fnmadd_s_insn (ival) || is_fadd_s_insn (ival)
> > + || is_fsub_s_insn (ival) || is_fmul_s_insn (ival)
> > + || is_fdiv_s_insn (ival) || is_fsqrt_s_insn (ival)
> > + || is_fsgnj_s_insn (ival) || is_fsgnjn_s_insn (ival)
> > + || is_fsgnjx_s_insn (ival) || is_fmin_s_insn (ival)
> > + || is_fmax_s_insn (ival) || is_fcvt_s_w_insn (ival)
> > + || is_fcvt_s_wu_insn (ival) || is_fmv_s_x_insn (ival)
> > + || is_fcvt_s_l_insn (ival) || is_fcvt_s_lu_insn (ival)
> > + || is_fld_insn (ival) || is_fmadd_d_insn (ival)
> > + || is_fmsub_d_insn (ival) || is_fnmsub_d_insn (ival)
> > + || is_fnmadd_d_insn (ival) || is_fadd_d_insn (ival)
> > + || is_fsub_d_insn (ival) || is_fmul_d_insn (ival)
> > + || is_fdiv_d_insn (ival) || is_fsqrt_d_insn (ival)
> > + || is_fsgnj_d_insn (ival) || is_fsgnjn_d_insn (ival)
> > + || is_fsgnjx_d_insn (ival) || is_fmin_d_insn (ival)
> > + || is_fmax_d_insn (ival) || is_fcvt_s_d_insn (ival)
> > + || is_fcvt_d_s_insn (ival) || is_fcvt_d_w_insn (ival)
> > + || is_fcvt_d_wu_insn (ival) || is_fcvt_d_l_insn (ival)
> > + || is_fcvt_d_lu_insn (ival) || is_fmv_d_x_insn (ival));
> > + }
> > +
> > + /* Returns true if instruction is classified. This function can set
> > + m_error_occured. */
> > + bool
> > + try_save_pc_fprd (ULONGEST ival) noexcept
> > + {
> > + if (!need_save_pc_fprd (ival))
> > + return false;
> > +
> > + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_rd (ival))
> > + || set_ordinary_record_type ());
> > + }
> > +
> > + /* Returns true if instruction needs only saving pc, rd and csr. */
> > + static bool
> > + need_save_pc_rd_csr (ULONGEST ival) noexcept
> > + {
> > + return (is_csrrw_insn (ival) || is_csrrs_insn (ival) || is_csrrc_insn (ival)
> > + || is_csrrwi_insn (ival) || is_csrrsi_insn (ival)
> > + || is_csrrc_insn (ival));
> > + }
> > +
> > + /* Returns true if instruction is classified. This function can set
> > + m_error_occured. */
> > + bool
> > + try_save_pc_rd_csr (ULONGEST ival) noexcept
> > + {
> > + if (!need_save_pc_rd_csr (ival))
> > + return false;
> > +
> > + return (!save_reg (decode_rd (ival))
> > + || !save_reg (RISCV_FIRST_CSR_REGNUM + decode_csr (ival))
> > + || set_ordinary_record_type ());
> > + }
> > +
> > + /* Returns the size of the memory chunk that needs to be saved if the
> > + instruction belongs to the group that needs only saving pc and memory.
> > + Otherwise returns 0. */
> > + static mem_len
> > + need_save_pc_mem (ULONGEST ival) noexcept
> > + {
> > + if (is_sb_insn (ival))
> > + return 1;
> > + if (is_sh_insn (ival))
> > + return 2;
> > + if (is_sw_insn (ival) || is_fsw_insn (ival))
> > + return 4;
> > + if (is_sd_insn (ival) || is_fsd_insn (ival))
> > + return 8;
> > + return 0;
> > + }
> > +
> > + /* Returns true if instruction is classified. This function can set
> > + m_error_occured. */
> > + bool
> > + try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
> > + {
> > + gdb_assert (regcache != nullptr);
> > +
> > + mem_addr addr = mem_addr{};
> > + mem_len len = need_save_pc_mem (ival);
> > + if (len <= 0)
> > + return false;
> > +
> > + mem_len offset = EXTRACT_STYPE_IMM (ival);
> > + return (!read_reg (regcache, decode_rs1 (ival), addr)
> > + || !save_mem (addr + offset, len) || set_ordinary_record_type ());
> > + }
> > +
> > + /* Returns the size of the memory chunk that needs to be saved if the
> > + instruction belongs to the group that needs only saving pc, rd and memory.
> > + Otherwise returns 0. */
> > + static mem_len
> > + need_save_pc_rd_mem (ULONGEST ival) noexcept
> > + {
> > + if (is_sc_w_insn (ival) || is_amoswap_w_insn (ival)
> > + || is_amoadd_w_insn (ival) || is_amoxor_w_insn (ival)
> > + || is_amoand_w_insn (ival) || is_amoor_w_insn (ival)
> > + || is_amomin_w_insn (ival) || is_amomax_w_insn (ival)
> > + || is_amominu_w_insn (ival) || is_amomaxu_w_insn (ival))
> > + return 4;
> > + if (is_sc_d_insn (ival) || is_amoswap_d_insn (ival)
> > + || is_amoadd_d_insn (ival) || is_amoxor_d_insn (ival)
> > + || is_amoand_d_insn (ival) || is_amoor_d_insn (ival)
> > + || is_amomin_d_insn (ival) || is_amomax_d_insn (ival)
> > + || is_amominu_d_insn (ival) || is_amomaxu_d_insn (ival))
> > + return 8;
> > + return 0;
> > + }
> > +
> > + /* Returns true if instruction is classified. This function can set
> > + m_error_occured. */
> > + bool
> > + try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
> > + {
> > + gdb_assert (regcache != nullptr);
> > +
> > + mem_len len = need_save_pc_rd_mem (ival);
> > + mem_addr addr = 0;
> > + if (len <= 0)
> > + return false;
> > +
> > + return (!read_reg (regcache, decode_rs1 (ival), addr)
> > + || !save_mem (addr, len) || !save_reg (decode_rd (ival))
> > + || set_ordinary_record_type ());
> > + }
> > +
> > + /* Returns true if instruction is successfully recordered. The length of
> > + the instruction must be equal 4 bytes. */
> > + bool
> > + record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept
> > + {
> > + gdb_assert (regcache != nullptr);
> > +
> > + if (is_ecall_insn (ival))
> > + {
> > + m_record_type = record_type::ECALL;
> > + return true;
> > + }
> > +
> > + if (is_ebreak_insn (ival))
> > + {
> > + m_record_type = record_type::EBREAK;
> > + return true;
> > + }
> > +
> > + if (try_save_pc (ival) || try_save_pc_rd (ival) || try_save_pc_fprd (ival)
> > + || try_save_pc_rd_csr (ival) || try_save_pc_mem (ival, regcache)
> > + || try_save_pc_rd_mem (ival, regcache))
> > + return !has_error ();
> > +
> > + warning (_("Currently this instruction with len 4(%lx) is unsupported"),
> > + ival);
> > + return false;
> > + }
> > +
> > + /* Returns true if instruction is successfully recordered. The length of
> > + the instruction must be equal 2 bytes. */
> > + bool
> > + record_insn_len2 (ULONGEST ival, struct regcache *regcache) noexcept
> > + {
> > + gdb_assert (regcache != nullptr);
> > +
> > + mem_addr addr = mem_addr{};
> > +
> > + /* The order here is very important, because
> > + opcodes of some instructions may be the same. */
> > +
> > + if (is_c_addi4spn_insn (ival) || is_c_lw_insn (ival)
> > + || (m_xlen == 8 && is_c_ld_insn (ival)))
> > + return (!save_reg (decode_crs2_short (ival))
> > + || set_ordinary_record_type ());
> > +
> > + if (is_c_fld_insn (ival) || (m_xlen == 4 && is_c_flw_insn (ival)))
> > + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival))
> > + || set_ordinary_record_type ());
> > +
> > + if (is_c_fsd_insn (ival) || (m_xlen == 8 && is_c_sd_insn (ival)))
> > + {
> > + ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LD_IMM (ival)};
> > + return (!read_reg (regcache, decode_crs1_short (ival), addr)
> > + || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
> > + }
> > +
> > + if ((m_xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
> > + {
> > + ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LW_IMM (ival)};
> > + return (!read_reg (regcache, decode_crs1_short (ival), addr)
> > + || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
> > + }
> > +
> > + if (is_c_nop_insn (ival))
> > + return set_ordinary_record_type ();
> > +
> > + if (is_c_addi_insn (ival))
> > + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> > +
> > + if (m_xlen == 4 && is_c_jal_insn (ival))
> > + return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
> > +
> > + if ((m_xlen == 8 && is_c_addiw_insn (ival)) || is_c_li_insn (ival))
> > + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> > +
> > + if (is_c_addi16sp_insn (ival))
> > + return (!save_reg (RISCV_SP_REGNUM) || set_ordinary_record_type ());
> > +
> > + if (is_c_lui_insn (ival))
> > + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> > +
> > + if (is_c_srli_insn (ival) || is_c_srai_insn (ival) || is_c_andi_insn (ival)
> > + || is_c_sub_insn (ival) || is_c_xor_insn (ival) || is_c_or_insn (ival)
> > + || is_c_and_insn (ival) || (m_xlen == 8 && is_c_subw_insn (ival))
> > + || (m_xlen == 8 && is_c_addw_insn (ival)))
> > + return (!save_reg (decode_crs1_short (ival))
> > + || set_ordinary_record_type ());
> > +
> > + if (is_c_j_insn (ival) || is_c_beqz_insn (ival) || is_c_bnez_insn (ival))
> > + return set_ordinary_record_type ();
> > +
> > + if (is_c_slli_insn (ival))
> > + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> > +
> > + if (is_c_fldsp_insn (ival) || (m_xlen == 4 && is_c_flwsp_insn (ival)))
> > + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival))
> > + || set_ordinary_record_type ());
> > +
> > + if (is_c_lwsp_insn (ival) || (m_xlen == 8 && is_c_ldsp_insn (ival)))
> > + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> > +
> > + if (is_c_jr_insn (ival))
> > + return set_ordinary_record_type ();
> > +
> > + if (is_c_mv_insn (ival))
> > + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> > +
> > + if (is_c_ebreak_insn (ival))
> > + {
> > + m_record_type = record_type::EBREAK;
> > + return true;
> > + }
> > +
> > + if (is_c_jalr_insn (ival))
> > + return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
> > +
> > + if (is_c_add_insn (ival))
> > + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> > +
> > + if (is_c_fsdsp_insn (ival) || (m_xlen == 8 && is_c_sdsp_insn (ival)))
> > + {
> > + ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SDSP_IMM (ival)};
> > + return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
> > + || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
> > + }
> > +
> > + if (is_c_swsp_insn (ival) || (m_xlen == 4 && is_c_fswsp_insn (ival)))
> > + {
> > + ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SWSP_IMM (ival)};
> > + return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
> > + || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
> > + }
> > +
> > + warning (_("Currently this instruction with len 2(%lx) is unsupported"),
> > + ival);
> > + return false;
> > + }
> > +
> > +public:
> > + /* Iterator for registers that need to be recorded. */
> > + using regs_iter = recorded_regs::const_iterator;
> > + /* Iterator for memory chunks that need to be recorded. */
> > + using mems_iter = recorded_mems::const_iterator;
> > +
> > + /* Record instruction at address addr. Returns false if error happened. */
> > + bool
> > + record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
> > + {
> > + gdb_assert (gdbarch != nullptr);
> > + gdb_assert (regcache != nullptr);
> > +
> > + int m_length = 0;
> > + m_xlen = riscv_isa_xlen (gdbarch);
> > + ULONGEST ival = riscv_insn::fetch_instruction (gdbarch, addr, &m_length);
> > + if (!save_reg (RISCV_PC_REGNUM))
> > + return false;
> > +
> > + if (m_length == 4)
> > + return record_insn_len4 (ival, regcache);
> > +
> > + if (m_length == 2)
> > + return record_insn_len2 (ival, regcache);
> > +
> > + /* 6 bytes or more. If the instruction is longer than 8 bytes, we don't
> > + have full instruction bits in ival. At least, such long instructions
> > + are not defined yet, so just ignore it. */
> > + gdb_assert (m_length > 0 && m_length % 2 == 0);
> > +
> > + warning (_("Can not record unknown instruction (opcode = %lx)"), ival);
> > + return false;
> > + }
> > +
> > + /* Get record type of instruction. */
> > + record_type
> > + get_record_type () const noexcept
> > + {
> > + return m_record_type;
> > + }
> > +
> > + /* Returns an iterator to the beginning of the registers that need
> > + to be saved. */
> > + regs_iter
> > + regs_begin () const noexcept
> > + {
> > + return m_regs.begin ();
> > + }
> > +
> > + /* Returns an iterator to the end of the registers that need
> > + to be saved. */
> > + regs_iter
> > + regs_end () const noexcept
> > + {
> > + return m_regs.end ();
> > + }
> > +
> > + /* Returns an iterator to the beginning of the memory chunks that need
> > + to be saved. */
> > + mems_iter
> > + mems_begin () const noexcept
> > + {
> > + return m_mems.begin ();
> > + }
> > +
> > + /* Returns an iterator to the end of the memory chunks that need
> > + to be saved. */
> > + mems_iter
> > + mems_end () const noexcept
> > + {
> > + return m_mems.end ();
> > + }
> > +};
> > +
> > +/* A helper function to record instruction using record API. */
> > +
> > +static int
> > +riscv_record_insn_details (struct gdbarch *gdbarch, struct regcache *regcache,
> > + const riscv_recorded_insn &insn)
> > +{
> > + gdb_assert (gdbarch != nullptr);
> > + gdb_assert (regcache != nullptr);
> > +
> > + riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> > + auto regs_begin = insn.regs_begin ();
> > + auto regs_end = insn.regs_end ();
> > + if (std::any_of (regs_begin,
> > + regs_end,
> > + [®cache] (auto &®_it)
> > + {
> > + return record_full_arch_list_add_reg (regcache, reg_it);
> > + }))
> > + return -1;
> > +
> > + auto mems_begin = insn.mems_begin ();
> > + auto mems_end = insn.mems_end ();
> > + if (std::any_of (mems_begin,
> > + mems_end,
> > + [] (auto &&mem_it)
> > + {
> > + return record_full_arch_list_add_mem (mem_it.first,
> > + mem_it.second);
> > + }))
> > + return -1;
> > +
> > + switch (insn.get_record_type ())
> > + {
> > + case riscv_recorded_insn::record_type::ORDINARY:
> > + break;
> > +
> > + case riscv_recorded_insn::record_type::ECALL:
> > + {
> > + if (!tdep->riscv_syscall_record)
> > + {
> > + warning (_("Syscall record is not supported"));
> > + return -1;
> > + }
> > + ULONGEST reg_val = ULONGEST{};
> > + if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
> > + return -1;
> > + return tdep->riscv_syscall_record (regcache, reg_val);
> > + }
> > +
> > + case riscv_recorded_insn::record_type::EBREAK:
> > + break;
> > +
> > + default:
> > + return -1;
> > + }
> > + return 0;
> > +}
> > +
> > +/* Parse the current instruction and record the values of the registers and
> > + memory that will be changed in current instruction to record_arch_list.
> > + Return -1 if something is wrong. */
> > +
> > +int
> > +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
> > + CORE_ADDR addr)
> > +{
> > + gdb_assert (gdbarch != nullptr);
> > + gdb_assert (regcache != nullptr);
> > +
> > + riscv_recorded_insn insn;
> > + if (!insn.record (gdbarch, regcache, addr))
> > + {
> > + record_full_arch_list_add_end ();
> > + return -1;
> > + }
> > +
> > + int ret_val = riscv_record_insn_details (gdbarch, regcache, insn);
> > +
> > + if (record_full_arch_list_add_end ())
> > + return -1;
> > +
> > + return ret_val;
> > +}
> > diff --git a/gdb/riscv-tdep.h b/gdb/riscv-tdep.h
> > index ad1e9596b83..2903aefd007 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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> > new file mode 100755
> > index 00000000000..30e52b7e472
> > --- /dev/null
> > +++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> > @@ -0,0 +1,163 @@
> > +#!/usr/bin/env python3
> > +# pylint: disable=invalid-name
> > +
> > +# Copyright (C) 2024-2025 Free Software Foundation, Inc.
> > +# Contributed by Timur Golubovich
> > +
> > +# This file is part of GDB.
> > +
> > +# This program is free software; you can redistribute it and/or modify
> > +# it under the terms of the GNU General Public License as published by
> > +# the Free Software Foundation; either version 3 of the License, or
> > +# (at your option) any later version.
> > +
> > +# This program is distributed in the hope that it will be useful,
> > +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > +# GNU General Public License for more details.
> > +
> > +# You should have received a copy of the GNU General Public License
> > +# along with this program. If not, see <http://www.gnu.org/licenses/>.
> > +
> > +
> > +# To get help message for this script, run:
> > +# ./gdb/syscalls/riscv-canonicalize-syscall-gen.py --help
> > +
> > +# Execution result:
> > +
> > +# usage: riscv-canonicalize-syscall-gen.py [-h] -i INPUT
> > +#
> > +# Generate file gdb/riscv-canonicalize-syscall-gen.c from path to riscv linux syscalls.
> > +#
> > +# options:
> > +# -h, --help show this help message and exit
> > +# -i INPUT, --input INPUT
> > +# path to riscv linux syscalls (glibc/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h)
> > +
> > +import argparse
> > +import re
> > +import sys
> > +from pathlib import Path as _Path
> > +
> > +head = """\
> > +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py
> > +
> > + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> > +
> > + This file is part of GDB.
> > +
> > + This program is free software; you can redistribute it and/or modify
> > + it under the terms of the GNU General Public License as published by
> > + the Free Software Foundation; either version 3 of the License, or
> > + (at your option) any later version.
> > +
> > + This program is distributed in the hope that it will be useful,
> > + but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + GNU General Public License for more details.
> > +
> > + You should have received a copy of the GNU General Public License
> > + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> > +
> > +#include "defs.h"
> > +#include "riscv-linux-tdep.h"
> > +
> > +/* riscv64_canonicalize_syscall maps from the native riscv 64 Linux set
> > + of syscall ids into a canonical set of syscall ids used by
> > + process record. */
> > +
> > +enum gdb_syscall
> > +riscv64_canonicalize_syscall (int syscall)
> > +{
> > + switch (syscall)
> > + {
> > +"""
> > +
> > +tail = """\
> > + default:
> > + return gdb_sys_no_syscall;
> > + }
> > +}
> > +"""
> > +
> > +
> > +class Generator:
> > + def _get_gdb_syscalls(self, gdb_syscalls_path: _Path) -> list[str]:
> > + gdb_syscalls: list[str] = []
> > + with open(gdb_syscalls_path, "r", encoding="UTF-8") as file:
> > + lines = file.readlines()
> > + for line in lines:
> > + match = re.search(r"\s*(?P<name>gdb_sys_[^S]+)\S*=", line)
> > + if match:
> > + gdb_syscalls.append(match.group("name").strip())
> > + return gdb_syscalls
> > +
> > + def _get_canon_syscalls_lines(self, syscalls_path: _Path, gdb_syscalls: list[str]) -> list[str]:
> > + canon_syscalls: dict[int, str] = {}
> > + with open(syscalls_path, "r", encoding="UTF-8") as file:
> > + lines = file.readlines()
> > + for line in lines:
> > + match = re.match(r"#define\s+__NR_(?P<name>[^\s]+)\s+(?P<number>\d+)", line)
> > + if match:
> > + syscall_name = match.group("name")
> > + syscall_num = int(match.group("number"))
> > + gdb_syscall_name = f"gdb_sys_{syscall_name}"
> > + if gdb_syscall_name in gdb_syscalls:
> > + value = f" case {syscall_num}: return {gdb_syscall_name};\n"
> > + canon_syscalls[syscall_num] = value
> > + # this is a place for corner cases
> > + elif syscall_name == "mmap":
> > + gdb_old_syscall_name = "gdb_old_mmap"
> > + value = f" case {syscall_num}: return {gdb_old_syscall_name};\n"
> > + canon_syscalls[syscall_num] = value
> > + else:
> > + value = f" /* case {syscall_num}: return {gdb_syscall_name}; */\n"
> > + canon_syscalls[syscall_num] = value
> > + return [canon_syscalls[syscall_num] for syscall_num in sorted(canon_syscalls)]
> > +
> > + def generate(self, syscalls_path: _Path) -> None:
> > + repo_path = _Path(__file__).parent.parent.parent
> > + gdb_syscalls_path = repo_path / "gdb" / "linux-record.h"
> > + canon_syscalls_path = repo_path / "gdb" / "riscv-canonicalize-syscall-gen.c"
> > +
> > + gdb_syscalls = self._get_gdb_syscalls(gdb_syscalls_path)
> > + canon_syscalls_lines = self._get_canon_syscalls_lines(syscalls_path, gdb_syscalls)
> > +
> > + with open(canon_syscalls_path, "w", encoding="UTF-8") as file:
> > + file.writelines(head)
> > + file.writelines(canon_syscalls_lines)
> > + file.writelines(tail)
> > +
> > +
> > +help_message = """\
> > +Generate file gdb/riscv-canonicalize-syscall-gen.c
> > +from path to riscv linux syscalls.
> > +"""
> > +
> > +
> > +def setup_parser() -> argparse.ArgumentParser:
> > + parser = argparse.ArgumentParser(description=help_message)
> > + parser.add_argument(
> > + "-i",
> > + "--input",
> > + type=_Path,
> > + required=True,
> > + help="path to riscv linux syscalls (glibc/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h)",
> > + )
> > + return parser
> > +
> > +
> > +def main(argv: list[str]) -> int:
> > + try:
> > + parser = setup_parser()
> > + args = parser.parse_args(argv)
> > + generator = Generator()
> > + generator.generate(args.input)
> > + return 0
> > + except RuntimeError as e:
> > + print(str(e))
> > + return -1
> > +
> > +
> > +if __name__ == "__main__":
> > + sys.exit(main(sys.argv[1:]))
> > diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> > index c37cc8974e2..0149125e60a 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*-*-linux*"] } {
> > return 1
> > }
> >
> > --
> > 2.34.1
>
^ permalink raw reply [flat|nested] 35+ messages in thread* Re: [PATCH v8] This commit adds record full support for rv64gc instruction set
2025-04-14 8:44 ` Timur Golubovich
@ 2025-04-14 12:03 ` Guinevere Larsen
2025-04-22 17:36 ` Timur Golubovich
1 sibling, 0 replies; 35+ messages in thread
From: Guinevere Larsen @ 2025-04-14 12:03 UTC (permalink / raw)
To: Timur Golubovich, Andrew Burgess; +Cc: gdb-patches
On 4/14/25 5:44 AM, Timur Golubovich wrote:
> Hi!
> Thank you Andrew for watching this.
> I think that now we can merge this patch?
If the copyright assignment process has been finished already, yes, this
can be merged now!
--
Cheers,
Guinevere Larsen
She/Her/Hers
>
> пн, 14 апр. 2025 г. в 10:41, Andrew Burgess <aburgess@redhat.com>:
>
>> Timur <timurgol007@gmail.com> writes:
>>
>>> It includes changes to the following files:
>>> - gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
>>> syscalls.
>>> - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
>>> rv64gc instructions.
>>> - gdb/configure.tgt: adds new files for compilation.
>>> - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
>>> targets.
>>> - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
>>> function that canonicalizes RISC-V syscall. This script can simplify support
>>> for syscalls on rv32 and rv64 system (currently support only for rv64). To
>>> use this script you need to pass a path to a file with syscalls description
>>> from riscv-glibc (example is in the help message). The script produces a
>>> mapping from syscall names to gdb_syscall enum.
>>> - gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
>>> - gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
>>> - gdb/NEWS: notification of new functionality.
>>>
>>> Approved-By: Guinevere Larsen <guinevere@redhat.com>
>>>
>>> Hi Andrew! Thank you for review.
>>>
>>>> I guess riscv-glibc is a fork of glibc that is running ahead of the
>>>> upstream glibc project?
>>>>
>>>> Is there any reason that this cannot work with the official glibc
>>>> project? That would be preferable I think unless there's a compelling
>>>> reason, which ideally we'd document.
>>>>
>>>> I tried running the script using current HEAD of glibc, the only
>>>> significant change is:
>>>>
>>>> diff --git i/gdb/riscv-canonicalize-syscall-gen.c w/gdb/riscv-canonicalize-syscall-gen.c
>>>> index 3749fc32f33..03579984f4a 100644
>>>> --- i/gdb/riscv-canonicalize-syscall-gen.c
>>>> +++ w/gdb/riscv-canonicalize-syscall-gen.c
>>>> @@ -270,7 +270,7 @@ riscv64_canonicalize_syscall (int syscall)
>>>> 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 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; */
>>>>
>>>> There was also a bunch of new syscalls that are added, but commented
>>>> out. I haven't tried to figure out if the change above is a bad thing
>>>> or not though, maybe you could advise.
>>> Yes, I agree with you, I just used riscv-glibc as I had aldready downloaded it
>>> and unfortunately the version was pretty old (I started working on this patch
>>> almost half a year ago).
>>>
>>>> This entry needs to move to the correct section of the NEWS file. If
>>>> you look for the lines starting '***' you'll see that the first header
>>>> is '*** Changes since GDB 16', this is the section where you should
>>>> place your entry, probably just before the '* New commands' line.
>>>> These include guards need to be renamed to GDB_RISCV_LINUX_TDEP_H. You
>>>> can do this automatically if you like with:
>>>>
>>>> ./gdb/check-include-guards.py --update gdb/*.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 */
>>> Addressed
>>>
>>>> @@ -3718,7 +3718,8 @@ proc supports_reverse {} {
>>>>> || [istarget "i\[34567\]86-*-linux*"]
>>>>> || [istarget "aarch64*-*-linux*"]
>>>>> || [istarget "powerpc*-*-linux*"]
>>>>> - || [istarget "s390*-*-linux*"] } {
>>>>> + || [istarget "s390*-*-linux*"]
>>>>> + || [istarget "riscv*-*-*"] } {
>>>> I notice that every other entry in this list restricts the match to
>>>> Linux based targets. Is RISC-V really different here? Or should RISC-V
>>>> be similarly restricted?
>>> Addressed, this was just my misprint.
>>>
>>> Hope that now everything is okay, wait for your feedback.
>> I scanned the fixes, they look good. I haven't looked at the rest of
>> the patch in detail, assuming it hasn't changed from the previous
>> version:
>>
>> Approved-By: Andrew Burgess <aburgess@redhat.com>
>>
>> Thanks,
>> Andrew
>>
>>
>>
>>> ---
>>> gdb/NEWS | 2 +
>>> gdb/configure.tgt | 5 +-
>>> gdb/doc/gdb.texinfo | 2 +-
>>> gdb/riscv-canonicalize-syscall-gen.c | 358 +++++++++
>>> gdb/riscv-linux-tdep.c | 256 +++++++
>>> gdb/riscv-linux-tdep.h | 29 +
>>> gdb/riscv-tdep.c | 686 +++++++++++++++++-
>>> gdb/riscv-tdep.h | 16 +-
>>> .../riscv-canonicalize-syscall-gen.py | 163 +++++
>>> gdb/testsuite/lib/gdb.exp | 3 +-
>>> 10 files changed, 1510 insertions(+), 10 deletions(-)
>>> create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
>>> create mode 100644 gdb/riscv-linux-tdep.h
>>> create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
>>>
>>> diff --git a/gdb/NEWS b/gdb/NEWS
>>> index 99ec392d4c4..1a05a9f9251 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/configure.tgt b/gdb/configure.tgt
>>> index 18a15c032c3..44da2257be8 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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
>>> index b251c8e1228..73f3b14cf74 100644
>>> --- a/gdb/doc/gdb.texinfo
>>> +++ b/gdb/doc/gdb.texinfo
>>> @@ -7807,7 +7807,7 @@ 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
>>> +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}.
>>>
>>> diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
>>> new file mode 100644
>>> index 00000000000..03579984f4a
>>> --- /dev/null
>>> +++ b/gdb/riscv-canonicalize-syscall-gen.c
>>> @@ -0,0 +1,358 @@
>>> +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py
>>> +
>>> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
>>> +
>>> + This file is part of GDB.
>>> +
>>> + This program is free software; you can redistribute it and/or modify
>>> + it under the terms of the GNU General Public License as published by
>>> + the Free Software Foundation; either version 3 of the License, or
>>> + (at your option) any later version.
>>> +
>>> + This program is distributed in the hope that it will be useful,
>>> + but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>> + GNU General Public License for more details.
>>> +
>>> + You should have received a copy of the GNU General Public License
>>> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
>>> +
>>> +#include "defs.h"
>>> +#include "riscv-linux-tdep.h"
>>> +
>>> +/* riscv64_canonicalize_syscall maps from the native riscv 64 Linux set
>>> + of syscall ids into a canonical set of syscall ids used by
>>> + process record. */
>>> +
>>> +enum gdb_syscall
>>> +riscv64_canonicalize_syscall (int syscall)
>>> +{
>>> + switch (syscall)
>>> + {
>>> + case 0: return gdb_sys_io_setup;
>>> + case 1: return gdb_sys_io_destroy;
>>> + case 2: return gdb_sys_io_submit;
>>> + case 3: return gdb_sys_io_cancel;
>>> + case 4: return gdb_sys_io_getevents;
>>> + case 5: return gdb_sys_setxattr;
>>> + case 6: return gdb_sys_lsetxattr;
>>> + case 7: return gdb_sys_fsetxattr;
>>> + case 8: return gdb_sys_getxattr;
>>> + case 9: return gdb_sys_lgetxattr;
>>> + case 10: return gdb_sys_fgetxattr;
>>> + case 11: return gdb_sys_listxattr;
>>> + case 12: return gdb_sys_llistxattr;
>>> + case 13: return gdb_sys_flistxattr;
>>> + case 14: return gdb_sys_removexattr;
>>> + case 15: return gdb_sys_lremovexattr;
>>> + case 16: return gdb_sys_fremovexattr;
>>> + case 17: return gdb_sys_getcwd;
>>> + case 18: return gdb_sys_lookup_dcookie;
>>> + case 19: return gdb_sys_eventfd2;
>>> + case 20: return gdb_sys_epoll_create1;
>>> + case 21: return gdb_sys_epoll_ctl;
>>> + case 22: return gdb_sys_epoll_pwait;
>>> + case 23: return gdb_sys_dup;
>>> + case 24: return gdb_sys_dup3;
>>> + case 25: return gdb_sys_fcntl;
>>> + case 26: return gdb_sys_inotify_init1;
>>> + case 27: return gdb_sys_inotify_add_watch;
>>> + case 28: return gdb_sys_inotify_rm_watch;
>>> + case 29: return gdb_sys_ioctl;
>>> + case 30: return gdb_sys_ioprio_set;
>>> + case 31: return gdb_sys_ioprio_get;
>>> + case 32: return gdb_sys_flock;
>>> + case 33: return gdb_sys_mknodat;
>>> + case 34: return gdb_sys_mkdirat;
>>> + case 35: return gdb_sys_unlinkat;
>>> + case 36: return gdb_sys_symlinkat;
>>> + case 37: return gdb_sys_linkat;
>>> + /* case 39: return gdb_sys_umount2; */
>>> + case 40: return gdb_sys_mount;
>>> + case 41: return gdb_sys_pivot_root;
>>> + case 42: return gdb_sys_nfsservctl;
>>> + case 43: return gdb_sys_statfs;
>>> + case 44: return gdb_sys_fstatfs;
>>> + case 45: return gdb_sys_truncate;
>>> + case 46: return gdb_sys_ftruncate;
>>> + case 47: return gdb_sys_fallocate;
>>> + case 48: return gdb_sys_faccessat;
>>> + case 49: return gdb_sys_chdir;
>>> + case 50: return gdb_sys_fchdir;
>>> + case 51: return gdb_sys_chroot;
>>> + case 52: return gdb_sys_fchmod;
>>> + case 53: return gdb_sys_fchmodat;
>>> + case 54: return gdb_sys_fchownat;
>>> + case 55: return gdb_sys_fchown;
>>> + case 56: return gdb_sys_openat;
>>> + case 57: return gdb_sys_close;
>>> + case 58: return gdb_sys_vhangup;
>>> + case 59: return gdb_sys_pipe2;
>>> + case 60: return gdb_sys_quotactl;
>>> + case 61: return gdb_sys_getdents64;
>>> + case 62: return gdb_sys_lseek;
>>> + case 63: return gdb_sys_read;
>>> + case 64: return gdb_sys_write;
>>> + case 65: return gdb_sys_readv;
>>> + case 66: return gdb_sys_writev;
>>> + case 67: return gdb_sys_pread64;
>>> + case 68: return gdb_sys_pwrite64;
>>> + /* case 69: return gdb_sys_preadv; */
>>> + /* case 70: return gdb_sys_pwritev; */
>>> + case 71: return gdb_sys_sendfile;
>>> + case 72: return gdb_sys_pselect6;
>>> + case 73: return gdb_sys_ppoll;
>>> + /* case 74: return gdb_sys_signalfd4; */
>>> + case 75: return gdb_sys_vmsplice;
>>> + case 76: return gdb_sys_splice;
>>> + case 77: return gdb_sys_tee;
>>> + case 78: return gdb_sys_readlinkat;
>>> + case 79: return gdb_sys_newfstatat;
>>> + case 80: return gdb_sys_fstat;
>>> + case 81: return gdb_sys_sync;
>>> + case 82: return gdb_sys_fsync;
>>> + case 83: return gdb_sys_fdatasync;
>>> + case 84: return gdb_sys_sync_file_range;
>>> + /* case 85: return gdb_sys_timerfd_create; */
>>> + /* case 86: return gdb_sys_timerfd_settime; */
>>> + /* case 87: return gdb_sys_timerfd_gettime; */
>>> + /* case 88: return gdb_sys_utimensat; */
>>> + case 89: return gdb_sys_acct;
>>> + case 90: return gdb_sys_capget;
>>> + case 91: return gdb_sys_capset;
>>> + case 92: return gdb_sys_personality;
>>> + case 93: return gdb_sys_exit;
>>> + case 94: return gdb_sys_exit_group;
>>> + case 95: return gdb_sys_waitid;
>>> + case 96: return gdb_sys_set_tid_address;
>>> + case 97: return gdb_sys_unshare;
>>> + case 98: return gdb_sys_futex;
>>> + case 99: return gdb_sys_set_robust_list;
>>> + case 100: return gdb_sys_get_robust_list;
>>> + case 101: return gdb_sys_nanosleep;
>>> + case 102: return gdb_sys_getitimer;
>>> + case 103: return gdb_sys_setitimer;
>>> + case 104: return gdb_sys_kexec_load;
>>> + case 105: return gdb_sys_init_module;
>>> + case 106: return gdb_sys_delete_module;
>>> + case 107: return gdb_sys_timer_create;
>>> + case 108: return gdb_sys_timer_gettime;
>>> + case 109: return gdb_sys_timer_getoverrun;
>>> + case 110: return gdb_sys_timer_settime;
>>> + case 111: return gdb_sys_timer_delete;
>>> + case 112: return gdb_sys_clock_settime;
>>> + case 113: return gdb_sys_clock_gettime;
>>> + case 114: return gdb_sys_clock_getres;
>>> + case 115: return gdb_sys_clock_nanosleep;
>>> + case 116: return gdb_sys_syslog;
>>> + case 117: return gdb_sys_ptrace;
>>> + case 118: return gdb_sys_sched_setparam;
>>> + case 119: return gdb_sys_sched_setscheduler;
>>> + case 120: return gdb_sys_sched_getscheduler;
>>> + case 121: return gdb_sys_sched_getparam;
>>> + case 122: return gdb_sys_sched_setaffinity;
>>> + case 123: return gdb_sys_sched_getaffinity;
>>> + case 124: return gdb_sys_sched_yield;
>>> + case 125: return gdb_sys_sched_get_priority_max;
>>> + case 126: return gdb_sys_sched_get_priority_min;
>>> + case 127: return gdb_sys_sched_rr_get_interval;
>>> + case 128: return gdb_sys_restart_syscall;
>>> + case 129: return gdb_sys_kill;
>>> + case 130: return gdb_sys_tkill;
>>> + case 131: return gdb_sys_tgkill;
>>> + case 132: return gdb_sys_sigaltstack;
>>> + case 133: return gdb_sys_rt_sigsuspend;
>>> + case 134: return gdb_sys_rt_sigaction;
>>> + case 135: return gdb_sys_rt_sigprocmask;
>>> + case 136: return gdb_sys_rt_sigpending;
>>> + case 137: return gdb_sys_rt_sigtimedwait;
>>> + case 138: return gdb_sys_rt_sigqueueinfo;
>>> + case 139: return gdb_sys_rt_sigreturn;
>>> + case 140: return gdb_sys_setpriority;
>>> + case 141: return gdb_sys_getpriority;
>>> + case 142: return gdb_sys_reboot;
>>> + case 143: return gdb_sys_setregid;
>>> + case 144: return gdb_sys_setgid;
>>> + case 145: return gdb_sys_setreuid;
>>> + case 146: return gdb_sys_setuid;
>>> + case 147: return gdb_sys_setresuid;
>>> + case 148: return gdb_sys_getresuid;
>>> + case 149: return gdb_sys_setresgid;
>>> + case 150: return gdb_sys_getresgid;
>>> + case 151: return gdb_sys_setfsuid;
>>> + case 152: return gdb_sys_setfsgid;
>>> + case 153: return gdb_sys_times;
>>> + case 154: return gdb_sys_setpgid;
>>> + case 155: return gdb_sys_getpgid;
>>> + case 156: return gdb_sys_getsid;
>>> + case 157: return gdb_sys_setsid;
>>> + case 158: return gdb_sys_getgroups;
>>> + case 159: return gdb_sys_setgroups;
>>> + case 160: return gdb_sys_uname;
>>> + case 161: return gdb_sys_sethostname;
>>> + case 162: return gdb_sys_setdomainname;
>>> + case 163: return gdb_sys_getrlimit;
>>> + case 164: return gdb_sys_setrlimit;
>>> + case 165: return gdb_sys_getrusage;
>>> + case 166: return gdb_sys_umask;
>>> + case 167: return gdb_sys_prctl;
>>> + case 168: return gdb_sys_getcpu;
>>> + case 169: return gdb_sys_gettimeofday;
>>> + case 170: return gdb_sys_settimeofday;
>>> + case 171: return gdb_sys_adjtimex;
>>> + case 172: return gdb_sys_getpid;
>>> + case 173: return gdb_sys_getppid;
>>> + case 174: return gdb_sys_getuid;
>>> + case 175: return gdb_sys_geteuid;
>>> + case 176: return gdb_sys_getgid;
>>> + case 177: return gdb_sys_getegid;
>>> + case 178: return gdb_sys_gettid;
>>> + case 179: return gdb_sys_sysinfo;
>>> + case 180: return gdb_sys_mq_open;
>>> + case 181: return gdb_sys_mq_unlink;
>>> + case 182: return gdb_sys_mq_timedsend;
>>> + case 183: return gdb_sys_mq_timedreceive;
>>> + case 184: return gdb_sys_mq_notify;
>>> + case 185: return gdb_sys_mq_getsetattr;
>>> + case 186: return gdb_sys_msgget;
>>> + case 187: return gdb_sys_msgctl;
>>> + case 188: return gdb_sys_msgrcv;
>>> + case 189: return gdb_sys_msgsnd;
>>> + case 190: return gdb_sys_semget;
>>> + case 191: return gdb_sys_semctl;
>>> + case 192: return gdb_sys_semtimedop;
>>> + case 193: return gdb_sys_semop;
>>> + case 194: return gdb_sys_shmget;
>>> + case 195: return gdb_sys_shmctl;
>>> + case 196: return gdb_sys_shmat;
>>> + case 197: return gdb_sys_shmdt;
>>> + case 198: return gdb_sys_socket;
>>> + case 199: return gdb_sys_socketpair;
>>> + case 200: return gdb_sys_bind;
>>> + case 201: return gdb_sys_listen;
>>> + case 202: return gdb_sys_accept;
>>> + case 203: return gdb_sys_connect;
>>> + case 204: return gdb_sys_getsockname;
>>> + case 205: return gdb_sys_getpeername;
>>> + case 206: return gdb_sys_sendto;
>>> + case 207: return gdb_sys_recvfrom;
>>> + case 208: return gdb_sys_setsockopt;
>>> + case 209: return gdb_sys_getsockopt;
>>> + case 210: return gdb_sys_shutdown;
>>> + case 211: return gdb_sys_sendmsg;
>>> + case 212: return gdb_sys_recvmsg;
>>> + case 213: return gdb_sys_readahead;
>>> + case 214: return gdb_sys_brk;
>>> + case 215: return gdb_sys_munmap;
>>> + case 216: return gdb_sys_mremap;
>>> + case 217: return gdb_sys_add_key;
>>> + case 218: return gdb_sys_request_key;
>>> + case 219: return gdb_sys_keyctl;
>>> + case 220: return gdb_sys_clone;
>>> + case 221: return gdb_sys_execve;
>>> + case 222: return gdb_old_mmap;
>>> + case 223: return gdb_sys_fadvise64;
>>> + case 224: return gdb_sys_swapon;
>>> + case 225: return gdb_sys_swapoff;
>>> + case 226: return gdb_sys_mprotect;
>>> + case 227: return gdb_sys_msync;
>>> + case 228: return gdb_sys_mlock;
>>> + case 229: return gdb_sys_munlock;
>>> + case 230: return gdb_sys_mlockall;
>>> + case 231: return gdb_sys_munlockall;
>>> + case 232: return gdb_sys_mincore;
>>> + case 233: return gdb_sys_madvise;
>>> + case 234: return gdb_sys_remap_file_pages;
>>> + case 235: return gdb_sys_mbind;
>>> + case 236: return gdb_sys_get_mempolicy;
>>> + case 237: return gdb_sys_set_mempolicy;
>>> + case 238: return gdb_sys_migrate_pages;
>>> + case 239: return gdb_sys_move_pages;
>>> + /* case 240: return gdb_sys_rt_tgsigqueueinfo; */
>>> + /* case 241: return gdb_sys_perf_event_open; */
>>> + case 242: return gdb_sys_accept4;
>>> + /* case 243: return gdb_sys_recvmmsg; */
>>> + /* case 258: return gdb_sys_riscv_hwprobe; */
>>> + /* case 259: return gdb_sys_riscv_flush_icache; */
>>> + case 260: return gdb_sys_wait4;
>>> + /* case 261: return gdb_sys_prlimit64; */
>>> + /* case 262: return gdb_sys_fanotify_init; */
>>> + /* case 263: return gdb_sys_fanotify_mark; */
>>> + /* case 264: return gdb_sys_name_to_handle_at; */
>>> + /* case 265: return gdb_sys_open_by_handle_at; */
>>> + /* case 266: return gdb_sys_clock_adjtime; */
>>> + /* case 267: return gdb_sys_syncfs; */
>>> + /* case 268: return gdb_sys_setns; */
>>> + /* case 269: return gdb_sys_sendmmsg; */
>>> + /* case 270: return gdb_sys_process_vm_readv; */
>>> + /* case 271: return gdb_sys_process_vm_writev; */
>>> + /* case 272: return gdb_sys_kcmp; */
>>> + /* case 273: return gdb_sys_finit_module; */
>>> + /* case 274: return gdb_sys_sched_setattr; */
>>> + /* case 275: return gdb_sys_sched_getattr; */
>>> + /* case 276: return gdb_sys_renameat2; */
>>> + /* case 277: return gdb_sys_seccomp; */
>>> + case 278: return gdb_sys_getrandom;
>>> + /* case 279: return gdb_sys_memfd_create; */
>>> + /* case 280: return gdb_sys_bpf; */
>>> + /* case 281: return gdb_sys_execveat; */
>>> + /* case 282: return gdb_sys_userfaultfd; */
>>> + /* case 283: return gdb_sys_membarrier; */
>>> + /* case 284: return gdb_sys_mlock2; */
>>> + /* case 285: return gdb_sys_copy_file_range; */
>>> + /* case 286: return gdb_sys_preadv2; */
>>> + /* case 287: return gdb_sys_pwritev2; */
>>> + /* case 288: return gdb_sys_pkey_mprotect; */
>>> + /* case 289: return gdb_sys_pkey_alloc; */
>>> + /* case 290: return gdb_sys_pkey_free; */
>>> + case 291: return gdb_sys_statx;
>>> + /* case 292: return gdb_sys_io_pgetevents; */
>>> + /* case 293: return gdb_sys_rseq; */
>>> + /* case 294: return gdb_sys_kexec_file_load; */
>>> + /* case 424: return gdb_sys_pidfd_send_signal; */
>>> + /* case 425: return gdb_sys_io_uring_setup; */
>>> + /* case 426: return gdb_sys_io_uring_enter; */
>>> + /* case 427: return gdb_sys_io_uring_register; */
>>> + /* case 428: return gdb_sys_open_tree; */
>>> + /* case 429: return gdb_sys_move_mount; */
>>> + /* case 430: return gdb_sys_fsopen; */
>>> + /* case 431: return gdb_sys_fsconfig; */
>>> + /* case 432: return gdb_sys_fsmount; */
>>> + /* case 433: return gdb_sys_fspick; */
>>> + /* case 434: return gdb_sys_pidfd_open; */
>>> + /* case 435: return gdb_sys_clone3; */
>>> + /* case 436: return gdb_sys_close_range; */
>>> + /* case 437: return gdb_sys_openat2; */
>>> + /* case 438: return gdb_sys_pidfd_getfd; */
>>> + /* case 439: return gdb_sys_faccessat2; */
>>> + /* case 440: return gdb_sys_process_madvise; */
>>> + /* case 441: return gdb_sys_epoll_pwait2; */
>>> + /* case 442: return gdb_sys_mount_setattr; */
>>> + /* case 443: return gdb_sys_quotactl_fd; */
>>> + /* case 444: return gdb_sys_landlock_create_ruleset; */
>>> + /* case 445: return gdb_sys_landlock_add_rule; */
>>> + /* case 446: return gdb_sys_landlock_restrict_self; */
>>> + /* case 447: return gdb_sys_memfd_secret; */
>>> + /* case 448: return gdb_sys_process_mrelease; */
>>> + /* case 449: return gdb_sys_futex_waitv; */
>>> + /* case 450: return gdb_sys_set_mempolicy_home_node; */
>>> + /* case 451: return gdb_sys_cachestat; */
>>> + /* case 452: return gdb_sys_fchmodat2; */
>>> + /* case 453: return gdb_sys_map_shadow_stack; */
>>> + /* case 454: return gdb_sys_futex_wake; */
>>> + /* case 455: return gdb_sys_futex_wait; */
>>> + /* case 456: return gdb_sys_futex_requeue; */
>>> + /* case 457: return gdb_sys_statmount; */
>>> + /* case 458: return gdb_sys_listmount; */
>>> + /* case 459: return gdb_sys_lsm_get_self_attr; */
>>> + /* case 460: return gdb_sys_lsm_set_self_attr; */
>>> + /* case 461: return gdb_sys_lsm_list_modules; */
>>> + /* case 462: return gdb_sys_mseal; */
>>> + /* case 463: return gdb_sys_setxattrat; */
>>> + /* case 464: return gdb_sys_getxattrat; */
>>> + /* case 465: return gdb_sys_listxattrat; */
>>> + /* case 466: return gdb_sys_removexattrat; */
>>> + default:
>>> + return gdb_sys_no_syscall;
>>> + }
>>> +}
>>> diff --git a/gdb/riscv-linux-tdep.c b/gdb/riscv-linux-tdep.c
>>> index 4c0c65c1457..f21039a613c 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 00000000000..9dd9e373caa
>>> --- /dev/null
>>> +++ b/gdb/riscv-linux-tdep.h
>>> @@ -0,0 +1,29 @@
>>> +/* Copyright (C) 2024-2025 Free Software Foundation, Inc.
>>> +
>>> + This file is part of GDB.
>>> +
>>> + This program is free software; you can redistribute it and/or modify
>>> + it under the terms of the GNU General Public License as published by
>>> + the Free Software Foundation; either version 3 of the License, or
>>> + (at your option) any later version.
>>> +
>>> + This program is distributed in the hope that it will be useful,
>>> + but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>> + GNU General Public License for more details.
>>> +
>>> + You should have received a copy of the GNU General Public License
>>> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
>>> +
>>> +#ifndef GDB_RISCV_LINUX_TDEP_H
>>> +#define GDB_RISCV_LINUX_TDEP_H
>>> +
>>> +#include "linux-record.h"
>>> +
>>> +/* riscv64_canonicalize_syscall maps from the native riscv Linux set
>>> + of syscall ids into a canonical set of syscall ids used by
>>> + process record. */
>>> +
>>> +extern enum gdb_syscall riscv64_canonicalize_syscall (int syscall);
>>> +
>>> +#endif /* GDB_RISCV_LINUX_TDEP_H */
>>> diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
>>> index 91f6dffebe1..19367ff55ba 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 @@ class riscv_insn
>>> 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 @@ class riscv_insn
>>> m_rs2 = decode_register_index_short (ival, OP_SH_CRS2S);
>>> }
>>>
>>> - /* Fetch instruction from target memory at ADDR, return the content of
>>> - the instruction, and update LEN with the instruction length. */
>>> - static ULONGEST fetch_instruction (struct gdbarch *gdbarch,
>>> - CORE_ADDR addr, int *len);
>>> -
>>> /* The length of the instruction in bytes. Should be 2 or 4. */
>>> int m_length;
>>>
>>> @@ -4433,6 +4436,9 @@ riscv_gdbarch_init (struct gdbarch_info info,
>>> set_gdbarch_stap_register_indirection_suffixes
>>> (gdbarch, stap_register_indirection_suffixes);
>>>
>>> + /* Process record-replay */
>>> + set_gdbarch_process_record (gdbarch, riscv_process_record);
>>> +
>>> /* Hook in OS ABI-specific overrides, if they have been registered. */
>>> gdbarch_init_osabi (info, gdbarch);
>>>
>>> @@ -4866,3 +4872,673 @@ equivalent change in the disassembler output."),
>>> &setriscvcmdlist,
>>> &showriscvcmdlist);
>>> }
>>> +
>>> +/* A wrapper to read register under number regnum to address addr.
>>> + Returns false if error happened and makes warning. */
>>> +
>>> +static bool
>>> +try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
>>> +{
>>> + gdb_assert (regcache != nullptr);
>>> +
>>> + if (regcache->raw_read (regnum, &addr)
>>> + != register_status::REG_VALID)
>>> + {
>>> + warning (_("Can not read at address %lx"), addr);
>>> + return false;
>>> + }
>>> + return true;
>>> +}
>>> +
>>> +/* Helper class to record instruction. */
>>> +
>>> +class riscv_recorded_insn final
>>> +{
>>> +public:
>>> + /* Type for saved register. */
>>> + using regnum_type = int;
>>> + /* Type for saved memory. First is address, second is length. */
>>> + using memory_type = std::pair<CORE_ADDR, int>;
>>> +
>>> + /* Enum class that represents which type does recording belong to. */
>>> + enum class record_type
>>> + {
>>> + UNKNOWN,
>>> + ORDINARY,
>>> +
>>> + /* Corner cases. */
>>> + ECALL,
>>> + EBREAK,
>>> + };
>>> +
>>> +private:
>>> + /* Type for set of registers that need to be saved. */
>>> + using recorded_regs = std::vector<regnum_type>;
>>> + /* Type for set of memory records that need to be saved. */
>>> + using recorded_mems = std::vector<memory_type>;
>>> +
>>> + /* Type for memory address, extracted from memory_type. */
>>> + using mem_addr = decltype (std::declval<memory_type> ().first);
>>> + /* Type for memory length, extracted from memory_type. */
>>> + using mem_len = decltype (std::declval<memory_type> ().second);
>>> +
>>> + /* Record type of current instruction. */
>>> + record_type m_record_type = record_type::UNKNOWN;
>>> +
>>> + /* Flag that represents was there an error in current recording. */
>>> + bool m_error_occured = false;
>>> +
>>> + /* Set of registers that need to be recorded. */
>>> + recorded_regs m_regs;
>>> + /* Set of memory chunks that need to be recorded. */
>>> + recorded_mems m_mems;
>>> +
>>> + /* Width in bytes of the general purpose registers for GDBARCH,
>>> + where recording is happening. */
>>> + int m_xlen = 0;
>>> +
>>> + /* Helper for decode 16-bit instruction RS1. */
>>> + static regnum_type
>>> + decode_crs1_short (ULONGEST opcode) noexcept
>>> + {
>>> + return ((opcode >> OP_SH_CRS1S) & OP_MASK_CRS1S) + 8;
>>> + }
>>> +
>>> + /* Helper for decode 16-bit instruction RS2. */
>>> + static regnum_type
>>> + decode_crs2_short (ULONGEST opcode) noexcept
>>> + {
>>> + return ((opcode >> OP_SH_CRS2S) & OP_MASK_CRS2S) + 8;
>>> + }
>>> +
>>> + /* Helper for decode 16-bit instruction CRS1. */
>>> + static regnum_type
>>> + decode_crs1 (ULONGEST opcode) noexcept
>>> + {
>>> + return ((opcode >> OP_SH_RD) & OP_MASK_RD);
>>> + }
>>> +
>>> + /* Helper for decode 16-bit instruction CRS2. */
>>> + static regnum_type
>>> + decode_crs2 (ULONGEST opcode) noexcept
>>> + {
>>> + return ((opcode >> OP_SH_CRS2) & OP_MASK_CRS2);
>>> + }
>>> +
>>> + /* Helper for decode 32-bit instruction RD. */
>>> + static regnum_type
>>> + decode_rd (ULONGEST ival) noexcept
>>> + {
>>> + return (ival >> OP_SH_RD) & OP_MASK_RD;
>>> + }
>>> +
>>> + /* Helper for decode 32-bit instruction RS1. */
>>> + static regnum_type
>>> + decode_rs1 (ULONGEST ival) noexcept
>>> + {
>>> + return (ival >> OP_SH_RS1) & OP_MASK_RS1;
>>> + }
>>> +
>>> + /* Helper for decode 32-bit instruction RS2. */
>>> + static regnum_type
>>> + decode_rs2 (ULONGEST ival) noexcept
>>> + {
>>> + return (ival >> OP_SH_RS2) & OP_MASK_RS2;
>>> + }
>>> +
>>> + /* Helper for decode 32-bit instruction CSR. */
>>> + static regnum_type
>>> + decode_csr (ULONGEST ival) noexcept
>>> + {
>>> + return (ival >> OP_SH_CSR) & OP_MASK_CSR;
>>> + }
>>> +
>>> + /* Set ordinary record type. Always returns true. */
>>> + bool
>>> + set_ordinary_record_type () noexcept
>>> + {
>>> + m_record_type = record_type::ORDINARY;
>>> + return true;
>>> + }
>>> +
>>> + /* Set error happened. Always returns false. */
>>> + bool
>>> + set_error () noexcept
>>> + {
>>> + m_error_occured = true;
>>> + return false;
>>> + }
>>> +
>>> + /* Check if current recording has an error. */
>>> + bool
>>> + has_error () const noexcept
>>> + {
>>> + return m_error_occured;
>>> + }
>>> +
>>> + /* Reads register. Sets error and returns false if error happened. */
>>> + bool
>>> + read_reg (struct regcache *regcache, regnum_type reg,
>>> + ULONGEST &addr) noexcept
>>> + {
>>> + gdb_assert (regcache != nullptr);
>>> +
>>> + if (!try_read (regcache, reg, addr))
>>> + return set_error ();
>>> + return true;
>>> + }
>>> +
>>> + /* Save register. Returns true or aborts if exception happened. */
>>> + bool
>>> + save_reg (regnum_type regnum) noexcept
>>> + {
>>> + m_regs.emplace_back (regnum);
>>> + return true;
>>> + }
>>> +
>>> + /* Save memory chunk. Returns true or aborts if exception happened. */
>>> + bool
>>> + save_mem (mem_addr addr, mem_len len) noexcept
>>> + {
>>> + m_mems.emplace_back (addr, len);
>>> + return true;
>>> + }
>>> +
>>> + /* Returns true if instruction needs only saving pc. */
>>> + static bool
>>> + need_save_pc (ULONGEST ival) noexcept
>>> + {
>>> + return (is_beq_insn (ival) || is_bne_insn (ival) || is_blt_insn (ival)
>>> + || is_bge_insn (ival) || is_bltu_insn (ival) || is_bgeu_insn (ival)
>>> + || is_fence_insn (ival) || is_pause_insn (ival)
>>> + || is_fence_i_insn (ival));
>>> + }
>>> +
>>> + /* Returns true if instruction is classified. */
>>> + bool
>>> + try_save_pc (ULONGEST ival) noexcept
>>> + {
>>> + if (!need_save_pc (ival))
>>> + return false;
>>> +
>>> + return set_ordinary_record_type ();
>>> + }
>>> +
>>> + /* Returns true if instruction needs only saving pc and rd. */
>>> + static bool
>>> + need_save_pc_rd (ULONGEST ival) noexcept
>>> + {
>>> + return (is_lui_insn (ival) || is_auipc_insn (ival) || is_jal_insn (ival)
>>> + || is_jalr_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
>>> + || is_lw_insn (ival) || is_lbu_insn (ival) || is_lhu_insn (ival)
>>> + || is_addi_insn (ival) || is_slti_insn (ival)
>>> + || is_sltiu_insn (ival) || is_xori_insn (ival) || is_ori_insn (ival)
>>> + || is_andi_insn (ival) || is_slli_rv32_insn (ival)
>>> + || is_srli_rv32_insn (ival) || is_srai_rv32_insn (ival)
>>> + || is_add_insn (ival) || is_sub_insn (ival) || is_sll_insn (ival)
>>> + || is_slt_insn (ival) || is_sltu_insn (ival) || is_xor_insn (ival)
>>> + || is_srl_insn (ival) || is_sra_insn (ival) || is_or_insn (ival)
>>> + || is_and_insn (ival) || is_lwu_insn (ival) || is_ld_insn (ival)
>>> + || is_slli_insn (ival) || is_srli_insn (ival) || is_srai_insn (ival)
>>> + || is_addiw_insn (ival) || is_slliw_insn (ival)
>>> + || is_srliw_insn (ival) || is_sraiw_insn (ival)
>>> + || is_addw_insn (ival) || is_subw_insn (ival) || is_sllw_insn (ival)
>>> + || is_srlw_insn (ival) || is_sraw_insn (ival) || is_mul_insn (ival)
>>> + || is_mulh_insn (ival) || is_mulhsu_insn (ival)
>>> + || is_mulhu_insn (ival) || is_div_insn (ival) || is_divu_insn (ival)
>>> + || is_rem_insn (ival) || is_remu_insn (ival) || is_mulw_insn (ival)
>>> + || is_divw_insn (ival) || is_divuw_insn (ival)
>>> + || is_remw_insn (ival) || is_remuw_insn (ival)
>>> + || is_lr_w_insn (ival) || is_lr_d_insn (ival)
>>> + || is_fcvt_w_s_insn (ival) || is_fcvt_wu_s_insn (ival)
>>> + || is_fmv_x_s_insn (ival) || is_feq_s_insn (ival)
>>> + || is_flt_s_insn (ival) || is_fle_s_insn (ival)
>>> + || is_fclass_s_insn (ival) || is_fcvt_l_s_insn (ival)
>>> + || is_fcvt_lu_s_insn (ival) || is_feq_d_insn (ival)
>>> + || is_flt_d_insn (ival) || is_fle_d_insn (ival)
>>> + || is_fclass_d_insn (ival) || is_fcvt_w_d_insn (ival)
>>> + || is_fcvt_wu_d_insn (ival) || is_fcvt_l_d_insn (ival)
>>> + || is_fcvt_lu_d_insn (ival) || is_fmv_x_d_insn (ival));
>>> + }
>>> +
>>> + /* Returns true if instruction is classified. This function can set
>>> + m_error_occured. */
>>> + bool
>>> + try_save_pc_rd (ULONGEST ival) noexcept
>>> + {
>>> + if (!need_save_pc_rd (ival))
>>> + return false;
>>> +
>>> + return (!save_reg (decode_rd (ival)) || set_ordinary_record_type ());
>>> + }
>>> +
>>> + /* Returns true if instruction needs only saving pc and
>>> + floating point rd. */
>>> + static bool
>>> + need_save_pc_fprd (ULONGEST ival) noexcept
>>> + {
>>> + return (is_flw_insn (ival) || is_fmadd_s_insn (ival)
>>> + || is_fmsub_s_insn (ival) || is_fnmsub_s_insn (ival)
>>> + || is_fnmadd_s_insn (ival) || is_fadd_s_insn (ival)
>>> + || is_fsub_s_insn (ival) || is_fmul_s_insn (ival)
>>> + || is_fdiv_s_insn (ival) || is_fsqrt_s_insn (ival)
>>> + || is_fsgnj_s_insn (ival) || is_fsgnjn_s_insn (ival)
>>> + || is_fsgnjx_s_insn (ival) || is_fmin_s_insn (ival)
>>> + || is_fmax_s_insn (ival) || is_fcvt_s_w_insn (ival)
>>> + || is_fcvt_s_wu_insn (ival) || is_fmv_s_x_insn (ival)
>>> + || is_fcvt_s_l_insn (ival) || is_fcvt_s_lu_insn (ival)
>>> + || is_fld_insn (ival) || is_fmadd_d_insn (ival)
>>> + || is_fmsub_d_insn (ival) || is_fnmsub_d_insn (ival)
>>> + || is_fnmadd_d_insn (ival) || is_fadd_d_insn (ival)
>>> + || is_fsub_d_insn (ival) || is_fmul_d_insn (ival)
>>> + || is_fdiv_d_insn (ival) || is_fsqrt_d_insn (ival)
>>> + || is_fsgnj_d_insn (ival) || is_fsgnjn_d_insn (ival)
>>> + || is_fsgnjx_d_insn (ival) || is_fmin_d_insn (ival)
>>> + || is_fmax_d_insn (ival) || is_fcvt_s_d_insn (ival)
>>> + || is_fcvt_d_s_insn (ival) || is_fcvt_d_w_insn (ival)
>>> + || is_fcvt_d_wu_insn (ival) || is_fcvt_d_l_insn (ival)
>>> + || is_fcvt_d_lu_insn (ival) || is_fmv_d_x_insn (ival));
>>> + }
>>> +
>>> + /* Returns true if instruction is classified. This function can set
>>> + m_error_occured. */
>>> + bool
>>> + try_save_pc_fprd (ULONGEST ival) noexcept
>>> + {
>>> + if (!need_save_pc_fprd (ival))
>>> + return false;
>>> +
>>> + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_rd (ival))
>>> + || set_ordinary_record_type ());
>>> + }
>>> +
>>> + /* Returns true if instruction needs only saving pc, rd and csr. */
>>> + static bool
>>> + need_save_pc_rd_csr (ULONGEST ival) noexcept
>>> + {
>>> + return (is_csrrw_insn (ival) || is_csrrs_insn (ival) || is_csrrc_insn (ival)
>>> + || is_csrrwi_insn (ival) || is_csrrsi_insn (ival)
>>> + || is_csrrc_insn (ival));
>>> + }
>>> +
>>> + /* Returns true if instruction is classified. This function can set
>>> + m_error_occured. */
>>> + bool
>>> + try_save_pc_rd_csr (ULONGEST ival) noexcept
>>> + {
>>> + if (!need_save_pc_rd_csr (ival))
>>> + return false;
>>> +
>>> + return (!save_reg (decode_rd (ival))
>>> + || !save_reg (RISCV_FIRST_CSR_REGNUM + decode_csr (ival))
>>> + || set_ordinary_record_type ());
>>> + }
>>> +
>>> + /* Returns the size of the memory chunk that needs to be saved if the
>>> + instruction belongs to the group that needs only saving pc and memory.
>>> + Otherwise returns 0. */
>>> + static mem_len
>>> + need_save_pc_mem (ULONGEST ival) noexcept
>>> + {
>>> + if (is_sb_insn (ival))
>>> + return 1;
>>> + if (is_sh_insn (ival))
>>> + return 2;
>>> + if (is_sw_insn (ival) || is_fsw_insn (ival))
>>> + return 4;
>>> + if (is_sd_insn (ival) || is_fsd_insn (ival))
>>> + return 8;
>>> + return 0;
>>> + }
>>> +
>>> + /* Returns true if instruction is classified. This function can set
>>> + m_error_occured. */
>>> + bool
>>> + try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
>>> + {
>>> + gdb_assert (regcache != nullptr);
>>> +
>>> + mem_addr addr = mem_addr{};
>>> + mem_len len = need_save_pc_mem (ival);
>>> + if (len <= 0)
>>> + return false;
>>> +
>>> + mem_len offset = EXTRACT_STYPE_IMM (ival);
>>> + return (!read_reg (regcache, decode_rs1 (ival), addr)
>>> + || !save_mem (addr + offset, len) || set_ordinary_record_type ());
>>> + }
>>> +
>>> + /* Returns the size of the memory chunk that needs to be saved if the
>>> + instruction belongs to the group that needs only saving pc, rd and memory.
>>> + Otherwise returns 0. */
>>> + static mem_len
>>> + need_save_pc_rd_mem (ULONGEST ival) noexcept
>>> + {
>>> + if (is_sc_w_insn (ival) || is_amoswap_w_insn (ival)
>>> + || is_amoadd_w_insn (ival) || is_amoxor_w_insn (ival)
>>> + || is_amoand_w_insn (ival) || is_amoor_w_insn (ival)
>>> + || is_amomin_w_insn (ival) || is_amomax_w_insn (ival)
>>> + || is_amominu_w_insn (ival) || is_amomaxu_w_insn (ival))
>>> + return 4;
>>> + if (is_sc_d_insn (ival) || is_amoswap_d_insn (ival)
>>> + || is_amoadd_d_insn (ival) || is_amoxor_d_insn (ival)
>>> + || is_amoand_d_insn (ival) || is_amoor_d_insn (ival)
>>> + || is_amomin_d_insn (ival) || is_amomax_d_insn (ival)
>>> + || is_amominu_d_insn (ival) || is_amomaxu_d_insn (ival))
>>> + return 8;
>>> + return 0;
>>> + }
>>> +
>>> + /* Returns true if instruction is classified. This function can set
>>> + m_error_occured. */
>>> + bool
>>> + try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
>>> + {
>>> + gdb_assert (regcache != nullptr);
>>> +
>>> + mem_len len = need_save_pc_rd_mem (ival);
>>> + mem_addr addr = 0;
>>> + if (len <= 0)
>>> + return false;
>>> +
>>> + return (!read_reg (regcache, decode_rs1 (ival), addr)
>>> + || !save_mem (addr, len) || !save_reg (decode_rd (ival))
>>> + || set_ordinary_record_type ());
>>> + }
>>> +
>>> + /* Returns true if instruction is successfully recordered. The length of
>>> + the instruction must be equal 4 bytes. */
>>> + bool
>>> + record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept
>>> + {
>>> + gdb_assert (regcache != nullptr);
>>> +
>>> + if (is_ecall_insn (ival))
>>> + {
>>> + m_record_type = record_type::ECALL;
>>> + return true;
>>> + }
>>> +
>>> + if (is_ebreak_insn (ival))
>>> + {
>>> + m_record_type = record_type::EBREAK;
>>> + return true;
>>> + }
>>> +
>>> + if (try_save_pc (ival) || try_save_pc_rd (ival) || try_save_pc_fprd (ival)
>>> + || try_save_pc_rd_csr (ival) || try_save_pc_mem (ival, regcache)
>>> + || try_save_pc_rd_mem (ival, regcache))
>>> + return !has_error ();
>>> +
>>> + warning (_("Currently this instruction with len 4(%lx) is unsupported"),
>>> + ival);
>>> + return false;
>>> + }
>>> +
>>> + /* Returns true if instruction is successfully recordered. The length of
>>> + the instruction must be equal 2 bytes. */
>>> + bool
>>> + record_insn_len2 (ULONGEST ival, struct regcache *regcache) noexcept
>>> + {
>>> + gdb_assert (regcache != nullptr);
>>> +
>>> + mem_addr addr = mem_addr{};
>>> +
>>> + /* The order here is very important, because
>>> + opcodes of some instructions may be the same. */
>>> +
>>> + if (is_c_addi4spn_insn (ival) || is_c_lw_insn (ival)
>>> + || (m_xlen == 8 && is_c_ld_insn (ival)))
>>> + return (!save_reg (decode_crs2_short (ival))
>>> + || set_ordinary_record_type ());
>>> +
>>> + if (is_c_fld_insn (ival) || (m_xlen == 4 && is_c_flw_insn (ival)))
>>> + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival))
>>> + || set_ordinary_record_type ());
>>> +
>>> + if (is_c_fsd_insn (ival) || (m_xlen == 8 && is_c_sd_insn (ival)))
>>> + {
>>> + ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LD_IMM (ival)};
>>> + return (!read_reg (regcache, decode_crs1_short (ival), addr)
>>> + || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
>>> + }
>>> +
>>> + if ((m_xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
>>> + {
>>> + ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LW_IMM (ival)};
>>> + return (!read_reg (regcache, decode_crs1_short (ival), addr)
>>> + || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
>>> + }
>>> +
>>> + if (is_c_nop_insn (ival))
>>> + return set_ordinary_record_type ();
>>> +
>>> + if (is_c_addi_insn (ival))
>>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>>> +
>>> + if (m_xlen == 4 && is_c_jal_insn (ival))
>>> + return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
>>> +
>>> + if ((m_xlen == 8 && is_c_addiw_insn (ival)) || is_c_li_insn (ival))
>>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>>> +
>>> + if (is_c_addi16sp_insn (ival))
>>> + return (!save_reg (RISCV_SP_REGNUM) || set_ordinary_record_type ());
>>> +
>>> + if (is_c_lui_insn (ival))
>>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>>> +
>>> + if (is_c_srli_insn (ival) || is_c_srai_insn (ival) || is_c_andi_insn (ival)
>>> + || is_c_sub_insn (ival) || is_c_xor_insn (ival) || is_c_or_insn (ival)
>>> + || is_c_and_insn (ival) || (m_xlen == 8 && is_c_subw_insn (ival))
>>> + || (m_xlen == 8 && is_c_addw_insn (ival)))
>>> + return (!save_reg (decode_crs1_short (ival))
>>> + || set_ordinary_record_type ());
>>> +
>>> + if (is_c_j_insn (ival) || is_c_beqz_insn (ival) || is_c_bnez_insn (ival))
>>> + return set_ordinary_record_type ();
>>> +
>>> + if (is_c_slli_insn (ival))
>>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>>> +
>>> + if (is_c_fldsp_insn (ival) || (m_xlen == 4 && is_c_flwsp_insn (ival)))
>>> + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival))
>>> + || set_ordinary_record_type ());
>>> +
>>> + if (is_c_lwsp_insn (ival) || (m_xlen == 8 && is_c_ldsp_insn (ival)))
>>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>>> +
>>> + if (is_c_jr_insn (ival))
>>> + return set_ordinary_record_type ();
>>> +
>>> + if (is_c_mv_insn (ival))
>>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>>> +
>>> + if (is_c_ebreak_insn (ival))
>>> + {
>>> + m_record_type = record_type::EBREAK;
>>> + return true;
>>> + }
>>> +
>>> + if (is_c_jalr_insn (ival))
>>> + return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
>>> +
>>> + if (is_c_add_insn (ival))
>>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>>> +
>>> + if (is_c_fsdsp_insn (ival) || (m_xlen == 8 && is_c_sdsp_insn (ival)))
>>> + {
>>> + ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SDSP_IMM (ival)};
>>> + return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
>>> + || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
>>> + }
>>> +
>>> + if (is_c_swsp_insn (ival) || (m_xlen == 4 && is_c_fswsp_insn (ival)))
>>> + {
>>> + ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SWSP_IMM (ival)};
>>> + return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
>>> + || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
>>> + }
>>> +
>>> + warning (_("Currently this instruction with len 2(%lx) is unsupported"),
>>> + ival);
>>> + return false;
>>> + }
>>> +
>>> +public:
>>> + /* Iterator for registers that need to be recorded. */
>>> + using regs_iter = recorded_regs::const_iterator;
>>> + /* Iterator for memory chunks that need to be recorded. */
>>> + using mems_iter = recorded_mems::const_iterator;
>>> +
>>> + /* Record instruction at address addr. Returns false if error happened. */
>>> + bool
>>> + record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
>>> + {
>>> + gdb_assert (gdbarch != nullptr);
>>> + gdb_assert (regcache != nullptr);
>>> +
>>> + int m_length = 0;
>>> + m_xlen = riscv_isa_xlen (gdbarch);
>>> + ULONGEST ival = riscv_insn::fetch_instruction (gdbarch, addr, &m_length);
>>> + if (!save_reg (RISCV_PC_REGNUM))
>>> + return false;
>>> +
>>> + if (m_length == 4)
>>> + return record_insn_len4 (ival, regcache);
>>> +
>>> + if (m_length == 2)
>>> + return record_insn_len2 (ival, regcache);
>>> +
>>> + /* 6 bytes or more. If the instruction is longer than 8 bytes, we don't
>>> + have full instruction bits in ival. At least, such long instructions
>>> + are not defined yet, so just ignore it. */
>>> + gdb_assert (m_length > 0 && m_length % 2 == 0);
>>> +
>>> + warning (_("Can not record unknown instruction (opcode = %lx)"), ival);
>>> + return false;
>>> + }
>>> +
>>> + /* Get record type of instruction. */
>>> + record_type
>>> + get_record_type () const noexcept
>>> + {
>>> + return m_record_type;
>>> + }
>>> +
>>> + /* Returns an iterator to the beginning of the registers that need
>>> + to be saved. */
>>> + regs_iter
>>> + regs_begin () const noexcept
>>> + {
>>> + return m_regs.begin ();
>>> + }
>>> +
>>> + /* Returns an iterator to the end of the registers that need
>>> + to be saved. */
>>> + regs_iter
>>> + regs_end () const noexcept
>>> + {
>>> + return m_regs.end ();
>>> + }
>>> +
>>> + /* Returns an iterator to the beginning of the memory chunks that need
>>> + to be saved. */
>>> + mems_iter
>>> + mems_begin () const noexcept
>>> + {
>>> + return m_mems.begin ();
>>> + }
>>> +
>>> + /* Returns an iterator to the end of the memory chunks that need
>>> + to be saved. */
>>> + mems_iter
>>> + mems_end () const noexcept
>>> + {
>>> + return m_mems.end ();
>>> + }
>>> +};
>>> +
>>> +/* A helper function to record instruction using record API. */
>>> +
>>> +static int
>>> +riscv_record_insn_details (struct gdbarch *gdbarch, struct regcache *regcache,
>>> + const riscv_recorded_insn &insn)
>>> +{
>>> + gdb_assert (gdbarch != nullptr);
>>> + gdb_assert (regcache != nullptr);
>>> +
>>> + riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
>>> + auto regs_begin = insn.regs_begin ();
>>> + auto regs_end = insn.regs_end ();
>>> + if (std::any_of (regs_begin,
>>> + regs_end,
>>> + [®cache] (auto &®_it)
>>> + {
>>> + return record_full_arch_list_add_reg (regcache, reg_it);
>>> + }))
>>> + return -1;
>>> +
>>> + auto mems_begin = insn.mems_begin ();
>>> + auto mems_end = insn.mems_end ();
>>> + if (std::any_of (mems_begin,
>>> + mems_end,
>>> + [] (auto &&mem_it)
>>> + {
>>> + return record_full_arch_list_add_mem (mem_it.first,
>>> + mem_it.second);
>>> + }))
>>> + return -1;
>>> +
>>> + switch (insn.get_record_type ())
>>> + {
>>> + case riscv_recorded_insn::record_type::ORDINARY:
>>> + break;
>>> +
>>> + case riscv_recorded_insn::record_type::ECALL:
>>> + {
>>> + if (!tdep->riscv_syscall_record)
>>> + {
>>> + warning (_("Syscall record is not supported"));
>>> + return -1;
>>> + }
>>> + ULONGEST reg_val = ULONGEST{};
>>> + if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
>>> + return -1;
>>> + return tdep->riscv_syscall_record (regcache, reg_val);
>>> + }
>>> +
>>> + case riscv_recorded_insn::record_type::EBREAK:
>>> + break;
>>> +
>>> + default:
>>> + return -1;
>>> + }
>>> + return 0;
>>> +}
>>> +
>>> +/* Parse the current instruction and record the values of the registers and
>>> + memory that will be changed in current instruction to record_arch_list.
>>> + Return -1 if something is wrong. */
>>> +
>>> +int
>>> +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
>>> + CORE_ADDR addr)
>>> +{
>>> + gdb_assert (gdbarch != nullptr);
>>> + gdb_assert (regcache != nullptr);
>>> +
>>> + riscv_recorded_insn insn;
>>> + if (!insn.record (gdbarch, regcache, addr))
>>> + {
>>> + record_full_arch_list_add_end ();
>>> + return -1;
>>> + }
>>> +
>>> + int ret_val = riscv_record_insn_details (gdbarch, regcache, insn);
>>> +
>>> + if (record_full_arch_list_add_end ())
>>> + return -1;
>>> +
>>> + return ret_val;
>>> +}
>>> diff --git a/gdb/riscv-tdep.h b/gdb/riscv-tdep.h
>>> index ad1e9596b83..2903aefd007 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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
>>> new file mode 100755
>>> index 00000000000..30e52b7e472
>>> --- /dev/null
>>> +++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
>>> @@ -0,0 +1,163 @@
>>> +#!/usr/bin/env python3
>>> +# pylint: disable=invalid-name
>>> +
>>> +# Copyright (C) 2024-2025 Free Software Foundation, Inc.
>>> +# Contributed by Timur Golubovich
>>> +
>>> +# This file is part of GDB.
>>> +
>>> +# This program is free software; you can redistribute it and/or modify
>>> +# it under the terms of the GNU General Public License as published by
>>> +# the Free Software Foundation; either version 3 of the License, or
>>> +# (at your option) any later version.
>>> +
>>> +# This program is distributed in the hope that it will be useful,
>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>> +# GNU General Public License for more details.
>>> +
>>> +# You should have received a copy of the GNU General Public License
>>> +# along with this program. If not, see <http://www.gnu.org/licenses/>.
>>> +
>>> +
>>> +# To get help message for this script, run:
>>> +# ./gdb/syscalls/riscv-canonicalize-syscall-gen.py --help
>>> +
>>> +# Execution result:
>>> +
>>> +# usage: riscv-canonicalize-syscall-gen.py [-h] -i INPUT
>>> +#
>>> +# Generate file gdb/riscv-canonicalize-syscall-gen.c from path to riscv linux syscalls.
>>> +#
>>> +# options:
>>> +# -h, --help show this help message and exit
>>> +# -i INPUT, --input INPUT
>>> +# path to riscv linux syscalls (glibc/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h)
>>> +
>>> +import argparse
>>> +import re
>>> +import sys
>>> +from pathlib import Path as _Path
>>> +
>>> +head = """\
>>> +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py
>>> +
>>> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
>>> +
>>> + This file is part of GDB.
>>> +
>>> + This program is free software; you can redistribute it and/or modify
>>> + it under the terms of the GNU General Public License as published by
>>> + the Free Software Foundation; either version 3 of the License, or
>>> + (at your option) any later version.
>>> +
>>> + This program is distributed in the hope that it will be useful,
>>> + but WITHOUT ANY WARRANTY; without even the implied warranty of
>>> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>> + GNU General Public License for more details.
>>> +
>>> + You should have received a copy of the GNU General Public License
>>> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
>>> +
>>> +#include "defs.h"
>>> +#include "riscv-linux-tdep.h"
>>> +
>>> +/* riscv64_canonicalize_syscall maps from the native riscv 64 Linux set
>>> + of syscall ids into a canonical set of syscall ids used by
>>> + process record. */
>>> +
>>> +enum gdb_syscall
>>> +riscv64_canonicalize_syscall (int syscall)
>>> +{
>>> + switch (syscall)
>>> + {
>>> +"""
>>> +
>>> +tail = """\
>>> + default:
>>> + return gdb_sys_no_syscall;
>>> + }
>>> +}
>>> +"""
>>> +
>>> +
>>> +class Generator:
>>> + def _get_gdb_syscalls(self, gdb_syscalls_path: _Path) -> list[str]:
>>> + gdb_syscalls: list[str] = []
>>> + with open(gdb_syscalls_path, "r", encoding="UTF-8") as file:
>>> + lines = file.readlines()
>>> + for line in lines:
>>> + match = re.search(r"\s*(?P<name>gdb_sys_[^S]+)\S*=", line)
>>> + if match:
>>> + gdb_syscalls.append(match.group("name").strip())
>>> + return gdb_syscalls
>>> +
>>> + def _get_canon_syscalls_lines(self, syscalls_path: _Path, gdb_syscalls: list[str]) -> list[str]:
>>> + canon_syscalls: dict[int, str] = {}
>>> + with open(syscalls_path, "r", encoding="UTF-8") as file:
>>> + lines = file.readlines()
>>> + for line in lines:
>>> + match = re.match(r"#define\s+__NR_(?P<name>[^\s]+)\s+(?P<number>\d+)", line)
>>> + if match:
>>> + syscall_name = match.group("name")
>>> + syscall_num = int(match.group("number"))
>>> + gdb_syscall_name = f"gdb_sys_{syscall_name}"
>>> + if gdb_syscall_name in gdb_syscalls:
>>> + value = f" case {syscall_num}: return {gdb_syscall_name};\n"
>>> + canon_syscalls[syscall_num] = value
>>> + # this is a place for corner cases
>>> + elif syscall_name == "mmap":
>>> + gdb_old_syscall_name = "gdb_old_mmap"
>>> + value = f" case {syscall_num}: return {gdb_old_syscall_name};\n"
>>> + canon_syscalls[syscall_num] = value
>>> + else:
>>> + value = f" /* case {syscall_num}: return {gdb_syscall_name}; */\n"
>>> + canon_syscalls[syscall_num] = value
>>> + return [canon_syscalls[syscall_num] for syscall_num in sorted(canon_syscalls)]
>>> +
>>> + def generate(self, syscalls_path: _Path) -> None:
>>> + repo_path = _Path(__file__).parent.parent.parent
>>> + gdb_syscalls_path = repo_path / "gdb" / "linux-record.h"
>>> + canon_syscalls_path = repo_path / "gdb" / "riscv-canonicalize-syscall-gen.c"
>>> +
>>> + gdb_syscalls = self._get_gdb_syscalls(gdb_syscalls_path)
>>> + canon_syscalls_lines = self._get_canon_syscalls_lines(syscalls_path, gdb_syscalls)
>>> +
>>> + with open(canon_syscalls_path, "w", encoding="UTF-8") as file:
>>> + file.writelines(head)
>>> + file.writelines(canon_syscalls_lines)
>>> + file.writelines(tail)
>>> +
>>> +
>>> +help_message = """\
>>> +Generate file gdb/riscv-canonicalize-syscall-gen.c
>>> +from path to riscv linux syscalls.
>>> +"""
>>> +
>>> +
>>> +def setup_parser() -> argparse.ArgumentParser:
>>> + parser = argparse.ArgumentParser(description=help_message)
>>> + parser.add_argument(
>>> + "-i",
>>> + "--input",
>>> + type=_Path,
>>> + required=True,
>>> + help="path to riscv linux syscalls (glibc/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h)",
>>> + )
>>> + return parser
>>> +
>>> +
>>> +def main(argv: list[str]) -> int:
>>> + try:
>>> + parser = setup_parser()
>>> + args = parser.parse_args(argv)
>>> + generator = Generator()
>>> + generator.generate(args.input)
>>> + return 0
>>> + except RuntimeError as e:
>>> + print(str(e))
>>> + return -1
>>> +
>>> +
>>> +if __name__ == "__main__":
>>> + sys.exit(main(sys.argv[1:]))
>>> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
>>> index c37cc8974e2..0149125e60a 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*-*-linux*"] } {
>>> return 1
>>> }
>>>
>>> --
>>> 2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* Re: [PATCH v8] This commit adds record full support for rv64gc instruction set
2025-04-14 8:44 ` Timur Golubovich
2025-04-14 12:03 ` Guinevere Larsen
@ 2025-04-22 17:36 ` Timur Golubovich
2025-04-23 12:13 ` Guinevere Larsen
1 sibling, 1 reply; 35+ messages in thread
From: Timur Golubovich @ 2025-04-22 17:36 UTC (permalink / raw)
To: Andrew Burgess; +Cc: gdb-patches, Guinevere Larsen
Hi!
Sorry to bother, but It seems like no one else has any questions about
this patch.
Maybe then it's possible to merge it?
>
> Hi!
> Thank you Andrew for watching this.
> I think that now we can merge this patch?
>
> пн, 14 апр. 2025 г. в 10:41, Andrew Burgess <aburgess@redhat.com>:
>
> >
> > Timur <timurgol007@gmail.com> writes:
> >
> > > It includes changes to the following files:
> > > - gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
> > > syscalls.
> > > - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
> > > rv64gc instructions.
> > > - gdb/configure.tgt: adds new files for compilation.
> > > - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
> > > targets.
> > > - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
> > > function that canonicalizes RISC-V syscall. This script can simplify support
> > > for syscalls on rv32 and rv64 system (currently support only for rv64). To
> > > use this script you need to pass a path to a file with syscalls description
> > > from riscv-glibc (example is in the help message). The script produces a
> > > mapping from syscall names to gdb_syscall enum.
> > > - gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
> > > - gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
> > > - gdb/NEWS: notification of new functionality.
> > >
> > > Approved-By: Guinevere Larsen <guinevere@redhat.com>
> > >
> > > Hi Andrew! Thank you for review.
> > >
> > >> I guess riscv-glibc is a fork of glibc that is running ahead of the
> > >> upstream glibc project?
> > >>
> > >> Is there any reason that this cannot work with the official glibc
> > >> project? That would be preferable I think unless there's a compelling
> > >> reason, which ideally we'd document.
> > >>
> > >> I tried running the script using current HEAD of glibc, the only
> > >> significant change is:
> > >>
> > >> diff --git i/gdb/riscv-canonicalize-syscall-gen.c w/gdb/riscv-canonicalize-syscall-gen.c
> > >> index 3749fc32f33..03579984f4a 100644
> > >> --- i/gdb/riscv-canonicalize-syscall-gen.c
> > >> +++ w/gdb/riscv-canonicalize-syscall-gen.c
> > >> @@ -270,7 +270,7 @@ riscv64_canonicalize_syscall (int syscall)
> > >> 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 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; */
> > >>
> > >> There was also a bunch of new syscalls that are added, but commented
> > >> out. I haven't tried to figure out if the change above is a bad thing
> > >> or not though, maybe you could advise.
> > >
> > > Yes, I agree with you, I just used riscv-glibc as I had aldready downloaded it
> > > and unfortunately the version was pretty old (I started working on this patch
> > > almost half a year ago).
> > >
> > >> This entry needs to move to the correct section of the NEWS file. If
> > >> you look for the lines starting '***' you'll see that the first header
> > >> is '*** Changes since GDB 16', this is the section where you should
> > >> place your entry, probably just before the '* New commands' line.
> > >> These include guards need to be renamed to GDB_RISCV_LINUX_TDEP_H. You
> > >> can do this automatically if you like with:
> > >>
> > >> ./gdb/check-include-guards.py --update gdb/*.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 */
> > >
> > > Addressed
> > >
> > >>@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
> > >>> || [istarget "i\[34567\]86-*-linux*"]
> > >>> || [istarget "aarch64*-*-linux*"]
> > >>> || [istarget "powerpc*-*-linux*"]
> > >>> - || [istarget "s390*-*-linux*"] } {
> > >>> + || [istarget "s390*-*-linux*"]
> > >>> + || [istarget "riscv*-*-*"] } {
> > >>
> > >> I notice that every other entry in this list restricts the match to
> > >> Linux based targets. Is RISC-V really different here? Or should RISC-V
> > >> be similarly restricted?
> > >
> > > Addressed, this was just my misprint.
> > >
> > > Hope that now everything is okay, wait for your feedback.
> >
> > I scanned the fixes, they look good. I haven't looked at the rest of
> > the patch in detail, assuming it hasn't changed from the previous
> > version:
> >
> > Approved-By: Andrew Burgess <aburgess@redhat.com>
> >
> > Thanks,
> > Andrew
> >
> >
> >
> > >
> > > ---
> > > gdb/NEWS | 2 +
> > > gdb/configure.tgt | 5 +-
> > > gdb/doc/gdb.texinfo | 2 +-
> > > gdb/riscv-canonicalize-syscall-gen.c | 358 +++++++++
> > > gdb/riscv-linux-tdep.c | 256 +++++++
> > > gdb/riscv-linux-tdep.h | 29 +
> > > gdb/riscv-tdep.c | 686 +++++++++++++++++-
> > > gdb/riscv-tdep.h | 16 +-
> > > .../riscv-canonicalize-syscall-gen.py | 163 +++++
> > > gdb/testsuite/lib/gdb.exp | 3 +-
> > > 10 files changed, 1510 insertions(+), 10 deletions(-)
> > > create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
> > > create mode 100644 gdb/riscv-linux-tdep.h
> > > create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
> > >
> > > diff --git a/gdb/NEWS b/gdb/NEWS
> > > index 99ec392d4c4..1a05a9f9251 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/configure.tgt b/gdb/configure.tgt
> > > index 18a15c032c3..44da2257be8 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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> > > index b251c8e1228..73f3b14cf74 100644
> > > --- a/gdb/doc/gdb.texinfo
> > > +++ b/gdb/doc/gdb.texinfo
> > > @@ -7807,7 +7807,7 @@ 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
> > > +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}.
> > >
> > > diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
> > > new file mode 100644
> > > index 00000000000..03579984f4a
> > > --- /dev/null
> > > +++ b/gdb/riscv-canonicalize-syscall-gen.c
> > > @@ -0,0 +1,358 @@
> > > +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py
> > > +
> > > + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> > > +
> > > + This file is part of GDB.
> > > +
> > > + This program is free software; you can redistribute it and/or modify
> > > + it under the terms of the GNU General Public License as published by
> > > + the Free Software Foundation; either version 3 of the License, or
> > > + (at your option) any later version.
> > > +
> > > + This program is distributed in the hope that it will be useful,
> > > + but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > > + GNU General Public License for more details.
> > > +
> > > + You should have received a copy of the GNU General Public License
> > > + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> > > +
> > > +#include "defs.h"
> > > +#include "riscv-linux-tdep.h"
> > > +
> > > +/* riscv64_canonicalize_syscall maps from the native riscv 64 Linux set
> > > + of syscall ids into a canonical set of syscall ids used by
> > > + process record. */
> > > +
> > > +enum gdb_syscall
> > > +riscv64_canonicalize_syscall (int syscall)
> > > +{
> > > + switch (syscall)
> > > + {
> > > + case 0: return gdb_sys_io_setup;
> > > + case 1: return gdb_sys_io_destroy;
> > > + case 2: return gdb_sys_io_submit;
> > > + case 3: return gdb_sys_io_cancel;
> > > + case 4: return gdb_sys_io_getevents;
> > > + case 5: return gdb_sys_setxattr;
> > > + case 6: return gdb_sys_lsetxattr;
> > > + case 7: return gdb_sys_fsetxattr;
> > > + case 8: return gdb_sys_getxattr;
> > > + case 9: return gdb_sys_lgetxattr;
> > > + case 10: return gdb_sys_fgetxattr;
> > > + case 11: return gdb_sys_listxattr;
> > > + case 12: return gdb_sys_llistxattr;
> > > + case 13: return gdb_sys_flistxattr;
> > > + case 14: return gdb_sys_removexattr;
> > > + case 15: return gdb_sys_lremovexattr;
> > > + case 16: return gdb_sys_fremovexattr;
> > > + case 17: return gdb_sys_getcwd;
> > > + case 18: return gdb_sys_lookup_dcookie;
> > > + case 19: return gdb_sys_eventfd2;
> > > + case 20: return gdb_sys_epoll_create1;
> > > + case 21: return gdb_sys_epoll_ctl;
> > > + case 22: return gdb_sys_epoll_pwait;
> > > + case 23: return gdb_sys_dup;
> > > + case 24: return gdb_sys_dup3;
> > > + case 25: return gdb_sys_fcntl;
> > > + case 26: return gdb_sys_inotify_init1;
> > > + case 27: return gdb_sys_inotify_add_watch;
> > > + case 28: return gdb_sys_inotify_rm_watch;
> > > + case 29: return gdb_sys_ioctl;
> > > + case 30: return gdb_sys_ioprio_set;
> > > + case 31: return gdb_sys_ioprio_get;
> > > + case 32: return gdb_sys_flock;
> > > + case 33: return gdb_sys_mknodat;
> > > + case 34: return gdb_sys_mkdirat;
> > > + case 35: return gdb_sys_unlinkat;
> > > + case 36: return gdb_sys_symlinkat;
> > > + case 37: return gdb_sys_linkat;
> > > + /* case 39: return gdb_sys_umount2; */
> > > + case 40: return gdb_sys_mount;
> > > + case 41: return gdb_sys_pivot_root;
> > > + case 42: return gdb_sys_nfsservctl;
> > > + case 43: return gdb_sys_statfs;
> > > + case 44: return gdb_sys_fstatfs;
> > > + case 45: return gdb_sys_truncate;
> > > + case 46: return gdb_sys_ftruncate;
> > > + case 47: return gdb_sys_fallocate;
> > > + case 48: return gdb_sys_faccessat;
> > > + case 49: return gdb_sys_chdir;
> > > + case 50: return gdb_sys_fchdir;
> > > + case 51: return gdb_sys_chroot;
> > > + case 52: return gdb_sys_fchmod;
> > > + case 53: return gdb_sys_fchmodat;
> > > + case 54: return gdb_sys_fchownat;
> > > + case 55: return gdb_sys_fchown;
> > > + case 56: return gdb_sys_openat;
> > > + case 57: return gdb_sys_close;
> > > + case 58: return gdb_sys_vhangup;
> > > + case 59: return gdb_sys_pipe2;
> > > + case 60: return gdb_sys_quotactl;
> > > + case 61: return gdb_sys_getdents64;
> > > + case 62: return gdb_sys_lseek;
> > > + case 63: return gdb_sys_read;
> > > + case 64: return gdb_sys_write;
> > > + case 65: return gdb_sys_readv;
> > > + case 66: return gdb_sys_writev;
> > > + case 67: return gdb_sys_pread64;
> > > + case 68: return gdb_sys_pwrite64;
> > > + /* case 69: return gdb_sys_preadv; */
> > > + /* case 70: return gdb_sys_pwritev; */
> > > + case 71: return gdb_sys_sendfile;
> > > + case 72: return gdb_sys_pselect6;
> > > + case 73: return gdb_sys_ppoll;
> > > + /* case 74: return gdb_sys_signalfd4; */
> > > + case 75: return gdb_sys_vmsplice;
> > > + case 76: return gdb_sys_splice;
> > > + case 77: return gdb_sys_tee;
> > > + case 78: return gdb_sys_readlinkat;
> > > + case 79: return gdb_sys_newfstatat;
> > > + case 80: return gdb_sys_fstat;
> > > + case 81: return gdb_sys_sync;
> > > + case 82: return gdb_sys_fsync;
> > > + case 83: return gdb_sys_fdatasync;
> > > + case 84: return gdb_sys_sync_file_range;
> > > + /* case 85: return gdb_sys_timerfd_create; */
> > > + /* case 86: return gdb_sys_timerfd_settime; */
> > > + /* case 87: return gdb_sys_timerfd_gettime; */
> > > + /* case 88: return gdb_sys_utimensat; */
> > > + case 89: return gdb_sys_acct;
> > > + case 90: return gdb_sys_capget;
> > > + case 91: return gdb_sys_capset;
> > > + case 92: return gdb_sys_personality;
> > > + case 93: return gdb_sys_exit;
> > > + case 94: return gdb_sys_exit_group;
> > > + case 95: return gdb_sys_waitid;
> > > + case 96: return gdb_sys_set_tid_address;
> > > + case 97: return gdb_sys_unshare;
> > > + case 98: return gdb_sys_futex;
> > > + case 99: return gdb_sys_set_robust_list;
> > > + case 100: return gdb_sys_get_robust_list;
> > > + case 101: return gdb_sys_nanosleep;
> > > + case 102: return gdb_sys_getitimer;
> > > + case 103: return gdb_sys_setitimer;
> > > + case 104: return gdb_sys_kexec_load;
> > > + case 105: return gdb_sys_init_module;
> > > + case 106: return gdb_sys_delete_module;
> > > + case 107: return gdb_sys_timer_create;
> > > + case 108: return gdb_sys_timer_gettime;
> > > + case 109: return gdb_sys_timer_getoverrun;
> > > + case 110: return gdb_sys_timer_settime;
> > > + case 111: return gdb_sys_timer_delete;
> > > + case 112: return gdb_sys_clock_settime;
> > > + case 113: return gdb_sys_clock_gettime;
> > > + case 114: return gdb_sys_clock_getres;
> > > + case 115: return gdb_sys_clock_nanosleep;
> > > + case 116: return gdb_sys_syslog;
> > > + case 117: return gdb_sys_ptrace;
> > > + case 118: return gdb_sys_sched_setparam;
> > > + case 119: return gdb_sys_sched_setscheduler;
> > > + case 120: return gdb_sys_sched_getscheduler;
> > > + case 121: return gdb_sys_sched_getparam;
> > > + case 122: return gdb_sys_sched_setaffinity;
> > > + case 123: return gdb_sys_sched_getaffinity;
> > > + case 124: return gdb_sys_sched_yield;
> > > + case 125: return gdb_sys_sched_get_priority_max;
> > > + case 126: return gdb_sys_sched_get_priority_min;
> > > + case 127: return gdb_sys_sched_rr_get_interval;
> > > + case 128: return gdb_sys_restart_syscall;
> > > + case 129: return gdb_sys_kill;
> > > + case 130: return gdb_sys_tkill;
> > > + case 131: return gdb_sys_tgkill;
> > > + case 132: return gdb_sys_sigaltstack;
> > > + case 133: return gdb_sys_rt_sigsuspend;
> > > + case 134: return gdb_sys_rt_sigaction;
> > > + case 135: return gdb_sys_rt_sigprocmask;
> > > + case 136: return gdb_sys_rt_sigpending;
> > > + case 137: return gdb_sys_rt_sigtimedwait;
> > > + case 138: return gdb_sys_rt_sigqueueinfo;
> > > + case 139: return gdb_sys_rt_sigreturn;
> > > + case 140: return gdb_sys_setpriority;
> > > + case 141: return gdb_sys_getpriority;
> > > + case 142: return gdb_sys_reboot;
> > > + case 143: return gdb_sys_setregid;
> > > + case 144: return gdb_sys_setgid;
> > > + case 145: return gdb_sys_setreuid;
> > > + case 146: return gdb_sys_setuid;
> > > + case 147: return gdb_sys_setresuid;
> > > + case 148: return gdb_sys_getresuid;
> > > + case 149: return gdb_sys_setresgid;
> > > + case 150: return gdb_sys_getresgid;
> > > + case 151: return gdb_sys_setfsuid;
> > > + case 152: return gdb_sys_setfsgid;
> > > + case 153: return gdb_sys_times;
> > > + case 154: return gdb_sys_setpgid;
> > > + case 155: return gdb_sys_getpgid;
> > > + case 156: return gdb_sys_getsid;
> > > + case 157: return gdb_sys_setsid;
> > > + case 158: return gdb_sys_getgroups;
> > > + case 159: return gdb_sys_setgroups;
> > > + case 160: return gdb_sys_uname;
> > > + case 161: return gdb_sys_sethostname;
> > > + case 162: return gdb_sys_setdomainname;
> > > + case 163: return gdb_sys_getrlimit;
> > > + case 164: return gdb_sys_setrlimit;
> > > + case 165: return gdb_sys_getrusage;
> > > + case 166: return gdb_sys_umask;
> > > + case 167: return gdb_sys_prctl;
> > > + case 168: return gdb_sys_getcpu;
> > > + case 169: return gdb_sys_gettimeofday;
> > > + case 170: return gdb_sys_settimeofday;
> > > + case 171: return gdb_sys_adjtimex;
> > > + case 172: return gdb_sys_getpid;
> > > + case 173: return gdb_sys_getppid;
> > > + case 174: return gdb_sys_getuid;
> > > + case 175: return gdb_sys_geteuid;
> > > + case 176: return gdb_sys_getgid;
> > > + case 177: return gdb_sys_getegid;
> > > + case 178: return gdb_sys_gettid;
> > > + case 179: return gdb_sys_sysinfo;
> > > + case 180: return gdb_sys_mq_open;
> > > + case 181: return gdb_sys_mq_unlink;
> > > + case 182: return gdb_sys_mq_timedsend;
> > > + case 183: return gdb_sys_mq_timedreceive;
> > > + case 184: return gdb_sys_mq_notify;
> > > + case 185: return gdb_sys_mq_getsetattr;
> > > + case 186: return gdb_sys_msgget;
> > > + case 187: return gdb_sys_msgctl;
> > > + case 188: return gdb_sys_msgrcv;
> > > + case 189: return gdb_sys_msgsnd;
> > > + case 190: return gdb_sys_semget;
> > > + case 191: return gdb_sys_semctl;
> > > + case 192: return gdb_sys_semtimedop;
> > > + case 193: return gdb_sys_semop;
> > > + case 194: return gdb_sys_shmget;
> > > + case 195: return gdb_sys_shmctl;
> > > + case 196: return gdb_sys_shmat;
> > > + case 197: return gdb_sys_shmdt;
> > > + case 198: return gdb_sys_socket;
> > > + case 199: return gdb_sys_socketpair;
> > > + case 200: return gdb_sys_bind;
> > > + case 201: return gdb_sys_listen;
> > > + case 202: return gdb_sys_accept;
> > > + case 203: return gdb_sys_connect;
> > > + case 204: return gdb_sys_getsockname;
> > > + case 205: return gdb_sys_getpeername;
> > > + case 206: return gdb_sys_sendto;
> > > + case 207: return gdb_sys_recvfrom;
> > > + case 208: return gdb_sys_setsockopt;
> > > + case 209: return gdb_sys_getsockopt;
> > > + case 210: return gdb_sys_shutdown;
> > > + case 211: return gdb_sys_sendmsg;
> > > + case 212: return gdb_sys_recvmsg;
> > > + case 213: return gdb_sys_readahead;
> > > + case 214: return gdb_sys_brk;
> > > + case 215: return gdb_sys_munmap;
> > > + case 216: return gdb_sys_mremap;
> > > + case 217: return gdb_sys_add_key;
> > > + case 218: return gdb_sys_request_key;
> > > + case 219: return gdb_sys_keyctl;
> > > + case 220: return gdb_sys_clone;
> > > + case 221: return gdb_sys_execve;
> > > + case 222: return gdb_old_mmap;
> > > + case 223: return gdb_sys_fadvise64;
> > > + case 224: return gdb_sys_swapon;
> > > + case 225: return gdb_sys_swapoff;
> > > + case 226: return gdb_sys_mprotect;
> > > + case 227: return gdb_sys_msync;
> > > + case 228: return gdb_sys_mlock;
> > > + case 229: return gdb_sys_munlock;
> > > + case 230: return gdb_sys_mlockall;
> > > + case 231: return gdb_sys_munlockall;
> > > + case 232: return gdb_sys_mincore;
> > > + case 233: return gdb_sys_madvise;
> > > + case 234: return gdb_sys_remap_file_pages;
> > > + case 235: return gdb_sys_mbind;
> > > + case 236: return gdb_sys_get_mempolicy;
> > > + case 237: return gdb_sys_set_mempolicy;
> > > + case 238: return gdb_sys_migrate_pages;
> > > + case 239: return gdb_sys_move_pages;
> > > + /* case 240: return gdb_sys_rt_tgsigqueueinfo; */
> > > + /* case 241: return gdb_sys_perf_event_open; */
> > > + case 242: return gdb_sys_accept4;
> > > + /* case 243: return gdb_sys_recvmmsg; */
> > > + /* case 258: return gdb_sys_riscv_hwprobe; */
> > > + /* case 259: return gdb_sys_riscv_flush_icache; */
> > > + case 260: return gdb_sys_wait4;
> > > + /* case 261: return gdb_sys_prlimit64; */
> > > + /* case 262: return gdb_sys_fanotify_init; */
> > > + /* case 263: return gdb_sys_fanotify_mark; */
> > > + /* case 264: return gdb_sys_name_to_handle_at; */
> > > + /* case 265: return gdb_sys_open_by_handle_at; */
> > > + /* case 266: return gdb_sys_clock_adjtime; */
> > > + /* case 267: return gdb_sys_syncfs; */
> > > + /* case 268: return gdb_sys_setns; */
> > > + /* case 269: return gdb_sys_sendmmsg; */
> > > + /* case 270: return gdb_sys_process_vm_readv; */
> > > + /* case 271: return gdb_sys_process_vm_writev; */
> > > + /* case 272: return gdb_sys_kcmp; */
> > > + /* case 273: return gdb_sys_finit_module; */
> > > + /* case 274: return gdb_sys_sched_setattr; */
> > > + /* case 275: return gdb_sys_sched_getattr; */
> > > + /* case 276: return gdb_sys_renameat2; */
> > > + /* case 277: return gdb_sys_seccomp; */
> > > + case 278: return gdb_sys_getrandom;
> > > + /* case 279: return gdb_sys_memfd_create; */
> > > + /* case 280: return gdb_sys_bpf; */
> > > + /* case 281: return gdb_sys_execveat; */
> > > + /* case 282: return gdb_sys_userfaultfd; */
> > > + /* case 283: return gdb_sys_membarrier; */
> > > + /* case 284: return gdb_sys_mlock2; */
> > > + /* case 285: return gdb_sys_copy_file_range; */
> > > + /* case 286: return gdb_sys_preadv2; */
> > > + /* case 287: return gdb_sys_pwritev2; */
> > > + /* case 288: return gdb_sys_pkey_mprotect; */
> > > + /* case 289: return gdb_sys_pkey_alloc; */
> > > + /* case 290: return gdb_sys_pkey_free; */
> > > + case 291: return gdb_sys_statx;
> > > + /* case 292: return gdb_sys_io_pgetevents; */
> > > + /* case 293: return gdb_sys_rseq; */
> > > + /* case 294: return gdb_sys_kexec_file_load; */
> > > + /* case 424: return gdb_sys_pidfd_send_signal; */
> > > + /* case 425: return gdb_sys_io_uring_setup; */
> > > + /* case 426: return gdb_sys_io_uring_enter; */
> > > + /* case 427: return gdb_sys_io_uring_register; */
> > > + /* case 428: return gdb_sys_open_tree; */
> > > + /* case 429: return gdb_sys_move_mount; */
> > > + /* case 430: return gdb_sys_fsopen; */
> > > + /* case 431: return gdb_sys_fsconfig; */
> > > + /* case 432: return gdb_sys_fsmount; */
> > > + /* case 433: return gdb_sys_fspick; */
> > > + /* case 434: return gdb_sys_pidfd_open; */
> > > + /* case 435: return gdb_sys_clone3; */
> > > + /* case 436: return gdb_sys_close_range; */
> > > + /* case 437: return gdb_sys_openat2; */
> > > + /* case 438: return gdb_sys_pidfd_getfd; */
> > > + /* case 439: return gdb_sys_faccessat2; */
> > > + /* case 440: return gdb_sys_process_madvise; */
> > > + /* case 441: return gdb_sys_epoll_pwait2; */
> > > + /* case 442: return gdb_sys_mount_setattr; */
> > > + /* case 443: return gdb_sys_quotactl_fd; */
> > > + /* case 444: return gdb_sys_landlock_create_ruleset; */
> > > + /* case 445: return gdb_sys_landlock_add_rule; */
> > > + /* case 446: return gdb_sys_landlock_restrict_self; */
> > > + /* case 447: return gdb_sys_memfd_secret; */
> > > + /* case 448: return gdb_sys_process_mrelease; */
> > > + /* case 449: return gdb_sys_futex_waitv; */
> > > + /* case 450: return gdb_sys_set_mempolicy_home_node; */
> > > + /* case 451: return gdb_sys_cachestat; */
> > > + /* case 452: return gdb_sys_fchmodat2; */
> > > + /* case 453: return gdb_sys_map_shadow_stack; */
> > > + /* case 454: return gdb_sys_futex_wake; */
> > > + /* case 455: return gdb_sys_futex_wait; */
> > > + /* case 456: return gdb_sys_futex_requeue; */
> > > + /* case 457: return gdb_sys_statmount; */
> > > + /* case 458: return gdb_sys_listmount; */
> > > + /* case 459: return gdb_sys_lsm_get_self_attr; */
> > > + /* case 460: return gdb_sys_lsm_set_self_attr; */
> > > + /* case 461: return gdb_sys_lsm_list_modules; */
> > > + /* case 462: return gdb_sys_mseal; */
> > > + /* case 463: return gdb_sys_setxattrat; */
> > > + /* case 464: return gdb_sys_getxattrat; */
> > > + /* case 465: return gdb_sys_listxattrat; */
> > > + /* case 466: return gdb_sys_removexattrat; */
> > > + default:
> > > + return gdb_sys_no_syscall;
> > > + }
> > > +}
> > > diff --git a/gdb/riscv-linux-tdep.c b/gdb/riscv-linux-tdep.c
> > > index 4c0c65c1457..f21039a613c 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 00000000000..9dd9e373caa
> > > --- /dev/null
> > > +++ b/gdb/riscv-linux-tdep.h
> > > @@ -0,0 +1,29 @@
> > > +/* Copyright (C) 2024-2025 Free Software Foundation, Inc.
> > > +
> > > + This file is part of GDB.
> > > +
> > > + This program is free software; you can redistribute it and/or modify
> > > + it under the terms of the GNU General Public License as published by
> > > + the Free Software Foundation; either version 3 of the License, or
> > > + (at your option) any later version.
> > > +
> > > + This program is distributed in the hope that it will be useful,
> > > + but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > > + GNU General Public License for more details.
> > > +
> > > + You should have received a copy of the GNU General Public License
> > > + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> > > +
> > > +#ifndef GDB_RISCV_LINUX_TDEP_H
> > > +#define GDB_RISCV_LINUX_TDEP_H
> > > +
> > > +#include "linux-record.h"
> > > +
> > > +/* riscv64_canonicalize_syscall maps from the native riscv Linux set
> > > + of syscall ids into a canonical set of syscall ids used by
> > > + process record. */
> > > +
> > > +extern enum gdb_syscall riscv64_canonicalize_syscall (int syscall);
> > > +
> > > +#endif /* GDB_RISCV_LINUX_TDEP_H */
> > > diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
> > > index 91f6dffebe1..19367ff55ba 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 @@ class riscv_insn
> > > 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 @@ class riscv_insn
> > > m_rs2 = decode_register_index_short (ival, OP_SH_CRS2S);
> > > }
> > >
> > > - /* Fetch instruction from target memory at ADDR, return the content of
> > > - the instruction, and update LEN with the instruction length. */
> > > - static ULONGEST fetch_instruction (struct gdbarch *gdbarch,
> > > - CORE_ADDR addr, int *len);
> > > -
> > > /* The length of the instruction in bytes. Should be 2 or 4. */
> > > int m_length;
> > >
> > > @@ -4433,6 +4436,9 @@ riscv_gdbarch_init (struct gdbarch_info info,
> > > set_gdbarch_stap_register_indirection_suffixes
> > > (gdbarch, stap_register_indirection_suffixes);
> > >
> > > + /* Process record-replay */
> > > + set_gdbarch_process_record (gdbarch, riscv_process_record);
> > > +
> > > /* Hook in OS ABI-specific overrides, if they have been registered. */
> > > gdbarch_init_osabi (info, gdbarch);
> > >
> > > @@ -4866,3 +4872,673 @@ equivalent change in the disassembler output."),
> > > &setriscvcmdlist,
> > > &showriscvcmdlist);
> > > }
> > > +
> > > +/* A wrapper to read register under number regnum to address addr.
> > > + Returns false if error happened and makes warning. */
> > > +
> > > +static bool
> > > +try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
> > > +{
> > > + gdb_assert (regcache != nullptr);
> > > +
> > > + if (regcache->raw_read (regnum, &addr)
> > > + != register_status::REG_VALID)
> > > + {
> > > + warning (_("Can not read at address %lx"), addr);
> > > + return false;
> > > + }
> > > + return true;
> > > +}
> > > +
> > > +/* Helper class to record instruction. */
> > > +
> > > +class riscv_recorded_insn final
> > > +{
> > > +public:
> > > + /* Type for saved register. */
> > > + using regnum_type = int;
> > > + /* Type for saved memory. First is address, second is length. */
> > > + using memory_type = std::pair<CORE_ADDR, int>;
> > > +
> > > + /* Enum class that represents which type does recording belong to. */
> > > + enum class record_type
> > > + {
> > > + UNKNOWN,
> > > + ORDINARY,
> > > +
> > > + /* Corner cases. */
> > > + ECALL,
> > > + EBREAK,
> > > + };
> > > +
> > > +private:
> > > + /* Type for set of registers that need to be saved. */
> > > + using recorded_regs = std::vector<regnum_type>;
> > > + /* Type for set of memory records that need to be saved. */
> > > + using recorded_mems = std::vector<memory_type>;
> > > +
> > > + /* Type for memory address, extracted from memory_type. */
> > > + using mem_addr = decltype (std::declval<memory_type> ().first);
> > > + /* Type for memory length, extracted from memory_type. */
> > > + using mem_len = decltype (std::declval<memory_type> ().second);
> > > +
> > > + /* Record type of current instruction. */
> > > + record_type m_record_type = record_type::UNKNOWN;
> > > +
> > > + /* Flag that represents was there an error in current recording. */
> > > + bool m_error_occured = false;
> > > +
> > > + /* Set of registers that need to be recorded. */
> > > + recorded_regs m_regs;
> > > + /* Set of memory chunks that need to be recorded. */
> > > + recorded_mems m_mems;
> > > +
> > > + /* Width in bytes of the general purpose registers for GDBARCH,
> > > + where recording is happening. */
> > > + int m_xlen = 0;
> > > +
> > > + /* Helper for decode 16-bit instruction RS1. */
> > > + static regnum_type
> > > + decode_crs1_short (ULONGEST opcode) noexcept
> > > + {
> > > + return ((opcode >> OP_SH_CRS1S) & OP_MASK_CRS1S) + 8;
> > > + }
> > > +
> > > + /* Helper for decode 16-bit instruction RS2. */
> > > + static regnum_type
> > > + decode_crs2_short (ULONGEST opcode) noexcept
> > > + {
> > > + return ((opcode >> OP_SH_CRS2S) & OP_MASK_CRS2S) + 8;
> > > + }
> > > +
> > > + /* Helper for decode 16-bit instruction CRS1. */
> > > + static regnum_type
> > > + decode_crs1 (ULONGEST opcode) noexcept
> > > + {
> > > + return ((opcode >> OP_SH_RD) & OP_MASK_RD);
> > > + }
> > > +
> > > + /* Helper for decode 16-bit instruction CRS2. */
> > > + static regnum_type
> > > + decode_crs2 (ULONGEST opcode) noexcept
> > > + {
> > > + return ((opcode >> OP_SH_CRS2) & OP_MASK_CRS2);
> > > + }
> > > +
> > > + /* Helper for decode 32-bit instruction RD. */
> > > + static regnum_type
> > > + decode_rd (ULONGEST ival) noexcept
> > > + {
> > > + return (ival >> OP_SH_RD) & OP_MASK_RD;
> > > + }
> > > +
> > > + /* Helper for decode 32-bit instruction RS1. */
> > > + static regnum_type
> > > + decode_rs1 (ULONGEST ival) noexcept
> > > + {
> > > + return (ival >> OP_SH_RS1) & OP_MASK_RS1;
> > > + }
> > > +
> > > + /* Helper for decode 32-bit instruction RS2. */
> > > + static regnum_type
> > > + decode_rs2 (ULONGEST ival) noexcept
> > > + {
> > > + return (ival >> OP_SH_RS2) & OP_MASK_RS2;
> > > + }
> > > +
> > > + /* Helper for decode 32-bit instruction CSR. */
> > > + static regnum_type
> > > + decode_csr (ULONGEST ival) noexcept
> > > + {
> > > + return (ival >> OP_SH_CSR) & OP_MASK_CSR;
> > > + }
> > > +
> > > + /* Set ordinary record type. Always returns true. */
> > > + bool
> > > + set_ordinary_record_type () noexcept
> > > + {
> > > + m_record_type = record_type::ORDINARY;
> > > + return true;
> > > + }
> > > +
> > > + /* Set error happened. Always returns false. */
> > > + bool
> > > + set_error () noexcept
> > > + {
> > > + m_error_occured = true;
> > > + return false;
> > > + }
> > > +
> > > + /* Check if current recording has an error. */
> > > + bool
> > > + has_error () const noexcept
> > > + {
> > > + return m_error_occured;
> > > + }
> > > +
> > > + /* Reads register. Sets error and returns false if error happened. */
> > > + bool
> > > + read_reg (struct regcache *regcache, regnum_type reg,
> > > + ULONGEST &addr) noexcept
> > > + {
> > > + gdb_assert (regcache != nullptr);
> > > +
> > > + if (!try_read (regcache, reg, addr))
> > > + return set_error ();
> > > + return true;
> > > + }
> > > +
> > > + /* Save register. Returns true or aborts if exception happened. */
> > > + bool
> > > + save_reg (regnum_type regnum) noexcept
> > > + {
> > > + m_regs.emplace_back (regnum);
> > > + return true;
> > > + }
> > > +
> > > + /* Save memory chunk. Returns true or aborts if exception happened. */
> > > + bool
> > > + save_mem (mem_addr addr, mem_len len) noexcept
> > > + {
> > > + m_mems.emplace_back (addr, len);
> > > + return true;
> > > + }
> > > +
> > > + /* Returns true if instruction needs only saving pc. */
> > > + static bool
> > > + need_save_pc (ULONGEST ival) noexcept
> > > + {
> > > + return (is_beq_insn (ival) || is_bne_insn (ival) || is_blt_insn (ival)
> > > + || is_bge_insn (ival) || is_bltu_insn (ival) || is_bgeu_insn (ival)
> > > + || is_fence_insn (ival) || is_pause_insn (ival)
> > > + || is_fence_i_insn (ival));
> > > + }
> > > +
> > > + /* Returns true if instruction is classified. */
> > > + bool
> > > + try_save_pc (ULONGEST ival) noexcept
> > > + {
> > > + if (!need_save_pc (ival))
> > > + return false;
> > > +
> > > + return set_ordinary_record_type ();
> > > + }
> > > +
> > > + /* Returns true if instruction needs only saving pc and rd. */
> > > + static bool
> > > + need_save_pc_rd (ULONGEST ival) noexcept
> > > + {
> > > + return (is_lui_insn (ival) || is_auipc_insn (ival) || is_jal_insn (ival)
> > > + || is_jalr_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
> > > + || is_lw_insn (ival) || is_lbu_insn (ival) || is_lhu_insn (ival)
> > > + || is_addi_insn (ival) || is_slti_insn (ival)
> > > + || is_sltiu_insn (ival) || is_xori_insn (ival) || is_ori_insn (ival)
> > > + || is_andi_insn (ival) || is_slli_rv32_insn (ival)
> > > + || is_srli_rv32_insn (ival) || is_srai_rv32_insn (ival)
> > > + || is_add_insn (ival) || is_sub_insn (ival) || is_sll_insn (ival)
> > > + || is_slt_insn (ival) || is_sltu_insn (ival) || is_xor_insn (ival)
> > > + || is_srl_insn (ival) || is_sra_insn (ival) || is_or_insn (ival)
> > > + || is_and_insn (ival) || is_lwu_insn (ival) || is_ld_insn (ival)
> > > + || is_slli_insn (ival) || is_srli_insn (ival) || is_srai_insn (ival)
> > > + || is_addiw_insn (ival) || is_slliw_insn (ival)
> > > + || is_srliw_insn (ival) || is_sraiw_insn (ival)
> > > + || is_addw_insn (ival) || is_subw_insn (ival) || is_sllw_insn (ival)
> > > + || is_srlw_insn (ival) || is_sraw_insn (ival) || is_mul_insn (ival)
> > > + || is_mulh_insn (ival) || is_mulhsu_insn (ival)
> > > + || is_mulhu_insn (ival) || is_div_insn (ival) || is_divu_insn (ival)
> > > + || is_rem_insn (ival) || is_remu_insn (ival) || is_mulw_insn (ival)
> > > + || is_divw_insn (ival) || is_divuw_insn (ival)
> > > + || is_remw_insn (ival) || is_remuw_insn (ival)
> > > + || is_lr_w_insn (ival) || is_lr_d_insn (ival)
> > > + || is_fcvt_w_s_insn (ival) || is_fcvt_wu_s_insn (ival)
> > > + || is_fmv_x_s_insn (ival) || is_feq_s_insn (ival)
> > > + || is_flt_s_insn (ival) || is_fle_s_insn (ival)
> > > + || is_fclass_s_insn (ival) || is_fcvt_l_s_insn (ival)
> > > + || is_fcvt_lu_s_insn (ival) || is_feq_d_insn (ival)
> > > + || is_flt_d_insn (ival) || is_fle_d_insn (ival)
> > > + || is_fclass_d_insn (ival) || is_fcvt_w_d_insn (ival)
> > > + || is_fcvt_wu_d_insn (ival) || is_fcvt_l_d_insn (ival)
> > > + || is_fcvt_lu_d_insn (ival) || is_fmv_x_d_insn (ival));
> > > + }
> > > +
> > > + /* Returns true if instruction is classified. This function can set
> > > + m_error_occured. */
> > > + bool
> > > + try_save_pc_rd (ULONGEST ival) noexcept
> > > + {
> > > + if (!need_save_pc_rd (ival))
> > > + return false;
> > > +
> > > + return (!save_reg (decode_rd (ival)) || set_ordinary_record_type ());
> > > + }
> > > +
> > > + /* Returns true if instruction needs only saving pc and
> > > + floating point rd. */
> > > + static bool
> > > + need_save_pc_fprd (ULONGEST ival) noexcept
> > > + {
> > > + return (is_flw_insn (ival) || is_fmadd_s_insn (ival)
> > > + || is_fmsub_s_insn (ival) || is_fnmsub_s_insn (ival)
> > > + || is_fnmadd_s_insn (ival) || is_fadd_s_insn (ival)
> > > + || is_fsub_s_insn (ival) || is_fmul_s_insn (ival)
> > > + || is_fdiv_s_insn (ival) || is_fsqrt_s_insn (ival)
> > > + || is_fsgnj_s_insn (ival) || is_fsgnjn_s_insn (ival)
> > > + || is_fsgnjx_s_insn (ival) || is_fmin_s_insn (ival)
> > > + || is_fmax_s_insn (ival) || is_fcvt_s_w_insn (ival)
> > > + || is_fcvt_s_wu_insn (ival) || is_fmv_s_x_insn (ival)
> > > + || is_fcvt_s_l_insn (ival) || is_fcvt_s_lu_insn (ival)
> > > + || is_fld_insn (ival) || is_fmadd_d_insn (ival)
> > > + || is_fmsub_d_insn (ival) || is_fnmsub_d_insn (ival)
> > > + || is_fnmadd_d_insn (ival) || is_fadd_d_insn (ival)
> > > + || is_fsub_d_insn (ival) || is_fmul_d_insn (ival)
> > > + || is_fdiv_d_insn (ival) || is_fsqrt_d_insn (ival)
> > > + || is_fsgnj_d_insn (ival) || is_fsgnjn_d_insn (ival)
> > > + || is_fsgnjx_d_insn (ival) || is_fmin_d_insn (ival)
> > > + || is_fmax_d_insn (ival) || is_fcvt_s_d_insn (ival)
> > > + || is_fcvt_d_s_insn (ival) || is_fcvt_d_w_insn (ival)
> > > + || is_fcvt_d_wu_insn (ival) || is_fcvt_d_l_insn (ival)
> > > + || is_fcvt_d_lu_insn (ival) || is_fmv_d_x_insn (ival));
> > > + }
> > > +
> > > + /* Returns true if instruction is classified. This function can set
> > > + m_error_occured. */
> > > + bool
> > > + try_save_pc_fprd (ULONGEST ival) noexcept
> > > + {
> > > + if (!need_save_pc_fprd (ival))
> > > + return false;
> > > +
> > > + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_rd (ival))
> > > + || set_ordinary_record_type ());
> > > + }
> > > +
> > > + /* Returns true if instruction needs only saving pc, rd and csr. */
> > > + static bool
> > > + need_save_pc_rd_csr (ULONGEST ival) noexcept
> > > + {
> > > + return (is_csrrw_insn (ival) || is_csrrs_insn (ival) || is_csrrc_insn (ival)
> > > + || is_csrrwi_insn (ival) || is_csrrsi_insn (ival)
> > > + || is_csrrc_insn (ival));
> > > + }
> > > +
> > > + /* Returns true if instruction is classified. This function can set
> > > + m_error_occured. */
> > > + bool
> > > + try_save_pc_rd_csr (ULONGEST ival) noexcept
> > > + {
> > > + if (!need_save_pc_rd_csr (ival))
> > > + return false;
> > > +
> > > + return (!save_reg (decode_rd (ival))
> > > + || !save_reg (RISCV_FIRST_CSR_REGNUM + decode_csr (ival))
> > > + || set_ordinary_record_type ());
> > > + }
> > > +
> > > + /* Returns the size of the memory chunk that needs to be saved if the
> > > + instruction belongs to the group that needs only saving pc and memory.
> > > + Otherwise returns 0. */
> > > + static mem_len
> > > + need_save_pc_mem (ULONGEST ival) noexcept
> > > + {
> > > + if (is_sb_insn (ival))
> > > + return 1;
> > > + if (is_sh_insn (ival))
> > > + return 2;
> > > + if (is_sw_insn (ival) || is_fsw_insn (ival))
> > > + return 4;
> > > + if (is_sd_insn (ival) || is_fsd_insn (ival))
> > > + return 8;
> > > + return 0;
> > > + }
> > > +
> > > + /* Returns true if instruction is classified. This function can set
> > > + m_error_occured. */
> > > + bool
> > > + try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
> > > + {
> > > + gdb_assert (regcache != nullptr);
> > > +
> > > + mem_addr addr = mem_addr{};
> > > + mem_len len = need_save_pc_mem (ival);
> > > + if (len <= 0)
> > > + return false;
> > > +
> > > + mem_len offset = EXTRACT_STYPE_IMM (ival);
> > > + return (!read_reg (regcache, decode_rs1 (ival), addr)
> > > + || !save_mem (addr + offset, len) || set_ordinary_record_type ());
> > > + }
> > > +
> > > + /* Returns the size of the memory chunk that needs to be saved if the
> > > + instruction belongs to the group that needs only saving pc, rd and memory.
> > > + Otherwise returns 0. */
> > > + static mem_len
> > > + need_save_pc_rd_mem (ULONGEST ival) noexcept
> > > + {
> > > + if (is_sc_w_insn (ival) || is_amoswap_w_insn (ival)
> > > + || is_amoadd_w_insn (ival) || is_amoxor_w_insn (ival)
> > > + || is_amoand_w_insn (ival) || is_amoor_w_insn (ival)
> > > + || is_amomin_w_insn (ival) || is_amomax_w_insn (ival)
> > > + || is_amominu_w_insn (ival) || is_amomaxu_w_insn (ival))
> > > + return 4;
> > > + if (is_sc_d_insn (ival) || is_amoswap_d_insn (ival)
> > > + || is_amoadd_d_insn (ival) || is_amoxor_d_insn (ival)
> > > + || is_amoand_d_insn (ival) || is_amoor_d_insn (ival)
> > > + || is_amomin_d_insn (ival) || is_amomax_d_insn (ival)
> > > + || is_amominu_d_insn (ival) || is_amomaxu_d_insn (ival))
> > > + return 8;
> > > + return 0;
> > > + }
> > > +
> > > + /* Returns true if instruction is classified. This function can set
> > > + m_error_occured. */
> > > + bool
> > > + try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
> > > + {
> > > + gdb_assert (regcache != nullptr);
> > > +
> > > + mem_len len = need_save_pc_rd_mem (ival);
> > > + mem_addr addr = 0;
> > > + if (len <= 0)
> > > + return false;
> > > +
> > > + return (!read_reg (regcache, decode_rs1 (ival), addr)
> > > + || !save_mem (addr, len) || !save_reg (decode_rd (ival))
> > > + || set_ordinary_record_type ());
> > > + }
> > > +
> > > + /* Returns true if instruction is successfully recordered. The length of
> > > + the instruction must be equal 4 bytes. */
> > > + bool
> > > + record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept
> > > + {
> > > + gdb_assert (regcache != nullptr);
> > > +
> > > + if (is_ecall_insn (ival))
> > > + {
> > > + m_record_type = record_type::ECALL;
> > > + return true;
> > > + }
> > > +
> > > + if (is_ebreak_insn (ival))
> > > + {
> > > + m_record_type = record_type::EBREAK;
> > > + return true;
> > > + }
> > > +
> > > + if (try_save_pc (ival) || try_save_pc_rd (ival) || try_save_pc_fprd (ival)
> > > + || try_save_pc_rd_csr (ival) || try_save_pc_mem (ival, regcache)
> > > + || try_save_pc_rd_mem (ival, regcache))
> > > + return !has_error ();
> > > +
> > > + warning (_("Currently this instruction with len 4(%lx) is unsupported"),
> > > + ival);
> > > + return false;
> > > + }
> > > +
> > > + /* Returns true if instruction is successfully recordered. The length of
> > > + the instruction must be equal 2 bytes. */
> > > + bool
> > > + record_insn_len2 (ULONGEST ival, struct regcache *regcache) noexcept
> > > + {
> > > + gdb_assert (regcache != nullptr);
> > > +
> > > + mem_addr addr = mem_addr{};
> > > +
> > > + /* The order here is very important, because
> > > + opcodes of some instructions may be the same. */
> > > +
> > > + if (is_c_addi4spn_insn (ival) || is_c_lw_insn (ival)
> > > + || (m_xlen == 8 && is_c_ld_insn (ival)))
> > > + return (!save_reg (decode_crs2_short (ival))
> > > + || set_ordinary_record_type ());
> > > +
> > > + if (is_c_fld_insn (ival) || (m_xlen == 4 && is_c_flw_insn (ival)))
> > > + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival))
> > > + || set_ordinary_record_type ());
> > > +
> > > + if (is_c_fsd_insn (ival) || (m_xlen == 8 && is_c_sd_insn (ival)))
> > > + {
> > > + ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LD_IMM (ival)};
> > > + return (!read_reg (regcache, decode_crs1_short (ival), addr)
> > > + || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
> > > + }
> > > +
> > > + if ((m_xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
> > > + {
> > > + ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LW_IMM (ival)};
> > > + return (!read_reg (regcache, decode_crs1_short (ival), addr)
> > > + || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
> > > + }
> > > +
> > > + if (is_c_nop_insn (ival))
> > > + return set_ordinary_record_type ();
> > > +
> > > + if (is_c_addi_insn (ival))
> > > + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> > > +
> > > + if (m_xlen == 4 && is_c_jal_insn (ival))
> > > + return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
> > > +
> > > + if ((m_xlen == 8 && is_c_addiw_insn (ival)) || is_c_li_insn (ival))
> > > + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> > > +
> > > + if (is_c_addi16sp_insn (ival))
> > > + return (!save_reg (RISCV_SP_REGNUM) || set_ordinary_record_type ());
> > > +
> > > + if (is_c_lui_insn (ival))
> > > + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> > > +
> > > + if (is_c_srli_insn (ival) || is_c_srai_insn (ival) || is_c_andi_insn (ival)
> > > + || is_c_sub_insn (ival) || is_c_xor_insn (ival) || is_c_or_insn (ival)
> > > + || is_c_and_insn (ival) || (m_xlen == 8 && is_c_subw_insn (ival))
> > > + || (m_xlen == 8 && is_c_addw_insn (ival)))
> > > + return (!save_reg (decode_crs1_short (ival))
> > > + || set_ordinary_record_type ());
> > > +
> > > + if (is_c_j_insn (ival) || is_c_beqz_insn (ival) || is_c_bnez_insn (ival))
> > > + return set_ordinary_record_type ();
> > > +
> > > + if (is_c_slli_insn (ival))
> > > + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> > > +
> > > + if (is_c_fldsp_insn (ival) || (m_xlen == 4 && is_c_flwsp_insn (ival)))
> > > + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival))
> > > + || set_ordinary_record_type ());
> > > +
> > > + if (is_c_lwsp_insn (ival) || (m_xlen == 8 && is_c_ldsp_insn (ival)))
> > > + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> > > +
> > > + if (is_c_jr_insn (ival))
> > > + return set_ordinary_record_type ();
> > > +
> > > + if (is_c_mv_insn (ival))
> > > + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> > > +
> > > + if (is_c_ebreak_insn (ival))
> > > + {
> > > + m_record_type = record_type::EBREAK;
> > > + return true;
> > > + }
> > > +
> > > + if (is_c_jalr_insn (ival))
> > > + return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
> > > +
> > > + if (is_c_add_insn (ival))
> > > + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
> > > +
> > > + if (is_c_fsdsp_insn (ival) || (m_xlen == 8 && is_c_sdsp_insn (ival)))
> > > + {
> > > + ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SDSP_IMM (ival)};
> > > + return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
> > > + || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
> > > + }
> > > +
> > > + if (is_c_swsp_insn (ival) || (m_xlen == 4 && is_c_fswsp_insn (ival)))
> > > + {
> > > + ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SWSP_IMM (ival)};
> > > + return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
> > > + || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
> > > + }
> > > +
> > > + warning (_("Currently this instruction with len 2(%lx) is unsupported"),
> > > + ival);
> > > + return false;
> > > + }
> > > +
> > > +public:
> > > + /* Iterator for registers that need to be recorded. */
> > > + using regs_iter = recorded_regs::const_iterator;
> > > + /* Iterator for memory chunks that need to be recorded. */
> > > + using mems_iter = recorded_mems::const_iterator;
> > > +
> > > + /* Record instruction at address addr. Returns false if error happened. */
> > > + bool
> > > + record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
> > > + {
> > > + gdb_assert (gdbarch != nullptr);
> > > + gdb_assert (regcache != nullptr);
> > > +
> > > + int m_length = 0;
> > > + m_xlen = riscv_isa_xlen (gdbarch);
> > > + ULONGEST ival = riscv_insn::fetch_instruction (gdbarch, addr, &m_length);
> > > + if (!save_reg (RISCV_PC_REGNUM))
> > > + return false;
> > > +
> > > + if (m_length == 4)
> > > + return record_insn_len4 (ival, regcache);
> > > +
> > > + if (m_length == 2)
> > > + return record_insn_len2 (ival, regcache);
> > > +
> > > + /* 6 bytes or more. If the instruction is longer than 8 bytes, we don't
> > > + have full instruction bits in ival. At least, such long instructions
> > > + are not defined yet, so just ignore it. */
> > > + gdb_assert (m_length > 0 && m_length % 2 == 0);
> > > +
> > > + warning (_("Can not record unknown instruction (opcode = %lx)"), ival);
> > > + return false;
> > > + }
> > > +
> > > + /* Get record type of instruction. */
> > > + record_type
> > > + get_record_type () const noexcept
> > > + {
> > > + return m_record_type;
> > > + }
> > > +
> > > + /* Returns an iterator to the beginning of the registers that need
> > > + to be saved. */
> > > + regs_iter
> > > + regs_begin () const noexcept
> > > + {
> > > + return m_regs.begin ();
> > > + }
> > > +
> > > + /* Returns an iterator to the end of the registers that need
> > > + to be saved. */
> > > + regs_iter
> > > + regs_end () const noexcept
> > > + {
> > > + return m_regs.end ();
> > > + }
> > > +
> > > + /* Returns an iterator to the beginning of the memory chunks that need
> > > + to be saved. */
> > > + mems_iter
> > > + mems_begin () const noexcept
> > > + {
> > > + return m_mems.begin ();
> > > + }
> > > +
> > > + /* Returns an iterator to the end of the memory chunks that need
> > > + to be saved. */
> > > + mems_iter
> > > + mems_end () const noexcept
> > > + {
> > > + return m_mems.end ();
> > > + }
> > > +};
> > > +
> > > +/* A helper function to record instruction using record API. */
> > > +
> > > +static int
> > > +riscv_record_insn_details (struct gdbarch *gdbarch, struct regcache *regcache,
> > > + const riscv_recorded_insn &insn)
> > > +{
> > > + gdb_assert (gdbarch != nullptr);
> > > + gdb_assert (regcache != nullptr);
> > > +
> > > + riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
> > > + auto regs_begin = insn.regs_begin ();
> > > + auto regs_end = insn.regs_end ();
> > > + if (std::any_of (regs_begin,
> > > + regs_end,
> > > + [®cache] (auto &®_it)
> > > + {
> > > + return record_full_arch_list_add_reg (regcache, reg_it);
> > > + }))
> > > + return -1;
> > > +
> > > + auto mems_begin = insn.mems_begin ();
> > > + auto mems_end = insn.mems_end ();
> > > + if (std::any_of (mems_begin,
> > > + mems_end,
> > > + [] (auto &&mem_it)
> > > + {
> > > + return record_full_arch_list_add_mem (mem_it.first,
> > > + mem_it.second);
> > > + }))
> > > + return -1;
> > > +
> > > + switch (insn.get_record_type ())
> > > + {
> > > + case riscv_recorded_insn::record_type::ORDINARY:
> > > + break;
> > > +
> > > + case riscv_recorded_insn::record_type::ECALL:
> > > + {
> > > + if (!tdep->riscv_syscall_record)
> > > + {
> > > + warning (_("Syscall record is not supported"));
> > > + return -1;
> > > + }
> > > + ULONGEST reg_val = ULONGEST{};
> > > + if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
> > > + return -1;
> > > + return tdep->riscv_syscall_record (regcache, reg_val);
> > > + }
> > > +
> > > + case riscv_recorded_insn::record_type::EBREAK:
> > > + break;
> > > +
> > > + default:
> > > + return -1;
> > > + }
> > > + return 0;
> > > +}
> > > +
> > > +/* Parse the current instruction and record the values of the registers and
> > > + memory that will be changed in current instruction to record_arch_list.
> > > + Return -1 if something is wrong. */
> > > +
> > > +int
> > > +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
> > > + CORE_ADDR addr)
> > > +{
> > > + gdb_assert (gdbarch != nullptr);
> > > + gdb_assert (regcache != nullptr);
> > > +
> > > + riscv_recorded_insn insn;
> > > + if (!insn.record (gdbarch, regcache, addr))
> > > + {
> > > + record_full_arch_list_add_end ();
> > > + return -1;
> > > + }
> > > +
> > > + int ret_val = riscv_record_insn_details (gdbarch, regcache, insn);
> > > +
> > > + if (record_full_arch_list_add_end ())
> > > + return -1;
> > > +
> > > + return ret_val;
> > > +}
> > > diff --git a/gdb/riscv-tdep.h b/gdb/riscv-tdep.h
> > > index ad1e9596b83..2903aefd007 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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> > > new file mode 100755
> > > index 00000000000..30e52b7e472
> > > --- /dev/null
> > > +++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
> > > @@ -0,0 +1,163 @@
> > > +#!/usr/bin/env python3
> > > +# pylint: disable=invalid-name
> > > +
> > > +# Copyright (C) 2024-2025 Free Software Foundation, Inc.
> > > +# Contributed by Timur Golubovich
> > > +
> > > +# This file is part of GDB.
> > > +
> > > +# This program is free software; you can redistribute it and/or modify
> > > +# it under the terms of the GNU General Public License as published by
> > > +# the Free Software Foundation; either version 3 of the License, or
> > > +# (at your option) any later version.
> > > +
> > > +# This program is distributed in the hope that it will be useful,
> > > +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > > +# GNU General Public License for more details.
> > > +
> > > +# You should have received a copy of the GNU General Public License
> > > +# along with this program. If not, see <http://www.gnu.org/licenses/>.
> > > +
> > > +
> > > +# To get help message for this script, run:
> > > +# ./gdb/syscalls/riscv-canonicalize-syscall-gen.py --help
> > > +
> > > +# Execution result:
> > > +
> > > +# usage: riscv-canonicalize-syscall-gen.py [-h] -i INPUT
> > > +#
> > > +# Generate file gdb/riscv-canonicalize-syscall-gen.c from path to riscv linux syscalls.
> > > +#
> > > +# options:
> > > +# -h, --help show this help message and exit
> > > +# -i INPUT, --input INPUT
> > > +# path to riscv linux syscalls (glibc/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h)
> > > +
> > > +import argparse
> > > +import re
> > > +import sys
> > > +from pathlib import Path as _Path
> > > +
> > > +head = """\
> > > +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py
> > > +
> > > + Copyright (C) 2024-2025 Free Software Foundation, Inc.
> > > +
> > > + This file is part of GDB.
> > > +
> > > + This program is free software; you can redistribute it and/or modify
> > > + it under the terms of the GNU General Public License as published by
> > > + the Free Software Foundation; either version 3 of the License, or
> > > + (at your option) any later version.
> > > +
> > > + This program is distributed in the hope that it will be useful,
> > > + but WITHOUT ANY WARRANTY; without even the implied warranty of
> > > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > > + GNU General Public License for more details.
> > > +
> > > + You should have received a copy of the GNU General Public License
> > > + along with this program. If not, see <http://www.gnu.org/licenses/>. */
> > > +
> > > +#include "defs.h"
> > > +#include "riscv-linux-tdep.h"
> > > +
> > > +/* riscv64_canonicalize_syscall maps from the native riscv 64 Linux set
> > > + of syscall ids into a canonical set of syscall ids used by
> > > + process record. */
> > > +
> > > +enum gdb_syscall
> > > +riscv64_canonicalize_syscall (int syscall)
> > > +{
> > > + switch (syscall)
> > > + {
> > > +"""
> > > +
> > > +tail = """\
> > > + default:
> > > + return gdb_sys_no_syscall;
> > > + }
> > > +}
> > > +"""
> > > +
> > > +
> > > +class Generator:
> > > + def _get_gdb_syscalls(self, gdb_syscalls_path: _Path) -> list[str]:
> > > + gdb_syscalls: list[str] = []
> > > + with open(gdb_syscalls_path, "r", encoding="UTF-8") as file:
> > > + lines = file.readlines()
> > > + for line in lines:
> > > + match = re.search(r"\s*(?P<name>gdb_sys_[^S]+)\S*=", line)
> > > + if match:
> > > + gdb_syscalls.append(match.group("name").strip())
> > > + return gdb_syscalls
> > > +
> > > + def _get_canon_syscalls_lines(self, syscalls_path: _Path, gdb_syscalls: list[str]) -> list[str]:
> > > + canon_syscalls: dict[int, str] = {}
> > > + with open(syscalls_path, "r", encoding="UTF-8") as file:
> > > + lines = file.readlines()
> > > + for line in lines:
> > > + match = re.match(r"#define\s+__NR_(?P<name>[^\s]+)\s+(?P<number>\d+)", line)
> > > + if match:
> > > + syscall_name = match.group("name")
> > > + syscall_num = int(match.group("number"))
> > > + gdb_syscall_name = f"gdb_sys_{syscall_name}"
> > > + if gdb_syscall_name in gdb_syscalls:
> > > + value = f" case {syscall_num}: return {gdb_syscall_name};\n"
> > > + canon_syscalls[syscall_num] = value
> > > + # this is a place for corner cases
> > > + elif syscall_name == "mmap":
> > > + gdb_old_syscall_name = "gdb_old_mmap"
> > > + value = f" case {syscall_num}: return {gdb_old_syscall_name};\n"
> > > + canon_syscalls[syscall_num] = value
> > > + else:
> > > + value = f" /* case {syscall_num}: return {gdb_syscall_name}; */\n"
> > > + canon_syscalls[syscall_num] = value
> > > + return [canon_syscalls[syscall_num] for syscall_num in sorted(canon_syscalls)]
> > > +
> > > + def generate(self, syscalls_path: _Path) -> None:
> > > + repo_path = _Path(__file__).parent.parent.parent
> > > + gdb_syscalls_path = repo_path / "gdb" / "linux-record.h"
> > > + canon_syscalls_path = repo_path / "gdb" / "riscv-canonicalize-syscall-gen.c"
> > > +
> > > + gdb_syscalls = self._get_gdb_syscalls(gdb_syscalls_path)
> > > + canon_syscalls_lines = self._get_canon_syscalls_lines(syscalls_path, gdb_syscalls)
> > > +
> > > + with open(canon_syscalls_path, "w", encoding="UTF-8") as file:
> > > + file.writelines(head)
> > > + file.writelines(canon_syscalls_lines)
> > > + file.writelines(tail)
> > > +
> > > +
> > > +help_message = """\
> > > +Generate file gdb/riscv-canonicalize-syscall-gen.c
> > > +from path to riscv linux syscalls.
> > > +"""
> > > +
> > > +
> > > +def setup_parser() -> argparse.ArgumentParser:
> > > + parser = argparse.ArgumentParser(description=help_message)
> > > + parser.add_argument(
> > > + "-i",
> > > + "--input",
> > > + type=_Path,
> > > + required=True,
> > > + help="path to riscv linux syscalls (glibc/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h)",
> > > + )
> > > + return parser
> > > +
> > > +
> > > +def main(argv: list[str]) -> int:
> > > + try:
> > > + parser = setup_parser()
> > > + args = parser.parse_args(argv)
> > > + generator = Generator()
> > > + generator.generate(args.input)
> > > + return 0
> > > + except RuntimeError as e:
> > > + print(str(e))
> > > + return -1
> > > +
> > > +
> > > +if __name__ == "__main__":
> > > + sys.exit(main(sys.argv[1:]))
> > > diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> > > index c37cc8974e2..0149125e60a 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*-*-linux*"] } {
> > > return 1
> > > }
> > >
> > > --
> > > 2.34.1
> >
^ permalink raw reply [flat|nested] 35+ messages in thread* Re: [PATCH v8] This commit adds record full support for rv64gc instruction set
2025-04-22 17:36 ` Timur Golubovich
@ 2025-04-23 12:13 ` Guinevere Larsen
2025-04-23 14:01 ` Aktemur, Tankut Baris
0 siblings, 1 reply; 35+ messages in thread
From: Guinevere Larsen @ 2025-04-23 12:13 UTC (permalink / raw)
To: Timur Golubovich, Andrew Burgess; +Cc: gdb-patches
Hi! Sorry for the delay, I'm applying and fixing merge conflicts, and
will be pushing this patch soon!
If you plan on continue to develop GDB, you may want to get in contact
with Tom Tromey to setup write-after-approval access, so you are able to
push your own patches :)
--
Cheers,
Guinevere Larsen
She/Her/Hers
On 4/22/25 2:36 PM, Timur Golubovich wrote:
> Hi!
> Sorry to bother, but It seems like no one else has any questions about
> this patch.
> Maybe then it's possible to merge it?
>
>> Hi!
>> Thank you Andrew for watching this.
>> I think that now we can merge this patch?
>>
>> пн, 14 апр. 2025 г. в 10:41, Andrew Burgess <aburgess@redhat.com>:
>>
>>> Timur <timurgol007@gmail.com> writes:
>>>
>>>> It includes changes to the following files:
>>>> - gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
>>>> syscalls.
>>>> - gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
>>>> rv64gc instructions.
>>>> - gdb/configure.tgt: adds new files for compilation.
>>>> - gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
>>>> targets.
>>>> - gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
>>>> function that canonicalizes RISC-V syscall. This script can simplify support
>>>> for syscalls on rv32 and rv64 system (currently support only for rv64). To
>>>> use this script you need to pass a path to a file with syscalls description
>>>> from riscv-glibc (example is in the help message). The script produces a
>>>> mapping from syscall names to gdb_syscall enum.
>>>> - gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
>>>> - gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
>>>> - gdb/NEWS: notification of new functionality.
>>>>
>>>> Approved-By: Guinevere Larsen <guinevere@redhat.com>
>>>>
>>>> Hi Andrew! Thank you for review.
>>>>
>>>>> I guess riscv-glibc is a fork of glibc that is running ahead of the
>>>>> upstream glibc project?
>>>>>
>>>>> Is there any reason that this cannot work with the official glibc
>>>>> project? That would be preferable I think unless there's a compelling
>>>>> reason, which ideally we'd document.
>>>>>
>>>>> I tried running the script using current HEAD of glibc, the only
>>>>> significant change is:
>>>>>
>>>>> diff --git i/gdb/riscv-canonicalize-syscall-gen.c w/gdb/riscv-canonicalize-syscall-gen.c
>>>>> index 3749fc32f33..03579984f4a 100644
>>>>> --- i/gdb/riscv-canonicalize-syscall-gen.c
>>>>> +++ w/gdb/riscv-canonicalize-syscall-gen.c
>>>>> @@ -270,7 +270,7 @@ riscv64_canonicalize_syscall (int syscall)
>>>>> 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 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; */
>>>>>
>>>>> There was also a bunch of new syscalls that are added, but commented
>>>>> out. I haven't tried to figure out if the change above is a bad thing
>>>>> or not though, maybe you could advise.
>>>> Yes, I agree with you, I just used riscv-glibc as I had aldready downloaded it
>>>> and unfortunately the version was pretty old (I started working on this patch
>>>> almost half a year ago).
>>>>
>>>>> This entry needs to move to the correct section of the NEWS file. If
>>>>> you look for the lines starting '***' you'll see that the first header
>>>>> is '*** Changes since GDB 16', this is the section where you should
>>>>> place your entry, probably just before the '* New commands' line.
>>>>> These include guards need to be renamed to GDB_RISCV_LINUX_TDEP_H. You
>>>>> can do this automatically if you like with:
>>>>>
>>>>> ./gdb/check-include-guards.py --update gdb/*.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 */
>>>> Addressed
>>>>
>>>>> @@ -3718,7 +3718,8 @@ proc supports_reverse {} {
>>>>>> || [istarget "i\[34567\]86-*-linux*"]
>>>>>> || [istarget "aarch64*-*-linux*"]
>>>>>> || [istarget "powerpc*-*-linux*"]
>>>>>> - || [istarget "s390*-*-linux*"] } {
>>>>>> + || [istarget "s390*-*-linux*"]
>>>>>> + || [istarget "riscv*-*-*"] } {
>>>>> I notice that every other entry in this list restricts the match to
>>>>> Linux based targets. Is RISC-V really different here? Or should RISC-V
>>>>> be similarly restricted?
>>>> Addressed, this was just my misprint.
>>>>
>>>> Hope that now everything is okay, wait for your feedback.
>>> I scanned the fixes, they look good. I haven't looked at the rest of
>>> the patch in detail, assuming it hasn't changed from the previous
>>> version:
>>>
>>> Approved-By: Andrew Burgess <aburgess@redhat.com>
>>>
>>> Thanks,
>>> Andrew
>>>
>>>
>>>
>>>> ---
>>>> gdb/NEWS | 2 +
>>>> gdb/configure.tgt | 5 +-
>>>> gdb/doc/gdb.texinfo | 2 +-
>>>> gdb/riscv-canonicalize-syscall-gen.c | 358 +++++++++
>>>> gdb/riscv-linux-tdep.c | 256 +++++++
>>>> gdb/riscv-linux-tdep.h | 29 +
>>>> gdb/riscv-tdep.c | 686 +++++++++++++++++-
>>>> gdb/riscv-tdep.h | 16 +-
>>>> .../riscv-canonicalize-syscall-gen.py | 163 +++++
>>>> gdb/testsuite/lib/gdb.exp | 3 +-
>>>> 10 files changed, 1510 insertions(+), 10 deletions(-)
>>>> create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
>>>> create mode 100644 gdb/riscv-linux-tdep.h
>>>> create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
>>>>
>>>> diff --git a/gdb/NEWS b/gdb/NEWS
>>>> index 99ec392d4c4..1a05a9f9251 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/configure.tgt b/gdb/configure.tgt
>>>> index 18a15c032c3..44da2257be8 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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
>>>> index b251c8e1228..73f3b14cf74 100644
>>>> --- a/gdb/doc/gdb.texinfo
>>>> +++ b/gdb/doc/gdb.texinfo
>>>> @@ -7807,7 +7807,7 @@ 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
>>>> +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}.
>>>>
>>>> diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
>>>> new file mode 100644
>>>> index 00000000000..03579984f4a
>>>> --- /dev/null
>>>> +++ b/gdb/riscv-canonicalize-syscall-gen.c
>>>> @@ -0,0 +1,358 @@
>>>> +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py
>>>> +
>>>> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
>>>> +
>>>> + This file is part of GDB.
>>>> +
>>>> + This program is free software; you can redistribute it and/or modify
>>>> + it under the terms of the GNU General Public License as published by
>>>> + the Free Software Foundation; either version 3 of the License, or
>>>> + (at your option) any later version.
>>>> +
>>>> + This program is distributed in the hope that it will be useful,
>>>> + but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>>> + GNU General Public License for more details.
>>>> +
>>>> + You should have received a copy of the GNU General Public License
>>>> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
>>>> +
>>>> +#include "defs.h"
>>>> +#include "riscv-linux-tdep.h"
>>>> +
>>>> +/* riscv64_canonicalize_syscall maps from the native riscv 64 Linux set
>>>> + of syscall ids into a canonical set of syscall ids used by
>>>> + process record. */
>>>> +
>>>> +enum gdb_syscall
>>>> +riscv64_canonicalize_syscall (int syscall)
>>>> +{
>>>> + switch (syscall)
>>>> + {
>>>> + case 0: return gdb_sys_io_setup;
>>>> + case 1: return gdb_sys_io_destroy;
>>>> + case 2: return gdb_sys_io_submit;
>>>> + case 3: return gdb_sys_io_cancel;
>>>> + case 4: return gdb_sys_io_getevents;
>>>> + case 5: return gdb_sys_setxattr;
>>>> + case 6: return gdb_sys_lsetxattr;
>>>> + case 7: return gdb_sys_fsetxattr;
>>>> + case 8: return gdb_sys_getxattr;
>>>> + case 9: return gdb_sys_lgetxattr;
>>>> + case 10: return gdb_sys_fgetxattr;
>>>> + case 11: return gdb_sys_listxattr;
>>>> + case 12: return gdb_sys_llistxattr;
>>>> + case 13: return gdb_sys_flistxattr;
>>>> + case 14: return gdb_sys_removexattr;
>>>> + case 15: return gdb_sys_lremovexattr;
>>>> + case 16: return gdb_sys_fremovexattr;
>>>> + case 17: return gdb_sys_getcwd;
>>>> + case 18: return gdb_sys_lookup_dcookie;
>>>> + case 19: return gdb_sys_eventfd2;
>>>> + case 20: return gdb_sys_epoll_create1;
>>>> + case 21: return gdb_sys_epoll_ctl;
>>>> + case 22: return gdb_sys_epoll_pwait;
>>>> + case 23: return gdb_sys_dup;
>>>> + case 24: return gdb_sys_dup3;
>>>> + case 25: return gdb_sys_fcntl;
>>>> + case 26: return gdb_sys_inotify_init1;
>>>> + case 27: return gdb_sys_inotify_add_watch;
>>>> + case 28: return gdb_sys_inotify_rm_watch;
>>>> + case 29: return gdb_sys_ioctl;
>>>> + case 30: return gdb_sys_ioprio_set;
>>>> + case 31: return gdb_sys_ioprio_get;
>>>> + case 32: return gdb_sys_flock;
>>>> + case 33: return gdb_sys_mknodat;
>>>> + case 34: return gdb_sys_mkdirat;
>>>> + case 35: return gdb_sys_unlinkat;
>>>> + case 36: return gdb_sys_symlinkat;
>>>> + case 37: return gdb_sys_linkat;
>>>> + /* case 39: return gdb_sys_umount2; */
>>>> + case 40: return gdb_sys_mount;
>>>> + case 41: return gdb_sys_pivot_root;
>>>> + case 42: return gdb_sys_nfsservctl;
>>>> + case 43: return gdb_sys_statfs;
>>>> + case 44: return gdb_sys_fstatfs;
>>>> + case 45: return gdb_sys_truncate;
>>>> + case 46: return gdb_sys_ftruncate;
>>>> + case 47: return gdb_sys_fallocate;
>>>> + case 48: return gdb_sys_faccessat;
>>>> + case 49: return gdb_sys_chdir;
>>>> + case 50: return gdb_sys_fchdir;
>>>> + case 51: return gdb_sys_chroot;
>>>> + case 52: return gdb_sys_fchmod;
>>>> + case 53: return gdb_sys_fchmodat;
>>>> + case 54: return gdb_sys_fchownat;
>>>> + case 55: return gdb_sys_fchown;
>>>> + case 56: return gdb_sys_openat;
>>>> + case 57: return gdb_sys_close;
>>>> + case 58: return gdb_sys_vhangup;
>>>> + case 59: return gdb_sys_pipe2;
>>>> + case 60: return gdb_sys_quotactl;
>>>> + case 61: return gdb_sys_getdents64;
>>>> + case 62: return gdb_sys_lseek;
>>>> + case 63: return gdb_sys_read;
>>>> + case 64: return gdb_sys_write;
>>>> + case 65: return gdb_sys_readv;
>>>> + case 66: return gdb_sys_writev;
>>>> + case 67: return gdb_sys_pread64;
>>>> + case 68: return gdb_sys_pwrite64;
>>>> + /* case 69: return gdb_sys_preadv; */
>>>> + /* case 70: return gdb_sys_pwritev; */
>>>> + case 71: return gdb_sys_sendfile;
>>>> + case 72: return gdb_sys_pselect6;
>>>> + case 73: return gdb_sys_ppoll;
>>>> + /* case 74: return gdb_sys_signalfd4; */
>>>> + case 75: return gdb_sys_vmsplice;
>>>> + case 76: return gdb_sys_splice;
>>>> + case 77: return gdb_sys_tee;
>>>> + case 78: return gdb_sys_readlinkat;
>>>> + case 79: return gdb_sys_newfstatat;
>>>> + case 80: return gdb_sys_fstat;
>>>> + case 81: return gdb_sys_sync;
>>>> + case 82: return gdb_sys_fsync;
>>>> + case 83: return gdb_sys_fdatasync;
>>>> + case 84: return gdb_sys_sync_file_range;
>>>> + /* case 85: return gdb_sys_timerfd_create; */
>>>> + /* case 86: return gdb_sys_timerfd_settime; */
>>>> + /* case 87: return gdb_sys_timerfd_gettime; */
>>>> + /* case 88: return gdb_sys_utimensat; */
>>>> + case 89: return gdb_sys_acct;
>>>> + case 90: return gdb_sys_capget;
>>>> + case 91: return gdb_sys_capset;
>>>> + case 92: return gdb_sys_personality;
>>>> + case 93: return gdb_sys_exit;
>>>> + case 94: return gdb_sys_exit_group;
>>>> + case 95: return gdb_sys_waitid;
>>>> + case 96: return gdb_sys_set_tid_address;
>>>> + case 97: return gdb_sys_unshare;
>>>> + case 98: return gdb_sys_futex;
>>>> + case 99: return gdb_sys_set_robust_list;
>>>> + case 100: return gdb_sys_get_robust_list;
>>>> + case 101: return gdb_sys_nanosleep;
>>>> + case 102: return gdb_sys_getitimer;
>>>> + case 103: return gdb_sys_setitimer;
>>>> + case 104: return gdb_sys_kexec_load;
>>>> + case 105: return gdb_sys_init_module;
>>>> + case 106: return gdb_sys_delete_module;
>>>> + case 107: return gdb_sys_timer_create;
>>>> + case 108: return gdb_sys_timer_gettime;
>>>> + case 109: return gdb_sys_timer_getoverrun;
>>>> + case 110: return gdb_sys_timer_settime;
>>>> + case 111: return gdb_sys_timer_delete;
>>>> + case 112: return gdb_sys_clock_settime;
>>>> + case 113: return gdb_sys_clock_gettime;
>>>> + case 114: return gdb_sys_clock_getres;
>>>> + case 115: return gdb_sys_clock_nanosleep;
>>>> + case 116: return gdb_sys_syslog;
>>>> + case 117: return gdb_sys_ptrace;
>>>> + case 118: return gdb_sys_sched_setparam;
>>>> + case 119: return gdb_sys_sched_setscheduler;
>>>> + case 120: return gdb_sys_sched_getscheduler;
>>>> + case 121: return gdb_sys_sched_getparam;
>>>> + case 122: return gdb_sys_sched_setaffinity;
>>>> + case 123: return gdb_sys_sched_getaffinity;
>>>> + case 124: return gdb_sys_sched_yield;
>>>> + case 125: return gdb_sys_sched_get_priority_max;
>>>> + case 126: return gdb_sys_sched_get_priority_min;
>>>> + case 127: return gdb_sys_sched_rr_get_interval;
>>>> + case 128: return gdb_sys_restart_syscall;
>>>> + case 129: return gdb_sys_kill;
>>>> + case 130: return gdb_sys_tkill;
>>>> + case 131: return gdb_sys_tgkill;
>>>> + case 132: return gdb_sys_sigaltstack;
>>>> + case 133: return gdb_sys_rt_sigsuspend;
>>>> + case 134: return gdb_sys_rt_sigaction;
>>>> + case 135: return gdb_sys_rt_sigprocmask;
>>>> + case 136: return gdb_sys_rt_sigpending;
>>>> + case 137: return gdb_sys_rt_sigtimedwait;
>>>> + case 138: return gdb_sys_rt_sigqueueinfo;
>>>> + case 139: return gdb_sys_rt_sigreturn;
>>>> + case 140: return gdb_sys_setpriority;
>>>> + case 141: return gdb_sys_getpriority;
>>>> + case 142: return gdb_sys_reboot;
>>>> + case 143: return gdb_sys_setregid;
>>>> + case 144: return gdb_sys_setgid;
>>>> + case 145: return gdb_sys_setreuid;
>>>> + case 146: return gdb_sys_setuid;
>>>> + case 147: return gdb_sys_setresuid;
>>>> + case 148: return gdb_sys_getresuid;
>>>> + case 149: return gdb_sys_setresgid;
>>>> + case 150: return gdb_sys_getresgid;
>>>> + case 151: return gdb_sys_setfsuid;
>>>> + case 152: return gdb_sys_setfsgid;
>>>> + case 153: return gdb_sys_times;
>>>> + case 154: return gdb_sys_setpgid;
>>>> + case 155: return gdb_sys_getpgid;
>>>> + case 156: return gdb_sys_getsid;
>>>> + case 157: return gdb_sys_setsid;
>>>> + case 158: return gdb_sys_getgroups;
>>>> + case 159: return gdb_sys_setgroups;
>>>> + case 160: return gdb_sys_uname;
>>>> + case 161: return gdb_sys_sethostname;
>>>> + case 162: return gdb_sys_setdomainname;
>>>> + case 163: return gdb_sys_getrlimit;
>>>> + case 164: return gdb_sys_setrlimit;
>>>> + case 165: return gdb_sys_getrusage;
>>>> + case 166: return gdb_sys_umask;
>>>> + case 167: return gdb_sys_prctl;
>>>> + case 168: return gdb_sys_getcpu;
>>>> + case 169: return gdb_sys_gettimeofday;
>>>> + case 170: return gdb_sys_settimeofday;
>>>> + case 171: return gdb_sys_adjtimex;
>>>> + case 172: return gdb_sys_getpid;
>>>> + case 173: return gdb_sys_getppid;
>>>> + case 174: return gdb_sys_getuid;
>>>> + case 175: return gdb_sys_geteuid;
>>>> + case 176: return gdb_sys_getgid;
>>>> + case 177: return gdb_sys_getegid;
>>>> + case 178: return gdb_sys_gettid;
>>>> + case 179: return gdb_sys_sysinfo;
>>>> + case 180: return gdb_sys_mq_open;
>>>> + case 181: return gdb_sys_mq_unlink;
>>>> + case 182: return gdb_sys_mq_timedsend;
>>>> + case 183: return gdb_sys_mq_timedreceive;
>>>> + case 184: return gdb_sys_mq_notify;
>>>> + case 185: return gdb_sys_mq_getsetattr;
>>>> + case 186: return gdb_sys_msgget;
>>>> + case 187: return gdb_sys_msgctl;
>>>> + case 188: return gdb_sys_msgrcv;
>>>> + case 189: return gdb_sys_msgsnd;
>>>> + case 190: return gdb_sys_semget;
>>>> + case 191: return gdb_sys_semctl;
>>>> + case 192: return gdb_sys_semtimedop;
>>>> + case 193: return gdb_sys_semop;
>>>> + case 194: return gdb_sys_shmget;
>>>> + case 195: return gdb_sys_shmctl;
>>>> + case 196: return gdb_sys_shmat;
>>>> + case 197: return gdb_sys_shmdt;
>>>> + case 198: return gdb_sys_socket;
>>>> + case 199: return gdb_sys_socketpair;
>>>> + case 200: return gdb_sys_bind;
>>>> + case 201: return gdb_sys_listen;
>>>> + case 202: return gdb_sys_accept;
>>>> + case 203: return gdb_sys_connect;
>>>> + case 204: return gdb_sys_getsockname;
>>>> + case 205: return gdb_sys_getpeername;
>>>> + case 206: return gdb_sys_sendto;
>>>> + case 207: return gdb_sys_recvfrom;
>>>> + case 208: return gdb_sys_setsockopt;
>>>> + case 209: return gdb_sys_getsockopt;
>>>> + case 210: return gdb_sys_shutdown;
>>>> + case 211: return gdb_sys_sendmsg;
>>>> + case 212: return gdb_sys_recvmsg;
>>>> + case 213: return gdb_sys_readahead;
>>>> + case 214: return gdb_sys_brk;
>>>> + case 215: return gdb_sys_munmap;
>>>> + case 216: return gdb_sys_mremap;
>>>> + case 217: return gdb_sys_add_key;
>>>> + case 218: return gdb_sys_request_key;
>>>> + case 219: return gdb_sys_keyctl;
>>>> + case 220: return gdb_sys_clone;
>>>> + case 221: return gdb_sys_execve;
>>>> + case 222: return gdb_old_mmap;
>>>> + case 223: return gdb_sys_fadvise64;
>>>> + case 224: return gdb_sys_swapon;
>>>> + case 225: return gdb_sys_swapoff;
>>>> + case 226: return gdb_sys_mprotect;
>>>> + case 227: return gdb_sys_msync;
>>>> + case 228: return gdb_sys_mlock;
>>>> + case 229: return gdb_sys_munlock;
>>>> + case 230: return gdb_sys_mlockall;
>>>> + case 231: return gdb_sys_munlockall;
>>>> + case 232: return gdb_sys_mincore;
>>>> + case 233: return gdb_sys_madvise;
>>>> + case 234: return gdb_sys_remap_file_pages;
>>>> + case 235: return gdb_sys_mbind;
>>>> + case 236: return gdb_sys_get_mempolicy;
>>>> + case 237: return gdb_sys_set_mempolicy;
>>>> + case 238: return gdb_sys_migrate_pages;
>>>> + case 239: return gdb_sys_move_pages;
>>>> + /* case 240: return gdb_sys_rt_tgsigqueueinfo; */
>>>> + /* case 241: return gdb_sys_perf_event_open; */
>>>> + case 242: return gdb_sys_accept4;
>>>> + /* case 243: return gdb_sys_recvmmsg; */
>>>> + /* case 258: return gdb_sys_riscv_hwprobe; */
>>>> + /* case 259: return gdb_sys_riscv_flush_icache; */
>>>> + case 260: return gdb_sys_wait4;
>>>> + /* case 261: return gdb_sys_prlimit64; */
>>>> + /* case 262: return gdb_sys_fanotify_init; */
>>>> + /* case 263: return gdb_sys_fanotify_mark; */
>>>> + /* case 264: return gdb_sys_name_to_handle_at; */
>>>> + /* case 265: return gdb_sys_open_by_handle_at; */
>>>> + /* case 266: return gdb_sys_clock_adjtime; */
>>>> + /* case 267: return gdb_sys_syncfs; */
>>>> + /* case 268: return gdb_sys_setns; */
>>>> + /* case 269: return gdb_sys_sendmmsg; */
>>>> + /* case 270: return gdb_sys_process_vm_readv; */
>>>> + /* case 271: return gdb_sys_process_vm_writev; */
>>>> + /* case 272: return gdb_sys_kcmp; */
>>>> + /* case 273: return gdb_sys_finit_module; */
>>>> + /* case 274: return gdb_sys_sched_setattr; */
>>>> + /* case 275: return gdb_sys_sched_getattr; */
>>>> + /* case 276: return gdb_sys_renameat2; */
>>>> + /* case 277: return gdb_sys_seccomp; */
>>>> + case 278: return gdb_sys_getrandom;
>>>> + /* case 279: return gdb_sys_memfd_create; */
>>>> + /* case 280: return gdb_sys_bpf; */
>>>> + /* case 281: return gdb_sys_execveat; */
>>>> + /* case 282: return gdb_sys_userfaultfd; */
>>>> + /* case 283: return gdb_sys_membarrier; */
>>>> + /* case 284: return gdb_sys_mlock2; */
>>>> + /* case 285: return gdb_sys_copy_file_range; */
>>>> + /* case 286: return gdb_sys_preadv2; */
>>>> + /* case 287: return gdb_sys_pwritev2; */
>>>> + /* case 288: return gdb_sys_pkey_mprotect; */
>>>> + /* case 289: return gdb_sys_pkey_alloc; */
>>>> + /* case 290: return gdb_sys_pkey_free; */
>>>> + case 291: return gdb_sys_statx;
>>>> + /* case 292: return gdb_sys_io_pgetevents; */
>>>> + /* case 293: return gdb_sys_rseq; */
>>>> + /* case 294: return gdb_sys_kexec_file_load; */
>>>> + /* case 424: return gdb_sys_pidfd_send_signal; */
>>>> + /* case 425: return gdb_sys_io_uring_setup; */
>>>> + /* case 426: return gdb_sys_io_uring_enter; */
>>>> + /* case 427: return gdb_sys_io_uring_register; */
>>>> + /* case 428: return gdb_sys_open_tree; */
>>>> + /* case 429: return gdb_sys_move_mount; */
>>>> + /* case 430: return gdb_sys_fsopen; */
>>>> + /* case 431: return gdb_sys_fsconfig; */
>>>> + /* case 432: return gdb_sys_fsmount; */
>>>> + /* case 433: return gdb_sys_fspick; */
>>>> + /* case 434: return gdb_sys_pidfd_open; */
>>>> + /* case 435: return gdb_sys_clone3; */
>>>> + /* case 436: return gdb_sys_close_range; */
>>>> + /* case 437: return gdb_sys_openat2; */
>>>> + /* case 438: return gdb_sys_pidfd_getfd; */
>>>> + /* case 439: return gdb_sys_faccessat2; */
>>>> + /* case 440: return gdb_sys_process_madvise; */
>>>> + /* case 441: return gdb_sys_epoll_pwait2; */
>>>> + /* case 442: return gdb_sys_mount_setattr; */
>>>> + /* case 443: return gdb_sys_quotactl_fd; */
>>>> + /* case 444: return gdb_sys_landlock_create_ruleset; */
>>>> + /* case 445: return gdb_sys_landlock_add_rule; */
>>>> + /* case 446: return gdb_sys_landlock_restrict_self; */
>>>> + /* case 447: return gdb_sys_memfd_secret; */
>>>> + /* case 448: return gdb_sys_process_mrelease; */
>>>> + /* case 449: return gdb_sys_futex_waitv; */
>>>> + /* case 450: return gdb_sys_set_mempolicy_home_node; */
>>>> + /* case 451: return gdb_sys_cachestat; */
>>>> + /* case 452: return gdb_sys_fchmodat2; */
>>>> + /* case 453: return gdb_sys_map_shadow_stack; */
>>>> + /* case 454: return gdb_sys_futex_wake; */
>>>> + /* case 455: return gdb_sys_futex_wait; */
>>>> + /* case 456: return gdb_sys_futex_requeue; */
>>>> + /* case 457: return gdb_sys_statmount; */
>>>> + /* case 458: return gdb_sys_listmount; */
>>>> + /* case 459: return gdb_sys_lsm_get_self_attr; */
>>>> + /* case 460: return gdb_sys_lsm_set_self_attr; */
>>>> + /* case 461: return gdb_sys_lsm_list_modules; */
>>>> + /* case 462: return gdb_sys_mseal; */
>>>> + /* case 463: return gdb_sys_setxattrat; */
>>>> + /* case 464: return gdb_sys_getxattrat; */
>>>> + /* case 465: return gdb_sys_listxattrat; */
>>>> + /* case 466: return gdb_sys_removexattrat; */
>>>> + default:
>>>> + return gdb_sys_no_syscall;
>>>> + }
>>>> +}
>>>> diff --git a/gdb/riscv-linux-tdep.c b/gdb/riscv-linux-tdep.c
>>>> index 4c0c65c1457..f21039a613c 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 00000000000..9dd9e373caa
>>>> --- /dev/null
>>>> +++ b/gdb/riscv-linux-tdep.h
>>>> @@ -0,0 +1,29 @@
>>>> +/* Copyright (C) 2024-2025 Free Software Foundation, Inc.
>>>> +
>>>> + This file is part of GDB.
>>>> +
>>>> + This program is free software; you can redistribute it and/or modify
>>>> + it under the terms of the GNU General Public License as published by
>>>> + the Free Software Foundation; either version 3 of the License, or
>>>> + (at your option) any later version.
>>>> +
>>>> + This program is distributed in the hope that it will be useful,
>>>> + but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>>> + GNU General Public License for more details.
>>>> +
>>>> + You should have received a copy of the GNU General Public License
>>>> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
>>>> +
>>>> +#ifndef GDB_RISCV_LINUX_TDEP_H
>>>> +#define GDB_RISCV_LINUX_TDEP_H
>>>> +
>>>> +#include "linux-record.h"
>>>> +
>>>> +/* riscv64_canonicalize_syscall maps from the native riscv Linux set
>>>> + of syscall ids into a canonical set of syscall ids used by
>>>> + process record. */
>>>> +
>>>> +extern enum gdb_syscall riscv64_canonicalize_syscall (int syscall);
>>>> +
>>>> +#endif /* GDB_RISCV_LINUX_TDEP_H */
>>>> diff --git a/gdb/riscv-tdep.c b/gdb/riscv-tdep.c
>>>> index 91f6dffebe1..19367ff55ba 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 @@ class riscv_insn
>>>> 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 @@ class riscv_insn
>>>> m_rs2 = decode_register_index_short (ival, OP_SH_CRS2S);
>>>> }
>>>>
>>>> - /* Fetch instruction from target memory at ADDR, return the content of
>>>> - the instruction, and update LEN with the instruction length. */
>>>> - static ULONGEST fetch_instruction (struct gdbarch *gdbarch,
>>>> - CORE_ADDR addr, int *len);
>>>> -
>>>> /* The length of the instruction in bytes. Should be 2 or 4. */
>>>> int m_length;
>>>>
>>>> @@ -4433,6 +4436,9 @@ riscv_gdbarch_init (struct gdbarch_info info,
>>>> set_gdbarch_stap_register_indirection_suffixes
>>>> (gdbarch, stap_register_indirection_suffixes);
>>>>
>>>> + /* Process record-replay */
>>>> + set_gdbarch_process_record (gdbarch, riscv_process_record);
>>>> +
>>>> /* Hook in OS ABI-specific overrides, if they have been registered. */
>>>> gdbarch_init_osabi (info, gdbarch);
>>>>
>>>> @@ -4866,3 +4872,673 @@ equivalent change in the disassembler output."),
>>>> &setriscvcmdlist,
>>>> &showriscvcmdlist);
>>>> }
>>>> +
>>>> +/* A wrapper to read register under number regnum to address addr.
>>>> + Returns false if error happened and makes warning. */
>>>> +
>>>> +static bool
>>>> +try_read (struct regcache *regcache, int regnum, ULONGEST &addr)
>>>> +{
>>>> + gdb_assert (regcache != nullptr);
>>>> +
>>>> + if (regcache->raw_read (regnum, &addr)
>>>> + != register_status::REG_VALID)
>>>> + {
>>>> + warning (_("Can not read at address %lx"), addr);
>>>> + return false;
>>>> + }
>>>> + return true;
>>>> +}
>>>> +
>>>> +/* Helper class to record instruction. */
>>>> +
>>>> +class riscv_recorded_insn final
>>>> +{
>>>> +public:
>>>> + /* Type for saved register. */
>>>> + using regnum_type = int;
>>>> + /* Type for saved memory. First is address, second is length. */
>>>> + using memory_type = std::pair<CORE_ADDR, int>;
>>>> +
>>>> + /* Enum class that represents which type does recording belong to. */
>>>> + enum class record_type
>>>> + {
>>>> + UNKNOWN,
>>>> + ORDINARY,
>>>> +
>>>> + /* Corner cases. */
>>>> + ECALL,
>>>> + EBREAK,
>>>> + };
>>>> +
>>>> +private:
>>>> + /* Type for set of registers that need to be saved. */
>>>> + using recorded_regs = std::vector<regnum_type>;
>>>> + /* Type for set of memory records that need to be saved. */
>>>> + using recorded_mems = std::vector<memory_type>;
>>>> +
>>>> + /* Type for memory address, extracted from memory_type. */
>>>> + using mem_addr = decltype (std::declval<memory_type> ().first);
>>>> + /* Type for memory length, extracted from memory_type. */
>>>> + using mem_len = decltype (std::declval<memory_type> ().second);
>>>> +
>>>> + /* Record type of current instruction. */
>>>> + record_type m_record_type = record_type::UNKNOWN;
>>>> +
>>>> + /* Flag that represents was there an error in current recording. */
>>>> + bool m_error_occured = false;
>>>> +
>>>> + /* Set of registers that need to be recorded. */
>>>> + recorded_regs m_regs;
>>>> + /* Set of memory chunks that need to be recorded. */
>>>> + recorded_mems m_mems;
>>>> +
>>>> + /* Width in bytes of the general purpose registers for GDBARCH,
>>>> + where recording is happening. */
>>>> + int m_xlen = 0;
>>>> +
>>>> + /* Helper for decode 16-bit instruction RS1. */
>>>> + static regnum_type
>>>> + decode_crs1_short (ULONGEST opcode) noexcept
>>>> + {
>>>> + return ((opcode >> OP_SH_CRS1S) & OP_MASK_CRS1S) + 8;
>>>> + }
>>>> +
>>>> + /* Helper for decode 16-bit instruction RS2. */
>>>> + static regnum_type
>>>> + decode_crs2_short (ULONGEST opcode) noexcept
>>>> + {
>>>> + return ((opcode >> OP_SH_CRS2S) & OP_MASK_CRS2S) + 8;
>>>> + }
>>>> +
>>>> + /* Helper for decode 16-bit instruction CRS1. */
>>>> + static regnum_type
>>>> + decode_crs1 (ULONGEST opcode) noexcept
>>>> + {
>>>> + return ((opcode >> OP_SH_RD) & OP_MASK_RD);
>>>> + }
>>>> +
>>>> + /* Helper for decode 16-bit instruction CRS2. */
>>>> + static regnum_type
>>>> + decode_crs2 (ULONGEST opcode) noexcept
>>>> + {
>>>> + return ((opcode >> OP_SH_CRS2) & OP_MASK_CRS2);
>>>> + }
>>>> +
>>>> + /* Helper for decode 32-bit instruction RD. */
>>>> + static regnum_type
>>>> + decode_rd (ULONGEST ival) noexcept
>>>> + {
>>>> + return (ival >> OP_SH_RD) & OP_MASK_RD;
>>>> + }
>>>> +
>>>> + /* Helper for decode 32-bit instruction RS1. */
>>>> + static regnum_type
>>>> + decode_rs1 (ULONGEST ival) noexcept
>>>> + {
>>>> + return (ival >> OP_SH_RS1) & OP_MASK_RS1;
>>>> + }
>>>> +
>>>> + /* Helper for decode 32-bit instruction RS2. */
>>>> + static regnum_type
>>>> + decode_rs2 (ULONGEST ival) noexcept
>>>> + {
>>>> + return (ival >> OP_SH_RS2) & OP_MASK_RS2;
>>>> + }
>>>> +
>>>> + /* Helper for decode 32-bit instruction CSR. */
>>>> + static regnum_type
>>>> + decode_csr (ULONGEST ival) noexcept
>>>> + {
>>>> + return (ival >> OP_SH_CSR) & OP_MASK_CSR;
>>>> + }
>>>> +
>>>> + /* Set ordinary record type. Always returns true. */
>>>> + bool
>>>> + set_ordinary_record_type () noexcept
>>>> + {
>>>> + m_record_type = record_type::ORDINARY;
>>>> + return true;
>>>> + }
>>>> +
>>>> + /* Set error happened. Always returns false. */
>>>> + bool
>>>> + set_error () noexcept
>>>> + {
>>>> + m_error_occured = true;
>>>> + return false;
>>>> + }
>>>> +
>>>> + /* Check if current recording has an error. */
>>>> + bool
>>>> + has_error () const noexcept
>>>> + {
>>>> + return m_error_occured;
>>>> + }
>>>> +
>>>> + /* Reads register. Sets error and returns false if error happened. */
>>>> + bool
>>>> + read_reg (struct regcache *regcache, regnum_type reg,
>>>> + ULONGEST &addr) noexcept
>>>> + {
>>>> + gdb_assert (regcache != nullptr);
>>>> +
>>>> + if (!try_read (regcache, reg, addr))
>>>> + return set_error ();
>>>> + return true;
>>>> + }
>>>> +
>>>> + /* Save register. Returns true or aborts if exception happened. */
>>>> + bool
>>>> + save_reg (regnum_type regnum) noexcept
>>>> + {
>>>> + m_regs.emplace_back (regnum);
>>>> + return true;
>>>> + }
>>>> +
>>>> + /* Save memory chunk. Returns true or aborts if exception happened. */
>>>> + bool
>>>> + save_mem (mem_addr addr, mem_len len) noexcept
>>>> + {
>>>> + m_mems.emplace_back (addr, len);
>>>> + return true;
>>>> + }
>>>> +
>>>> + /* Returns true if instruction needs only saving pc. */
>>>> + static bool
>>>> + need_save_pc (ULONGEST ival) noexcept
>>>> + {
>>>> + return (is_beq_insn (ival) || is_bne_insn (ival) || is_blt_insn (ival)
>>>> + || is_bge_insn (ival) || is_bltu_insn (ival) || is_bgeu_insn (ival)
>>>> + || is_fence_insn (ival) || is_pause_insn (ival)
>>>> + || is_fence_i_insn (ival));
>>>> + }
>>>> +
>>>> + /* Returns true if instruction is classified. */
>>>> + bool
>>>> + try_save_pc (ULONGEST ival) noexcept
>>>> + {
>>>> + if (!need_save_pc (ival))
>>>> + return false;
>>>> +
>>>> + return set_ordinary_record_type ();
>>>> + }
>>>> +
>>>> + /* Returns true if instruction needs only saving pc and rd. */
>>>> + static bool
>>>> + need_save_pc_rd (ULONGEST ival) noexcept
>>>> + {
>>>> + return (is_lui_insn (ival) || is_auipc_insn (ival) || is_jal_insn (ival)
>>>> + || is_jalr_insn (ival) || is_lb_insn (ival) || is_lh_insn (ival)
>>>> + || is_lw_insn (ival) || is_lbu_insn (ival) || is_lhu_insn (ival)
>>>> + || is_addi_insn (ival) || is_slti_insn (ival)
>>>> + || is_sltiu_insn (ival) || is_xori_insn (ival) || is_ori_insn (ival)
>>>> + || is_andi_insn (ival) || is_slli_rv32_insn (ival)
>>>> + || is_srli_rv32_insn (ival) || is_srai_rv32_insn (ival)
>>>> + || is_add_insn (ival) || is_sub_insn (ival) || is_sll_insn (ival)
>>>> + || is_slt_insn (ival) || is_sltu_insn (ival) || is_xor_insn (ival)
>>>> + || is_srl_insn (ival) || is_sra_insn (ival) || is_or_insn (ival)
>>>> + || is_and_insn (ival) || is_lwu_insn (ival) || is_ld_insn (ival)
>>>> + || is_slli_insn (ival) || is_srli_insn (ival) || is_srai_insn (ival)
>>>> + || is_addiw_insn (ival) || is_slliw_insn (ival)
>>>> + || is_srliw_insn (ival) || is_sraiw_insn (ival)
>>>> + || is_addw_insn (ival) || is_subw_insn (ival) || is_sllw_insn (ival)
>>>> + || is_srlw_insn (ival) || is_sraw_insn (ival) || is_mul_insn (ival)
>>>> + || is_mulh_insn (ival) || is_mulhsu_insn (ival)
>>>> + || is_mulhu_insn (ival) || is_div_insn (ival) || is_divu_insn (ival)
>>>> + || is_rem_insn (ival) || is_remu_insn (ival) || is_mulw_insn (ival)
>>>> + || is_divw_insn (ival) || is_divuw_insn (ival)
>>>> + || is_remw_insn (ival) || is_remuw_insn (ival)
>>>> + || is_lr_w_insn (ival) || is_lr_d_insn (ival)
>>>> + || is_fcvt_w_s_insn (ival) || is_fcvt_wu_s_insn (ival)
>>>> + || is_fmv_x_s_insn (ival) || is_feq_s_insn (ival)
>>>> + || is_flt_s_insn (ival) || is_fle_s_insn (ival)
>>>> + || is_fclass_s_insn (ival) || is_fcvt_l_s_insn (ival)
>>>> + || is_fcvt_lu_s_insn (ival) || is_feq_d_insn (ival)
>>>> + || is_flt_d_insn (ival) || is_fle_d_insn (ival)
>>>> + || is_fclass_d_insn (ival) || is_fcvt_w_d_insn (ival)
>>>> + || is_fcvt_wu_d_insn (ival) || is_fcvt_l_d_insn (ival)
>>>> + || is_fcvt_lu_d_insn (ival) || is_fmv_x_d_insn (ival));
>>>> + }
>>>> +
>>>> + /* Returns true if instruction is classified. This function can set
>>>> + m_error_occured. */
>>>> + bool
>>>> + try_save_pc_rd (ULONGEST ival) noexcept
>>>> + {
>>>> + if (!need_save_pc_rd (ival))
>>>> + return false;
>>>> +
>>>> + return (!save_reg (decode_rd (ival)) || set_ordinary_record_type ());
>>>> + }
>>>> +
>>>> + /* Returns true if instruction needs only saving pc and
>>>> + floating point rd. */
>>>> + static bool
>>>> + need_save_pc_fprd (ULONGEST ival) noexcept
>>>> + {
>>>> + return (is_flw_insn (ival) || is_fmadd_s_insn (ival)
>>>> + || is_fmsub_s_insn (ival) || is_fnmsub_s_insn (ival)
>>>> + || is_fnmadd_s_insn (ival) || is_fadd_s_insn (ival)
>>>> + || is_fsub_s_insn (ival) || is_fmul_s_insn (ival)
>>>> + || is_fdiv_s_insn (ival) || is_fsqrt_s_insn (ival)
>>>> + || is_fsgnj_s_insn (ival) || is_fsgnjn_s_insn (ival)
>>>> + || is_fsgnjx_s_insn (ival) || is_fmin_s_insn (ival)
>>>> + || is_fmax_s_insn (ival) || is_fcvt_s_w_insn (ival)
>>>> + || is_fcvt_s_wu_insn (ival) || is_fmv_s_x_insn (ival)
>>>> + || is_fcvt_s_l_insn (ival) || is_fcvt_s_lu_insn (ival)
>>>> + || is_fld_insn (ival) || is_fmadd_d_insn (ival)
>>>> + || is_fmsub_d_insn (ival) || is_fnmsub_d_insn (ival)
>>>> + || is_fnmadd_d_insn (ival) || is_fadd_d_insn (ival)
>>>> + || is_fsub_d_insn (ival) || is_fmul_d_insn (ival)
>>>> + || is_fdiv_d_insn (ival) || is_fsqrt_d_insn (ival)
>>>> + || is_fsgnj_d_insn (ival) || is_fsgnjn_d_insn (ival)
>>>> + || is_fsgnjx_d_insn (ival) || is_fmin_d_insn (ival)
>>>> + || is_fmax_d_insn (ival) || is_fcvt_s_d_insn (ival)
>>>> + || is_fcvt_d_s_insn (ival) || is_fcvt_d_w_insn (ival)
>>>> + || is_fcvt_d_wu_insn (ival) || is_fcvt_d_l_insn (ival)
>>>> + || is_fcvt_d_lu_insn (ival) || is_fmv_d_x_insn (ival));
>>>> + }
>>>> +
>>>> + /* Returns true if instruction is classified. This function can set
>>>> + m_error_occured. */
>>>> + bool
>>>> + try_save_pc_fprd (ULONGEST ival) noexcept
>>>> + {
>>>> + if (!need_save_pc_fprd (ival))
>>>> + return false;
>>>> +
>>>> + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_rd (ival))
>>>> + || set_ordinary_record_type ());
>>>> + }
>>>> +
>>>> + /* Returns true if instruction needs only saving pc, rd and csr. */
>>>> + static bool
>>>> + need_save_pc_rd_csr (ULONGEST ival) noexcept
>>>> + {
>>>> + return (is_csrrw_insn (ival) || is_csrrs_insn (ival) || is_csrrc_insn (ival)
>>>> + || is_csrrwi_insn (ival) || is_csrrsi_insn (ival)
>>>> + || is_csrrc_insn (ival));
>>>> + }
>>>> +
>>>> + /* Returns true if instruction is classified. This function can set
>>>> + m_error_occured. */
>>>> + bool
>>>> + try_save_pc_rd_csr (ULONGEST ival) noexcept
>>>> + {
>>>> + if (!need_save_pc_rd_csr (ival))
>>>> + return false;
>>>> +
>>>> + return (!save_reg (decode_rd (ival))
>>>> + || !save_reg (RISCV_FIRST_CSR_REGNUM + decode_csr (ival))
>>>> + || set_ordinary_record_type ());
>>>> + }
>>>> +
>>>> + /* Returns the size of the memory chunk that needs to be saved if the
>>>> + instruction belongs to the group that needs only saving pc and memory.
>>>> + Otherwise returns 0. */
>>>> + static mem_len
>>>> + need_save_pc_mem (ULONGEST ival) noexcept
>>>> + {
>>>> + if (is_sb_insn (ival))
>>>> + return 1;
>>>> + if (is_sh_insn (ival))
>>>> + return 2;
>>>> + if (is_sw_insn (ival) || is_fsw_insn (ival))
>>>> + return 4;
>>>> + if (is_sd_insn (ival) || is_fsd_insn (ival))
>>>> + return 8;
>>>> + return 0;
>>>> + }
>>>> +
>>>> + /* Returns true if instruction is classified. This function can set
>>>> + m_error_occured. */
>>>> + bool
>>>> + try_save_pc_mem (ULONGEST ival, struct regcache *regcache) noexcept
>>>> + {
>>>> + gdb_assert (regcache != nullptr);
>>>> +
>>>> + mem_addr addr = mem_addr{};
>>>> + mem_len len = need_save_pc_mem (ival);
>>>> + if (len <= 0)
>>>> + return false;
>>>> +
>>>> + mem_len offset = EXTRACT_STYPE_IMM (ival);
>>>> + return (!read_reg (regcache, decode_rs1 (ival), addr)
>>>> + || !save_mem (addr + offset, len) || set_ordinary_record_type ());
>>>> + }
>>>> +
>>>> + /* Returns the size of the memory chunk that needs to be saved if the
>>>> + instruction belongs to the group that needs only saving pc, rd and memory.
>>>> + Otherwise returns 0. */
>>>> + static mem_len
>>>> + need_save_pc_rd_mem (ULONGEST ival) noexcept
>>>> + {
>>>> + if (is_sc_w_insn (ival) || is_amoswap_w_insn (ival)
>>>> + || is_amoadd_w_insn (ival) || is_amoxor_w_insn (ival)
>>>> + || is_amoand_w_insn (ival) || is_amoor_w_insn (ival)
>>>> + || is_amomin_w_insn (ival) || is_amomax_w_insn (ival)
>>>> + || is_amominu_w_insn (ival) || is_amomaxu_w_insn (ival))
>>>> + return 4;
>>>> + if (is_sc_d_insn (ival) || is_amoswap_d_insn (ival)
>>>> + || is_amoadd_d_insn (ival) || is_amoxor_d_insn (ival)
>>>> + || is_amoand_d_insn (ival) || is_amoor_d_insn (ival)
>>>> + || is_amomin_d_insn (ival) || is_amomax_d_insn (ival)
>>>> + || is_amominu_d_insn (ival) || is_amomaxu_d_insn (ival))
>>>> + return 8;
>>>> + return 0;
>>>> + }
>>>> +
>>>> + /* Returns true if instruction is classified. This function can set
>>>> + m_error_occured. */
>>>> + bool
>>>> + try_save_pc_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
>>>> + {
>>>> + gdb_assert (regcache != nullptr);
>>>> +
>>>> + mem_len len = need_save_pc_rd_mem (ival);
>>>> + mem_addr addr = 0;
>>>> + if (len <= 0)
>>>> + return false;
>>>> +
>>>> + return (!read_reg (regcache, decode_rs1 (ival), addr)
>>>> + || !save_mem (addr, len) || !save_reg (decode_rd (ival))
>>>> + || set_ordinary_record_type ());
>>>> + }
>>>> +
>>>> + /* Returns true if instruction is successfully recordered. The length of
>>>> + the instruction must be equal 4 bytes. */
>>>> + bool
>>>> + record_insn_len4 (ULONGEST ival, struct regcache *regcache) noexcept
>>>> + {
>>>> + gdb_assert (regcache != nullptr);
>>>> +
>>>> + if (is_ecall_insn (ival))
>>>> + {
>>>> + m_record_type = record_type::ECALL;
>>>> + return true;
>>>> + }
>>>> +
>>>> + if (is_ebreak_insn (ival))
>>>> + {
>>>> + m_record_type = record_type::EBREAK;
>>>> + return true;
>>>> + }
>>>> +
>>>> + if (try_save_pc (ival) || try_save_pc_rd (ival) || try_save_pc_fprd (ival)
>>>> + || try_save_pc_rd_csr (ival) || try_save_pc_mem (ival, regcache)
>>>> + || try_save_pc_rd_mem (ival, regcache))
>>>> + return !has_error ();
>>>> +
>>>> + warning (_("Currently this instruction with len 4(%lx) is unsupported"),
>>>> + ival);
>>>> + return false;
>>>> + }
>>>> +
>>>> + /* Returns true if instruction is successfully recordered. The length of
>>>> + the instruction must be equal 2 bytes. */
>>>> + bool
>>>> + record_insn_len2 (ULONGEST ival, struct regcache *regcache) noexcept
>>>> + {
>>>> + gdb_assert (regcache != nullptr);
>>>> +
>>>> + mem_addr addr = mem_addr{};
>>>> +
>>>> + /* The order here is very important, because
>>>> + opcodes of some instructions may be the same. */
>>>> +
>>>> + if (is_c_addi4spn_insn (ival) || is_c_lw_insn (ival)
>>>> + || (m_xlen == 8 && is_c_ld_insn (ival)))
>>>> + return (!save_reg (decode_crs2_short (ival))
>>>> + || set_ordinary_record_type ());
>>>> +
>>>> + if (is_c_fld_insn (ival) || (m_xlen == 4 && is_c_flw_insn (ival)))
>>>> + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs2_short (ival))
>>>> + || set_ordinary_record_type ());
>>>> +
>>>> + if (is_c_fsd_insn (ival) || (m_xlen == 8 && is_c_sd_insn (ival)))
>>>> + {
>>>> + ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LD_IMM (ival)};
>>>> + return (!read_reg (regcache, decode_crs1_short (ival), addr)
>>>> + || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
>>>> + }
>>>> +
>>>> + if ((m_xlen == 4 && is_c_fsw_insn (ival)) || is_c_sw_insn (ival))
>>>> + {
>>>> + ULONGEST offset = ULONGEST{EXTRACT_CLTYPE_LW_IMM (ival)};
>>>> + return (!read_reg (regcache, decode_crs1_short (ival), addr)
>>>> + || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
>>>> + }
>>>> +
>>>> + if (is_c_nop_insn (ival))
>>>> + return set_ordinary_record_type ();
>>>> +
>>>> + if (is_c_addi_insn (ival))
>>>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>>>> +
>>>> + if (m_xlen == 4 && is_c_jal_insn (ival))
>>>> + return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
>>>> +
>>>> + if ((m_xlen == 8 && is_c_addiw_insn (ival)) || is_c_li_insn (ival))
>>>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>>>> +
>>>> + if (is_c_addi16sp_insn (ival))
>>>> + return (!save_reg (RISCV_SP_REGNUM) || set_ordinary_record_type ());
>>>> +
>>>> + if (is_c_lui_insn (ival))
>>>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>>>> +
>>>> + if (is_c_srli_insn (ival) || is_c_srai_insn (ival) || is_c_andi_insn (ival)
>>>> + || is_c_sub_insn (ival) || is_c_xor_insn (ival) || is_c_or_insn (ival)
>>>> + || is_c_and_insn (ival) || (m_xlen == 8 && is_c_subw_insn (ival))
>>>> + || (m_xlen == 8 && is_c_addw_insn (ival)))
>>>> + return (!save_reg (decode_crs1_short (ival))
>>>> + || set_ordinary_record_type ());
>>>> +
>>>> + if (is_c_j_insn (ival) || is_c_beqz_insn (ival) || is_c_bnez_insn (ival))
>>>> + return set_ordinary_record_type ();
>>>> +
>>>> + if (is_c_slli_insn (ival))
>>>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>>>> +
>>>> + if (is_c_fldsp_insn (ival) || (m_xlen == 4 && is_c_flwsp_insn (ival)))
>>>> + return (!save_reg (RISCV_FIRST_FP_REGNUM + decode_crs1 (ival))
>>>> + || set_ordinary_record_type ());
>>>> +
>>>> + if (is_c_lwsp_insn (ival) || (m_xlen == 8 && is_c_ldsp_insn (ival)))
>>>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>>>> +
>>>> + if (is_c_jr_insn (ival))
>>>> + return set_ordinary_record_type ();
>>>> +
>>>> + if (is_c_mv_insn (ival))
>>>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>>>> +
>>>> + if (is_c_ebreak_insn (ival))
>>>> + {
>>>> + m_record_type = record_type::EBREAK;
>>>> + return true;
>>>> + }
>>>> +
>>>> + if (is_c_jalr_insn (ival))
>>>> + return (!save_reg (RISCV_RA_REGNUM) || set_ordinary_record_type ());
>>>> +
>>>> + if (is_c_add_insn (ival))
>>>> + return (!save_reg (decode_crs1 (ival)) || set_ordinary_record_type ());
>>>> +
>>>> + if (is_c_fsdsp_insn (ival) || (m_xlen == 8 && is_c_sdsp_insn (ival)))
>>>> + {
>>>> + ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SDSP_IMM (ival)};
>>>> + return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
>>>> + || !save_mem (addr + offset, 8) || set_ordinary_record_type ());
>>>> + }
>>>> +
>>>> + if (is_c_swsp_insn (ival) || (m_xlen == 4 && is_c_fswsp_insn (ival)))
>>>> + {
>>>> + ULONGEST offset = ULONGEST{EXTRACT_CSSTYPE_SWSP_IMM (ival)};
>>>> + return (!read_reg (regcache, RISCV_SP_REGNUM, addr)
>>>> + || !save_mem (addr + offset, 4) || set_ordinary_record_type ());
>>>> + }
>>>> +
>>>> + warning (_("Currently this instruction with len 2(%lx) is unsupported"),
>>>> + ival);
>>>> + return false;
>>>> + }
>>>> +
>>>> +public:
>>>> + /* Iterator for registers that need to be recorded. */
>>>> + using regs_iter = recorded_regs::const_iterator;
>>>> + /* Iterator for memory chunks that need to be recorded. */
>>>> + using mems_iter = recorded_mems::const_iterator;
>>>> +
>>>> + /* Record instruction at address addr. Returns false if error happened. */
>>>> + bool
>>>> + record (gdbarch *gdbarch, struct regcache *regcache, CORE_ADDR addr) noexcept
>>>> + {
>>>> + gdb_assert (gdbarch != nullptr);
>>>> + gdb_assert (regcache != nullptr);
>>>> +
>>>> + int m_length = 0;
>>>> + m_xlen = riscv_isa_xlen (gdbarch);
>>>> + ULONGEST ival = riscv_insn::fetch_instruction (gdbarch, addr, &m_length);
>>>> + if (!save_reg (RISCV_PC_REGNUM))
>>>> + return false;
>>>> +
>>>> + if (m_length == 4)
>>>> + return record_insn_len4 (ival, regcache);
>>>> +
>>>> + if (m_length == 2)
>>>> + return record_insn_len2 (ival, regcache);
>>>> +
>>>> + /* 6 bytes or more. If the instruction is longer than 8 bytes, we don't
>>>> + have full instruction bits in ival. At least, such long instructions
>>>> + are not defined yet, so just ignore it. */
>>>> + gdb_assert (m_length > 0 && m_length % 2 == 0);
>>>> +
>>>> + warning (_("Can not record unknown instruction (opcode = %lx)"), ival);
>>>> + return false;
>>>> + }
>>>> +
>>>> + /* Get record type of instruction. */
>>>> + record_type
>>>> + get_record_type () const noexcept
>>>> + {
>>>> + return m_record_type;
>>>> + }
>>>> +
>>>> + /* Returns an iterator to the beginning of the registers that need
>>>> + to be saved. */
>>>> + regs_iter
>>>> + regs_begin () const noexcept
>>>> + {
>>>> + return m_regs.begin ();
>>>> + }
>>>> +
>>>> + /* Returns an iterator to the end of the registers that need
>>>> + to be saved. */
>>>> + regs_iter
>>>> + regs_end () const noexcept
>>>> + {
>>>> + return m_regs.end ();
>>>> + }
>>>> +
>>>> + /* Returns an iterator to the beginning of the memory chunks that need
>>>> + to be saved. */
>>>> + mems_iter
>>>> + mems_begin () const noexcept
>>>> + {
>>>> + return m_mems.begin ();
>>>> + }
>>>> +
>>>> + /* Returns an iterator to the end of the memory chunks that need
>>>> + to be saved. */
>>>> + mems_iter
>>>> + mems_end () const noexcept
>>>> + {
>>>> + return m_mems.end ();
>>>> + }
>>>> +};
>>>> +
>>>> +/* A helper function to record instruction using record API. */
>>>> +
>>>> +static int
>>>> +riscv_record_insn_details (struct gdbarch *gdbarch, struct regcache *regcache,
>>>> + const riscv_recorded_insn &insn)
>>>> +{
>>>> + gdb_assert (gdbarch != nullptr);
>>>> + gdb_assert (regcache != nullptr);
>>>> +
>>>> + riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
>>>> + auto regs_begin = insn.regs_begin ();
>>>> + auto regs_end = insn.regs_end ();
>>>> + if (std::any_of (regs_begin,
>>>> + regs_end,
>>>> + [®cache] (auto &®_it)
>>>> + {
>>>> + return record_full_arch_list_add_reg (regcache, reg_it);
>>>> + }))
>>>> + return -1;
>>>> +
>>>> + auto mems_begin = insn.mems_begin ();
>>>> + auto mems_end = insn.mems_end ();
>>>> + if (std::any_of (mems_begin,
>>>> + mems_end,
>>>> + [] (auto &&mem_it)
>>>> + {
>>>> + return record_full_arch_list_add_mem (mem_it.first,
>>>> + mem_it.second);
>>>> + }))
>>>> + return -1;
>>>> +
>>>> + switch (insn.get_record_type ())
>>>> + {
>>>> + case riscv_recorded_insn::record_type::ORDINARY:
>>>> + break;
>>>> +
>>>> + case riscv_recorded_insn::record_type::ECALL:
>>>> + {
>>>> + if (!tdep->riscv_syscall_record)
>>>> + {
>>>> + warning (_("Syscall record is not supported"));
>>>> + return -1;
>>>> + }
>>>> + ULONGEST reg_val = ULONGEST{};
>>>> + if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
>>>> + return -1;
>>>> + return tdep->riscv_syscall_record (regcache, reg_val);
>>>> + }
>>>> +
>>>> + case riscv_recorded_insn::record_type::EBREAK:
>>>> + break;
>>>> +
>>>> + default:
>>>> + return -1;
>>>> + }
>>>> + return 0;
>>>> +}
>>>> +
>>>> +/* Parse the current instruction and record the values of the registers and
>>>> + memory that will be changed in current instruction to record_arch_list.
>>>> + Return -1 if something is wrong. */
>>>> +
>>>> +int
>>>> +riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
>>>> + CORE_ADDR addr)
>>>> +{
>>>> + gdb_assert (gdbarch != nullptr);
>>>> + gdb_assert (regcache != nullptr);
>>>> +
>>>> + riscv_recorded_insn insn;
>>>> + if (!insn.record (gdbarch, regcache, addr))
>>>> + {
>>>> + record_full_arch_list_add_end ();
>>>> + return -1;
>>>> + }
>>>> +
>>>> + int ret_val = riscv_record_insn_details (gdbarch, regcache, insn);
>>>> +
>>>> + if (record_full_arch_list_add_end ())
>>>> + return -1;
>>>> +
>>>> + return ret_val;
>>>> +}
>>>> diff --git a/gdb/riscv-tdep.h b/gdb/riscv-tdep.h
>>>> index ad1e9596b83..2903aefd007 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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
>>>> new file mode 100755
>>>> index 00000000000..30e52b7e472
>>>> --- /dev/null
>>>> +++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
>>>> @@ -0,0 +1,163 @@
>>>> +#!/usr/bin/env python3
>>>> +# pylint: disable=invalid-name
>>>> +
>>>> +# Copyright (C) 2024-2025 Free Software Foundation, Inc.
>>>> +# Contributed by Timur Golubovich
>>>> +
>>>> +# This file is part of GDB.
>>>> +
>>>> +# This program is free software; you can redistribute it and/or modify
>>>> +# it under the terms of the GNU General Public License as published by
>>>> +# the Free Software Foundation; either version 3 of the License, or
>>>> +# (at your option) any later version.
>>>> +
>>>> +# This program is distributed in the hope that it will be useful,
>>>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>>> +# GNU General Public License for more details.
>>>> +
>>>> +# You should have received a copy of the GNU General Public License
>>>> +# along with this program. If not, see <http://www.gnu.org/licenses/>.
>>>> +
>>>> +
>>>> +# To get help message for this script, run:
>>>> +# ./gdb/syscalls/riscv-canonicalize-syscall-gen.py --help
>>>> +
>>>> +# Execution result:
>>>> +
>>>> +# usage: riscv-canonicalize-syscall-gen.py [-h] -i INPUT
>>>> +#
>>>> +# Generate file gdb/riscv-canonicalize-syscall-gen.c from path to riscv linux syscalls.
>>>> +#
>>>> +# options:
>>>> +# -h, --help show this help message and exit
>>>> +# -i INPUT, --input INPUT
>>>> +# path to riscv linux syscalls (glibc/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h)
>>>> +
>>>> +import argparse
>>>> +import re
>>>> +import sys
>>>> +from pathlib import Path as _Path
>>>> +
>>>> +head = """\
>>>> +/* DO NOT EDIT: Autogenerated by riscv-canonicalize-syscall-gen.py
>>>> +
>>>> + Copyright (C) 2024-2025 Free Software Foundation, Inc.
>>>> +
>>>> + This file is part of GDB.
>>>> +
>>>> + This program is free software; you can redistribute it and/or modify
>>>> + it under the terms of the GNU General Public License as published by
>>>> + the Free Software Foundation; either version 3 of the License, or
>>>> + (at your option) any later version.
>>>> +
>>>> + This program is distributed in the hope that it will be useful,
>>>> + but WITHOUT ANY WARRANTY; without even the implied warranty of
>>>> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
>>>> + GNU General Public License for more details.
>>>> +
>>>> + You should have received a copy of the GNU General Public License
>>>> + along with this program. If not, see <http://www.gnu.org/licenses/>. */
>>>> +
>>>> +#include "defs.h"
>>>> +#include "riscv-linux-tdep.h"
>>>> +
>>>> +/* riscv64_canonicalize_syscall maps from the native riscv 64 Linux set
>>>> + of syscall ids into a canonical set of syscall ids used by
>>>> + process record. */
>>>> +
>>>> +enum gdb_syscall
>>>> +riscv64_canonicalize_syscall (int syscall)
>>>> +{
>>>> + switch (syscall)
>>>> + {
>>>> +"""
>>>> +
>>>> +tail = """\
>>>> + default:
>>>> + return gdb_sys_no_syscall;
>>>> + }
>>>> +}
>>>> +"""
>>>> +
>>>> +
>>>> +class Generator:
>>>> + def _get_gdb_syscalls(self, gdb_syscalls_path: _Path) -> list[str]:
>>>> + gdb_syscalls: list[str] = []
>>>> + with open(gdb_syscalls_path, "r", encoding="UTF-8") as file:
>>>> + lines = file.readlines()
>>>> + for line in lines:
>>>> + match = re.search(r"\s*(?P<name>gdb_sys_[^S]+)\S*=", line)
>>>> + if match:
>>>> + gdb_syscalls.append(match.group("name").strip())
>>>> + return gdb_syscalls
>>>> +
>>>> + def _get_canon_syscalls_lines(self, syscalls_path: _Path, gdb_syscalls: list[str]) -> list[str]:
>>>> + canon_syscalls: dict[int, str] = {}
>>>> + with open(syscalls_path, "r", encoding="UTF-8") as file:
>>>> + lines = file.readlines()
>>>> + for line in lines:
>>>> + match = re.match(r"#define\s+__NR_(?P<name>[^\s]+)\s+(?P<number>\d+)", line)
>>>> + if match:
>>>> + syscall_name = match.group("name")
>>>> + syscall_num = int(match.group("number"))
>>>> + gdb_syscall_name = f"gdb_sys_{syscall_name}"
>>>> + if gdb_syscall_name in gdb_syscalls:
>>>> + value = f" case {syscall_num}: return {gdb_syscall_name};\n"
>>>> + canon_syscalls[syscall_num] = value
>>>> + # this is a place for corner cases
>>>> + elif syscall_name == "mmap":
>>>> + gdb_old_syscall_name = "gdb_old_mmap"
>>>> + value = f" case {syscall_num}: return {gdb_old_syscall_name};\n"
>>>> + canon_syscalls[syscall_num] = value
>>>> + else:
>>>> + value = f" /* case {syscall_num}: return {gdb_syscall_name}; */\n"
>>>> + canon_syscalls[syscall_num] = value
>>>> + return [canon_syscalls[syscall_num] for syscall_num in sorted(canon_syscalls)]
>>>> +
>>>> + def generate(self, syscalls_path: _Path) -> None:
>>>> + repo_path = _Path(__file__).parent.parent.parent
>>>> + gdb_syscalls_path = repo_path / "gdb" / "linux-record.h"
>>>> + canon_syscalls_path = repo_path / "gdb" / "riscv-canonicalize-syscall-gen.c"
>>>> +
>>>> + gdb_syscalls = self._get_gdb_syscalls(gdb_syscalls_path)
>>>> + canon_syscalls_lines = self._get_canon_syscalls_lines(syscalls_path, gdb_syscalls)
>>>> +
>>>> + with open(canon_syscalls_path, "w", encoding="UTF-8") as file:
>>>> + file.writelines(head)
>>>> + file.writelines(canon_syscalls_lines)
>>>> + file.writelines(tail)
>>>> +
>>>> +
>>>> +help_message = """\
>>>> +Generate file gdb/riscv-canonicalize-syscall-gen.c
>>>> +from path to riscv linux syscalls.
>>>> +"""
>>>> +
>>>> +
>>>> +def setup_parser() -> argparse.ArgumentParser:
>>>> + parser = argparse.ArgumentParser(description=help_message)
>>>> + parser.add_argument(
>>>> + "-i",
>>>> + "--input",
>>>> + type=_Path,
>>>> + required=True,
>>>> + help="path to riscv linux syscalls (glibc/sysdeps/unix/sysv/linux/riscv/rv64/arch-syscall.h)",
>>>> + )
>>>> + return parser
>>>> +
>>>> +
>>>> +def main(argv: list[str]) -> int:
>>>> + try:
>>>> + parser = setup_parser()
>>>> + args = parser.parse_args(argv)
>>>> + generator = Generator()
>>>> + generator.generate(args.input)
>>>> + return 0
>>>> + except RuntimeError as e:
>>>> + print(str(e))
>>>> + return -1
>>>> +
>>>> +
>>>> +if __name__ == "__main__":
>>>> + sys.exit(main(sys.argv[1:]))
>>>> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
>>>> index c37cc8974e2..0149125e60a 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*-*-linux*"] } {
>>>> return 1
>>>> }
>>>>
>>>> --
>>>> 2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* RE: [PATCH v8] This commit adds record full support for rv64gc instruction set
2025-04-23 12:13 ` Guinevere Larsen
@ 2025-04-23 14:01 ` Aktemur, Tankut Baris
2025-04-23 14:13 ` Guinevere Larsen
0 siblings, 1 reply; 35+ messages in thread
From: Aktemur, Tankut Baris @ 2025-04-23 14:01 UTC (permalink / raw)
To: Guinevere Larsen, Timur Golubovich; +Cc: gdb-patches, Andrew Burgess
On Wednesday, April 23, 2025 2:14 PM, Guinevere Larsen wrote:
> Hi! Sorry for the delay, I'm applying and fixing merge conflicts, and
> will be pushing this patch soon!
>
> If you plan on continue to develop GDB, you may want to get in contact
> with Tom Tromey to setup write-after-approval access, so you are able to
> push your own patches :)
>
> --
> Cheers,
> Guinevere Larsen
> She/Her/Hers
>
> On 4/22/25 2:36 PM, Timur Golubovich wrote:
> > Hi!
> > Sorry to bother, but It seems like no one else has any questions about
> > this patch.
> > Maybe then it's possible to merge it?
Hello Guinevere, Timur,
With this patch, I started getting
/usr/bin/ld: riscv-linux-tdep.o: in function `riscv_linux_syscall_record(regcache*, unsigned long)':
/user/taktemur/gdb/gdb/riscv-linux-tdep.c:230: undefined reference to `riscv64_canonicalize_syscall(int)'
I had configured GDB with --enable-targets=all.
Thanks
-Baris
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de
Managing Directors: Sean Fennelly, Jeffrey Schneiderman, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v8] This commit adds record full support for rv64gc instruction set
2025-04-23 14:01 ` Aktemur, Tankut Baris
@ 2025-04-23 14:13 ` Guinevere Larsen
2025-04-23 14:39 ` Timur Golubovich
0 siblings, 1 reply; 35+ messages in thread
From: Guinevere Larsen @ 2025-04-23 14:13 UTC (permalink / raw)
To: Aktemur, Tankut Baris, Timur Golubovich; +Cc: gdb-patches, Andrew Burgess
On 4/23/25 11:01 AM, Aktemur, Tankut Baris wrote:
> On Wednesday, April 23, 2025 2:14 PM, Guinevere Larsen wrote:
>> Hi! Sorry for the delay, I'm applying and fixing merge conflicts, and
>> will be pushing this patch soon!
>>
>> If you plan on continue to develop GDB, you may want to get in contact
>> with Tom Tromey to setup write-after-approval access, so you are able to
>> push your own patches :)
>>
>> --
>> Cheers,
>> Guinevere Larsen
>> She/Her/Hers
>>
>> On 4/22/25 2:36 PM, Timur Golubovich wrote:
>>> Hi!
>>> Sorry to bother, but It seems like no one else has any questions about
>>> this patch.
>>> Maybe then it's possible to merge it?
> Hello Guinevere, Timur,
>
> With this patch, I started getting
>
> /usr/bin/ld: riscv-linux-tdep.o: in function `riscv_linux_syscall_record(regcache*, unsigned long)':
> /user/taktemur/gdb/gdb/riscv-linux-tdep.c:230: undefined reference to `riscv64_canonicalize_syscall(int)'
>
> I had configured GDB with --enable-targets=all.
Oh, oops. the riscv-canonicalize-syscall.c file hasn't been added to the
makefile, so all targets fails. I'm fixing this and the other build
issue (a commit from Tromey slightly changing one gdb syscall name) and
pushing the fix asap
--
Cheers,
Guinevere Larsen
She/Her/Hers
^ permalink raw reply [flat|nested] 35+ messages in thread
* Re: [PATCH v8] This commit adds record full support for rv64gc instruction set
2025-04-23 14:13 ` Guinevere Larsen
@ 2025-04-23 14:39 ` Timur Golubovich
0 siblings, 0 replies; 35+ messages in thread
From: Timur Golubovich @ 2025-04-23 14:39 UTC (permalink / raw)
To: Guinevere Larsen; +Cc: Aktemur, Tankut Baris, gdb-patches, Andrew Burgess
Hi!
Thanks for the quick fix. That was a great review, good job.
I'd be happy to continue working on gdb.
>
> On 4/23/25 11:01 AM, Aktemur, Tankut Baris wrote:
> > On Wednesday, April 23, 2025 2:14 PM, Guinevere Larsen wrote:
> >> Hi! Sorry for the delay, I'm applying and fixing merge conflicts, and
> >> will be pushing this patch soon!
> >>
> >> If you plan on continue to develop GDB, you may want to get in contact
> >> with Tom Tromey to setup write-after-approval access, so you are able to
> >> push your own patches :)
> >>
> >> --
> >> Cheers,
> >> Guinevere Larsen
> >> She/Her/Hers
> >>
> >> On 4/22/25 2:36 PM, Timur Golubovich wrote:
> >>> Hi!
> >>> Sorry to bother, but It seems like no one else has any questions about
> >>> this patch.
> >>> Maybe then it's possible to merge it?
> > Hello Guinevere, Timur,
> >
> > With this patch, I started getting
> >
> > /usr/bin/ld: riscv-linux-tdep.o: in function `riscv_linux_syscall_record(regcache*, unsigned long)':
> > /user/taktemur/gdb/gdb/riscv-linux-tdep.c:230: undefined reference to `riscv64_canonicalize_syscall(int)'
> >
> > I had configured GDB with --enable-targets=all.
> Oh, oops. the riscv-canonicalize-syscall.c file hasn't been added to the
> makefile, so all targets fails. I'm fixing this and the other build
> issue (a commit from Tromey slightly changing one gdb syscall name) and
> pushing the fix asap
>
> --
> Cheers,
> Guinevere Larsen
> She/Her/Hers
>
^ permalink raw reply [flat|nested] 35+ messages in thread
* [PING][PATCH] This commit adds record full support for rv64gc instruction set
@ 2025-03-04 11:14 Timur
2025-03-12 0:23 ` Timur
0 siblings, 1 reply; 35+ messages in thread
From: Timur @ 2025-03-04 11:14 UTC (permalink / raw)
To: gdb-patches; +Cc: guinevere, eliz, Timur
It includes changes to the following files:
- gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
syscalls.
- gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
rv64gc instructions.
- gdb/configure.tgt: adds new files for compilation.
- gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
targets.
- gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
function that canonicalizes RISC-V syscall. This script can simplify support
for syscalls on rv32 and rv64 system (currently support only for rv64). To
use this script you need to pass a path to a file with syscalls description
from riscv-glibc (example is in the help message). The script produces a
mapping from syscall names to gdb_syscall enum.
- gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
- gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
- gdb/NEWS: notification of new functionality.
Approved-By: Guinevere Larsen <guinevere@redhat.com>
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Hi!
One more week has passed, polite ping.
---
gdb/NEWS | 2 +
gdb/configure.tgt | 5 +-
gdb/doc/gdb.texinfo | 2 +-
gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
gdb/riscv-linux-tdep.c | 260 +++++++
gdb/riscv-linux-tdep.h | 29 +
gdb/riscv-tdep.c | 710 +++++++++++++++++-
gdb/riscv-tdep.h | 14 +
.../riscv-canonicalize-syscall-gen.py | 148 ++++
gdb/testsuite/lib/gdb.exp | 3 +-
10 files changed, 1506 insertions(+), 9 deletions(-)
create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
create mode 100644 gdb/riscv-linux-tdep.h
create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
diff --git a/gdb/NEWS b/gdb/NEWS
index 964183910dc..20fdb571047 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -9581,3 +9581,5 @@ GDB now handles cross debugging. If you are remotely debugging between
two different machines, type ``./configure host -target=targ''.
Host is the machine where GDB will run; targ is the machine
where the program that you are debugging will run.
+
+ * Add record full support for rv64gc architectures
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 62df71b13fa..0cd5c509695 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -545,8 +545,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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 99720f1206e..d5b989d681e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7759,7 +7759,7 @@ 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,
-Moxie, PowerPC, PowerPC64, S/390, and x86 (i386/amd64) running
+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}.
diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
new file mode 100644
index 00000000000..a33f7d88b6a
--- /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 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 ff478cf4c28..d08302d8f34 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,258 @@ 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. */
+/* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+
+static void
+riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
+ struct linux_record_tdep &
+ riscv_linux_record_tdep)
+{
+ gdb_assert (gdbarch != nullptr);
+
+ /* Initialize the riscv_linux_record_tdep. */
+ /* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+ 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;
+ riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
+}
+
/* Initialize RISC-V Linux ABI info. */
static void
@@ -205,6 +462,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 00000000000..8e48ab8e1fd
--- /dev/null
+++ b/gdb/riscv-linux-tdep.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2024 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 932708ca4e9..c171fb05b3b 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
@@ -1655,6 +1658,11 @@ class riscv_insn
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. */
@@ -1800,11 +1808,6 @@ class riscv_insn
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;
@@ -4415,6 +4418,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);
@@ -4833,3 +4839,697 @@ this option can be used."),
&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<ULONGEST> (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:
+ using regnum_type = int;
+ using memory_type = std::pair<CORE_ADDR, int>;
+
+ enum class record_type
+ {
+ UNKNOWN,
+ ORDINARY,
+
+ /* Corner cases. */
+ ECALL,
+ EBREAK,
+ };
+
+private:
+ using recorded_regs = std::vector<regnum_type>;
+ using recorded_mems = std::vector<memory_type>;
+
+ using mem_addr = decltype (std::declval<memory_type> ().first);
+ using mem_len = decltype (std::declval<memory_type> ().second);
+
+ record_type m_record_type = record_type::UNKNOWN;
+
+ bool m_error_occured = false;
+
+ recorded_regs m_regs;
+ recorded_mems m_mems;
+
+ int m_xlen = int{};
+
+ /* 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. Sets error and returns false if error happened. */
+ bool
+ save_reg (regnum_type regnum) noexcept
+ {
+ try
+ {
+ m_regs.emplace_back (regnum);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ /* Save memory chunk. Sets error and returns false if error happened. */
+ bool
+ save_mem (mem_addr addr, mem_len len) noexcept
+ {
+ try
+ {
+ m_mems.emplace_back (addr, len);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ /* 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. Needs error checking after. */
+ 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. Needs error checking after. */
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc and
+ memory chunk of appropriate length. */
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc, rd and
+ memory chunk of appropriate length. */
+ static mem_len
+ need_save_pc_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_sc_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_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. Needs error checking after. */
+ 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 = mem_addr{};
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc, rs2, rd and
+ memory chunk of appropriate length. */
+ static mem_len
+ need_save_pc_rs2_rd_mem (ULONGEST ival) noexcept
+ {
+ if (is_amoswap_w_insn (ival))
+ return 4;
+ if (is_amoswap_d_insn (ival))
+ return 8;
+ return 0;
+ }
+
+ /* Returns true if instruction is classified. Needs error checking after. */
+ bool
+ try_save_pc_rs2_rd_mem (ULONGEST ival, struct regcache *regcache) noexcept
+ {
+ gdb_assert (regcache != nullptr);
+
+ mem_addr addr = mem_addr{};
+ mem_len len = need_save_pc_rs2_rd_mem (ival);
+ if (len <= 0)
+ return false;
+
+ return !read_reg (regcache, decode_rs1 (ival), addr)
+ || !save_mem (addr, len) || !save_reg (decode_rs2 (ival))
+ || !save_reg (decode_rd (ival)) || set_ordinary_record_type ();
+ }
+
+ /* Returns true if instruction is successfully recorder. */
+ 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)
+ || try_save_pc_rs2_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 recorder.
+ Else returns false. */
+ 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:
+ using regs_iter = recorded_regs::const_iterator;
+ 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 = int{};
+ 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_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
+ const riscv_recorded_insn &insn)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto regs_begin = insn.regs_begin ();
+ auto regs_end = insn.regs_end ();
+ if (std::any_of (regs_begin,
+ regs_end,
+ [®cache] (auto &®_it)
+ {
+ return record_full_arch_list_add_reg (regcache, reg_it);
+ }))
+ return -1;
+
+ auto mems_begin = insn.mems_begin ();
+ auto mems_end = insn.mems_end ();
+ if (std::any_of (mems_begin,
+ mems_end,
+ [] (auto &&mem_it)
+ {
+ return record_full_arch_list_add_mem (mem_it.first,
+ mem_it.second);
+ }))
+ return -1;
+
+ switch (insn.get_record_type ())
+ {
+ case riscv_recorded_insn::record_type::ORDINARY:
+ break;
+
+ case riscv_recorded_insn::record_type::ECALL:
+ {
+ if (!tdep->riscv_syscall_record)
+ {
+ warning (_ ("Syscall record is not supported"));
+ return -1;
+ }
+ ULONGEST reg_val = ULONGEST{};
+ if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
+ return -1;
+ return tdep->riscv_syscall_record (regcache, reg_val);
+ }
+
+ case riscv_recorded_insn::record_type::EBREAK:
+ break;
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* Parse the current instruction and record the values of the registers and
+ memory that will be changed in current instruction to record_arch_list.
+ Return -1 if something is wrong. */
+
+int
+riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+ CORE_ADDR addr)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_recorded_insn insn;
+ if (!insn.record (gdbarch, regcache, addr))
+ {
+ record_full_arch_list_add_end ();
+ return -1;
+ }
+
+ int ret_val = riscv_make_record_process (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 4bdc2e7a3d5..52b2306c35d 100644
--- a/gdb/riscv-tdep.h
+++ b/gdb/riscv-tdep.h
@@ -35,6 +35,10 @@ enum
RISCV_FP_REGNUM = 8, /* Frame Pointer. */
RISCV_A0_REGNUM = 10, /* First argument. */
RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
RISCV_PC_REGNUM = 32, /* Program Counter. */
@@ -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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
new file mode 100755
index 00000000000..e553d380924
--- /dev/null
+++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+# pylint: disable=invalid-name
+
+# Copyright (C) 2024 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/>.
+
+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 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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 410f99e3350..b048ac7078b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
- || [istarget "s390*-*-linux*"] } {
+ || [istarget "s390*-*-linux*"]
+ || [istarget "riscv*-*-*"] } {
return 1
}
--
2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread* [PING][PATCH] This commit adds record full support for rv64gc instruction set
2025-03-04 11:14 [PING][PATCH] " Timur
@ 2025-03-12 0:23 ` Timur
0 siblings, 0 replies; 35+ messages in thread
From: Timur @ 2025-03-12 0:23 UTC (permalink / raw)
To: gdb-patches; +Cc: guinevere, eliz, Timur
It includes changes to the following files:
- gdb/riscv-linux-tdep.c, gdb/riscv-linux-tdep.h: adds facilities to record
syscalls.
- gdb/riscv-tdep.c, gdb/riscv-tdep.h: adds facilities to record execution of
rv64gc instructions.
- gdb/configure.tgt: adds new files for compilation.
- gdb/testsuite/lib/gdb.exp: enables testing of full record mode for RISC-V
targets.
- gdb/syscalls/riscv-canonicalize-syscall-gen.py: a script to generate
function that canonicalizes RISC-V syscall. This script can simplify support
for syscalls on rv32 and rv64 system (currently support only for rv64). To
use this script you need to pass a path to a file with syscalls description
from riscv-glibc (example is in the help message). The script produces a
mapping from syscall names to gdb_syscall enum.
- gdb/riscv-canonicalize-syscall.c: the file generated by the previous script.
- gdb/doc/gdb.texinfo: notification that record mode is enabled in RISC-V.
- gdb/NEWS: notification of new functionality.
Approved-By: Guinevere Larsen <guinevere@redhat.com>
Hi!
It has passes one more week, so I am pinging. In total a month has already
passed, so someone please take a look at this patch or if no one has
questions, I would prefer it to merge.
---
gdb/NEWS | 2 +
gdb/configure.tgt | 5 +-
gdb/doc/gdb.texinfo | 2 +-
gdb/riscv-canonicalize-syscall-gen.c | 342 +++++++++
gdb/riscv-linux-tdep.c | 260 +++++++
gdb/riscv-linux-tdep.h | 29 +
gdb/riscv-tdep.c | 681 +++++++++++++++++-
gdb/riscv-tdep.h | 14 +
.../riscv-canonicalize-syscall-gen.py | 148 ++++
gdb/testsuite/lib/gdb.exp | 3 +-
10 files changed, 1477 insertions(+), 9 deletions(-)
create mode 100644 gdb/riscv-canonicalize-syscall-gen.c
create mode 100644 gdb/riscv-linux-tdep.h
create mode 100755 gdb/syscalls/riscv-canonicalize-syscall-gen.py
diff --git a/gdb/NEWS b/gdb/NEWS
index 964183910dc..20fdb571047 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -9581,3 +9581,5 @@ GDB now handles cross debugging. If you are remotely debugging between
two different machines, type ``./configure host -target=targ''.
Host is the machine where GDB will run; targ is the machine
where the program that you are debugging will run.
+
+ * Add record full support for rv64gc architectures
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index 62df71b13fa..0cd5c509695 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -545,8 +545,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/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 99720f1206e..d5b989d681e 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7759,7 +7759,7 @@ 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,
-Moxie, PowerPC, PowerPC64, S/390, and x86 (i386/amd64) running
+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}.
diff --git a/gdb/riscv-canonicalize-syscall-gen.c b/gdb/riscv-canonicalize-syscall-gen.c
new file mode 100644
index 00000000000..a33f7d88b6a
--- /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 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 ff478cf4c28..d08302d8f34 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,258 @@ 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. */
+/* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+
+static void
+riscv64_linux_record_tdep_init (struct gdbarch *gdbarch,
+ struct linux_record_tdep &
+ riscv_linux_record_tdep)
+{
+ gdb_assert (gdbarch != nullptr);
+
+ /* Initialize the riscv_linux_record_tdep. */
+ /* These values are the size of the type that will be used in a system
+ call. They are obtained from Linux Kernel source. */
+ 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;
+ riscv_linux_record_tdep.arg7 = RISCV_A7_REGNUM;
+}
+
/* Initialize RISC-V Linux ABI info. */
static void
@@ -205,6 +462,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 00000000000..8e48ab8e1fd
--- /dev/null
+++ b/gdb/riscv-linux-tdep.h
@@ -0,0 +1,29 @@
+/* Copyright (C) 2024 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 932708ca4e9..656af38c384 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
@@ -1655,6 +1658,11 @@ class riscv_insn
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. */
@@ -1800,11 +1808,6 @@ class riscv_insn
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;
@@ -4415,6 +4418,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);
@@ -4833,3 +4839,668 @@ this option can be used."),
&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<ULONGEST> (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:
+ using regnum_type = int;
+ using memory_type = std::pair<CORE_ADDR, int>;
+
+ enum class record_type
+ {
+ UNKNOWN,
+ ORDINARY,
+
+ /* Corner cases. */
+ ECALL,
+ EBREAK,
+ };
+
+private:
+ using recorded_regs = std::vector<regnum_type>;
+ using recorded_mems = std::vector<memory_type>;
+
+ using mem_addr = decltype (std::declval<memory_type> ().first);
+ using mem_len = decltype (std::declval<memory_type> ().second);
+
+ record_type m_record_type = record_type::UNKNOWN;
+
+ bool m_error_occured = false;
+
+ recorded_regs m_regs;
+ recorded_mems m_mems;
+
+ int m_xlen = int{};
+
+ /* 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. Sets error and returns false if error happened. */
+ bool
+ save_reg (regnum_type regnum) noexcept
+ {
+ try
+ {
+ m_regs.emplace_back (regnum);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ /* Save memory chunk. Sets error and returns false if error happened. */
+ bool
+ save_mem (mem_addr addr, mem_len len) noexcept
+ {
+ try
+ {
+ m_mems.emplace_back (addr, len);
+ return true;
+ }
+ catch (const std::bad_alloc &ex)
+ {
+ warning (_ ("Exception was caught during reverse execution: %s"),
+ ex.what ());
+ return set_error ();
+ }
+ }
+
+ /* 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. Needs error checking after. */
+ 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. Needs error checking after. */
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc and
+ memory chunk of appropriate length. */
+ 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. Needs error checking after. */
+ 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 ();
+ }
+
+ /* If return value is > 0, instruction needs only saving pc, rd and
+ memory chunk of appropriate length. */
+ 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. Needs error checking after. */
+ 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 = mem_addr{};
+ 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 recorder. */
+ 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 recorder.
+ Else returns false. */
+ 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:
+ using regs_iter = recorded_regs::const_iterator;
+ 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 = int{};
+ 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_make_record_process (struct gdbarch *gdbarch, struct regcache *regcache,
+ const riscv_recorded_insn &insn)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_gdbarch_tdep *tdep = gdbarch_tdep<riscv_gdbarch_tdep> (gdbarch);
+ auto regs_begin = insn.regs_begin ();
+ auto regs_end = insn.regs_end ();
+ if (std::any_of (regs_begin,
+ regs_end,
+ [®cache] (auto &®_it)
+ {
+ return record_full_arch_list_add_reg (regcache, reg_it);
+ }))
+ return -1;
+
+ auto mems_begin = insn.mems_begin ();
+ auto mems_end = insn.mems_end ();
+ if (std::any_of (mems_begin,
+ mems_end,
+ [] (auto &&mem_it)
+ {
+ return record_full_arch_list_add_mem (mem_it.first,
+ mem_it.second);
+ }))
+ return -1;
+
+ switch (insn.get_record_type ())
+ {
+ case riscv_recorded_insn::record_type::ORDINARY:
+ break;
+
+ case riscv_recorded_insn::record_type::ECALL:
+ {
+ if (!tdep->riscv_syscall_record)
+ {
+ warning (_ ("Syscall record is not supported"));
+ return -1;
+ }
+ ULONGEST reg_val = ULONGEST{};
+ if (!try_read (regcache, RISCV_A7_REGNUM, reg_val))
+ return -1;
+ return tdep->riscv_syscall_record (regcache, reg_val);
+ }
+
+ case riscv_recorded_insn::record_type::EBREAK:
+ break;
+
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* Parse the current instruction and record the values of the registers and
+ memory that will be changed in current instruction to record_arch_list.
+ Return -1 if something is wrong. */
+
+int
+riscv_process_record (struct gdbarch *gdbarch, struct regcache *regcache,
+ CORE_ADDR addr)
+{
+ gdb_assert (gdbarch != nullptr);
+ gdb_assert (regcache != nullptr);
+
+ riscv_recorded_insn insn;
+ if (!insn.record (gdbarch, regcache, addr))
+ {
+ record_full_arch_list_add_end ();
+ return -1;
+ }
+
+ int ret_val = riscv_make_record_process (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 4bdc2e7a3d5..52b2306c35d 100644
--- a/gdb/riscv-tdep.h
+++ b/gdb/riscv-tdep.h
@@ -35,6 +35,10 @@ enum
RISCV_FP_REGNUM = 8, /* Frame Pointer. */
RISCV_A0_REGNUM = 10, /* First argument. */
RISCV_A1_REGNUM = 11, /* Second 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, /* Seventh argument. */
RISCV_PC_REGNUM = 32, /* Program Counter. */
@@ -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/syscalls/riscv-canonicalize-syscall-gen.py b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
new file mode 100755
index 00000000000..e553d380924
--- /dev/null
+++ b/gdb/syscalls/riscv-canonicalize-syscall-gen.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+# pylint: disable=invalid-name
+
+# Copyright (C) 2024 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/>.
+
+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 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/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 410f99e3350..b048ac7078b 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -3718,7 +3718,8 @@ proc supports_reverse {} {
|| [istarget "i\[34567\]86-*-linux*"]
|| [istarget "aarch64*-*-linux*"]
|| [istarget "powerpc*-*-linux*"]
- || [istarget "s390*-*-linux*"] } {
+ || [istarget "s390*-*-linux*"]
+ || [istarget "riscv*-*-*"] } {
return 1
}
--
2.34.1
^ permalink raw reply [flat|nested] 35+ messages in thread
end of thread, other threads:[~2025-04-23 14:40 UTC | newest]
Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-11-15 22:26 [PATCH] gdb/riscv: Add record support for rv64gc instructions Timur
2024-11-18 19:46 ` Guinevere Larsen
2024-11-26 14:17 ` [PATCH v2] " Timur
2024-11-27 14:23 ` Guinevere Larsen
2024-12-03 18:30 ` [PATCH v3] " Timur
2024-12-13 18:10 ` Guinevere Larsen
2024-12-17 0:10 ` [PATCH v4] This commit adds record full support for rv64gc instruction set Timur
2024-12-17 14:23 ` Guinevere Larsen
2024-12-23 15:12 ` [PATCH v5] " Timur
2024-12-23 18:55 ` Guinevere Larsen
2024-12-31 16:09 ` [PATCH v6] " Timur
2025-01-10 18:12 ` Guinevere Larsen
2025-01-16 15:48 ` [PATCH v7] " Timur
2025-02-10 18:07 ` [PING][PATCH v8] " Timur
2025-02-10 19:30 ` Eli Zaretskii
2025-02-11 22:06 ` [PATCH] " Timur
2025-02-26 11:14 ` [PATCH][PING] " Timur
2025-03-13 10:38 ` Andrew Burgess
2025-03-13 15:03 ` [PATCH] " Andrew Burgess
2025-03-18 18:49 ` [PATCH v8] " Timur
2025-03-31 17:50 ` [PING][PATCH] " Timur
2025-04-08 12:33 ` Timur Golubovich
2025-04-10 9:16 ` Andrew Burgess
2025-04-10 9:16 ` [PATCH v8] " Andrew Burgess
2025-04-10 11:55 ` Timur
2025-04-14 7:40 ` Andrew Burgess
2025-04-14 8:44 ` Timur Golubovich
2025-04-14 12:03 ` Guinevere Larsen
2025-04-22 17:36 ` Timur Golubovich
2025-04-23 12:13 ` Guinevere Larsen
2025-04-23 14:01 ` Aktemur, Tankut Baris
2025-04-23 14:13 ` Guinevere Larsen
2025-04-23 14:39 ` Timur Golubovich
2025-03-04 11:14 [PING][PATCH] " Timur
2025-03-12 0:23 ` Timur
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox