* config/i386/nm-go32.h <top level>: Don't include nm-i386v.h,

include nm-i386.h instead.
	(TARGET_HAS_HARDWARE_WATCHPOINTS, HAVE_CONTINUABLE_WATCHPOINT)
	(TARGET_CAN_USE_HARDWARE_WATCHPOINT, STOPPED_BY_WATCHPOINT)
	(TARGET_REGION_OK_FOR_HW_WATCHPOINT, DECR_PC_AFTER_HW_BREAK)
	(target_stopped_data_address, target_insert_watchpoint)
	(target_remove_watchpoint, target_insert_hw_breakpoint)
	(target_remove_hw_breakpoint): Don't define.
	(I386_USE_GENERIC_WATCHPOINTS, I386_DR_LOW_SET_CONTROL)
	(I386_DR_LOW_SET_ADDR, I386_DR_LOW_RESET_ADDR)
	(I386_DR_LOW_GET_STATUS): Define to call appropriate go32_*
	functions from go32-nat.c.

	* config/i386/go32.mh (NATDEPFILES): Add i386-nat.o.

	* go32-nat.c <top level>: Remove prototypes for watchpoint-
	related functions.  Remove definitions of watchpoint-related
	macros.
	(go32_mourn_inferior): Call i386_cleanup_dregs instead of the
	private cleanup_dregs function.
	(cleanup_dregs, go32_insert_watchpoint)
	(go32_insert_aligned_watchpoint, go32_handle_nonaligned_watchpoint)
	(go32_remove_watchpoint, go32_remove_aligned_watchpoint)
	(go32_region_ok_for_watchpoint, go32_stopped_by_watchpoint)
	(go32_remove_hw_breakpoint, go32_insert_hw_breakpoint): Remove.
	(go32_set_dr, go32_set_dr7, go32_get_dr6): New functions.
This commit is contained in:
Eli Zaretskii 2001-03-22 09:35:19 +00:00
parent 469b781cfb
commit e24d4c64ff
4 changed files with 67 additions and 446 deletions

View File

@ -1,3 +1,35 @@
2001-03-22 Eli Zaretskii <eliz@is.elta.co.il>
Make DJGPP use the new unified support for hardware
breakpoints and watchpoints on x86 targets:
* config/i386/nm-go32.h <top level>: Don't include nm-i386v.h,
include nm-i386.h instead.
(TARGET_HAS_HARDWARE_WATCHPOINTS, HAVE_CONTINUABLE_WATCHPOINT)
(TARGET_CAN_USE_HARDWARE_WATCHPOINT, STOPPED_BY_WATCHPOINT)
(TARGET_REGION_OK_FOR_HW_WATCHPOINT, DECR_PC_AFTER_HW_BREAK)
(target_stopped_data_address, target_insert_watchpoint)
(target_remove_watchpoint, target_insert_hw_breakpoint)
(target_remove_hw_breakpoint): Don't define.
(I386_USE_GENERIC_WATCHPOINTS, I386_DR_LOW_SET_CONTROL)
(I386_DR_LOW_SET_ADDR, I386_DR_LOW_RESET_ADDR)
(I386_DR_LOW_GET_STATUS): Define to call appropriate go32_*
functions from go32-nat.c.
* config/i386/go32.mh (NATDEPFILES): Add i386-nat.o.
* go32-nat.c <top level>: Remove prototypes for watchpoint-
related functions. Remove definitions of watchpoint-related
macros.
(go32_mourn_inferior): Call i386_cleanup_dregs instead of the
private cleanup_dregs function.
(cleanup_dregs, go32_insert_watchpoint)
(go32_insert_aligned_watchpoint, go32_handle_nonaligned_watchpoint)
(go32_remove_watchpoint, go32_remove_aligned_watchpoint)
(go32_region_ok_for_watchpoint, go32_stopped_by_watchpoint)
(go32_remove_hw_breakpoint, go32_insert_hw_breakpoint): Remove.
(go32_set_dr, go32_set_dr7, go32_get_dr6): New functions.
2001-03-21 Kevin Buettner <kevinb@redhat.com>
* ia64-tdep.c (fetch_instruction): Warn about slot numbers greater

View File

@ -8,9 +8,9 @@ XM_FILE= xm-go32.h
XDEPFILES=
NAT_FILE= nm-go32.h
NATDEPFILES= go32-nat.o i387-nat.o
NATDEPFILES= go32-nat.o i386-nat.o i387-nat.o
TERMCAP=
TERMCAP=
HOST_IPC=
CC= gcc
XM_CLIBS= -ldbg

