/* Native debugging support for Intel x86 running DJGPP.
- Copyright 1997, 1999 Free Software Foundation, Inc.
+ Copyright 1997, 1999, 2000, 2001 Free Software Foundation, Inc.
Written by Robert Hoehne.
This file is part of GDB.
#include "command.h"
#include "floatformat.h"
#include "buildsym.h"
+#include "i387-nat.h"
+#include "value.h"
+#include "regcache.h"
+#include "gdb_string.h"
#include <stdio.h> /* required for __DJGPP_MINOR__ */
#include <stdlib.h>
-#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <io.h>
int redirected;
} cmdline_t;
-void redir_cmdline_delete (cmdline_t *ptr) {ptr->redirected = 0;}
-int redir_cmdline_parse (const char *args, cmdline_t *ptr)
+void
+redir_cmdline_delete (cmdline_t *ptr) {ptr->redirected = 0;}
+
+int
+redir_cmdline_parse (const char *args, cmdline_t *ptr)
{
return -1;
}
-int redir_to_child (cmdline_t *ptr)
+int
+redir_to_child (cmdline_t *ptr)
{
return 1;
}
-int redir_to_debugger (cmdline_t *ptr)
+int
+redir_to_debugger (cmdline_t *ptr)
{
return 1;
}
-int redir_debug_init (cmdline_t *ptr) { return 0; }
+int
+redir_debug_init (cmdline_t *ptr) { return 0; }
#endif /* __DJGPP_MINOR < 3 */
-extern void _initialize_go32_nat (void);
-
typedef enum { wp_insert, wp_remove, wp_count } wp_op;
/* This holds the current reference counts for each debug register. */
static int dr_ref_count[4];
-extern char **environ;
-
#define SOME_PID 42
static int prog_has_started = 0;
static void go32_store_registers (int regno);
static void go32_prepare_to_store (void);
static int go32_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len,
- int write, struct target_ops *target);
+ int write,
+ struct mem_attrib *attrib,
+ struct target_ops *target);
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);
};
static void
-go32_open (char *name ATTRIBUTE_UNUSED, int from_tty ATTRIBUTE_UNUSED)
+go32_open (char *name, int from_tty)
{
printf_unfiltered ("Done. Use the \"run\" command to run the program.\n");
}
static void
-go32_close (int quitting ATTRIBUTE_UNUSED)
+go32_close (int quitting)
{
}
static void
-go32_attach (char *args ATTRIBUTE_UNUSED, int from_tty ATTRIBUTE_UNUSED)
+go32_attach (char *args, int from_tty)
{
error ("\
You cannot attach to a running program on this platform.\n\
}
static void
-go32_detach (char *args ATTRIBUTE_UNUSED, int from_tty ATTRIBUTE_UNUSED)
+go32_detach (char *args, int from_tty)
{
}
static int resume_signal = -1;
static void
-go32_resume (int pid ATTRIBUTE_UNUSED, int step, enum target_signal siggnal)
+go32_resume (int pid, int step, enum target_signal siggnal)
{
int i;
static char child_cwd[FILENAME_MAX];
static int
-go32_wait (int pid ATTRIBUTE_UNUSED, struct target_waitstatus *status)
+go32_wait (int pid, struct target_waitstatus *status)
{
int i;
unsigned char saved_opcode;
point of changing back to where GDB thinks is its cwd, when we
return control to the debugger, but restore child's cwd before we
run it. */
+ /* Initialize child_cwd, before the first call to run_child and not
+ in the initialization, so the child get also the changed directory
+ set with the gdb-command "cd ..." */
+ if (!*child_cwd)
+ /* Initialize child's cwd with the current one. */
+ getcwd (child_cwd, sizeof (child_cwd));
+
chdir (child_cwd);
#if __DJGPP_MINOR__ < 3
}
static void
-go32_fetch_registers (int regno)
+fetch_register (int regno)
{
- /*JHW */
- int end_reg = regno + 1; /* just one reg initially */
-
- if (regno < 0) /* do the all registers */
- {
- regno = 0; /* start at first register */
- /* # regs in table */
- end_reg = sizeof (regno_mapping) / sizeof (regno_mapping[0]);
- }
+ if (regno < FP0_REGNUM)
+ supply_register (regno, (char *) &a_tss + regno_mapping[regno].tss_ofs);
+ else if (regno <= LAST_FPU_CTRL_REGNUM)
+ i387_supply_register (regno, (char *) &npx);
+ else
+ internal_error (__FILE__, __LINE__,
+ "Invalid register no. %d in fetch_register.", regno);
+}
- for (; regno < end_reg; regno++)
+static void
+go32_fetch_registers (int regno)
+{
+ if (regno >= 0)
+ fetch_register (regno);
+ else
{
- if (regno < 16)
- supply_register (regno,
- (char *) &a_tss + regno_mapping[regno].tss_ofs);
- else if (regno < 24)
- supply_register (regno,
- (char *) &npx.reg[regno_mapping[regno].tss_ofs]);
- else if (regno < 32)
- {
- unsigned regval;
-
- switch (regno_mapping[regno].size)
- {
- case 2:
- regval = *(unsigned short *)
- ((char *) &npx + regno_mapping[regno].tss_ofs);
- regval &= 0xffff;
- if (regno == FOP_REGNUM && regval)
- /* Feature: restore the 5 bits of the opcode
- stripped by FSAVE/FNSAVE. */
- regval |= 0xd800;
- break;
- case 4:
- regval = *(unsigned *)
- ((char *) &npx + regno_mapping[regno].tss_ofs);
- break;
- default:
- internal_error ("\
-Invalid native size for register no. %d in go32_fetch_register.", regno);
- }
- supply_register (regno, (char *) ®val);
- }
- else
- internal_error ("Invalid register no. %d in go32_fetch_register.",
- regno);
+ for (regno = 0; regno < FP0_REGNUM; regno++)
+ fetch_register (regno);
+ i387_supply_fsave ((char *) &npx);
}
}
store_register (int regno)
{
void *rp;
- void *v = (void *) ®isters[REGISTER_BYTE (regno)];
-
- if (regno < 16)
- rp = (char *) &a_tss + regno_mapping[regno].tss_ofs;
- else if (regno < 24)
- rp = (char *) &npx.reg[regno_mapping[regno].tss_ofs];
- else if (regno < 32)
- rp = (char *) &npx + regno_mapping[regno].tss_ofs;
+ void *v = (void *) register_buffer (regno);
+
+ if (regno < FP0_REGNUM)
+ memcpy ((char *) &a_tss + regno_mapping[regno].tss_ofs,
+ v, regno_mapping[regno].size);
+ else if (regno <= LAST_FPU_CTRL_REGNUM)
+ i387_fill_fsave ((char *)&npx, regno);
else
- internal_error ("Invalid register no. %d in store_register.", regno);
- memcpy (rp, v, regno_mapping[regno].size);
- if (regno == FOP_REGNUM)
- *(short *)rp &= 0x07ff; /* strip high 5 bits, in case they added them */
+ internal_error (__FILE__, __LINE__,
+ "Invalid register no. %d in store_register.", regno);
}
static void
store_register (regno);
else
{
- for (r = 0; r < sizeof (regno_mapping) / sizeof (regno_mapping[0]); r++)
+ for (r = 0; r < FP0_REGNUM; r++)
store_register (r);
+ i387_fill_fsave ((char *) &npx, -1);
}
}
static int
go32_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int write,
- struct target_ops *target ATTRIBUTE_UNUSED)
+ struct mem_attrib *attrib, struct target_ops *target)
{
if (write)
{
static cmdline_t child_cmd; /* parsed child's command line kept here */
static void
-go32_files_info (struct target_ops *target ATTRIBUTE_UNUSED)
+go32_files_info (struct target_ops *target)
{
printf_unfiltered ("You are running a DJGPP V2 program.\n");
}
static void
go32_create_inferior (char *exec_file, char *args, char **env)
{
+ extern char **environ;
jmp_buf start_state;
char *cmdline;
char **env_save = environ;
}
resume_signal = -1;
resume_is_step = 0;
+
+ /* Initialize child's cwd as empty to be initialized when starting
+ the child. */
+ *child_cwd = 0;
+
/* Init command line storage. */
if (redir_debug_init (&child_cmd) == -1)
- internal_error ("Cannot allocate redirection storage: not enough memory.\n");
+ internal_error (__FILE__, __LINE__,
+ "Cannot allocate redirection storage: not enough memory.\n");
/* Parse the command line and create redirections. */
if (strpbrk (args, "<>"))
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);
+#define CONTROL D_REGS[7]
+#define STATUS D_REGS[6]
- 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. */
-
-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;
+ if (i < 0 || i > 3)
+ internal_error (__FILE__, __LINE__,
+ "Invalid register %d in go32_set_dr.\n", i);
+ 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
}
static void
-go32_terminal_info (char *args ATTRIBUTE_UNUSED, int from_tty ATTRIBUTE_UNUSED)
+go32_terminal_info (char *args, int from_tty)
{
printf_unfiltered ("Inferior's terminal is in %s mode.\n",
!inf_mode_valid
go32_ops.to_has_execution = 1;
go32_ops.to_magic = OPS_MAGIC;
- /* Initialize child's cwd with the current one. */
- getcwd (child_cwd, sizeof (child_cwd));
+ /* Initialize child's cwd as empty to be initialized when starting
+ the child. */
+ *child_cwd = 0;
/* Initialize child's command line storage. */
if (redir_debug_init (&child_cmd) == -1)
- internal_error ("Cannot allocate redirection storage: not enough memory.\n");
+ internal_error (__FILE__, __LINE__,
+ "Cannot allocate redirection storage: not enough memory.\n");
/* We are always processing GCC-compiled programs. */
processing_gcc_compilation = 2;