From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 15885 invoked by alias); 1 May 2009 08:10:54 -0000 Received: (qmail 15581 invoked by uid 22791); 1 May 2009 08:10:37 -0000 X-SWARE-Spam-Status: No, hits=-0.2 required=5.0 tests=AWL,BAYES_00,HK_OBFDOM,HK_OBFDOMREQ,J_CHICKENPOX_12,J_CHICKENPOX_93 X-Spam-Check-By: sourceware.org Received: from mailhost.u-strasbg.fr (HELO mailhost.u-strasbg.fr) (130.79.200.154) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 01 May 2009 08:10:23 +0000 Received: from baal.u-strasbg.fr (baal.u-strasbg.fr [IPv6:2001:660:2402::41]) by mailhost.u-strasbg.fr (8.14.2/jtpda-5.5pre1) with ESMTP id n418A9wm030171 ; Fri, 1 May 2009 10:10:09 +0200 (CEST) Received: from mailserver.u-strasbg.fr (ms1.u-strasbg.fr [IPv6:2001:660:2402:d::10]) by baal.u-strasbg.fr (8.14.0/jtpda-5.5pre1) with ESMTP id n418A9fT077968 ; Fri, 1 May 2009 10:10:09 +0200 (CEST) (envelope-from muller@ics.u-strasbg.fr) Received: from d620muller (lec67-4-82-230-53-140.fbx.proxad.net [82.230.53.140]) (user=mullerp mech=LOGIN) by mailserver.u-strasbg.fr (8.14.3/jtpda-5.5pre1) with ESMTP id n418A593018438 (version=TLSv1/SSLv3 cipher=RC4-MD5 bits=128 verify=NO) ; Fri, 1 May 2009 10:10:06 +0200 (CEST) (envelope-from muller@ics.u-strasbg.fr) From: "Pierre Muller" To: "'Doug Evans'" , References: <20090430071853.99F5584890@localhost> In-Reply-To: <20090430071853.99F5584890@localhost> Subject: RE: [RFA] i386/amd64 h/w watchpoints in gdbserver Date: Fri, 01 May 2009 08:10:00 -0000 Message-ID: <013201c9ca34$3e5f4170$bb1dc450$@u-strasbg.fr> MIME-Version: 1.0 Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable x-cr-hashedpuzzle: AECs ApH+ BRph BZN6 C30h Gixt JrLL KITT MO2d MixF MuEH O7wO PKhg P0Ap Qc5G VAQm;2;ZABqAGUAQABnAG8AbwBnAGwAZQAuAGMAbwBtADsAZwBkAGIALQBwAGEAdABjAGgAZQBzAEAAcwBvAHUAcgBjAGUAdwBhAHIAZQAuAG8AcgBnAA==;Sosha1_v1;7;{33F1F72C-15EB-4D20-B032-ED5A7EB60C02};bQB1AGwAbABlAHIAQABpAGMAcwAuAHUALQBzAHQAcgBhAHMAYgBnAC4AZgByAA==;Fri, 01 May 2009 08:10:02 GMT;UgBFADoAIABbAFIARgBBAF0AIABpADMAOAA2AC8AYQBtAGQANgA0ACAAaAAvAHcAIAB3AGEAdABjAGgAcABvAGkAbgB0AHMAIABpAG4AIABnAGQAYgBzAGUAcgB2AGUAcgA= x-cr-puzzleid: {33F1F72C-15EB-4D20-B032-ED5A7EB60C02} Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2009-05/txt/msg00006.txt.bz2 Hi Doug, I think that your patch seems to be globally OK, but it is missing some parts for the win32 support: You added the i386_debug_reg_state struct, but forgot to add that to the win32 part of your patch. I wrote a minimal change so that I was able to use your code. I only tested it faintly, and it seems to work. What is still wrong is that we still use default_region_ok_for_hw_watchpoint, this for instance generates a failure if I try to set a watchpoint on a 8-byte long address (which is possible using 2 debug registers). But implementing this would require more changes, so I suppose that it is better to leave that for later. Anyhow, with the patch below, it does add=20 hardware support for win32 gdbserver, and as such should be accepted IMHO. Pierre Muller Pascal language support maintainer for GDB PS: By the way, what is the current status of the win64 gdbserver? I thought that there was a patch submitted, on? =20=20 >>>>>>>>>>>>>>>>>>>>>>><<< Here is an updated diff for win32-i386-low that allowed me to compile gdbserver successfully for cygwin. $ cvs diff -up win32-i386-low.c Index: win32-i386-low.c =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: /cvs/src/src/gdb/gdbserver/win32-i386-low.c,v retrieving revision 1.14 diff -u -p -r1.14 win32-i386-low.c --- win32-i386-low.c 3 Jan 2009 05:57:57 -0000 1.14 +++ win32-i386-low.c 1 May 2009 08:01:28 -0000 @@ -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,77 @@ /* Defined in auto-generated file reg-i386.c. */ void init_registers_i386 (void); -static unsigned dr[8]; +struct i386_debug_reg_state debug_reg_state; static int debug_registers_changed =3D 0; static int debug_registers_used =3D 0; +/* Pass the address ADDR to the inferior in the I'th debug register. + Here we just store the address in dr array, the registers will be + actually set up when windows_continue is called. */ + +void +i386_dr_low_set_addr (int i, CORE_ADDR addr) +{ + if (i < 0 || i > 3) + return; + debug_reg_state.dr_mirror[i] =3D (unsigned) addr; + debug_registers_changed =3D 1; + debug_registers_used =3D 1; +} + +/* Pass the value VAL to the inferior in the DR7 debug control register. + Here we just store the address in D_REGS, the watchpoint + will be actually set up in windows_wait. */ + +void +i386_dr_low_set_control (unsigned val) +{ + debug_reg_state.dr_control_mirror =3D val; + debug_registers_changed =3D 1; + debug_registers_used =3D 1; +} + +/* Get the value of the DR6 debug status register from the inferior. + Here we just return the value stored in dr[6] + by the last call to thread_rec for current_event.dwThreadId id. */ + +unsigned +i386_dr_low_get_status (void) +{ + return debug_reg_state.dr_status_mirror; +} + +/* Watchpoint support. */ + +static int +i386_insert_watchpoint (char type, CORE_ADDR addr, int len) +{ + return i386_low_insert_watchpoint (&debug_reg_state, type, addr, len); +} + +static int +i386_remove_watchpoint (char type, CORE_ADDR addr, int len) +{ + return i386_low_remove_watchpoint (&debug_reg_state, type, addr, len); +} + +static int +i386_stopped_by_watchpoint () +{ + return i386_low_stopped_by_watchpoint (&debug_reg_state); +} + +static CORE_ADDR +i386_stopped_data_address () +{ + return i386_low_stopped_data_address (&debug_reg_state); +} + static void i386_initial_stuff (void) { - memset (&dr, 0, sizeof (dr)); + memset (&debug_reg_state, 0, sizeof (debug_reg_state)); debug_registers_changed =3D 0; debug_registers_used =3D 0; } @@ -55,12 +118,12 @@ i386_get_thread_context (win32_thread_in if (th->tid =3D=3D current_event->dwThreadId) { /* Copy dr values from the current thread. */ - dr[0] =3D th->context.Dr0; - dr[1] =3D th->context.Dr1; - dr[2] =3D th->context.Dr2; - dr[3] =3D th->context.Dr3; - dr[6] =3D th->context.Dr6; - dr[7] =3D th->context.Dr7; + debug_reg_state.dr_mirror[0] =3D th->context.Dr0; + debug_reg_state.dr_mirror[1] =3D th->context.Dr1; + debug_reg_state.dr_mirror[2] =3D th->context.Dr2; + debug_reg_state.dr_mirror[3] =3D th->context.Dr3; + debug_reg_state.dr_status_mirror =3D th->context.Dr6; + debug_reg_state.dr_control_mirror =3D th->context.Dr7; } } @@ -69,13 +132,13 @@ i386_set_thread_context (win32_thread_in { if (debug_registers_changed) { - th->context.Dr0 =3D dr[0]; - th->context.Dr1 =3D dr[1]; - th->context.Dr2 =3D dr[2]; - th->context.Dr3 =3D dr[3]; - /* th->context.Dr6 =3D dr[6]; + th->context.Dr0 =3D debug_reg_state.dr_mirror[0]; + th->context.Dr1 =3D debug_reg_state.dr_mirror[1]; + th->context.Dr2 =3D debug_reg_state.dr_mirror[2]; + th->context.Dr3 =3D debug_reg_state.dr_mirror[3]; + /* th->context.Dr6 =3D debug_reg_state.dr_status_mirror; FIXME: should we set dr6 also ?? */ - th->context.Dr7 =3D dr[7]; + th->context.Dr7 =3D debug_reg_state.dr_control_mirror; } SetThreadContext (th->h, &th->context); @@ -90,14 +153,14 @@ i386_thread_added (win32_thread_info *th th->context.ContextFlags =3D CONTEXT_DEBUG_REGISTERS; GetThreadContext (th->h, &th->context); - th->context.Dr0 =3D dr[0]; - th->context.Dr1 =3D dr[1]; - th->context.Dr2 =3D dr[2]; - th->context.Dr3 =3D dr[3]; - /* th->context.Dr6 =3D dr[6]; + th->context.Dr0 =3D debug_reg_state.dr_mirror[0]; + th->context.Dr1 =3D debug_reg_state.dr_mirror[1]; + th->context.Dr2 =3D debug_reg_state.dr_mirror[2]; + th->context.Dr3 =3D debug_reg_state.dr_mirror[3]; + /* th->context.Dr6 =3D debug_reg_state.dr_status_mirror; FIXME: should we set dr6 also ?? */ - th->context.Dr7 =3D dr[7]; - + th->context.Dr7 =3D debug_reg_state.dr_control_mirror; + SetThreadContext (th->h, &th->context); th->context.ContextFlags =3D 0; } @@ -205,4 +268,8 @@ struct win32_target_ops the_low_target =3D i386_single_step, NULL, /* breakpoint */ 0, /* breakpoint_len */ + i386_insert_watchpoint, + i386_remove_watchpoint, + i386_stopped_by_watchpoint, + i386_stopped_data_address }; > -----Message d'origine----- > De=A0: Doug Evans [mailto:dje@google.com] > Envoy=E9=A0: Thursday, April 30, 2009 9:19 AM > =C0=A0: gdb-patches@sourceware.org; muller@ics.u-strasbg.fr > Objet=A0: [RFA] i386/amd64 h/w watchpoints in gdbserver >=20 > Hi. >=20 > I took Pierre's patch here > http://sourceware.org/ml/gdb-patches/2009-02/msg00206.html >=20 > and added support for {i386,amd64}-linux. > Pierre, I _think_ I didn't break win32 support, but it is different > than what you originally wrote. Can you test it? >=20 > This patch assumes the gdbserver memory leak fix has been applied: > http://sourceware.org/ml/gdb-patches/2009-04/msg00803.html >=20 >=20 > 2009-04-29 Pierre Muller > Doug Evans >=20 > * Makefile.in (SFILES): Add i386-low.c > (i386_low_h): Define. > (i386-low.o): Add dependencies. > (linux-i386-low.o): Add i386-low.h dependency. > (linux-x86-64-low.o): Ditto. > (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-i386-low.c: Include stddef.h, i386-low.h. > (arch_process_info): New struct. > (i386_linux_dr_get, i386_linux_dr_set): New functions. > (i386_dr_low_set_addr, i386_dr_low_set_control): New functions. > (i386_dr_low_get_status): New function. > (i386_insert_watchpoint, i386_remove_watchpoint): New functions. > (i386_stopped_by_watchpoint, i386_stopped_data_address): New > functions. > (i386_linux_new_process, i386_linux_new_thread): New functions. > (the_low_target): Add entries for insert_watchpoint, > remove_watchpoint, > stopped_by_watchpoint, stopped_data_address, new_process, > new_thread. > * linux-low.c (linux_add_process): Initialize arch_private. > (linux_remove_process): Free arch_private. > (notify_low_target_new_thread): New function. > (handle_extended_wait): Call it. > (linux_attach_lwp_1, linux_wait_1): Ditto. > (linux_insert_watchpoint): Update call to > the_low_target.insert_watchpoint. > (linux_remove_watchpoint): Update call to > the_low_target.remove_watchpoint. > (linux_stopped_by_watchpoint): Update call to > the_low_target.stopped_by_watchpoint. > (linux_stopped_data_address): Update call to > the_low_target.stopped_data_address. > * linux-low.h (process_info_private): New member arch_private. > (linux_target_ops): Add arch_private parameter to > insert_watchpoint, > remove_watchpoint, stopped_by_watchpoint, stopped_data_address. > New members new_process, new_thread. > (ptid_of, PIDGET, TIDGET): New macros. > * linux-x86-64-low.c: Include stddef.h, i386-low.h. > (arch_process_info): New struct. > (x86_64_linux_dr_get, x86_64_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_64_insert_watchpoint, x86_64_remove_watchpoint): New > functions. > (x86_64_stopped_by_watchpoint): New function. > (x86_64_stopped_data_address): New function. > (x86_64_linux_new_process, x86_64_linux_new_thread): New > functions. > (the_low_target): Add entries for insert_watchpoint, > remove_watchpoint, > stopped_by_watchpoint, stopped_data_address, new_process, > new_thread. > * server.h (paddr): Declare. > * utils.c (NUMCELLS, CELLSIZE): New macros. > (get_sell, xsnprintf, paddr): New functions. > * win32-arm-low.c (the_low_target): Add entries for > insert_watchpoint, > remove_watchpoint, stopped_by_watchpoint, stopped_data_address. > * win32-i386-low.c: Include i386-low.h. > (i386_dr_low_set_addr, i386_dr_low_set_control): New functions. > (i386_dr_low_get_status): New function. > (the_low_target): Add entries for insert_watchpoint, > remove_watchpoint, 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_watchpoint, > remove_watchpoint, stopped_by_watchpoint, stopped_data_address. >=20 > Index: Makefile.in > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > RCS file: /cvs/src/src/gdb/gdbserver/Makefile.in,v > retrieving revision 1.73 > diff -u -p -r1.73 Makefile.in > --- Makefile.in 1 Apr 2009 22:48:05 -0000 1.73 > +++ Makefile.in 30 Apr 2009 01:23:29 -0000 > @@ -104,7 +104,7 @@ SFILES=3D $(srcdir)/gdbreplay.c $(srcdir)/ > $(srcdir)/thread-db.c $(srcdir)/utils.c \ > $(srcdir)/linux-arm-low.c $(srcdir)/linux-cris-low.c \ > $(srcdir)/linux-crisv32-low.c $(srcdir)/linux-i386-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_ > memmem.o: ../gnulib/memmem.c > $(CC) -o memmem.o -c $(CPPFLAGS) $(INTERNAL_CFLAGS) $< >=20 > +i386_low_h =3D $(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) >=20 > linux-low.o: linux-low.c $(linux_low_h) $(server_h) > @@ -297,7 +301,7 @@ linux-arm-low.o: linux-arm-low.c $(linux > linux-cris-low.o: linux-cris-low.c $(linux_low_h) $(server_h) > linux-crisv32-low.o: linux-crisv32-low.c $(linux_low_h) $(server_h) > linux-i386-low.o: linux-i386-low.c $(linux_low_h) $(server_h) \ > - $(gdb_proc_service_h) > + $(gdb_proc_service_h) $(i386_low_h) > linux-ia64-low.o: linux-ia64-low.c $(linux_low_h) $(server_h) > linux-m32r-low.o: linux-m32r-low.c $(linux_low_h) $(server_h) > linux-mips-low.o: linux-mips-low.c $(linux_low_h) $(server_h) \ > @@ -306,7 +310,7 @@ linux-ppc-low.o: linux-ppc-low.c $(linux > 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-64-low.o: linux-x86-64-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) >=20 > win32_low_h =3D $(srcdir)/win32-low.h > @@ -314,7 +318,7 @@ win32_low_h =3D $(srcdir)/win32-low.h > win32-low.o: win32-low.c $(win32_low_h) $(server_h) $(regdef_h) > $(regcache_h) >=20 > 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) >=20 > spu-low.o: spu-low.c $(server_h) >=20 > Index: configure.srv > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > RCS file: /cvs/src/src/gdb/gdbserver/configure.srv,v > retrieving revision 1.40 > diff -u -p -r1.40 configure.srv > --- configure.srv 19 Apr 2009 17:54:52 -0000 1.40 > +++ configure.srv 30 Apr 2009 01:23:29 -0000 > @@ -53,10 +53,10 @@ case "${target}" in > srv_linux_thread_db=3Dyes > ;; > i[34567]86-*-cygwin*) srv_regobj=3Dreg-i386.o > - srv_tgtobj=3D"win32-low.o win32-i386-low.o" > + srv_tgtobj=3D"i386-low.o win32-low.o win32-i386-low.o" > ;; > i[34567]86-*-linux*) srv_regobj=3Dreg-i386-linux.o > - srv_tgtobj=3D"linux-low.o linux-i386-low.o i387-fp.o" > + srv_tgtobj=3D"linux-low.o linux-i386-low.o i386-low.o > i387-fp.o" > srv_linux_usrregs=3Dyes > srv_linux_regsets=3Dyes > srv_linux_thread_db=3Dyes > @@ -71,7 +71,7 @@ case "${target}" in > srv_mingwce=3Dyes > ;; > i[34567]86-*-mingw*) srv_regobj=3Dreg-i386.o > - srv_tgtobj=3D"win32-low.o win32-i386-low.o" > + srv_tgtobj=3D"i386-low.o win32-low.o win32-i386-low.o" > srv_mingw=3Dyes > ;; > ia64-*-linux*) srv_regobj=3Dreg-ia64.o > @@ -170,7 +170,7 @@ case "${target}" in > srv_tgtobj=3D"spu-low.o" > ;; > x86_64-*-linux*) srv_regobj=3Dreg-x86-64-linux.o > - srv_tgtobj=3D"linux-low.o linux-x86-64-low.o i387-fp.o" > + srv_tgtobj=3D"linux-low.o linux-x86-64-low.o i386-low.o > i387-fp.o" > srv_linux_regsets=3Dyes > srv_linux_thread_db=3Dyes > ;; > Index: i386-low.c > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > RCS file: i386-low.c > diff -N i386-low.c > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ i386-low.c 30 Apr 2009 01:23:29 -0000 > @@ -0,0 +1,667 @@ > +/* Debug register code for the i386. > + > + Copyright (C) 2009 > + 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 > . */ > + > +#include "server.h" > +#include "target.h" > +#include "i386-low.h" > + > +/* Support for 8-byte wide hw watchpoints. */ > +#ifndef TARGET_HAS_DR_LEN_8 > +#define TARGET_HAS_DR_LEN_8 (sizeof (long) =3D=3D 8) > +#endif > + > +enum target_hw_bp_type > + { > + hw_write =3D 0, /* Common HW watchpoint */ > + hw_read =3D 1, /* Read HW watchpoint */ > + hw_access =3D 2, /* Access HW watchpoint */ > + hw_execute =3D 3 /* Execute HW breakpoint */ > + }; > + > +/* DR7 Debug Control register fields. */ > + > +/* How many bits to skip in DR7 to get to R/W and LEN fields. */ > +#define DR_CONTROL_SHIFT 16 > +/* How many bits in DR7 per R/W and LEN field for each watchpoint. */ > +#define DR_CONTROL_SIZE 4 > + > +/* Watchpoint/breakpoint read/write fields in DR7. */ > +#define DR_RW_EXECUTE (0x0) /* Break on instruction execution. */ > +#define DR_RW_WRITE (0x1) /* Break on data writes. */ > +#define DR_RW_READ (0x3) /* Break on data reads or writes. */ > + > +/* This is here for completeness. No platform supports this > + functionality yet (as of March 2001). Note that the DE flag in the > + CR4 register needs to be set to support this. */ > +#ifndef DR_RW_IORW > +#define DR_RW_IORW (0x2) /* Break on I/O reads or writes. */ > +#endif > + > +/* Watchpoint/breakpoint length fields in DR7. The 2-bit left shift > + is so we could OR this with the read/write field defined above. */ > +#define DR_LEN_1 (0x0 << 2) /* 1-byte region watch or breakpoint. */ > +#define DR_LEN_2 (0x1 << 2) /* 2-byte region watch. */ > +#define DR_LEN_4 (0x3 << 2) /* 4-byte region watch. */ > +#define DR_LEN_8 (0x2 << 2) /* 8-byte region watch (AMD64). */ > + > +/* Local and Global Enable flags in DR7. > + > + When the Local Enable flag is set, the breakpoint/watchpoint is > + enabled only for the current task; the processor automatically > + clears this flag on every task switch. When the Global Enable flag > + is set, the breakpoint/watchpoint is enabled for all tasks; the > + processor never clears this flag. > + > + Currently, all watchpoint are locally enabled. If you need to > + enable them globally, read the comment which pertains to this in > + i386_insert_aligned_watchpoint below. */ > +#define DR_LOCAL_ENABLE_SHIFT 0 /* Extra shift to the local > enable bit. */ > +#define DR_GLOBAL_ENABLE_SHIFT 1 /* Extra shift to the global > enable bit. */ > +#define DR_ENABLE_SIZE 2 /* Two enable bits per debug register. > */ > + > +/* Local and global exact breakpoint enable flags (a.k.a. slowdown > + flags). These are only required on i386, to allow detection of the > + exact instruction which caused a watchpoint to break; i486 and > + later processors do that automatically. We set these flags for > + backwards compatibility. */ > +#define DR_LOCAL_SLOWDOWN (0x100) > +#define DR_GLOBAL_SLOWDOWN (0x200) > + > +/* Fields reserved by Intel. This includes the GD (General Detect > + Enable) flag, which causes a debug exception to be generated when a > + MOV instruction accesses one of the debug registers. > + > + FIXME: My Intel manual says we should use 0xF800, not 0xFC00. */ > +#define DR_CONTROL_RESERVED (0xFC00) > + > +/* Auxiliary helper macros. */ > + > +/* A value that masks all fields in DR7 that are reserved by Intel. > */ > +#define I386_DR_CONTROL_MASK (~DR_CONTROL_RESERVED) > + > +/* The I'th debug register is vacant if its Local and Global Enable > + bits are reset in the Debug Control register. */ > +#define I386_DR_VACANT(state, i) \ > + (((state)->dr_control_mirror & (3 << (DR_ENABLE_SIZE * (i)))) =3D=3D 0) > + > +/* Locally enable the break/watchpoint in the I'th debug register. */ > +#define I386_DR_LOCAL_ENABLE(state, i) \ > + do { \ > + (state)->dr_control_mirror |=3D \ > + (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * (i))); \ > + } while (0) > + > +/* Globally enable the break/watchpoint in the I'th debug register. > */ > +#define I386_DR_GLOBAL_ENABLE(state, i) \ > + do { \ > + (state)->dr_control_mirror |=3D \ > + (1 << (DR_GLOBAL_ENABLE_SHIFT + DR_ENABLE_SIZE * (i))); \ > + } while (0) > + > +/* Disable the break/watchpoint in the I'th debug register. */ > +#define I386_DR_DISABLE(state, i) \ > + do { \ > + (state)->dr_control_mirror &=3D \ > + ~(3 << (DR_ENABLE_SIZE * (i))); \ > + } while (0) > + > +/* Set in DR7 the RW and LEN fields for the I'th debug register. */ > +#define I386_DR_SET_RW_LEN(state, i,rwlen) \ > + do { \ > + (state)->dr_control_mirror &=3D \ > + ~(0x0f << (DR_CONTROL_SHIFT+DR_CONTROL_SIZE*(i))); \ > + (state)->dr_control_mirror |=3D \ > + ((rwlen) << (DR_CONTROL_SHIFT+DR_CONTROL_SIZE*(i))); \ > + } while (0) > + > +/* Get from DR7 the RW and LEN fields for the I'th debug register. */ > +#define I386_DR_GET_RW_LEN(state, i) \ > + (((state)->dr_control_mirror >> (DR_CONTROL_SHIFT + DR_CONTROL_SIZE > * (i))) & 0x0f) > + > +/* Did the watchpoint whose address is in the I'th register break? */ > +#define I386_DR_WATCH_HIT(state,i) ((state)->dr_status_mirror & (1 << > (i))) > + > +/* A macro to loop over all debug registers. */ > +#define ALL_DEBUG_REGISTERS(i) for (i =3D 0; i < DR_NADDR; i++) > + > +/* Whether or not to print the mirrored debug registers. */ > +static int maint_show_dr =3D 0; > + > +/* Types of operations supported by i386_handle_nonaligned_watchpoint. > */ > +typedef enum { WP_INSERT, WP_REMOVE, WP_COUNT } i386_wp_op_t; > + > +/* Internal functions. */ > + > +/* Return the value of a 4-bit field for DR7 suitable for watching a > + region of LEN bytes for accesses of type TYPE. LEN is assumed to > + have the value of 1, 2, or 4. */ > +static unsigned i386_length_and_rw_bits (int len, enum > target_hw_bp_type type); > + > +/* Insert a watchpoint at address ADDR, which is assumed to be aligned > + according to the length of the region to watch. LEN_RW_BITS is the > + value of the bit-field from DR7 which describes the length and > + access type of the region to be watched by this watchpoint. Return > + 0 on success, -1 on failure. */ > +static int i386_insert_aligned_watchpoint (struct i386_debug_reg_state > *state, > + CORE_ADDR addr, > + unsigned len_rw_bits); > + > +/* Remove a watchpoint at address ADDR, which is assumed to be aligned > + according to the length of the region to watch. LEN_RW_BITS is the > + value of the bits from DR7 which describes the length and access > + type of the region watched by this watchpoint. Return 0 on > + success, -1 on failure. */ > +static int i386_remove_aligned_watchpoint (struct i386_debug_reg_state > *state, > + CORE_ADDR addr, > + unsigned len_rw_bits); > + > +/* Insert or remove a (possibly non-aligned) watchpoint, or count the > + number of debug registers required to watch a region at address > + ADDR whose length is LEN for accesses of type TYPE. Return 0 on > + successful insertion or removal, a positive number when queried > + about the number of registers, or -1 on failure. If WHAT is not a > + valid value, bombs through internal_error. */ > +static int i386_handle_nonaligned_watchpoint (struct > i386_debug_reg_state *state, > + i386_wp_op_t what, > + CORE_ADDR addr, int len, > + enum target_hw_bp_type type); > + > +/* Implementation. */ > + > +/* Clear the reference counts and forget everything we knew about the > + debug registers. */ > + > +void > +i386_low_cleanup_dregs (struct i386_debug_reg_state *state) > +{ > + int i; > + > + ALL_DEBUG_REGISTERS(i) > + { > + state->dr_mirror[i] =3D 0; > + state->dr_ref_count[i] =3D 0; > + } > + state->dr_control_mirror =3D 0; > + state->dr_status_mirror =3D 0; > +} > + > +/* Print the values of the mirrored debug registers. This is called > + when maint_show_dr is non-zero. To set that up, type "maint > + show-debug-regs" at GDB's prompt. */ > + > +static void > +i386_show_dr (struct i386_debug_reg_state *state, > + const char *func, CORE_ADDR addr, > + int len, enum target_hw_bp_type type) > +{ > + int i; > + > + printf (func); > + if (addr || len) > + printf (" (addr=3D%lx, len=3D%d, type=3D%s)", > + (unsigned long) addr, len, > + type =3D=3D hw_write ? "data-write" > + : (type =3D=3D hw_read ? "data-read" > + : (type =3D=3D hw_access ? "data-read/write" > + : (type =3D=3D hw_execute ? "instruction-execute" > + /* FIXME: if/when I/O read/write > + watchpoints are supported, add them > + here. */ > + : "??unknown??")))); > + printf (":\n"); > + printf ("\tCONTROL (DR7): %08x STATUS (DR6): %08x\n", > + state->dr_control_mirror, state->dr_status_mirror); > + ALL_DEBUG_REGISTERS (i) > + { > + printf ("\ > +\tDR%d: addr=3D0x%s, ref.count=3D%d DR%d: addr=3D0x%s, ref.count=3D%d\n= ", > + i, paddr (state->dr_mirror[i]), state- > >dr_ref_count[i], > + i+1, paddr (state->dr_mirror[i+1]), state- > >dr_ref_count[i+1]); > + i++; > + } > +} > + > +/* Return the value of a 4-bit field for DR7 suitable for watching a > + region of LEN bytes for accesses of type TYPE. LEN is assumed to > + have the value of 1, 2, or 4. */ > + > +static unsigned > +i386_length_and_rw_bits (int len, enum target_hw_bp_type type) > +{ > + unsigned rw; > + > + switch (type) > + { > + case hw_execute: > + rw =3D DR_RW_EXECUTE; > + break; > + case hw_write: > + rw =3D DR_RW_WRITE; > + break; > + case hw_read: > + /* The i386 doesn't support data-read watchpoints. */ > + case hw_access: > + rw =3D DR_RW_READ; > + break; > +#if 0 > + /* Not yet supported. */ > + case hw_io_access: > + rw =3D DR_RW_IORW; > + break; > +#endif > + default: > + error ("\ > +Invalid hardware breakpoint type %d in i386_length_and_rw_bits.\n", > + (int) type); > + } > + > + switch (len) > + { > + case 1: > + return (DR_LEN_1 | rw); > + case 2: > + return (DR_LEN_2 | rw); > + case 4: > + return (DR_LEN_4 | rw); > + case 8: > + if (TARGET_HAS_DR_LEN_8) > + return (DR_LEN_8 | rw); > + default: > + error ("\ > +Invalid hardware breakpoint length %d in i386_length_and_rw_bits.\n", > len); > + } > +} > + > +/* Insert a watchpoint at address ADDR, which is assumed to be aligned > + according to the length of the region to watch. LEN_RW_BITS is the > + value of the bits from DR7 which describes the length and access > + type of the region to be watched by this watchpoint. Return 0 on > + success, -1 on failure. */ > + > +static int > +i386_insert_aligned_watchpoint (struct i386_debug_reg_state *state, > + CORE_ADDR addr, unsigned len_rw_bits) > +{ > + int i; > + > + /* First, look for an occupied debug register with the same address > + and the same RW and LEN definitions. If we find one, we can > + reuse it for this watchpoint as well (and save a register). */ > + ALL_DEBUG_REGISTERS(i) > + { > + if (!I386_DR_VACANT (state, i) > + && state->dr_mirror[i] =3D=3D addr > + && I386_DR_GET_RW_LEN (state, i) =3D=3D len_rw_bits) > + { > + state->dr_ref_count[i]++; > + return 0; > + } > + } > + > + /* Next, look for a vacant debug register. */ > + ALL_DEBUG_REGISTERS(i) > + { > + if (I386_DR_VACANT (state, i)) > + break; > + } > + > + /* No more debug registers! */ > + if (i >=3D DR_NADDR) > + return -1; > + > + /* Now set up the register I to watch our region. */ > + > + /* Record the info in our local mirrored array. */ > + state->dr_mirror[i] =3D addr; > + state->dr_ref_count[i] =3D 1; > + I386_DR_SET_RW_LEN (state, i, len_rw_bits); > + /* Note: we only enable the watchpoint locally, i.e. in the current > + task. Currently, no i386 target allows or supports global > + watchpoints; however, if any target would want that in the > + future, GDB should probably provide a command to control whether > + to enable watchpoints globally or locally, and the code below > + should use global or local enable and slow-down flags as > + appropriate. */ > + I386_DR_LOCAL_ENABLE (state, i); > + state->dr_control_mirror |=3D DR_LOCAL_SLOWDOWN; > + state->dr_control_mirror &=3D I386_DR_CONTROL_MASK; > + > + /* Finally, actually pass the info to the inferior. */ > + i386_dr_low_set_addr (i, addr); > + i386_dr_low_set_control (state->dr_control_mirror); > + > + return 0; > +} > + > +/* Reset a watched address for debug reg DR. > + All implementations just do foo_set_addr (addr, 0), so this is a > wrapper > + around foo_set_addr to maintain the concept that this is a reset. > */ > + > +static void > +i386_dr_low_reset_addr (int dr) > +{ > + i386_dr_low_set_addr (dr, 0); > +} > + > +/* Remove a watchpoint at address ADDR, which is assumed to be aligned > + according to the length of the region to watch. LEN_RW_BITS is the > + value of the bits from DR7 which describes the length and access > + type of the region watched by this watchpoint. Return 0 on > + success, -1 on failure. */ > + > +static int > +i386_remove_aligned_watchpoint (struct i386_debug_reg_state *state, > + CORE_ADDR addr, unsigned len_rw_bits) > +{ > + int i, retval =3D -1; > + > + ALL_DEBUG_REGISTERS(i) > + { > + if (!I386_DR_VACANT (state, i) > + && state->dr_mirror[i] =3D=3D addr > + && I386_DR_GET_RW_LEN (state, i) =3D=3D len_rw_bits) > + { > + if (--state->dr_ref_count[i] =3D=3D 0) /* no longer in use? */ > + { > + /* Reset our mirror. */ > + state->dr_mirror[i] =3D 0; > + I386_DR_DISABLE (state, i); > + /* Reset it in the inferior. */ > + i386_dr_low_set_control (state->dr_control_mirror); > + i386_dr_low_reset_addr (i); > + } > + retval =3D 0; > + } > + } > + > + return retval; > +} > + > +/* Insert or remove a (possibly non-aligned) watchpoint, or count the > + number of debug registers required to watch a region at address > + ADDR whose length is LEN for accesses of type TYPE. Return 0 on > + successful insertion or removal, a positive number when queried > + about the number of registers, or -1 on failure. If WHAT is not a > + valid value, bombs through internal_error. */ > + > +static int > +i386_handle_nonaligned_watchpoint (struct i386_debug_reg_state *state, > + i386_wp_op_t what, CORE_ADDR addr, int len, > + enum target_hw_bp_type type) > +{ > + int retval =3D 0, status =3D 0; > + int max_wp_len =3D TARGET_HAS_DR_LEN_8 ? 8 : 4; > + > + static int size_try_array[8][8] =3D > + { > + {1, 1, 1, 1, 1, 1, 1, 1}, /* Trying size one. */ > + {2, 1, 2, 1, 2, 1, 2, 1}, /* Trying size two. */ > + {2, 1, 2, 1, 2, 1, 2, 1}, /* Trying size three. */ > + {4, 1, 2, 1, 4, 1, 2, 1}, /* Trying size four. */ > + {4, 1, 2, 1, 4, 1, 2, 1}, /* Trying size five. */ > + {4, 1, 2, 1, 4, 1, 2, 1}, /* Trying size six. */ > + {4, 1, 2, 1, 4, 1, 2, 1}, /* Trying size seven. */ > + {8, 1, 2, 1, 4, 1, 2, 1}, /* Trying size eight. */ > + }; > + > + while (len > 0) > + { > + int align =3D addr % max_wp_len; > + /* Four (eight on AMD64) is the maximum length a debug register > + can watch. */ > + int try =3D (len > max_wp_len ? (max_wp_len - 1) : len - 1); > + int size =3D size_try_array[try][align]; > + > + if (what =3D=3D WP_COUNT) > + { > + /* size_try_array[] is defined such that each iteration > + through the loop is guaranteed to produce an address and a > + size that can be watched with a single debug register. > + Thus, for counting the registers required to watch a > + region, we simply need to increment the count on each > + iteration. */ > + retval++; > + } > + else > + { > + unsigned len_rw =3D i386_length_and_rw_bits (size, type); > + > + if (what =3D=3D WP_INSERT) > + status =3D i386_insert_aligned_watchpoint (state, addr, > len_rw); > + else if (what =3D=3D WP_REMOVE) > + status =3D i386_remove_aligned_watchpoint (state, addr, > len_rw); > +#if 0 > + else > + internal_error (__FILE__, __LINE__, _("\ > +Invalid value %d of operation in > i386_handle_nonaligned_watchpoint.\n"), > + (int)what); > +#endif > + /* We keep the loop going even after a failure, because some > + of the other aligned watchpoints might still succeed > + (e.g. if they watch addresses that are already watched, > + in which case we just increment the reference counts of > + occupied debug registers). If we break out of the loop > + too early, we could cause those addresses watched by > + other watchpoints to be disabled when breakpoint.c reacts > + to our failure to insert this watchpoint and tries to > + remove it. */ > + if (status) > + retval =3D status; > + } > + > + addr +=3D size; > + len -=3D size; > + } > + > + return retval; > +} > + > +#define Z_PACKET_WRITE_WP '2' > +#define Z_PACKET_READ_WP '3' > +#define Z_PACKET_ACCESS_WP '4' > + > +static int > +Z_packet_to_hw_type (char type) > +{ > + switch (type) > + { > + case Z_PACKET_WRITE_WP: return hw_write; > + case Z_PACKET_READ_WP: return hw_read; > + case Z_PACKET_ACCESS_WP: return hw_access; > + default: > + error ("Z_packet_to_hw_type: bad watchpoint type %c", type); > + } > +} > + > +/* Insert a watchpoint to watch a memory region which starts at > + address ADDR and whose length is LEN bytes. Watch memory accesses > + of the type TYPE_FROM_PACKET. Return 0 on success, -1 on failure. > */ > + > +int > +i386_low_insert_watchpoint (struct i386_debug_reg_state *state, > + char type_from_packet, CORE_ADDR addr, int len) > +{ > + int retval; > + int type =3D Z_packet_to_hw_type (type_from_packet); > + > + if (((len !=3D 1 && len !=3D2 && len !=3D4) && !(TARGET_HAS_DR_LEN_8 && > len =3D=3D 8)) > + || addr % len !=3D 0) > + { > + retval =3D i386_handle_nonaligned_watchpoint (state, WP_INSERT, > + addr, len, type); > + } > + else > + { > + unsigned len_rw =3D i386_length_and_rw_bits (len, type); > + > + retval =3D i386_insert_aligned_watchpoint (state, addr, len_rw); > + } > + > + if (maint_show_dr) > + i386_show_dr (state, "insert_watchpoint", addr, len, type); > + > + return retval; > +} > + > +/* Remove a watchpoint that watched the memory region which starts at > + address ADDR, whose length is LEN bytes, and for accesses of the > + type TYPE_FROM_PACKET. Return 0 on success, -1 on failure. */ > + > +int > +i386_low_remove_watchpoint (struct i386_debug_reg_state *state, > + char type_from_packet, CORE_ADDR addr, int len) > +{ > + int retval; > + int type =3D Z_packet_to_hw_type (type_from_packet); > + > + if (((len !=3D 1 && len !=3D2 && len !=3D4) && !(TARGET_HAS_DR_LEN_8 && > len =3D=3D 8)) > + || addr % len !=3D 0) > + { > + retval =3D i386_handle_nonaligned_watchpoint (state, WP_REMOVE, > + addr, len, type); > + } > + else > + { > + unsigned len_rw =3D i386_length_and_rw_bits (len, type); > + > + retval =3D i386_remove_aligned_watchpoint (state, addr, len_rw); > + } > + if (maint_show_dr) > + i386_show_dr (state, "remove_watchpoint", addr, len, type); > + return retval; > +} > + > +/* Return non-zero if we can watch a memory region that starts at > + address ADDR and whose length is LEN bytes. */ > + > +int > +i386_low_region_ok_for_watchpoint (struct i386_debug_reg_state *state, > + CORE_ADDR addr, int len) > +{ > + int nregs; > + > + /* Compute how many aligned watchpoints we would need to cover this > + region. */ > + nregs =3D i386_handle_nonaligned_watchpoint (state, WP_COUNT, > + addr, len, hw_write); > + return nregs <=3D DR_NADDR ? 1 : 0; > +} > + > +/* If the inferior has some watchpoint that triggered, set the > + address associated with that watchpoint and return non-zero. > + Otherwise, return zero. */ > + > +CORE_ADDR > +i386_low_stopped_data_address (struct i386_debug_reg_state *state) > +{ > + CORE_ADDR addr =3D 0; > + int i; > + int rc =3D 0; > + > + state->dr_status_mirror =3D i386_dr_low_get_status (); > + > + ALL_DEBUG_REGISTERS(i) > + { > + if (I386_DR_WATCH_HIT (state, i) > + /* This second condition makes sure DRi is set up for a data > + watchpoint, not a hardware breakpoint. The reason is > + that GDB doesn't call the target_stopped_data_address > + method except for data watchpoints. In other words, I'm > + being paranoiac. */ > + && I386_DR_GET_RW_LEN (state, i) !=3D 0) > + { > + addr =3D state->dr_mirror[i]; > + rc =3D 1; > + if (maint_show_dr) > + i386_show_dr (state, "watchpoint_hit", addr, -1, hw_write); > + } > + } > + if (maint_show_dr && addr =3D=3D 0) > + i386_show_dr (state, "stopped_data_addr", 0, 0, hw_write); > + > + /* NOTE: gdb version checks rc !=3D 0 here. */ > + return addr; > +} > + > +int > +i386_low_stopped_by_watchpoint (struct i386_debug_reg_state *state) > +{ > + CORE_ADDR addr =3D 0; > + /* NOTE: gdb version passes boolean found/not-found result from > + i386_stopped_data_address. */ > + addr =3D i386_low_stopped_data_address (state); > + return (addr !=3D 0); > +} > + > +/* Support for h/w breakpoints. > + This support is not currently used, kept for reference. */ > + > +/* Return non-zero if the inferior has some break/watchpoint that > + triggered. */ > + > +int > +i386_low_stopped_by_hwbp (struct i386_debug_reg_state *state) > +{ > + int i; > + > + state->dr_status_mirror =3D i386_dr_low_get_status (); > + if (maint_show_dr) > + i386_show_dr (state, "stopped_by_hwbp", 0, 0, hw_execute); > + > + ALL_DEBUG_REGISTERS(i) > + { > + if (I386_DR_WATCH_HIT (state, i)) > + return 1; > + } > + > + return 0; > +} > + > +/* Insert a hardware-assisted breakpoint at ADDR. > + Return 0 on success, -1 on failure. */ > + > +int > +i386_low_insert_hw_breakpoint (struct i386_debug_reg_state *state, > + CORE_ADDR addr) > +{ > + unsigned len_rw =3D i386_length_and_rw_bits (1, hw_execute); > + int retval =3D i386_insert_aligned_watchpoint (state, addr, len_rw); > + > + if (maint_show_dr) > + i386_show_dr (state, "insert_hwbp", addr, 1, hw_execute); > + > + return retval; > +} > + > +/* Remove a hardware-assisted breakpoint at ADDR. > + Return 0 on success, -1 on failure. */ > + > +int > +i386_low_remove_hw_breakpoint (struct i386_debug_reg_state *state, > + CORE_ADDR addr) > +{ > + unsigned len_rw =3D i386_length_and_rw_bits (1, hw_execute); > + int retval =3D i386_remove_aligned_watchpoint (state, addr, len_rw); > + > + if (maint_show_dr) > + i386_show_dr (state, "remove_hwbp", addr, 1, hw_execute); > + > + return retval; > +} > Index: i386-low.h > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > RCS file: i386-low.h > diff -N i386-low.h > --- /dev/null 1 Jan 1970 00:00:00 -0000 > +++ i386-low.h 30 Apr 2009 01:23:29 -0000 > @@ -0,0 +1,118 @@ > +/* Misc. low level support for i386. > + > + Copyright (C) 2009 > + 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 > . */ > + > +/* Support for hardware watchpoints and breakpoints using the i386 > + debug registers. > + > + This provides several functions for inserting and removing > + hardware-assisted breakpoints and watchpoints, testing if one or > + more of the watchpoints triggered and at what address, checking > + whether a given region can be watched, etc. > + > + The functions below implement debug registers sharing by reference > + counts, and allow to watch regions up to 16 bytes long > + (32 bytes on 64 bit hosts). */ > + > + > +/* Debug registers' indices. */ > +#define DR_FIRSTADDR 0 > +#define DR_LASTADDR 3 > +#define DR_NADDR 4 /* The number of debug address registers. */ > +#define DR_STATUS 6 > +#define DR_CONTROL 7 > + > +/* Global state needed to track h/w watchpoints. */ > + > +struct i386_debug_reg_state > +{ > + /* Mirror the inferior's DRi registers. We keep the status and > + control registers separated because they don't hold addresses. > */ > + CORE_ADDR dr_mirror[DR_NADDR]; > + unsigned dr_status_mirror, dr_control_mirror; > + > + /* Reference counts for each debug register. */ > + int dr_ref_count[DR_NADDR]; > +}; > + > +/* Clear the reference counts and forget everything we knew about DRi. > */ > +extern void i386_low_cleanup_dregs (struct i386_debug_reg_state > *state); > + > +/* Insert a watchpoint to watch a memory region which starts at > + address ADDR and whose length is LEN bytes. Watch memory accesses > + of the type TYPE_FROM_PACKET. Return 0 on success, -1 on failure. > */ > +extern int i386_low_insert_watchpoint (struct i386_debug_reg_state > *state, > + char type_from_packet, CORE_ADDR addr, > + int len); > + > +/* Remove a watchpoint that watched the memory region which starts at > + address ADDR, whose length is LEN bytes, and for accesses of the > + type TYPE_FROM_PACKET. Return 0 on success, -1 on failure. */ > +extern int i386_low_remove_watchpoint (struct i386_debug_reg_state > *state, > + char type_from_packet, CORE_ADDR addr, > + int len); > + > +/* Return non-zero if we can watch a memory region that starts at > + address ADDR and whose length is LEN bytes. */ > +extern int i386_low_region_ok_for_watchpoint (struct > i386_debug_reg_state *state, > + CORE_ADDR addr, int len); > + > +/* If the inferior has some break/watchpoint that triggered, set > + the address associated with that break/watchpoint and return > + true. Otherwise, return false. */ > +extern CORE_ADDR i386_low_stopped_data_address (struct > i386_debug_reg_state *state); > + > +extern int i386_low_stopped_by_watchpoint (struct i386_debug_reg_state > *state); > + > +/* Return non-zero if the inferior has some break/watchpoint that > + triggered. */ > +extern int i386_low_stopped_by_hwbp (struct i386_debug_reg_state > *state); > + > +/* Insert a hardware-assisted breakpoint at ADDR. > + Return 0 on success, -1 on failure. */ > +extern int i386_low_insert_hw_breakpoint (struct i386_debug_reg_state > *state, > + CORE_ADDR addr); > + > +/* Remove a hardware-assisted breakpoint at ADDR. > + Return 0 on success, -1 on failure. */ > +extern int i386_low_remove_hw_breakpoint (struct i386_debug_reg_state > *state, > + CORE_ADDR addr); > + > +/* Each target needs to provide several low-level functions > + that will be called to insert watchpoints and hardware breakpoints > + into the inferior, remove them, and check their status. These > + functions are: > + > + i386_dr_low_set_control -- set the debug control (DR7) > + register to a given value > + > + i386_dr_low_set_addr -- put an address into one debug > register > + > + i386_dr_low_get_status -- return the value of the debug > + status (DR6) register. > +*/ > + > +/* Pass the address ADDR to the inferior in the I'th debug register. > */ > +extern void i386_dr_low_set_addr (int i, CORE_ADDR addr); > + > +/* Pass the value VAL to the inferior in the DR7 debug control > register. */ > +extern void i386_dr_low_set_control (unsigned val); > + > +/* Get the value of the DR6 debug status register from the inferior. > */ > +extern unsigned i386_dr_low_get_status (void); > Index: linux-i386-low.c > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > RCS file: /cvs/src/src/gdb/gdbserver/linux-i386-low.c,v > retrieving revision 1.19 > diff -u -p -r1.19 linux-i386-low.c > --- linux-i386-low.c 22 Mar 2009 23:57:10 -0000 1.19 > +++ linux-i386-low.c 30 Apr 2009 01:23:29 -0000 > @@ -17,9 +17,11 @@ > You should have received a copy of the GNU General Public License > along with this program. If not, see > . */ >=20 > +#include > #include "server.h" > #include "linux-low.h" > #include "i387-fp.h" > +#include "i386-low.h" >=20 > #include "gdb_proc_service.h" >=20 > @@ -33,10 +35,14 @@ > #define PTRACE_GET_THREAD_AREA 25 > #endif >=20 > +struct arch_process_info > +{ > + struct i386_debug_reg_state debug_reg_state; > +}; > + > /* Defined in auto-generated file reg-i386-linux.c. */ > void init_registers_i386_linux (void); >=20 > - > /* This module only supports access to the general purpose registers. > */ >=20 > #define i386_num_regs 16 > @@ -187,6 +193,148 @@ i386_breakpoint_at (CORE_ADDR pc) >=20 > return 0; > } > + > +/* Support for debug registers. */ > + > +static unsigned long > +i386_linux_dr_get (ptid_t ptid, int regnum) > +{ > + int tid; > + unsigned long value; > + > + tid =3D TIDGET (ptid); > + if (tid =3D=3D 0) > + tid =3D PIDGET (ptid); > + > + errno =3D 0; > + value =3D ptrace (PTRACE_PEEKUSER, tid, > + offsetof (struct user, u_debugreg[regnum]), 0); > + if (errno !=3D 0) > + error ("Couldn't read debug register"); > + > + return value; > +} > + > +static void > +i386_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) > +{ > + int tid; > + > + tid =3D TIDGET (ptid); > + if (tid =3D=3D 0) > + tid =3D PIDGET (ptid); > + > + errno =3D 0; > + ptrace (PTRACE_POKEUSER, tid, > + offsetof (struct user, u_debugreg[regnum]), value); > + if (errno !=3D 0) > + error ("Couldn't write debug register"); > +} > + > +/* Pass the address ADDR to the inferior in debug register REGNUM. */ > + > +void > +i386_dr_low_set_addr (int regnum, CORE_ADDR addr) > +{ > + struct inferior_list_entry *lp; > + > + if (! (regnum >=3D 0 && regnum <=3D DR_LASTADDR - DR_FIRSTADDR)) > + error ("Invalid debug register"); > + > + for (lp =3D all_lwps.head; lp; lp =3D lp->next) > + { > + struct lwp_info *lwp =3D (struct lwp_info *) lp; > + i386_linux_dr_set (ptid_of (lwp), DR_FIRSTADDR + regnum, addr); > + } > +} > + > +/* Pass the value VAL to the inferior in the DR7 debug control > register. */ > + > +void > +i386_dr_low_set_control (unsigned control) > +{ > + struct inferior_list_entry *lp; > + > + for (lp =3D all_lwps.head; lp; lp =3D lp->next) > + { > + struct lwp_info *lwp =3D (struct lwp_info *) lp; > + i386_linux_dr_set (ptid_of (lwp), DR_CONTROL, control); > + } > +} > + > +/* Get the value of the DR6 debug status register from the inferior. > */ > + > +unsigned > +i386_dr_low_get_status (void) > +{ > + ptid_t inferior_ptid =3D ptid_of (get_thread_lwp (current_inferior)); > + return i386_linux_dr_get (inferior_ptid, DR_STATUS); > +} > + > +/* Watchpoint support. */ > + > +static int > +i386_insert_watchpoint (struct arch_process_info *arch_private, > + char type, CORE_ADDR addr, int len) > +{ > + return i386_low_insert_watchpoint (&arch_private->debug_reg_state, > + type, addr, len); > +} > + > +static int > +i386_remove_watchpoint (struct arch_process_info *arch_private, > + char type, CORE_ADDR addr, int len) > +{ > + return i386_low_remove_watchpoint (&arch_private->debug_reg_state, > + type, addr, len); > +} > + > +static int > +i386_stopped_by_watchpoint (struct arch_process_info *arch_private) > +{ > + return i386_low_stopped_by_watchpoint (&arch_private- > >debug_reg_state); > +} > + > +static CORE_ADDR > +i386_stopped_data_address (struct arch_process_info *arch_private) > +{ > + return i386_low_stopped_data_address (&arch_private- > >debug_reg_state); > +} > + > +/* Called when a new process is created. */ > + > +struct arch_process_info * > +i386_linux_new_process (int pid, int attached) > +{ > + struct arch_process_info *info =3D xcalloc (1, sizeof (*info)); > + > + return info; > +} > + > +/* Called when a new thread is detected. > + INITIAL is non-zero for the main thread, zero otherwise. */ > + > +static void > +i386_linux_new_thread (struct lwp_info *lwp, int initial) > +{ > + /* The initial thread has debug regs set later during process > + creation/attaching handling. */ > + > + if (! initial) > + { > + int i; > + ptid_t ptid =3D ptid_of (lwp); > + int pid =3D PIDGET (ptid); > + struct process_info *proc =3D find_process_pid (pid); > + struct arch_process_info *arch_private =3D proc->private- > >arch_private; > + struct i386_debug_reg_state *state =3D &arch_private- > >debug_reg_state; > + > + for (i =3D DR_FIRSTADDR; i <=3D DR_LASTADDR; i++) > + i386_linux_dr_set (ptid, i, state->dr_mirror[i]); > + > + i386_linux_dr_set (ptid, DR_CONTROL, state->dr_control_mirror); > + } > +} >=20 > struct linux_target_ops the_low_target =3D { > init_registers_i386_linux, > @@ -201,10 +349,12 @@ struct linux_target_ops the_low_target =3D > NULL, > 1, > i386_breakpoint_at, > + i386_insert_watchpoint, > + i386_remove_watchpoint, > + i386_stopped_by_watchpoint, > + i386_stopped_data_address, > NULL, > NULL, > - NULL, > - NULL, > - NULL, > - NULL, > + i386_linux_new_process, > + i386_linux_new_thread > }; > Index: linux-low.c > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > RCS file: /cvs/src/src/gdb/gdbserver/linux-low.c,v > retrieving revision 1.97 > diff -u -p -r1.97 linux-low.c > --- linux-low.c 3 Apr 2009 11:40:02 -0000 1.97 > +++ linux-low.c 30 Apr 2009 06:26:54 -0000 > @@ -120,6 +120,7 @@ static int must_set_ptrace_flags; > control of gdbserver have the same architecture. */ > static int new_inferior; >=20 > +static void notify_low_target_new_thread (struct lwp_info *lwp, int > initial); > static void linux_resume_one_lwp (struct inferior_list_entry *entry, > int step, int signal, siginfo_t *info); > static void linux_resume (struct thread_resume *resume_info, size_t > n); > @@ -179,6 +180,9 @@ linux_add_process (int pid, int attached > proc =3D add_process (pid, attached); > proc->private =3D xcalloc (1, sizeof (*proc->private)); >=20 > + if (the_low_target.new_process !=3D NULL) > + proc->private->arch_private =3D the_low_target.new_process (pid, > attached); > + > return proc; > } >=20 > @@ -188,6 +192,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); > } > @@ -231,6 +236,7 @@ handle_extended_wait (struct lwp_info *e > ptid =3D ptid_build (pid_of (event_child), new_pid, 0); > new_lwp =3D (struct lwp_info *) add_lwp (ptid); > add_thread (ptid, new_lwp); > + notify_low_target_new_thread (new_lwp, 0); >=20 > /* Normally we will get the pending SIGSTOP. But in some cases > we might get another signal delivered to the group first. > @@ -313,6 +319,18 @@ add_lwp (ptid_t ptid) > return lwp; > } >=20 > +/* Notify the backend that we have a new thread, be it the main thread > + or a later one. > + INITIAL is non-zero for the main thread, zero otherwise. > + NOTE: This must be called when the thread is stopped. */ > + > +static void > +notify_low_target_new_thread (struct lwp_info *lwp, int initial) > +{ > + if (the_low_target.new_thread !=3D NULL) > + the_low_target.new_thread (lwp, initial); > +} > + > /* Start an inferior process and returns its pid. > ALLARGS is a vector of program-name and args. */ >=20 > @@ -354,6 +372,8 @@ linux_create_inferior (char *program, ch > ptid =3D ptid_build (pid, pid, 0); > new_lwp =3D add_lwp (ptid); > add_thread (ptid, new_lwp); > + /* We can't call notify_low_target_new_thread there, the thread > isn't > + stopped. Piggyback on must_set_ptrace_flags and call it then. > */ > must_set_ptrace_flags =3D 1; >=20 > return pid; > @@ -401,6 +421,7 @@ linux_attach_lwp_1 (unsigned long lwpid, >=20 > new_lwp =3D (struct lwp_info *) add_lwp (ptid); > add_thread (ptid, new_lwp); > + notify_low_target_new_thread (new_lwp, initial); >=20 > /* The next time we wait for this LWP we'll see a SIGSTOP as > PTRACE_ATTACH > brings it to a halt. > @@ -1261,6 +1282,7 @@ retry: > if (must_set_ptrace_flags) > { > ptrace (PTRACE_SETOPTIONS, lwpid_of (lwp), 0, > PTRACE_O_TRACECLONE); > + notify_low_target_new_thread (lwp, 1); > must_set_ptrace_flags =3D 0; > } > /* If we are waiting for a particular child, and it exited, > @@ -2599,7 +2623,11 @@ static int > linux_insert_watchpoint (char type, CORE_ADDR addr, int len) > { > if (the_low_target.insert_watchpoint !=3D NULL) > - return the_low_target.insert_watchpoint (type, addr, len); > + { > + struct process_info *proc =3D current_process (); > + return the_low_target.insert_watchpoint (proc->private- > >arch_private, > + type, addr, len); > + } > else > /* Unsupported (see target.h). */ > return 1; > @@ -2609,7 +2637,11 @@ static int > linux_remove_watchpoint (char type, CORE_ADDR addr, int len) > { > if (the_low_target.remove_watchpoint !=3D NULL) > - return the_low_target.remove_watchpoint (type, addr, len); > + { > + struct process_info *proc =3D current_process (); > + return the_low_target.remove_watchpoint (proc->private- > >arch_private, > + type, addr, len); > + } > else > /* Unsupported (see target.h). */ > return 1; > @@ -2619,7 +2651,10 @@ static int > linux_stopped_by_watchpoint (void) > { > if (the_low_target.stopped_by_watchpoint !=3D NULL) > - return the_low_target.stopped_by_watchpoint (); > + { > + struct process_info *proc =3D current_process (); > + return the_low_target.stopped_by_watchpoint (proc->private- > >arch_private); > + } > else > return 0; > } > @@ -2628,7 +2663,10 @@ static CORE_ADDR > linux_stopped_data_address (void) > { > if (the_low_target.stopped_data_address !=3D NULL) > - return the_low_target.stopped_data_address (); > + { > + struct process_info *proc =3D current_process (); > + return the_low_target.stopped_data_address (proc->private- > >arch_private); > + } > else > return 0; > } > Index: linux-low.h > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > RCS file: /cvs/src/src/gdb/gdbserver/linux-low.h,v > retrieving revision 1.28 > diff -u -p -r1.28 linux-low.h > --- linux-low.h 1 Apr 2009 22:50:24 -0000 1.28 > +++ linux-low.h 30 Apr 2009 01:23:29 -0000 > @@ -54,8 +54,13 @@ struct process_info_private >=20 > /* Connection to the libthread_db library. */ > td_thragent_t *thread_agent; > + > + /* Target-specific additions. */ > + struct arch_process_info *arch_private; > }; >=20 > +struct lwp_info; > + > struct linux_target_ops > { > /* Architecture-specific setup. */ > @@ -75,26 +80,37 @@ struct linux_target_ops > int breakpoint_len; > CORE_ADDR (*breakpoint_reinsert_addr) (void); >=20 > - > int decr_pc_after_break; > int (*breakpoint_at) (CORE_ADDR pc); >=20 > /* Watchpoint related functions. See target.h for comments. */ > - int (*insert_watchpoint) (char type, CORE_ADDR addr, int len); > - int (*remove_watchpoint) (char type, CORE_ADDR addr, int len); > - int (*stopped_by_watchpoint) (void); > - CORE_ADDR (*stopped_data_address) (void); > + int (*insert_watchpoint) (struct arch_process_info *arch_private, > + char type, CORE_ADDR addr, int len); > + int (*remove_watchpoint) (struct arch_process_info *arch_private, > + char type, CORE_ADDR addr, int len); > + int (*stopped_by_watchpoint) (struct arch_process_info > *arch_private); > + CORE_ADDR (*stopped_data_address) (struct arch_process_info > *arch_private); >=20 > /* Hooks to reformat register data for PEEKUSR/POKEUSR (in > particular > for registers smaller than an xfer unit). */ > void (*collect_ptrace_register) (int regno, char *buf); > void (*supply_ptrace_register) (int regno, const char *buf); > + > + /* Hook to call when a new process is created or attached to. */ > + struct arch_process_info * (*new_process) (int pid, int attached); > + > + /* Hook to call when a new thread is detected. > + INITIAL is non-zero for the main thread, zero otherwise. */ > + void (*new_thread) (struct lwp_info *, int initial); > }; >=20 > extern struct linux_target_ops the_low_target; >=20 > +#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) > +#define PIDGET(ptid) ptid_get_pid (ptid) > +#define TIDGET(ptid) ptid_get_lwp (ptid) >=20 > #define get_lwp(inf) ((struct lwp_info *)(inf)) > #define get_thread_lwp(thr) (get_lwp (inferior_target_data (thr))) > Index: linux-x86-64-low.c > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > RCS file: /cvs/src/src/gdb/gdbserver/linux-x86-64-low.c,v > retrieving revision 1.22 > diff -u -p -r1.22 linux-x86-64-low.c > --- linux-x86-64-low.c 22 Mar 2009 23:57:10 -0000 1.22 > +++ linux-x86-64-low.c 30 Apr 2009 01:23:29 -0000 > @@ -18,9 +18,11 @@ > You should have received a copy of the GNU General Public License > along with this program. If not, see > . */ >=20 > +#include > #include "server.h" > #include "linux-low.h" > #include "i387-fp.h" > +#include "i386-low.h" >=20 > #include "gdb_proc_service.h" >=20 > @@ -45,6 +47,11 @@ void init_registers_x86_64_linux (void); > #define ARCH_GET_GS 0x1004 > #endif >=20 > +struct arch_process_info > +{ > + struct i386_debug_reg_state debug_reg_state; > +}; > + > static int x86_64_regmap[] =3D { > RAX * 8, RBX * 8, RCX * 8, RDX * 8, > RSI * 8, RDI * 8, RBP * 8, RSP * 8, > @@ -161,6 +168,148 @@ x86_64_breakpoint_at (CORE_ADDR pc) >=20 > return 0; > } > + > +/* Support for debug registers. */ > + > +static unsigned long > +x86_64_linux_dr_get (ptid_t ptid, int regnum) > +{ > + int tid; > + unsigned long value; > + > + tid =3D TIDGET (ptid); > + if (tid =3D=3D 0) > + tid =3D PIDGET (ptid); > + > + errno =3D 0; > + value =3D ptrace (PTRACE_PEEKUSER, tid, > + offsetof (struct user, u_debugreg[regnum]), 0); > + if (errno !=3D 0) > + error ("Couldn't read debug register"); > + > + return value; > +} > + > +static void > +x86_64_linux_dr_set (ptid_t ptid, int regnum, unsigned long value) > +{ > + int tid; > + > + tid =3D TIDGET (ptid); > + if (tid =3D=3D 0) > + tid =3D PIDGET (ptid); > + > + errno =3D 0; > + ptrace (PTRACE_POKEUSER, tid, > + offsetof (struct user, u_debugreg[regnum]), value); > + if (errno !=3D 0) > + error ("Couldn't write debug register"); > +} > + > +/* Pass the address ADDR to the inferior in debug register REGNUM. */ > + > +void > +i386_dr_low_set_addr (int regnum, CORE_ADDR addr) > +{ > + struct inferior_list_entry *lp; > + > + if (! (regnum >=3D 0 && regnum <=3D DR_LASTADDR - DR_FIRSTADDR)) > + error ("Invalid debug register"); > + > + for (lp =3D all_lwps.head; lp; lp =3D lp->next) > + { > + struct lwp_info *lwp =3D (struct lwp_info *) lp; > + x86_64_linux_dr_set (ptid_of (lwp), DR_FIRSTADDR + regnum, > addr); > + } > +} > + > +/* Pass the value VAL to the inferior in the DR7 debug control > register. */ > + > +void > +i386_dr_low_set_control (unsigned control) > +{ > + struct inferior_list_entry *lp; > + > + for (lp =3D all_lwps.head; lp; lp =3D lp->next) > + { > + struct lwp_info *lwp =3D (struct lwp_info *) lp; > + x86_64_linux_dr_set (ptid_of (lwp), DR_CONTROL, control); > + } > +} > + > +/* Get the value of the DR6 debug status register from the inferior. > */ > + > +unsigned > +i386_dr_low_get_status (void) > +{ > + ptid_t inferior_ptid =3D ptid_of (get_thread_lwp (current_inferior)); > + return x86_64_linux_dr_get (inferior_ptid, DR_STATUS); > +} > + > +/* Watchpoint support. */ > + > +static int > +x86_64_insert_watchpoint (struct arch_process_info *arch_private, > + char type, CORE_ADDR addr, int len) > +{ > + return i386_low_insert_watchpoint (&arch_private->debug_reg_state, > + type, addr, len); > +} > + > +static int > +x86_64_remove_watchpoint (struct arch_process_info *arch_private, > + char type, CORE_ADDR addr, int len) > +{ > + return i386_low_remove_watchpoint (&arch_private->debug_reg_state, > + type, addr, len); > +} > + > +static int > +x86_64_stopped_by_watchpoint (struct arch_process_info *arch_private) > +{ > + return i386_low_stopped_by_watchpoint (&arch_private- > >debug_reg_state); > +} > + > +static CORE_ADDR > +x86_64_stopped_data_address (struct arch_process_info *arch_private) > +{ > + return i386_low_stopped_data_address (&arch_private- > >debug_reg_state); > +} > + > +/* Called when a new process is created. */ > + > +struct arch_process_info * > +x86_64_linux_new_process (int pid, int attached) > +{ > + struct arch_process_info *info =3D xcalloc (1, sizeof (*info)); > + > + return info; > +} > + > +/* Called when a new thread is detected. > + INITIAL is non-zero for the main thread, zero otherwise. */ > + > +static void > +x86_64_linux_new_thread (struct lwp_info *lwp, int initial) > +{ > + /* The initial thread has debug regs set later during process > + creation/attaching handling. */ > + > + if (! initial) > + { > + int i; > + ptid_t ptid =3D ptid_of (lwp); > + int pid =3D PIDGET (ptid); > + struct process_info *proc =3D find_process_pid (pid); > + struct arch_process_info *arch_private =3D proc->private- > >arch_private; > + struct i386_debug_reg_state *state =3D &arch_private- > >debug_reg_state; > + > + for (i =3D DR_FIRSTADDR; i <=3D DR_LASTADDR; i++) > + x86_64_linux_dr_set (ptid, i, state->dr_mirror[i]); > + > + x86_64_linux_dr_set (ptid, DR_CONTROL, state- > >dr_control_mirror); > + } > +} >=20 > struct linux_target_ops the_low_target =3D { > init_registers_x86_64_linux, > @@ -175,10 +324,12 @@ struct linux_target_ops the_low_target =3D > NULL, > 1, > x86_64_breakpoint_at, > + x86_64_insert_watchpoint, > + x86_64_remove_watchpoint, > + x86_64_stopped_by_watchpoint, > + x86_64_stopped_data_address, > NULL, > NULL, > - NULL, > - NULL, > - NULL, > - NULL, > + x86_64_linux_new_process, > + x86_64_linux_new_thread > }; > Index: server.h > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > RCS file: /cvs/src/src/gdb/gdbserver/server.h,v > retrieving revision 1.56 > diff -u -p -r1.56 server.h > --- server.h 3 Apr 2009 20:15:51 -0000 1.56 > +++ server.h 30 Apr 2009 01:23:29 -0000 > @@ -405,6 +405,7 @@ void perror_with_name (const char *strin > 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 *paddr (CORE_ADDR addr); >=20 > /* 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). */ > Index: utils.c > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > RCS file: /cvs/src/src/gdb/gdbserver/utils.c,v > retrieving revision 1.18 > diff -u -p -r1.18 utils.c > --- utils.c 19 Jan 2009 00:16:46 -0000 1.18 > +++ utils.c 30 Apr 2009 01:23:29 -0000 > @@ -170,3 +170,37 @@ warning (const char *string,...) > fprintf (stderr, "\n"); > va_end (args); > } > + > +/* temporary storage using circular buffer */ > +#define NUMCELLS 4 > +#define CELLSIZE 50 > +static char * > +get_cell (void) > +{ > + static char buf[NUMCELLS][CELLSIZE]; > + static int cell =3D 0; > + if (++cell >=3D NUMCELLS) > + cell =3D 0; > + return buf[cell]; > +} > + > +static int > +xsnprintf (char *str, size_t size, const char *format, ...) > +{ > + va_list args; > + int ret; > + > + va_start (args, format); > + ret =3D vsnprintf (str, size, format, args); > + va_end (args); > + > + return ret; > +} > + > +char * > +paddr (CORE_ADDR addr) > +{ > + char *str =3D get_cell (); > + xsnprintf (str, CELLSIZE, "%lx", (long) addr); > + return str; > +} > Index: win32-arm-low.c > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > RCS file: /cvs/src/src/gdb/gdbserver/win32-arm-low.c,v > retrieving revision 1.8 > diff -u -p -r1.8 win32-arm-low.c > --- win32-arm-low.c 3 Jan 2009 05:57:57 -0000 1.8 > +++ win32-arm-low.c 30 Apr 2009 01:23:29 -0000 > @@ -122,4 +122,9 @@ struct win32_target_ops the_low_target =3D > NULL, /* single_step */ > (const unsigned char *) &arm_wince_breakpoint, > arm_wince_breakpoint_len, > + /* Watchpoint related functions. See target.h for comments. */ > + NULL, /* insert_watchpoint */ > + NULL, /* remove_watchpoint */ > + NULL, /* stopped_by_watchpoint */ > + NULL /* stopped_data_address */ > }; > Index: win32-i386-low.c > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > RCS file: /cvs/src/src/gdb/gdbserver/win32-i386-low.c,v > retrieving revision 1.14 > diff -u -p -r1.14 win32-i386-low.c > --- win32-i386-low.c 3 Jan 2009 05:57:57 -0000 1.14 > +++ win32-i386-low.c 30 Apr 2009 01:23:29 -0000 > @@ -17,6 +17,7 @@ >=20 > #include "server.h" > #include "win32-low.h" > +#include "i386-low.h" >=20 > #define FCS_REGNUM 27 > #define FOP_REGNUM 31 > @@ -31,6 +32,42 @@ static unsigned dr[8]; > static int debug_registers_changed =3D 0; > static int debug_registers_used =3D 0; >=20 > +/* Pass the address ADDR to the inferior in the I'th debug register. > + Here we just store the address in dr array, the registers will be > + actually set up when windows_continue is called. */ > + > +void > +i386_dr_low_set_addr (int i, CORE_ADDR addr) > +{ > + if (i < 0 || i > 3) > + return; > + dr[i] =3D (unsigned) addr; > + debug_registers_changed =3D 1; > + debug_registers_used =3D 1; > +} > + > +/* Pass the value VAL to the inferior in the DR7 debug control > register. > + Here we just store the address in D_REGS, the watchpoint > + will be actually set up in windows_wait. */ > + > +void > +i386_dr_low_set_control (unsigned val) > +{ > + dr[7] =3D val; > + debug_registers_changed =3D 1; > + debug_registers_used =3D 1; > +} > + > +/* Get the value of the DR6 debug status register from the inferior. > + Here we just return the value stored in dr[6] > + by the last call to thread_rec for current_event.dwThreadId id. */ > + > +unsigned > +i386_dr_low_get_status (void) > +{ > + return dr[6]; > +} > + > static void > i386_initial_stuff (void) > { > @@ -205,4 +242,8 @@ struct win32_target_ops the_low_target =3D > i386_single_step, > NULL, /* breakpoint */ > 0, /* breakpoint_len */ > + i386_insert_watchpoint, > + i386_remove_watchpoint, > + i386_stopped_by_watchpoint, > + i386_stopped_data_address > }; > Index: win32-low.c > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > RCS file: /cvs/src/src/gdb/gdbserver/win32-low.c,v > retrieving revision 1.35 > diff -u -p -r1.35 win32-low.c > --- win32-low.c 1 Apr 2009 22:50:24 -0000 1.35 > +++ win32-low.c 30 Apr 2009 01:23:29 -0000 > @@ -228,6 +228,48 @@ child_delete_thread (DWORD pid, DWORD ti > delete_thread_info (thread); > } >=20 > +/* These watchpoint related wrapper functions simply pass on the > function call > + if the target has registered a corresponding function. */ > + > +static int > +win32_insert_watchpoint (char type, CORE_ADDR addr, int len) > +{ > + if (the_low_target.insert_watchpoint !=3D NULL) > + return the_low_target.insert_watchpoint (type, addr, len); > + else > + /* Unsupported (see target.h). */ > + return 1; > +} > + > +static int > +win32_remove_watchpoint (char type, CORE_ADDR addr, int len) > +{ > + if (the_low_target.remove_watchpoint !=3D NULL) > + return the_low_target.remove_watchpoint (type, addr, len); > + else > + /* Unsupported (see target.h). */ > + return 1; > +} > + > +static int > +win32_stopped_by_watchpoint (void) > +{ > + if (the_low_target.stopped_by_watchpoint !=3D 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 !=3D 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_op > NULL, > win32_request_interrupt, > NULL, > - NULL, > - NULL, > - NULL, > - NULL, > + win32_insert_watchpoint, > + win32_remove_watchpoint, > + win32_stopped_by_watchpoint, > + win32_stopped_data_address, > NULL, > NULL, > NULL, > Index: win32-low.h > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D > RCS file: /cvs/src/src/gdb/gdbserver/win32-low.h,v > retrieving revision 1.9 > diff -u -p -r1.9 win32-low.h > --- win32-low.h 3 Jan 2009 05:57:57 -0000 1.9 > +++ win32-low.h 30 Apr 2009 01:23:29 -0000 > @@ -70,6 +70,13 @@ struct win32_target_ops >=20 > const unsigned char *breakpoint; > int breakpoint_len; > + > + /* Watchpoint related functions. See target.h for comments. */ > + int (*insert_watchpoint) (char type, CORE_ADDR addr, int len); > + int (*remove_watchpoint) (char type, CORE_ADDR addr, int len); > + int (*stopped_by_watchpoint) (void); > + CORE_ADDR (*stopped_data_address) (void); > + > }; >=20 > extern struct win32_target_ops the_low_target;