View File

@ -1,5 +1,5 @@
/* Native definitions for Intel x86 running DJGPP.
Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.
Copyright (C) 1997, 1998, 1999, 2001 Free Software Foundation, Inc.
This file is part of GDB.
@ -20,65 +20,19 @@
#define NO_PTRACE_H
#include "i386/nm-i386v.h"
#define I386_USE_GENERIC_WATCHPOINTS
#define TARGET_HAS_HARDWARE_WATCHPOINTS
#include "i386/nm-i386.h"
/* Returns the number of hardware watchpoints of type TYPE that we can
set. Value is positive if we can set CNT watchpoints, zero if
setting watchpoints of type TYPE is not supported, and negative if
CNT is more than the maximum number of watchpoints of type TYPE
that we can support. TYPE is one of bp_hardware_watchpoint,
bp_read_watchpoint, bp_write_watchpoint, or bp_hardware_breakpoint.
CNT is the number of such watchpoints used so far (including this
one). OTHERTYPE is non-zero if other types of watchpoints are
currently enabled.
/* Support for hardware-assisted breakpoints and watchpoints. */
We always return 1 here because we don't have enough information
about possible overlap of addresses that they want to watch. As
an extreme example, consider the case where all the watchpoints
watch the same address and the same region length: then we can
handle a virtually unlimited number of watchpoints, due to debug
register sharing implemented via reference counts in go32-nat.c. */
#define I386_DR_LOW_SET_CONTROL(VAL) go32_set_dr7 (VAL)
extern void go32_set_dr7 (unsigned);
#define TARGET_CAN_USE_HARDWARE_WATCHPOINT(type, cnt, ot) 1
#define I386_DR_LOW_SET_ADDR(N,ADDR) go32_set_dr (N,ADDR)
extern void go32_set_dr (int, CORE_ADDR);
/* Returns non-zero if we can use hardware watchpoints to watch a region
whose address is ADDR and whose length is LEN. */
#define I386_DR_LOW_RESET_ADDR(N)
#define TARGET_REGION_OK_FOR_HW_WATCHPOINT(addr,len) \
go32_region_ok_for_watchpoint(addr,len)
extern int go32_region_ok_for_watchpoint (CORE_ADDR, int);
/* After a watchpoint trap, the PC points to the instruction after the
one that caused the trap. Therefore we don't need to step over it.
But we do need to reset the status register to avoid another trap. */
#define HAVE_CONTINUABLE_WATCHPOINT
#define STOPPED_BY_WATCHPOINT(W) \
go32_stopped_by_watchpoint (inferior_pid, 0)
#define target_stopped_data_address() \
go32_stopped_by_watchpoint (inferior_pid, 1)
extern CORE_ADDR go32_stopped_by_watchpoint (int, int);
/* Use these macros for watchpoint insertion/removal. */
#define target_insert_watchpoint(addr, len, type) \
go32_insert_watchpoint (inferior_pid, addr, len, type)
extern int go32_insert_watchpoint (int, CORE_ADDR, int, int);
#define target_remove_watchpoint(addr, len, type) \
go32_remove_watchpoint (inferior_pid, addr, len, type)
extern int go32_remove_watchpoint (int, CORE_ADDR, int, int);
#define target_insert_hw_breakpoint(addr, shadow) \
go32_insert_hw_breakpoint(addr, shadow)
extern int go32_insert_hw_breakpoint (CORE_ADDR, void *);
#define target_remove_hw_breakpoint(addr, shadow) \
go32_remove_hw_breakpoint(addr, shadow)
extern int go32_remove_hw_breakpoint (CORE_ADDR, void *);
#define DECR_PC_AFTER_HW_BREAK 0
#define I386_DR_LOW_GET_STATUS() go32_get_dr6 ()
extern unsigned go32_get_dr6 (void);

View File

