diff options
author | Michael Snyder <msnyder@vmware.com> | 2008-12-26 21:09:13 +0000 |
---|---|---|
committer | Michael Snyder <msnyder@vmware.com> | 2008-12-26 21:09:13 +0000 |
commit | 188e9314e3536b0ab0b2d5a78baffc7b7dfccd56 (patch) | |
tree | 8395e9ef763ecf087387a5d188f4425c479f1ace | |
parent | 6f581415c153fc050f7c12ba17016825506a719e (diff) | |
download | gdb-188e9314e3536b0ab0b2d5a78baffc7b7dfccd56.zip gdb-188e9314e3536b0ab0b2d5a78baffc7b7dfccd56.tar.gz gdb-188e9314e3536b0ab0b2d5a78baffc7b7dfccd56.tar.bz2 |
2008-12-26 Michael Snyder <msnyder@vmware.com>
* Marker: adding teawater patches to branch.
-rw-r--r-- | gdb/ChangeLog | 4 | ||||
-rw-r--r-- | gdb/Makefile.in | 8 | ||||
-rw-r--r-- | gdb/configure.tgt | 6 | ||||
-rw-r--r-- | gdb/doc/gdb.texinfo | 107 | ||||
-rw-r--r-- | gdb/gdbarch.c | 66 | ||||
-rw-r--r-- | gdb/gdbarch.h | 14 | ||||
-rwxr-xr-x | gdb/gdbarch.sh | 4 | ||||
-rw-r--r-- | gdb/i386-linux-tdep.c | 318 | ||||
-rw-r--r-- | gdb/i386-tdep.c | 2964 | ||||
-rw-r--r-- | gdb/i386-tdep.h | 3 | ||||
-rw-r--r-- | gdb/infrun.c | 11 | ||||
-rw-r--r-- | gdb/linux-record.c | 2507 | ||||
-rw-r--r-- | gdb/linux-record.h | 171 | ||||
-rw-r--r-- | gdb/record.c | 1272 | ||||
-rw-r--r-- | gdb/record.h | 98 | ||||
-rw-r--r-- | gdb/target.c | 36 | ||||
-rw-r--r-- | gdb/target.h | 3 | ||||
-rwxr-xr-x | gdb/testsuite/configure | 3 | ||||
-rw-r--r-- | gdb/testsuite/configure.ac | 2 | ||||
-rw-r--r-- | gdb/testsuite/gdb.twreverse/machinestate.exp | 101 |
20 files changed, 7679 insertions, 19 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index c78678b..39ecb43 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,7 @@ +2008-12-26 Michael Snyder <msnyder@vmware.com> + + * Marker: adding teawater patches to branch. + 2008-12-26 Sandra Loosemore <sandra@codesourcery.com> * breakpoint.c (update_watchpoint): Refactor to avoid compiler diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 47b3be0..0935ffe 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -515,7 +515,7 @@ ALL_TARGET_OBS = \ xcoffread.o \ prologue-value.o \ symfile-mem.o \ - corelow.o + corelow.o linux-record.o # Host-dependent makefile fragment comes in here. @host_makefile_frag@ @@ -657,7 +657,7 @@ SFILES = ada-exp.y ada-lang.c ada-typeprint.c ada-valprint.c ada-tasks.c \ valarith.c valops.c valprint.c value.c varobj.c vec.c \ wrapper.c \ xml-tdesc.c xml-support.c \ - inferior.c + inferior.c record.c LINTFILES = $(SFILES) $(YYFILES) $(CONFIG_SRCS) init.c @@ -808,7 +808,7 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ solib.o solib-null.o \ prologue-value.o memory-map.o xml-support.o \ target-descriptions.o target-memory.o xml-tdesc.o xml-builtin.o \ - inferior.o osdata.o + inferior.o osdata.o record.o TSOBS = inflow.o @@ -1297,7 +1297,7 @@ ALLDEPFILES = \ inf-ptrace.c inf-ttrace.c \ irix5-nat.c \ libunwind-frame.c \ - linux-fork.c \ + linux-fork.c linux-record.c \ m68hc11-tdep.c \ m32r-tdep.c \ m32r-linux-nat.c m32r-linux-tdep.c \ diff --git a/gdb/configure.tgt b/gdb/configure.tgt index b9cd21b..d81cded 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -190,7 +190,8 @@ i[34567]86-*-solaris*) i[34567]86-*-linux*) # Target: Intel 386 running GNU/Linux gdb_target_obs="i386-tdep.o i386-linux-tdep.o glibc-tdep.o i387-tdep.o \ - solib.o solib-svr4.o symfile-mem.o corelow.o" + solib.o solib-svr4.o symfile-mem.o corelow.o \ + linux-record.o" build_gdbserver=yes ;; i[34567]86-*-gnu*) @@ -513,7 +514,8 @@ x86_64-*-linux*) # Target: GNU/Linux x86-64 gdb_target_obs="amd64-tdep.o amd64-linux-tdep.o i386-tdep.o \ i387-tdep.o i386-linux-tdep.o glibc-tdep.o \ - solib.o solib-svr4.o corelow.o symfile-mem.o" + solib.o solib-svr4.o corelow.o symfile-mem.o \ + linux-record.o" build_gdbserver=yes ;; x86_64-*-freebsd* | x86_64-*-kfreebsd*-gnu) diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index 6ef124c..c758952 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -144,6 +144,7 @@ software in general. We will miss him. * Running:: Running programs under @value{GDBN} * Stopping:: Stopping and continuing * Reverse Execution:: Running programs backward +* Process record and replay:: Recording inferior's execution and replaying it * Stack:: Examining the stack * Source:: Examining source files * Data:: Examining data @@ -4964,6 +4965,112 @@ This is the default. @end table +@node Process record and replay +@chapter Recording inferior's execution and replaying it +@cindex process record and replay +@cindex recording inferior's execution and replaying it + +In a architecture environment that supports process record and replay, +process record and replay target can record a log of the process execution, +and replay it with both forward and reverse execute commands. + +When this target is in use, if the execution log includes the record for +the next instruction, @value{GDBN} will debug in replay mode. So inferior +will not really execute and all the execution events are taken from the +execution log. Just the values of registers (include pc register) and +memory of the inferior will be changed. + +Otherwise, @value{GDBN} will debug in record mode. So inferior will +execute normally and @value{GDBN} will record the execution log. + +If you are debugging in a architecture environment that supports +process record and replay, @value{GDBN} provides the following commands. + +@table @code +@kindex target record +@kindex record +@kindex rec +@item target record +This a standard command to start process record and replay target. +Process record and replay target can only debug a process that already +running. Therefore you need to first start the process @code{run}, +and then start the recording @code{target record}. + +Both @code{record} and @code{rec} are the aliases of @code{target record}. + +Displaced stepping function will disable when process record and replay +target is opened. Because process record and replay target doesn't +support displaced stepping function. + +If inferior in non-stop mode (non-stop) or in asynchronous mode +(target-async), process record and replay target can't be open because +it doesn't support these two modes. + +@kindex stoprecord +@kindex sr +@item stoprecord +Stop process record and replay target at once. When Process record and +replay target stops, all the execution log will be deleted and the inferior +will either be terminated, or remain in its final state. + +When you stop the process record and replay target in record mode (at the +end of the execution log), the inferior will be stopped at the next +instruction that would have been recorded. In other words, if you record +for a while and then stop recording, the inferior process will be left in +the same state as if recording never happened. + +On the other hand, if the process record and replay target is stopped while +in replay mode (that is, not at the end of the execution log but at some +earlier point), the inferior process will become ``live'' at that earlier state, +and it will then be possible to continue debugging the process ``live'' from +that state. + +When the inferior process exits, or @value{GDBN} detaches from it, process +record and replay target will automatically stop itself. + +@kindex set record-insn-number-max +@item set record-insn-number-max @var{limit} +Set the limit of instructions to be recorded. Default value is 200000. + +In this case, if record instructions number is bigger than @var{limit}, +@value{GDBN} will auto delete the earliest recorded instruction execute +log. + +If set to 0, @value{GDBN} will not delete the earliest recorded instruction +execute log. Record instructions number limit function will disable. + +@kindex show record-insn-number-max +@item show record-insn-number-max +Show the value of recorded instructions limit. + +@kindex set record-stop-at-limit +@item set record-stop-at-limit on +Set the behavior when record instructions limit is reached. +This is the default mode. Meaning that @value{GDBN} will stop ask user +want close @code{record-stop-at-limit} or stop inferior. + +@item set record-stop-at-limit off +This mean that @value{GDBN} will auto delete the oldest record to make +room for each new one. + +@kindex show record-stop-at-limit +@item show record-stop-at-limit +Show the value of record-stop-at-limit. + +@kindex info record-insn-number +@item info record-insn-number +Show the current number of recorded instructions. + +@kindex delrecord +@kindex dr +@item delrecord +When record target running in replay mode (``in the past''), delete the +subsequent execution log and begin to record a new execution log starting +from the current address. It means you will abandon the previously +recorded ``future'' and begin recording a new ``future''. +@end table + + @node Stack @chapter Examining the Stack diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index dd6ad7f..de07768 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -237,6 +237,8 @@ struct gdbarch gdbarch_core_read_description_ftype *core_read_description; gdbarch_static_transform_name_ftype *static_transform_name; int sofun_address_maybe_missing; + gdbarch_process_record_ftype *process_record; + gdbarch_process_record_dasm_ftype *process_record_dasm; gdbarch_target_signal_from_host_ftype *target_signal_from_host; gdbarch_target_signal_to_host_ftype *target_signal_to_host; gdbarch_record_special_symbol_ftype *record_special_symbol; @@ -369,6 +371,8 @@ struct gdbarch startup_gdbarch = 0, /* core_read_description */ 0, /* static_transform_name */ 0, /* sofun_address_maybe_missing */ + 0, /* process_record */ + 0, /* process_record_dasm */ default_target_signal_from_host, /* target_signal_from_host */ default_target_signal_to_host, /* target_signal_to_host */ 0, /* record_special_symbol */ @@ -622,6 +626,8 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of core_read_description, has predicate */ /* Skip verify of static_transform_name, has predicate */ /* Skip verify of sofun_address_maybe_missing, invalid_p == 0 */ + /* Skip verify of process_record, has predicate */ + /* Skip verify of process_record_dasm, has predicate */ /* Skip verify of target_signal_from_host, invalid_p == 0 */ /* Skip verify of target_signal_to_host, invalid_p == 0 */ /* Skip verify of record_special_symbol, has predicate */ @@ -922,6 +928,18 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) "gdbarch_dump: print_vector_info = <0x%lx>\n", (long) gdbarch->print_vector_info); fprintf_unfiltered (file, + "gdbarch_dump: gdbarch_process_record_p() = %d\n", + gdbarch_process_record_p (gdbarch)); + fprintf_unfiltered (file, + "gdbarch_dump: process_record = <0x%lx>\n", + (long) gdbarch->process_record); + fprintf_unfiltered (file, + "gdbarch_dump: gdbarch_process_record_dasm_p() = %d\n", + gdbarch_process_record_dasm_p (gdbarch)); + fprintf_unfiltered (file, + "gdbarch_dump: process_record_dasm = <0x%lx>\n", + (long) gdbarch->process_record_dasm); + fprintf_unfiltered (file, "gdbarch_dump: ps_regnum = %s\n", plongest (gdbarch->ps_regnum)); fprintf_unfiltered (file, @@ -3185,6 +3203,54 @@ set_gdbarch_sofun_address_maybe_missing (struct gdbarch *gdbarch, gdbarch->sofun_address_maybe_missing = sofun_address_maybe_missing; } +int +gdbarch_process_record_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->process_record != NULL; +} + +int +gdbarch_process_record (struct gdbarch *gdbarch, CORE_ADDR addr) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->process_record != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_process_record called\n"); + return gdbarch->process_record (gdbarch, addr); +} + +void +set_gdbarch_process_record (struct gdbarch *gdbarch, + gdbarch_process_record_ftype process_record) +{ + gdbarch->process_record = process_record; +} + +int +gdbarch_process_record_dasm_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->process_record_dasm != NULL; +} + +void +gdbarch_process_record_dasm (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->process_record_dasm != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_process_record_dasm called\n"); + gdbarch->process_record_dasm (gdbarch); +} + +void +set_gdbarch_process_record_dasm (struct gdbarch *gdbarch, + gdbarch_process_record_dasm_ftype process_record_dasm) +{ + gdbarch->process_record_dasm = process_record_dasm; +} + enum target_signal gdbarch_target_signal_from_host (struct gdbarch *gdbarch, int signo) { diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index 35f8a36..19b1b1f 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -789,6 +789,20 @@ extern void set_gdbarch_static_transform_name (struct gdbarch *gdbarch, gdbarch_ extern int gdbarch_sofun_address_maybe_missing (struct gdbarch *gdbarch); extern void set_gdbarch_sofun_address_maybe_missing (struct gdbarch *gdbarch, int sofun_address_maybe_missing); +/* For the process record and replay target */ + +extern int gdbarch_process_record_p (struct gdbarch *gdbarch); + +typedef int (gdbarch_process_record_ftype) (struct gdbarch *gdbarch, CORE_ADDR addr); +extern int gdbarch_process_record (struct gdbarch *gdbarch, CORE_ADDR addr); +extern void set_gdbarch_process_record (struct gdbarch *gdbarch, gdbarch_process_record_ftype *process_record); + +extern int gdbarch_process_record_dasm_p (struct gdbarch *gdbarch); + +typedef void (gdbarch_process_record_dasm_ftype) (struct gdbarch *gdbarch); +extern void gdbarch_process_record_dasm (struct gdbarch *gdbarch); +extern void set_gdbarch_process_record_dasm (struct gdbarch *gdbarch, gdbarch_process_record_dasm_ftype *process_record_dasm); + /* Signal translation: translate inferior's signal (host's) number into GDB's representation. */ diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index 79ca862..8a827f4 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -698,6 +698,10 @@ F:char *:static_transform_name:char *name:name # Set if the address in N_SO or N_FUN stabs may be zero. v:int:sofun_address_maybe_missing:::0:0::0 +# For the process record and replay target +M:int:process_record:CORE_ADDR addr:addr +M:void:process_record_dasm:void + # Signal translation: translate inferior's signal (host's) number into # GDB's representation. m:enum target_signal:target_signal_from_host:int signo:signo::default_target_signal_from_host::0 diff --git a/gdb/i386-linux-tdep.c b/gdb/i386-linux-tdep.c index 5284f4a..6b8bf9b 100644 --- a/gdb/i386-linux-tdep.c +++ b/gdb/i386-linux-tdep.c @@ -37,6 +37,10 @@ #include "arch-utils.h" #include "regset.h" +#include "record.h" +#include "linux-record.h" +#include <stdint.h> + /* Supported register note sections. */ static struct core_regset_section i386_linux_regset_sections[] = { @@ -346,6 +350,37 @@ i386_linux_write_pc (struct regcache *regcache, CORE_ADDR pc) restarted. */ regcache_cooked_write_unsigned (regcache, I386_LINUX_ORIG_EAX_REGNUM, -1); } + +/* Parse the arguments of current system call instruction and record the + values of the registers and memory that will be changed in current system + call instruction to "record_arch_list". This instruction is "int 0x80" (Linux + Kernel2.4) or "sysenter" (Linux Kernel 2.6). + Return -1 if something wrong. */ + +static linux_record_tdep_t linux_record_tdep; + +static int +i386_linux_intx80_sysenter_record (void) +{ + int ret; + uint32_t tmpu32; + + regcache_raw_read (record_regcache, I386_EAX_REGNUM, (gdb_byte *) & tmpu32); + + ret = record_linux_system_call (tmpu32, &linux_record_tdep); + if (ret) + { + return ret; + } + + /* Record the return of system call. */ + if (record_arch_list_add_reg (I386_EAX_REGNUM)) + { + return -1; + } + + return 0; +} /* The register sets used in GNU/Linux ELF core-dumps are identical to @@ -413,6 +448,145 @@ static int i386_linux_sc_reg_offset[] = 0 * 4 /* %gs */ }; +/* These macros are the size of the type that will be used in system + call. The values of these macros are gotten from Linux Kernel + source. */ +#define I386_RECORD_SIZE__old_kernel_stat 32 +#define I386_RECORD_SIZE_tms 16 +#define I386_RECORD_SIZE_loff_t 8 +#define I386_RECORD_SIZE_flock 16 +#define I386_RECORD_SIZE_oldold_utsname 45 +#define I386_RECORD_SIZE_ustat 20 +#define I386_RECORD_SIZE_old_sigaction 140 +#define I386_RECORD_SIZE_old_sigset_t 128 +#define I386_RECORD_SIZE_rlimit 8 +#define I386_RECORD_SIZE_rusage 72 +#define I386_RECORD_SIZE_timeval 8 +#define I386_RECORD_SIZE_timezone 8 +#define I386_RECORD_SIZE_old_gid_t 2 +#define I386_RECORD_SIZE_old_uid_t 2 +#define I386_RECORD_SIZE_fd_set 128 +#define I386_RECORD_SIZE_dirent 268 +#define I386_RECORD_SIZE_dirent64 276 +#define I386_RECORD_SIZE_statfs 64 +#define I386_RECORD_SIZE_statfs64 84 +#define I386_RECORD_SIZE_sockaddr 16 +#define I386_RECORD_SIZE_int 4 +#define I386_RECORD_SIZE_long 4 +#define I386_RECORD_SIZE_ulong 4 +#define I386_RECORD_SIZE_msghdr 28 +#define I386_RECORD_SIZE_itimerval 16 +#define I386_RECORD_SIZE_stat 88 +#define I386_RECORD_SIZE_old_utsname 325 +#define I386_RECORD_SIZE_sysinfo 64 +#define I386_RECORD_SIZE_msqid_ds 88 +#define I386_RECORD_SIZE_shmid_ds 84 +#define I386_RECORD_SIZE_new_utsname 390 +#define I386_RECORD_SIZE_timex 128 +#define I386_RECORD_SIZE_mem_dqinfo 24 +#define I386_RECORD_SIZE_if_dqblk 68 +#define I386_RECORD_SIZE_fs_quota_stat 68 +#define I386_RECORD_SIZE_timespec 8 +#define I386_RECORD_SIZE_pollfd 8 +#define I386_RECORD_SIZE_NFS_FHSIZE 32 +#define I386_RECORD_SIZE_knfsd_fh 132 +#define I386_RECORD_SIZE_TASK_COMM_LEN 16 +#define I386_RECORD_SIZE_sigaction 140 +#define I386_RECORD_SIZE_sigset_t 8 +#define I386_RECORD_SIZE_siginfo_t 128 +#define I386_RECORD_SIZE_cap_user_data_t 12 +#define I386_RECORD_SIZE_stack_t 12 +#define I386_RECORD_SIZE_off_t I386_RECORD_SIZE_long +#define I386_RECORD_SIZE_stat64 96 +#define I386_RECORD_SIZE_gid_t 2 +#define I386_RECORD_SIZE_uid_t 2 +#define I386_RECORD_SIZE_PAGE_SIZE 4096 +#define I386_RECORD_SIZE_flock64 24 +#define I386_RECORD_SIZE_user_desc 16 +#define I386_RECORD_SIZE_io_event 32 +#define I386_RECORD_SIZE_iocb 64 +#define I386_RECORD_SIZE_epoll_event 12 +#define I386_RECORD_SIZE_itimerspec (I386_RECORD_SIZE_timespec * 2) +#define I386_RECORD_SIZE_mq_attr 32 +#define I386_RECORD_SIZE_siginfo 128 +#define I386_RECORD_SIZE_termios 36 +#define I386_RECORD_SIZE_termios2 44 +#define I386_RECORD_SIZE_pid_t 4 +#define I386_RECORD_SIZE_winsize 8 +#define I386_RECORD_SIZE_char 8 +#define I386_RECORD_SIZE_serial_struct 60 +#define I386_RECORD_SIZE_serial_icounter_struct 80 +#define I386_RECORD_SIZE_hayes_esp_config 12 + +/* These macros are the values of the second argument of system call + "sys_ioctl". The values of these macros are gotten from Linux Kernel + source. */ +#define I386_RECORD_IOCTL_TCGETS 0x5401 +#define I386_RECORD_IOCTL_TCSETS 0x5402 +#define I386_RECORD_IOCTL_TCSETSW 0x5403 +#define I386_RECORD_IOCTL_TCSETSF 0x5404 +#define I386_RECORD_IOCTL_TCGETA 0x5405 +#define I386_RECORD_IOCTL_TCSETA 0x5406 +#define I386_RECORD_IOCTL_TCSETAW 0x5407 +#define I386_RECORD_IOCTL_TCSETAF 0x5408 +#define I386_RECORD_IOCTL_TCSBRK 0x5409 +#define I386_RECORD_IOCTL_TCXONC 0x540A +#define I386_RECORD_IOCTL_TCFLSH 0x540B +#define I386_RECORD_IOCTL_TIOCEXCL 0x540C +#define I386_RECORD_IOCTL_TIOCNXCL 0x540D +#define I386_RECORD_IOCTL_TIOCSCTTY 0x540E +#define I386_RECORD_IOCTL_TIOCGPGRP 0x540F +#define I386_RECORD_IOCTL_TIOCSPGRP 0x5410 +#define I386_RECORD_IOCTL_TIOCOUTQ 0x5411 +#define I386_RECORD_IOCTL_TIOCSTI 0x5412 +#define I386_RECORD_IOCTL_TIOCGWINSZ 0x5413 +#define I386_RECORD_IOCTL_TIOCSWINSZ 0x5414 +#define I386_RECORD_IOCTL_TIOCMGET 0x5415 +#define I386_RECORD_IOCTL_TIOCMBIS 0x5416 +#define I386_RECORD_IOCTL_TIOCMBIC 0x5417 +#define I386_RECORD_IOCTL_TIOCMSET 0x5418 +#define I386_RECORD_IOCTL_TIOCGSOFTCAR 0x5419 +#define I386_RECORD_IOCTL_TIOCSSOFTCAR 0x541A +#define I386_RECORD_IOCTL_FIONREAD 0x541B +#define I386_RECORD_IOCTL_TIOCINQ I386_RECORD_IOCTL_FIONREAD +#define I386_RECORD_IOCTL_TIOCLINUX 0x541C +#define I386_RECORD_IOCTL_TIOCCONS 0x541D +#define I386_RECORD_IOCTL_TIOCGSERIAL 0x541E +#define I386_RECORD_IOCTL_TIOCSSERIAL 0x541F +#define I386_RECORD_IOCTL_TIOCPKT 0x5420 +#define I386_RECORD_IOCTL_FIONBIO 0x5421 +#define I386_RECORD_IOCTL_TIOCNOTTY 0x5422 +#define I386_RECORD_IOCTL_TIOCSETD 0x5423 +#define I386_RECORD_IOCTL_TIOCGETD 0x5424 +#define I386_RECORD_IOCTL_TCSBRKP 0x5425 +#define I386_RECORD_IOCTL_TIOCTTYGSTRUCT 0x5426 +#define I386_RECORD_IOCTL_TIOCSBRK 0x5427 +#define I386_RECORD_IOCTL_TIOCCBRK 0x5428 +#define I386_RECORD_IOCTL_TIOCGSID 0x5429 +#define I386_RECORD_IOCTL_TCGETS2 0x802c542a +#define I386_RECORD_IOCTL_TCSETS2 0x402c542b +#define I386_RECORD_IOCTL_TCSETSW2 0x402c542c +#define I386_RECORD_IOCTL_TCSETSF2 0x402c542d +#define I386_RECORD_IOCTL_TIOCGPTN 0x80045430 +#define I386_RECORD_IOCTL_TIOCSPTLCK 0x40045431 +#define I386_RECORD_IOCTL_FIONCLEX 0x5450 +#define I386_RECORD_IOCTL_FIOCLEX 0x5451 +#define I386_RECORD_IOCTL_FIOASYNC 0x5452 +#define I386_RECORD_IOCTL_TIOCSERCONFIG 0x5453 +#define I386_RECORD_IOCTL_TIOCSERGWILD 0x5454 +#define I386_RECORD_IOCTL_TIOCSERSWILD 0x5455 +#define I386_RECORD_IOCTL_TIOCGLCKTRMIOS 0x5456 +#define I386_RECORD_IOCTL_TIOCSLCKTRMIOS 0x5457 +#define I386_RECORD_IOCTL_TIOCSERGSTRUCT 0x5458 +#define I386_RECORD_IOCTL_TIOCSERGETLSR 0x5459 +#define I386_RECORD_IOCTL_TIOCSERGETMULTI 0x545A +#define I386_RECORD_IOCTL_TIOCSERSETMULTI 0x545B +#define I386_RECORD_IOCTL_TIOCMIWAIT 0x545C +#define I386_RECORD_IOCTL_TIOCGICOUNT 0x545D +#define I386_RECORD_IOCTL_TIOCGHAYESESP 0x545E +#define I386_RECORD_IOCTL_TIOCSHAYESESP 0x545F +#define I386_RECORD_IOCTL_FIOQSIZE 0x5460 + static void i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { @@ -440,6 +614,150 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) tdep->sc_reg_offset = i386_linux_sc_reg_offset; tdep->sc_num_regs = ARRAY_SIZE (i386_linux_sc_reg_offset); + /* Initial the linux_record_tdep */ + linux_record_tdep.size__old_kernel_stat = I386_RECORD_SIZE__old_kernel_stat; + linux_record_tdep.size_tms = I386_RECORD_SIZE_tms; + linux_record_tdep.size_loff_t = I386_RECORD_SIZE_loff_t; + linux_record_tdep.size_flock = I386_RECORD_SIZE_flock; + linux_record_tdep.size_oldold_utsname = I386_RECORD_SIZE_oldold_utsname; + linux_record_tdep.size_ustat = I386_RECORD_SIZE_ustat; + linux_record_tdep.size_old_sigaction = I386_RECORD_SIZE_old_sigaction; + linux_record_tdep.size_old_sigset_t = I386_RECORD_SIZE_old_sigset_t; + linux_record_tdep.size_rlimit = I386_RECORD_SIZE_rlimit; + linux_record_tdep.size_rusage = I386_RECORD_SIZE_rusage; + linux_record_tdep.size_timeval = I386_RECORD_SIZE_timeval; + linux_record_tdep.size_timezone = I386_RECORD_SIZE_timezone; + linux_record_tdep.size_old_gid_t = I386_RECORD_SIZE_old_gid_t; + linux_record_tdep.size_old_uid_t = I386_RECORD_SIZE_old_uid_t; + linux_record_tdep.size_fd_set = I386_RECORD_SIZE_fd_set; + linux_record_tdep.size_dirent = I386_RECORD_SIZE_dirent; + linux_record_tdep.size_dirent64 = I386_RECORD_SIZE_dirent64; + linux_record_tdep.size_statfs = I386_RECORD_SIZE_statfs; + linux_record_tdep.size_statfs64 = I386_RECORD_SIZE_statfs64; + linux_record_tdep.size_sockaddr = I386_RECORD_SIZE_sockaddr; + linux_record_tdep.size_int = I386_RECORD_SIZE_int; + linux_record_tdep.size_long = I386_RECORD_SIZE_long; + linux_record_tdep.size_ulong = I386_RECORD_SIZE_ulong; + linux_record_tdep.size_msghdr = I386_RECORD_SIZE_msghdr; + linux_record_tdep.size_itimerval = I386_RECORD_SIZE_itimerval; + linux_record_tdep.size_stat = I386_RECORD_SIZE_stat; + linux_record_tdep.size_old_utsname = I386_RECORD_SIZE_old_utsname; + linux_record_tdep.size_sysinfo = I386_RECORD_SIZE_sysinfo; + linux_record_tdep.size_msqid_ds = I386_RECORD_SIZE_msqid_ds; + linux_record_tdep.size_shmid_ds = I386_RECORD_SIZE_shmid_ds; + linux_record_tdep.size_new_utsname = I386_RECORD_SIZE_new_utsname; + linux_record_tdep.size_timex = I386_RECORD_SIZE_timex; + linux_record_tdep.size_mem_dqinfo = I386_RECORD_SIZE_mem_dqinfo; + linux_record_tdep.size_if_dqblk = I386_RECORD_SIZE_if_dqblk; + linux_record_tdep.size_fs_quota_stat = I386_RECORD_SIZE_fs_quota_stat; + linux_record_tdep.size_timespec = I386_RECORD_SIZE_timespec; + linux_record_tdep.size_pollfd = I386_RECORD_SIZE_pollfd; + linux_record_tdep.size_NFS_FHSIZE = I386_RECORD_SIZE_NFS_FHSIZE; + linux_record_tdep.size_knfsd_fh = I386_RECORD_SIZE_knfsd_fh; + linux_record_tdep.size_TASK_COMM_LEN = I386_RECORD_SIZE_TASK_COMM_LEN; + linux_record_tdep.size_sigaction = I386_RECORD_SIZE_sigaction; + linux_record_tdep.size_sigset_t = I386_RECORD_SIZE_sigset_t; + linux_record_tdep.size_siginfo_t = I386_RECORD_SIZE_siginfo_t; + linux_record_tdep.size_cap_user_data_t = I386_RECORD_SIZE_cap_user_data_t; + linux_record_tdep.size_stack_t = I386_RECORD_SIZE_stack_t; + linux_record_tdep.size_off_t = I386_RECORD_SIZE_off_t; + linux_record_tdep.size_stat64 = I386_RECORD_SIZE_stat64; + linux_record_tdep.size_gid_t = I386_RECORD_SIZE_gid_t; + linux_record_tdep.size_uid_t = I386_RECORD_SIZE_uid_t; + linux_record_tdep.size_PAGE_SIZE = I386_RECORD_SIZE_PAGE_SIZE; + linux_record_tdep.size_flock64 = I386_RECORD_SIZE_flock64; + linux_record_tdep.size_user_desc = I386_RECORD_SIZE_user_desc; + linux_record_tdep.size_io_event = I386_RECORD_SIZE_io_event; + linux_record_tdep.size_iocb = I386_RECORD_SIZE_iocb; + linux_record_tdep.size_epoll_event = I386_RECORD_SIZE_epoll_event; + linux_record_tdep.size_itimerspec = I386_RECORD_SIZE_itimerspec; + linux_record_tdep.size_mq_attr = I386_RECORD_SIZE_mq_attr; + linux_record_tdep.size_siginfo = I386_RECORD_SIZE_siginfo; + linux_record_tdep.size_termios = I386_RECORD_SIZE_termios; + linux_record_tdep.size_termios2 = I386_RECORD_SIZE_termios2; + linux_record_tdep.size_pid_t = I386_RECORD_SIZE_pid_t; + linux_record_tdep.size_winsize = I386_RECORD_SIZE_winsize; + linux_record_tdep.size_char = I386_RECORD_SIZE_char; + linux_record_tdep.size_serial_struct = I386_RECORD_SIZE_serial_struct; + linux_record_tdep.size_serial_icounter_struct = + I386_RECORD_SIZE_serial_icounter_struct; + linux_record_tdep.size_hayes_esp_config = I386_RECORD_SIZE_hayes_esp_config; + + linux_record_tdep.ioctl_TCGETS = I386_RECORD_IOCTL_TCGETS; + linux_record_tdep.ioctl_TCSETS = I386_RECORD_IOCTL_TCSETS; + linux_record_tdep.ioctl_TCSETSW = I386_RECORD_IOCTL_TCSETSW; + linux_record_tdep.ioctl_TCSETSF = I386_RECORD_IOCTL_TCSETSF; + linux_record_tdep.ioctl_TCGETA = I386_RECORD_IOCTL_TCGETA; + linux_record_tdep.ioctl_TCSETA = I386_RECORD_IOCTL_TCSETA; + linux_record_tdep.ioctl_TCSETAW = I386_RECORD_IOCTL_TCSETAW; + linux_record_tdep.ioctl_TCSETAF = I386_RECORD_IOCTL_TCSETAF; + linux_record_tdep.ioctl_TCSBRK = I386_RECORD_IOCTL_TCSBRK; + linux_record_tdep.ioctl_TCXONC = I386_RECORD_IOCTL_TCXONC; + linux_record_tdep.ioctl_TCFLSH = I386_RECORD_IOCTL_TCFLSH; + linux_record_tdep.ioctl_TIOCEXCL = I386_RECORD_IOCTL_TIOCEXCL; + linux_record_tdep.ioctl_TIOCNXCL = I386_RECORD_IOCTL_TIOCNXCL; + linux_record_tdep.ioctl_TIOCSCTTY = I386_RECORD_IOCTL_TIOCSCTTY; + linux_record_tdep.ioctl_TIOCGPGRP = I386_RECORD_IOCTL_TIOCGPGRP; + linux_record_tdep.ioctl_TIOCSPGRP = I386_RECORD_IOCTL_TIOCSPGRP; + linux_record_tdep.ioctl_TIOCOUTQ = I386_RECORD_IOCTL_TIOCOUTQ; + linux_record_tdep.ioctl_TIOCSTI = I386_RECORD_IOCTL_TIOCSTI; + linux_record_tdep.ioctl_TIOCGWINSZ = I386_RECORD_IOCTL_TIOCGWINSZ; + linux_record_tdep.ioctl_TIOCSWINSZ = I386_RECORD_IOCTL_TIOCSWINSZ; + linux_record_tdep.ioctl_TIOCMGET = I386_RECORD_IOCTL_TIOCMGET; + linux_record_tdep.ioctl_TIOCMBIS = I386_RECORD_IOCTL_TIOCMBIS; + linux_record_tdep.ioctl_TIOCMBIC = I386_RECORD_IOCTL_TIOCMBIC; + linux_record_tdep.ioctl_TIOCMSET = I386_RECORD_IOCTL_TIOCMSET; + linux_record_tdep.ioctl_TIOCGSOFTCAR = I386_RECORD_IOCTL_TIOCGSOFTCAR; + linux_record_tdep.ioctl_TIOCSSOFTCAR = I386_RECORD_IOCTL_TIOCSSOFTCAR; + linux_record_tdep.ioctl_FIONREAD = I386_RECORD_IOCTL_FIONREAD; + linux_record_tdep.ioctl_TIOCINQ = I386_RECORD_IOCTL_TIOCINQ; + linux_record_tdep.ioctl_TIOCLINUX = I386_RECORD_IOCTL_TIOCLINUX; + linux_record_tdep.ioctl_TIOCCONS = I386_RECORD_IOCTL_TIOCCONS; + linux_record_tdep.ioctl_TIOCGSERIAL = I386_RECORD_IOCTL_TIOCGSERIAL; + linux_record_tdep.ioctl_TIOCSSERIAL = I386_RECORD_IOCTL_TIOCSSERIAL; + linux_record_tdep.ioctl_TIOCPKT = I386_RECORD_IOCTL_TIOCPKT; + linux_record_tdep.ioctl_FIONBIO = I386_RECORD_IOCTL_FIONBIO; + linux_record_tdep.ioctl_TIOCNOTTY = I386_RECORD_IOCTL_TIOCNOTTY; + linux_record_tdep.ioctl_TIOCSETD = I386_RECORD_IOCTL_TIOCSETD; + linux_record_tdep.ioctl_TIOCGETD = I386_RECORD_IOCTL_TIOCGETD; + linux_record_tdep.ioctl_TCSBRKP = I386_RECORD_IOCTL_TCSBRKP; + linux_record_tdep.ioctl_TIOCTTYGSTRUCT = I386_RECORD_IOCTL_TIOCTTYGSTRUCT; + linux_record_tdep.ioctl_TIOCSBRK = I386_RECORD_IOCTL_TIOCSBRK; + linux_record_tdep.ioctl_TIOCCBRK = I386_RECORD_IOCTL_TIOCCBRK; + linux_record_tdep.ioctl_TIOCGSID = I386_RECORD_IOCTL_TIOCGSID; + linux_record_tdep.ioctl_TCGETS2 = I386_RECORD_IOCTL_TCGETS2; + linux_record_tdep.ioctl_TCSETS2 = I386_RECORD_IOCTL_TCSETS2; + linux_record_tdep.ioctl_TCSETSW2 = I386_RECORD_IOCTL_TCSETSW2; + linux_record_tdep.ioctl_TCSETSF2 = I386_RECORD_IOCTL_TCSETSF2; + linux_record_tdep.ioctl_TIOCGPTN = I386_RECORD_IOCTL_TIOCGPTN; + linux_record_tdep.ioctl_TIOCSPTLCK = I386_RECORD_IOCTL_TIOCSPTLCK; + linux_record_tdep.ioctl_FIONCLEX = I386_RECORD_IOCTL_FIONCLEX; + linux_record_tdep.ioctl_FIOCLEX = I386_RECORD_IOCTL_FIOCLEX; + linux_record_tdep.ioctl_FIOASYNC = I386_RECORD_IOCTL_FIOASYNC; + linux_record_tdep.ioctl_TIOCSERCONFIG = I386_RECORD_IOCTL_TIOCSERCONFIG; + linux_record_tdep.ioctl_TIOCSERGWILD = I386_RECORD_IOCTL_TIOCSERGWILD; + linux_record_tdep.ioctl_TIOCSERSWILD = I386_RECORD_IOCTL_TIOCSERSWILD; + linux_record_tdep.ioctl_TIOCGLCKTRMIOS = I386_RECORD_IOCTL_TIOCGLCKTRMIOS; + linux_record_tdep.ioctl_TIOCSLCKTRMIOS = I386_RECORD_IOCTL_TIOCSLCKTRMIOS; + linux_record_tdep.ioctl_TIOCSERGSTRUCT = I386_RECORD_IOCTL_TIOCSERGSTRUCT; + linux_record_tdep.ioctl_TIOCSERGETLSR = I386_RECORD_IOCTL_TIOCSERGETLSR; + linux_record_tdep.ioctl_TIOCSERGETMULTI = I386_RECORD_IOCTL_TIOCSERGETMULTI; + linux_record_tdep.ioctl_TIOCSERSETMULTI = I386_RECORD_IOCTL_TIOCSERSETMULTI; + linux_record_tdep.ioctl_TIOCMIWAIT = I386_RECORD_IOCTL_TIOCMIWAIT; + linux_record_tdep.ioctl_TIOCGICOUNT = I386_RECORD_IOCTL_TIOCGICOUNT; + linux_record_tdep.ioctl_TIOCGHAYESESP = I386_RECORD_IOCTL_TIOCGHAYESESP; + linux_record_tdep.ioctl_TIOCSHAYESESP = I386_RECORD_IOCTL_TIOCSHAYESESP; + linux_record_tdep.ioctl_FIOQSIZE = I386_RECORD_IOCTL_FIOQSIZE; + + linux_record_tdep.arg1 = I386_EBX_REGNUM; + linux_record_tdep.arg2 = I386_ECX_REGNUM; + linux_record_tdep.arg3 = I386_EDX_REGNUM; + linux_record_tdep.arg4 = I386_ESI_REGNUM; + linux_record_tdep.arg5 = I386_EDI_REGNUM; + + tdep->i386_intx80_record = i386_linux_intx80_sysenter_record; + tdep->i386_sysenter_record = i386_linux_intx80_sysenter_record; + /* N_FUN symbols in shared libaries have 0 for their values and need to be relocated. */ set_gdbarch_sofun_address_maybe_missing (gdbarch, 1); diff --git a/gdb/i386-tdep.c b/gdb/i386-tdep.c index edaac21..fba6cee 100644 --- a/gdb/i386-tdep.c +++ b/gdb/i386-tdep.c @@ -49,6 +49,9 @@ #include "i386-tdep.h" #include "i387-tdep.h" +#include "record.h" +#include <stdint.h> + /* Register names. */ static char *i386_register_names[] = @@ -2638,6 +2641,2965 @@ i386_skip_permanent_breakpoint (struct regcache *regcache) } +#define PREFIX_REPZ 0x01 +#define PREFIX_REPNZ 0x02 +#define PREFIX_LOCK 0x04 +#define PREFIX_DATA 0x08 +#define PREFIX_ADDR 0x10 + +/* operand size */ +enum +{ + OT_BYTE = 0, + OT_WORD, + OT_LONG, +}; + +/* i386 arith/logic operations */ +enum +{ + OP_ADDL, + OP_ORL, + OP_ADCL, + OP_SBBL, + OP_ANDL, + OP_SUBL, + OP_XORL, + OP_CMPL, +}; + +static int aflag = 1; +static int dflag = 1; +static int override = 0; +static uint8_t modrm; +static uint8_t mod, reg, rm; +static int ot; +static CORE_ADDR i386_record_pc; + +/* Parse "modrm" part in current memory address that i386_record_pc point to. + Return -1 if something wrong. */ +static int +i386_record_modrm (void) +{ + if (target_read_memory (i386_record_pc, &modrm, 1)) + { + if (record_debug) + { + printf_unfiltered (_ + ("Process record: error reading memory at addr 0x%s len = 1.\n"), + paddr_nz (i386_record_pc)); + } + return (-1); + } + i386_record_pc++; + mod = (modrm >> 6) & 3; + reg = (modrm >> 3) & 7; + rm = modrm & 7; + + return (0); +} + +/* Get the memory address that current instruction write to and set it to + the argument "addr". + Return -1 if something wrong. */ +static int +i386_record_lea_modrm_addr (uint32_t * addr) +{ + uint8_t tmpu8; + uint16_t tmpu16; + uint32_t tmpu32; + + *addr = 0; + if (aflag) + { + /* 32 bits */ + int havesib = 0; + uint8_t scale = 0; + uint8_t index = 0; + uint8_t base = rm; + + if (base == 4) + { + havesib = 1; + if (target_read_memory (i386_record_pc, &tmpu8, 1)) + { + if (record_debug) + { + printf_unfiltered (_ + ("Process record: error reading memory at addr 0x%s len = 1.\n"), + paddr_nz (i386_record_pc)); + } + return (-1); + } + i386_record_pc++; + scale = (tmpu8 >> 6) & 3; + index = ((tmpu8 >> 3) & 7); + base = (tmpu8 & 7); + } + + switch (mod) + { + case 0: + if ((base & 7) == 5) + { + base = 0xff; + if (target_read_memory (i386_record_pc, (gdb_byte *) addr, 4)) + { + if (record_debug) + { + printf_unfiltered (_ + ("Process record: error reading memory at addr 0x%s len = 4.\n"), + paddr_nz (i386_record_pc)); + } + return (-1); + } + i386_record_pc += 4; + } + else + { + *addr = 0; + } + break; + case 1: + if (target_read_memory (i386_record_pc, &tmpu8, 1)) + { + if (record_debug) + { + printf_unfiltered (_ + ("Process record: error reading memory at addr 0x%s len = 1.\n"), + paddr_nz (i386_record_pc)); + } + return (-1); + } + i386_record_pc++; + *addr = (int8_t) tmpu8; + break; + case 2: + if (target_read_memory (i386_record_pc, (gdb_byte *) addr, 4)) + { + if (record_debug) + { + printf_unfiltered (_ + ("Process record: error reading memory at addr 0x%s len = 4.\n"), + paddr_nz (i386_record_pc)); + } + return (-1); + } + i386_record_pc += 4; + break; + } + + if (base != 0xff) + { + regcache_raw_read (record_regcache, base, (gdb_byte *) & tmpu32); + *addr += tmpu32; + } + + /* XXX: index == 4 is always invalid */ + if (havesib && (index != 4 || scale != 0)) + { + regcache_raw_read (record_regcache, index, (gdb_byte *) & tmpu32); + *addr += tmpu32 << scale; + } + } + else + { + /* 16 bits */ + switch (mod) + { + case 0: + if (rm == 6) + { + if (target_read_memory + (i386_record_pc, (gdb_byte *) & tmpu16, 2)) + { + if (record_debug) + { + printf_unfiltered (_ + ("Process record: error reading memory at addr 0x%s len = 2.\n"), + paddr_nz (i386_record_pc)); + } + return (-1); + } + i386_record_pc += 2; + *addr = (int16_t) tmpu16; + rm = 0; + goto no_rm; + } + else + { + *addr = 0; + } + break; + case 1: + if (target_read_memory (i386_record_pc, &tmpu8, 1)) + { + if (record_debug) + { + printf_unfiltered (_ + ("Process record: error reading memory at addr 0x%s len = 1.\n"), + paddr_nz (i386_record_pc)); + } + return (-1); + } + i386_record_pc++; + *addr = (int8_t) tmpu8; + break; + case 2: + if (target_read_memory (i386_record_pc, (gdb_byte *) & tmpu16, 2)) + { + if (record_debug) + { + printf_unfiltered (_ + ("Process record: error reading memory at addr 0x%s len = 2.\n"), + paddr_nz (i386_record_pc)); + } + return (-1); + } + i386_record_pc += 2; + *addr = (int16_t) tmpu16; + break; + } + + switch (rm) + { + case 0: + regcache_raw_read (record_regcache, I386_EBX_REGNUM, + (gdb_byte *) & tmpu32); + *addr += tmpu32; + regcache_raw_read (record_regcache, I386_ESI_REGNUM, + (gdb_byte *) & tmpu32); + *addr += tmpu32; + break; + case 1: + regcache_raw_read (record_regcache, I386_EBX_REGNUM, + (gdb_byte *) & tmpu32); + *addr += tmpu32; + regcache_raw_read (record_regcache, I386_EDI_REGNUM, + (gdb_byte *) & tmpu32); + *addr += tmpu32; + break; + case 2: + regcache_raw_read (record_regcache, I386_EBP_REGNUM, + (gdb_byte *) & tmpu32); + *addr += tmpu32; + regcache_raw_read (record_regcache, I386_ESI_REGNUM, + (gdb_byte *) & tmpu32); + *addr += tmpu32; + break; + case 3: + regcache_raw_read (record_regcache, I386_EBP_REGNUM, + (gdb_byte *) & tmpu32); + *addr += tmpu32; + regcache_raw_read (record_regcache, I386_EDI_REGNUM, + (gdb_byte *) & tmpu32); + *addr += tmpu32; + break; + case 4: + regcache_raw_read (record_regcache, I386_ESI_REGNUM, + (gdb_byte *) & tmpu32); + *addr += tmpu32; + break; + case 5: + regcache_raw_read (record_regcache, I386_EDI_REGNUM, + (gdb_byte *) & tmpu32); + *addr += tmpu32; + break; + case 6: + regcache_raw_read (record_regcache, I386_EBP_REGNUM, + (gdb_byte *) & tmpu32); + *addr += tmpu32; + break; + case 7: + regcache_raw_read (record_regcache, I386_EBX_REGNUM, + (gdb_byte *) & tmpu32); + *addr += tmpu32; + break; + } + *addr &= 0xffff; + } + +no_rm: + return (0); +} + +/* Record the value of the memory that willbe changed in current instruction + to "record_arch_list". + Return -1 if something wrong. */ +static int +i386_record_lea_modrm (void) +{ + uint32_t addr; + + if (override) + { + if (record_debug) + printf_unfiltered (_ + ("Process record ignores the memory change of instruction at address 0x%s because it can't get the value of the segment register.\n"), + paddr_nz (i386_record_pc)); + return (0); + } + + if (i386_record_lea_modrm_addr (&addr)) + { + return (-1); + } + + if (record_arch_list_add_mem (addr, 1 << ot)) + { + 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 wrong. */ +static int +i386_process_record (struct gdbarch *gdbarch, CORE_ADDR addr) +{ + int prefixes = 0; + uint8_t tmpu8; + uint16_t tmpu16; + uint32_t tmpu32; + uint32_t opcode; + + i386_record_pc = addr; + aflag = 1; + dflag = 1; + override = 0; + + if (record_debug > 1) + { + fprintf_unfiltered (gdb_stdlog, "Process record: i386_record pc = 0x%s\n", + paddr_nz (i386_record_pc)); + } + + /* prefixes */ + while (1) + { + if (target_read_memory (i386_record_pc, &tmpu8, 1)) + { + if (record_debug) + { + printf_unfiltered (_ + ("Process record: error reading memory at addr 0x%s len = 1.\n"), + paddr_nz (i386_record_pc)); + } + return (-1); + } + i386_record_pc++; + switch (tmpu8) + { + case 0xf3: + prefixes |= PREFIX_REPZ; + break; + case 0xf2: + prefixes |= PREFIX_REPNZ; + break; + case 0xf0: + prefixes |= PREFIX_LOCK; + break; + case 0x2e: + override = I386_CS_REGNUM; + break; + case 0x36: + override = I386_SS_REGNUM; + break; + case 0x3e: + override = I386_DS_REGNUM; + break; + case 0x26: + override = I386_ES_REGNUM; + break; + case 0x64: + override = I386_FS_REGNUM; + break; + case 0x65: + override = I386_GS_REGNUM; + break; + case 0x66: + prefixes |= PREFIX_DATA; + break; + case 0x67: + prefixes |= PREFIX_ADDR; + break; + default: + goto out_prefixes; + break; + } + } +out_prefixes: + if (prefixes & PREFIX_DATA) + { + dflag ^= 1; + } + if (prefixes & PREFIX_ADDR) + { + aflag ^= 1; + } + + /* now check op code */ + opcode = (uint32_t) tmpu8; +reswitch: + switch (opcode) + { + case 0x0f: + if (target_read_memory (i386_record_pc, &tmpu8, 1)) + { + if (record_debug) + { + printf_unfiltered (_ + ("Process record: error reading memory at addr 0x%s len = 1.\n"), + paddr_nz (i386_record_pc)); + } + return (-1); + } + i386_record_pc++; + opcode = (uint16_t) tmpu8 | 0x0f00; + goto reswitch; + break; + + /* arith & logic */ + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x08: + case 0x09: + case 0x0a: + case 0x0b: + case 0x0c: + case 0x0d: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x1c: + case 0x1d: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x28: + case 0x29: + case 0x2a: + case 0x2b: + case 0x2c: + case 0x2d: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x38: + case 0x39: + case 0x3a: + case 0x3b: + case 0x3c: + case 0x3d: + if (((opcode >> 3) & 7) != OP_CMPL) + { + if ((opcode & 1) == 0) + { + ot = OT_BYTE; + } + else + { + ot = dflag + OT_WORD; + } + + switch ((opcode >> 1) & 3) + { + /* OP Ev, Gv */ + case 0: + if (i386_record_modrm ()) + { + return (-1); + } + if (mod != 3) + { + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + else + { + if (ot == OT_BYTE) + { + rm &= 0x3; + } + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + break; + /* OP Gv, Ev */ + case 1: + if (i386_record_modrm ()) + { + return (-1); + } + if (ot == OT_BYTE) + { + reg &= 0x3; + } + if (record_arch_list_add_reg (reg)) + { + return (-1); + } + break; + /* OP A, Iv */ + case 2: + if (record_arch_list_add_reg (I386_EAX_REGNUM)) + { + return (-1); + } + break; + } + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* GRP1 */ + case 0x80: + case 0x81: + case 0x82: + case 0x83: + if (i386_record_modrm ()) + { + return (-1); + } + + if (reg != OP_CMPL) + { + if ((opcode & 1) == 0) + { + ot = OT_BYTE; + } + else + { + ot = dflag + OT_WORD; + } + + if (mod != 3) + { + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + else + { + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* inv */ + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + /* dec */ + case 0x48: + case 0x49: + case 0x4a: + case 0x4b: + case 0x4c: + case 0x4d: + case 0x4e: + case 0x4f: + if (record_arch_list_add_reg (opcode & 7)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* GRP3 */ + case 0xf6: + case 0xf7: + if ((opcode & 1) == 0) + { + ot = OT_BYTE; + } + else + { + ot = dflag + OT_WORD; + } + if (i386_record_modrm ()) + { + return (-1); + } + + switch (reg) + { + /* test */ + case 0: + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + /* not */ + case 2: + if (mod != 3) + { + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + else + { + if (ot == OT_BYTE) + { + rm &= 0x3; + } + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + break; + /* neg */ + case 3: + if (mod != 3) + { + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + else + { + if (ot == OT_BYTE) + { + rm &= 0x3; + } + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + /* mul */ + case 4: + /* imul */ + case 5: + /* div */ + case 6: + /* idiv */ + case 7: + if (record_arch_list_add_reg (I386_EAX_REGNUM)) + { + return (-1); + } + if (ot != OT_BYTE) + { + if (record_arch_list_add_reg (I386_EDX_REGNUM)) + { + return (-1); + } + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + default: + i386_record_pc -= 2; + opcode = opcode << 8 | modrm; + goto no_support; + break; + } + break; + + /* GRP4 */ + case 0xfe: + /* GRP5 */ + case 0xff: + if ((opcode & 1) == 0) + { + ot = OT_BYTE; + } + else + { + ot = dflag + OT_WORD; + } + if (i386_record_modrm ()) + { + return (-1); + } + if (reg >= 2 && opcode == 0xfe) + { + i386_record_pc -= 2; + opcode = opcode << 8 | modrm; + goto no_support; + } + + switch (reg) + { + /* inc */ + case 0: + /* dec */ + case 1: + if (mod != 3) + { + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + else + { + if (ot == OT_BYTE) + { + rm &= 0x3; + } + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + /* call */ + case 2: + /* push */ + case 6: + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + regcache_raw_read (record_regcache, I386_ESP_REGNUM, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem + ((CORE_ADDR) tmpu32 - (1 << (dflag + 1)), (1 << (dflag + 1)))) + { + return (-1); + } + break; + /* lcall */ + case 3: + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_CS_REGNUM)) + { + return (-1); + } + regcache_raw_read (record_regcache, I386_ESP_REGNUM, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem + ((CORE_ADDR) tmpu32 - (1 << (dflag + 2)), (1 << (dflag + 2)))) + { + return (-1); + } + break; + /* jmp */ + case 4: + /* ljmp */ + case 5: + break; + default: + i386_record_pc -= 2; + opcode = opcode << 8 | modrm; + goto no_support; + break; + } + break; + + /* test */ + case 0x84: + case 0x85: + case 0xa8: + case 0xa9: + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* CWDE/CBW */ + case 0x98: + if (record_arch_list_add_reg (I386_EAX_REGNUM)) + { + return (-1); + } + break; + + /* CDQ/CWD */ + case 0x99: + if (record_arch_list_add_reg (I386_EAX_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_EDX_REGNUM)) + { + return (-1); + } + break; + + /* imul */ + case 0x0faf: + case 0x69: + case 0x6b: + ot = dflag + OT_WORD; + if (i386_record_modrm ()) + { + return (-1); + } + if (ot == OT_BYTE) + { + reg &= 0x3; + } + if (record_arch_list_add_reg (reg)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* xadd */ + case 0x0fc0: + case 0x0fc1: + if ((opcode & 1) == 0) + { + ot = OT_BYTE; + } + else + { + ot = dflag + OT_WORD; + } + if (i386_record_modrm ()) + { + return (-1); + } + if (mod == 3) + { + if (ot == OT_BYTE) + { + reg &= 0x3; + } + if (record_arch_list_add_reg (reg)) + { + return (-1); + } + if (ot == OT_BYTE) + { + rm &= 0x3; + } + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + else + { + if (i386_record_lea_modrm ()) + { + return (-1); + } + if (ot == OT_BYTE) + { + reg &= 0x3; + } + if (record_arch_list_add_reg (reg)) + { + return (-1); + } + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* cmpxchg */ + case 0x0fb0: + case 0x0fb1: + if ((opcode & 1) == 0) + { + ot = OT_BYTE; + } + else + { + ot = dflag + OT_WORD; + } + if (i386_record_modrm ()) + { + return (-1); + } + if (mod == 3) + { + if (record_arch_list_add_reg (I386_EAX_REGNUM)) + { + return (-1); + } + if (ot == OT_BYTE) + { + reg &= 0x3; + } + if (record_arch_list_add_reg (reg)) + { + return (-1); + } + } + else + { + if (record_arch_list_add_reg (I386_EAX_REGNUM)) + { + return (-1); + } + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* cmpxchg8b */ + case 0x0fc7: + if (i386_record_modrm ()) + { + return (-1); + } + if (mod == 3) + { + i386_record_pc -= 2; + opcode = opcode << 8 | modrm; + goto no_support; + } + if (record_arch_list_add_reg (I386_EAX_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_EDX_REGNUM)) + { + return (-1); + } + if (i386_record_lea_modrm ()) + { + return (-1); + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* push */ + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x68: + case 0x6a: + /* push es */ + case 0x06: + /* push cs */ + case 0x0e: + /* push ss */ + case 0x16: + /* push ds */ + case 0x1e: + /* push fs */ + case 0x0fa0: + /* push gs */ + case 0x0fa8: + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + regcache_raw_read (record_regcache, I386_ESP_REGNUM, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem + ((CORE_ADDR) tmpu32 - (1 << (dflag + 1)), (1 << (dflag + 1)))) + { + return (-1); + } + break; + + /* pop */ + case 0x58: + case 0x59: + case 0x5a: + case 0x5b: + case 0x5c: + case 0x5d: + case 0x5e: + case 0x5f: + ot = dflag + OT_WORD; + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + if (ot == OT_BYTE) + { + opcode &= 0x3; + } + if (record_arch_list_add_reg (opcode & 0x7)) + { + return (-1); + } + break; + + /* pusha */ + case 0x60: + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + regcache_raw_read (record_regcache, I386_ESP_REGNUM, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem + ((CORE_ADDR) tmpu32 - (1 << (dflag + 4)), (1 << (dflag + 4)))) + { + return (-1); + } + break; + + /* popa */ + case 0x61: + for (tmpu8 = I386_EAX_REGNUM; tmpu8 <= I386_EDI_REGNUM; tmpu8++) + { + if (record_arch_list_add_reg (tmpu8)) + { + return (-1); + } + } + break; + + /* pop */ + case 0x8f: + ot = dflag + OT_WORD; + if (i386_record_modrm ()) + { + return (-1); + } + if (mod == 3) + { + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + else + { + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + break; + + /* enter */ + case 0xc8: + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_EBP_REGNUM)) + { + return (-1); + } + regcache_raw_read (record_regcache, I386_ESP_REGNUM, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem + ((CORE_ADDR) tmpu32 - (1 << (dflag + 1)), (1 << (dflag + 1)))) + { + return (-1); + } + break; + + /* leave */ + case 0xc9: + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_EBP_REGNUM)) + { + return (-1); + } + break; + + /* pop es */ + case 0x07: + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_ES_REGNUM)) + { + return (-1); + } + break; + + /* pop ss */ + case 0x17: + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_SS_REGNUM)) + { + return (-1); + } + break; + + /* pop ds */ + case 0x1f: + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_DS_REGNUM)) + { + return (-1); + } + break; + + /* pop fs */ + case 0x0fa1: + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_FS_REGNUM)) + { + return (-1); + } + break; + + /* pop gs */ + case 0x0fa9: + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_GS_REGNUM)) + { + return (-1); + } + break; + + /* mov */ + case 0x88: + case 0x89: + case 0xc6: + case 0xc7: + if ((opcode & 1) == 0) + { + ot = OT_BYTE; + } + else + { + ot = dflag + OT_WORD; + } + + if (i386_record_modrm ()) + { + return (-1); + } + + if (mod != 3) + { + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + else + { + if (ot == OT_BYTE) + { + rm &= 0x3; + } + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + /* mov */ + case 0x8a: + case 0x8b: + if ((opcode & 1) == 0) + { + ot = OT_BYTE; + } + else + { + ot = dflag + OT_WORD; + } + + if (i386_record_modrm ()) + { + return (-1); + } + + if (ot == OT_BYTE) + { + reg &= 0x3; + } + if (record_arch_list_add_reg (reg)) + { + return (-1); + } + + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* mov seg */ + case 0x8e: + if (i386_record_modrm ()) + { + return (-1); + } + + switch (reg) + { + case 0: + tmpu8 = I386_ES_REGNUM; + break; + case 2: + tmpu8 = I386_SS_REGNUM; + break; + case 3: + tmpu8 = I386_DS_REGNUM; + break; + case 4: + tmpu8 = I386_FS_REGNUM; + break; + case 5: + tmpu8 = I386_GS_REGNUM; + break; + default: + i386_record_pc -= 2; + opcode = opcode << 8 | modrm; + goto no_support; + break; + } + if (record_arch_list_add_reg (tmpu8)) + { + return (-1); + } + + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* mov seg */ + case 0x8c: + if (i386_record_modrm ()) + { + return (-1); + } + if (reg > 5) + { + i386_record_pc -= 2; + opcode = opcode << 8 | modrm; + goto no_support; + } + + if (mod == 3) + { + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + else + { + ot = OT_WORD; + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* movzbS */ + case 0x0fb6: + /* movzwS */ + case 0x0fb7: + /* movsbS */ + case 0x0fbe: + /* movswS */ + case 0x0fbf: + if (i386_record_modrm ()) + { + return (-1); + } + if (record_arch_list_add_reg (reg)) + { + return (-1); + } + break; + + /* lea */ + case 0x8d: + if (i386_record_modrm ()) + { + return (-1); + } + if (mod == 3) + { + i386_record_pc -= 2; + opcode = opcode << 8 | modrm; + goto no_support; + } + + ot = dflag; + if (ot == OT_BYTE) + { + reg &= 0x3; + } + if (record_arch_list_add_reg (reg)) + { + return (-1); + } + break; + + /* mov EAX */ + case 0xa0: + case 0xa1: + /* xlat */ + case 0xd7: + if (record_arch_list_add_reg (I386_EAX_REGNUM)) + { + return (-1); + } + break; + + /* mov EAX */ + case 0xa2: + case 0xa3: + { + uint32_t addr; + + if (override) + { + if (record_debug) + printf_unfiltered (_ + ("Process record ignores the memory change of instruction at address 0x%s because it can't get the value of the segment register.\n"), + paddr_nz (i386_record_pc)); + } + else + { + if ((opcode & 1) == 0) + { + ot = OT_BYTE; + } + else + { + ot = dflag + OT_WORD; + } + if (aflag) + { + if (target_read_memory + (i386_record_pc, (gdb_byte *) & addr, 4)) + { + if (record_debug) + { + printf_unfiltered (_ + ("Process record: error reading memory at addr 0x%s len = 4.\n"), + paddr_nz (i386_record_pc)); + } + return (-1); + } + i386_record_pc += 4; + } + else + { + if (target_read_memory + (i386_record_pc, (gdb_byte *) & tmpu16, 4)) + { + if (record_debug) + { + printf_unfiltered (_ + ("Process record: error reading memory at addr 0x%s len = 4.\n"), + paddr_nz (i386_record_pc)); + } + return (-1); + } + i386_record_pc += 2; + addr = tmpu16; + } + if (record_arch_list_add_mem (addr, 1 << ot)) + { + return (-1); + } + } + } + break; + + /* mov R, Ib */ + case 0xb0: + case 0xb1: + case 0xb2: + case 0xb3: + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + if (record_arch_list_add_reg ((opcode & 0x7) & 0x3)) + { + return (-1); + } + break; + + /* mov R, Iv */ + case 0xb8: + case 0xb9: + case 0xba: + case 0xbb: + case 0xbc: + case 0xbd: + case 0xbe: + case 0xbf: + if (record_arch_list_add_reg (opcode & 0x7)) + { + return (-1); + } + break; + + /* xchg R, EAX */ + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + if (record_arch_list_add_reg (I386_EAX_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (opcode & 0x7)) + { + return (-1); + } + break; + + /* xchg Ev, Gv */ + case 0x86: + case 0x87: + if ((opcode & 1) == 0) + { + ot = OT_BYTE; + } + else + { + ot = dflag + OT_WORD; + } + + if (i386_record_modrm ()) + { + return (-1); + } + + if (mod == 3) + { + if (ot == OT_BYTE) + { + rm &= 0x3; + } + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + else + { + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + + if (ot == OT_BYTE) + { + reg &= 0x3; + } + if (record_arch_list_add_reg (reg)) + { + return (-1); + } + break; + + /* les Gv */ + case 0xc4: + /* lds Gv */ + case 0xc5: + /* lss Gv */ + case 0x0fb2: + /* lfs Gv */ + case 0x0fb4: + /* lgs Gv */ + case 0x0fb5: + if (i386_record_modrm ()) + { + return (-1); + } + if (mod == 3) + { + if (opcode > 0xff) + { + i386_record_pc -= 3; + } + else + { + i386_record_pc -= 2; + } + opcode = opcode << 8 | modrm; + goto no_support; + } + + switch (opcode) + { + /* les Gv */ + case 0xc4: + tmpu8 = I386_ES_REGNUM; + break; + /* lds Gv */ + case 0xc5: + tmpu8 = I386_DS_REGNUM; + break; + /* lss Gv */ + case 0x0fb2: + tmpu8 = I386_SS_REGNUM; + break; + /* lfs Gv */ + case 0x0fb4: + tmpu8 = I386_FS_REGNUM; + break; + /* lgs Gv */ + case 0x0fb5: + tmpu8 = I386_GS_REGNUM; + break; + } + if (record_arch_list_add_reg (tmpu8)) + { + return (-1); + } + + if (record_arch_list_add_reg (reg)) + { + return (-1); + } + break; + + /* shifts */ + case 0xc0: + case 0xc1: + case 0xd0: + case 0xd1: + case 0xd2: + case 0xd3: + if ((opcode & 1) == 0) + { + ot = OT_BYTE; + } + else + { + ot = dflag + OT_WORD; + } + + if (i386_record_modrm ()) + { + return (-1); + } + + if (mod != 3 && (opcode == 0xd2 || opcode == 0xd3)) + { + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + else + { + if (ot == OT_BYTE) + { + rm &= 0x3; + } + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + case 0x0fa4: + case 0x0fa5: + case 0x0fac: + case 0x0fad: + if (i386_record_modrm ()) + { + return (-1); + } + if (mod == 3) + { + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + else + { + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + break; + + /* floats */ + /* It just record the memory change of instrcution. */ + case 0xd8: + case 0xd9: + case 0xda: + case 0xdb: + case 0xdc: + case 0xdd: + case 0xde: + case 0xdf: + if (i386_record_modrm ()) + { + return (-1); + } + reg |= ((opcode & 7) << 3); + if (mod != 3) + { + /* memory */ + uint32_t addr; + + if (i386_record_lea_modrm_addr (&addr)) + { + return (-1); + } + switch (reg) + { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + break; + case 0x08: + case 0x0a: + case 0x0b: + case 0x18: + case 0x19: + case 0x1a: + case 0x1b: + case 0x28: + case 0x29: + case 0x2a: + case 0x2b: + case 0x38: + case 0x39: + case 0x3a: + case 0x3b: + switch (reg & 7) + { + case 0: + break; + case 1: + switch (reg >> 4) + { + case 0: + if (record_arch_list_add_mem (addr, 4)) + { + return (-1); + } + break; + case 2: + if (record_arch_list_add_mem (addr, 8)) + { + return (-1); + } + break; + case 3: + default: + if (record_arch_list_add_mem (addr, 2)) + { + return (-1); + } + break; + } + break; + default: + switch (reg >> 4) + { + case 0: + case 1: + if (record_arch_list_add_mem (addr, 4)) + { + return (-1); + } + break; + case 2: + if (record_arch_list_add_mem (addr, 8)) + { + return (-1); + } + break; + case 3: + default: + if (record_arch_list_add_mem (addr, 2)) + { + return (-1); + } + break; + } + break; + } + break; + case 0x0c: + case 0x0d: + case 0x1d: + case 0x2c: + case 0x3c: + case 0x3d: + break; + case 0x0e: + if (dflag) + { + if (record_arch_list_add_mem (addr, 28)) + { + return (-1); + } + } + else + { + if (record_arch_list_add_mem (addr, 14)) + { + return (-1); + } + } + break; + case 0x0f: + case 0x2f: + if (record_arch_list_add_mem (addr, 2)) + { + return (-1); + } + break; + case 0x1f: + case 0x3e: + if (record_arch_list_add_mem (addr, 10)) + { + return (-1); + } + break; + case 0x2e: + if (dflag) + { + if (record_arch_list_add_mem (addr, 28)) + { + return (-1); + } + addr += 28; + } + else + { + if (record_arch_list_add_mem (addr, 14)) + { + return (-1); + } + addr += 14; + } + if (record_arch_list_add_mem (addr, 80)) + { + return (-1); + } + break; + case 0x3f: + if (record_arch_list_add_mem (addr, 8)) + { + return (-1); + } + break; + default: + i386_record_pc -= 2; + opcode = opcode << 8 | modrm; + goto no_support; + break; + } + } + break; + + /* string ops */ + /* movsS */ + case 0xa4: + case 0xa5: + /* stosS */ + case 0xaa: + case 0xab: + /* insS */ + case 0x6c: + case 0x6d: + { + uint32_t addr; + + if ((opcode & 1) == 0) + { + ot = OT_BYTE; + } + else + { + ot = dflag + OT_WORD; + } + if (opcode == 0xa4 || opcode == 0xa5) + { + if (record_arch_list_add_reg (I386_ESI_REGNUM)) + { + return (-1); + } + } + if (record_arch_list_add_reg (I386_EDI_REGNUM)) + { + return (-1); + } + + regcache_raw_read (record_regcache, I386_EDI_REGNUM, + (gdb_byte *) & addr); + if (!aflag) + { + addr &= 0xffff; + /* addr += ((uint32_t)read_register (I386_ES_REGNUM)) << 4; */ + if (record_debug) + printf_unfiltered (_ + ("Process record ignores the memory change of instruction at address 0x%s because it can't get the value of the segment register.\n"), + paddr_nz (i386_record_pc)); + } + + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) + { + uint32_t count; + + regcache_raw_read (record_regcache, I386_ECX_REGNUM, + (gdb_byte *) & count); + if (!aflag) + { + count &= 0xffff; + } + + regcache_raw_read (record_regcache, I386_EFLAGS_REGNUM, + (gdb_byte *) & tmpu32); + if ((tmpu32 >> 10) & 0x1) + { + addr -= (count - 1) * (1 << ot); + } + + if (aflag) + { + if (record_arch_list_add_mem (addr, count * (1 << ot))) + { + return (-1); + } + } + + if (record_arch_list_add_reg (I386_ECX_REGNUM)) + { + return (-1); + } + } + else + { + if (aflag) + { + if (record_arch_list_add_mem (addr, 1 << ot)) + { + return (-1); + } + } + } + } + break; + + /* lodsS */ + case 0xac: + case 0xad: + if (record_arch_list_add_reg (I386_EAX_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_ESI_REGNUM)) + { + return (-1); + } + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) + { + if (record_arch_list_add_reg (I386_ECX_REGNUM)) + { + return (-1); + } + } + break; + + /* outsS */ + case 0x6e: + case 0x6f: + if (record_arch_list_add_reg (I386_ESI_REGNUM)) + { + return (-1); + } + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) + { + if (record_arch_list_add_reg (I386_ECX_REGNUM)) + { + return (-1); + } + } + break; + + /* scasS */ + case 0xae: + case 0xaf: + if (record_arch_list_add_reg (I386_EDI_REGNUM)) + { + return (-1); + } + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) + { + if (record_arch_list_add_reg (I386_ECX_REGNUM)) + { + return (-1); + } + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* cmpsS */ + case 0xa6: + case 0xa7: + if (record_arch_list_add_reg (I386_EDI_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_ESI_REGNUM)) + { + return (-1); + } + if (prefixes & (PREFIX_REPZ | PREFIX_REPNZ)) + { + if (record_arch_list_add_reg (I386_ECX_REGNUM)) + { + return (-1); + } + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* port I/O */ + case 0xe4: + case 0xe5: + case 0xec: + case 0xed: + if (record_arch_list_add_reg (I386_EAX_REGNUM)) + { + return (-1); + } + break; + + case 0xe6: + case 0xe7: + case 0xee: + case 0xef: + break; + + /* control */ + /* ret im */ + case 0xc2: + /* ret */ + case 0xc3: + /* lret im */ + case 0xca: + /* lret */ + case 0xcb: + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_CS_REGNUM)) + { + return (-1); + } + break; + + /* iret */ + case 0xcf: + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_CS_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* call im */ + case 0xe8: + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + regcache_raw_read (record_regcache, I386_ESP_REGNUM, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem + ((CORE_ADDR) tmpu32 - (1 << (dflag + 1)), (1 << (dflag + 1)))) + { + return (-1); + } + break; + + /* lcall im */ + case 0x9a: + if (record_arch_list_add_reg (I386_CS_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + regcache_raw_read (record_regcache, I386_ESP_REGNUM, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem + ((CORE_ADDR) tmpu32 - (1 << (dflag + 2)), (1 << (dflag + 2)))) + { + return (-1); + } + break; + + /* jmp im */ + case 0xe9: + /* ljmp im */ + case 0xea: + /* jmp Jb */ + case 0xeb: + /* jcc Jb */ + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7a: + case 0x7b: + case 0x7c: + case 0x7d: + case 0x7e: + case 0x7f: + /* jcc Jv */ + case 0x0f80: + case 0x0f81: + case 0x0f82: + case 0x0f83: + case 0x0f84: + case 0x0f85: + case 0x0f86: + case 0x0f87: + case 0x0f88: + case 0x0f89: + case 0x0f8a: + case 0x0f8b: + case 0x0f8c: + case 0x0f8d: + case 0x0f8e: + case 0x0f8f: + break; + + /* setcc Gv */ + case 0x0f90: + case 0x0f91: + case 0x0f92: + case 0x0f93: + case 0x0f94: + case 0x0f95: + case 0x0f96: + case 0x0f97: + case 0x0f98: + case 0x0f99: + case 0x0f9a: + case 0x0f9b: + case 0x0f9c: + case 0x0f9d: + case 0x0f9e: + case 0x0f9f: + ot = OT_BYTE; + if (i386_record_modrm ()) + { + return (-1); + } + if (mod == 3) + { + if (record_arch_list_add_reg (rm & 0x3)) + { + return (-1); + } + } + else + { + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + break; + + /* cmov Gv, Ev */ + case 0x0f40: + case 0x0f41: + case 0x0f42: + case 0x0f43: + case 0x0f44: + case 0x0f45: + case 0x0f46: + case 0x0f47: + case 0x0f48: + case 0x0f49: + case 0x0f4a: + case 0x0f4b: + case 0x0f4c: + case 0x0f4d: + case 0x0f4e: + case 0x0f4f: + if (i386_record_modrm ()) + { + return (-1); + } + if (dflag == OT_BYTE) + { + reg &= 0x3; + } + if (record_arch_list_add_reg (reg & 0x3)) + { + return (-1); + } + break; + + /* flags */ + /* pushf */ + case 0x9c: + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + regcache_raw_read (record_regcache, I386_ESP_REGNUM, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem + ((CORE_ADDR) tmpu32 - (1 << (dflag + 1)), (1 << (dflag + 1)))) + { + return (-1); + } + break; + + /* popf */ + case 0x9d: + if (record_arch_list_add_reg (I386_ESP_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* sahf */ + case 0x9e: + /* cmc */ + case 0xf5: + /* clc */ + case 0xf8: + /* stc */ + case 0xf9: + /* cld */ + case 0xfc: + /* std */ + case 0xfd: + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* lahf */ + case 0x9f: + if (record_arch_list_add_reg (I386_EAX_REGNUM)) + { + return (-1); + } + break; + + /* bit operations */ + /* bt/bts/btr/btc Gv, im */ + case 0x0fba: + /* bts */ + case 0x0fab: + /* btr */ + case 0x0fb3: + /* btc */ + case 0x0fbb: + ot = dflag + OT_WORD; + if (i386_record_modrm ()) + { + return (-1); + } + if (reg < 4) + { + i386_record_pc -= 3; + opcode = opcode << 8 | modrm; + goto no_support; + } + reg -= 4; + if (reg != 0) + { + if (mod != 3) + { + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + else + { + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* bt Gv, Ev */ + case 0x0fa3: + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* bsf */ + case 0x0fbc: + /* bsr */ + case 0x0fbd: + if (record_arch_list_add_reg (reg)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* bcd */ + /* daa */ + case 0x27: + /* das */ + case 0x2f: + /* aaa */ + case 0x37: + /* aas */ + case 0x3f: + /* aam */ + case 0xd4: + /* aad */ + case 0xd5: + if (record_arch_list_add_reg (I386_EAX_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* misc */ + /* nop */ + case 0x90: + if (prefixes & PREFIX_LOCK) + { + i386_record_pc -= 1; + goto no_support; + } + break; + + /* fwait */ + /* XXX */ + case 0x9b: + printf_unfiltered (_ + ("Process record doesn't support instruction fwait.\n")); + i386_record_pc -= 1; + goto no_support; + break; + + /* int3 */ + /* XXX */ + case 0xcc: + printf_unfiltered (_ + ("Process record doesn't support instruction int3.\n")); + i386_record_pc -= 1; + goto no_support; + break; + + /* int */ + /* XXX */ + case 0xcd: + { + int ret; + if (target_read_memory (i386_record_pc, &tmpu8, 1)) + { + if (record_debug) + { + printf_unfiltered (_ + ("Process record: error reading memory at addr 0x%s len = 1.\n"), + paddr_nz (i386_record_pc)); + } + return (-1); + } + i386_record_pc++; + if (tmpu8 != 0x80 + || gdbarch_tdep (gdbarch)->i386_intx80_record == NULL) + { + printf_unfiltered (_ + ("Process record doesn't support instruction int 0x%02x.\n"), + tmpu8); + i386_record_pc -= 2; + goto no_support; + } + ret = gdbarch_tdep (gdbarch)->i386_intx80_record (); + if (ret) + { + return (ret); + } + } + break; + + /* into */ + /* XXX */ + case 0xce: + printf_unfiltered (_ + ("Process record doesn't support instruction into.\n")); + i386_record_pc -= 1; + goto no_support; + break; + + /* cli */ + case 0xfa: + /* sti */ + case 0xfb: + break; + + /* bound */ + case 0x62: + printf_unfiltered (_ + ("Process record doesn't support instruction bound.\n")); + i386_record_pc -= 1; + goto no_support; + break; + + /* bswap reg */ + case 0x0fc8: + case 0x0fc9: + case 0x0fca: + case 0x0fcb: + case 0x0fcc: + case 0x0fcd: + case 0x0fce: + case 0x0fcf: + if (record_arch_list_add_reg (opcode & 7)) + { + return (-1); + } + break; + + /* salc */ + case 0xd6: + if (record_arch_list_add_reg (I386_EAX_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* loopnz */ + case 0xe0: + /* loopz */ + case 0xe1: + /* loop */ + case 0xe2: + /* jecxz */ + case 0xe3: + if (record_arch_list_add_reg (I386_ECX_REGNUM)) + { + return (-1); + } + break; + + /* wrmsr */ + case 0x0f30: + printf_unfiltered (_ + ("Process record doesn't support instruction wrmsr.\n")); + i386_record_pc -= 2; + goto no_support; + break; + + /* rdmsr */ + case 0x0f32: + printf_unfiltered (_ + ("Process record doesn't support instruction rdmsr.\n")); + i386_record_pc -= 2; + goto no_support; + break; + + /* rdtsc */ + case 0x0f31: + printf_unfiltered (_ + ("Process record doesn't support instruction rdtsc.\n")); + i386_record_pc -= 2; + goto no_support; + break; + + /* sysenter */ + case 0x0f34: + { + int ret; + if (gdbarch_tdep (gdbarch)->i386_sysenter_record == NULL) + { + printf_unfiltered (_ + ("Process record doesn't support instruction sysenter.\n")); + i386_record_pc -= 2; + goto no_support; + } + ret = gdbarch_tdep (gdbarch)->i386_sysenter_record (); + if (ret) + { + return (ret); + } + } + break; + + /* sysexit */ + case 0x0f35: + printf_unfiltered (_ + ("Process record doesn't support instruction sysexit.\n")); + i386_record_pc -= 2; + goto no_support; + break; + + /* cpuid */ + case 0x0fa2: + if (record_arch_list_add_reg (I386_EAX_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_ECX_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_EDX_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_EBX_REGNUM)) + { + return (-1); + } + break; + + /* hlt */ + case 0xf4: + printf_unfiltered (_ + ("Process record doesn't support instruction hlt.\n")); + i386_record_pc -= 1; + goto no_support; + break; + + case 0x0f00: + if (i386_record_modrm ()) + { + return (-1); + } + switch (reg) + { + /* sldt */ + case 0: + /* str */ + case 1: + if (mod == 3) + { + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + else + { + ot = OT_WORD; + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + break; + /* lldt */ + case 2: + /* ltr */ + case 3: + break; + /* verr */ + case 4: + /* verw */ + case 5: + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + default: + i386_record_pc -= 3; + opcode = opcode << 8 | modrm; + goto no_support; + break; + } + break; + + case 0x0f01: + if (i386_record_modrm ()) + { + return (-1); + } + switch (reg) + { + /* sgdt */ + case 0: + { + uint32_t addr; + + if (mod == 3) + { + i386_record_pc -= 3; + opcode = opcode << 8 | modrm; + goto no_support; + } + + if (override) + { + if (record_debug) + printf_unfiltered (_ + ("Process record ignores the memory change of instruction at address 0x%s because it can't get the value of the segment register.\n"), + paddr_nz (i386_record_pc)); +error("3"); + } + else + { + if (i386_record_lea_modrm_addr (&addr)) + { + return (-1); + } + if (record_arch_list_add_mem (addr, 2)) + { + return (-1); + } + addr += 2; + if (record_arch_list_add_mem (addr, 4)) + { + return (-1); + } + } + } + break; + case 1: + if (mod == 3) + { + switch (rm) + { + /* monitor */ + case 0: + break; + /* mwait */ + case 1: + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + default: + i386_record_pc -= 3; + opcode = opcode << 8 | modrm; + goto no_support; + break; + } + } + else + { + /* sidt */ + if (override) + { + if (record_debug) + printf_unfiltered (_ + ("Process record ignores the memory change of instruction at address 0x%s because it can't get the value of the segment register.\n"), + paddr_nz (i386_record_pc)); + } + else + { + uint32_t addr; + + if (i386_record_lea_modrm_addr (&addr)) + { + return (-1); + } + if (record_arch_list_add_mem (addr, 2)) + { + return (-1); + } + addr += 2; + if (record_arch_list_add_mem (addr, 4)) + { + return (-1); + } + } + } + break; + /* lgdt */ + case 2: + /* lidt */ + case 3: + /* invlpg */ + case 7: + default: + if (mod == 3) + { + i386_record_pc -= 3; + opcode = opcode << 8 | modrm; + goto no_support; + } + break; + /* smsw */ + case 4: + if (mod == 3) + { + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + else + { + ot = OT_WORD; + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + break; + /* lmsw */ + case 6: + break; + } + break; + + /* invd */ + case 0x0f08: + /* wbinvd */ + case 0x0f09: + break; + + /* arpl */ + case 0x63: + ot = dflag ? OT_LONG : OT_WORD; + if (i386_record_modrm ()) + { + return (-1); + } + if (mod != 3) + { + if (i386_record_lea_modrm ()) + { + return (-1); + } + } + else + { + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + /* lar */ + case 0x0f02: + /* lsl */ + case 0x0f03: + if (i386_record_modrm ()) + { + return (-1); + } + if (record_arch_list_add_reg (reg)) + { + return (-1); + } + if (record_arch_list_add_reg (I386_EFLAGS_REGNUM)) + { + return (-1); + } + break; + + case 0x0f18: + break; + + /* nop (multi byte) */ + case 0x0f19: + case 0x0f1a: + case 0x0f1b: + case 0x0f1c: + case 0x0f1d: + case 0x0f1e: + case 0x0f1f: + break; + + /* mov reg, crN */ + case 0x0f20: + /* mov crN, reg */ + case 0x0f22: + if (i386_record_modrm ()) + { + return (-1); + } + if ((modrm & 0xc0) != 0xc0) + { + i386_record_pc -= 2; + opcode = opcode << 8 | modrm; + goto no_support; + } + switch (reg) + { + case 0: + case 2: + case 3: + case 4: + case 8: + if (opcode & 2) + { + } + else + { + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + break; + default: + i386_record_pc -= 2; + opcode = opcode << 8 | modrm; + goto no_support; + break; + } + break; + + /* mov reg, drN */ + case 0x0f21: + /* mov drN, reg */ + case 0x0f23: + if (i386_record_modrm ()) + { + return (-1); + } + if ((modrm & 0xc0) != 0xc0 || reg == 4 || reg == 5 || reg >= 8) + { + i386_record_pc -= 2; + opcode = opcode << 8 | modrm; + goto no_support; + } + if (opcode & 2) + { + } + else + { + if (record_arch_list_add_reg (rm)) + { + return (-1); + } + } + break; + + /* clts */ + case 0x0f06: + break; + + /* MMX/SSE/SSE2/PNI support */ + /* XXX */ + + default: + if (opcode > 0xff) + { + i386_record_pc -= 2; + } + else + { + i386_record_pc -= 1; + } + goto no_support; + break; + } + +/* In the future, Maybe still need to deal with need_dasm */ + if (record_arch_list_add_reg (I386_EIP_REGNUM)) + { + return (-1); + } + if (record_arch_list_add_end (0)) + { + return (-1); + } + + return (0); + +no_support: + printf_unfiltered (_ + ("Process record doesn't support instruction 0x%02x at address 0x%s.\n"), + (unsigned int) (opcode), paddr_nz (i386_record_pc)); + return (-1); +} + static struct gdbarch * i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) @@ -2829,6 +5791,8 @@ i386_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) set_gdbarch_skip_permanent_breakpoint (gdbarch, i386_skip_permanent_breakpoint); + set_gdbarch_process_record (gdbarch, i386_process_record); + return gdbarch; } diff --git a/gdb/i386-tdep.h b/gdb/i386-tdep.h index d1366b6..a5ba588 100644 --- a/gdb/i386-tdep.h +++ b/gdb/i386-tdep.h @@ -106,6 +106,9 @@ struct gdbarch_tdep /* ISA-specific data types. */ struct type *i386_mmx_type; struct type *i386_sse_type; + + int (*i386_intx80_record) (void); + int (*i386_sysenter_record) (void); }; /* Floating-point registers. */ diff --git a/gdb/infrun.c b/gdb/infrun.c index eb2528c..95022a2 100644 --- a/gdb/infrun.c +++ b/gdb/infrun.c @@ -50,6 +50,8 @@ #include "mi/mi-common.h" #include "event-top.h" +#include "record.h" + /* Prototypes for local functions */ static void signals_info (char *, int); @@ -604,7 +606,8 @@ use_displaced_stepping (struct gdbarch *gdbarch) return (((can_use_displaced_stepping == can_use_displaced_stepping_auto && non_stop) || can_use_displaced_stepping == can_use_displaced_stepping_on) - && gdbarch_displaced_step_copy_insn_p (gdbarch)); + && gdbarch_displaced_step_copy_insn_p (gdbarch) + && !RECORD_IS_USED); } /* Clean out any stray displaced stepping state. */ @@ -1309,6 +1312,12 @@ proceed (CORE_ADDR addr, enum target_signal siggnal, int step) if (step < 0) stop_after_trap = 1; + /* When GDB resume the inferior, process record target doesn't need to + record the memory and register store operation of GDB. So set + record_not_record to 1. */ + if (RECORD_IS_USED) + record_not_record_set (); + if (addr == (CORE_ADDR) -1) { if (pc == stop_pc && breakpoint_here_p (pc) diff --git a/gdb/linux-record.c b/gdb/linux-record.c new file mode 100644 index 0000000..b5dae50 --- /dev/null +++ b/gdb/linux-record.c @@ -0,0 +1,2507 @@ +/* Process record and replay target code for GNU/Linux. + + Copyright (C) 2008 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 "target.h" +#include "regcache.h" +#include "record.h" +#include "linux-record.h" +#include <stdint.h> + +/* These macros are the values of the first argument of system call + "sys_ptrace". The values of these macros are gotten from Linux Kernel + source. */ + +#define RECORD_PTRACE_PEEKTEXT 1 +#define RECORD_PTRACE_PEEKDATA 2 +#define RECORD_PTRACE_PEEKUSR 3 + +/* These macros are the values of the first argument of system call + "sys_socketcall". The values of these macros are gotten from Linux Kernel + source. */ + +#define RECORD_SYS_SOCKET 1 +#define RECORD_SYS_BIND 2 +#define RECORD_SYS_CONNECT 3 +#define RECORD_SYS_LISTEN 4 +#define RECORD_SYS_ACCEPT 5 +#define RECORD_SYS_GETSOCKNAME 6 +#define RECORD_SYS_GETPEERNAME 7 +#define RECORD_SYS_SOCKETPAIR 8 +#define RECORD_SYS_SEND 9 +#define RECORD_SYS_RECV 10 +#define RECORD_SYS_SENDTO 11 +#define RECORD_SYS_RECVFROM 12 +#define RECORD_SYS_SHUTDOWN 13 +#define RECORD_SYS_SETSOCKOPT 14 +#define RECORD_SYS_GETSOCKOPT 15 +#define RECORD_SYS_SENDMSG 16 +#define RECORD_SYS_RECVMSG 17 + +/* These macros are the values of the first argument of system call + "sys_ipc". The values of these macros are gotten from Linux Kernel source. + */ + +#define RECORD_SEMOP 1 +#define RECORD_SEMGET 2 +#define RECORD_SEMCTL 3 +#define RECORD_SEMTIMEDOP 4 +#define RECORD_MSGSND 11 +#define RECORD_MSGRCV 12 +#define RECORD_MSGGET 13 +#define RECORD_MSGCTL 14 +#define RECORD_SHMAT 21 +#define RECORD_SHMDT 22 +#define RECORD_SHMGET 23 +#define RECORD_SHMCTL 24 + +/* These macros are the values of the first argument of system call + "sys_quotactl". The values of these macros are gotten from Linux Kernel + source. */ + +#define RECORD_Q_GETFMT 0x800004 +#define RECORD_Q_GETINFO 0x800005 +#define RECORD_Q_GETQUOTA 0x800007 +#define RECORD_Q_XGETQSTAT (('5'<<8)+(5)) +#define RECORD_Q_XGETQUOTA (('3'<<8)+(3)) + +/* When the architecture process record get a Linux syscall instruction, it + will get a Linux syscall number of this architecture and convert it to the + Linux syscall number "num" which is internal to GDB. + Most Linux syscalls across architectures in Linux would be similar and + mostly differ by sizes of types and structures. This sizes are put + to "tdep". + Record the values of the registers and memory that will be changed in + current system call. + Return -1 if something wrong. */ + +int +record_linux_system_call (int num, linux_record_tdep_t * tdep) +{ + uint32_t tmpu32; + + switch (num) + { + /* sys_restart_syscall */ + case 0: + break; + + /* sys_exit */ + case 1: + { + int q; + target_terminal_ours (); + q = + yquery (_ + ("The next instruction is syscall exit. It will make the program exit. Do you want to stop the program?")); + target_terminal_inferior (); + if (q) + { + return (1); + } + } + break; + + /* sys_fork */ + case 2: + break; + + /* sys_read */ + case 3: + { + uint32_t addr, count; + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & addr); + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & count); + if (record_arch_list_add_mem (addr, count)) + { + return (-1); + } + } + break; + + /* sys_write */ + case 4: + /* sys_open */ + case 5: + /* sys_close */ + case 6: + /* sys_waitpid */ + case 7: + /* sys_creat */ + case 8: + /* sys_link */ + case 9: + /* sys_unlink */ + case 10: + /* sys_execve */ + case 11: + /* sys_chdir */ + case 12: + /* sys_time */ + case 13: + /* sys_mknod */ + case 14: + /* sys_chmod */ + case 15: + /* sys_lchown16 */ + case 16: + /* sys_ni_syscall */ + case 17: + break; + + /* sys_stat */ + case 18: + /* sys_fstat */ + case 28: + /* sys_lstat */ + case 84: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size__old_kernel_stat)) + { + return (-1); + } + break; + + /* sys_lseek */ + case 19: + /* sys_getpid */ + case 20: + /* sys_mount */ + case 21: + /* sys_oldumount */ + case 22: + /* sys_setuid16 */ + case 23: + /* sys_getuid16 */ + case 24: + /* sys_stime */ + case 25: + break; + + /* sys_ptrace */ + case 26: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (tmpu32 == RECORD_PTRACE_PEEKTEXT + || tmpu32 == RECORD_PTRACE_PEEKDATA + || tmpu32 == RECORD_PTRACE_PEEKUSR) + { + regcache_raw_read (record_regcache, tdep->arg4, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, 4)) + { + return (-1); + } + } + break; + + /* sys_alarm */ + case 27: + /* sys_pause */ + case 29: + /* sys_utime */ + case 30: + /* sys_ni_syscall */ + case 31: + /* sys_ni_syscall */ + case 32: + /* sys_access */ + case 33: + /* sys_nice */ + case 34: + /* sys_ni_syscall */ + case 35: + /* sys_sync */ + case 36: + /* sys_kill */ + case 37: + /* sys_rename */ + case 38: + /* sys_mkdir */ + case 39: + /* sys_rmdir */ + case 40: + /* sys_dup */ + case 41: + /* sys_pipe */ + case 42: + break; + + /* sys_times */ + case 43: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_tms)) + { + return (-1); + } + break; + + /* sys_ni_syscall */ + case 44: + /* sys_brk */ + case 45: + /* sys_setgid16 */ + case 46: + /* sys_getgid16 */ + case 47: + /* sys_signal */ + case 48: + /* sys_geteuid16 */ + case 49: + /* sys_getegid16 */ + case 50: + /* sys_acct */ + case 51: + /* sys_umount */ + case 52: + /* sys_ni_syscall */ + case 53: + break; + + /* sys_ioctl */ + case 54: + /* XXX there need add a lot of support of other ioctl requests. */ + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (tmpu32 == tdep->ioctl_FIOCLEX || tmpu32 == tdep->ioctl_FIONCLEX + || tmpu32 == tdep->ioctl_FIONBIO || tmpu32 == tdep->ioctl_FIOASYNC + || tmpu32 == tdep->ioctl_TCSETS || tmpu32 == tdep->ioctl_TCSETSW + || tmpu32 == tdep->ioctl_TCSETSF || tmpu32 == tdep->ioctl_TCSETA + || tmpu32 == tdep->ioctl_TCSETAW || tmpu32 == tdep->ioctl_TCSETAF + || tmpu32 == tdep->ioctl_TCSBRK || tmpu32 == tdep->ioctl_TCXONC + || tmpu32 == tdep->ioctl_TCFLSH || tmpu32 == tdep->ioctl_TIOCEXCL + || tmpu32 == tdep->ioctl_TIOCNXCL + || tmpu32 == tdep->ioctl_TIOCSCTTY + || tmpu32 == tdep->ioctl_TIOCSPGRP || tmpu32 == tdep->ioctl_TIOCSTI + || tmpu32 == tdep->ioctl_TIOCSWINSZ + || tmpu32 == tdep->ioctl_TIOCMBIS || tmpu32 == tdep->ioctl_TIOCMBIC + || tmpu32 == tdep->ioctl_TIOCMSET + || tmpu32 == tdep->ioctl_TIOCSSOFTCAR + || tmpu32 == tdep->ioctl_TIOCCONS + || tmpu32 == tdep->ioctl_TIOCSSERIAL + || tmpu32 == tdep->ioctl_TIOCPKT || tmpu32 == tdep->ioctl_TIOCNOTTY + || tmpu32 == tdep->ioctl_TIOCSETD || tmpu32 == tdep->ioctl_TCSBRKP + || tmpu32 == tdep->ioctl_TIOCTTYGSTRUCT + || tmpu32 == tdep->ioctl_TIOCSBRK || tmpu32 == tdep->ioctl_TIOCCBRK + || tmpu32 == tdep->ioctl_TCSETS2 || tmpu32 == tdep->ioctl_TCSETSW2 + || tmpu32 == tdep->ioctl_TCSETSF2 + || tmpu32 == tdep->ioctl_TIOCSPTLCK + || tmpu32 == tdep->ioctl_TIOCSERCONFIG + || tmpu32 == tdep->ioctl_TIOCSERGWILD + || tmpu32 == tdep->ioctl_TIOCSERSWILD + || tmpu32 == tdep->ioctl_TIOCSLCKTRMIOS + || tmpu32 == tdep->ioctl_TIOCSERGETMULTI + || tmpu32 == tdep->ioctl_TIOCSERSETMULTI + || tmpu32 == tdep->ioctl_TIOCMIWAIT + || tmpu32 == tdep->ioctl_TIOCSHAYESESP) + { + /* Nothing to do. */ + } + else if (tmpu32 == tdep->ioctl_TCGETS || tmpu32 == tdep->ioctl_TCGETA + || tmpu32 == tdep->ioctl_TIOCGLCKTRMIOS) + { + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_termios)) + { + return (-1); + } + } + else if (tmpu32 == tdep->ioctl_TIOCGPGRP + || tmpu32 == tdep->ioctl_TIOCGSID) + { + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_pid_t)) + { + return (-1); + } + } + else if (tmpu32 == tdep->ioctl_TIOCOUTQ + || tmpu32 == tdep->ioctl_TIOCMGET + || tmpu32 == tdep->ioctl_TIOCGSOFTCAR + || tmpu32 == tdep->ioctl_FIONREAD + || tmpu32 == tdep->ioctl_TIOCINQ + || tmpu32 == tdep->ioctl_TIOCGETD + || tmpu32 == tdep->ioctl_TIOCGPTN + || tmpu32 == tdep->ioctl_TIOCSERGETLSR) + { + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_int)) + { + return (-1); + } + } + else if (tmpu32 == tdep->ioctl_TIOCGWINSZ) + { + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_winsize)) + { + return (-1); + } + } + else if (tmpu32 == tdep->ioctl_TIOCLINUX) + { + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_char)) + { + return (-1); + } + } + else if (tmpu32 == tdep->ioctl_TIOCGSERIAL) + { + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_serial_struct)) + { + return (-1); + } + } + else if (tmpu32 == tdep->ioctl_TCGETS2) + { + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_termios2)) + { + return (-1); + } + } + else if (tmpu32 == tdep->ioctl_FIOQSIZE) + { + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_loff_t)) + { + return (-1); + } + } + else if (tmpu32 == tdep->ioctl_TIOCGICOUNT) + { + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem + (tmpu32, tdep->size_serial_icounter_struct)) + { + return (-1); + } + } + else if (tmpu32 == tdep->ioctl_TIOCGHAYESESP) + { + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_hayes_esp_config)) + { + return (-1); + } + } + else if (tmpu32 == tdep->ioctl_TIOCSERGSTRUCT) + { + printf_unfiltered (_ + ("Process record and replay target doesn't support ioctl request TIOCSERGSTRUCT\n")); + return (1); + } + else + { + printf_unfiltered (_ + ("Process record and replay target doesn't support ioctl request 0x%08x.\n"), + tmpu32); + return (1); + } + break; + + /* sys_fcntl */ + case 55: + /* XXX */ + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + sys_fcntl: + if (tmpu32 == F_GETLK) + { + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_flock)) + { + return (-1); + } + } + break; + + /* sys_ni_syscall */ + case 56: + /* sys_setpgid */ + case 57: + /* sys_ni_syscall */ + case 58: + break; + + /* sys_olduname */ + case 59: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_oldold_utsname)) + { + return (-1); + } + break; + + /* sys_umask */ + case 60: + /* sys_chroot */ + case 61: + break; + + /* sys_ustat */ + case 62: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_ustat)) + { + return (-1); + } + break; + + /* sys_dup2 */ + case 63: + /* sys_getppid */ + case 64: + /* sys_getpgrp */ + case 65: + /* sys_setsid */ + case 66: + break; + + /* sys_sigaction */ + case 67: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_old_sigaction)) + { + return (-1); + } + break; + + /* sys_sgetmask */ + case 68: + /* sys_ssetmask */ + case 69: + /* sys_setreuid16 */ + case 70: + /* sys_setregid16 */ + case 71: + /* sys_sigsuspend */ + case 72: + break; + + /* sys_sigpending */ + case 73: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_old_sigset_t)) + { + return (-1); + } + break; + + /* sys_sethostname */ + case 74: + /* sys_setrlimit */ + case 75: + break; + + /* sys_old_getrlimit */ + case 76: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_rlimit)) + { + return (-1); + } + break; + + /* sys_getrusage */ + case 77: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_rusage)) + { + return (-1); + } + break; + + /* sys_gettimeofday */ + case 78: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_timeval)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_timezone)) + { + return (-1); + } + break; + + /* sys_settimeofday */ + case 79: + break; + + /* sys_getgroups16 */ + case 80: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_old_gid_t)) + { + return (-1); + } + break; + + /* sys_setgroups16 */ + case 81: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_old_gid_t)) + { + return (-1); + } + break; + + /* old_select */ + case 82: + { + struct sel_arg_struct + { + CORE_ADDR n; + CORE_ADDR inp; + CORE_ADDR outp; + CORE_ADDR exp; + CORE_ADDR tvp; + } sel; + + regcache_raw_read (record_regcache, tdep->arg1, + (gdb_byte *) & tmpu32); + if (tmpu32) + { + if (target_read_memory (tmpu32, (gdb_byte *) & sel, sizeof (sel))) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: error reading memory at addr = 0x%s len = %d.\n", + paddr_nz (tmpu32), sizeof (sel)); + } + return (-1); + } + if (record_arch_list_add_mem (sel.inp, tdep->size_fd_set)) + { + return (-1); + } + if (record_arch_list_add_mem (sel.outp, tdep->size_fd_set)) + { + return (-1); + } + if (record_arch_list_add_mem (sel.exp, tdep->size_fd_set)) + { + return (-1); + } + if (record_arch_list_add_mem (sel.tvp, tdep->size_timeval)) + { + return (-1); + } + } + } + break; + + /* sys_symlink */ + case 83: + break; + + /* sys_readlink */ + case 85: + { + uint32_t len; + regcache_raw_read (record_regcache, tdep->arg2, + (gdb_byte *) & tmpu32); + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & len); + if (record_arch_list_add_mem (tmpu32, len)) + { + return (-1); + } + } + break; + + /* sys_uselib */ + case 86: + /* sys_swapon */ + case 87: + break; + + /* sys_reboot */ + case 88: + { + int q; + target_terminal_ours (); + q = + yquery (_ + ("The next instruction is syscall reboot. It will restart the computer. Do you want to stop the program?")); + target_terminal_inferior (); + if (q) + { + return (1); + } + } + break; + + /* old_readdir */ + case 89: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_dirent)) + { + return (-1); + } + break; + + /* old_mmap */ + case 90: + break; + + /* sys_munmap */ + case 91: + { + int q; + uint32_t len; + + regcache_raw_read (record_regcache, tdep->arg1, + (gdb_byte *) & tmpu32); + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & len); + target_terminal_ours (); + q = + yquery (_ + ("The next instruction is syscall munmap. It will free the memory addr = 0x%s len = %d. It will make record target get error. Do you want to stop the program?"), + paddr_nz (tmpu32), len); + target_terminal_inferior (); + if (q) + { + return (1); + } + } + break; + + /* sys_truncate */ + case 92: + /* sys_ftruncate */ + case 93: + /* sys_fchmod */ + case 94: + /* sys_fchown16 */ + case 95: + /* sys_getpriority */ + case 96: + /* sys_setpriority */ + case 97: + /* sys_ni_syscall */ + case 98: + break; + + /* sys_statfs */ + case 99: + /* sys_fstatfs */ + case 100: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_statfs)) + { + return (-1); + } + break; + + /* sys_ioperm */ + case 101: + break; + + /* sys_socketcall */ + case 102: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + switch (tmpu32) + { + case RECORD_SYS_SOCKET: + case RECORD_SYS_BIND: + case RECORD_SYS_CONNECT: + case RECORD_SYS_LISTEN: + break; + case RECORD_SYS_ACCEPT: + case RECORD_SYS_GETSOCKNAME: + case RECORD_SYS_GETPEERNAME: + { + uint32_t a[3]; + regcache_raw_read (record_regcache, tdep->arg2, + (gdb_byte *) & tmpu32); + if (tmpu32) + { + if (target_read_memory (tmpu32, (gdb_byte *) a, sizeof (a))) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: error reading memory at addr = 0x%s len = %d.\n", + paddr_nz (tmpu32), sizeof (a)); + } + return (-1); + } + if (record_arch_list_add_mem (a[1], tdep->size_sockaddr)) + { + return (-1); + } + if (record_arch_list_add_mem (a[2], tdep->size_int)) + { + return (-1); + } + } + } + break; + + case RECORD_SYS_SOCKETPAIR: + { + uint32_t a[4]; + regcache_raw_read (record_regcache, tdep->arg2, + (gdb_byte *) & tmpu32); + if (tmpu32) + { + if (target_read_memory (tmpu32, (gdb_byte *) a, sizeof (a))) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: error reading memory at addr = 0x%s len = %d.\n", + paddr_nz (tmpu32), sizeof (a)); + } + return (-1); + } + if (record_arch_list_add_mem (a[3], tdep->size_int)) + { + return (-1); + } + } + } + break; + case RECORD_SYS_SEND: + case RECORD_SYS_SENDTO: + break; + case RECORD_SYS_RECV: + { + uint32_t a[3]; + regcache_raw_read (record_regcache, tdep->arg2, + (gdb_byte *) & tmpu32); + if (tmpu32) + { + if (target_read_memory (tmpu32, (gdb_byte *) a, sizeof (a))) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: error reading memory at addr = 0x%s len = %d.\n", + paddr_nz (tmpu32), sizeof (a)); + } + return (-1); + } + if (a[2]) + { + if (target_read_memory + (a[2], (gdb_byte *) & (a[2]), sizeof (a[2]))) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: error reading memory at addr = 0x%s len = %d.\n", + paddr_nz (a[2]), sizeof (a[2])); + } + return (-1); + } + if (record_arch_list_add_mem (a[1], a[2])) + { + return (-1); + } + } + } + } + break; + case RECORD_SYS_RECVFROM: + { + uint32_t a[6]; + regcache_raw_read (record_regcache, tdep->arg2, + (gdb_byte *) & tmpu32); + if (tmpu32) + { + if (target_read_memory (tmpu32, (gdb_byte *) a, sizeof (a))) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: error reading memory at addr = 0x%s len = %d.\n", + paddr_nz (tmpu32), sizeof (a)); + } + return (-1); + } + if (a[2]) + { + if (target_read_memory + (a[2], (gdb_byte *) & (a[2]), sizeof (a[2]))) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: error reading memory at addr = 0x%s len = %d.\n", + paddr_nz (a[2]), + sizeof (a[2])); + } + return (-1); + } + if (record_arch_list_add_mem (a[1], a[2])) + { + return (-1); + } + if (record_arch_list_add_mem (a[4], tdep->size_sockaddr)) + { + return (-1); + } + if (record_arch_list_add_mem (a[5], tdep->size_int)) + { + return (-1); + } + } + } + } + break; + case RECORD_SYS_SHUTDOWN: + case RECORD_SYS_SETSOCKOPT: + break; + case RECORD_SYS_GETSOCKOPT: + { + uint32_t a[5]; + uint32_t av; + + regcache_raw_read (record_regcache, tdep->arg2, + (gdb_byte *) & tmpu32); + if (tmpu32) + { + if (target_read_memory (tmpu32, (gdb_byte *) a, sizeof (a))) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: error reading memory at addr = 0x%s len = %d.\n", + paddr_nz (tmpu32), sizeof (a)); + } + return (-1); + } + if (a[4]) + { + if (target_read_memory + (a[4], (gdb_byte *) & av, sizeof (av))) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: error reading memory at addr = 0x%s len = %d.\n", + paddr_nz (a[4]), sizeof (av)); + } + return (-1); + } + if (record_arch_list_add_mem (a[3], av)) + { + return (-1); + } + if (record_arch_list_add_mem (a[4], tdep->size_int)) + { + return (-1); + } + } + } + } + break; + case RECORD_SYS_SENDMSG: + break; + case RECORD_SYS_RECVMSG: + { + uint32_t a[2], i; + struct record_msghdr + { + uint32_t msg_name; + uint32_t msg_namelen; + uint32_t msg_iov; + uint32_t msg_iovlen; + uint32_t msg_control; + uint32_t msg_controllen; + uint32_t msg_flags; + } rec; + struct record_iovec + { + uint32_t iov_base; + uint32_t iov_len; + } iov; + + regcache_raw_read (record_regcache, tdep->arg2, + (gdb_byte *) & tmpu32); + if (tmpu32) + { + if (target_read_memory (tmpu32, (gdb_byte *) a, sizeof (a))) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: error reading memory at addr = 0x%s len = %d.\n", + paddr_nz (tmpu32), sizeof (a)); + } + return (-1); + } + if (record_arch_list_add_mem (a[1], tdep->size_msghdr)) + { + return (-1); + } + if (a[1]) + { + if (target_read_memory + (a[1], (gdb_byte *) & rec, sizeof (rec))) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: error reading memory at addr = 0x%s len = %d.\n", + paddr_nz (a[1]), + sizeof (rec)); + } + return (-1); + } + if (record_arch_list_add_mem + (rec.msg_name, rec.msg_namelen)) + { + return (-1); + } + if (record_arch_list_add_mem + (rec.msg_control, rec.msg_controllen)) + { + return (-1); + } + if (rec.msg_iov) + { + for (i = 0; i < rec.msg_iovlen; i++) + { + if (target_read_memory + (rec.msg_iov, (gdb_byte *) & iov, + sizeof (iov))) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: error reading memory at addr = 0x%s len = %d.\n", + paddr_nz (rec. + msg_iov), + sizeof (iov)); + } + return (-1); + } + if (record_arch_list_add_mem + (iov.iov_base, iov.iov_len)) + { + return (-1); + } + rec.msg_iov += sizeof (struct record_iovec); + } + } + } + } + } + break; + default: + printf_unfiltered (_ + ("Process record and replay target doesn't support socketcall call 0x%08x\n"), + tmpu32); + return (-1); + break; + } + break; + + /* sys_syslog */ + case 103: + break; + + /* sys_setitimer */ + case 104: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_itimerval)) + { + return (-1); + } + break; + + /* sys_getitimer */ + case 105: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_itimerval)) + { + return (-1); + } + break; + + /* sys_newstat */ + case 106: + /* sys_newlstat */ + case 107: + /* sys_newfstat */ + case 108: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_stat)) + { + return (-1); + } + break; + + /* sys_uname */ + case 109: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_old_utsname)) + { + return (-1); + } + break; + + /* sys_iopl */ + case 110: + /* sys_vhangup */ + case 111: + /* sys_ni_syscall */ + case 112: + /* sys_vm86old */ + case 113: + break; + + /* sys_wait4 */ + case 114: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_int)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg4, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_rusage)) + { + return (-1); + } + break; + + /* sys_swapoff */ + case 115: + break; + + /* sys_sysinfo */ + case 116: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_sysinfo)) + { + return (-1); + } + break; + + /* sys_ipc */ + case 117: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + switch (tmpu32) + { + case RECORD_MSGRCV: + { + int32_t second; + uint32_t ptr; + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & second); + regcache_raw_read (record_regcache, tdep->arg5, + (gdb_byte *) & ptr); + if (record_arch_list_add_mem (ptr, second + tdep->size_long)) + { + return (-1); + } + } + break; + case RECORD_MSGCTL: + regcache_raw_read (record_regcache, tdep->arg5, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_msqid_ds)) + { + return (-1); + } + break; + case RECORD_SHMAT: + regcache_raw_read (record_regcache, tdep->arg4, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_ulong)) + { + return (-1); + } + break; + case RECORD_SHMCTL: + regcache_raw_read (record_regcache, tdep->arg5, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_shmid_ds)) + { + return (-1); + } + break; + } + break; + + /* sys_fsync */ + case 118: + /* sys_sigreturn */ + case 119: + /* sys_clone */ + case 120: + /* sys_setdomainname */ + case 121: + break; + + /* sys_newuname */ + case 122: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_new_utsname)) + { + return (-1); + } + break; + + /* sys_modify_ldt */ + case 123: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (tmpu32 == 0 || tmpu32 == 2) + { + uint32_t ptr, bytecount; + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & ptr); + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & bytecount); + if (record_arch_list_add_mem (ptr, bytecount)) + { + return (-1); + } + } + break; + + /* sys_adjtimex */ + case 124: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_timex)) + { + return (-1); + } + break; + + /* sys_mprotect */ + case 125: + break; + + /* sys_sigprocmask */ + case 126: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_old_sigset_t)) + { + return (-1); + } + break; + + /* sys_ni_syscall */ + case 127: + /* sys_init_module */ + case 128: + /* sys_delete_module */ + case 129: + /* sys_ni_syscall */ + case 130: + break; + + /* sys_quotactl */ + case 131: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + switch (tmpu32) + { + case RECORD_Q_GETFMT: + regcache_raw_read (record_regcache, tdep->arg4, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, 4)) + { + return (-1); + } + break; + case RECORD_Q_GETINFO: + regcache_raw_read (record_regcache, tdep->arg4, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_mem_dqinfo)) + { + return (-1); + } + break; + case RECORD_Q_GETQUOTA: + regcache_raw_read (record_regcache, tdep->arg4, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_if_dqblk)) + { + return (-1); + } + break; + case RECORD_Q_XGETQSTAT: + case RECORD_Q_XGETQUOTA: + regcache_raw_read (record_regcache, tdep->arg4, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_fs_quota_stat)) + { + return (-1); + } + break; + } + break; + + /* sys_getpgid */ + case 132: + /* sys_fchdir */ + case 133: + /* sys_bdflush */ + case 134: + break; + + /* sys_sysfs */ + case 135: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (tmpu32 == 2) + { + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & tmpu32); + /*XXX the size of memory is not very clear */ + if (record_arch_list_add_mem (tmpu32, 10)) + { + return (-1); + } + } + break; + + /* sys_personality */ + case 136: + /* sys_ni_syscall */ + case 137: + /* sys_setfsuid16 */ + case 138: + /* sys_setfsgid16 */ + case 139: + break; + + /* sys_llseek */ + case 140: + regcache_raw_read (record_regcache, tdep->arg4, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_loff_t)) + { + return (-1); + } + break; + + /* sys_getdents */ + case 141: + { + uint32_t count; + regcache_raw_read (record_regcache, tdep->arg2, + (gdb_byte *) & tmpu32); + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & count); + if (record_arch_list_add_mem (tmpu32, tdep->size_dirent * count)) + { + return (-1); + } + } + break; + + /* sys_select */ + case 142: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_fd_set)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_fd_set)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg4, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_fd_set)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg5, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_timeval)) + { + return (-1); + } + break; + + /* sys_flock */ + case 143: + /* sys_msync */ + case 144: + break; + + /* sys_readv */ + case 145: + { + uint32_t vec; + uint32_t vlen; + struct record_iovec + { + uint32_t iov_base; + uint32_t iov_len; + } iov; + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & vec); + if (vec) + { + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & vlen); + for (tmpu32 = 0; tmpu32 < vlen; tmpu32++) + { + if (target_read_memory + (vec, (gdb_byte *) & iov, sizeof (struct record_iovec))) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: error reading memory at addr = 0x%s len = %d.\n", + paddr_nz (vec), + sizeof (struct record_iovec)); + } + return (-1); + } + if (record_arch_list_add_mem (iov.iov_base, iov.iov_len)) + { + return (-1); + } + vec += sizeof (struct record_iovec); + } + } + } + break; + + /* sys_writev */ + case 146: + /* sys_getsid */ + case 147: + /* sys_fdatasync */ + case 148: + /* sys_sysctl */ + case 149: + /* sys_mlock */ + case 150: + /* sys_munlock */ + case 151: + /* sys_mlockall */ + case 152: + /* sys_munlockall */ + case 153: + /* sys_sched_setparam */ + case 154: + break; + + /* sys_sched_getparam */ + case 155: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_int)) + { + return (-1); + } + break; + + /* sys_sched_setscheduler */ + case 156: + /* sys_sched_getscheduler */ + case 157: + /* sys_sched_yield */ + case 158: + /* sys_sched_get_priority_max */ + case 159: + /* sys_sched_get_priority_min */ + case 160: + break; + + /* sys_sched_rr_get_interval */ + case 161: + /* sys_nanosleep */ + case 162: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_timespec)) + { + return (-1); + } + break; + + /* sys_mremap */ + case 163: + /* sys_setresuid16 */ + case 164: + break; + + /* sys_getresuid16 */ + case 165: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_old_uid_t)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_old_uid_t)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_old_uid_t)) + { + return (-1); + } + break; + + /* sys_vm86 */ + case 166: + /* sys_ni_syscall */ + case 167: + break; + + /* sys_poll */ + case 168: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (tmpu32) + { + uint32_t nfds; + regcache_raw_read (record_regcache, tdep->arg2, + (gdb_byte *) & nfds); + if (record_arch_list_add_mem (tmpu32, tdep->size_pollfd * nfds)) + { + return (-1); + } + } + break; + + /* sys_nfsservctl */ + case 169: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (tmpu32 == 7 || tmpu32 == 8) + { + uint32_t rsize; + if (tmpu32 == 7) + { + rsize = tdep->size_NFS_FHSIZE; + } + else + { + rsize = tdep->size_knfsd_fh; + } + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, rsize)) + { + return (-1); + } + } + break; + + /* sys_setresgid16 */ + case 170: + break; + + /* sys_getresgid16 */ + case 171: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_old_gid_t)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_old_gid_t)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_old_gid_t)) + { + return (-1); + } + break; + + /* sys_prctl */ + case 172: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + switch (tmpu32) + { + case 2: + regcache_raw_read (record_regcache, tdep->arg2, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_int)) + { + return (-1); + } + break; + case 16: + regcache_raw_read (record_regcache, tdep->arg2, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_TASK_COMM_LEN)) + { + return (-1); + } + break; + } + break; + + /* sys_rt_sigreturn */ + case 173: + break; + + /* sys_rt_sigaction */ + case 174: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_sigaction)) + { + return (-1); + } + break; + + /* sys_rt_sigprocmask */ + case 175: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_sigset_t)) + { + return (-1); + } + break; + + /* sys_rt_sigpending */ + case 176: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (tmpu32) + { + uint32_t sigsetsize; + regcache_raw_read (record_regcache, tdep->arg2, + (gdb_byte *) & sigsetsize); + if (record_arch_list_add_mem (tmpu32, sigsetsize)) + { + return (-1); + } + } + break; + + /* sys_rt_sigtimedwait */ + case 177: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_siginfo_t)) + { + return (-1); + } + break; + + /* sys_rt_sigqueueinfo */ + case 178: + /* sys_rt_sigsuspend */ + case 179: + break; + + /* sys_pread64 */ + case 180: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (tmpu32) + { + uint32_t count; + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & count); + if (record_arch_list_add_mem (tmpu32, count)) + { + return (-1); + } + } + break; + + /* sys_pwrite64 */ + case 181: + /* sys_chown16 */ + case 182: + break; + + /* sys_getcwd */ + case 183: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (tmpu32) + { + uint32_t size; + regcache_raw_read (record_regcache, tdep->arg2, + (gdb_byte *) & size); + if (record_arch_list_add_mem (tmpu32, size)) + { + return (-1); + } + } + break; + + /* sys_capget */ + case 184: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_cap_user_data_t)) + { + return (-1); + } + break; + + /* sys_capset */ + case 185: + break; + + /* sys_sigaltstack */ + case 186: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_stack_t)) + { + return (-1); + } + break; + + /* sys_sendfile */ + case 187: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_off_t)) + { + return (-1); + } + break; + + /* sys_ni_syscall */ + case 188: + /* sys_ni_syscall */ + case 189: + /* sys_vfork */ + case 190: + break; + + /* sys_getrlimit */ + case 191: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_rlimit)) + { + return (-1); + } + break; + + /* sys_mmap2 */ + case 192: + break; + + /* sys_truncate64 */ + case 193: + /* sys_ftruncate64 */ + case 194: + break; + + /* sys_stat64 */ + case 195: + /* sys_lstat64 */ + case 196: + /* sys_fstat64 */ + case 197: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_stat64)) + { + return (-1); + } + break; + + /* sys_lchown */ + case 198: + /* sys_getuid */ + case 199: + /* sys_getgid */ + case 200: + /* sys_geteuid */ + case 201: + /* sys_getegid */ + case 202: + /* sys_setreuid */ + case 203: + /* sys_setregid */ + case 204: + break; + + /* sys_getgroups */ + case 205: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (tmpu32) + { + int gidsetsize; + regcache_raw_read (record_regcache, tdep->arg1, + (gdb_byte *) & gidsetsize); + if (record_arch_list_add_mem + (tmpu32, tdep->size_gid_t * gidsetsize)) + { + return (-1); + } + } + break; + + /* sys_setgroups */ + case 206: + /* sys_fchown */ + case 207: + /* sys_setresuid */ + case 208: + break; + + /* sys_getresuid */ + case 209: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_uid_t)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_uid_t)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_uid_t)) + { + return (-1); + } + break; + + /* sys_setresgid */ + case 210: + break; + + /* sys_getresgid */ + case 211: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_gid_t)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_gid_t)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_gid_t)) + { + return (-1); + } + break; + + /* sys_chown */ + case 212: + /* sys_setuid */ + case 213: + /* sys_setgid */ + case 214: + /* sys_setfsuid */ + case 215: + /* sys_setfsgid */ + case 216: + /* sys_pivot_root */ + case 217: + break; + + /* sys_mincore */ + case 218: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_PAGE_SIZE)) + { + return (-1); + } + break; + + /* sys_madvise */ + case 219: + break; + + /* sys_getdents64 */ + case 220: + { + uint32_t count; + regcache_raw_read (record_regcache, tdep->arg2, + (gdb_byte *) & tmpu32); + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & count); + if (record_arch_list_add_mem (tmpu32, tdep->size_dirent64 * count)) + { + return (-1); + } + } + break; + + /* sys_fcntl64 */ + case 221: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + switch (tmpu32) + { + case F_GETLK64: + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_flock64)) + { + return (-1); + } + break; + case F_SETLK64: + case F_SETLKW64: + break; + default: + goto sys_fcntl; + break; + } + break; + + /* sys_ni_syscall */ + case 222: + /* sys_ni_syscall */ + case 223: + /* sys_gettid */ + case 224: + /* sys_readahead */ + case 225: + /* sys_setxattr */ + case 226: + /* sys_lsetxattr */ + case 227: + /* sys_fsetxattr */ + case 228: + break; + + /* sys_getxattr */ + case 229: + /* sys_lgetxattr */ + case 230: + /* sys_fgetxattr */ + case 231: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (tmpu32) + { + uint32_t size; + regcache_raw_read (record_regcache, tdep->arg4, + (gdb_byte *) & size); + if (record_arch_list_add_mem (tmpu32, size)) + { + return (-1); + } + } + break; + + /* sys_listxattr */ + case 232: + /* sys_llistxattr */ + case 233: + /* sys_flistxattr */ + case 234: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (tmpu32) + { + uint32_t size; + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & size); + if (record_arch_list_add_mem (tmpu32, size)) + { + return (-1); + } + } + break; + + /* sys_removexattr */ + case 235: + /* sys_lremovexattr */ + case 236: + /* sys_fremovexattr */ + case 237: + /* sys_tkill */ + case 238: + break; + + /* sys_sendfile64 */ + case 239: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_loff_t)) + { + return (-1); + } + break; + + /* sys_futex */ + case 240: + /* sys_sched_setaffinity */ + case 241: + break; + + /* sys_sched_getaffinity */ + case 242: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (tmpu32) + { + uint32_t len; + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & len); + if (record_arch_list_add_mem (tmpu32, len)) + { + return (-1); + } + } + break; + + /* sys_set_thread_area */ + case 243: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_int)) + { + return (-1); + } + break; + + /* sys_get_thread_area */ + case 244: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_user_desc)) + { + return (-1); + } + break; + + /* sys_io_setup */ + case 245: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_long)) + { + return (-1); + } + break; + + /* sys_io_destroy */ + case 246: + break; + + /* sys_io_getevents */ + case 247: + regcache_raw_read (record_regcache, tdep->arg4, (gdb_byte *) & tmpu32); + if (tmpu32) + { + int32_t nr; + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & nr); + if (record_arch_list_add_mem (tmpu32, nr * tdep->size_io_event)) + { + return (-1); + } + } + break; + + /* sys_io_submit */ + case 248: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (tmpu32) + { + int32_t i, nr; + uint32_t *iocbp; + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & nr); + iocbp = (uint32_t *) alloca (nr * tdep->size_int); + if (target_read_memory + (tmpu32, (gdb_byte *) iocbp, nr * tdep->size_int)) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: error reading memory at addr = 0x%s len = %d.\n", + paddr_nz (tmpu32), nr * tdep->size_int); + } + return (-1); + } + for (i = 0; i < nr; i++) + { + if (record_arch_list_add_mem (iocbp[i], tdep->size_iocb)) + { + return (-1); + } + } + } + break; + + /* sys_io_cancel */ + case 249: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_io_event)) + { + return (-1); + } + break; + + /* sys_fadvise64 */ + case 250: + /* sys_ni_syscall */ + case 251: + break; + + /* sys_exit_group */ + case 252: + { + int q; + target_terminal_ours (); + q = + yquery (_ + ("The next instruction is syscall exit_group. It will make the program exit. Do you want to stop the program?")); + target_terminal_inferior (); + if (q) + { + return (1); + } + } + break; + + /* sys_lookup_dcookie */ + case 253: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (tmpu32) + { + uint32_t len; + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & len); + if (record_arch_list_add_mem (tmpu32, len)) + { + return (-1); + } + } + break; + + /* sys_epoll_create */ + case 254: + /* sys_epoll_ctl */ + case 255: + break; + + /* sys_epoll_wait */ + case 256: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (tmpu32) + { + int32_t maxevents; + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & maxevents); + if (record_arch_list_add_mem + (tmpu32, maxevents * tdep->size_epoll_event)) + { + return (-1); + } + } + break; + + /* sys_remap_file_pages */ + case 257: + /* sys_set_tid_address */ + case 258: + break; + + /* sys_timer_create */ + case 259: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_int)) + { + return (-1); + } + break; + + /* sys_timer_settime */ + case 260: + regcache_raw_read (record_regcache, tdep->arg4, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_itimerspec)) + { + return (-1); + } + break; + + /* sys_timer_gettime */ + case 261: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_itimerspec)) + { + return (-1); + } + break; + + /* sys_timer_getoverrun */ + case 262: + /* sys_timer_delete */ + case 263: + /* sys_clock_settime */ + case 264: + break; + + /* sys_clock_gettime */ + case 265: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_timespec)) + { + return (-1); + } + break; + + /* sys_clock_getres */ + case 266: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_timespec)) + { + return (-1); + } + break; + + /* sys_clock_nanosleep */ + case 267: + regcache_raw_read (record_regcache, tdep->arg4, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_timespec)) + { + return (-1); + } + break; + + /* sys_statfs64 */ + case 268: + /* sys_fstatfs64 */ + case 269: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_statfs64)) + { + return (-1); + } + break; + + /* sys_tgkill */ + case 270: + /* sys_utimes */ + case 271: + /* sys_fadvise64_64 */ + case 272: + /* sys_ni_syscall */ + case 273: + /* sys_mbind */ + case 274: + break; + + /* sys_get_mempolicy */ + case 275: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_int)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (tmpu32) + { + uint32_t maxnode; + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & maxnode); + if (record_arch_list_add_mem (tmpu32, maxnode * tdep->size_long)) + { + return (-1); + } + } + break; + + /* sys_set_mempolicy */ + case 276: + /* sys_mq_open */ + case 277: + /* sys_mq_unlink */ + case 278: + /* sys_mq_timedsend */ + case 279: + break; + + /* sys_mq_timedreceive */ + case 280: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (tmpu32) + { + uint32_t msg_len; + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & msg_len); + if (record_arch_list_add_mem (tmpu32, msg_len)) + { + return (-1); + } + } + regcache_raw_read (record_regcache, tdep->arg4, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_int)) + { + return (-1); + } + break; + + /* sys_mq_notify */ + case 281: + break; + + /* sys_mq_getsetattr */ + case 282: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_mq_attr)) + { + return (-1); + } + break; + + /* sys_kexec_load */ + case 283: + break; + + /* sys_waitid */ + case 284: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_siginfo)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg5, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_rusage)) + { + return (-1); + } + break; + + /* sys_ni_syscall */ + case 285: + /* sys_add_key */ + case 286: + /* sys_request_key */ + case 287: + break; + + /* sys_keyctl */ + case 288: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (tmpu32 == 6 || tmpu32 == 11) + { + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & tmpu32); + if (tmpu32) + { + uint32_t buflen; + regcache_raw_read (record_regcache, tdep->arg4, + (gdb_byte *) & buflen); + if (record_arch_list_add_mem (tmpu32, buflen)) + { + return (-1); + } + } + } + break; + + /* sys_ioprio_set */ + case 289: + /* sys_ioprio_get */ + case 290: + /* sys_inotify_init */ + case 291: + /* sys_inotify_add_watch */ + case 292: + /* sys_inotify_rm_watch */ + case 293: + /* sys_migrate_pages */ + case 294: + /* sys_openat */ + case 295: + /* sys_mkdirat */ + case 296: + /* sys_mknodat */ + case 297: + /* sys_fchownat */ + case 298: + /* sys_futimesat */ + case 299: + break; + + /* sys_fstatat64 */ + case 300: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_stat64)) + { + return (-1); + } + break; + + /* sys_unlinkat */ + case 301: + /* sys_renameat */ + case 302: + /* sys_linkat */ + case 303: + /* sys_symlinkat */ + case 304: + break; + + /* sys_readlinkat */ + case 305: + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (tmpu32) + { + int32_t bufsiz; + regcache_raw_read (record_regcache, tdep->arg4, + (gdb_byte *) & bufsiz); + if (record_arch_list_add_mem (tmpu32, bufsiz)) + { + return (-1); + } + } + break; + + /* sys_fchmodat */ + case 306: + /* sys_faccessat */ + case 307: + break; + + /* sys_pselect6 */ + case 308: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_fd_set)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_fd_set)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg4, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_fd_set)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg5, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_timespec)) + { + return (-1); + } + break; + + /* sys_ppoll */ + case 309: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (tmpu32) + { + uint32_t nfds; + regcache_raw_read (record_regcache, tdep->arg2, + (gdb_byte *) & nfds); + if (record_arch_list_add_mem (tmpu32, tdep->size_pollfd * nfds)) + { + return (-1); + } + } + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_timespec)) + { + return (-1); + } + break; + + /* sys_unshare */ + case 310: + /* sys_set_robust_list */ + case 311: + break; + + /* sys_get_robust_list */ + case 312: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_int)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_int)) + { + return (-1); + } + break; + + /* sys_splice */ + case 313: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_loff_t)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg4, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_loff_t)) + { + return (-1); + } + break; + + /* sys_sync_file_range */ + case 314: + /* sys_tee */ + case 315: + /* sys_vmsplice */ + case 316: + break; + + /* sys_move_pages */ + case 317: + regcache_raw_read (record_regcache, tdep->arg5, (gdb_byte *) & tmpu32); + if (tmpu32) + { + uint32_t nr_pages; + regcache_raw_read (record_regcache, tdep->arg2, + (gdb_byte *) & nr_pages); + if (record_arch_list_add_mem (tmpu32, nr_pages * tdep->size_int)) + { + return (-1); + } + } + break; + + /* sys_getcpu */ + case 318: + regcache_raw_read (record_regcache, tdep->arg1, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_int)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_int)) + { + return (-1); + } + regcache_raw_read (record_regcache, tdep->arg3, (gdb_byte *) & tmpu32); + if (record_arch_list_add_mem (tmpu32, tdep->size_ulong * 2)) + { + return (-1); + } + break; + + /* sys_epoll_pwait */ + case 319: + regcache_raw_read (record_regcache, tdep->arg2, (gdb_byte *) & tmpu32); + if (tmpu32) + { + int32_t maxevents; + regcache_raw_read (record_regcache, tdep->arg3, + (gdb_byte *) & maxevents); + if (record_arch_list_add_mem + (tmpu32, maxevents * tdep->size_epoll_event)) + { + return (-1); + } + } + break; + + default: + printf_unfiltered (_ + ("Process record and replay target doesn't support syscall number 0x%08x\n"), + tmpu32); + return (-1); + break; + } + + return (0); +} diff --git a/gdb/linux-record.h b/gdb/linux-record.h new file mode 100644 index 0000000..b0dd048 --- /dev/null +++ b/gdb/linux-record.h @@ -0,0 +1,171 @@ +/* Process record and replay target code for GNU/Linux. + + Copyright (C) 2008 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 _LINUX_RECORD_H_ +#define _LINUX_RECORD_H_ + +typedef struct linux_record_tdep_s +{ + /* The size of the type that will be use in system call. */ + int size__old_kernel_stat; + int size_tms; + int size_loff_t; + int size_flock; + int size_oldold_utsname; + int size_ustat; + int size_old_sigaction; + int size_old_sigset_t; + int size_rlimit; + int size_rusage; + int size_timeval; + int size_timezone; + int size_old_gid_t; + int size_old_uid_t; + int size_fd_set; + int size_dirent; + int size_dirent64; + int size_statfs; + int size_statfs64; + int size_sockaddr; + int size_int; + int size_long; + int size_ulong; + int size_msghdr; + int size_itimerval; + int size_stat; + int size_old_utsname; + int size_sysinfo; + int size_msqid_ds; + int size_shmid_ds; + int size_new_utsname; + int size_timex; + int size_mem_dqinfo; + int size_if_dqblk; + int size_fs_quota_stat; + int size_timespec; + int size_pollfd; + int size_NFS_FHSIZE; + int size_knfsd_fh; + int size_TASK_COMM_LEN; + int size_sigaction; + int size_sigset_t; + int size_siginfo_t; + int size_cap_user_data_t; + int size_stack_t; + int size_off_t; + int size_stat64; + int size_gid_t; + int size_uid_t; + int size_PAGE_SIZE; + int size_flock64; + int size_user_desc; + int size_io_event; + int size_iocb; + int size_epoll_event; + int size_itimerspec; + int size_mq_attr; + int size_siginfo; + + int size_termios; + int size_termios2; + int size_pid_t; + int size_winsize; + int size_char; + int size_serial_struct; + int size_serial_icounter_struct; + int size_hayes_esp_config; + + /* the values of the second argument of system call "sys_ioctl". */ + int ioctl_TCGETS; + int ioctl_TCSETS; + int ioctl_TCSETSW; + int ioctl_TCSETSF; + int ioctl_TCGETA; + int ioctl_TCSETA; + int ioctl_TCSETAW; + int ioctl_TCSETAF; + int ioctl_TCSBRK; + int ioctl_TCXONC; + int ioctl_TCFLSH; + int ioctl_TIOCEXCL; + int ioctl_TIOCNXCL; + int ioctl_TIOCSCTTY; + int ioctl_TIOCGPGRP; + int ioctl_TIOCSPGRP; + int ioctl_TIOCOUTQ; + int ioctl_TIOCSTI; + int ioctl_TIOCGWINSZ; + int ioctl_TIOCSWINSZ; + int ioctl_TIOCMGET; + int ioctl_TIOCMBIS; + int ioctl_TIOCMBIC; + int ioctl_TIOCMSET; + int ioctl_TIOCGSOFTCAR; + int ioctl_TIOCSSOFTCAR; + int ioctl_FIONREAD; + int ioctl_TIOCINQ; + int ioctl_TIOCLINUX; + int ioctl_TIOCCONS; + int ioctl_TIOCGSERIAL; + int ioctl_TIOCSSERIAL; + int ioctl_TIOCPKT; + int ioctl_FIONBIO; + int ioctl_TIOCNOTTY; + int ioctl_TIOCSETD; + int ioctl_TIOCGETD; + int ioctl_TCSBRKP; + int ioctl_TIOCTTYGSTRUCT; + int ioctl_TIOCSBRK; + int ioctl_TIOCCBRK; + int ioctl_TIOCGSID; + int ioctl_TCGETS2; + int ioctl_TCSETS2; + int ioctl_TCSETSW2; + int ioctl_TCSETSF2; + int ioctl_TIOCGPTN; + int ioctl_TIOCSPTLCK; + int ioctl_FIONCLEX; + int ioctl_FIOCLEX; + int ioctl_FIOASYNC; + int ioctl_TIOCSERCONFIG; + int ioctl_TIOCSERGWILD; + int ioctl_TIOCSERSWILD; + int ioctl_TIOCGLCKTRMIOS; + int ioctl_TIOCSLCKTRMIOS; + int ioctl_TIOCSERGSTRUCT; + int ioctl_TIOCSERGETLSR; + int ioctl_TIOCSERGETMULTI; + int ioctl_TIOCSERSETMULTI; + int ioctl_TIOCMIWAIT; + int ioctl_TIOCGICOUNT; + int ioctl_TIOCGHAYESESP; + int ioctl_TIOCSHAYESESP; + int ioctl_FIOQSIZE; + + /* The number of the registers that use to be the arguments of system call. */ + int arg1; + int arg2; + int arg3; + int arg4; + int arg5; +} linux_record_tdep_t; + +extern int record_linux_system_call (int num, linux_record_tdep_t * tdep); + +#endif /* _LINUX_RECORD_H_ */ diff --git a/gdb/record.c b/gdb/record.c new file mode 100644 index 0000000..8e2d13d --- /dev/null +++ b/gdb/record.c @@ -0,0 +1,1272 @@ +/* Process record and replay target for GDB, the GNU debugger. + + Copyright (C) 2008 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 "target.h" +#include "gdbcmd.h" +#include "regcache.h" +#include "inferior.h" +#include "gdbthread.h" +#include "record.h" + +#include <signal.h> + +#define DEFAULT_RECORD_INSN_MAX_NUM 200000 + +int record_debug = 0; + +record_t record_first; +record_t *record_list = &record_first; +record_t *record_arch_list_head = NULL; +record_t *record_arch_list_tail = NULL; +struct regcache *record_regcache = NULL; + +/* 1 ask user. 0 auto delete the last record_t. */ +static int record_stop_at_limit = 1; +static int record_insn_max_num = DEFAULT_RECORD_INSN_MAX_NUM; +static int record_insn_num = 0; + +struct target_ops record_ops; +static int record_resume_step = 0; +static enum target_signal record_resume_siggnal; +static int record_get_sig = 0; +static sigset_t record_maskall; +static int record_not_record = 0; +int record_will_store_registers = 0; + +extern struct bp_location *bp_location_chain; + +/* The real beneath function pointers. */ +void (*record_beneath_to_resume) (ptid_t, int, enum target_signal); +ptid_t (*record_beneath_to_wait) (ptid_t, struct target_waitstatus *); +void (*record_beneath_to_store_registers) (struct regcache *, int regno); +LONGEST (*record_beneath_to_xfer_partial) (struct target_ops * ops, + enum target_object object, + const char *annex, + gdb_byte * readbuf, + const gdb_byte * writebuf, + ULONGEST offset, LONGEST len); +int (*record_beneath_to_insert_breakpoint) (struct bp_target_info *); +int (*record_beneath_to_remove_breakpoint) (struct bp_target_info *); + +static void +record_list_release (record_t * rec) +{ + record_t *tmp; + + if (!rec) + return; + + while (rec->next) + { + rec = rec->next; + } + + while (rec->prev) + { + tmp = rec; + rec = rec->prev; + if (tmp->type == record_reg) + { + xfree (tmp->u.reg.val); + } + else if (tmp->type == record_mem) + { + xfree (tmp->u.mem.val); + } + xfree (tmp); + } + + if (rec != &record_first) + { + xfree (rec); + } +} + +static void +record_list_release_next (void) +{ + record_t *rec = record_list; + record_t *tmp = rec->next; + rec->next = NULL; + while (tmp) + { + rec = tmp->next; + if (tmp->type == record_reg) + { + record_insn_num--; + } + else if (tmp->type == record_reg) + { + xfree (tmp->u.reg.val); + } + else if (tmp->type == record_mem) + { + xfree (tmp->u.mem.val); + } + xfree (tmp); + tmp = rec; + } +} + +static void +record_list_release_first (void) +{ + record_t *tmp = NULL; + enum record_type type; + + if (!record_first.next) + { + return; + } + + while (1) + { + type = record_first.next->type; + + if (type == record_reg) + { + xfree (record_first.next->u.reg.val); + } + else if (type == record_mem) + { + xfree (record_first.next->u.mem.val); + } + tmp = record_first.next; + record_first.next = tmp->next; + xfree (tmp); + + if (!record_first.next) + { + gdb_assert (record_insn_num == 1); + break; + } + + record_first.next->prev = &record_first; + + if (type == record_end) + { + break; + } + } + + record_insn_num--; +} + +/* Add a record_t to record_arch_list. */ +static void +record_arch_list_add (record_t * rec) +{ + if (record_arch_list_tail) + { + record_arch_list_tail->next = rec; + rec->prev = record_arch_list_tail; + record_arch_list_tail = rec; + } + else + { + record_arch_list_head = rec; + record_arch_list_tail = rec; + } +} + +/* Record the value of a register ("num") to record_arch_list. */ +int +record_arch_list_add_reg (int num) +{ + record_t *rec; + + if (record_debug > 1) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: add register num = %d to record list.\n", + num); + } + + rec = (record_t *) xmalloc (sizeof (record_t)); + rec->u.reg.val = (gdb_byte *) xmalloc (MAX_REGISTER_SIZE); + rec->prev = NULL; + rec->next = NULL; + rec->type = record_reg; + rec->u.reg.num = num; + + regcache_raw_read (record_regcache, num, rec->u.reg.val); + + record_arch_list_add (rec); + + return (0); +} + +/* Record the value of a region of memory whose address is "addr" and + length is "len" to record_arch_list. */ + +int +record_arch_list_add_mem (CORE_ADDR addr, int len) +{ + record_t *rec; + + if (record_debug > 1) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: add mem addr = 0x%s len = %d to record list.\n", + paddr_nz (addr), len); + } + + if (!addr) + { + return (0); + } + + rec = (record_t *) xmalloc (sizeof (record_t)); + rec->u.mem.val = (gdb_byte *) xmalloc (len); + rec->prev = NULL; + rec->next = NULL; + rec->type = record_mem; + rec->u.mem.addr = addr; + rec->u.mem.len = len; + + if (target_read_memory (addr, rec->u.mem.val, len)) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: error reading memory at addr = 0x%s len = %d.\n", + paddr_nz (addr), len); + } + xfree (rec->u.mem.val); + xfree (rec); + return (-1); + } + + record_arch_list_add (rec); + + return (0); +} + +/* Add a record_end type record_t to record_arch_list. */ +int +record_arch_list_add_end (int need_dasm) +{ + record_t *rec; + + if (record_debug > 1) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: add end need_dasm = %d to arch list.\n", + need_dasm); + } + + rec = (record_t *) xmalloc (sizeof (record_t)); + rec->prev = NULL; + rec->next = NULL; + rec->type = record_end; + + rec->u.need_dasm = need_dasm; + + record_arch_list_add (rec); + + return (0); +} + +static void +record_check_insn_num (int set_terminal) +{ + if (record_insn_max_num) + { + gdb_assert (record_insn_num <= record_insn_max_num); + if (record_insn_num == record_insn_max_num) + { + /* Ask user what to do */ + if (record_stop_at_limit) + { + int q; + if (set_terminal) + target_terminal_ours (); + q = yquery (_("Do you want to auto delete previous execute log entries when record/replay buffer becomes full (record-stop-at-limit)?")); + if (set_terminal) + target_terminal_inferior (); + if (q) + { + record_stop_at_limit = 0; + } + else + { + error (_("Process record: inferior program stopped.")); + } + } + } + } +} + +/* Before inferior step (when GDB record the running message, inferior + only can step), GDB will call this function to record the values to + record_list. This function will call gdbarch_process_record to + record the running message of inferior and set them to + record_arch_list, and add it to record_list. */ + +static void +record_message_cleanups (void *ignore) +{ + record_list_release (record_arch_list_tail); + set_executing (inferior_ptid, 0); + normal_stop (); +} + +void +record_message (struct gdbarch *gdbarch) +{ + int ret; + struct cleanup *old_cleanups = make_cleanup (record_message_cleanups, 0); + + /* Check record_insn_num. */ + record_check_insn_num (1); + + record_arch_list_head = NULL; + record_arch_list_tail = NULL; + record_regcache = get_current_regcache (); + + ret = gdbarch_process_record (gdbarch, + regcache_read_pc (record_regcache)); + if (ret > 0) + error (_("Process record: inferior program stopped.")); + if (ret < 0) + error (_("Process record: failed to record execution log.")); + + discard_cleanups (old_cleanups); + + record_list->next = record_arch_list_head; + record_arch_list_head->prev = record_list; + record_list = record_arch_list_tail; + + if (record_insn_num == record_insn_max_num && record_insn_max_num) + { + record_list_release_first (); + } + else + { + record_insn_num++; + } +} + +/* Things to clean up if we QUIT out of function that set + record_not_record. */ +static void +record_not_record_cleanups (void *ignore) +{ + record_not_record = 0; +} + +void +record_not_record_set (void) +{ + struct cleanup *old_cleanups = make_cleanup (record_not_record_cleanups, 0); + record_not_record = 1; +} + +static void +record_open (char *name, int from_tty) +{ + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, "Process record: record_open\n"); + } + + /* check exec */ + if (!target_has_execution) + { + error (_("Process record: the program is not being run.")); + } + if (non_stop) + { + error (_("Process record target can't debug inferior in non-stop mode (non-stop).")); + } + if (target_async_permitted) + { + error (_("Process record target can't debug inferior in asynchronous mode (target-async).")); + } + + if (!gdbarch_process_record_p (current_gdbarch)) + { + error (_("Process record: the current architecture doesn't support record function.")); + } + + /* Check if record target is already running */ + if (RECORD_IS_USED) + { + if (!nquery + (_("Process record target already running, do you want to delete the old record log?"))) + { + return; + } + } + + push_target (&record_ops); + + /* Reset */ + record_insn_num = 0; + record_list = &record_first; + record_list->next = NULL; +} + +static void +record_close (int quitting) +{ + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, "Process record: record_close\n"); + } + record_list_release (record_list); +} + +static void +record_resume (ptid_t ptid, int step, enum target_signal siggnal) +{ + record_resume_step = step; + record_resume_siggnal = siggnal; + + if (!RECORD_IS_REPLAY) + { + record_message (current_gdbarch); + record_beneath_to_resume (ptid, 1, siggnal); + } +} + +static void +record_sig_handler (int signo) +{ + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, "Process record: get a signal\n"); + } + record_resume_step = 1; + record_get_sig = 1; +} + +static void +record_wait_cleanups (void *ignore) +{ + if (execution_direction == EXEC_REVERSE) + { + if (record_list->next) + { + record_list = record_list->next; + } + } + else + { + record_list = record_list->prev; + } + set_executing (inferior_ptid, 0); + normal_stop (); +} + +/* record_wait + In replay mode, this function examines the recorded log and + determines where to stop. */ + +static ptid_t +record_wait (ptid_t ptid, struct target_waitstatus *status) +{ + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: record_wait record_resume_step = %d\n", + record_resume_step); + } + + if (!RECORD_IS_REPLAY) + { + if (record_resume_step) + { + /* This is a single step. */ + return record_beneath_to_wait (ptid, status); + } + else + { + if (record_resume_step) + { + /* This is a single step. */ + return record_beneath_to_wait (ptid, status); + } + else + { + /* This is not a single step. */ + ptid_t ret; + int is_breakpoint = 1; + CORE_ADDR pc; + int pc_is_read = 0; + struct bp_location *bl; + struct breakpoint *b; + + do + { + ret = record_beneath_to_wait (ptid, status); + + if (status->kind == TARGET_WAITKIND_STOPPED + && status->value.sig == TARGET_SIGNAL_TRAP) + { + /* Check if there is a breakpoint. */ + pc_is_read = 0; + registers_changed (); + for (bl = bp_location_chain; bl; bl = bl->global_next) + { + b = bl->owner; + gdb_assert (b); + if (b->enable_state != bp_enabled + && b->enable_state != bp_permanent) + continue; + if (!pc_is_read) + { + pc = + regcache_read_pc (get_thread_regcache (ret)); + pc_is_read = 1; + } + switch (b->type) + { + default: + if (bl->address == pc) + { + goto breakpoint; + } + break; + + case bp_watchpoint: + /*XXX teawater: I still not very clear how to deal with it. */ + goto breakpoint; + break; + + case bp_catchpoint: + gdb_assert (b->ops != NULL + && b->ops->breakpoint_hit != NULL); + if (b->ops->breakpoint_hit (b)) + { + goto breakpoint; + } + break; + + case bp_hardware_watchpoint: + case bp_read_watchpoint: + case bp_access_watchpoint: + if (STOPPED_BY_WATCHPOINT (0)) + { + goto breakpoint; + } + break; + } + } + + /* There is not a breakpoint. */ + record_message (current_gdbarch); + record_beneath_to_resume (ptid, 1, + record_resume_siggnal); + continue; + } + + is_breakpoint = 0; + + breakpoint: + /* Add gdbarch_decr_pc_after_break to pc because pc will + be break at address add gdbarch_decr_pc_after_break + when inferior non-step execute. */ + if (is_breakpoint) + { + CORE_ADDR decr_pc_after_break = + gdbarch_decr_pc_after_break (current_gdbarch); + if (decr_pc_after_break) + { + if (!pc_is_read) + { + pc = + regcache_read_pc (get_thread_regcache (ret)); + } + regcache_write_pc (get_thread_regcache (ret), + pc + decr_pc_after_break); + } + } + + break; + } + while (1); + + return ret; + } + } + } + else + { + struct sigaction act, old_act; + int need_dasm = 0; + struct regcache *regcache = get_current_regcache (); + int continue_flag = 1; + int first_record_end = 1; + struct cleanup *old_cleanups = make_cleanup (record_wait_cleanups, 0); + CORE_ADDR tmp_pc; + + status->kind = TARGET_WAITKIND_STOPPED; + + /* Check breakpoint when forward execute. */ + if (execution_direction == EXEC_FORWARD) + { + tmp_pc = regcache_read_pc (regcache); + if (breakpoint_inserted_here_p (tmp_pc)) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: break at 0x%s.\n", + paddr_nz (tmp_pc)); + } + if (gdbarch_decr_pc_after_break (get_regcache_arch (regcache)) + && !record_resume_step) + { + regcache_write_pc (regcache, + tmp_pc + + gdbarch_decr_pc_after_break + (get_regcache_arch (regcache))); + } + goto replay_out; + } + } + + record_get_sig = 0; + act.sa_handler = record_sig_handler; + act.sa_mask = record_maskall; + act.sa_flags = SA_RESTART; + if (sigaction (SIGINT, &act, &old_act)) + { + perror_with_name (_("Process record: sigaction failed")); + } + /* If GDB is in terminal_inferior, it will not get the signal. + And in GDB replay mode, GDB doesn't need to in terminal_inferior + because inferior will not executed. + Then set it to terminal_ours to make GDB get the signal. */ + target_terminal_ours (); + + /* In EXEC_FORWARD mode, record_list point to the tail of prev + instruction. */ + if (execution_direction == EXEC_FORWARD && record_list->next) + { + record_list = record_list->next; + } + + /* Loop over the record_list, looking for the next place to + stop. */ + do + { + /* Check for beginning and end of log. */ + if (execution_direction == EXEC_REVERSE + && record_list == &record_first) + { + /* Hit beginning of record log in reverse. */ + status->kind = TARGET_WAITKIND_NO_HISTORY; + break; + } + if (execution_direction != EXEC_REVERSE && !record_list->next) + { + /* Hit end of record log going forward. */ + status->kind = TARGET_WAITKIND_NO_HISTORY; + break; + } + + /* set ptid, register and memory according to record_list */ + if (record_list->type == record_reg) + { + /* reg */ + gdb_byte reg[MAX_REGISTER_SIZE]; + if (record_debug > 1) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: record_reg 0x%s to inferior num = %d.\n", + paddr_nz ((CORE_ADDR)record_list), + record_list->u.reg.num); + } + regcache_cooked_read (regcache, record_list->u.reg.num, reg); + regcache_cooked_write (regcache, record_list->u.reg.num, + record_list->u.reg.val); + memcpy (record_list->u.reg.val, reg, MAX_REGISTER_SIZE); + } + else if (record_list->type == record_mem) + { + /* mem */ + gdb_byte *mem = alloca (record_list->u.mem.len); + if (record_debug > 1) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: record_mem 0x%s to inferior addr = 0x%s len = %d.\n", + paddr_nz ((CORE_ADDR)record_list), + paddr_nz (record_list->u.mem.addr), + record_list->u.mem.len); + } + if (target_read_memory + (record_list->u.mem.addr, mem, record_list->u.mem.len)) + { + error (_("Process record: error reading memory at addr = 0x%s len = %d."), + paddr_nz (record_list->u.mem.addr), + record_list->u.mem.len); + } + if (target_write_memory + (record_list->u.mem.addr, record_list->u.mem.val, + record_list->u.mem.len)) + { + error (_ + ("Process record: error writing memory at addr = 0x%s len = %d."), + paddr_nz (record_list->u.mem.addr), + record_list->u.mem.len); + } + memcpy (record_list->u.mem.val, mem, record_list->u.mem.len); + } + else + { + if (record_debug > 1) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: record_end 0x%s to inferior need_dasm = %d.\n", + paddr_nz ((CORE_ADDR)record_list), + record_list->u.need_dasm); + } + + if (execution_direction == EXEC_FORWARD) + { + need_dasm = record_list->u.need_dasm; + } + if (need_dasm) + { + gdbarch_process_record_dasm (current_gdbarch); + } + + if (first_record_end && execution_direction == EXEC_REVERSE) + { + /* When reverse excute, the first record_end is the part of + current instruction. */ + first_record_end = 0; + } + else + { + /* In EXEC_REVERSE mode, this is the record_end of prev + instruction. + In EXEC_FORWARD mode, this is the record_end of current + instruction. */ + /* step */ + if (record_resume_step) + { + if (record_debug > 1) + { + fprintf_unfiltered (gdb_stdlog, "Process record: step.\n"); + } + continue_flag = 0; + } + + /* check breakpoint */ + tmp_pc = regcache_read_pc (regcache); + if (breakpoint_inserted_here_p (tmp_pc)) + { + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + "Process record: break at 0x%s.\n", + paddr_nz (tmp_pc)); + } + if (gdbarch_decr_pc_after_break (get_regcache_arch (regcache)) + && execution_direction == EXEC_FORWARD + && !record_resume_step) + { + regcache_write_pc (regcache, + tmp_pc + + gdbarch_decr_pc_after_break + (get_regcache_arch (regcache))); + } + continue_flag = 0; + } + } + if (execution_direction == EXEC_REVERSE) + { + need_dasm = record_list->u.need_dasm; + } + } + +next: + if (continue_flag) + { + if (execution_direction == EXEC_REVERSE) + { + if (record_list->prev) + record_list = record_list->prev; + } + else + { + if (record_list->next) + record_list = record_list->next; + } + } + } + while (continue_flag); + + if (sigaction (SIGALRM, &old_act, NULL)) + { + perror_with_name (_("Process record: sigaction failed")); + } + +replay_out: + if (record_get_sig) + { + status->value.sig = TARGET_SIGNAL_INT; + } + else + { + status->value.sig = TARGET_SIGNAL_TRAP; + } + + discard_cleanups (old_cleanups); + } + + return inferior_ptid; +} + +static void +record_disconnect (struct target_ops *target, char *args, int from_tty) +{ + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, "Process record: record_disconnect\n"); + } + unpush_target (&record_ops); + target_disconnect (args, from_tty); +} + +static void +record_detach (struct target_ops *ops, char *args, int from_tty) +{ + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, "Process record: record_detach\n"); + } + unpush_target (&record_ops); + target_detach (args, from_tty); +} + +static void +record_mourn_inferior (struct target_ops *ops) +{ + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, "Process record: record_mourn_inferior\n"); + } + unpush_target (&record_ops); + target_mourn_inferior (); +} + +/* Close process record target before kill the inferior process. */ +static void +record_kill (void) +{ + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, "Process record: record_kill\n"); + } + unpush_target (&record_ops); + target_kill (); +} + +/* Record registers change (by user or by GDB) to list as an instruction. */ +static void +record_registers_change (struct regcache *regcache, int regnum) +{ + /* Check record_insn_num. */ + record_check_insn_num (0); + + record_arch_list_head = NULL; + record_arch_list_tail = NULL; + + record_regcache = get_current_regcache (); + + if (regnum < 0) + { + int i; + for (i = 0; i < gdbarch_num_regs (get_regcache_arch (regcache)); i++) + { + if (record_arch_list_add_reg (i)) + { + record_list_release (record_arch_list_tail); + error (_("Process record: failed to record execution log.")); + } + } + } + else + { + if (record_arch_list_add_reg (regnum)) + { + record_list_release (record_arch_list_tail); + error (_("Process record: failed to record execution log.")); + } + } + if (record_arch_list_add_end (0)) + { + record_list_release (record_arch_list_tail); + error (_("Process record: failed to record execution log.")); + } + record_list->next = record_arch_list_head; + record_arch_list_head->prev = record_list; + record_list = record_arch_list_tail; + + if (record_insn_num == record_insn_max_num && record_insn_max_num) + { + record_list_release_first (); + } + else + { + record_insn_num++; + } +} + +static void +record_store_registers (struct regcache *regcache, int regno) +{ + if (!record_not_record) + { + if (RECORD_IS_REPLAY) + { + int n; + struct cleanup *old_cleanups; + + /* Let user choice if he want to write register or not. */ + if (regno < 0) + { + n = + nquery (_ + ("Becuse GDB is in replay mode, changing the value of a register will make the execute log unusable from this point onward. Change all register?")); + } + else + { + n = + nquery (_ + ("Becuse GDB is in replay mode, changing the value of a register will make the execute log unusable from this point onward. Change register %s?"), + gdbarch_register_name (get_regcache_arch (regcache), + regno)); + } + + if (!n) + { + /* Invalidate the value of regcache that set in function + "regcache_raw_write". */ + if (regno < 0) + { + int i; + for (i = 0; + i < gdbarch_num_regs (get_regcache_arch (regcache)); + i++) + { + regcache_invalidate (regcache, i); + } + } + else + { + regcache_invalidate (regcache, regno); + } + + error (_("Process record canceled the operation.")); + } + + /* Destroy the record from here forward. */ + record_list_release_next (); + } + + record_registers_change (regcache, regno); + } + record_beneath_to_store_registers (regcache, regno); +} + +/* record_xfer_partial -- behavior is conditional on RECORD_IS_REPLAY. + In replay mode, we cannot write memory unles we are willing to + invalidate the record/replay log from this point forward. */ + +static LONGEST +record_xfer_partial (struct target_ops *ops, enum target_object object, + const char *annex, gdb_byte * readbuf, + const gdb_byte * writebuf, ULONGEST offset, LONGEST len) +{ + if (!record_not_record + && (object == TARGET_OBJECT_MEMORY + || object == TARGET_OBJECT_RAW_MEMORY) && writebuf) + { + if (RECORD_IS_REPLAY) + { + /* Let user choice if he want to write memory or not. */ + if (!nquery (_("Because GDB is in replay mode, writing to memory will make the execute log unusable from this point onward. Write memory at address 0x%s?"), + paddr_nz (offset))) + { + return -1; + } + + /* Destroy the record from here forward. */ + record_list_release_next (); + } + + /* Check record_insn_num */ + record_check_insn_num (0); + + /* Record registers change to list as an instruction. */ + record_arch_list_head = NULL; + record_arch_list_tail = NULL; + if (record_arch_list_add_mem (offset, len)) + { + record_list_release (record_arch_list_tail); + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + _ + ("Process record: failed to record execution log.")); + } + return -1; + } + if (record_arch_list_add_end (0)) + { + record_list_release (record_arch_list_tail); + if (record_debug) + { + fprintf_unfiltered (gdb_stdlog, + _ + ("Process record: failed to record execution log.")); + } + return -1; + } + record_list->next = record_arch_list_head; + record_arch_list_head->prev = record_list; + record_list = record_arch_list_tail; + + if (record_insn_num == record_insn_max_num && record_insn_max_num) + { + record_list_release_first (); + } + else + { + record_insn_num++; + } + } + + return record_beneath_to_xfer_partial (ops, object, annex, readbuf, + writebuf, offset, len); +} + +/* record_insert_breakpoint + record_remove_breakpoint + Behavior is conditional on RECORD_IS_REPLAY. + We will not actually insert or remove breakpoints when replaying. */ + +static int +record_insert_breakpoint (struct bp_target_info *bp_tgt) +{ + if (!RECORD_IS_REPLAY) + { + return record_beneath_to_insert_breakpoint (bp_tgt); + } + + return 0; +} + +static int +record_remove_breakpoint (struct bp_target_info *bp_tgt) +{ + if (!RECORD_IS_REPLAY) + { + return record_beneath_to_remove_breakpoint (bp_tgt); + } + + return 0; +} + +static int +record_can_execute_reverse (void) +{ + return 1; +} + +static void +init_record_ops (void) +{ + record_ops.to_shortname = "record"; + record_ops.to_longname = "Process record and replay target"; + record_ops.to_doc = + "Log program while executing and replay execution from log."; + record_ops.to_open = record_open; + record_ops.to_close = record_close; + record_ops.to_resume = record_resume; + record_ops.to_wait = record_wait; + record_ops.to_disconnect = record_disconnect; + record_ops.to_detach = record_detach; + record_ops.to_mourn_inferior = record_mourn_inferior; + record_ops.to_kill = record_kill; + record_ops.to_create_inferior = find_default_create_inferior; /* Make record suppport command "run". */ + record_ops.to_store_registers = record_store_registers; + record_ops.to_xfer_partial = record_xfer_partial; + record_ops.to_insert_breakpoint = record_insert_breakpoint; + record_ops.to_remove_breakpoint = record_remove_breakpoint; + record_ops.to_can_execute_reverse = record_can_execute_reverse; + record_ops.to_stratum = record_stratum; + record_ops.to_magic = OPS_MAGIC; +} + +static void +show_record_debug (struct ui_file *file, int from_tty, + struct cmd_list_element *c, const char *value) +{ + fprintf_filtered (file, _("Debugging of process record target is %s.\n"), value); +} + +/* cmd_record_start -- alias for "target record". */ + +static void +cmd_record_start (char *args, int from_tty) +{ + execute_command ("target record", from_tty); +} + +/* cmd_record_delete -- truncate the record log from the present point + of replay until the end. */ + +static void +cmd_record_delete (char *args, int from_tty) +{ + if (RECORD_IS_USED) + { + if (RECORD_IS_REPLAY) + { + if (!from_tty || query (_("Delete the log from this point forward and begin to record the running message at current PC?"))) + { + record_list_release_next (); + } + } + else + { + printf_unfiltered (_("Already at end of record list.\n")); + } + + } + else + { + printf_unfiltered (_("Process record is not started.\n")); + } +} + +/* cmd_record_stop -- implement the "stoprecord" command. */ + +static void +cmd_record_stop (char *args, int from_tty) +{ + if (RECORD_IS_USED) + { + if (!record_list || !from_tty || query (_("Delete recorded log and stop recording?"))) + { + unpush_target (&record_ops); + } + } + else + { + printf_unfiltered (_("Process record is not started.\n")); + } +} + +/* set_record_insn_max_num -- set upper limit of record log size. */ + +static void +set_record_insn_max_num (char *args, int from_tty, struct cmd_list_element *c) +{ + if (record_insn_num > record_insn_max_num && record_insn_max_num) + { + printf_unfiltered (_("Record instructions number is bigger than record instructions max number. Auto delete the first ones?\n")); + + while (record_insn_num > record_insn_max_num) + { + record_list_release_first (); + } + } +} + +/* show_record_insn_number -- print the current index + into the record log (number of insns recorded so far). */ + +static void +show_record_insn_number (char *ignore, int from_tty) +{ + printf_unfiltered (_("Record instruction number is %d.\n"), + record_insn_num); +} + +void +_initialize_record (void) +{ + /* Init record_maskall. */ + if (sigfillset (&record_maskall) == -1) + { + perror_with_name (_("Process record: sigfillset failed")); + } + + /* Init record_first. */ + record_first.prev = NULL; + record_first.next = NULL; + record_first.type = record_end; + record_first.u.need_dasm = 0; + + init_record_ops (); + add_target (&record_ops); + + add_setshow_zinteger_cmd ("record", no_class, &record_debug, + _("Set debugging of record/replay feature."), + _("Show debugging of record/replay feature."), + _ + ("When enabled, debugging output for record/replay feature is displayed."), + NULL, show_record_debug, &setdebuglist, + &showdebuglist); + + add_com ("record", class_obscure, cmd_record_start, + _("Abbreviated form of \"target record\" command.")); + + add_com_alias ("rec", "record", class_obscure, 1); + + /* XXX: I try to use some simple commands such as "disconnect" and + "detach" to support this functions. But these commands all have + other affect to GDB such as call function "no_shared_libraries". + So I add special commands to GDB. */ + add_com ("delrecord", class_obscure, cmd_record_delete, + _("Delete the rest of execution log and start recording it anew.")); + add_com_alias ("dr", "delrecord", class_obscure, 1); + add_com ("stoprecord", class_obscure, cmd_record_stop, + _("Stop the record/replay target.")); + add_com_alias ("sr", "stoprecord", class_obscure, 1); + + /* Record instructions number limit command. */ + add_setshow_boolean_cmd ("record-stop-at-limit", no_class, + &record_stop_at_limit, + _("Set whether record/replay stop when record/replay buffer becomes full."), + _("Show whether record/replay stop when record/replay buffer becomes full."), _("\ +Enable is default value.\n\ +When enabled, if the record/replay buffer becomes full,\n\ +ask user what to do.\n\ +When disabled, if the record/replay buffer becomes full,\n\ +delete it and start new recording."), NULL, NULL, &setlist, &showlist); + add_setshow_zinteger_cmd ("record-insn-number-max", no_class, + &record_insn_max_num, + _("Set record/replay buffer limit."), + _("Show record/replay buffer limit."), _("\ +Set the maximum number of instructions to be stored in the\n\ +record/replay buffer. Zero means unlimited (default 200000)."), + set_record_insn_max_num, + NULL, &setlist, &showlist); + add_info ("record-insn-number", show_record_insn_number, _("\ +Show the current number of instructions in the record/replay buffer.")); +} diff --git a/gdb/record.h b/gdb/record.h new file mode 100644 index 0000000..fd69273 --- /dev/null +++ b/gdb/record.h @@ -0,0 +1,98 @@ +/* Process record and replay target for GDB, the GNU debugger. + + Copyright (C) 2008 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 _RECORD_H_ +#define _RECORD_H_ + +#define RECORD_IS_USED \ + (current_target.beneath == &record_ops) +#define RECORD_IS_REPLAY \ + (record_list->next || execution_direction == EXEC_REVERSE) + +typedef struct record_reg_s +{ + int num; + gdb_byte *val; +} record_reg_t; + +typedef struct record_mem_s +{ + CORE_ADDR addr; + int len; + gdb_byte *val; +} record_mem_t; + +enum record_type +{ + record_end = 0, + record_reg, + record_mem +}; + +/* This is the core struct of record function. + An entity of record_t is a record of the value change of a register + ("record_reg") or a part of memory ("record_mem"). And Each instruction must + has a record_t ("record_end") that point out this is the last record_t of + this instruction. + Each record_t is linked to "record_list" by "prev" and "next". + */ +typedef struct record_s +{ + struct record_s *prev; + struct record_s *next; + enum record_type type; + union + { + /* reg */ + record_reg_t reg; + /* mem */ + record_mem_t mem; + /* end */ + int need_dasm; + } u; +} record_t; + +extern int record_debug; +extern record_t *record_list; +extern record_t *record_arch_list_head; +extern record_t *record_arch_list_tail; +extern struct regcache *record_regcache; + +extern struct target_ops record_ops; + +extern int record_arch_list_add_reg (int num); +extern int record_arch_list_add_mem (CORE_ADDR addr, int len); +extern int record_arch_list_add_end (int need_dasm); +extern void record_message (struct gdbarch *gdbarch); +extern void record_not_record_set (void); + +extern void (*record_beneath_to_resume) (ptid_t, int, enum target_signal); +extern ptid_t (*record_beneath_to_wait) (ptid_t, struct target_waitstatus *); +extern void (*record_beneath_to_store_registers) (struct regcache *, int regno); +extern LONGEST (*record_beneath_to_xfer_partial) (struct target_ops * ops, + enum target_object object, + const char *annex, + gdb_byte * readbuf, + const gdb_byte * writebuf, + ULONGEST offset, + LONGEST len); +extern int (*record_beneath_to_insert_breakpoint) (struct bp_target_info *); +extern int (*record_beneath_to_remove_breakpoint) (struct bp_target_info *); + +#endif /* _RECORD_H_ */ diff --git a/gdb/target.c b/gdb/target.c index c16b55c..a636598 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -41,6 +41,7 @@ #include "target-descriptions.h" #include "gdbthread.h" #include "solib.h" +#include "record.h" static void target_info (char *, int); @@ -405,6 +406,12 @@ update_current_target (void) if (!current_target.FIELD) \ current_target.FIELD = (TARGET)->FIELD + record_beneath_to_resume = NULL; + record_beneath_to_store_registers = NULL; + record_beneath_to_xfer_partial = NULL; + record_beneath_to_insert_breakpoint = NULL; + record_beneath_to_remove_breakpoint = NULL; + for (t = target_stack; t; t = t->beneath) { INHERIT (to_shortname, t); @@ -494,6 +501,35 @@ update_current_target (void) /* Do not inherit to_memory_map. */ /* Do not inherit to_flash_erase. */ /* Do not inherit to_flash_done. */ + + /* Set pointers to functions in the target beneath us. */ + if (t != &record_ops) + { + if (!record_beneath_to_resume) + { + record_beneath_to_resume = t->to_resume; + } + if (!record_beneath_to_wait) + { + record_beneath_to_wait = t->to_wait; + } + if (!record_beneath_to_store_registers) + { + record_beneath_to_store_registers = t->to_store_registers; + } + if (!record_beneath_to_xfer_partial) + { + record_beneath_to_xfer_partial = t->to_xfer_partial; + } + if (!record_beneath_to_insert_breakpoint) + { + record_beneath_to_insert_breakpoint = t->to_insert_breakpoint; + } + if (!record_beneath_to_remove_breakpoint) + { + record_beneath_to_remove_breakpoint = t->to_remove_breakpoint; + } + } } #undef INHERIT diff --git a/gdb/target.h b/gdb/target.h index 2722945..7244f94 100644 --- a/gdb/target.h +++ b/gdb/target.h @@ -62,7 +62,8 @@ enum strata file_stratum, /* Executable files, etc */ core_stratum, /* Core dump files */ process_stratum, /* Executing processes */ - thread_stratum /* Executing threads */ + thread_stratum, /* Executing threads */ + record_stratum /* Support record debugging */ }; enum thread_control_capabilities diff --git a/gdb/testsuite/configure b/gdb/testsuite/configure index ef35b13..1f39ba9 100755 --- a/gdb/testsuite/configure +++ b/gdb/testsuite/configure @@ -3131,7 +3131,7 @@ done - ac_config_files="$ac_config_files Makefile gdb.ada/Makefile gdb.arch/Makefile gdb.asm/Makefile gdb.base/Makefile gdb.cp/Makefile gdb.disasm/Makefile gdb.dwarf2/Makefile gdb.fortran/Makefile gdb.server/Makefile gdb.java/Makefile gdb.mi/Makefile gdb.modula2/Makefile gdb.objc/Makefile gdb.opt/Makefile gdb.pascal/Makefile gdb.python/Makefile gdb.threads/Makefile gdb.trace/Makefile gdb.xml/Makefile" + ac_config_files="$ac_config_files Makefile gdb.ada/Makefile gdb.arch/Makefile gdb.asm/Makefile gdb.base/Makefile gdb.cp/Makefile gdb.disasm/Makefile gdb.dwarf2/Makefile gdb.fortran/Makefile gdb.server/Makefile gdb.java/Makefile gdb.mi/Makefile gdb.modula2/Makefile gdb.objc/Makefile gdb.opt/Makefile gdb.pascal/Makefile gdb.python/Makefile gdb.twreverse/Makefile gdb.threads/Makefile gdb.trace/Makefile gdb.xml/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure @@ -3699,6 +3699,7 @@ do "gdb.opt/Makefile" ) CONFIG_FILES="$CONFIG_FILES gdb.opt/Makefile" ;; "gdb.pascal/Makefile" ) CONFIG_FILES="$CONFIG_FILES gdb.pascal/Makefile" ;; "gdb.python/Makefile" ) CONFIG_FILES="$CONFIG_FILES gdb.python/Makefile" ;; + "gdb.twreverse/Makefile" ) CONFIG_FILES="$CONFIG_FILES gdb.twreverse/Makefile" ;; "gdb.threads/Makefile" ) CONFIG_FILES="$CONFIG_FILES gdb.threads/Makefile" ;; "gdb.trace/Makefile" ) CONFIG_FILES="$CONFIG_FILES gdb.trace/Makefile" ;; "gdb.xml/Makefile" ) CONFIG_FILES="$CONFIG_FILES gdb.xml/Makefile" ;; diff --git a/gdb/testsuite/configure.ac b/gdb/testsuite/configure.ac index 3d8fae4..4446115 100644 --- a/gdb/testsuite/configure.ac +++ b/gdb/testsuite/configure.ac @@ -117,5 +117,5 @@ AC_OUTPUT([Makefile \ gdb.fortran/Makefile gdb.server/Makefile \ gdb.java/Makefile gdb.mi/Makefile gdb.modula2/Makefile \ gdb.objc/Makefile gdb.opt/Makefile gdb.pascal/Makefile \ - gdb.python/Makefile \ + gdb.python/Makefile gdb.twreverse/Makefile \ gdb.threads/Makefile gdb.trace/Makefile gdb.xml/Makefile]) diff --git a/gdb/testsuite/gdb.twreverse/machinestate.exp b/gdb/testsuite/gdb.twreverse/machinestate.exp index 3b4b0f6..f27674a 100644 --- a/gdb/testsuite/gdb.twreverse/machinestate.exp +++ b/gdb/testsuite/gdb.twreverse/machinestate.exp @@ -35,6 +35,7 @@ # shared library variable # heap variable (pointer)... # overlay variables... +# Test forward replay # @@ -71,7 +72,7 @@ gdb_continue_to_breakpoint "end of main" ".*/$srcfile:$endmain.*" # Now run backward to each of several points where data is changed. # -# Module global variable +# Module global variable, reverse set breakloc [gdb_get_line_number \ "module_global_state: set breakpoint here" $srcfile] @@ -81,12 +82,12 @@ gdb_test "reverse-continue" ".*/$srcfile:$breakloc.*" "reverse to $breakloc" gdb_test "print aglobal" ".* = 0$newline" "module global reverse-breakpoint" gdb_test "step" ".* module global post-change .*" "" -gdb_test "print aglobal" ".* = 1$newline" "module global forward" +gdb_test "print aglobal" ".* = 1$newline" "module global forward past bp" gdb_test "reverse-step" ".*$newline$breakloc.*" "" -gdb_test "print aglobal" ".* = 0$newline" "module global reverse-step" +gdb_test "print aglobal" ".* = 0$newline" "module global reverse-step to bp" -# Module static variable +# Module static variable, reverse set breakloc [gdb_get_line_number \ "module_static_state: set breakpoint here" $srcfile] @@ -100,7 +101,7 @@ gdb_test "print astatic" ".* = 1$newline" "module static forward" gdb_test "reverse-step" ".*$newline$breakloc.*" "" gdb_test "print astatic" ".* = 0$newline" "module static reverse-step" -# Function static variable +# Function static variable, reverse set breakloc [gdb_get_line_number \ "function_static_state: set breakpoint here" $srcfile] @@ -114,7 +115,7 @@ gdb_test "print a" ".* = 1$newline" "function static forward" gdb_test "reverse-step" ".*$newline$breakloc.*" "" gdb_test "print a" ".* = 0$newline" "function static reverse-step" -# Auto variable +# Auto variable, reverse set breakloc [gdb_get_line_number \ "auto_state: set breakpoint here" $srcfile] @@ -128,7 +129,7 @@ gdb_test "print a" ".* = 1$newline" "auto var forward" gdb_test "reverse-step" ".*$newline$breakloc.*" "" gdb_test "print a" ".* = 0$newline" "auto var reverse-step" -# Register variable +# Register variable, reverse set breakloc [gdb_get_line_number \ "register_state: set breakpoint here" $srcfile] @@ -138,7 +139,89 @@ gdb_test "reverse-continue" ".*/$srcfile:$breakloc.*" "reverse to $breakloc" gdb_test "print a" ".* = 0$newline" "register var reverse-breakpoint" gdb_test "step" ".* register post-change .*" "" -gdb_test "print a" ".* = 1$newline" "register var forward" +gdb_test "print a" ".* = 1$newline" "register var step post-change" gdb_test "reverse-step" ".*$newline$breakloc.*" "" -gdb_test "print a" ".* = 0$newline" "register var reverse-step" +gdb_test "print a" ".* = 0$newline" "register var reverse step-to" + +# Proceed to beginning of main + +gdb_test "tbreak $beginmain" ".*/$srcfile, line $beginmain.*" "" +gdb_test "reverse-continue" ".*/$srcfile:$beginmain.*" "reverse to main" + +# Now repeat tests while replaying forward. + +# Register variable, forward + +set breakloc [gdb_get_line_number \ + "register_state: set breakpoint here" $srcfile] + +gdb_test "tbreak $breakloc" ".*/$srcfile, line $breakloc.*" "" +gdb_test "continue" ".*/$srcfile:$breakloc.*" "forward to $breakloc" + +gdb_test "print a" ".* = 0$newline" "register var forward-breakpoint" +gdb_test "reverse-step" ".*hide.*" "" +gdb_test "step" ".*$newline$breakloc.*" "" +gdb_test "print a" ".* = 0$newline" "register var forward step-to" +gdb_test "step" ".* register post-change .*" "" +gdb_test "print a" ".* = 1$newline" "register var step post-change" + +# Auto variable, forward + +set breakloc [gdb_get_line_number \ + "auto_state: set breakpoint here" $srcfile] + +gdb_test "tbreak $breakloc" ".*/$srcfile, line $breakloc.*" "" +gdb_test "continue" ".*/$srcfile:$breakloc.*" "forward to $breakloc" + +gdb_test "print a" ".* = 0$newline" "auto var forward-breakpoint" +gdb_test "reverse-step" ".*hide.*" "" +gdb_test "step" ".*$newline$breakloc.*" "" +gdb_test "print a" ".* = 0$newline" "auto var forward step-to" +gdb_test "step" ".* auto post-change .*" "" +gdb_test "print a" ".* = 1$newline" "auto var step post-change" + +# Function static variable, forward + +set breakloc [gdb_get_line_number \ + "function_static_state: set breakpoint here" $srcfile] + +gdb_test "tbreak $breakloc" ".*/$srcfile, line $breakloc.*" "" +gdb_test "continue" ".*/$srcfile:$breakloc.*" "forward to $breakloc" + +gdb_test "print a" ".* = 0$newline" "function static forward-breakpoint" +gdb_test "reverse-step" ".*hide.*" "" +gdb_test "step" ".*$newline$breakloc.*" "" +gdb_test "print a" ".* = 0$newline" "function static forward step-to" +gdb_test "step" ".* function static post-change .*" "" +gdb_test "print a" ".* = 1$newline" "function static step post-change" + +# Module static variable, forward + +set breakloc [gdb_get_line_number \ + "module_static_state: set breakpoint here" $srcfile] + +gdb_test "tbreak $breakloc" ".*/$srcfile, line $breakloc.*" "" +gdb_test "continue" ".*/$srcfile:$breakloc.*" "forward to $breakloc" + +gdb_test "print astatic" ".* = 0$newline" "module static forward-breakpoint" +gdb_test "reverse-step" ".*hide.*" "" +gdb_test "step" ".*$newline$breakloc.*" "" +gdb_test "print astatic" ".* = 0$newline" "module static forward step-to" +gdb_test "step" ".* module static post-change .*" "" +gdb_test "print astatic" ".* = 1$newline" "module static step post-change" + +# Module global variable, forward + +set breakloc [gdb_get_line_number \ + "module_global_state: set breakpoint here" $srcfile] + +gdb_test "tbreak $breakloc" ".*/$srcfile, line $breakloc.*" "" +gdb_test "continue" ".*/$srcfile:$breakloc.*" "forward to $breakloc" + +gdb_test "print aglobal" ".* = 0$newline" "module global forward-breakpoint" +gdb_test "reverse-step" ".*hide.*" "" +gdb_test "step" ".*$newline$breakloc.*" "" +gdb_test "print aglobal" ".* = 0$newline" "module global forward step-to" +gdb_test "step" ".* module global post-change .*" "" +gdb_test "print aglobal" ".* = 1$newline" "module global step post-change" |