rizin/librz/debug/p/native/drx.c
2023-01-25 22:02:26 +08:00

240 lines
6.5 KiB
C

// SPDX-FileCopyrightText: 2014 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: LGPL-3.0-only
#include <rz_types.h>
/* -------------------- drx.h ------------------- */
#define DRXN 8
#define DR_STATUS 6
#define DR_CONTROL 7
#define DR_LOCAL_ENABLE_SHIFT 0 /* Extra shift to the local enable bit. */
#define DR_GLOBAL_ENABLE_SHIFT 1 /* Extra shift to the global enable bit. */
#define DR_ENABLE_SIZE 2 /* Two enable bits per debug register. */
/* Fields reserved by Intel. This includes the GD (General Detect
Enable) flag, which causes a debug exception to be generated when a
MOV instruction accesses one of the debug registers.
FIXME: My Intel manual says we should use 0xF800, not 0xFC00. */
#define DR_CONTROL_RESERVED (0xFC00)
#define I386_DR_CONTROL_MASK (~DR_CONTROL_RESERVED)
#define DR_LOCAL_SLOWDOWN (0x100)
#define DR_GLOBAL_SLOWDOWN (0x200)
/* DR7 fields */
/* How many bits to skip in DR7 to get to R/W and LEN fields. */
#define DR_CONTROL_SHIFT 16
/* How many bits in DR7 per R/W and LEN field for each watchpoint. */
#define DR_CONTROL_SIZE 4
#define DR_RW_EXECUTE (0x0) /* Break on instruction execution. */
#define DR_RW_WRITE (0x1) /* Break on data writes. */
#define DR_RW_IORW (0x2) /* Break on I/O reads or writes (not supported (2001) */
#define DR_RW_READ (0x3) /* Break on data reads or writes. */
/* Debug registers' indices. */
#define DR_NADDR 4 /* The number of debug address registers. */
#define DR_STATUS 6 /* Index of debug status register (DR6). */
#define DR_CONTROL 7 /* Index of debug control register (DR7). */
// 32 for 32bits and 64 for 64bits
#define drxt size_t
#define DR_LEN_1 (0 << 2) /* 1-byte region watch or breakpoint. */
#define DR_LEN_2 (1 << 2) /* 2-byte region watch. */
#define DR_LEN_4 (3 << 2) /* 4-byte region watch. */
#define DR_LEN_8 (2 << 2) /* 8-byte region watch (AMD64). */
#define I386_DR_CONTROL_MASK (~DR_CONTROL_RESERVED)
/* unused */
#define I386_DR_VACANT(control, i) \
((control & (3 << (DR_ENABLE_SIZE * (i)))) == 0)
/* local/global */
#define I386_DR_LOCAL_ENABLE(control, i) \
control |= (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * (i)))
#define I386_DR_GLOBAL_ENABLE(control, i) \
control |= (1 << (DR_GLOBAL_ENABLE_SHIFT + DR_ENABLE_SIZE * (i)))
#define I386_DR_IS_LOCAL_ENABLED(control, i) \
(control & (1 << (DR_LOCAL_ENABLE_SHIFT + DR_ENABLE_SIZE * (i))))
/* enable/disable */
#define I386_DR_IS_ENABLED(control, i) \
control & (3 << (DR_ENABLE_SIZE * (i)))
#define I386_DR_ENABLE(control, i) \
control |= (3 << (DR_ENABLE_SIZE * (i)))
#define I386_DR_DISABLE(control, i) \
control &= ~(3 << (DR_ENABLE_SIZE * (i)))
#define I386_DR_SET_RW_LEN(control, i, rwlen) \
do { \
control &= ~(0x0f << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * (i))); \
control |= ((rwlen) << (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * (i))); \
} while (0)
#define I386_DR_GET_RW_LEN(control, i) \
((control >> (DR_CONTROL_SHIFT + DR_CONTROL_SIZE * (i))) & 0x0f)
/* ----------------------------- */
int drx_set(drxt *drx, int n, ut64 addr, int len, int rwx, int global) {
ut32 control = drx[DR_CONTROL];
if (n < 0 || n >= DR_NADDR) {
eprintf("Invalid DRX index (0-%d)\n", DR_NADDR - 1);
return false;
}
switch (rwx) {
case 1: rwx = 0; break;
case 2: rwx = 1; break;
case 4: rwx = 3; break;
default:
rwx = 0;
}
switch (len) {
case 1: len = 0; break;
case 2: len = 1 << 2; break;
case 4: len = 3 << 2; break;
case 8: len = 2 << 2; break; // AMD64 only
case -1: {
I386_DR_DISABLE(control, n);
control &= I386_DR_CONTROL_MASK;
drx[DR_CONTROL] = control;
drx[n] = 0;
return true;
}
default:
eprintf("Invalid DRX length (%d) must be 1, 2, 4, 8 bytes\n", len);
return false;
}
I386_DR_SET_RW_LEN(control, n, len | rwx);
if (global) {
I386_DR_GLOBAL_ENABLE(control, n);
// control |= DR_GLOBAL_SLOWDOWN;
} else {
I386_DR_LOCAL_ENABLE(control, n);
// control |= DR_LOCAL_SLOWDOWN; // XXX: This is wrong
}
control &= I386_DR_CONTROL_MASK;
drx[n] = addr;
// eprintf ("drx[DR_CONTROL] = %x \n", drx[DR_CONTROL]);
drx[DR_CONTROL] = control;
// eprintf ("CONTROL = %x\n", control);
return true;
}
ut64 drx_get(drxt *drx, int n, int *rwx, int *len, int *global, int *enabled) {
int ret = I386_DR_GET_RW_LEN(drx[DR_CONTROL], n);
if (global) {
*global = I386_DR_IS_LOCAL_ENABLED(drx[7], n);
}
if (len) {
switch (ret & 0xC) {
case DR_LEN_1: *len = 1; break;
case DR_LEN_2: *len = 2; break;
case DR_LEN_4: *len = 4; break;
case DR_LEN_8: *len = 8; break;
default: *len = 0; break;
}
}
if (enabled) {
*enabled = I386_DR_IS_ENABLED(drx[7], n);
}
if (rwx) {
*rwx = ret & 0x3;
}
return (ut64)drx[n];
}
int drx_next(drxt *drx) {
int i;
for (i = 0; i < 4; i++) {
if (!drx[i]) {
return i;
}
}
return -1;
}
int drx_get_at(drxt *drx, ut64 at_addr) {
ut64 addr;
int i, rwx, len, g, en;
for (i = 0; i < 8; i++) {
if (i == 4 || i == 5) {
continue;
}
rwx = len = g = en = 0;
addr = drx_get(drx, i, &rwx, &len, &g, &en);
if (addr == at_addr) {
return i;
}
}
return -1;
}
void drx_list(drxt *drx) {
ut64 addr;
int i, rwx, len, g, en;
for (i = 0; i < 8; i++) {
if (i == 4 || i == 5) {
continue;
}
rwx = len = g = en = 0;
addr = drx_get(drx, i, &rwx, &len, &g, &en);
printf("%c dr%d %c%c 0x%08" PFMT64x " %d\n",
en ? '*' : '-', i, g ? 'G' : 'L',
(rwx == DR_RW_READ) ? 'r' : (rwx == DR_RW_WRITE) ? 'w'
: (rwx == DR_RW_EXECUTE) ? 'x'
: (rwx == DR_RW_IORW) ? 'i'
: '?',
addr, len);
}
}
void drx_init(drxt *r) {
memset(r, 0, sizeof(drxt) * (DRXN + 1));
}
void drx_enable(drxt *r, int n, int enabled) {
if (enabled) {
I386_DR_ENABLE(r[DR_CONTROL], n);
} else {
I386_DR_DISABLE(r[DR_CONTROL], n);
}
}
bool drx_add(RzDebug *dbg, RzBreakpoint *bp, RzBreakpointItem *b) {
if (bp->nhwbps < 4) {
rz_debug_reg_sync(dbg, RZ_REG_TYPE_DRX, false);
rz_debug_drx_set(dbg, bp->nhwbps, b->addr, b->size, b->perm, 0);
rz_debug_reg_sync(dbg, RZ_REG_TYPE_DRX, true);
bp->nhwbps++;
return true;
}
return false;
}
bool drx_del(RzDebug *dbg, RzBreakpoint *bp, RzBreakpointItem *b) {
if (bp->nhwbps > 0) {
rz_debug_reg_sync(dbg, RZ_REG_TYPE_DRX, false);
rz_debug_drx_unset(dbg, bp->nhwbps - 1);
rz_debug_reg_sync(dbg, RZ_REG_TYPE_DRX, true);
bp->nhwbps--;
}
return true;
}
#if MAIN
int main() {
drxt regs[DRXN + 1];
drx_init(regs);
drx_set(regs, 1, 0x8048123, 1, DR_RW_EXECUTE, 0);
drx_set(regs, 0, 0x8048123, 4, DR_RW_READ, 1);
// drx_enable (regs, 0, true);
// drx_enable (regs, 0, false);
drx_list(regs);
}
#endif