/* 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.
#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);
/* 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.
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);
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 ();
}
/* 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 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)
-{
- int i;
-
- CONTROL = 0;
- STATUS = 0;
- for (i = 0; i < 4; i++)
- {
- D_REGS[i] = 0;
- dr_ref_count[i] = 0;
- }
-}
-
-/* Insert a watchpoint. */
-
-int
-go32_insert_watchpoint (int pid ATTRIBUTE_UNUSED, CORE_ADDR addr,
- int len, int rw)
-{
- int ret = go32_insert_aligned_watchpoint (addr, addr, len, rw);
-
- SHOW_DR (insert_watch, len);
- return ret;
-}
-
-static int
-go32_insert_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;
- }
-
- /* 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. */
+#define CONTROL D_REGS[7]
+#define STATUS D_REGS[6]
-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)
+/* 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, 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;
+ D_REGS[i] = addr;
}
-/* Remove a breakpoint. */
-
-int
-go32_remove_hw_breakpoint (CORE_ADDR addr, void *shadow ATTRIBUTE_UNUSED)
+/* 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 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;
+ CONTROL = val;
}
-int
-go32_insert_hw_breakpoint (CORE_ADDR addr, void *shadow ATTRIBUTE_UNUSED)
+/* 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;
-
- /* 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