@ -1,5 +1,5 @@
/* Native debugging support for Intel x86 running DJGPP.
Copyright 1997, 1999, 2001 Free Software Foundation, Inc.
Copyright 1997, 1999, 2000, 2001 Free Software Foundation, Inc.
Written by Robert Hoehne.
This file is part of GDB.
@ -166,15 +166,8 @@ static void go32_files_info (struct target_ops *target);
static void go32_stop (void);
static void go32_kill_inferior (void);
static void go32_create_inferior (char *exec_file, char *args, char **env);
static void cleanup_dregs (void);
static void go32_mourn_inferior (void);
static int go32_can_run (void);
static int go32_insert_aligned_watchpoint (CORE_ADDR waddr, CORE_ADDR addr,
int len, int rw);
static int go32_remove_aligned_watchpoint (CORE_ADDR waddr, CORE_ADDR addr,
int len, int rw);
static int go32_handle_nonaligned_watchpoint (wp_op what, CORE_ADDR waddr,
CORE_ADDR addr, int len, int rw);
static struct target_ops go32_ops;
static void go32_terminal_init (void);
@ -645,7 +638,7 @@ go32_mourn_inferior (void)
be nice if GDB itself would take care to remove all breakpoints
at all times, but it doesn't, probably under an assumption that
the OS cleans up when the debuggee exits. */
cleanup_dregs ();
i386_cleanup_dregs ();
go32_kill_inferior ();
generic_mourn_inferior ();
}
@ -658,393 +651,35 @@ go32_can_run (void)
/* Hardware watchpoint support. */
#define DR_STATUS 6
#define DR_CONTROL 7
#define DR_ENABLE_SIZE 2
#define DR_LOCAL_ENABLE_SHIFT 0
#define DR_GLOBAL_ENABLE_SHIFT 1
#define DR_LOCAL_SLOWDOWN 0x100
#define DR_GLOBAL_SLOWDOWN 0x200
#define DR_CONTROL_SHIFT 16
#define DR_CONTROL_SIZE 4
#define DR_RW_READWRITE 0x3
#define DR_RW_WRITE 0x1
#define DR_CONTROL_MASK 0xf
#define DR_ENABLE_MASK 0x3
#define DR_LEN_1 0x0
#define DR_LEN_2 0x4
#define DR_LEN_4 0xc
#define D_REGS edi.dr
#define CONTROL D_REGS[DR_CONTROL]
#define STATUS D_REGS[DR_STATUS]
#define CONTROL D_REGS[7]
#define STATUS D_REGS[6]
#define IS_REG_FREE(index) \
(!(CONTROL & (3 << (DR_ENABLE_SIZE * (index)))))
#define LOCAL_ENABLE_REG(index) \
(CONTROL |= (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * (index))))
#define GLOBAL_ENABLE_REG(index) \
(CONTROL |= (1 << (DR_GLOBAL_ENABLE_SHIFT + DR_ENABLE_SIZE * (index))))
#define DISABLE_REG(index) \
(CONTROL &= ~(3 << (DR_ENABLE_SIZE * (index))))
#define SET_LOCAL_EXACT() \
(CONTROL |= DR_LOCAL_SLOWDOWN)
#define SET_GLOBAL_EXACT() \
(CONTROL |= DR_GLOBAL_SLOWDOWN)
#define RESET_LOCAL_EXACT() \
(CONTROL &= ~(DR_LOCAL_SLOWDOWN))
#define RESET_GLOBAL_EXACT() \
(CONTROL &= ~(DR_GLOBAL_SLOWDOWN))
#define SET_BREAK(index,address) \
do {\
CONTROL &= ~(DR_CONTROL_MASK << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * (index)));\
D_REGS[index] = address;\
dr_ref_count[index]++;\
} while(0)
#define SET_WATCH(index,address,rw,len) \
do {\
SET_BREAK(index,address);\
CONTROL |= ((len)|(rw)) << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * (index));\
} while (0)
#define IS_WATCH(index) \
(CONTROL & (DR_CONTROL_MASK << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE*(index))))
#define WATCH_HIT(index) ((STATUS & (1 << (index))) && IS_WATCH(index))
#define DR_DEF(index) \
((CONTROL >> (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * (index))) & 0x0f)
#if 0 /* use debugging macro */
#define SHOW_DR(text,len) \
do { \
if (!getenv ("GDB_SHOW_DR")) break; \
fprintf(stderr,"%08x %08x ",edi.dr[7],edi.dr[6]); \
fprintf(stderr,"%08x %d %08x %d ", \
edi.dr[0],dr_ref_count[0],edi.dr[1],dr_ref_count[1]); \
fprintf(stderr,"%08x %d %08x %d ", \
edi.dr[2],dr_ref_count[2],edi.dr[3],dr_ref_count[3]); \
fprintf(stderr,(len)?"(%s:%d)\n":"(%s)\n",#text,len); \
} while (0)
#else
#define SHOW_DR(text,len) do {} while (0)
#endif
static void
cleanup_dregs (void)
/* Pass the address ADDR to the inferior in the I'th debug register.
Here we just store the address in D_REGS, the watchpoint will be
actually set up when go32_wait runs the debuggee. */
void
go32_set_dr (int i, CORE_ADDR addr)
{
int i;
CONTROL = 0;
STATUS = 0;
for (i = 0; i < 4; i++)
{
D_REGS[i] = 0;
dr_ref_count[i] = 0;
}
D_REGS[i] = addr;
}
/* Insert a watchpoint. */
int
go32_insert_watchpoint (int pid ATTRIBUTE_UNUSED, CORE_ADDR addr,
int len, int rw)
/* 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 when go32_wait runs the debuggee. */
void
go32_set_dr7 (unsigned val)
{
int ret = go32_insert_aligned_watchpoint (addr, addr, len, rw);
SHOW_DR (insert_watch, len);
return ret;
CONTROL = val;
}
static int
go32_insert_aligned_watchpoint (CORE_ADDR waddr, CORE_ADDR addr,
int len, int rw)
/* Get the value of the DR6 debug status register from the inferior.
Here we just return the value stored in D_REGS, as we've got it
from the last go32_wait call. */
unsigned
go32_get_dr6 (void)
{
int i;
int read_write_bits, len_bits;
/* Values of rw: 0 - write, 1 - read, 2 - access (read and write).
However, x86 doesn't support read-only data breakpoints. */
read_write_bits = rw ? DR_RW_READWRITE : DR_RW_WRITE;
switch (len)
{
case 4:
len_bits = DR_LEN_4;
break;
case 2:
len_bits = DR_LEN_2;
break;
case 1:
len_bits = DR_LEN_1;
break;
default:
/* The debug registers only have 2 bits for the length, so
so this value will always fail the loop below. */
len_bits = 0x10;
}
/* Look for an occupied debug register with the same address and the
same RW and LEN definitions. If we find one, we can use it for
this watchpoint as well (and save a register). */
for (i = 0; i < 4; i++)
{
if (!IS_REG_FREE (i) && D_REGS[i] == addr
&& DR_DEF (i) == (unsigned)(len_bits | read_write_bits))
{
dr_ref_count[i]++;
return 0;
}
}
/* Look for a free debug register. */
for (i = 0; i <= 3; i++)
{
if (IS_REG_FREE (i))
break;
}
/* No more debug registers! */
if (i > 3)
return -1;
if (len == 2)
{
if (addr % 2)
return go32_handle_nonaligned_watchpoint (wp_insert, waddr, addr,
len, rw);
}
else if (len == 4)
{
if (addr % 4)
return go32_handle_nonaligned_watchpoint (wp_insert, waddr, addr,
len, rw);
}
else if (len != 1)
return go32_handle_nonaligned_watchpoint (wp_insert, waddr, addr, len, rw);
SET_WATCH (i, addr, read_write_bits, len_bits);
LOCAL_ENABLE_REG (i);
SET_LOCAL_EXACT ();
SET_GLOBAL_EXACT ();
return 0;
}
static int
go32_handle_nonaligned_watchpoint (wp_op what, CORE_ADDR waddr, CORE_ADDR addr,
int len, int rw)
{
int align;
int size;
int rv = 0, status = 0;
static int size_try_array[4][4] =
{
{ 1, 1, 1, 1 }, /* trying size one */
{ 2, 1, 2, 1 }, /* trying size two */
{ 2, 1, 2, 1 }, /* trying size three */
{ 4, 1, 2, 1 } /* trying size four */
};
while (len > 0)
{
align = addr % 4;
/* Four is the maximum length a 386 debug register can watch. */
size = size_try_array[len > 4 ? 3 : len - 1][align];
if (what == wp_insert)
status = go32_insert_aligned_watchpoint (waddr, addr, size, rw);
else if (what == wp_remove)
status = go32_remove_aligned_watchpoint (waddr, addr, size, rw);
else if (what == wp_count)
rv++;
else
status = EINVAL;
/* 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, and thus 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
GDB reacts to our failure to insert this watchpoint and tries
to remove it. */
if (status)
rv = status;
addr += size;
len -= size;
}
return rv;
}
/* Remove a watchpoint. */
int
go32_remove_watchpoint (int pid ATTRIBUTE_UNUSED, CORE_ADDR addr,
int len, int rw)
{
int ret = go32_remove_aligned_watchpoint (addr, addr, len, rw);
SHOW_DR (remove_watch, len);
return ret;
}
static int
go32_remove_aligned_watchpoint (CORE_ADDR waddr, CORE_ADDR addr,
int len, int rw)
{
int i;
int read_write_bits, len_bits;
/* Values of rw: 0 - write, 1 - read, 2 - access (read and write).
However, x86 doesn't support read-only data breakpoints. */
read_write_bits = rw ? DR_RW_READWRITE : DR_RW_WRITE;
switch (len)
{
case 4:
len_bits = DR_LEN_4;
break;
case 2:
len_bits = DR_LEN_2;
break;
case 1:
len_bits = DR_LEN_1;
break;
default:
/* The debug registers only have 2 bits for the length, so
so this value will always fail the loop below. */
len_bits = 0x10;
}
if (len == 2)
{
if (addr % 2)
return go32_handle_nonaligned_watchpoint (wp_remove, waddr, addr,
len, rw);
}
else if (len == 4)
{
if (addr % 4)
return go32_handle_nonaligned_watchpoint (wp_remove, waddr, addr,
len, rw);
}
else if (len != 1)
return go32_handle_nonaligned_watchpoint (wp_remove, waddr, addr, len, rw);
for (i = 0; i <= 3; i++)
{
if (!IS_REG_FREE (i) && D_REGS[i] == addr
&& DR_DEF (i) == (unsigned)(len_bits | read_write_bits))
{
dr_ref_count[i]--;
if (dr_ref_count[i] == 0)
DISABLE_REG (i);
}
}
RESET_LOCAL_EXACT ();
RESET_GLOBAL_EXACT ();
return 0;
}
/* Can we use debug registers to watch a region whose address is ADDR
and whose length is LEN bytes? */
int
go32_region_ok_for_watchpoint (CORE_ADDR addr, int len)
{
/* Compute how many aligned watchpoints we would need to cover this
region. */
int nregs = go32_handle_nonaligned_watchpoint (wp_count, addr, addr, len, 0);
return nregs <= 4 ? 1 : 0;
}
/* Check if stopped by a data watchpoint. If so, return the address
whose access triggered the watchpoint. */
CORE_ADDR
go32_stopped_by_watchpoint (int pid ATTRIBUTE_UNUSED, int data_watchpoint)
{
int i, ret = 0;
int status;
status = edi.dr[DR_STATUS];
SHOW_DR (stopped_by, 0);
for (i = 0; i <= 3; i++)
{
if (WATCH_HIT (i) && data_watchpoint)
{
SHOW_DR (WP_HIT, 0);
ret = D_REGS[i];
}
}
return ret;
}
/* Remove a breakpoint. */
int
go32_remove_hw_breakpoint (CORE_ADDR addr, void *shadow ATTRIBUTE_UNUSED)
{
int i;
for (i = 0; i <= 3; i++)
{
if (!IS_REG_FREE (i) && D_REGS[i] == addr && DR_DEF (i) == 0)
{
dr_ref_count[i]--;
if (dr_ref_count[i] == 0)
DISABLE_REG (i);
}
}
SHOW_DR (remove_hw, 0);
return 0;
}
int
go32_insert_hw_breakpoint (CORE_ADDR addr, void *shadow ATTRIBUTE_UNUSED)
{
int i;
/* Look for an occupied debug register with the same address and the
same RW and LEN definitions. If we find one, we can use it for
this breakpoint as well (and save a register). */
for (i = 0; i < 4; i++)
{
if (!IS_REG_FREE (i) && D_REGS[i] == addr && DR_DEF (i) == 0)
{
dr_ref_count[i]++;
SHOW_DR (insert_hw, 0);
return 0;
}
}
/* Look for a free debug register. */
for (i = 0; i <= 3; i++)
{
if (IS_REG_FREE (i))
break;
}
/* No more debug registers? */
if (i < 4)
{
SET_BREAK (i, addr);
LOCAL_ENABLE_REG (i);
}
SHOW_DR (insert_hw, 0);
return i < 4 ? 0 : EBUSY;
return STATUS;
}
/* Put the device open on handle FD into either raw or cooked