mirror of
https://github.com/radareorg/radare2.git
synced 2024-11-27 07:00:30 +00:00
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:
parent
48c30dfd99
commit
2dfa75cc47
@ -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;
|
||||
|
@ -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], ®delta_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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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, ®);
|
||||
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;
|
||||
}
|
||||
|
@ -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__
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -229,12 +229,12 @@ dr3
|
||||
dr2
|
||||
dr1
|
||||
dr0
|
||||
gs
|
||||
fs
|
||||
es
|
||||
ds
|
||||
gs_base
|
||||
fs_base
|
||||
es
|
||||
ds
|
||||
gs
|
||||
fs
|
||||
ss
|
||||
orax
|
||||
of
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user