Article View: gnu.gdb.bug
Article #10195Hardware data and text breakpoints for gdb version 3.5 on 386 systems
From: Lawrence Freil
Date: Wed, 07 Mar 1990 21:15
Date: Wed, 07 Mar 1990 21:15
544 lines
17683 bytes
17683 bytes
The following diffs contain modifications to gdb version 3.5 files
i386-dep.c and m-i386.h. These modifications allows access to the hardware
breakpoint registers of the 386 under Interactive 386/ix. I suspect that
it will work under any AT&T based 386 System V.3.2 system.
The modifications work by adding five additional registers to the list
of 386 registers. These registers are dr0, dr1, dr2, dr3 and dr7. The
first four registers are the addresses to set breakpoints at and the fifth
register (dr7) is the control register for the breakpoint hardware. These
registers can be accessed with the info reg command and set with the
set command, however, I have also added two new commands for setting and
displaying these registers. The commands are hbreak <arguments> for
setting the breakpoints and info hbreak for displaying the current settings.
Just hbreak with no arguments will display the various forms. With these
changes it is possable to stop a program based on a variable reference without
the performance problems of stepping single instructions.
*** /usr3/gnu/old-gdb/i386-dep.c Wed Mar 7 13:04:44 1990
--- /usr3/gnu/gdb-3.5/i386-dep.c Tue Mar 6 10:50:25 1990
*************** the Free Software Foundation, 675 Mass A
*** 52,59 ****
--- 52,349 ----
#include <sys/stat.h>
#include <sys/reg.h>
+ #include <sys/debugreg.h>
extern int errno;
+
+ static void hbreak_command();
+ static void hbreak_info();
+
+
+ /*
+ * The following routines were added by Lawrence Freil (lef@dogwood.atl.ga.us)
+ * _initialize_i386_dep, hbreak_command, hbreak_info, find_dr_reg
+ * plus modifications to the fetch_inferior_registers, store_inferior_registers
+ * and m-i386.h header file to add the 5 debug registers.
+ *
+ * These routines provide access to the hardware debugging registers provided
+ * by the 386 processor and allow for a user to break the program on a data
+ * address or a text address. The execution address breakpoint will probably
+ * not be used very often because it duplicates the functionality provided by
+ * the normal breakpoints except fewer are available. The data breakpoints
+ * are not provided by any other means and are useful when a section
+ * of memory is being clobbered and the address of the bad pointer is not
+ * known. An example of using the function hbreak to stop the program when
+ * the variable foobar is modified:
+ *
+ * hbreak write int &foobar
+ *
+ * For more details just type hbreak.
+ * -- Lawrence Freil
+ */
+
+ /*
+ * Initialize the i386 specific functions.
+ */
+ _initialize_i386_dep()
+ {
+ add_com("hbreak",
+ class_breakpoint,
+ hbreak_command,
+ "Stop execution on program address or data reference with\n\
+ the hardware breakpoint registers");
+ add_info("hbreak",
+ hbreak_info,
+ "Display status of hardware breakpoint registers");
+ }
+
+ int
+ find_dr_reg(ptr)
+ char *ptr;
+ {
+ register int i;
+ int reg;
+
+ if (ptr[0] == '\0') {
+ for (i = DR0; i < DR3; i++)
+ if (read_register(i) == 0)
+ return i - DR0;
+ printf("All registers in use. Use hbreak clear to release one\n");
+ return -1;
+ }
+ if (ptr[0] == 'd' && ptr[1] == 'r')
+ return atoi(&ptr[2]);
+ else
+ if (ptr[0] >= '0' && ptr[0] <= '9')
+ return atoi(ptr);
+ else {
+ printf("register number must be either 0-3 or dr0 through dr3\n");
+ return -1;
+ }
+ }
+
+
+ #define EXPSIZE 40
+
+ static void
+ hbreak_command(args, from_tty)
+ char *args;
+ int from_tty;
+ {
+ char a[EXPSIZE], b[EXPSIZE], c[EXPSIZE], d[EXPSIZE], e[EXPSIZE];
+ int reg;
+ unsigned long address;
+ unsigned long control;
+ int type;
+
+ a[0] = b[0] = c[0] = d[0] = e[0] = '\0';
+ if (args != NULL)
+ sscanf(args, "%s%s%s%s", a, b, c, d);
+ if (a[0] == '\0') {
+ printf("hbreak execute address [ register ] | \n\
+ write ( char | short | int ) address [ register ]\n\
+ access ( char | short | int ) address [ register ]\n\
+ clear ( register | all )\n\
+ enable ( register | all)\n\
+ disable ( register | all)\n");
+ return;
+ }
+ control = read_register(DR7);
+ if (!strcmp("execute", a)) {
+ /*
+ * Format: execute address [ register ]
+ *
+ * set an execution breakpoint at address. This shouldn't be used
+ * very often because the normal gdb breakpoint stuff should work fine
+ * for this.
+ */
+ if (b[0] == '\0') {
+ printf("address must be specified for execute\n");
+ return;
+ }
+ reg = find_dr_reg(c);
+ if (reg == -1)
+ return;
+ address = parse_and_eval_address(b);
+ supply_register(DR0 + reg, &address);
+ control |= 0x1 << (reg * 2);
+ control &= (0xf << (reg * 4 + 16) ^ 0xffffffff);
+ control |= (0x0 << (reg * 4 + 16));
+ control |= 0x00000100;
+ supply_register(DR7, &control);
+ }
+ else if (!strcmp("write", a)) {
+ /*
+ * Format: write ( char | short | int ) address [ register ]
+ *
+ */
+ reg = find_dr_reg(d);
+ if (reg == -1)
+ return;
+ address = parse_and_eval_address(c);
+ if (!strcmp("char", b))
+ type = 1;
+ else
+ if (!strcmp("short", b))
+ type = 5;
+ else
+ if (!strcmp("int", b))
+ type = 0xd;
+ else {
+ printf("Length must be one of char(8), short(16), int(32)\n");
+ return;
+ }
+ supply_register(DR0 + reg, &address);
+ control |= 0x1 << (reg * 2);
+ control &= (0xf << (reg * 4 + 16) ^ 0xffffffff);
+ control |= (type << (reg * 4 + 16));
+ control |= 0x00000100;
+ supply_register(DR7, &control);
+ } else if (!strcmp("access", a)) {
+ /*
+ * Format: access ( char | short | int ) address [ register ]
+ *
+ */
+ reg = find_dr_reg(d);
+ if (reg == -1)
+ return;
+ address = parse_and_eval_address(c);
+ if (!strcmp("char", b))
+ type = 3;
+ else
+ if (!strcmp("short", b))
+ type = 7;
+ else
+ if (!strcmp("int", b))
+ type = 0xf;
+ else {
+ printf("Length must be one of char(8), short(16), int(32)\n");
+ return;
+ }
+ supply_register(DR0 + reg, &address);
+ control |= 0x1 << (reg * 2);
+ control &= (0xf << (reg * 4 + 16) ^ 0xffffffff);
+ control |= (type << (reg * 4 + 16));
+ control |= 0x00000100;
+ supply_register(DR7, &control);
+ } else if (!strcmp("clear", a)) {
+ /*
+ * Format: clear ( register | all )
+ *
+ */
+ address = 0;
+ if (b[0] == '\0') {
+ printf("clear requires a register number or 'all'\n");
+ return;
+ }
+ if (!strcmp("all", b)) {
+ supply_register(DR0, &address);
+ supply_register(DR1, &address);
+ supply_register(DR2, &address);
+ supply_register(DR3, &address);
+ supply_register(DR7, &address);
+ }
+ else {
+ reg = find_dr_reg(b);
+ if (reg == -1)
+ return;
+ supply_register(DR0 + reg, &address);
+ control &= (0x3 << (reg * 2)) ^ 0xffffffff;
+ control &= (0xf << (reg * 4 + 16)) ^ 0xffffffff;
+ supply_register(DR7, &control);
+ }
+ } else if (!strcmp("disable", a)) {
+ /*
+ * Format: disable ( register | all )
+ *
+ */
+ address = 0;
+ if (b[0] == '\0') {
+ printf("disable requires a register number or 'all'\n");
+ return;
+ }
+ if (!strcmp("all", b)) {
+ control &= 0xffff0000;
+ supply_register(DR7, &control);
+ }
+ else {
+ reg = find_dr_reg(b);
+ if (reg == -1)
+ return;
+ control &= (0x3 << (reg * 2)) ^ 0xffffffff;
+ supply_register(DR7, &control);
+ }
+ } else if (!strcmp("enable", a)) {
+ /*
+ * Format: enable ( register | all )
+ *
+ */
+ address = 0;
+ if (b[0] == '\0') {
+ printf("enable requires a register number or 'all'\n");
+ return;
+ }
+ if (!strcmp("all", b)) {
+ control &=0xffff0355;
+ supply_register(DR7, &address);
+ }
+ else {
+ reg = find_dr_reg(b);
+ if (reg == -1)
+ return;
+ control |= (0x1 << (reg * 2));
+ supply_register(DR7, &control);
+ }
+ } else {
+ printf("Command has to be one of execute access write clear enable disable\n");
+ return;
+ }
+ store_inferior_registers (-1);
+ }
+
+ void
+ hbreak_info()
+ {
+ register int drseven;
+ register int i;
+ char *types[16] = {
+ "execution ", /* 0 */
+ "char write ", /* 1 */
+ "Invalid ", /* 2 */
+ "char read/write ", /* 3 */
+ "Invalid ", /* 4 */
+ "short write ", /* 5 */
+ "Invalid ", /* 6 */
+ "short read/write ", /* 7 */
+ "Invalid ", /* 8 */
+ "Invalid ", /* 9 */
+ "Invalid ", /* a */
+ "Invalid ", /* b */
+ "Invalid ", /* c */
+ "integer write ", /* d */
+ "Invalid ", /* e */
+ "integer read/write", /* f */
+ };
+ char *flags[4] = {
+ "disabled", /* 0 */
+ "enabled", /* 1 */
+ "global enabled", /* 2 */
+ "global/local enabled", /* 3 */
+ };
+
+ drseven = read_register(DR7);
+ printf("Hardware Breakpoint Registers Type Flags\n");
+ for (i = DR0; i <= DR3; i++) {
+ printf(" Register dr%d: 0x%08lx %s %s\n",
+ i-DR0,
+ read_register(i),
+ types[(drseven >> (16 + (i * 4))) & 0xf],
+ flags[(drseven >> (i * 2)) & 0x3]);
+ }
+ if (drseven & 0x0300)
+ printf(" Program is execution with Pipeline disabled\n");
+ }
+
/* This function simply calls ptrace with the given arguments.
It exists so that all calls to ptrace are isolated in this
*************** fetch_inferior_registers (
*** 120,126 ****
unsigned int offset = (char *) &u.u_ar0 - (char *) &u;
offset = ptrace (3, inferior_pid, offset, 0) - KERNEL_U_ADDR;
! for (regno = 0; regno < NUM_REGS; regno++)
{
regaddr = register_addr (regno, offset);
for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (int))
--- 410,416 ----
unsigned int offset = (char *) &u.u_ar0 - (char *) &u;
offset = ptrace (3, inferior_pid, offset, 0) - KERNEL_U_ADDR;
! for (regno = 0; regno < NUM_BASE_REGS; regno++)
{
regaddr = register_addr (regno, offset);
for (i = 0; i < REGISTER_RAW_SIZE (regno); i += sizeof (int))
*************** fetch_inferior_registers (
*** 130,135 ****
--- 420,439 ----
}
supply_register (regno, buf);
}
+ /*
+ * Read the 4 debug registers plus the control register DR7.
+ */
+ for (i = DR_FIRSTADDR; i <= DR_LASTADDR; i++) {
+ *(int *) &buf[0] = ptrace(3, inferior_pid,
+ (char *) &u.u_debugreg[i] -
+ (char *) &u);
+ supply_register(regno++, buf);
+ }
+ *(int *) &buf[0] = ptrace(3, inferior_pid,
+ (char *) &u.u_debugreg[DR_CONTROL] -
+ (char *) &u);
+ supply_register(regno++, buf);
+
}
/* Store our register values back into the inferior.
*************** store_inferior_registers (regno
*** 148,173 ****
if (regno >= 0)
{
! regaddr = register_addr (regno, offset);
! errno = 0;
! ptrace (6, inferior_pid, regaddr, read_register (regno));
! if (errno != 0)
{
! sprintf (buf, "writing register number %d", regno);
! perror_with_name (buf);
}
}
! else for (regno = 0; regno < NUM_REGS; regno++)
{
! regaddr = register_addr (regno, offset);
! errno = 0;
! ptrace (6, inferior_pid, regaddr, read_register (regno));
! if (errno != 0)
{
! sprintf (buf, "writing register number %d", regno);
! perror_with_name (buf);
}
! }
}
/* Copy LEN bytes from inferior's memory starting at MEMADDR
--- 452,514 ----
if (regno >= 0)
{
! if (regno < NUM_BASE_REGS)
{
! regaddr = register_addr (regno, offset);
! errno = 0;
! ptrace (6, inferior_pid, regaddr, read_register (regno));
! if (errno != 0)
! {
! sprintf (buf, "writing register number %d", regno);
! perror_with_name (buf);
! }
! }
! else
! {
! if (regno == DEBUG_CONTROL)
! ptrace(6, inferior_pid,
! (char *) &u.u_debugreg[DR_CONTROL] - (char *) &u,
! read_register(regno));
! else
! ptrace(6, inferior_pid,
! (char *) &u.u_debugreg[DR_FIRSTADDR +
! (regno - NUM_BASE_REGS)] -
! (char *) &u,
! read_register(regno));
! if (errno != 0)
! {
! sprintf (buf, "writing register number %d", regno);
! perror_with_name (buf);
! }
}
}
! else
{
! for (regno = 0; regno < NUM_BASE_REGS; regno++)
{
! regaddr = register_addr (regno, offset);
! errno = 0;
! ptrace (6, inferior_pid, regaddr, read_register (regno));
! if (errno != 0)
! {
! sprintf (buf, "writing register number %d", regno);
! perror_with_name (buf);
! }
}
! /*
! * Now save the debugging registers. First the four breakpoint registers
! * and then the control register.
! */
! for (regno = DEBUG_REGNUM; regno <= DEBUG_REGEND; regno++)
! ptrace(6, inferior_pid,
! (char *) &u.u_debugreg[DR_FIRSTADDR +
! (regno - DEBUG_REGNUM)] -
! (char *) &u,
! read_register(regno));
! ptrace(6, inferior_pid,
! (char *) &u.u_debugreg[DR_CONTROL] - (char *) &u,
! read_register(DEBUG_CONTROL));
! }
}
/* Copy LEN bytes from inferior's memory starting at MEMADDR
*************** static int regmap[] =
*** 982,987 ****
--- 1323,1329 ----
UESP, EBP, ESI, EDI,
EIP, EFL, CS, SS,
DS, ES, FS, GS,
+ DR0, DR1, DR2, DR3, DR7
};
/* blockend is the value of u.u_ar0, and points to the
*** /usr3/gnu/old-gdb/m-i386.h Wed Mar 7 13:04:56 1990
--- /usr3/gnu/gdb-3.5/m-i386.h Mon Mar 5 21:51:18 1990
*************** the Free Software Foundation, 675 Mass A
*** 32,50 ****
#define i386
#endif
- /* I'm running gdb 3.4 under 386/ix 2.0.2, which is a derivative of AT&T's
- Sys V/386 3.2.
-
- On some machines, gdb crashes when it's starting up while calling the
- vendor's termio tgetent() routine. It always works when run under
- itself (actually, under 3.2, it's not an infinitely recursive bug.)
- After some poking around, it appears that depending on the environment
- size, or whether you're running YP, or the phase of the moon or something,
- the stack is not always long-aligned when main() is called, and tgetent()
- takes strong offense at that. On some machines this bug never appears, but
- on those where it does, it occurs quite reliably. */
- #define ALIGN_STACK_ON_STARTUP
-
/* define USG if you are using sys5 /usr/include's */
#define USG
--- 32,37 ----
*************** on those where it does, it occurs quite
*** 155,161 ****
/* Number of machine registers */
! #define NUM_REGS 16
/* Initializer for an array of names of registers.
There should be NUM_REGS strings in this initializer. */
--- 142,151 ----
/* Number of machine registers */
! #define NUM_REGS 21
!
! /* Number of contigous registers, the rest are the debugging registers */
! #define NUM_BASE_REGS 16
/* Initializer for an array of names of registers.
There should be NUM_REGS strings in this initializer. */
*************** on those where it does, it occurs quite
*** 168,173 ****
--- 158,164 ----
"esp", "ebp", "esi", "edi", \
"eip", "ps", "cs", "ss", \
"ds", "es", "fs", "gs", \
+ "dr0", "dr1", "dr2", "dr3", "dr7", \
}
/* Register numbers of various important registers.
*************** on those where it does, it occurs quite
*** 182,187 ****
--- 173,191 ----
#define PC_REGNUM 8
#define PS_REGNUM 9
+
+ #define DEBUG_REGNUM 16 /* First debug register in the MMU */
+ #define DEBUG_REGEND 19
+ #define DEBUG_CONTROL 20
+
+ /*
+ * Must define names for the debug registers because they arn't in sys/reg.h
+ */
+ #define DR0 16
+ #define DR1 17
+ #define DR2 18
+ #define DR3 19
+ #define DR7 20
#define REGISTER_U_ADDR(addr, blockend, regno) \
(addr) = i386_register_u_addr ((blockend),(regno));
--
Lawrence Freil Usenet/DDN:lef@dogwood.atl.ga.us
National Science Center Foundation Phone:(404)-828-8459
P.O. Box 1648
Augusta, Ga 30903
Technical Details
Message-ID:
<1990Mar7.211532.5626@dogwood.atl.ga.us>Path:
arc1-ca.pugleaf.net!gmdzi!unido!mcsun!uunet!bu.edu!purdue!ames!think!mintaka!ogicse!emory!mephisto!gatech!dogwood!lefShow Full Headers
Complete Article Headers
X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: f5053,972e6066ddf584a6,start X-Google-Attributes: gidf5053,public X-Google-ArrivalTime: 1990-03-07 13:15:32 PST Path: arc1-ca.pugleaf.net!gmdzi!unido!mcsun!uunet!bu.edu!purdue!ames!think!mintaka!ogicse!emory!mephisto!gatech!dogwood!lef From: lef@dogwood.atl.ga.us (Lawrence Freil) Newsgroups: gnu.gdb.bug Subject: Hardware data and text breakpoints for gdb version 3.5 on 386 systems Keywords: gdb3 breakpoints Message-ID: <1990Mar7.211532.5626@dogwood.atl.ga.us> Date: 7 Mar 90 21:15:32 GMT Organization: National Science Center Foundation Lines: 544 Posted: Wed Mar 7 22:15:32 1990