int i386_clr_watch(int watchnum, struct dbreg* d) { if (watchnum < 0 || watchnum >= 4) { return -1; } DBREG_DRX(d, 7) = DBREG_DRX(d, 7) & ~((0x3 << (watchnum * 2)) | (0x0f << (watchnum * 4 + 16))); DBREG_DRX(d, watchnum) = 0; return 0; }
int i386_set_watch(int watchnum, unsigned int watchaddr, int size, int access, struct dbreg * d) { int i; unsigned int mask; if (watchnum == -1) { for (i = 0, mask = 0x3; i < 4; i++, mask <<= 2) if ((DBREG_DRX(d,7) & mask) == 0) break; if (i < 4) watchnum = i; else return -1; } switch (access) { case DBREG_DR7_EXEC: size = 1; /* size must be 1 for an execution breakpoint */ /* fall through */ case DBREG_DR7_WRONLY: case DBREG_DR7_RDWR: break; default : return -1; break; } /* * we can watch a 1, 2, or 4 byte sized location */ switch (size) { case 1 : mask = 0x00; break; case 2 : mask = 0x01 << 2; break; case 4 : mask = 0x03 << 2; break; default : return -1; break; } mask |= access; /* clear the bits we are about to affect */ DBREG_DRX(d,7) &= ~((0x3 << (watchnum*2)) | (0x0f << (watchnum*4+16))); /* set drN register to the address, N=watchnum */ DBREG_DRX(d,watchnum) = watchaddr; /* enable the watchpoint */ DBREG_DRX(d,7) |= (0x2 << (watchnum*2)) | (mask << (watchnum*4+16)); return watchnum; }
/* set the thread x86 registers */ void set_thread_context( struct thread *thread, const context_t *context, unsigned int flags ) { int pid = get_ptrace_tid(thread); struct dbreg dbregs; /* all other regs are handled on the client side */ assert( flags == SERVER_CTX_DEBUG_REGISTERS ); if (!suspend_for_ptrace( thread )) return; #ifdef DBREG_DRX /* needed for FreeBSD, the structure fields have changed under 5.x */ DBREG_DRX((&dbregs), 0) = context->debug.i386_regs.dr0; DBREG_DRX((&dbregs), 1) = context->debug.i386_regs.dr1; DBREG_DRX((&dbregs), 2) = context->debug.i386_regs.dr2; DBREG_DRX((&dbregs), 3) = context->debug.i386_regs.dr3; DBREG_DRX((&dbregs), 4) = 0; DBREG_DRX((&dbregs), 5) = 0; DBREG_DRX((&dbregs), 6) = context->debug.i386_regs.dr6; DBREG_DRX((&dbregs), 7) = context->debug.i386_regs.dr7; #else dbregs.dr0 = context->debug.i386_regs.dr0; dbregs.dr1 = context->debug.i386_regs.dr1; dbregs.dr2 = context->debug.i386_regs.dr2; dbregs.dr3 = context->debug.i386_regs.dr3; dbregs.dr4 = 0; dbregs.dr5 = 0; dbregs.dr6 = context->debug.i386_regs.dr6; dbregs.dr7 = context->debug.i386_regs.dr7; #endif if (ptrace( PTRACE_SETDBREGS, pid, (caddr_t) &dbregs, 0 ) == -1) file_set_error(); else if (thread->context) thread->context->debug.i386_regs = context->debug.i386_regs; /* update the cached values */ resume_after_ptrace( thread ); }
static void i386bsd_dr_set (int regnum, unsigned int value) { struct dbreg dbregs; if (ptrace (PT_GETDBREGS, PIDGET (inferior_ptid), (PTRACE_TYPE_ARG3) &dbregs, 0) == -1) perror_with_name (_("Couldn't get debug registers")); /* For some mysterious reason, some of the reserved bits in the debug control register get set. Mask these off, otherwise the ptrace call below will fail. */ DBREG_DRX ((&dbregs), 7) &= ~(0x0000fc00); DBREG_DRX ((&dbregs), regnum) = value; if (ptrace (PT_SETDBREGS, PIDGET (inferior_ptid), (PTRACE_TYPE_ARG3) &dbregs, 0) == -1) perror_with_name (_("Couldn't write debug registers")); }
static unsigned long i386bsd_dr_get (ptid_t ptid, int regnum) { struct dbreg dbregs; if (ptrace (PT_GETDBREGS, PIDGET (inferior_ptid), (PTRACE_TYPE_ARG3) &dbregs, 0) == -1) perror_with_name (_("Couldn't read debug registers")); return DBREG_DRX ((&dbregs), regnum); }
/* retrieve the thread x86 registers */ void get_thread_context( struct thread *thread, context_t *context, unsigned int flags ) { int pid = get_ptrace_tid(thread); struct dbreg dbregs; /* all other regs are handled on the client side */ assert( flags == SERVER_CTX_DEBUG_REGISTERS ); if (!suspend_for_ptrace( thread )) return; if (ptrace( PTRACE_GETDBREGS, pid, (caddr_t) &dbregs, 0 ) == -1) file_set_error(); else { #ifdef DBREG_DRX /* needed for FreeBSD, the structure fields have changed under 5.x */ context->debug.i386_regs.dr0 = DBREG_DRX((&dbregs), 0); context->debug.i386_regs.dr1 = DBREG_DRX((&dbregs), 1); context->debug.i386_regs.dr2 = DBREG_DRX((&dbregs), 2); context->debug.i386_regs.dr3 = DBREG_DRX((&dbregs), 3); context->debug.i386_regs.dr6 = DBREG_DRX((&dbregs), 6); context->debug.i386_regs.dr7 = DBREG_DRX((&dbregs), 7); #else context->debug.i386_regs.dr0 = dbregs.dr0; context->debug.i386_regs.dr1 = dbregs.dr1; context->debug.i386_regs.dr2 = dbregs.dr2; context->debug.i386_regs.dr3 = dbregs.dr3; context->debug.i386_regs.dr6 = dbregs.dr6; context->debug.i386_regs.dr7 = dbregs.dr7; #endif context->flags |= SERVER_CTX_DEBUG_REGISTERS; } resume_after_ptrace( thread ); }
unsigned long i386bsd_dr_get_status (void) { struct dbreg dbregs; /* FIXME: kettenis/2001-03-31: Calling perror_with_name if the ptrace call fails breaks debugging remote targets. The correct way to fix this is to add the hardware breakpoint and watchpoint stuff to the target vector. For now, just return zero if the ptrace call fails. */ if (ptrace (PT_GETDBREGS, PIDGET (inferior_ptid), (PTRACE_ARG3_TYPE) & dbregs, 0) == -1) #if 0 perror_with_name ("Couldn't read debug registers"); #else return 0; #endif return DBREG_DRX ((&dbregs), 6); }