From: Doug Evans Date: Tue, 30 Jun 2009 16:35:25 +0000 (+0000) Subject: Add h/w watchpoint support to x86-linux, win32-i386. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=aa5ca48fad05503860c254806750c8a304ec9fc5;p=binutils-gdb.git Add h/w watchpoint support to x86-linux, win32-i386. * Makefile.in (SFILES): Add i386-low.c (i386_low_h): Define. (i386-low.o): Add dependencies. (linux-x86-low.o): Add i386-low.h dependency. (win32-i386-low.o): Ditto. * i386-low.c: New file. * i386-low.h: New file. * configure.srv (i[34567]86-*-cygwin*): Add i386-low.o to srv_tgtobj. (i[34567]86-*-linux*, i[34567]86-*-mingw*, x86_64-*-linux*): Ditto. * linux-low.c (linux_add_process): Initialize arch_private. (linux_remove_process): Free arch_private. (add_lwp): Initialize arch_private. (delete_lwp): Free arch_private. (linux_resume_one_lwp): Call the_low_target.prepare_to_resume if provided. * linux-low.h (process_info_private): New member arch_private. (lwp_info): New member arch_private. (linux_target_ops): New members new_process, new_thread, prepare_to_resume. (ptid_of): New macro. * linux-x86-low.c: Include stddef.h, i386-low.h. (arch_process_info): New struct. (arch_lwp_info): New struct. (x86_linux_dr_get, x86_linux_dr_set): New functions. (i386_dr_low_set_addr, i386_dr_low_set_control): New functions. (i386_dr_low_get_status): New function. (x86_insert_point, x86_remove_point): New functions. (x86_stopped_by_watchpoint): New function. (x86_stopped_data_address): New function. (x86_linux_new_process, x86_linux_new_thread): New functions. (x86_linux_prepare_to_resume): New function. (the_low_target): Add entries for insert_point, remove_point, stopped_by_watchpoint, stopped_data_address, new_process, new_thread, prepare_to_resume. * server.c (debug_hw_points): New global. (monitor_show_help): Document set debug-hw-points. (handle_query): Process "set debug-hw-points". * server.h (debug_hw_points): Declare. (paddress): Declare. * utils.c (NUMCELLS, CELLSIZE): New macros. (get_sell, xsnprintf, paddress): New functions. * win32-arm-low.c (the_low_target): Add entries for insert_point, remove_point, stopped_by_watchpoint, stopped_data_address. * win32-i386-low.c: Include i386-low.h. (debug_reg_state): Replaces dr. (i386_dr_low_set_addr, i386_dr_low_set_control): New functions. (i386_dr_low_get_status): New function. (i386_insert_point, i386_remove_point): New functions. (i386_stopped_by_watchpoint): New function. (i386_stopped_data_address): New function. (i386_initial_stuff): Update. (get_thread_context,set_thread_context,i386_thread_added): Update. (the_low_target): Add entries for insert_point, remove_point, stopped_by_watchpoint, stopped_data_address. * win32-low.c (win32_insert_watchpoint): New function. (win32_remove_watchpoint): New function. (win32_stopped_by_watchpoint): New function. (win32_stopped_data_address): New function. (win32_target_ops): Add entries for insert_watchpoint, remove_watchpoint, stopped_by_watchpoint, stopped_data_address. * win32-low.h (win32_target_ops): New members insert_point, remove_point, stopped_by_watchpoint, stopped_data_address. --- diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index 8cb2d92f499..86fe1746ae6 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,70 @@ +2009-06-30 Doug Evans + Pierre Muller + + Add h/w watchpoint support to x86-linux, win32-i386. + * Makefile.in (SFILES): Add i386-low.c + (i386_low_h): Define. + (i386-low.o): Add dependencies. + (linux-x86-low.o): Add i386-low.h dependency. + (win32-i386-low.o): Ditto. + * i386-low.c: New file. + * i386-low.h: New file. + * configure.srv (i[34567]86-*-cygwin*): Add i386-low.o to srv_tgtobj. + (i[34567]86-*-linux*, i[34567]86-*-mingw*, x86_64-*-linux*): Ditto. + * linux-low.c (linux_add_process): Initialize arch_private. + (linux_remove_process): Free arch_private. + (add_lwp): Initialize arch_private. + (delete_lwp): Free arch_private. + (linux_resume_one_lwp): Call the_low_target.prepare_to_resume if + provided. + * linux-low.h (process_info_private): New member arch_private. + (lwp_info): New member arch_private. + (linux_target_ops): New members new_process, new_thread, + prepare_to_resume. + (ptid_of): New macro. + * linux-x86-low.c: Include stddef.h, i386-low.h. + (arch_process_info): New struct. + (arch_lwp_info): New struct. + (x86_linux_dr_get, x86_linux_dr_set): New functions. + (i386_dr_low_set_addr, i386_dr_low_set_control): New functions. + (i386_dr_low_get_status): New function. + (x86_insert_point, x86_remove_point): New functions. + (x86_stopped_by_watchpoint): New function. + (x86_stopped_data_address): New function. + (x86_linux_new_process, x86_linux_new_thread): New functions. + (x86_linux_prepare_to_resume): New function. + (the_low_target): Add entries for insert_point, remove_point, + stopped_by_watchpoint, stopped_data_address, new_process, new_thread, + prepare_to_resume. + * server.c (debug_hw_points): New global. + (monitor_show_help): Document set debug-hw-points. + (handle_query): Process "set debug-hw-points". + * server.h (debug_hw_points): Declare. + (paddress): Declare. + * utils.c (NUMCELLS, CELLSIZE): New macros. + (get_sell, xsnprintf, paddress): New functions. + * win32-arm-low.c (the_low_target): Add entries for insert_point, + remove_point, stopped_by_watchpoint, stopped_data_address. + * win32-i386-low.c: Include i386-low.h. + (debug_reg_state): Replaces dr. + (i386_dr_low_set_addr, i386_dr_low_set_control): New functions. + (i386_dr_low_get_status): New function. + (i386_insert_point, i386_remove_point): New functions. + (i386_stopped_by_watchpoint): New function. + (i386_stopped_data_address): New function. + (i386_initial_stuff): Update. + (get_thread_context,set_thread_context,i386_thread_added): Update. + (the_low_target): Add entries for insert_point, + remove_point, stopped_by_watchpoint, stopped_data_address. + * win32-low.c (win32_insert_watchpoint): New function. + (win32_remove_watchpoint): New function. + (win32_stopped_by_watchpoint): New function. + (win32_stopped_data_address): New function. + (win32_target_ops): Add entries for insert_watchpoint, + remove_watchpoint, stopped_by_watchpoint, stopped_data_address. + * win32-low.h (win32_target_ops): New members insert_point, + remove_point, stopped_by_watchpoint, stopped_data_address. + 2009-06-25 Pedro Alves * server.c (process_serial_event): Re-return unsupported, not diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in index f395d9edb6a..37d6095922e 100644 --- a/gdb/gdbserver/Makefile.in +++ b/gdb/gdbserver/Makefile.in @@ -104,7 +104,7 @@ SFILES= $(srcdir)/gdbreplay.c $(srcdir)/inferiors.c \ $(srcdir)/thread-db.c $(srcdir)/utils.c \ $(srcdir)/linux-arm-low.c $(srcdir)/linux-cris-low.c \ $(srcdir)/linux-crisv32-low.c \ - $(srcdir)/i387-fp.c \ + ${srcdir}/i386-low.c $(srcdir)/i387-fp.c \ $(srcdir)/linux-ia64-low.c $(srcdir)/linux-low.c \ $(srcdir)/linux-m32r-low.c \ $(srcdir)/linux-m68k-low.c $(srcdir)/linux-mips-low.c \ @@ -287,6 +287,10 @@ signals.o: ../common/signals.c $(server_h) memmem.o: ../gnulib/memmem.c $(CC) -o memmem.o -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< +i386_low_h = $(srcdir)/i386-low.h + +i386-low.o: i386-low.c $(i386_low_h) $(server_h) $(target_h) + i387-fp.o: i387-fp.c $(server_h) linux-low.o: linux-low.c $(linux_low_h) $(server_h) @@ -304,7 +308,7 @@ linux-ppc-low.o: linux-ppc-low.c $(linux_low_h) $(server_h) linux-s390-low.o: linux-s390-low.c $(linux_low_h) $(server_h) linux-sh-low.o: linux-sh-low.c $(linux_low_h) $(server_h) linux-x86-low.o: linux-x86-low.c $(linux_low_h) $(server_h) \ - $(gdb_proc_service_h) + $(gdb_proc_service_h) $(i386_low_h) linux-xtensa-low.o: linux-xtensa-low.c xtensa-xtregs.c $(linux_low_h) $(server_h) win32_low_h = $(srcdir)/win32-low.h @@ -312,7 +316,7 @@ win32_low_h = $(srcdir)/win32-low.h win32-low.o: win32-low.c $(win32_low_h) $(server_h) $(regdef_h) $(regcache_h) win32-arm-low.o: win32-arm-low.c $(win32_low_h) $(server_h) -win32-i386-low.o: win32-i386-low.c $(win32_low_h) $(server_h) +win32-i386-low.o: win32-i386-low.c $(win32_low_h) $(server_h) $(i386_low_h) spu-low.o: spu-low.c $(server_h) diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv index e4252af01e8..a3ffcb4733b 100644 --- a/gdb/gdbserver/configure.srv +++ b/gdb/gdbserver/configure.srv @@ -53,10 +53,10 @@ case "${target}" in srv_linux_thread_db=yes ;; i[34567]86-*-cygwin*) srv_regobj=reg-i386.o - srv_tgtobj="win32-low.o win32-i386-low.o" + srv_tgtobj="i386-low.o win32-low.o win32-i386-low.o" ;; i[34567]86-*-linux*) srv_regobj=reg-i386-linux.o - srv_tgtobj="linux-low.o linux-x86-low.o i387-fp.o" + srv_tgtobj="linux-low.o linux-x86-low.o i386-low.o i387-fp.o" srv_linux_usrregs=yes srv_linux_regsets=yes srv_linux_thread_db=yes @@ -71,7 +71,7 @@ case "${target}" in srv_mingwce=yes ;; i[34567]86-*-mingw*) srv_regobj=reg-i386.o - srv_tgtobj="win32-low.o win32-i386-low.o" + srv_tgtobj="i386-low.o win32-low.o win32-i386-low.o" srv_mingw=yes ;; ia64-*-linux*) srv_regobj=reg-ia64.o @@ -170,7 +170,7 @@ case "${target}" in srv_tgtobj="spu-low.o" ;; x86_64-*-linux*) srv_regobj="reg-x86-64-linux.o reg-i386-linux.o" - srv_tgtobj="linux-low.o linux-x86-low.o i387-fp.o" + srv_tgtobj="linux-low.o linux-x86-low.o i386-low.o i387-fp.o" srv_linux_usrregs=yes # This is for i386 progs. srv_linux_regsets=yes srv_linux_thread_db=yes diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c index 9f325181391..5284a983971 100644 --- a/gdb/gdbserver/linux-low.c +++ b/gdb/gdbserver/linux-low.c @@ -224,6 +224,7 @@ delete_lwp (struct lwp_info *lwp) { remove_thread (get_lwp_thread (lwp)); remove_inferior (&all_lwps, &lwp->head); + free (lwp->arch_private); free (lwp); } @@ -242,6 +243,9 @@ linux_add_process (int pid, int attached) proc = add_process (pid, attached); proc->private = xcalloc (1, sizeof (*proc->private)); + if (the_low_target.new_process != NULL) + proc->private->arch_private = the_low_target.new_process (); + return proc; } @@ -251,6 +255,7 @@ linux_add_process (int pid, int attached) static void linux_remove_process (struct process_info *process) { + free (process->private->arch_private); free (process->private); remove_process (process); } @@ -376,6 +381,9 @@ add_lwp (ptid_t ptid) lwp->head.id = ptid; + if (the_low_target.new_thread != NULL) + lwp->arch_private = the_low_target.new_thread (); + add_inferior_to_list (&all_lwps, &lwp->head); return lwp; @@ -466,7 +474,6 @@ linux_attach_lwp_1 (unsigned long lwpid, int initial) new_lwp = (struct lwp_info *) add_lwp (ptid); add_thread (ptid, new_lwp); - /* We need to wait for SIGSTOP before being able to make the next ptrace call on this LWP. */ new_lwp->must_set_ptrace_flags = 1; @@ -1740,6 +1747,9 @@ linux_resume_one_lwp (struct lwp_info *lwp, *p_sig = NULL; } + if (the_low_target.prepare_to_resume != NULL) + the_low_target.prepare_to_resume (lwp); + regcache_invalidate_one ((struct inferior_list_entry *) get_lwp_thread (lwp)); errno = 0; diff --git a/gdb/gdbserver/linux-low.h b/gdb/gdbserver/linux-low.h index 8369db912d1..603963e9b08 100644 --- a/gdb/gdbserver/linux-low.h +++ b/gdb/gdbserver/linux-low.h @@ -56,8 +56,13 @@ struct process_info_private /* Connection to the libthread_db library. */ td_thragent_t *thread_agent; + + /* Arch-specific additions. */ + struct arch_process_info *arch_private; }; +struct lwp_info; + struct linux_target_ops { /* Architecture-specific setup. */ @@ -97,10 +102,24 @@ struct linux_target_ops If DIRECTION is 1, then copy from INF to NATIVE. If DIRECTION is 0, copy from NATIVE to INF. */ int (*siginfo_fixup) (struct siginfo *native, void *inf, int direction); + + /* Hook to call when a new process is created or attached to. + If extra per-process architecture-specific data is needed, + allocate it here. */ + struct arch_process_info * (*new_process) (void); + + /* Hook to call when a new thread is detected. + If extra per-thread architecture-specific data is needed, + allocate it here. */ + struct arch_lwp_info * (*new_thread) (void); + + /* Hook to call prior to resuming a thread. */ + void (*prepare_to_resume) (struct lwp_info *); }; extern struct linux_target_ops the_low_target; +#define ptid_of(proc) ((proc)->head.id) #define pid_of(proc) ptid_get_pid ((proc)->head.id) #define lwpid_of(proc) ptid_get_lwp ((proc)->head.id) @@ -173,6 +192,9 @@ struct lwp_info THREAD_KNOWN is set. */ td_thrhandle_t th; #endif + + /* Arch-specific additions. */ + struct arch_lwp_info *arch_private; }; extern struct inferior_list all_lwps; diff --git a/gdb/gdbserver/linux-x86-low.c b/gdb/gdbserver/linux-x86-low.c index 416f19f9abb..2f17f45927d 100644 --- a/gdb/gdbserver/linux-x86-low.c +++ b/gdb/gdbserver/linux-x86-low.c @@ -18,10 +18,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ +#include #include #include "server.h" #include "linux-low.h" #include "i387-fp.h" +#include "i386-low.h" #include "gdb_proc_service.h" @@ -56,6 +58,21 @@ void init_registers_x86_64_linux (void); #define ARCH_GET_GS 0x1004 #endif +/* Per-process arch-specific data we want to keep. */ + +struct arch_process_info +{ + struct i386_debug_reg_state debug_reg_state; +}; + +/* Per-thread arch-specific data we want to keep. */ + +struct arch_lwp_info +{ + /* Non-zero if our copy differs from what's recorded in the thread. */ + int debug_registers_changed; +}; + #ifdef __x86_64__ /* Mapping between the general-purpose registers in `struct user' @@ -317,6 +334,198 @@ x86_breakpoint_at (CORE_ADDR pc) return 0; } +/* Support for debug registers. */ + +static unsigned long +x86_linux_dr_get (ptid_t ptid, int regnum) +{ + int tid; + unsigned long value; + + tid = ptid_get_lwp (ptid); + + errno = 0; + value = ptrace (PTRACE_PEEKUSER, tid, + offsetof (struct user, u_debugreg[regnum]), 0); + if (errno != 0) + error ("Couldn't read debug register"); + + return value; +} + +static void +x86_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) +{ + int tid; + + tid = ptid_get_lwp (ptid); + + errno = 0; + ptrace (PTRACE_POKEUSER, tid, + offsetof (struct user, u_debugreg[regnum]), value); + if (errno != 0) + error ("Couldn't write debug register"); +} + +/* Update the inferior's debug register REGNUM from STATE. */ + +void +i386_dr_low_set_addr (const struct i386_debug_reg_state *state, int regnum) +{ + struct inferior_list_entry *lp; + CORE_ADDR addr; + /* Only need to update the threads of this process. */ + int pid = pid_of (get_thread_lwp (current_inferior)); + + if (! (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR)) + fatal ("Invalid debug register %d", regnum); + + addr = state->dr_mirror[regnum]; + + for (lp = all_lwps.head; lp; lp = lp->next) + { + struct lwp_info *lwp = (struct lwp_info *) lp; + + /* The actual update is done later, we just mark that the register + needs updating. */ + if (pid_of (lwp) == pid) + lwp->arch_private->debug_registers_changed = 1; + } +} + +/* Update the inferior's DR7 debug control register from STATE. */ + +void +i386_dr_low_set_control (const struct i386_debug_reg_state *state) +{ + struct inferior_list_entry *lp; + /* Only need to update the threads of this process. */ + int pid = pid_of (get_thread_lwp (current_inferior)); + + for (lp = all_lwps.head; lp; lp = lp->next) + { + struct lwp_info *lwp = (struct lwp_info *) lp; + + /* The actual update is done later, we just mark that the register + needs updating. */ + if (pid_of (lwp) == pid) + lwp->arch_private->debug_registers_changed = 1; + } +} + +/* Get the value of the DR6 debug status register from the inferior + and record it in STATE. */ + +void +i386_dr_low_get_status (struct i386_debug_reg_state *state) +{ + struct lwp_info *lwp = get_thread_lwp (current_inferior); + ptid_t ptid = ptid_of (lwp); + + state->dr_status_mirror = x86_linux_dr_get (ptid, DR_STATUS); +} + +/* Watchpoint support. */ + +static int +x86_insert_point (char type, CORE_ADDR addr, int len) +{ + struct process_info *proc = current_process (); + switch (type) + { + case '2': + case '3': + case '4': + return i386_low_insert_watchpoint (&proc->private->arch_private->debug_reg_state, + type, addr, len); + default: + /* Unsupported. */ + return 1; + } +} + +static int +x86_remove_point (char type, CORE_ADDR addr, int len) +{ + struct process_info *proc = current_process (); + switch (type) + { + case '2': + case '3': + case '4': + return i386_low_remove_watchpoint (&proc->private->arch_private->debug_reg_state, + type, addr, len); + default: + /* Unsupported. */ + return 1; + } +} + +static int +x86_stopped_by_watchpoint (void) +{ + struct process_info *proc = current_process (); + return i386_low_stopped_by_watchpoint (&proc->private->arch_private->debug_reg_state); +} + +static CORE_ADDR +x86_stopped_data_address (void) +{ + struct process_info *proc = current_process (); + CORE_ADDR addr; + if (i386_low_stopped_data_address (&proc->private->arch_private->debug_reg_state, + &addr)) + return addr; + return 0; +} + +/* Called when a new process is created. */ + +static struct arch_process_info * +x86_linux_new_process (void) +{ + struct arch_process_info *info = xcalloc (1, sizeof (*info)); + + i386_low_init_dregs (&info->debug_reg_state); + + return info; +} + +/* Called when a new thread is detected. */ + +static struct arch_lwp_info * +x86_linux_new_thread (void) +{ + struct arch_lwp_info *info = xcalloc (1, sizeof (*info)); + + info->debug_registers_changed = 1; + + return info; +} + +/* Called when resuming a thread. + If the debug regs have changed, update the thread's copies. */ + +static void +x86_linux_prepare_to_resume (struct lwp_info *lwp) +{ + if (lwp->arch_private->debug_registers_changed) + { + int i; + ptid_t ptid = ptid_of (lwp); + int pid = ptid_get_pid (ptid); + struct process_info *proc = find_process_pid (pid); + struct i386_debug_reg_state *state = &proc->private->arch_private->debug_reg_state; + + for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++) + x86_linux_dr_set (ptid, i, state->dr_mirror[i]); + + x86_linux_dr_set (ptid, DR_CONTROL, state->dr_control_mirror); + + lwp->arch_private->debug_registers_changed = 0; + } +} + /* When GDBSERVER is built as a 64-bit application on linux, the PTRACE_GETSIGINFO data is always presented in 64-bit layout. Since debugging a 32-bit inferior with a 64-bit GDBSERVER should look the same @@ -630,10 +839,10 @@ struct linux_target_ops the_low_target = NULL, 1, x86_breakpoint_at, - NULL, - NULL, - NULL, - NULL, + x86_insert_point, + x86_remove_point, + x86_stopped_by_watchpoint, + x86_stopped_data_address, /* collect_ptrace_register/supply_ptrace_register are not needed in the native i386 case (no registers smaller than an xfer unit), and are not used in the biarch case (HAVE_LINUX_USRREGS is not defined). */ @@ -641,4 +850,7 @@ struct linux_target_ops the_low_target = NULL, /* need to fix up i386 siginfo if host is amd64 */ x86_siginfo_fixup, + x86_linux_new_process, + x86_linux_new_thread, + x86_linux_prepare_to_resume }; diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c index 76c2e0ec550..d704180a539 100644 --- a/gdb/gdbserver/server.c +++ b/gdb/gdbserver/server.c @@ -51,6 +51,9 @@ static char **program_argv, **wrapper_argv; was originally used to debug LinuxThreads support. */ int debug_threads; +/* Enable debugging of h/w breakpoint/watchpoint support. */ +int debug_hw_points; + int pass_signals[TARGET_SIGNAL_LAST]; jmp_buf toplevel; @@ -495,6 +498,8 @@ monitor_show_help (void) monitor_output ("The following monitor commands are supported:\n"); monitor_output (" set debug <0|1>\n"); monitor_output (" Enable general debugging messages\n"); + monitor_output (" set debug-hw-points <0|1>\n"); + monitor_output (" Enable h/w breakpoint/watchpoint debugging messages\n"); monitor_output (" set remote-debug <0|1>\n"); monitor_output (" Enable remote protocol debugging messages\n"); monitor_output (" exit\n"); @@ -1216,6 +1221,16 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p) debug_threads = 0; monitor_output ("Debug output disabled.\n"); } + else if (strcmp (mon, "set debug-hw-points 1") == 0) + { + debug_hw_points = 1; + monitor_output ("H/W point debugging output enabled.\n"); + } + else if (strcmp (mon, "set debug-hw-points 0") == 0) + { + debug_hw_points = 0; + monitor_output ("H/W point debugging output disabled.\n"); + } else if (strcmp (mon, "set remote-debug 1") == 0) { remote_debug = 1; diff --git a/gdb/gdbserver/server.h b/gdb/gdbserver/server.h index 89ffeb7ea61..9dc7d3b1547 100644 --- a/gdb/gdbserver/server.h +++ b/gdb/gdbserver/server.h @@ -271,6 +271,7 @@ extern ptid_t step_thread; extern int server_waiting; extern int debug_threads; +extern int debug_hw_points; extern int pass_signals[]; extern jmp_buf toplevel; @@ -407,6 +408,7 @@ void perror_with_name (const char *string); void error (const char *string,...) ATTR_NORETURN ATTR_FORMAT (printf, 1, 2); void fatal (const char *string,...) ATTR_NORETURN ATTR_FORMAT (printf, 1, 2); void warning (const char *string,...) ATTR_FORMAT (printf, 1, 2); +char *paddress (CORE_ADDR addr); /* Maximum number of bytes to read/write at once. The value here is chosen to fill up a packet (the headers account for the 32). */ diff --git a/gdb/gdbserver/utils.c b/gdb/gdbserver/utils.c index a03c6b2a6b2..5935380e064 100644 --- a/gdb/gdbserver/utils.c +++ b/gdb/gdbserver/utils.c @@ -170,3 +170,46 @@ warning (const char *string,...) fprintf (stderr, "\n"); va_end (args); } + +/* Temporary storage using circular buffer. */ +#define NUMCELLS 4 +#define CELLSIZE 50 + +/* Return the next entry in the circular buffer. */ + +static char * +get_cell (void) +{ + static char buf[NUMCELLS][CELLSIZE]; + static int cell = 0; + if (++cell >= NUMCELLS) + cell = 0; + return buf[cell]; +} + +/* Stdarg wrapper around vsnprintf. + SIZE is the size of the buffer pointed to by STR. */ + +static int +xsnprintf (char *str, size_t size, const char *format, ...) +{ + va_list args; + int ret; + + va_start (args, format); + ret = vsnprintf (str, size, format, args); + va_end (args); + + return ret; +} + +/* Convert a CORE_ADDR into a HEX string, like %lx. + The result is stored in a circular static buffer, NUMCELLS deep. */ + +char * +paddress (CORE_ADDR addr) +{ + char *str = get_cell (); + xsnprintf (str, CELLSIZE, "%lx", (long) addr); + return str; +} diff --git a/gdb/gdbserver/win32-arm-low.c b/gdb/gdbserver/win32-arm-low.c index 3913a969316..3254eec1a95 100644 --- a/gdb/gdbserver/win32-arm-low.c +++ b/gdb/gdbserver/win32-arm-low.c @@ -122,4 +122,9 @@ struct win32_target_ops the_low_target = { NULL, /* single_step */ (const unsigned char *) &arm_wince_breakpoint, arm_wince_breakpoint_len, + /* Watchpoint related functions. See target.h for comments. */ + NULL, /* insert_point */ + NULL, /* remove_point */ + NULL, /* stopped_by_watchpoint */ + NULL /* stopped_data_address */ }; diff --git a/gdb/gdbserver/win32-i386-low.c b/gdb/gdbserver/win32-i386-low.c index a9ede549a53..7e058a58b56 100644 --- a/gdb/gdbserver/win32-i386-low.c +++ b/gdb/gdbserver/win32-i386-low.c @@ -17,6 +17,7 @@ #include "server.h" #include "win32-low.h" +#include "i386-low.h" #define FCS_REGNUM 27 #define FOP_REGNUM 31 @@ -26,15 +27,101 @@ /* Defined in auto-generated file reg-i386.c. */ void init_registers_i386 (void); -static unsigned dr[8]; +static struct i386_debug_reg_state debug_reg_state; static int debug_registers_changed = 0; static int debug_registers_used = 0; +/* Update the inferior's debug register REGNUM from STATE. */ + +void +i386_dr_low_set_addr (const struct i386_debug_reg_state *state, int regnum) +{ + if (! (regnum >= 0 && regnum <= DR_LASTADDR - DR_FIRSTADDR)) + fatal ("Invalid debug register %d", regnum); + + /* debug_reg_state.dr_mirror is already set. + Just notify i386_set_thread_context, i386_thread_added + that the registers need to be updated. */ + debug_registers_changed = 1; + debug_registers_used = 1; +} + +/* Update the inferior's DR7 debug control register from STATE. */ + +void +i386_dr_low_set_control (const struct i386_debug_reg_state *state) +{ + /* debug_reg_state.dr_control_mirror is already set. + Just notify i386_set_thread_context, i386_thread_added + that the registers need to be updated. */ + debug_registers_changed = 1; + debug_registers_used = 1; +} + +/* Get the value of the DR6 debug status register from the inferior + and record it in STATE. */ + +void +i386_dr_low_get_status (struct i386_debug_reg_state *state) +{ + /* We don't need to do anything here, the last call to thread_rec for + current_event.dwThreadId id has already set it. */ +} + +/* Watchpoint support. */ + +static int +i386_insert_point (char type, CORE_ADDR addr, int len) +{ + switch (type) + { + case '2': + case '3': + case '4': + return i386_low_insert_watchpoint (&debug_reg_state, + type, addr, len); + default: + /* Unsupported. */ + return 1; + } +} + +static int +i386_remove_point (char type, CORE_ADDR addr, int len) +{ + switch (type) + { + case '2': + case '3': + case '4': + return i386_low_remove_watchpoint (&debug_reg_state, + type, addr, len); + default: + /* Unsupported. */ + return 1; + } +} + +static int +i386_stopped_by_watchpoint (void) +{ + return i386_low_stopped_by_watchpoint (&debug_reg_state); +} + +static CORE_ADDR +i386_stopped_data_address (void) +{ + CORE_ADDR addr; + if (i386_low_stopped_data_address (&debug_reg_state, &addr)) + return addr; + return 0; +} + static void i386_initial_stuff (void) { - memset (&dr, 0, sizeof (dr)); + i386_low_init_dregs (&debug_reg_state); debug_registers_changed = 0; debug_registers_used = 0; } @@ -55,12 +142,13 @@ i386_get_thread_context (win32_thread_info *th, DEBUG_EVENT* current_event) if (th->tid == current_event->dwThreadId) { /* Copy dr values from the current thread. */ - dr[0] = th->context.Dr0; - dr[1] = th->context.Dr1; - dr[2] = th->context.Dr2; - dr[3] = th->context.Dr3; - dr[6] = th->context.Dr6; - dr[7] = th->context.Dr7; + struct i386_debug_reg_state *dr = &debug_reg_state; + dr->dr_mirror[0] = th->context.Dr0; + dr->dr_mirror[1] = th->context.Dr1; + dr->dr_mirror[2] = th->context.Dr2; + dr->dr_mirror[3] = th->context.Dr3; + dr->dr_status_mirror = th->context.Dr6; + dr->dr_control_mirror = th->context.Dr7; } } @@ -69,13 +157,14 @@ i386_set_thread_context (win32_thread_info *th, DEBUG_EVENT* current_event) { if (debug_registers_changed) { - th->context.Dr0 = dr[0]; - th->context.Dr1 = dr[1]; - th->context.Dr2 = dr[2]; - th->context.Dr3 = dr[3]; - /* th->context.Dr6 = dr[6]; + struct i386_debug_reg_state *dr = &debug_reg_state; + th->context.Dr0 = dr->dr_mirror[0]; + th->context.Dr1 = dr->dr_mirror[1]; + th->context.Dr2 = dr->dr_mirror[2]; + th->context.Dr3 = dr->dr_mirror[3]; + /* th->context.Dr6 = dr->dr_status_mirror; FIXME: should we set dr6 also ?? */ - th->context.Dr7 = dr[7]; + th->context.Dr7 = dr->dr_control_mirror; } SetThreadContext (th->h, &th->context); @@ -87,16 +176,17 @@ i386_thread_added (win32_thread_info *th) /* Set the debug registers for the new thread if they are used. */ if (debug_registers_used) { + struct i386_debug_reg_state *dr = &debug_reg_state; th->context.ContextFlags = CONTEXT_DEBUG_REGISTERS; GetThreadContext (th->h, &th->context); - th->context.Dr0 = dr[0]; - th->context.Dr1 = dr[1]; - th->context.Dr2 = dr[2]; - th->context.Dr3 = dr[3]; - /* th->context.Dr6 = dr[6]; + th->context.Dr0 = dr->dr_mirror[0]; + th->context.Dr1 = dr->dr_mirror[1]; + th->context.Dr2 = dr->dr_mirror[2]; + th->context.Dr3 = dr->dr_mirror[3]; + /* th->context.Dr6 = dr->dr_status_mirror; FIXME: should we set dr6 also ?? */ - th->context.Dr7 = dr[7]; + th->context.Dr7 = dr->dr_control_mirror; SetThreadContext (th->h, &th->context); th->context.ContextFlags = 0; @@ -205,4 +295,8 @@ struct win32_target_ops the_low_target = { i386_single_step, NULL, /* breakpoint */ 0, /* breakpoint_len */ + i386_insert_point, + i386_remove_point, + i386_stopped_by_watchpoint, + i386_stopped_data_address }; diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c index 869a1cdba79..a1380f6030b 100644 --- a/gdb/gdbserver/win32-low.c +++ b/gdb/gdbserver/win32-low.c @@ -228,6 +228,48 @@ child_delete_thread (DWORD pid, DWORD tid) delete_thread_info (thread); } +/* These watchpoint related wrapper functions simply pass on the function call + if the low target has registered a corresponding function. */ + +static int +win32_insert_point (char type, CORE_ADDR addr, int len) +{ + if (the_low_target.insert_point != NULL) + return the_low_target.insert_point (type, addr, len); + else + /* Unsupported (see target.h). */ + return 1; +} + +static int +win32_remove_point (char type, CORE_ADDR addr, int len) +{ + if (the_low_target.remove_point != NULL) + return the_low_target.remove_point (type, addr, len); + else + /* Unsupported (see target.h). */ + return 1; +} + +static int +win32_stopped_by_watchpoint (void) +{ + if (the_low_target.stopped_by_watchpoint != NULL) + return the_low_target.stopped_by_watchpoint (); + else + return 0; +} + +static CORE_ADDR +win32_stopped_data_address (void) +{ + if (the_low_target.stopped_data_address != NULL) + return the_low_target.stopped_data_address (); + else + return 0; +} + + /* Transfer memory from/to the debugged process. */ static int child_xfer_memory (CORE_ADDR memaddr, char *our, int len, @@ -1697,10 +1739,10 @@ static struct target_ops win32_target_ops = { NULL, win32_request_interrupt, NULL, - NULL, - NULL, - NULL, - NULL, + win32_insert_point, + win32_remove_point, + win32_stopped_by_watchpoint, + win32_stopped_data_address, NULL, NULL, NULL, diff --git a/gdb/gdbserver/win32-low.h b/gdb/gdbserver/win32-low.h index 10c257eb2dc..5720c558040 100644 --- a/gdb/gdbserver/win32-low.h +++ b/gdb/gdbserver/win32-low.h @@ -70,6 +70,12 @@ struct win32_target_ops const unsigned char *breakpoint; int breakpoint_len; + + /* Breakpoint/Watchpoint related functions. See target.h for comments. */ + int (*insert_point) (char type, CORE_ADDR addr, int len); + int (*remove_point) (char type, CORE_ADDR addr, int len); + int (*stopped_by_watchpoint) (void); + CORE_ADDR (*stopped_data_address) (void); }; extern struct win32_target_ops the_low_target;