Update record & replay features to trace reg/mem changes ##debug (#17127)

* Update record & replay features to trace reg/mem changes ##debug
* Support tracing in r_debug_continue_kill ##debug
* Fix error writing registers when stepping back and refactor ##debug
* Implement checkpoints for reverse debugging and make tests green ##debug
* Add `dbg.trace_continue` option to enable/disable tracing every instruction when continue
* Fix continue when tracing to allow skipping and continuing ##debug
This commit is contained in:
Zi Fan 2020-07-26 21:54:33 -07:00 committed by GitHub
parent 48c30dfd99
commit 2dfa75cc47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 971 additions and 1443 deletions

View File

@ -43,6 +43,8 @@ R_API bool r_anal_op_fini(RAnalOp *op) {
op->src[2] = NULL;
r_anal_value_free (op->dst);
op->dst = NULL;
r_list_free (op->access);
op->access = NULL;
r_strbuf_fini (&op->opex);
r_strbuf_fini (&op->esil);
r_anal_switch_op_free (op->switch_op);
@ -155,6 +157,15 @@ R_API RAnalOp *r_anal_op_copy(RAnalOp *op) {
nop->src[1] = r_anal_value_copy (op->src[1]);
nop->src[2] = r_anal_value_copy (op->src[2]);
nop->dst = r_anal_value_copy (op->dst);
if (op->access) {
RListIter *it;
RAnalValue *val;
RList *naccess = r_list_newf ((RListFree)r_anal_value_free);
r_list_foreach (op->access, it, val) {
r_list_append (naccess, r_anal_value_copy (val));
}
nop->access = naccess;
}
r_strbuf_init (&nop->esil);
r_strbuf_copy (&nop->esil, &op->esil);
return nop;

View File

@ -54,9 +54,6 @@ call = 4
#define ARG1_AR 1
#define ARG2_AR 2
static RRegItem *base_regs[4];
static RRegItem *regdelta_regs[4];
struct Getarg {
csh handle;
cs_insn *insn;
@ -1867,57 +1864,184 @@ static void anop_esil(RAnal *a, RAnalOp *op, ut64 addr, const ut8 *buf, int len,
}
}
static bool is_valid(x86_reg reg) {
return reg != X86_REG_INVALID;
static RRegItem *cs_reg2reg(RReg *reg, csh *h, int id) {
if (id == X86_REG_INVALID) {
return NULL;
}
return r_reg_get (reg, (char *)cs_reg_name (*h, id), -1);
}
static int parse_reg_name(RReg *reg, RRegItem **reg_base, RRegItem **reg_delta, csh *handle, cs_insn *insn, int reg_num) {
cs_x86_op x86op = INSOP (reg_num);
switch (x86op.type) {
case X86_OP_REG:
*reg_base = r_reg_get (reg, (char *)cs_reg_name (*handle, x86op.reg), -1);
static void set_access_info(RReg *reg, RAnalOp *op, csh *handle, cs_insn *insn, int mode) {
int i;
RAnalValue *val;
int regsz;
x86_reg sp;
switch (mode) {
case CS_MODE_64:
regsz = 8;
sp = X86_REG_RSP;
break;
case X86_OP_MEM:
if (is_valid (x86op.mem.base) && is_valid (x86op.mem.index)) {
*reg_base = r_reg_get (reg, (char *)cs_reg_name (*handle, x86op.mem.base), -1);
*reg_delta = r_reg_get (reg, (char *)cs_reg_name (*handle, x86op.mem.index), -1);
} else if (is_valid (x86op.mem.base)) {
*reg_base = r_reg_get (reg, (char *)cs_reg_name (*handle, x86op.mem.base), -1);
} else if (is_valid (x86op.mem.index)) {
*reg_base = r_reg_get (reg, (char *)cs_reg_name (*handle, x86op.mem.index), -1);
case CS_MODE_32:
regsz = 4;
sp = X86_REG_ESP;
break;
case CS_MODE_16:
regsz = 4;
sp = X86_REG_ESP;
break;
default:
regsz = 4;
sp = X86_REG_ESP;
break;
}
RList *ret = r_list_newf ((RListFree)r_anal_value_free);
if (!ret) {
return;
}
// PC register
val = r_anal_value_new ();
val->type = R_ANAL_VAL_REG;
val->access = R_ANAL_ACC_W;
val->reg = cs_reg2reg (reg, handle, X86_REG_RIP);
r_list_append (ret, val);
// Register access info
cs_regs regs_read, regs_write;
ut8 read_count, write_count;
if (cs_regs_access (*handle, insn, regs_read, &read_count, regs_write, &write_count) == 0) {
if (read_count > 0) {
for (i = 0; i < read_count; i++) {
val = r_anal_value_new ();
val->type = R_ANAL_VAL_REG;
val->access = R_ANAL_ACC_R;
val->reg = cs_reg2reg (reg, handle, regs_read[i]);
r_list_append (ret, val);
}
}
if (write_count > 0) {
for (i = 0; i < write_count; i++) {
val = r_anal_value_new ();
val->type = R_ANAL_VAL_REG;
val->access = R_ANAL_ACC_W;
val->reg = cs_reg2reg (reg, handle, regs_write[i]);
r_list_append (ret, val);
}
}
}
switch (insn->id) {
case X86_INS_PUSH:
val = r_anal_value_new ();
val->type = R_ANAL_VAL_MEM;
val->access = CS_AC_WRITE;
val->reg = cs_reg2reg (reg, handle, sp);
val->delta = -INSOP(0).size;
val->memref = INSOP(0).size;
r_list_append (ret, val);
break;
case X86_INS_PUSHAW:
// AX, CX, DX, BX, SP, BP, SI, DI
val = r_anal_value_new ();
val->type = R_ANAL_VAL_MEM;
val->access = CS_AC_WRITE;
val->reg = cs_reg2reg (reg, handle, sp);
val->delta = -16;
val->memref = 16;
r_list_append (ret, val);
break;
case X86_INS_PUSHAL:
// EAX, ECX, EDX, EBX, EBP, ESP, EBP, ESI, EDI
val = r_anal_value_new ();
val->type = R_ANAL_VAL_MEM;
val->access = CS_AC_WRITE;
val->reg = cs_reg2reg (reg, handle, sp);
val->delta = -32;
val->memref = 32;
r_list_append (ret, val);
break;
case X86_INS_PUSHF:
val = r_anal_value_new ();
val->type = R_ANAL_VAL_MEM;
val->access = CS_AC_WRITE;
val->reg = cs_reg2reg (reg, handle, sp);
val->delta = -2;
val->memref = 2;
r_list_append (ret, val);
break;
case X86_INS_PUSHFD:
val = r_anal_value_new ();
val->type = R_ANAL_VAL_MEM;
val->access = CS_AC_WRITE;
val->reg = cs_reg2reg (reg, handle, sp);
val->delta = -4;
val->memref = 4;
r_list_append (ret, val);
break;
case X86_INS_PUSHFQ:
val = r_anal_value_new ();
val->type = R_ANAL_VAL_MEM;
val->access = CS_AC_WRITE;
val->reg = cs_reg2reg (reg, handle, sp);
val->delta = -8;
val->memref = 8;
r_list_append (ret, val);
break;
case X86_INS_CALL:
case X86_INS_LCALL:
val = r_anal_value_new ();
val->type = R_ANAL_VAL_MEM;
val->access = CS_AC_WRITE;
val->reg = cs_reg2reg (reg, handle, sp);
val->delta = -regsz;
val->memref = regsz;
r_list_append (ret, val);
break;
default:
break;
}
return 0;
// Memory access info based on operands
for (i = 0; i < INSOPS; i++) {
if (INSOP (i).type == X86_OP_MEM) {
val = r_anal_value_new ();
val->type = R_ANAL_VAL_MEM;
val->access = INSOP (i).access;
val->mul = INSOP (i).mem.scale;
val->delta = INSOP (i).mem.disp;
if (INSOP(0).mem.base == X86_REG_RIP ||
INSOP(0).mem.base == X86_REG_EIP) {
val->delta += insn->size;
}
val->memref = INSOP (i).size;
val->seg = cs_reg2reg (reg, handle, INSOP (i).mem.segment);
val->reg = cs_reg2reg (reg, handle, INSOP (i).mem.base);
val->regdelta = cs_reg2reg (reg, handle, INSOP (i).mem.index);
r_list_append (ret, val);
}
}
op->access = ret;
}
#define CREATE_SRC_DST(op) \
(op)->src[0] = r_anal_value_new ();\
(op)->src[1] = r_anal_value_new ();\
(op)->src[2] = r_anal_value_new ();\
(op)->dst = r_anal_value_new ();\
ZERO_FILL (base_regs[0]);\
ZERO_FILL (base_regs[1]);\
ZERO_FILL (base_regs[2]);\
ZERO_FILL (base_regs[3]);\
ZERO_FILL (regdelta_regs[0]);\
ZERO_FILL (regdelta_regs[1]);\
ZERO_FILL (regdelta_regs[2]);\
ZERO_FILL (regdelta_regs[3]);
(op)->src[0] = r_anal_value_new (); \
(op)->src[1] = r_anal_value_new (); \
(op)->src[2] = r_anal_value_new (); \
(op)->dst = r_anal_value_new ();
static void set_src_dst(RReg *reg, RAnalValue *val, csh *handle, cs_insn *insn, int x) {
parse_reg_name (reg, &base_regs[x], &regdelta_regs[x], handle, insn, x);
switch (INSOP (x).type) {
case X86_OP_MEM:
val->mul = INSOP (x).mem.scale;
val->delta = INSOP (x).mem.disp;
val->sel = INSOP (x).mem.segment;
val->memref = INSOP (x).size;
val->regdelta = regdelta_regs[x];
val->seg = cs_reg2reg (reg, handle, INSOP (x).mem.segment);
val->reg = cs_reg2reg (reg, handle, INSOP (x).mem.base);
val->regdelta = cs_reg2reg (reg, handle, INSOP (x).mem.index);
break;
case X86_OP_REG:
val->reg = cs_reg2reg (reg, handle, INSOP (x).reg);
break;
case X86_OP_IMM:
val->imm = INSOP (x).imm;
@ -1925,10 +2049,10 @@ static void set_src_dst(RReg *reg, RAnalValue *val, csh *handle, cs_insn *insn,
default:
break;
}
val->reg = base_regs[x];
}
static void op_fillval(RAnal *a, RAnalOp *op, csh *handle, cs_insn *insn) {
static void op_fillval(RAnal *a, RAnalOp *op, csh *handle, cs_insn *insn, int mode) {
set_access_info (a->reg, op, handle, insn, mode);
switch (op->type & R_ANAL_OP_TYPE_MASK) {
case R_ANAL_OP_TYPE_MOV:
case R_ANAL_OP_TYPE_CMP:
@ -3086,7 +3210,7 @@ static int analop(RAnal *a, RAnalOp *op, ut64 addr, const ut8 *buf, int len, RAn
opex (&op->opex, insn, mode);
}
if (mask & R_ANAL_OP_MASK_VAL) {
op_fillval (a, op, &handle, insn);
op_fillval (a, op, &handle, insn, mode);
}
}
//#if X86_GRP_PRIVILEGE>0

View File

@ -12,10 +12,13 @@ R_API RAnalValue *r_anal_value_new_from_string(const char *str) {
}
R_API RAnalValue *r_anal_value_copy(RAnalValue *ov) {
r_return_val_if_fail (ov, NULL);
RAnalValue *v = R_NEW0 (RAnalValue);
if (!v) {
return NULL;
}
memcpy (v, ov, sizeof (RAnalValue));
// reference to reg and regdelta should be kept
return v;

View File

@ -1434,6 +1434,13 @@ static bool cb_dbg_follow_child(void *user, void *data) {
return true;
}
static bool cb_dbg_trace_continue(void *user, void *data) {
RCore *core = (RCore*)user;
RConfigNode *node = (RConfigNode*)data;
core->dbg->trace_continue = node->i_value;
return true;
}
static bool cb_dbg_aftersc(void *user, void *data) {
RCore *core = (RCore*) user;
RConfigNode *node = (RConfigNode*) data;
@ -2755,16 +2762,6 @@ static bool cb_malloc(void *user, void *data) {
return true;
}
static bool cb_dbgsnap(void *user, void *data) {
RCore *core = (RCore*) user;
RConfigNode *node = (RConfigNode*) data;
if (node->value){
r_debug_session_path (core->dbg, node->value);
}
return true;
}
static bool cb_log_config_level(void *coreptr, void *nodeptr) {
RConfigNode *node = (RConfigNode *)nodeptr;
r_log_set_level (node->i_value);
@ -3275,7 +3272,6 @@ R_API int r_core_config_init(RCore *core) {
/* dir */
SETI ("dir.depth", 10, "Maximum depth when searching recursively for files");
SETCB ("dir.dbgsnap", ".", &cb_dbgsnap, "Path to session dump files");
{
char *path = r_str_newf (R_JOIN_2_PATHS ("%s", R2_SDB_MAGIC), r_config_get (core->config, "dir.prefix"));
SETPREF ("dir.magic", path, "Path to r_magic files");
@ -3324,6 +3320,7 @@ R_API int r_core_config_init(RCore *core) {
SETCB ("dbg.profile", "", &cb_runprofile, "Path to RRunProfile file");
SETCB ("dbg.args", "", &cb_dbg_args, "Set the args of the program to debug");
SETCB ("dbg.follow.child", "false", &cb_dbg_follow_child, "Continue tracing the child process on fork. By default the parent process is traced");
SETCB ("dbg.trace_continue", "true", &cb_dbg_trace_continue, "Trace every instruction between the initial PC position and the PC position at the end of continue's execution");
/* debug */
SETCB ("dbg.status", "false", &cb_dbgstatus, "Set cmd.prompt to '.dr*' or '.dr*;drd;sr PC;pi 1;s-'");
#if DEBUGGER

View File

@ -274,24 +274,6 @@ static const char *help_msg_dmp[] = {
NULL
};
static const char *help_msg_dms[] = {
"Usage:", "dms", " # Memory map snapshots",
"dms", "", "List memory snapshots",
"dmsj", "", "List snapshots in JSON",
"dms*", "", "List snapshots in r2 commands",
"dms", " addr", "Take snapshot with given id of map at address",
"dms", "-id", "Delete memory snapshot",
"dmsA", " id", "Apply memory snapshot",
"dmsC", " id comment", "Add comment for given snapshot",
"dmsd", " id", "Hexdiff given snapshot. See `ccc`.",
"dmsw", "", "Snapshot of the writable maps",
"dmsa", "", "Full snapshot of all `dm` maps",
"dmsf", " [file] @ addr", "Read snapshot from disk",
"dmst", " [file] @ addr", "Dump snapshot to disk",
// TODO: dmsj - for json
NULL
};
static const char *help_msg_do[] = {
"Usage:", "do", " # Debug (re)open commands",
"do", "", "Open process (reload, alias for 'oo')",
@ -488,12 +470,9 @@ static const char *help_msg_dte[] = {
static const char *help_msg_dts[] = {
"Usage:", "dts[*]", "",
"dts", "", "List all trace sessions",
"dts+", "", "Add trace session",
"dts-", "id", "Delete trace session",
"dtsf", " [file] ", "Read trace sessions from disk",
"dtst", " [file] ", "Save trace sessions to disk",
"dtsC", " id comment", "Add comment for given trace session",
"dts+", "", "Start trace session",
"dts-", "", "Stop trace session",
"dtsm", "", "List current memory map and hash",
NULL
};
@ -546,7 +525,6 @@ static void cmd_debug_init(RCore *core, RCmdDesc *parent) {
DEFINE_CMD_DESCRIPTOR (core, dmi);
DEFINE_CMD_DESCRIPTOR (core, dmm);
DEFINE_CMD_DESCRIPTOR (core, dmp);
DEFINE_CMD_DESCRIPTOR (core, dms);
DEFINE_CMD_DESCRIPTOR (core, do);
DEFINE_CMD_DESCRIPTOR (core, dp);
DEFINE_CMD_DESCRIPTOR (core, dr);
@ -1227,124 +1205,6 @@ static void cmd_debug_backtrace(RCore *core, const char *input) {
}
}
static int __r_debug_snap_diff(RCore *core, int idx) {
ut32 count = 0;
RDebug *dbg = core->dbg;
ut32 oflags = core->print->flags;
int col = core->cons->columns>123;
RDebugSnap *snap;
RListIter *iter;
core->print->flags |= R_PRINT_FLAGS_DIFFOUT;
r_list_foreach (dbg->snaps, iter, snap) {
if (count == idx) {
ut8 *b = malloc (snap->size);
if (!b) {
eprintf ("Cannot allocate snapshot\n");
continue;
}
dbg->iob.read_at (dbg->iob.io, snap->addr, b , snap->size);
r_print_hexdiff (core->print,
snap->addr, snap->data,
snap->addr, b,
snap->size, col);
free (b);
}
count ++;
}
core->print->flags = oflags;
return 0;
}
static int cmd_debug_map_snapshot(RCore *core, const char *input) {
switch (*input) {
case 'f':
{
char *file;
RDebugSnap *snap;
if (input[1] == ' ') {
file = strdup (input + 2);
} else {
file = r_str_newf ("0x%08"PFMT64x".dump", core->offset);
}
snap = r_debug_snap_get (core->dbg, core->offset);
if (!snap) {
r_debug_snap (core->dbg, core->offset);
snap = r_debug_snap_get (core->dbg, core->offset);
}
if (snap) {
size_t fsz = 0;
char *data = r_file_slurp (file, &fsz);
if (data) {
if (fsz >= snap->size) {
memcpy (snap->data, data, snap->size);
} else {
eprintf ("This file is smaller than the snapshot size\n");
}
free (data);
} else eprintf ("Cannot slurp '%s'\n", file);
} else {
eprintf ("Unable to find a snapshot for 0x%08"PFMT64x"\n", core->offset);
}
free (file);
}
break;
case 't':
{
char *file;
RDebugSnap *snap;
if (input[1] == ' ') {
file = strdup (input + 2);
} else {
file = r_str_newf ("0x%08"PFMT64x".dump", core->offset);
}
snap = r_debug_snap_get (core->dbg, core->offset);
if (snap) {
if (!r_file_dump (file, snap->data, snap->size, 0)) {
eprintf ("Cannot slurp '%s'\n", file);
}
} else {
eprintf ("Unable to find a snapshot for 0x%08"PFMT64x"\n", core->offset);
}
free (file);
}
break;
case '?':
r_core_cmd_help (core, help_msg_dms);
break;
case '-':
if (input[1]=='*') {
r_debug_snap_delete (core->dbg, -1);
} else {
r_debug_snap_delete (core->dbg, r_num_math (core->num, input + 1));
}
break;
case ' ':
r_debug_snap (core->dbg, r_num_math (core->num, input + 1));
break;
case 'A':
r_debug_snap_set_idx (core->dbg, atoi (input + 1));
break;
case 'C':
r_debug_snap_comment (core->dbg, atoi (input + 1), strchr (input, ' '));
break;
case 'd':
__r_debug_snap_diff (core, atoi (input + 1));
break;
case 'a':
r_debug_snap_all (core->dbg, 0);
break;
case 'w':
r_debug_snap_all (core->dbg, R_PERM_RW);
break;
case 0:
case 'j':
case '*':
r_debug_snap_list (core->dbg, -1, input[0]);
break;
}
return 0;
}
static int grab_bits(RCore *core, const char *arg, int *pcbits2) {
int pcbits = atoi (arg);
if (pcbits2) {
@ -1608,9 +1468,6 @@ static int cmd_debug_map(RCore *core, const char *input) {
ut64 addr = core->offset;
switch (input[0]) {
case 's': // "dms"
cmd_debug_map_snapshot (core, input + 1);
break;
case '.': // "dm."
r_debug_map_list (core->dbg, addr, input);
break;
@ -2383,7 +2240,7 @@ static void __tableRegList (RCore *core, RReg *reg, const char *str) {
sdb_fmt ("%d", e->size),
sdb_fmt ("%d", e->packed_size),
sdb_fmt ("%d", e->index),
sdb_fmt ("%d", e->arena),
sdb_fmt ("%d", i),
r_str_bool (e->is_float),
e->name? e->name: "",
e->flags? e->flags: "",
@ -4484,6 +4341,10 @@ static int cmd_debug_continue (RCore *core, const char *input) {
break;
case 'b': // "dcb"
{
if (!core->dbg->session) {
eprintf ("Error: Session has not started\n");
break;
}
if (!r_debug_continue_back (core->dbg)) {
eprintf ("cannot continue back\n");
}
@ -4611,8 +4472,9 @@ static int cmd_debug_step (RCore *core, const char *input) {
ut8 buf[64];
RAnalOp aop;
int i, times = 1;
if (strlen (input) > 2) {
times = r_num_math (core->num, input + 2);
char *ptr = strchr (input, ' ');
if (ptr) {
times = r_num_math (core->num, ptr + 1);
}
if (times < 1) {
times = 1;
@ -4757,8 +4619,10 @@ static int cmd_debug_step (RCore *core, const char *input) {
break;
case 'b': // "dsb"
if (r_config_get_i (core->config, "cfg.debug")) {
if (!r_debug_step_back (core->dbg)) {
eprintf ("cannot step back\n");
if (!core->dbg->session) {
eprintf ("Session has not started\n");
} else if (r_debug_step_back (core->dbg, times) < 0) {
eprintf ("Error: stepping back failed\n");
}
} else {
if (r_core_esil_step_back (core)) {
@ -5013,43 +4877,31 @@ static int cmd_debug(void *data, const char *input) {
break;
case 's': // "dts"
switch (input[2]) {
case 0: // "dts"
r_debug_session_list (core->dbg);
break;
case '+': // "dts+"
r_debug_session_add (core->dbg, NULL);
if (r_debug_is_dead (core->dbg)) {
eprintf ("Cannot start session outside of debug mode, run ood?\n");
break;
}
if (core->dbg->session) {
eprintf ("Session already started\n");
break;
}
core->dbg->session = r_debug_session_new (core->dbg);
r_debug_add_checkpoint (core->dbg);
break;
case '-': // "dts-"
if (input[3] == ' ') {
r_debug_session_delete (core->dbg, r_num_math (core->num, input + 3));
} else {
r_cons_println ("Usage: dts- [id] - Delete trace session");
if (!core->dbg->session) {
eprintf ("No session started\n");
break;
}
r_debug_session_free (core->dbg->session);
core->dbg->session = NULL;
break;
case 't': // "dtst"
if (input[3] == ' ') {
r_debug_session_save (core->dbg, input + 4);
} else {
r_cons_println ("Usage: dtst [file] - save trace sessions to disk");
case 'm': // "dtsm"
if (core->dbg->session) {
r_debug_session_list_memory (core->dbg);
}
break;
case 'f': // "dtsf"
if (input[3] == ' ') {
r_debug_session_restore (core->dbg, input + 4);
} else {
r_cons_println ("Usage: dtsf [file] - read trace sessions from disk");
}
break;
case 'C': // "dtsC"
if (input[3] == ' ') {
r_debug_session_comment (core->dbg, atoi (input + 3), strchr (input + 4, ' '));
} else {
r_cons_println ("Usage: dtsC id comment - add comment for given trace session");
}
break;
case 'A': // "dtsA" for debugging command (private command for developer)
r_debug_session_set_idx (core->dbg, atoi (input + 4));
break;
default:
r_core_cmd_help (core, help_msg_dts);
}

View File

@ -177,7 +177,7 @@ static int r_debug_bp_hit(RDebug *dbg, RRegItem *pc_ri, ut64 pc, RBreakpointItem
/* inform the user of what happened */
if (dbg->hitinfo) {
eprintf ("hit %spoint at: %" PFMT64x "\n",
eprintf ("hit %spoint at: 0x%" PFMT64x "\n",
b->trace ? "trace" : "break", pc);
}
@ -255,6 +255,7 @@ static int r_debug_recoil(RDebug *dbg, RDebugRecoilMode rc_mode) {
if (!dbg->reason.bp_addr && dbg->recoil_mode == R_DBG_RECOIL_STEP) {
return true;
}
dbg->reason.bp_addr = 0;
return r_debug_bps_enable (dbg);
}
@ -364,8 +365,6 @@ R_API RDebug *r_debug_new(int hard) {
R_FREE (dbg->btalgo);
dbg->trace_execs = 0;
dbg->anal = NULL;
dbg->snaps = r_list_newf ((RListFree)r_debug_snap_free);
dbg->sessions = r_list_newf ((RListFree)r_debug_session_free);
dbg->pid = -1;
dbg->bpsize = 1;
dbg->tid = -1;
@ -414,8 +413,6 @@ R_API RDebug *r_debug_free(RDebug *dbg) {
r_bp_free (dbg->bp);
//r_reg_free(&dbg->reg);
free (dbg->snap_path);
r_list_free (dbg->snaps);
r_list_free (dbg->sessions);
r_list_free (dbg->maps);
r_list_free (dbg->maps_user);
r_list_free (dbg->threads);
@ -428,6 +425,8 @@ R_API RDebug *r_debug_free(RDebug *dbg) {
r_list_free (dbg->call_frames);
free (dbg->btalgo);
r_debug_trace_free (dbg->trace);
r_debug_session_free (dbg->session);
r_anal_op_free (dbg->cur_op);
dbg->trace = NULL;
r_egg_free (dbg->egg);
free (dbg->arch);
@ -724,9 +723,9 @@ R_API RDebugReasonType r_debug_wait(RDebug *dbg, RBreakpointItem **bp) {
bool libs_bp = (dbg->glob_libs || dbg->glob_unlibs) ? true : false;
/* if the underlying stop reason is a breakpoint, call the handlers */
if (reason == R_DEBUG_REASON_BREAKPOINT || reason == R_DEBUG_REASON_STEP ||
(libs_bp &&
((reason == R_DEBUG_REASON_NEW_LIB) || (reason == R_DEBUG_REASON_EXIT_LIB)))) {
if (reason == R_DEBUG_REASON_BREAKPOINT ||
reason == R_DEBUG_REASON_STEP ||
(libs_bp && ((reason == R_DEBUG_REASON_NEW_LIB) || (reason == R_DEBUG_REASON_EXIT_LIB)))) {
RRegItem *pc_ri;
RBreakpointItem *b = NULL;
ut64 pc;
@ -749,6 +748,9 @@ R_API RDebugReasonType r_debug_wait(RDebug *dbg, RBreakpointItem **bp) {
*bp = b;
}
if (b && reason == R_DEBUG_REASON_STEP) {
reason = R_DEBUG_REASON_BREAKPOINT;
}
/* if we hit a tracing breakpoint, we need to continue in
* whatever mode the user desired. */
if (dbg->corebind.core && b && b->cond) {
@ -881,7 +883,7 @@ R_API int r_debug_step_soft(RDebug *dbg) {
return ret;
}
R_API int r_debug_step_hard(RDebug *dbg) {
R_API int r_debug_step_hard(RDebug *dbg, RBreakpointItem **pb) {
RDebugReasonType reason;
dbg->reason.type = R_DEBUG_REASON_STEP;
@ -906,12 +908,13 @@ R_API int r_debug_step_hard(RDebug *dbg) {
if (!dbg->h->step (dbg)) {
return false;
}
reason = r_debug_wait (dbg, NULL);
reason = r_debug_wait (dbg, pb);
if (reason == R_DEBUG_REASON_DEAD || r_debug_is_dead (dbg)) {
return false;
}
// Unset breakpoints before leaving
if (reason != R_DEBUG_REASON_BREAKPOINT && reason != R_DEBUG_REASON_COND &&
if (reason != R_DEBUG_REASON_BREAKPOINT &&
reason != R_DEBUG_REASON_COND &&
reason != R_DEBUG_REASON_TRACEPOINT) {
r_bp_restore (dbg->bp, false);
}
@ -923,6 +926,7 @@ R_API int r_debug_step_hard(RDebug *dbg) {
}
R_API int r_debug_step(RDebug *dbg, int steps) {
RBreakpointItem *bp = NULL;
int ret, steps_taken = 0;
/* who calls this without giving a positive number? */
@ -940,16 +944,40 @@ R_API int r_debug_step(RDebug *dbg, int steps) {
dbg->reason.type = R_DEBUG_REASON_STEP;
if (dbg->session) {
if (dbg->session->cnum != dbg->session->maxcnum) {
steps_taken = r_debug_step_cnum (dbg, steps);
}
}
for (; steps_taken < steps; steps_taken++) {
if (dbg->session && dbg->recoil_mode == R_DBG_RECOIL_NONE) {
dbg->session->cnum++;
dbg->session->maxcnum++;
dbg->session->bp = 0;
if (!r_debug_trace_ins_before (dbg)) {
eprintf ("trace_ins_before: failed");
}
}
if (dbg->swstep) {
ret = r_debug_step_soft (dbg);
} else {
ret = r_debug_step_hard (dbg);
ret = r_debug_step_hard (dbg, &bp);
}
if (!ret) {
eprintf ("Stepping failed!\n");
return steps_taken;
}
if (dbg->session && dbg->recoil_mode == R_DBG_RECOIL_NONE) {
if (!r_debug_trace_ins_after (dbg)) {
eprintf ("trace_ins_after: failed");
}
dbg->session->reasontype = dbg->reason.type;
dbg->session->bp = bp;
}
dbg->steps++;
dbg->reason.type = R_DEBUG_REASON_STEP;
}
@ -984,9 +1012,17 @@ R_API int r_debug_step_over(RDebug *dbg, int steps) {
if (dbg->h && dbg->h->step_over) {
for (; steps_taken < steps; steps_taken++) {
if (dbg->session && dbg->recoil_mode == R_DBG_RECOIL_NONE) {
dbg->session->cnum++;
dbg->session->maxcnum++;
r_debug_trace_ins_before (dbg);
}
if (!dbg->h->step_over (dbg)) {
return steps_taken;
}
if (dbg->session && dbg->recoil_mode == R_DBG_RECOIL_NONE) {
r_debug_trace_ins_after (dbg);
}
}
return steps_taken;
}
@ -1037,105 +1073,35 @@ R_API int r_debug_step_over(RDebug *dbg, int steps) {
return steps_taken;
}
static ut64 get_prev_instr(RDebug *dbg, ut64 from, ut64 to) {
int i, ret, bsize = 256;
int inc;
ut64 prev = 0, at;
RAnalOp aop = {0};
const int mininstrsz = r_anal_archinfo (dbg->anal, R_ANAL_ARCHINFO_MIN_OP_SIZE);
const int minopcode = R_MAX (1, mininstrsz);
ut8 *buf = malloc (bsize);
R_API bool r_debug_goto_cnum(RDebug *dbg, ut32 cnum) {
if (cnum > dbg->session->maxcnum) {
eprintf ("Error: out of cnum range\n");
return false;
}
dbg->session->cnum = cnum;
r_debug_session_restore_reg_mem (dbg, cnum);
if (!buf) {
eprintf ("Cannot allocate %d byte(s)\n", bsize);
free (buf);
return 0;
}
for (i = 0, at = from; at < to; at++, i++) {
if (i >= (bsize - 32)) {
i = 0;
}
if (!i) {
dbg->iob.read_at (dbg->iob.io, at, buf, bsize);
}
ret = r_anal_op (dbg->anal, &aop, at, buf + i, bsize - i, R_ANAL_OP_MASK_BASIC);
inc = ret - 1;
if (inc < 0) {
inc = minopcode;
}
prev = at;
i += inc;
at += inc;
r_anal_op_fini (&aop);
}
free (buf);
return prev;
return true;
}
// TODO: add <int steps> parameter for repetition like step() and step_over() do and change return type to int
R_API bool r_debug_step_back(RDebug *dbg) {
ut64 pc, prev = 0, end;
RDebugSession *before;
R_API int r_debug_step_back(RDebug *dbg, int steps) {
if (steps > dbg->session->cnum) {
steps = dbg->session->cnum;
}
if (!r_debug_goto_cnum (dbg, dbg->session->cnum - steps)) {
return -1;
}
return steps;
}
if (r_debug_is_dead (dbg)) {
return false;
R_API int r_debug_step_cnum(RDebug *dbg, int steps) {
if (steps > dbg->session->maxcnum - dbg->session->cnum) {
steps = dbg->session->maxcnum - dbg->session->cnum;
}
if (!dbg->anal || !dbg->reg) {
return false;
}
if (r_list_empty (dbg->sessions)) {
return false;
}
end = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]);
/* Get previous state */
before = r_debug_session_get (dbg, dbg->sessions->tail);
if (!before) {
return false;
}
#if 0
ut64 cnt = 0;
//eprintf ("before session (%d) 0x%08"PFMT64x"\n", before->key.id, before->key.addr);
r_debug_goto_cnum (dbg, dbg->session->cnum + steps);
/* Rollback to previous state */
r_debug_session_set (dbg, before);
pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]);
//eprintf ("execute from 0x%08"PFMT64x" to 0x%08"PFMT64x"\n", pc, end);
/* Get the previous operation address.
* XXX: too slow... */
for (;;) {
if (r_debug_is_dead (dbg)) {
return false;
}
pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]);
if (pc == end) {
/* Reached the target address */
break;
}
prev = pc;
//eprintf ("executing 0x%08"PFMT64x"\n", pc);
if (cnt > CHECK_POINT_LIMIT) {
//eprintf ("Hit count limit %lld\n", cnt);
r_debug_session_add (dbg, NULL);
cnt = 0;
}
if (!r_debug_step (dbg, 1)) {
return false;
}
cnt++;
}
#endif
/* Rollback to previous state and then run to the desired point */
r_debug_session_set (dbg, before);
pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]);
prev = get_prev_instr (dbg, pc, end);
if (prev) {
eprintf ("continue until 0x%08"PFMT64x"\n", prev);
r_debug_continue_until_nonblock (dbg, prev);
}
return true;
return steps;
}
R_API int r_debug_continue_kill(RDebug *dbg, int sig) {
@ -1146,11 +1112,46 @@ R_API int r_debug_continue_kill(RDebug *dbg, int sig) {
if (!dbg) {
return 0;
}
// If the debugger is not at the end of the changes
// Go to the end or the next breakpoint in the changes
if (dbg->session && dbg->session->cnum != dbg->session->maxcnum) {
bool has_bp = false;
RRegItem *ripc = r_reg_get (dbg->reg, dbg->reg->name[R_REG_NAME_PC], R_REG_TYPE_GPR);
RVector *vreg = ht_up_find (dbg->session->registers, ripc->offset | (ripc->arena << 16), NULL);
RDebugChangeReg *reg;
r_vector_foreach_prev (vreg, reg) {
if (reg->cnum <= dbg->session->cnum) {
continue;
}
has_bp = r_bp_get_in (dbg->bp, reg->data, R_BP_PROT_EXEC) != NULL;
if (has_bp) {
eprintf ("hit breakpoint at: 0x%" PFMT64x " cnum: %d\n", reg->data, reg->cnum);
r_debug_goto_cnum (dbg, reg->cnum);
return dbg->tid;
}
}
r_debug_goto_cnum (dbg, dbg->session->maxcnum);
return dbg->tid;
}
repeat:
if (r_debug_is_dead (dbg)) {
return 0;
}
if (dbg->h && dbg->h->cont) {
if (dbg->session && dbg->trace_continue) {
while (!r_cons_is_breaked ()) {
if (r_debug_step (dbg, 1) != 1) {
break;
}
if (dbg->session->reasontype != R_DEBUG_REASON_STEP) {
break;
}
}
reason = dbg->session->reasontype;
bp = dbg->session->bp;
} else if (dbg->h && dbg->h->cont) {
/* handle the stage-2 of breakpoints */
if (!r_debug_recoil (dbg, R_DBG_RECOIL_CONTINUE)) {
return 0;
@ -1158,119 +1159,121 @@ repeat:
/* tell the inferior to go! */
ret = dbg->h->cont (dbg, dbg->pid, dbg->tid, sig);
//XXX(jjd): why? //dbg->reason.signum = 0;
reason = r_debug_wait (dbg, &bp);
if (dbg->corebind.core) {
RCore *core = (RCore *)dbg->corebind.core;
RNum *num = core->num;
if (reason == R_DEBUG_REASON_COND) {
if (bp && bp->cond && dbg->corebind.cmd) {
dbg->corebind.cmd (dbg->corebind.core, bp->cond);
}
if (num->value) {
goto repeat;
}
} else {
return 0;
}
if (dbg->corebind.core) {
RCore *core = (RCore *)dbg->corebind.core;
RNum *num = core->num;
if (reason == R_DEBUG_REASON_COND) {
if (bp && bp->cond && dbg->corebind.cmd) {
dbg->corebind.cmd (dbg->corebind.core, bp->cond);
}
if (num->value) {
goto repeat;
}
}
if (reason == R_DEBUG_REASON_BREAKPOINT &&
((bp && !bp->enabled) || (!bp && !r_cons_is_breaked () && dbg->corebind.core &&
dbg->corebind.cfggeti (dbg->corebind.core, "dbg.bpsysign")))) {
goto repeat;
}
}
if (reason == R_DEBUG_REASON_BREAKPOINT &&
((bp && !bp->enabled) || (!bp && !r_cons_is_breaked () && dbg->corebind.core &&
dbg->corebind.cfggeti (dbg->corebind.core, "dbg.bpsysign")))) {
goto repeat;
}
#if __linux__
if (reason == R_DEBUG_REASON_NEW_PID && dbg->follow_child) {
if (reason == R_DEBUG_REASON_NEW_PID && dbg->follow_child) {
#if DEBUGGER
/// if the plugin is not compiled link fails, so better do runtime linking
/// until this code gets fixed
static bool (*linux_attach_new_process) (RDebug *dbg, int pid) = NULL;
if (!linux_attach_new_process) {
linux_attach_new_process = r_lib_dl_sym (NULL, "linux_attach_new_process");
}
if (linux_attach_new_process) {
linux_attach_new_process (dbg, dbg->forked_pid);
}
#endif
goto repeat;
/// if the plugin is not compiled link fails, so better do runtime linking
/// until this code gets fixed
static bool (*linux_attach_new_process) (RDebug *dbg, int pid) = NULL;
if (!linux_attach_new_process) {
linux_attach_new_process = r_lib_dl_sym (NULL, "linux_attach_new_process");
}
if (linux_attach_new_process) {
linux_attach_new_process (dbg, dbg->forked_pid);
}
#endif
goto repeat;
}
if (reason == R_DEBUG_REASON_NEW_TID) {
ret = dbg->tid;
if (!dbg->trace_clone) {
goto repeat;
}
if (reason == R_DEBUG_REASON_NEW_TID) {
ret = dbg->tid;
if (!dbg->trace_clone) {
goto repeat;
}
}
if (reason == R_DEBUG_REASON_EXIT_TID) {
goto repeat;
}
if (reason == R_DEBUG_REASON_EXIT_TID) {
goto repeat;
}
#endif
if (reason != R_DEBUG_REASON_DEAD) {
ret = dbg->tid;
}
if (reason != R_DEBUG_REASON_DEAD) {
ret = dbg->tid;
}
#if __WINDOWS__
if (reason == R_DEBUG_REASON_NEW_LIB ||
reason == R_DEBUG_REASON_EXIT_LIB ||
reason == R_DEBUG_REASON_NEW_TID ||
reason == R_DEBUG_REASON_NONE ||
reason == R_DEBUG_REASON_EXIT_TID ) {
goto repeat;
}
if (reason == R_DEBUG_REASON_NEW_LIB ||
reason == R_DEBUG_REASON_EXIT_LIB ||
reason == R_DEBUG_REASON_NEW_TID ||
reason == R_DEBUG_REASON_NONE ||
reason == R_DEBUG_REASON_EXIT_TID ) {
goto repeat;
}
#endif
if (reason == R_DEBUG_REASON_EXIT_PID) {
if (reason == R_DEBUG_REASON_EXIT_PID) {
#if __WINDOWS__
dbg->pid = -1;
dbg->pid = -1;
#elif __linux__
r_debug_bp_update (dbg);
r_bp_restore (dbg->bp, false); // (vdf) there has got to be a better way
r_debug_bp_update (dbg);
r_bp_restore (dbg->bp, false); // (vdf) there has got to be a better way
#endif
}
}
/* if continuing killed the inferior, we won't be able to get
* the registers.. */
if (reason == R_DEBUG_REASON_DEAD || r_debug_is_dead (dbg)) {
return 0;
}
/* if continuing killed the inferior, we won't be able to get
* the registers.. */
if (reason == R_DEBUG_REASON_DEAD || r_debug_is_dead (dbg)) {
return 0;
}
/* if we hit a tracing breakpoint, we need to continue in
* whatever mode the user desired. */
if (reason == R_DEBUG_REASON_TRACEPOINT) {
r_debug_step (dbg, 1);
/* if we hit a tracing breakpoint, we need to continue in
* whatever mode the user desired. */
if (reason == R_DEBUG_REASON_TRACEPOINT) {
r_debug_step (dbg, 1);
goto repeat;
}
/* choose the thread that was returned from the continue function */
// XXX(jjd): there must be a cleaner way to do this...
if (ret != dbg->tid) {
r_debug_select (dbg, dbg->pid, ret);
}
sig = 0; // clear continuation after signal if needed
/* handle general signals here based on the return from the wait
* function */
if (dbg->reason.signum != -1) {
int what = r_debug_signal_what (dbg, dbg->reason.signum);
if (what & R_DBG_SIGNAL_CONT) {
sig = dbg->reason.signum;
eprintf ("Continue into the signal %d handler\n", sig);
goto repeat;
}
/* choose the thread that was returned from the continue function */
// XXX(jjd): there must be a cleaner way to do this...
if (ret != dbg->tid) {
r_debug_select (dbg, dbg->pid, ret);
}
sig = 0; // clear continuation after signal if needed
/* handle general signals here based on the return from the wait
* function */
if (dbg->reason.signum != -1) {
int what = r_debug_signal_what (dbg, dbg->reason.signum);
if (what & R_DBG_SIGNAL_CONT) {
sig = dbg->reason.signum;
eprintf ("Continue into the signal %d handler\n", sig);
} else if (what & R_DBG_SIGNAL_SKIP) {
// skip signal. requires skipping one instruction
ut8 buf[64];
RAnalOp op = {0};
ut64 pc = r_debug_reg_get (dbg, "PC");
dbg->iob.read_at (dbg->iob.io, pc, buf, sizeof (buf));
r_anal_op (dbg->anal, &op, pc, buf, sizeof (buf), R_ANAL_OP_MASK_BASIC);
if (op.size > 0) {
const char *signame = r_signal_to_string (dbg->reason.signum);
r_debug_reg_set (dbg, "PC", pc+op.size);
eprintf ("Skip signal %d handler %s\n",
dbg->reason.signum, signame);
goto repeat;
} else if (what & R_DBG_SIGNAL_SKIP) {
// skip signal. requires skipping one instruction
ut8 buf[64];
RAnalOp op = {0};
} else {
ut64 pc = r_debug_reg_get (dbg, "PC");
dbg->iob.read_at (dbg->iob.io, pc, buf, sizeof (buf));
r_anal_op (dbg->anal, &op, pc, buf, sizeof (buf), R_ANAL_OP_MASK_BASIC);
if (op.size > 0) {
const char *signame = r_signal_to_string (dbg->reason.signum);
r_debug_reg_set (dbg, "PC", pc+op.size);
eprintf ("Skip signal %d handler %s\n",
dbg->reason.signum, signame);
goto repeat;
} else {
ut64 pc = r_debug_reg_get (dbg, "PC");
eprintf ("Stalled with an exception at 0x%08"PFMT64x"\n", pc);
}
eprintf ("Stalled with an exception at 0x%08"PFMT64x"\n", pc);
}
}
}
@ -1286,6 +1289,14 @@ repeat:
if (reason != R_DEBUG_REASON_BREAKPOINT) {
r_bp_restore (dbg->bp, false);
}
// Add a checkpoint at stops
if (dbg->session && !dbg->trace_continue) {
dbg->session->cnum++;
dbg->session->maxcnum++;
r_debug_add_checkpoint (dbg);
}
return ret;
}
@ -1402,78 +1413,36 @@ R_API int r_debug_continue_until_nonblock(RDebug *dbg, ut64 addr) {
}
R_API bool r_debug_continue_back(RDebug *dbg) {
RBreakpointItem *prev = NULL;
ut64 pc;
if (!dbg) {
return false;
}
if (!dbg->anal || !dbg->reg) {
return false;
}
if (r_debug_is_dead (dbg)) {
return false;
}
if (r_list_empty (dbg->sessions)) {
return false;
}
int cnum;
bool has_bp = false;
if (!dbg->sessions) {
RRegItem *ripc = r_reg_get (dbg->reg, dbg->reg->name[R_REG_NAME_PC], R_REG_TYPE_GPR);
RVector *vreg = ht_up_find (dbg->session->registers, ripc->offset | (ripc->arena << 16), NULL);
if (!vreg) {
eprintf ("Error: cannot find PC change vector");
return false;
}
/* Get previous state */
RListIter *iter = dbg->sessions->head;
if (!iter || !iter->data) {
return false;
}
RDebugSession *before = iter->data; //XXX: currently use first session.
ut64 end_addr = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]);
//eprintf ("before session (%d) 0x%08"PFMT64x"=> to 0x%08"PFMT64x"\n", before->key.id, before->key.addr, end_addr);
/* Rollback to previous state */
r_debug_session_set (dbg, before);
/* ### Get previous breakpoint ### */
// Firstly set the breakpoint at end address
bool has_bp = r_bp_get_in (dbg->bp, end_addr, R_BP_PROT_EXEC) != NULL;
if (!has_bp) {
r_bp_add_sw (dbg->bp, end_addr, dbg->bpsize, R_BP_PROT_EXEC);
}
// Continue until end_addr
for (;;) {
if (r_debug_is_dead (dbg)) {
RDebugChangeReg *reg;
r_vector_foreach_prev (vreg, reg) {
if (reg->cnum >= dbg->session->cnum) {
continue;
}
has_bp = r_bp_get_in (dbg->bp, reg->data, R_BP_PROT_EXEC) != NULL;
if (has_bp) {
cnum = reg->cnum;
eprintf ("hit breakpoint at: 0x%" PFMT64x " cnum: %d\n", reg->data, reg->cnum);
break;
}
pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]);
if (pc == end_addr) {
break;
}
prev = r_bp_get_at (dbg->bp, pc);
r_debug_continue (dbg);
}
// Clean up if needed
if (!has_bp) {
r_bp_del (dbg->bp, end_addr);
}
if (!prev) {
return false;
}
//eprintf ("prev->addr = 0x%08"PFMT64x"\n", prev->addr);
/* Now we got previous breakpoint.
* ### Continue until prev breakpoint ### */
/* Rollback to previous state again */
r_debug_session_set (dbg, before);
for (;;) {
if (r_debug_is_dead (dbg)) {
break;
if (has_bp) {
r_debug_goto_cnum (dbg, cnum);
} else {
if (dbg->session->maxcnum > 0) {
r_debug_goto_cnum (dbg, 0);
}
pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]);
if (prev == r_bp_get_at (dbg->bp, pc)) {
break;
}
r_debug_continue (dbg);
}
return true;
}

View File

@ -2,511 +2,232 @@
#include <r_debug.h>
R_API void r_debug_session_free(void *p) {
RDebugSession *session = (RDebugSession *) p;
free (session->comment);
free (session);
}
static int r_debug_session_lastid(RDebug *dbg) {
return r_list_length (dbg->sessions);
}
R_API void r_debug_session_list(RDebug *dbg) {
ut32 count = 0;
RListIter *iterse, *itersn, *iterpg;
RDebugSnap *snap;
RDebugSnapDiff *diff;
RDebugSession *session;
RPageData *page;
r_list_foreach (dbg->sessions, iterse, session) {
count = 0;
dbg->cb_printf ("session:%2d at:0x%08"PFMT64x " \"%s\"\n", session->key.id, session->key.addr, session->comment);
r_list_foreach (session->memlist, itersn, diff) {
snap = diff->base;
dbg->cb_printf (" - %d 0x%08"PFMT64x " - 0x%08"PFMT64x " size: %d ",
count, snap->addr, snap->addr_end, snap->size);
dbg->cb_printf ("(pages: ");
r_list_foreach (diff->pages, iterpg, page) {
dbg->cb_printf ("%d ", page->page_off);
}
dbg->cb_printf (")\n");
count++;
}
R_API void r_debug_session_free(RDebugSession *session) {
if (session) {
r_vector_free (session->checkpoints);
ht_up_free (session->registers);
ht_up_free (session->memory);
R_FREE (session);
}
}
R_API RDebugSession *r_debug_session_add(RDebug *dbg, RListIter **tail) {
RDebugSession *session;
RDebugSnapDiff *diff;
RListIter *iter;
RDebugMap *map;
ut64 addr;
int i, perms = R_PERM_RW;
static void r_debug_checkpoint_fini(void *element, void *user) {
RDebugCheckpoint *checkpoint = element;
r_list_free (checkpoint->snaps);
}
addr = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]);
/* Session has already existed at this addr? */
r_list_foreach (dbg->sessions, iter, session) {
if (session->key.addr == addr) {
if (tail) {
*tail = iter;
}
return session;
}
}
static void htup_vector_free(HtUPKv *kv) {
r_vector_free (kv->value);
}
session = R_NEW0 (RDebugSession);
R_API RDebugSession *r_debug_session_new(RDebug *dbg) {
RDebugSession *session = R_NEW0 (RDebugSession);
if (!session) {
return NULL;
}
session->key = (RDebugKey) {
addr, r_debug_session_lastid (dbg)
};
session->comment = r_str_new ("");
/* save current registers */
r_debug_reg_sync (dbg, R_REG_TYPE_ALL, 0);
for (i = 0; i < R_REG_TYPE_LAST; i++) {
session->reg[i] = r_list_tail (dbg->reg->regset[i].pool);
session->checkpoints = r_vector_new (sizeof (RDebugCheckpoint), r_debug_checkpoint_fini, NULL);
if (!session->checkpoints) {
r_debug_session_free (session);
return NULL;
}
r_reg_arena_push (dbg->reg);
/* save memory snapshots */
session->memlist = r_list_newf ((RListFree)r_debug_diff_free);
r_debug_map_sync (dbg);
r_list_foreach (dbg->maps, iter, map) {
if (!perms || (map->perm & perms) == perms) {
diff = r_debug_snap_map (dbg, map);
if (diff) {
/* Add diff history */
r_list_append (session->memlist, diff);
}
}
session->registers = ht_up_new (NULL, htup_vector_free, NULL);
if (!session->registers) {
r_debug_session_free (session);
return NULL;
}
session->memory = ht_up_new (NULL, htup_vector_free, NULL);
if (!session->memory) {
r_debug_session_free (session);
return NULL;
}
r_list_append (dbg->sessions, session);
if (tail) {
*tail = dbg->sessions->tail;
}
return session;
}
R_API bool r_debug_session_delete(RDebug *dbg, int idx) {
RListIter *iter;
RDebugSession *session;
if (idx == -1) {
r_list_free (dbg->sessions);
dbg->sessions = r_list_newf ((RListFree)r_debug_session_free);
return true;
}
r_list_foreach (dbg->sessions, iter, session) {
if (session->key.id == idx) {
r_list_delete (dbg->sessions, iter);
return true;
}
}
return false;
}
R_API bool r_debug_add_checkpoint(RDebug *dbg) {
r_return_val_if_fail (dbg->session, false);
size_t i;
RDebugCheckpoint checkpoint = { 0 };
R_API bool r_debug_session_comment(RDebug *dbg, int idx, const char *msg) {
RDebugSession *session;
RListIter *iter;
ut32 count = 0;
if (!dbg || idx < 0 || !msg || !*msg) {
// Save current registers arena iter
r_debug_reg_sync (dbg, R_REG_TYPE_ALL, 0);
for (i = 0; i < R_REG_TYPE_LAST; i++) {
checkpoint.reg[i] = r_list_tail (dbg->reg->regset[i].pool);
}
r_reg_arena_push (dbg->reg);
// Save current memory maps
checkpoint.snaps = r_list_newf ((RListFree)r_debug_snap_free);
if (!checkpoint.snaps) {
return false;
}
r_list_foreach (dbg->sessions, iter, session) {
if (count == idx) {
if (session->comment) {
free (session->comment);
RListIter *iter;
RDebugMap *map;
r_debug_map_sync (dbg);
r_list_foreach (dbg->maps, iter, map) {
if ((map->perm & R_PERM_RW) == R_PERM_RW) {
RDebugSnap *snap = r_debug_snap_map (dbg, map);
if (snap) {
r_list_append (checkpoint.snaps, snap);
}
session->comment = strdup (r_str_trim_head_ro (msg));
break;
}
count++;
}
checkpoint.cnum = dbg->session->cnum;
r_vector_push (dbg->session->checkpoints, &checkpoint);
// Add PC register change so we can check for breakpoints when continue [back]
RRegItem *ripc = r_reg_get (dbg->reg, dbg->reg->name[R_REG_NAME_PC], R_REG_TYPE_GPR);
ut64 data = r_reg_get_value (dbg->reg, ripc);
r_debug_session_add_reg_change (dbg->session, ripc->arena, ripc->offset, data);
return true;
}
static void r_debug_session_set_registers(RDebug *dbg, RDebugSession *session) {
RRegArena *arena;
RListIter *iterr;
int i;
/* Restore all register values from the stack area pointed by session */
r_debug_reg_sync (dbg, R_REG_TYPE_ALL, 0);
static void _set_initial_registers(RDebug *dbg) {
size_t i;
for (i = 0; i < R_REG_TYPE_LAST; i++) {
iterr = session->reg[i];
arena = iterr->data;
RListIter *iter = dbg->session->cur_chkpt->reg[i];
RRegArena *arena = iter->data;
if (dbg->reg->regset[i].arena->bytes) {
memcpy (dbg->reg->regset[i].arena->bytes, arena->bytes, arena->size);
}
}
r_debug_reg_sync (dbg, R_REG_TYPE_ALL, 1);
}
static void r_debug_session_set_diff(RDebug *dbg, RDebugSession *session) {
RListIter *iter;
RDebugSnapDiff *diff;
r_debug_session_set_registers (dbg, session);
/* Restore all memory values from memory (diff) snapshots */
r_list_foreach (session->memlist, iter, diff) {
r_debug_diff_set (dbg, diff);
static void _set_register(RDebug *dbg, RRegItem *ri, ut32 cnum) {
RVector *vreg = ht_up_find (dbg->session->registers, ri->offset | (ri->arena << 16), NULL);
if (!vreg) {
return;
}
size_t index;
r_vector_upper_bound (vreg, cnum, index, CMP_CNUM_REG);
if (index > 0 && index <= vreg->len) {
RDebugChangeReg *reg = r_vector_index_ptr (vreg, index - 1);
if (reg->cnum > dbg->session->cur_chkpt->cnum) {
r_reg_set_value (dbg->reg, ri, reg->data);
}
}
}
static void r_debug_session_set_base(RDebug *dbg, RDebugSession *before) {
R_API void _restore_registers(RDebug *dbg, ut32 cnum) {
RListIter *iter;
RRegItem *ri;
_set_initial_registers (dbg);
r_list_foreach (dbg->reg->allregs, iter, ri) {
_set_register (dbg, ri, cnum);
}
}
static void _set_initial_memory(RDebug *dbg) {
RListIter *iter;
RDebugSnap *snap;
r_debug_session_set_registers (dbg, before);
/* Restore all memory values from base memory snapshots */
r_list_foreach (dbg->snaps, iter, snap) {
r_debug_diff_set_base (dbg, snap);
r_list_foreach (dbg->session->cur_chkpt->snaps, iter, snap) {
dbg->iob.write_at (dbg->iob.io, snap->addr, snap->data, snap->size);
}
}
R_API void r_debug_session_set(RDebug *dbg, RDebugSession *before) {
if (!r_list_length (before->memlist)) {
/* Diff list is empty. (i.e. Before session is base snapshot) *
So set base memory snapshot */
r_debug_session_set_base (dbg, before);
} else {
r_debug_session_set_diff (dbg, before);
static bool _restore_memory_cb(void *user, const ut64 key, const void *value) {
size_t index;
RDebug *dbg = user;
RVector *vmem = (RVector *)value;
r_vector_upper_bound (vmem, dbg->session->cnum, index, CMP_CNUM_MEM);
if (index > 0 && index <= vmem->len) {
RDebugChangeMem *mem = r_vector_index_ptr (vmem, index - 1);
if (mem->cnum > dbg->session->cur_chkpt->cnum) {
dbg->iob.write_at (dbg->iob.io, key, &mem->data, 1);
}
}
return true;
}
R_API bool r_debug_session_set_idx(RDebug *dbg, int idx) {
RDebugSession *session;
static void _restore_memory(RDebug *dbg, ut32 cnum) {
_set_initial_memory (dbg);
ht_up_foreach (dbg->session->memory, _restore_memory_cb, dbg);
}
static RDebugCheckpoint *_get_checkpoint_before(RDebugSession *session, ut32 cnum) {
RDebugCheckpoint *checkpoint = NULL;
size_t index;
r_vector_upper_bound (session->checkpoints, cnum, index, CMP_CNUM_CHKPT);
if (index > 0 && index <= session->checkpoints->len) {
checkpoint = r_vector_index_ptr (session->checkpoints, index - 1);
}
return checkpoint;
}
R_API void r_debug_session_restore_reg_mem(RDebug *dbg, ut32 cnum) {
// Set checkpoint for initial registers and memory
dbg->session->cur_chkpt = _get_checkpoint_before (dbg->session, cnum);
// Restore registers
_restore_registers (dbg, cnum);
r_debug_reg_sync (dbg, R_REG_TYPE_ALL, true);
// Restore memory
_restore_memory (dbg, cnum);
}
R_API void r_debug_session_list_memory(RDebug *dbg) {
RListIter *iter;
ut32 count = 0;
RDebugMap *map;
r_debug_map_sync (dbg);
r_list_foreach (dbg->maps, iter, map) {
if ((map->perm & R_PERM_RW) == R_PERM_RW) {
RDebugSnap *snap = r_debug_snap_map (dbg, map);
if (!snap) {
return;
}
if (!dbg || idx < 0) {
return false;
}
r_list_foreach (dbg->sessions, iter, session) {
if (session->key.id == idx) {
r_debug_session_set (dbg, session);
return true;
ut8 *hash = r_debug_snap_get_hash (snap);
if (!hash) {
r_debug_snap_free (snap);
return;
}
char *hexstr = r_hex_bin2strdup (hash, R_HASH_SIZE_SHA256);
if (!hexstr) {
free (hash);
r_debug_snap_free (snap);
return;
}
dbg->cb_printf ("%s: %s\n", snap->name, hexstr);
free (hexstr);
free (hash);
r_debug_snap_free (snap);
}
count++;
}
return false;
}
/* Get most recent used session at the time */
R_API RDebugSession *r_debug_session_get(RDebug *dbg, RListIter *tail) {
RDebugSession *session;
if (!tail) {
return NULL;
R_API bool r_debug_session_add_reg_change(RDebugSession *session, int arena, ut64 offset, ut64 data) {
RVector *vreg = ht_up_find (session->registers, offset | (arena << 16), NULL);
if (!vreg) {
vreg = r_vector_new (sizeof (RDebugChangeReg), NULL, NULL);
if (!vreg) {
eprintf ("Error: creating a register vector.\n");
return false;
}
ht_up_insert (session->registers, offset | (arena << 16), vreg);
}
session = (RDebugSession *) tail->data;
return session;
RDebugChangeReg reg = { session->cnum, data };
r_vector_push (vreg, &reg);
return true;
}
/* XXX: bit ugly... :( )*/
static ut32 r_snap_to_idx(RDebug *dbg, RDebugSnap *snap) {
RListIter *iter;
RDebugSnap *s;
ut32 base_idx = 0;
r_list_foreach (dbg->snaps, iter, s) {
if (snap == s) {
break;
R_API bool r_debug_session_add_mem_change(RDebugSession *session, ut64 addr, ut8 data) {
RVector *vmem = ht_up_find (session->memory, addr, NULL);
if (!vmem) {
vmem = r_vector_new (sizeof (RDebugChangeMem), NULL, NULL);
if (!vmem) {
eprintf ("Error: creating a memory vector.\n");
return false;
}
base_idx++;
ht_up_insert (session->memory, addr, vmem);
}
return base_idx;
}
static RDebugSnap *r_idx_to_snap(RDebug *dbg, ut32 idx) {
RListIter *iter;
RDebugSnap *s;
ut32 base_idx = 0;
r_list_foreach (dbg->snaps, iter, s) {
if (base_idx == idx) {
return s;
}
base_idx++;
}
return NULL;
}
R_API void r_debug_session_path(RDebug *dbg, const char *path) {
R_FREE (dbg->snap_path);
dbg->snap_path = r_file_abspath (path);
}
R_API void r_debug_session_save(RDebug *dbg, const char *file) {
RListIter *iter, *iter2, *iter3;
RDebugSession *session;
RDebugSnap *base;
RDebugSnapDiff *snapdiff;
RPageData *page;
RSessionHeader header;
RDiffEntry diffentry;
RSnapEntry snapentry;
ut32 i;
const char *path = dbg->snap_path;
if (!r_file_is_directory (path)) {
eprintf ("%s is not correct path\n", path);
return;
}
char *base_file = r_str_newf ("%s/%s.dump", path, file);
char *diff_file = r_str_newf ("%s/%s.session", path, file);
if (!base_file) {
free (diff_file);
return;
}
if (!diff_file) {
free (base_file);
return;
}
/* dump all base snapshots */
r_list_foreach (dbg->snaps, iter, base) {
snapentry.addr = base->addr;
snapentry.size = base->size;
snapentry.timestamp = base->timestamp;
snapentry.perm = base->perm;
r_file_dump (base_file, (const ut8 *) &snapentry, sizeof (RSnapEntry), 1);
r_file_dump (base_file, (const ut8 *) base->data, base->size, 1);
/* dump all hashes */
for (i = 0; i < base->page_num; i++) {
r_file_dump (base_file, (const ut8 *) base->hashes[i], 128, 1);
}
}
/* dump all sessions */
r_list_foreach (dbg->sessions, iter, session) {
/* dump session header */
header.id = session->key.id;
header.addr = session->key.addr;
header.difflist_len = r_list_length (session->memlist);
r_file_dump (diff_file, (ut8 *) &header, sizeof (RSessionHeader), 1);
/* dump registers */
r_debug_reg_sync (dbg, R_REG_TYPE_ALL, 0);
for (i = 0; i < R_REG_TYPE_LAST; i++) {
RRegArena *arena = session->reg[i]->data;
r_file_dump (diff_file, (const ut8 *) &arena->size, sizeof (int), 1);
r_file_dump (diff_file, (const ut8 *) arena->bytes, arena->size, 1);
// eprintf ("arena[%d] size=%d\n", i, arena->size);
}
if (!header.difflist_len) {
continue;
}
// eprintf ("#### Session ####\n");
// eprintf ("Saved all registers off=0x%"PFMT64x"\n", curp);
/* Dump all diff entries */
r_list_foreach (session->memlist, iter2, snapdiff) {
/* Dump diff header */
diffentry.pages_len = r_list_length (snapdiff->pages);
diffentry.base_idx = r_snap_to_idx (dbg, snapdiff->base);
r_file_dump (diff_file, (const ut8 *) &diffentry, sizeof (RDiffEntry), 1);
/* Dump page entries */
r_list_foreach (snapdiff->pages, iter3, page) {
r_file_dump (diff_file, (const ut8 *) &page->page_off, sizeof (ut32), 1);
r_file_dump (diff_file, (const ut8 *) page->data, SNAP_PAGE_SIZE, 1);
r_file_dump (diff_file, (const ut8 *) page->hash, 128, 1);
}
}
}
eprintf ("Session saved in %s and dump in %s\n", diff_file, base_file);
free (base_file);
free (diff_file);
}
R_API void r_debug_session_restore(RDebug *dbg, const char *file) {
RDebugSnap *base = NULL;
RDebugSnapDiff *snapdiff;
RPageData *page;
RSessionHeader header;
RDiffEntry diffentry;
RSnapEntry snapentry;
ut32 i;
RReg *reg = dbg->reg;
const char *path = dbg->snap_path;
if (!r_file_is_directory (path)) {
eprintf ("%s is not correct path\n", path);
return;
}
char *base_file = r_str_newf ("%s/%s.dump", path, file);
char *diff_file = r_str_newf ("%s/%s.session", path, file);
if (!base_file || !diff_file) {
free (base_file);
free (diff_file);
return;
}
FILE *fd = r_sandbox_fopen (base_file, "rb");
if (!fd) {
free (base_file);
free (diff_file);
return;
}
/* Clear current sessions to be replaced */
r_list_purge (dbg->snaps);
/* Restore base snapshots */
while (true) {
base = r_debug_snap_new ();
memset (&snapentry, 0, sizeof (RSnapEntry));
if (fread (&snapentry, sizeof (RSnapEntry), 1, fd) != 1) {
break;
}
base->addr = snapentry.addr;
base->size = snapentry.size;
base->addr_end = base->addr + base->size;
base->page_num = base->size / SNAP_PAGE_SIZE;
base->timestamp = snapentry.timestamp;
base->perm = snapentry.perm;
base->data = calloc (base->size, 1);
if (!base->data) {
R_FREE (base);
break;
}
if (fread (base->data, base->size, 1, fd) != 1) {
free (base->data);
R_FREE (base);
break;
}
/* restore all hashes */
base->hashes = R_NEWS0 (ut8 *, base->page_num);
for (i = 0; i < base->page_num; i++) {
base->hashes[i] = calloc (1, 128);
if (fread (base->hashes[i], 128, 1, fd) != 1) {
break;
}
}
r_list_append (dbg->snaps, base);
}
fclose (fd);
R_FREE (base_file);
/* Restore trace sessions */
fd = r_sandbox_fopen (diff_file, "rb");
R_FREE (diff_file);
if (!fd) {
if (base) {
free (base->data);
free (base);
}
return;
}
/* Clear current sessions to be replaced */
r_list_purge (dbg->sessions);
for (i = 0; i < R_REG_TYPE_LAST; i++) {
r_list_purge (reg->regset[i].pool);
}
while (true) {
/* Restore session header */
if (fread (&header, sizeof (RSessionHeader), 1, fd) != 1) {
break;
}
RDebugSession *session = R_NEW0 (RDebugSession);
if (!session) {
break;
}
session->memlist = r_list_newf ((RListFree)r_debug_diff_free);
session->key.id = header.id;
session->key.addr = header.addr;
r_list_append (dbg->sessions, session);
eprintf ("session: %d, 0x%"PFMT64x " diffs: %d\n", header.id, header.addr, header.difflist_len);
/* Restore registers */
for (i = 0; i < R_REG_TYPE_LAST; i++) {
/* Resotre RReagArena from raw dump */
int arena_size;
if (fread (&arena_size, sizeof (int), 1, fd) != 1) {
break;
}
if (arena_size < 1 || arena_size > 1024*1024) {
eprintf ("Invalid arena size?\n");
break;
}
ut8 *arena_raw = calloc (arena_size, 1);
if (!arena_raw) {
break;
}
if (fread (arena_raw, arena_size, 1, fd) != 1) {
free (arena_raw);
break;
}
RRegArena *arena = R_NEW0 (RRegArena);
if (!arena) {
free (arena_raw);
break;
}
arena->bytes = arena_raw;
arena->size = arena_size;
/* Push RRegArena to regset.pool */
r_list_push (reg->regset[i].pool, arena);
reg->regset[i].arena = arena;
reg->regset[i].cur = reg->regset[i].pool->tail;
}
if (!header.difflist_len) {
continue;
}
/* Restore diff entries */
for (i = 0; i < header.difflist_len; i++) {
if (fread (&diffentry, sizeof (RDiffEntry), 1, fd) != 1) {
break;
}
// eprintf ("diffentry base=%d pages=%d\n", diffentry.base_idx, diffentry.pages_len);
snapdiff = R_NEW0 (RDebugSnapDiff);
if (!snapdiff) {
break;
}
/* Restore diff->base */
base = r_idx_to_snap (dbg, diffentry.base_idx);
snapdiff->base = base;
snapdiff->pages = r_list_newf ((RListFree)r_page_data_free);
snapdiff->last_changes = R_NEWS0 (RPageData *, base->page_num);
if (r_list_length (base->history)) {
/* Inherit last changes from previous SnapDiff */
RDebugSnapDiff *prev_diff = (RDebugSnapDiff *) r_list_tail (base->history)->data;
memcpy (snapdiff->last_changes, prev_diff->last_changes, sizeof (RPageData *) * base->page_num);
}
/* Restore pages */
ut32 p;
ut32 clust_page = R_MIN (SNAP_PAGE_SIZE, base->size);
for (p = 0; p < diffentry.pages_len; p++) {
page = R_NEW0 (RPageData);
if (!page) {
break;
}
page->data = calloc (1, clust_page);
if (!page->data) {
free (page);
break;
}
if (1 != fread (&page->page_off, sizeof (ut32), 1, fd)
|| 1 != fread (page->data, SNAP_PAGE_SIZE, 1, fd)
|| 1 != fread (page->hash, 128, 1, fd)) {
break;
}
snapdiff->last_changes[page->page_off] = page;
r_list_append (snapdiff->pages, page);
}
r_list_append (base->history, snapdiff);
r_list_append (session->memlist, snapdiff);
}
}
/* After restoring all sessions, now sync register */
r_debug_reg_sync (dbg, R_REG_TYPE_ALL, 1);
fclose (fd);
// #endif
RDebugChangeMem mem = { session->cnum, data };
r_vector_push (vmem, &mem);
return true;
}

View File

@ -111,19 +111,25 @@ int linux_handle_signals (RDebug *dbg, int tid) {
}
b->data = r_str_appendf (b->data, ";ps@r:%s", name);
dbg->reason.type = R_DEBUG_REASON_NEW_LIB;
break;
} else if (r_str_startswith (p, "dbg.unlibs")) {
dbg->reason.type = R_DEBUG_REASON_EXIT_LIB;
break;
}
}
}
}
if (dbg->reason.type != R_DEBUG_REASON_NEW_LIB &&
dbg->reason.type != R_DEBUG_REASON_EXIT_LIB) {
dbg->reason.bp_addr = (ut64)(size_t)siginfo.si_addr;
dbg->reason.type = R_DEBUG_REASON_BREAKPOINT;
// Switch to the thread that hit the breakpoint
r_debug_select (dbg, dbg->pid, tid);
dbg->tid = tid;
if (siginfo.si_code == TRAP_TRACE) {
dbg->reason.type = R_DEBUG_REASON_STEP;
} else {
dbg->reason.bp_addr = (ut64)(size_t)siginfo.si_addr;
dbg->reason.type = R_DEBUG_REASON_BREAKPOINT;
// Switch to the thread that hit the breakpoint
r_debug_select (dbg, dbg->pid, tid);
dbg->tid = tid;
}
}
}
break;
@ -269,8 +275,7 @@ RDebugReasonType linux_ptrace_event (RDebug *dbg, int pid, int status) {
int linux_step(RDebug *dbg) {
int ret = false;
int pid = dbg->tid;
ut64 addr = r_debug_reg_get (dbg, "PC");
ret = r_debug_ptrace (dbg, PTRACE_SINGLESTEP, pid, (void*)(size_t)addr, 0);
ret = r_debug_ptrace (dbg, PTRACE_SINGLESTEP, pid, 0, 0);
//XXX(jjd): why?? //linux_handle_signals (dbg);
if (ret == -1) {
perror ("native-singlestep");
@ -366,9 +371,9 @@ bool linux_attach_new_process(RDebug *dbg, int pid) {
}
static void linux_dbg_wait_break(RDebug *dbg) {
// Stop the currently debugged thread
if (!linux_kill_thread (dbg->tid, SIGSTOP)) {
eprintf ("Could not stop pid (%d)\n", dbg->pid);
// Interrupt the currently debugged thread
if (!linux_kill_thread (dbg->tid, SIGINT)) {
eprintf ("Could not interrupt pid (%d)\n", dbg->pid);
return;
}
}
@ -383,11 +388,8 @@ RDebugReasonType linux_dbg_wait(RDebug *dbg, int my_pid) {
flags |= WNOHANG;
}
// Ignore keyboard interrupt while waiting to avoid signaling the child process twice on
// break(SIGSTOP is sent by wait_break) which forced the user to continue an extra time
// to handle SIGINT
r_sys_signal (SIGINT, SIG_IGN);
r_cons_break_push ((RConsBreak)linux_dbg_wait_break, dbg);
// Send SIGINT to the target thread when interrupted
r_cons_break_push ((RConsBreakCallback) linux_dbg_wait_break, dbg);
repeat:
for (;;) {
if (r_cons_is_breaked ()) {
@ -473,7 +475,6 @@ repeat:
}
}
r_cons_break_pop ();
r_sys_signal (SIGINT, SIG_DFL);
dbg->reason.tid = pid;
return reason;
}
@ -1172,7 +1173,11 @@ int linux_reg_write (RDebug *dbg, int type, const ut8 *buf, int size) {
size = sizeof (R_DEBUG_REG_T);
}
#endif
return (ret != 0) ? false : true;
if (ret == -1) {
r_sys_perror ("reg_write");
return false;
}
return true;
}
if (type == R_REG_TYPE_FPU) {
#if __i386__ || __x86_64__

View File

@ -108,6 +108,13 @@ typedef ut64 mips64_regs_t [274];
#endif
#endif
// SIGTRAP si_codes from <asm/siginfo.h>
#define TRAP_BRKPT 1
#define TRAP_TRACE 2
#define TRAP_BRANCH 3
#define TRAP_HWBKPT 4
#define TRAP_UNK 5
//API
bool linux_set_options(RDebug *dbg, int pid);
int linux_step(RDebug *dbg);

View File

@ -96,12 +96,12 @@ return strdup (
"gpr orax .64 120 0\n"
"seg@gpr ss .64 160 0\n"
"seg@gpr fs_base .64 168 0\n"
"seg@gpr gs_base .64 176 0\n"
"seg@gpr fs .64 168 0\n"
"seg@gpr gs .64 176 0\n"
"seg@gpr ds .64 184 0\n"
"seg@gpr es .64 192 0\n"
"seg@gpr fs .64 200 0\n"
"seg@gpr gs .64 208 0\n"
"seg@gpr fs_base .64 200 0\n"
"seg@gpr gs_base .64 208 0\n"
"drx dr0 .64 0 0\n"
"drx dr1 .64 8 0\n"
"drx dr2 .64 16 0\n"

View File

@ -1,407 +1,98 @@
/* radare - LGPL - Copyright 2015-2020 - pancake, rkx1209 */
#include <r_debug.h>
#include <r_hash.h>
R_API RDebugSnap *r_debug_snap_new(void) {
RDebugSnap *snap = R_NEW0 (RDebugSnap);
ut64 algobit = r_hash_name_to_bits ("sha256");
if (!snap) {
return NULL;
}
snap->history = r_list_newf (r_debug_diff_free);
snap->hash_ctx = r_hash_new (true, algobit);
return snap;
}
R_API void r_debug_snap_free(void *p) {
RDebugSnap *snap = (RDebugSnap *) p;
r_list_free (snap->history);
free (snap->data);
free (snap->comment);
free (snap->hashes);
free (snap);
}
R_API int r_debug_snap_delete(RDebug *dbg, int idx) {
ut32 count = 0;
RListIter *iter;
if (idx == -1) {
r_list_free (dbg->snaps);
dbg->snaps = r_list_newf (r_debug_snap_free);
return 1;
}
r_list_foreach_iter (dbg->snaps, iter) {
if (idx != -1) {
if (idx != count) {
continue;
}
}
r_list_delete (dbg->snaps, iter);
count++;
break;
}
return 1;
}
R_API void r_debug_snap_list(RDebug *dbg, int idx, int mode) {
const char *comment, *comma;
ut32 count = 0;
RListIter *iter;
RDebugSnap *snap;
if (mode == 'j') {
dbg->cb_printf ("[");
}
r_list_foreach (dbg->snaps, iter, snap) {
comment = "";
comma = (iter->n)? ",": "";
if (idx != -1) {
if (idx != count) {
continue;
}
}
if (snap->comment && *snap->comment) {
comment = snap->comment;
}
switch (mode) {
case 'j':
dbg->cb_printf ("{\"count\":%d,\"addr\":%"PFMT64d ",\"size\":%d,\"history\":%d,\"comment\":\"%s\"}%s",
count, snap->addr, snap->size, r_list_length (snap->history), comment, comma);
break;
case '*':
dbg->cb_printf ("dms 0x%08"PFMT64x "\n", snap->addr);
break;
default:
dbg->cb_printf ("%d 0x%08"PFMT64x " - 0x%08"PFMT64x " history: %d size: %d -- %s\n",
count, snap->addr, snap->addr_end, r_list_length (snap->history), snap->size, comment);
}
count++;
}
if (mode == 'j') {
dbg->cb_printf ("]\n");
R_API void r_debug_snap_free(RDebugSnap *snap) {
if (snap) {
free (snap->name);
free (snap->data);
R_FREE (snap);
}
}
static RDebugSnap *r_debug_snap_get_map(RDebug *dbg, RDebugMap *map) {
RListIter *iter;
RDebugSnap *snap;
if (dbg && map) {
r_list_foreach (dbg->snaps, iter, snap) {
if (snap->addr <= map->addr && map->addr_end <= snap->addr_end) {
return snap;
}
}
}
return NULL;
}
R_API RDebugSnap *r_debug_snap_get(RDebug *dbg, ut64 addr) {
RDebugMap *map = r_debug_map_get (dbg, addr);
return r_debug_snap_get_map (dbg, map);
}
static void r_page_data_set(RDebug *dbg, RPageData *page) {
RDebugSnapDiff *diff = page->diff;
ut64 addr = diff->base->addr + page->page_off * SNAP_PAGE_SIZE;
dbg->iob.write_at (dbg->iob.io, addr, page->data, SNAP_PAGE_SIZE);
}
/* snap->history must have at least one entry */
R_API void r_debug_diff_set(RDebug *dbg, RDebugSnapDiff *diff) {
RPageData *prev_page, *last_page;
RDebugSnap *snap = diff->base;
RDebugMap *cur_map = r_debug_map_get (dbg, snap->addr + 1);
RDebugSnapDiff *latest;
ut64 addr;
ut32 page_off;
/* Save current snapshot. It is marked as a finish point of reverse execution */
latest = r_debug_snap_map (dbg, cur_map);
if (!latest) {
return;
}
//eprintf ("Apply diff [0x%08"PFMT64x ", 0x%08"PFMT64x "]\n", snap->addr, snap->addr_end);
/* Roll back page datas that's been changed **after** specified SnapDiff 'diff' */
for (addr = snap->addr; addr < snap->addr_end; addr += SNAP_PAGE_SIZE) {
page_off = (addr - snap->addr) / SNAP_PAGE_SIZE;
prev_page = diff->last_changes[page_off];
/* Roll back only latest page, that's been changed after prev_page */
if ((last_page = latest->last_changes[page_off]) && !prev_page) {
ut64 off = (ut64) last_page->page_off * SNAP_PAGE_SIZE;
/* Copy a page data of base snap to current addr. (i.e. roll back) */
dbg->iob.write_at (dbg->iob.io, addr, snap->data + off, SNAP_PAGE_SIZE);
//eprintf ("Roll back 0x%08"PFMT64x "(page: %d)\n", addr, page_off);
}
}
/* Set all previous history (including specified SnapDiff 'diff')*/
for (addr = snap->addr; addr < snap->addr_end; addr += SNAP_PAGE_SIZE) {
page_off = (addr - snap->addr) / SNAP_PAGE_SIZE;
if ((prev_page = diff->last_changes[page_off])) {
r_page_data_set (dbg, prev_page);
//eprintf ("Update 0x%08"PFMT64x "(page: %d)\n", addr, page_off);
}
}
r_list_pop (snap->history);
r_debug_diff_free (latest);
}
/* Roll back to base snapshot */
R_API void r_debug_diff_set_base(RDebug *dbg, RDebugSnap *base) {
RPageData *last_page;
RDebugMap *cur_map = r_debug_map_get (dbg, base->addr + 1);
RDebugSnapDiff *latest;
ut64 addr;
ut32 page_off;
/* Save current snapshot. It is marked as a finish point of reverse execution */
latest = r_debug_snap_map (dbg, cur_map);
if (!latest) {
return;
}
//eprintf ("Roll back to base [0x%08"PFMT64x ", 0x%08"PFMT64x "]\n", cur_map->addr, cur_map->addr_end);
for (addr = base->addr; addr < base->addr_end; addr += SNAP_PAGE_SIZE) {
page_off = (addr - base->addr) / SNAP_PAGE_SIZE;
if ((last_page = latest->last_changes[page_off])) {
ut64 off = (ut64) last_page->page_off * SNAP_PAGE_SIZE;
/* Copy a page data of base snap to current addr. (i.e. roll back) */
dbg->iob.write_at (dbg->iob.io, addr, base->data + off, SNAP_PAGE_SIZE);
//eprintf ("Roll back 0x%08"PFMT64x "(page: %d)\n", addr, page_off);
}
}
r_list_pop (base->history);
r_debug_diff_free (latest);
}
// XXX: snap_set will be duplicated soon
R_API int r_debug_snap_set(RDebug *dbg, RDebugSnap *snap) {
return 1;
}
R_API int r_debug_snap_set_idx(RDebug *dbg, int idx) {
RDebugSnap *snap;
RListIter *iter;
ut32 count = 0;
if (!dbg || idx < 0) {
return 0;
}
r_list_foreach (dbg->snaps, iter, snap) {
if (count == idx) {
r_debug_snap_set (dbg, snap);
break;
}
count++;
}
return 1;
}
/* XXX: Just for debugging. Duplicate soon */
#if 0
static void print_hash(ut8 *hash, int digest_size) {
int i = 0;
for (i = 0; i < digest_size; i++) {
eprintf ("%02"PFMT32x, hash[i]);
}
eprintf ("\n");
}
#endif
R_API RDebugSnapDiff *r_debug_snap_map(RDebug *dbg, RDebugMap *map) {
if (!dbg || !map || map->size < 1) {
R_API RDebugSnap *r_debug_snap_map(RDebug *dbg, RDebugMap *map) {
r_return_val_if_fail (dbg && map, NULL);
if (map->size < 1) {
eprintf ("Invalid map size\n");
return NULL;
}
ut8 *hash;
ut64 addr;
ut64 algobit = r_hash_name_to_bits ("sha256");
ut32 page_num = map->size / SNAP_PAGE_SIZE;
ut32 digest_size;
/* Get an existing snapshot entry */
RDebugSnap *snap = r_debug_snap_get_map (dbg, map);
RDebugSnap *snap = R_NEW0 (RDebugSnap);
if (!snap) {
/* Create a new one */
if (!(snap = r_debug_snap_new ())) {
return NULL;
}
snap->timestamp = sdb_now ();
snap->addr = map->addr;
snap->addr_end = map->addr_end;
snap->size = map->size;
snap->page_num = page_num;
snap->data = malloc (map->size);
snap->perm = map->perm;
if (!snap->data) {
goto error;
}
snap->hashes = R_NEWS0 (ut8 *, page_num);
if (!snap->hashes) {
free (snap->data);
goto error;
}
eprintf ("Reading %d byte(s) from 0x%08"PFMT64x "...\n", snap->size, snap->addr);
dbg->iob.read_at (dbg->iob.io, snap->addr, snap->data, snap->size);
ut32 clust_page = R_MIN (SNAP_PAGE_SIZE, snap->size);
/* Calculate all hashes of pages */
for (addr = snap->addr; addr < snap->addr_end; addr += SNAP_PAGE_SIZE) {
ut32 page_off = (addr - snap->addr) / SNAP_PAGE_SIZE;
digest_size = r_hash_calculate (snap->hash_ctx, algobit, &snap->data[addr - snap->addr], clust_page);
hash = calloc (128, 1); // Fix hash size to 128 byte
memcpy (hash, snap->hash_ctx->digest, digest_size);
snap->hashes[page_off] = hash;
// eprintf ("0x%08"PFMT64x"(page: %d)...\n",addr, page_off);
// print_hash (hash, digest_size);
}
r_list_append (dbg->snaps, snap);
goto okay;
} else {
/* A base snapshot have already been saved. *
So we only need to save different parts. */
return r_debug_diff_add (dbg, snap);
}
error:
free (snap);
okay:
return NULL;
}
R_API int r_debug_snap_all(RDebug *dbg, int perms) {
RDebugMap *map;
RListIter *iter;
r_debug_map_sync (dbg);
r_list_foreach (dbg->maps, iter, map) {
if (!perms || (map->perm & perms) == perms) {
r_debug_snap_map (dbg, map);
}
}
return 0;
}
R_API int r_debug_snap(RDebug *dbg, ut64 addr) {
RDebugMap *map = r_debug_map_get (dbg, addr);
if (!map) {
eprintf ("Cannot find map at 0x%08"PFMT64x "\n", addr);
return 0;
}
if (!r_debug_snap_map (dbg, map)) {
return 0;
}
return 1;
}
R_API int r_debug_snap_comment(RDebug *dbg, int idx, const char *msg) {
RDebugSnap *snap;
RListIter *iter;
ut32 count = 0;
if (!dbg || idx < 0 || !msg || !*msg) {
return 0;
}
r_list_foreach (dbg->snaps, iter, snap) {
if (count == idx) {
free (snap->comment);
snap->comment = strdup (r_str_trim_head_ro (msg));
break;
}
count++;
}
return 1;
}
R_API void r_page_data_free(void *p) {
RPageData *page = (RPageData *) p;
free (page->data);
free (page);
}
R_API void r_debug_diff_free(void *p) {
RDebugSnapDiff *diff = (RDebugSnapDiff *) p;
r_list_free (diff->pages);
free (diff->last_changes);
free (diff);
}
R_API RDebugSnapDiff *r_debug_diff_add(RDebug *dbg, RDebugSnap *base) {
RDebugSnapDiff *prev_diff = NULL, *new_diff;
RPageData *new_page, *last_page;
ut64 addr;
int digest_size;
ut32 page_off;
ut64 algobit = r_hash_name_to_bits ("sha256");
ut32 clust_page = R_MIN (SNAP_PAGE_SIZE, base->size);
new_diff = R_NEW0 (RDebugSnapDiff);
if (!new_diff) {
goto error;
}
new_diff->base = base;
new_diff->pages = r_list_newf (r_page_data_free);
new_diff->last_changes = R_NEWS0 (RPageData *, base->page_num);
if (r_list_length (base->history)) {
/* Inherit last changes from previous SnapDiff */
RListIter *tail = r_list_tail (base->history);
if (tail) {
prev_diff = (RDebugSnapDiff *) tail->data;
memcpy (new_diff->last_changes, prev_diff->last_changes, sizeof (RPageData *) * base->page_num);
}
}
/* Compare hash of pages. */
for (addr = base->addr; addr < base->addr_end; addr += SNAP_PAGE_SIZE) {
ut8 *prev_hash, *cur_hash;
ut8 *buf = malloc (clust_page);
/* Copy current memory value to buf and calculate cur_hash from it. */
dbg->iob.read_at (dbg->iob.io, addr, buf, clust_page);
digest_size = r_hash_calculate (base->hash_ctx, algobit, buf, clust_page);
cur_hash = base->hash_ctx->digest;
page_off = (addr - base->addr) / SNAP_PAGE_SIZE;
/* Check If there is any last change for this page. */
if (prev_diff && (last_page = prev_diff->last_changes[page_off])) {
/* Use hash of last SnapDiff */
// eprintf ("use hash of diff\n");
prev_hash = last_page->hash;
} else {
/* Use hash of base snapshot */
// eprintf ("use hash of base\n");
prev_hash = base->hashes[page_off];
}
/* Memory has been changed. So add new diff entry for this addr */
if (memcmp (cur_hash, prev_hash, digest_size) != 0) {
// print_hash (cur_hash, digest_size);
// print_hash (prev_hash, digest_size);
/* Create new page diff entry, save one page and calculate hash. */
new_page = R_NEW0 (RPageData);
new_page->diff = new_diff;
new_page->page_off = page_off;
new_page->data = buf;
memcpy (new_page->hash, cur_hash, digest_size);
new_diff->last_changes[page_off] = new_page; // Update last change to new page
r_list_append (new_diff->pages, new_page);
}
}
if (r_list_length (new_diff->pages)) {
#if 0
RPageData *page;
RListIter *iter;
eprintf ("saved: 0x%08"PFMT64x "(page: ", base->addr);
r_list_foreach (new_diff->pages, iter, page) {
eprintf ("%d ", page->page_off);
}
eprintf (")\n");
#endif
r_list_append (base->history, new_diff);
return new_diff;
} else {
r_debug_diff_free (new_diff);
return NULL;
}
error:
free (new_diff);
return NULL;
snap->name = strdup (map->name);
snap->addr = map->addr;
snap->addr_end = map->addr_end;
snap->size = map->size;
snap->perm = map->perm;
snap->user = map->user;
snap->shared = map->shared;
snap->data = malloc (map->size);
if (!snap->data) {
r_debug_snap_free (snap);
return NULL;
}
eprintf ("Reading %d byte(s) from 0x%08"PFMT64x "...\n", snap->size, snap->addr);
dbg->iob.read_at (dbg->iob.io, snap->addr, snap->data, snap->size);
return snap;
}
R_API bool r_debug_snap_contains(RDebugSnap *snap, ut64 addr) {
return (snap->addr <= addr && addr >= snap->addr_end);
}
R_API ut8 *r_debug_snap_get_hash(RDebugSnap *snap) {
ut64 algobit = r_hash_name_to_bits ("sha256");
RHash *ctx = r_hash_new (true, algobit);
if (!ctx) {
return NULL;
}
r_hash_do_begin (ctx, algobit);
r_hash_calculate (ctx, algobit, snap->data, snap->size);
r_hash_do_end (ctx, algobit);
ut8 *ret = malloc (R_HASH_SIZE_SHA256);
if (!ret) {
r_hash_free (ctx);
return NULL;
}
memcpy (ret, ctx->digest, R_HASH_SIZE_SHA256);
r_hash_free (ctx);
return ret;
}
R_API bool r_debug_snap_is_equal(RDebugSnap *a, RDebugSnap *b) {
bool ret = false;
ut64 algobit = r_hash_name_to_bits ("sha256");
RHash *ctx = r_hash_new (true, algobit);
if (!ctx) {
return ret;
}
r_hash_do_begin (ctx, algobit);
r_hash_calculate (ctx, algobit, a->data, a->size);
r_hash_do_end (ctx, algobit);
ut8 *temp = malloc (R_HASH_SIZE_SHA256);
if (!temp) {
r_hash_free (ctx);
return ret;
}
memcpy (temp, ctx->digest, R_HASH_SIZE_SHA256);
r_hash_do_begin (ctx, algobit);
r_hash_calculate (ctx, algobit, b->data, b->size);
r_hash_do_end (ctx, algobit);
ret = memcmp (temp, ctx->digest, R_HASH_SIZE_SHA256) == 0;
free (temp);
r_hash_free (ctx);
return ret;
}

View File

@ -43,6 +43,114 @@ R_API int r_debug_trace_tag (RDebug *dbg, int tag) {
return (dbg->trace->tag = (tag>0)? tag: UT32_MAX);
}
R_API bool r_debug_trace_ins_before(RDebug *dbg) {
RListIter *it, *it_tmp;
RAnalValue *val;
ut8 buf_pc[32];
// Analyze current instruction
ut64 pc = r_debug_reg_get (dbg, dbg->reg->name[R_REG_NAME_PC]);
if (!dbg->iob.read_at) {
return false;
}
if (!dbg->iob.read_at (dbg->iob.io, pc, buf_pc, sizeof (buf_pc))) {
return false;
}
dbg->cur_op = R_NEW0 (RAnalOp);
if (!dbg->cur_op) {
return false;
}
if (!r_anal_op (dbg->anal, dbg->cur_op, pc, buf_pc, sizeof (buf_pc), R_ANAL_OP_MASK_VAL)) {
r_anal_op_free (dbg->cur_op);
dbg->cur_op = NULL;
return false;
}
// resolve mem write address
r_list_foreach_safe (dbg->cur_op->access, it, it_tmp, val) {
switch (val->type) {
case R_ANAL_VAL_REG:
if (!(val->access & R_ANAL_ACC_W)) {
r_list_delete (dbg->cur_op->access, it);
}
break;
case R_ANAL_VAL_MEM:
if (val->memref > 32) {
eprintf ("Error: adding changes to %d bytes in memory.\n", val->memref);
r_list_delete (dbg->cur_op->access, it);
break;
}
if (val->access & R_ANAL_ACC_W) {
// resolve memory address
ut64 addr = 0;
addr += val->delta;
if (val->seg) {
addr += r_reg_get_value (dbg->reg, val->seg);
}
if (val->reg) {
addr += r_reg_get_value (dbg->reg, val->reg);
}
if (val->regdelta) {
int mul = val->mul ? val->mul : 1;
addr += mul * r_reg_get_value (dbg->reg, val->regdelta);
}
// resolve address into base for ins_after
val->base = addr;
} else {
r_list_delete (dbg->cur_op->access, it);
}
default:
break;
}
}
return true;
}
R_API bool r_debug_trace_ins_after(RDebug *dbg) {
RListIter *it;
RAnalValue *val;
// Add reg/mem write change
r_debug_reg_sync (dbg, R_REG_TYPE_ALL, false);
r_list_foreach (dbg->cur_op->access, it, val) {
if (!(val->access & R_ANAL_ACC_W)) {
continue;
}
switch (val->type) {
case R_ANAL_VAL_REG:
{
ut64 data = r_reg_get_value (dbg->reg, val->reg);
// add reg write
r_debug_session_add_reg_change (dbg->session, val->reg->arena, val->reg->offset, data);
break;
}
case R_ANAL_VAL_MEM:
{
ut8 buf[32] = { 0 };
if (!dbg->iob.read_at (dbg->iob.io, val->base, buf, val->memref)) {
eprintf ("Error reading memory at 0x%"PFMT64x"\n", val->base);
break;
}
// add mem write
size_t i;
for (i = 0; i < val->memref; i++) {
r_debug_session_add_mem_change (dbg->session, val->base + i, buf[i]);
}
break;
}
default:
break;
}
}
r_anal_op_free (dbg->cur_op);
dbg->cur_op = NULL;
return true;
}
/*
* something happened at the given pc that we need to trace
*/
@ -98,8 +206,6 @@ static int cmpaddr (const void *_a, const void *_b) {
(r_itv_begin (a->pitv) < r_itv_begin (b->pitv))? -1: 0;
}
R_API void r_debug_trace_list (RDebug *dbg, int mode, ut64 offset) {
int tag = dbg->trace->tag;
RListIter *iter;

View File

@ -799,16 +799,29 @@ R_DEPRECATE typedef struct r_anal_var_field_t {
bool field;
} RAnalVarField;
// mul*value+regbase+regidx+delta
typedef enum {
R_ANAL_ACC_R = (1 << 0),
R_ANAL_ACC_W = (1 << 1),
} RAnalValueAccess;
typedef enum {
R_ANAL_VAL_REG,
R_ANAL_VAL_MEM,
R_ANAL_VAL_IMM,
} RAnalValueType;
// base+reg+regdelta*mul+delta
typedef struct r_anal_value_t {
RAnalValueType type;
RAnalValueAccess access;
int absolute; // if true, unsigned cast is used
int memref; // is memory reference? which size? 1, 2 ,4, 8
ut64 base ; // numeric address
st64 delta; // numeric delta
st64 imm; // immediate value
int mul; // multiplier (reg*4+base)
ut16 sel; // segment selector
RRegItem *reg; // register index used (-1 if no reg)
RRegItem *seg; // segment selector register
RRegItem *reg; // register / register base used (-1 if no reg)
RRegItem *regdelta; // register index used (-1 if no reg)
} RAnalValue;
@ -860,6 +873,7 @@ typedef struct r_anal_op_t {
int refptr; /* if (0) ptr = "reference" else ptr = "load memory of refptr bytes" */
RAnalValue *src[3];
RAnalValue *dst;
RList *access; /* RAnalValue access information */
RStrBuf esil;
RStrBuf opex;
const char *reg; /* destination register */

View File

@ -161,47 +161,46 @@ typedef struct r_debug_desc_t {
ut64 off;
} RDebugDesc;
struct r_debug_snap_diff_t;
typedef struct r_page_data_t {
struct r_debug_snap_diff_t *diff; // Pointing SnapDiff that has this pagedata.
ut32 page_off;
ut8 *data;
ut8 hash[128];
} RPageData;
struct r_debug_snap_t;
typedef struct r_debug_snap_diff_t {
struct r_debug_snap_t *base;
RList *pages; // <RPageData*>
RPageData **last_changes; // Last diff entries of each pages
} RDebugSnapDiff;
typedef struct r_debug_snap_t {
char *name;
ut64 addr;
ut64 addr_end;
ut8 *data;
ut32 size;
ut32 page_num;
ut64 timestamp;
RHash *hash_ctx;
ut8 **hashes; // Hash of each pages
RList *history; // <RDebugSnapDiff*>
ut8 *data;
int perm;
char *comment;
int user;
bool shared;
} RDebugSnap;
typedef struct r_debug_key {
ut64 addr;
ut32 id;
} RDebugKey;
#define CMP_CNUM_REG(x, y) ((x) >= ((RDebugChangeReg *)y)->cnum ? 1 : -1)
#define CMP_CNUM_MEM(x, y) ((x) >= ((RDebugChangeMem *)y)->cnum ? 1 : -1)
#define CMP_CNUM_CHKPT(x, y) ((x) >= ((RDebugCheckpoint *)y)->cnum ? 1 : -1)
typedef struct {
int cnum;
ut64 data;
} RDebugChangeReg;
typedef struct {
int cnum;
ut8 data;
} RDebugChangeMem;
typedef struct r_debug_checkpoint_t {
int cnum;
RListIter *reg[R_REG_TYPE_LAST];
RList *snaps; // <RDebugSnap>
} RDebugCheckpoint;
typedef struct r_debug_session_t {
RDebugKey key;
RListIter *reg[R_REG_TYPE_LAST];
RList *memlist; // <RDebugSnapDiff*>
/* XXX: DebugSession should have base snapshot of memlist. */
//RDebugSnap *base;
char *comment;
ut32 cnum;
ut32 maxcnum;
RDebugCheckpoint *cur_chkpt;
RVector *checkpoints; /* RVector<RDebugCheckpoint> */
HtUP *memory; /* RVector<RDebugChangeMem> */
HtUP *registers; /* RVector<RDebugChangeReg> */
int /*RDebugReasonType*/ reasontype;
RBreakpointItem *bp;
} RDebugSession;
/* Session file format */
@ -312,8 +311,11 @@ typedef struct r_debug_t {
RAnal *anal;
RList *maps; // <RDebugMap>
RList *maps_user; // <RDebugMap>
RList *snaps; // <RDebugSnap>
RList *sessions; // <RDebugSession>
bool trace_continue;
RAnalOp *cur_op;
RDebugSession *session;
Sdb *sgnls;
RCoreBind corebind;
// internal use only
@ -502,7 +504,7 @@ R_API RDebugMap *r_debug_map_alloc(RDebug *dbg, ut64 addr, int size, bool thp);
R_API int r_debug_map_dealloc(RDebug *dbg, RDebugMap *map);
R_API RList *r_debug_map_list_new(void);
R_API RDebugMap *r_debug_map_get(RDebug *dbg, ut64 addr);
R_API RDebugMap *r_debug_map_new (char *name, ut64 addr, ut64 addr_end, int perm, int user);
R_API RDebugMap *r_debug_map_new(char *name, ut64 addr, ut64 addr_end, int perm, int user);
R_API void r_debug_map_free(RDebugMap *map);
R_API void r_debug_map_list(RDebug *dbg, ut64 addr, const char *input);
R_API void r_debug_map_list_visual(RDebug *dbg, ut64 addr, const char *input, int colors);
@ -577,41 +579,28 @@ R_API void r_debug_esil_watch_list(RDebug *dbg);
R_API int r_debug_esil_watch_empty(RDebug *dbg);
R_API void r_debug_esil_prestep (RDebug *d, int p);
/* snap */
R_API RDebugSnap *r_debug_snap_new(void);
R_API void r_debug_snap_free(void *snap);
R_API int r_debug_snap_delete(RDebug *dbg, int idx);
R_API void r_debug_snap_list(RDebug *dbg, int idx, int mode);
R_API int r_debug_snap(RDebug *dbg, ut64 addr);
R_API int r_debug_snap_comment(RDebug *dbg, int idx, const char *msg);
R_API RDebugSnapDiff *r_debug_snap_map(RDebug *dbg, RDebugMap *map);
R_API int r_debug_snap_all(RDebug *dbg, int perms);
R_API RDebugSnap *r_debug_snap_get(RDebug *dbg, ut64 addr);
R_API int r_debug_snap_set_idx(RDebug *dbg, int idx);
R_API int r_debug_snap_set(RDebug *dbg, RDebugSnap *snap);
/* record & replay */
// R_API ut8 r_debug_get_byte(RDebug *dbg, ut32 cnum, ut64 addr);
R_API bool r_debug_add_checkpoint(RDebug *dbg);
R_API bool r_debug_session_add_reg_change(RDebugSession *session, int arena, ut64 offset, ut64 data);
R_API bool r_debug_session_add_mem_change(RDebugSession *session, ut64 addr, ut8 data);
R_API void r_debug_session_restore_reg_mem(RDebug *dbg, ut32 cnum);
R_API void r_debug_session_list_memory(RDebug *dbg);
R_API bool r_debug_trace_ins_before(RDebug *dbg);
R_API bool r_debug_trace_ins_after(RDebug *dbg);
/* snap diff */
R_API void r_debug_diff_free(void *p);
R_API RDebugSnapDiff *r_debug_diff_add(RDebug *dbg, RDebugSnap *base);
R_API void r_debug_diff_set(RDebug *dbg, RDebugSnapDiff *diff);
R_API void r_debug_diff_set_base(RDebug *dbg, RDebugSnap *base);
R_API RDebugSession *r_debug_session_new(RDebug *dbg);
R_API void r_debug_session_free(RDebugSession *session);
/* page data */
R_API void r_page_data_free(void *p);
R_API RDebugSnap *r_debug_snap_map(RDebug *dbg, RDebugMap *map);
R_API bool r_debug_snap_contains(RDebugSnap *snap, ut64 addr);
R_API ut8 *r_debug_snap_get_hash(RDebugSnap *snap);
R_API bool r_debug_snap_is_equal(RDebugSnap *a, RDebugSnap *b);
R_API void r_debug_snap_free(RDebugSnap *snap);
/* debug session */
R_API void r_debug_session_free(void *p);
R_API void r_debug_session_list(RDebug *dbg);
R_API RDebugSession *r_debug_session_add(RDebug *dbg, RListIter **tail);
R_API bool r_debug_session_delete(RDebug *dbg, int idx);
R_API bool r_debug_session_comment(RDebug *dbg, int idx, const char *msg);
R_API void r_debug_session_path(RDebug *dbg, const char *path);
R_API void r_debug_session_set(RDebug *dbg, RDebugSession *session);
R_API bool r_debug_session_set_idx(RDebug *dbg, int idx);
R_API RDebugSession *r_debug_session_get(RDebug *dbg, RListIter *tail);
R_API void r_debug_session_save(RDebug *dbg, const char *file);
R_API void r_debug_session_restore(RDebug *dbg, const char *file);
R_API bool r_debug_step_back(RDebug *dbg);
R_API int r_debug_step_back(RDebug *dbg, int steps);
R_API bool r_debug_goto_cnum(RDebug *dbg, ut32 cnum);
R_API int r_debug_step_cnum(RDebug *dbg, int steps);
R_API bool r_debug_continue_back(RDebug *dbg);
/* ptrace */

View File

@ -132,6 +132,10 @@ R_API void *r_vector_shrink(RVector *vec);
if (!r_vector_empty (vec)) \
for (it = (void *)(vec)->a; (char *)it != (char *)(vec)->a + ((vec)->len * (vec)->elem_size); it = (void *)((char *)it + (vec)->elem_size))
#define r_vector_foreach_prev(vec, it) \
if (!r_vector_empty (vec)) \
for (it = (void *)((char *)(vec)->a + (((vec)->len - 1)* (vec)->elem_size)); (char *)it != (char *)(vec)->a; it = (void *)((char *)it - (vec)->elem_size))
#define r_vector_enumerate(vec, it, i) \
if (!r_vector_empty (vec)) \
for (it = (void *)(vec)->a, i = 0; i < (vec)->len; it = (void *)((char *)it + (vec)->elem_size), i++)
@ -158,6 +162,19 @@ R_API void *r_vector_shrink(RVector *vec);
} \
} while (0) \
#define r_vector_upper_bound(vec, x, i, cmp) \
do { \
size_t h = (vec)->len, m; \
for (i = 0; i < h; ) { \
m = i + ((h - i) >> 1); \
if ((cmp (x, ((char *)(vec)->a + (vec)->elem_size * m))) < 0) { \
h = m; \
} else { \
i = m + 1; \
} \
} \
} while (0) \
// RPVector
R_API void r_pvector_init(RPVector *vec, RPVectorFree free);

View File

@ -20,6 +20,7 @@
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
static void *th_run(ptrace_wrap_instance *inst);
@ -150,4 +151,4 @@ void *ptrace_wrap_func(ptrace_wrap_instance *inst, ptrace_wrap_func_func func, v
sem_post (&inst->request_sem);
sem_wait (&inst->result_sem);
return inst->func_result;
}
}

View File

@ -1,6 +1,6 @@
NAME=debug continue back from callee
FILE=bins/elf/analysis/calls_x64
ARGS=-d
ARGS=-d -e dbg.bpsysign=true
CMDS=<<EOF
db main
db 0x0040052f
@ -8,6 +8,7 @@ dc
dts+
dc
dcb
dcb
dr rip
dk 9
EOF
@ -36,7 +37,7 @@ RUN
NAME=dbg.manycontback
FILE=bins/elf/analysis/ls-linux-x86_64-zlul
ARGS=-d
ARGS=-d -e dbg.bpsysign=true
CMDS=<<EOF
db main
db 0x004028ca
@ -49,12 +50,16 @@ dc
dc
dc
dc
dsb
dcb
dr rip
dsb
dcb
dr rip
dsb
dcb
dr rip
dsb
dcb
dr rip
dk 9

View File

@ -229,12 +229,12 @@ dr3
dr2
dr1
dr0
gs
fs
es
ds
gs_base
fs_base
es
ds
gs
fs
ss
orax
of

View File

@ -1,6 +1,6 @@
NAME=dbg.stepback
FILE=bins/elf/analysis/ls-linux-x86_64-zlul
ARGS=-d
ARGS=-d -e dbg.bpsysign=true
CMDS=<<EOF
db main
db 0x004028fe
@ -8,10 +8,15 @@ dc
dts+
dc
dsb
dr rip
dsb
dr rbx,rcx,rdx,r12,rip
dk 9
EOF
EXPECT=<<EOF
0x00000001
0x00000001
0x00000001
0x00404870
0x004028f9
EOF
RUN
@ -19,26 +24,37 @@ RUN
NAME=debug stepback from callee
FILE=bins/elf/analysis/calls_x64
ARGS=-d
BROKEN=1
CMDS=<<EOF
db main
db 0x0040052f
dc
dts+
dc
dr rax,rbx,rcx,r10,rbp,rip
dsb
dr rip
dsb
dr rax,rbx,rcx,r10,rbp,rip
dk 9
EOF
EXPECT=<<EOF
0x00400574
0x00000000
0x00400590
0x00000003
0x00400590
0x0040052f
0x00400574
0x00000000
0x00400590
0x00000003
0x00400590
0x00400575
EOF
RUN
NAME=debug stepback from caller
FILE=bins/elf/analysis/calls_x64
ARGS=-d
BROKEN=1
ARGS=-d -e dbg.bpsysign=true
CMDS=<<EOF
db main
db 0x0040057c
@ -46,10 +62,10 @@ dc
dts+
dc
dsb
dsb
dr rip
EOF
EXPECT=<<EOF
0x0040053b
Hello world
EOF
RUN