aboutsummaryrefslogtreecommitdiff
path: root/gdb
diff options
context:
space:
mode:
Diffstat (limited to 'gdb')
-rw-r--r--gdb/gdbserver/ChangeLog67
-rw-r--r--gdb/gdbserver/Makefile.in10
-rw-r--r--gdb/gdbserver/configure.srv8
-rw-r--r--gdb/gdbserver/linux-low.c12
-rw-r--r--gdb/gdbserver/linux-low.h22
-rw-r--r--gdb/gdbserver/linux-x86-low.c220
-rw-r--r--gdb/gdbserver/server.c15
-rw-r--r--gdb/gdbserver/server.h2
-rw-r--r--gdb/gdbserver/utils.c43
-rw-r--r--gdb/gdbserver/win32-arm-low.c5
-rw-r--r--gdb/gdbserver/win32-i386-low.c134
-rw-r--r--gdb/gdbserver/win32-low.c50
-rw-r--r--gdb/gdbserver/win32-low.h6
13 files changed, 558 insertions, 36 deletions
diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog
index 8cb2d92..86fe174 100644
--- a/gdb/gdbserver/ChangeLog
+++ b/gdb/gdbserver/ChangeLog
@@ -1,3 +1,70 @@
+2009-06-30 Doug Evans <dje@google.com>
+ Pierre Muller <muller@ics.u-strasbg.fr>
+
+ 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 <pedro@codesourcery.com>
* server.c (process_serial_event): Re-return unsupported, not
diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in
index f395d9e..37d6095 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 e4252af..a3ffcb4 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 9f32518..5284a98 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 8369db9..603963e 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 416f19f..2f17f45 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 <http://www.gnu.org/licenses/>. */
+#include <stddef.h>
#include <signal.h>
#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 76c2e0e..d704180 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 89ffeb7..9dc7d3b 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 a03c6b2..5935380 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 3913a96..3254eec 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 a9ede54..7e058a5 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 869a1cd..a1380f6 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 10c257e..5720c55 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;