mirror of
https://github.com/radareorg/radare2.git
synced 2025-02-26 00:55:59 +00:00
429 lines
10 KiB
C
429 lines
10 KiB
C
/* radare - LGPL - Copyright 2009-2024 - pancake, defragger, madprogrammer */
|
|
|
|
#include <r_debug.h>
|
|
#include <libqnxr.h>
|
|
|
|
/* HACK_FOR_PLUGIN_LINKAGE */
|
|
R_API RDebugPid *__r_debug_pid_new(const char *path, int pid, char status, ut64 pc) {
|
|
RDebugPid *p = R_NEW0 (RDebugPid);
|
|
if (R_LIKELY (p)) {
|
|
p->path = strdup (path);
|
|
p->pid = pid;
|
|
p->status = status;
|
|
p->runnable = true;
|
|
p->pc = pc;
|
|
}
|
|
return p;
|
|
}
|
|
R_API void *__r_debug_pid_free(RDebugPid *pid) {
|
|
free (pid->path);
|
|
free (pid);
|
|
return NULL;
|
|
}
|
|
/* ------------------- */
|
|
|
|
typedef struct {
|
|
libqnxr_t desc;
|
|
} RIOQnx;
|
|
|
|
typedef struct plugin_data_t {
|
|
libqnxr_t *desc;
|
|
ut8 *reg_buf;
|
|
int buf_size;
|
|
} PluginData;
|
|
|
|
static void pidlist_cb(void *ctx, pid_t pid, char *name) {
|
|
RList *list = ctx;
|
|
r_list_append (list, __r_debug_pid_new (name, pid, 's', 0));
|
|
}
|
|
|
|
static bool r_debug_qnx_select(RDebug *dbg, int pid, int tid) {
|
|
PluginData *pd = R_UNWRAP3 (dbg, current, plugin_data);
|
|
return qnxr_select (pd->desc, pid, tid);
|
|
}
|
|
|
|
static RList *r_debug_qnx_tids(RDebug *dbg, int pid) {
|
|
eprintf ("%s: TODO: Threads\n", __func__);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static RList *r_debug_qnx_pids(RDebug *dbg, int pid) {
|
|
RList *list = r_list_new ();
|
|
if (!list) {
|
|
return NULL;
|
|
}
|
|
list->free = (RListFree)&__r_debug_pid_free;
|
|
|
|
PluginData *pd = R_UNWRAP3 (dbg, current, plugin_data);
|
|
/* TODO */
|
|
if (pd && pid) {
|
|
r_list_append (list, __r_debug_pid_new ("(current)", pid, 's', 0));
|
|
} else {
|
|
qnxr_pidlist (pd->desc, list, &pidlist_cb);
|
|
}
|
|
|
|
return list;
|
|
}
|
|
|
|
static bool r_debug_qnx_reg_read(RDebug *dbg, int type, ut8 *buf, int size) {
|
|
int copy_size;
|
|
int buflen = 0;
|
|
PluginData *pd = R_UNWRAP3 (dbg, current, plugin_data);
|
|
if (!pd || !pd->desc) {
|
|
return false;
|
|
}
|
|
int len = qnxr_read_registers (pd->desc);
|
|
if (len <= 0) {
|
|
return false;
|
|
}
|
|
// read the len of the current area
|
|
free (r_reg_get_bytes (dbg->reg, type, &buflen));
|
|
if (size < len) {
|
|
R_LOG_WARN ("qnx_reg_read got a small buffer %d vs %d", (int)size, (int)len);
|
|
}
|
|
copy_size = R_MIN (len, size);
|
|
buflen = R_MAX (len, buflen);
|
|
if (pd->reg_buf) {
|
|
if (pd->buf_size < copy_size) {
|
|
ut8 *new_buf = realloc (pd->reg_buf, copy_size);
|
|
if (!new_buf) {
|
|
return false;
|
|
}
|
|
pd->reg_buf = new_buf;
|
|
buflen = copy_size;
|
|
pd->buf_size = len;
|
|
}
|
|
} else {
|
|
pd->reg_buf = calloc (buflen, 1);
|
|
if (!pd->reg_buf) {
|
|
return false;
|
|
}
|
|
pd->buf_size = buflen;
|
|
}
|
|
memset ((void *)(volatile void *) buf, 0, size);
|
|
memcpy ((void *)(volatile void *) buf, pd->desc->recv.data, copy_size);
|
|
memset ((void *)(volatile void *) pd->reg_buf, 0, buflen);
|
|
memcpy ((void *)(volatile void *) pd->reg_buf, pd->desc->recv.data, copy_size);
|
|
return true; // len
|
|
}
|
|
|
|
static RList *r_debug_qnx_map_get(RDebug *dbg) {
|
|
return NULL;
|
|
}
|
|
|
|
static bool r_debug_qnx_reg_write(RDebug *dbg, int type, const ut8 *buf, int size) {
|
|
int buflen = 0;
|
|
int bits = dbg->anal->config->bits;
|
|
const char *pcname = r_reg_get_name (dbg->anal->reg, R_REG_NAME_PC);
|
|
RRegItem *reg = r_reg_get (dbg->anal->reg, pcname, 0);
|
|
PluginData *pd = R_UNWRAP3 (dbg, current, plugin_data);
|
|
if (!pd || !pd->reg_buf) {
|
|
// we cannot write registers before we once read them
|
|
return false;
|
|
}
|
|
if (reg) {
|
|
if (bits != reg->size) {
|
|
bits = reg->size;
|
|
}
|
|
}
|
|
free (r_reg_get_bytes (dbg->reg, type, &buflen));
|
|
// some implementations of the gdb protocol are acting weird.
|
|
// so winedbg is not able to write registers through the <G> packet
|
|
// and also it does not return the whole gdb register profile after
|
|
// calling <g>
|
|
// so this workaround resizes the small register profile buffer
|
|
// to the whole set and fills the rest with 0
|
|
if (pd->buf_size < buflen) {
|
|
ut8 *new_buf = realloc (pd->reg_buf, buflen * sizeof (ut8));
|
|
if (!new_buf) {
|
|
return false;
|
|
}
|
|
pd->reg_buf = new_buf;
|
|
memset (new_buf + pd->buf_size, 0, buflen - pd->buf_size);
|
|
}
|
|
|
|
RRegItem *current = NULL;
|
|
for (;;) {
|
|
current = r_reg_next_diff (dbg->reg, type, pd->reg_buf, buflen, current, bits);
|
|
if (!current) {
|
|
break;
|
|
}
|
|
ut64 val = r_reg_get_value (dbg->reg, current);
|
|
const int bytes = bits / 8;
|
|
qnxr_write_reg (pd->desc, current->name, (char *)&val, bytes);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool r_debug_qnx_continue(RDebug *dbg, int pid, int tid, int sig) {
|
|
PluginData *pd = R_UNWRAP3 (dbg, current, plugin_data);
|
|
if (!pd) {
|
|
return false;
|
|
}
|
|
|
|
qnxr_continue (pd->desc, -1);
|
|
return true;
|
|
}
|
|
|
|
static bool r_debug_qnx_step(RDebug *dbg) {
|
|
PluginData *pd = R_UNWRAP3 (dbg, current, plugin_data);
|
|
if (!pd) {
|
|
return false;
|
|
}
|
|
|
|
qnxr_step (pd->desc, -1);
|
|
return true;
|
|
}
|
|
|
|
static RDebugReasonType r_debug_qnx_wait(RDebug *dbg, int pid) {
|
|
PluginData *pd = R_UNWRAP3 (dbg, current, plugin_data);
|
|
if (!pd) {
|
|
return 0;
|
|
}
|
|
|
|
ptid_t ptid = qnxr_wait (pd->desc, pid);
|
|
if (!ptid_equal (ptid, null_ptid)) {
|
|
dbg->reason.signum = pd->desc->signal;
|
|
return pd->desc->notify_type;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static bool r_debug_qnx_stop(RDebug *dbg) {
|
|
PluginData *pd = R_UNWRAP3 (dbg, current, plugin_data);
|
|
if (!pd) {
|
|
return false;
|
|
}
|
|
|
|
qnxr_stop (pd->desc);
|
|
return true;
|
|
}
|
|
|
|
static bool r_debug_qnx_attach(RDebug *dbg, int pid) {
|
|
RIODesc *d = dbg->iob.io->desc;
|
|
dbg->swstep = false;
|
|
|
|
PluginData *pd = R_UNWRAP3 (dbg, current, plugin_data);
|
|
if (!pd) {
|
|
return false;
|
|
}
|
|
|
|
if (d && d->plugin && d->plugin->meta.name && d->data) {
|
|
if (!strcmp ("qnx", d->plugin->meta.name)) {
|
|
RIOQnx *g = d->data;
|
|
int arch = r_sys_arch_id (dbg->arch);
|
|
int bits = dbg->anal->config->bits;
|
|
if ((pd->desc = &g->desc)) {
|
|
switch (arch) {
|
|
case R_SYS_ARCH_X86:
|
|
if (bits == 16 || bits == 32) {
|
|
qnxr_set_architecture (&g->desc, X86_32);
|
|
} else {
|
|
eprintf ("Not supported register %s %d profile\n", dbg->arch, bits);
|
|
return false;
|
|
}
|
|
break;
|
|
case R_SYS_ARCH_ARM:
|
|
if (bits == 16 || bits == 32) {
|
|
qnxr_set_architecture (&g->desc, ARM_32);
|
|
} else {
|
|
eprintf ("Not supported register %s %d profile\n", dbg->arch, bits);
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (pid) {
|
|
qnxr_attach (pd->desc, pid);
|
|
}
|
|
} else {
|
|
R_LOG_ERROR ("underlying IO descriptor isn't a QNX one", __func__);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
dbg->pid = 0;
|
|
return true;
|
|
}
|
|
|
|
static bool r_debug_qnx_detach(RDebug *dbg, int pid) {
|
|
PluginData *pd = R_UNWRAP3 (dbg, current, plugin_data);
|
|
if (!pd) {
|
|
return false;
|
|
}
|
|
|
|
qnxr_disconnect (pd->desc);
|
|
free (pd->reg_buf);
|
|
return true;
|
|
}
|
|
|
|
static char *r_debug_qnx_reg_profile(RDebug *dbg) {
|
|
int arch = r_sys_arch_id (dbg->arch);
|
|
int bits = dbg->anal->config->bits;
|
|
switch (arch) {
|
|
case R_SYS_ARCH_X86:
|
|
return strdup (
|
|
"=PC eip\n"
|
|
"=SP esp\n"
|
|
"=BP ebp\n"
|
|
"=A0 eax\n"
|
|
"=A1 ebx\n"
|
|
"=A2 ecx\n"
|
|
"=A3 edi\n"
|
|
"gpr eax .32 0 0\n"
|
|
"gpr ecx .32 4 0\n"
|
|
"gpr edx .32 8 0\n"
|
|
"gpr ebx .32 12 0\n"
|
|
"gpr esp .32 16 0\n"
|
|
"gpr ebp .32 20 0\n"
|
|
"gpr esi .32 24 0\n"
|
|
"gpr edi .32 28 0\n"
|
|
"gpr eip .32 32 0\n"
|
|
"gpr eflags .32 36 0\n"
|
|
"seg cs .32 40 0\n"
|
|
"seg ss .32 44 0\n"
|
|
#if 0
|
|
"seg ds .32 48 0\n"
|
|
"seg es .32 52 0\n"
|
|
"seg fs .32 56 0\n"
|
|
"seg gs .32 60 0\n"
|
|
#endif
|
|
);
|
|
case R_SYS_ARCH_ARM:
|
|
if (bits == 32) {
|
|
return strdup (
|
|
"=PC r15\n"
|
|
"=SP r14\n" // XXX
|
|
"=A0 r0\n"
|
|
"=A1 r1\n"
|
|
"=A2 r2\n"
|
|
"=A3 r3\n"
|
|
"gpr r0 .32 0 0\n"
|
|
"gpr r1 .32 4 0\n"
|
|
"gpr r2 .32 8 0\n"
|
|
"gpr r3 .32 12 0\n"
|
|
"gpr r4 .32 16 0\n"
|
|
"gpr r5 .32 20 0\n"
|
|
"gpr r6 .32 24 0\n"
|
|
"gpr r7 .32 28 0\n"
|
|
"gpr r8 .32 32 0\n"
|
|
"gpr r9 .32 36 0\n"
|
|
"gpr r10 .32 40 0\n"
|
|
"gpr r11 .32 44 0\n"
|
|
"gpr r12 .32 48 0\n"
|
|
"gpr sp .32 52 0\n" // r13
|
|
"gpr lr .32 56 0\n" // r14
|
|
"gpr pc .32 60 0\n" // r15
|
|
"gpr r13 .32 52 0\n"
|
|
"gpr r14 .32 56 0\n"
|
|
"gpr r15 .32 60 0\n"
|
|
"gpr cpsr .96 64 0\n"
|
|
"mmx d0 .64 68 0\n" // neon
|
|
"mmx d1 .64 76 0\n" // neon
|
|
"mmx d2 .64 84 0\n" // neon
|
|
"mmx d3 .64 92 0\n" // neon
|
|
"mmx d4 .64 100 0\n" // neon
|
|
"mmx d5 .64 108 0\n" // neon
|
|
"mmx d6 .64 116 0\n" // neon
|
|
"mmx d7 .64 124 0\n" // neon
|
|
"mmx d8 .64 132 0\n" // neon
|
|
"mmx d9 .64 140 0\n" // neon
|
|
"mmx d10 .64 148 0\n" // neon
|
|
"mmx d11 .64 156 0\n" // neon
|
|
"mmx d12 .64 164 0\n" // neon
|
|
"mmx d13 .64 172 0\n" // neon
|
|
"mmx d14 .64 180 0\n" // neon
|
|
"mmx d15 .64 188 0\n" // neon
|
|
"mmx d16 .64 196 0\n" // neon
|
|
"mmx d17 .64 204 0\n" // neon
|
|
"mmx d18 .64 212 0\n" // neon
|
|
"mmx d19 .64 220 0\n" // neon
|
|
"mmx d20 .64 228 0\n" // neon
|
|
"mmx d21 .64 236 0\n" // neon
|
|
"mmx d22 .64 244 0\n" // neon
|
|
"mmx d23 .64 252 0\n" // neon
|
|
"mmx d24 .64 260 0\n" // neon
|
|
"mmx d25 .64 268 0\n" // neon
|
|
"mmx d26 .64 276 0\n" // neon
|
|
"mmx d27 .64 284 0\n" // neon
|
|
"mmx d28 .64 292 0\n" // neon
|
|
"mmx d29 .64 300 0\n" // neon
|
|
"mmx d30 .64 308 0\n" // neon
|
|
"mmx d31 .64 316 0\n" // neon
|
|
"mmx fpscr .32 324 0\n" // neon
|
|
);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static int r_debug_qnx_breakpoint(RBreakpoint *bp, RBreakpointItem *b, bool set) {
|
|
RDebug *dbg = bp->user;
|
|
PluginData *pd = R_UNWRAP3 (dbg, current, plugin_data);
|
|
if (!pd || !b) {
|
|
return false;
|
|
}
|
|
|
|
int ret = set
|
|
? b->hw
|
|
? qnxr_set_hwbp (pd->desc, b->addr, "")
|
|
: qnxr_set_bp (pd->desc, b->addr, "")
|
|
: b->hw
|
|
? qnxr_remove_hwbp (pd->desc, b->addr)
|
|
: qnxr_remove_bp (pd->desc, b->addr);
|
|
return !ret;
|
|
}
|
|
|
|
static bool init_plugin(RDebug *dbg, RDebugPluginSession *ds) {
|
|
R_RETURN_VAL_IF_FAIL (dbg && ds, false);
|
|
|
|
ds->plugin_data = R_NEW0 (PluginData);
|
|
return !!ds->plugin_data;
|
|
}
|
|
|
|
static bool fini_plugin(RDebug *dbg, RDebugPluginSession *ds) {
|
|
R_RETURN_VAL_IF_FAIL (dbg && ds && ds->plugin_data, false);
|
|
|
|
PluginData *pd = ds->plugin_data;
|
|
R_FREE (pd->reg_buf);
|
|
// no need to free desc? owned by other code?
|
|
R_FREE (ds->plugin_data);
|
|
return true;
|
|
}
|
|
|
|
RDebugPlugin r_debug_plugin_qnx = {
|
|
.meta = {
|
|
.name = "qnx",
|
|
.author = "pancake, defragger, madprogrammer",
|
|
.desc = "qnx debug plugin",
|
|
.license = "LGPL-3.0-only",
|
|
},
|
|
.arch = "x86,arm",
|
|
.bits = R_SYS_BITS_32,
|
|
.init_plugin = init_plugin,
|
|
.fini_plugin = fini_plugin,
|
|
.step = r_debug_qnx_step,
|
|
.cont = r_debug_qnx_continue,
|
|
.attach = r_debug_qnx_attach,
|
|
.detach = r_debug_qnx_detach,
|
|
.pids = r_debug_qnx_pids,
|
|
.tids = r_debug_qnx_tids,
|
|
.select = r_debug_qnx_select,
|
|
.stop = r_debug_qnx_stop,
|
|
.canstep = 1,
|
|
.wait = r_debug_qnx_wait,
|
|
.map_get = r_debug_qnx_map_get,
|
|
.breakpoint = r_debug_qnx_breakpoint,
|
|
.reg_read = r_debug_qnx_reg_read,
|
|
.reg_write = r_debug_qnx_reg_write,
|
|
.reg_profile = r_debug_qnx_reg_profile,
|
|
};
|
|
|
|
#ifndef R2_PLUGIN_INCORE
|
|
R_API RLibStruct radare_plugin = {
|
|
.type = R_LIB_TYPE_DBG,
|
|
.data = &r_debug_plugin_qnx,
|
|
.version = R2_VERSION};
|
|
#endif
|