Fix r2 arch plugin for radare2 >= 5.8 RArchPlugin API
- Rewrote plugin using RArchPlugin (decode callback) - Matches z80 reference plugin structure - Tested against r2 6.1.3 - Decodes: NOP, Type 1 (Compute|DM|PM), Type 6 (Imm16), Type 10 (Jump/Call), Type 11 (DO UNTIL), Type 20 (RTS/RTI) - Proper code alignment (3 bytes) via archinfo callback - Delayed branch flag support
This commit is contained in:
@@ -1,14 +1,24 @@
|
|||||||
NAME=asm_adsp219x
|
NAME=asm_adsp219x
|
||||||
R2_PLUGIN_PATH=$(shell r2 -H R2_USER_PLUGINS)
|
R2_PLUGINS=$(shell r2 -H R2_USER_PLUGINS)
|
||||||
|
R2_INCDIR=$(shell r2 -H R2_INCDIR)
|
||||||
|
R2_LIBDIR=$(shell r2 -H R2_LIBDIR)
|
||||||
|
|
||||||
all: $(NAME).so
|
all: $(NAME).so
|
||||||
|
|
||||||
$(NAME).so: $(NAME).c
|
$(NAME).so: $(NAME).c
|
||||||
gcc -shared -Wall -O2 -fPIC $(shell pkg-config --cflags --libs r_asm) $< -o $@
|
gcc -shared -Wall -O2 -fPIC \
|
||||||
|
-I$(R2_INCDIR) -I$(R2_INCDIR)/sdb \
|
||||||
|
-L$(R2_LIBDIR) \
|
||||||
|
$(NAME).c -o $@ -lr_arch -lr_util
|
||||||
|
|
||||||
install: $(NAME).so
|
install: $(NAME).so
|
||||||
mkdir -p $(R2_PLUGIN_PATH)
|
mkdir -p $(R2_PLUGINS)
|
||||||
cp $(NAME).so $(R2_PLUGIN_PATH)
|
cp -f $(NAME).so $(R2_PLUGINS)/
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -f $(R2_PLUGINS)/$(NAME).so
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(NAME).so
|
rm -f $(NAME).so
|
||||||
|
|
||||||
|
.PHONY: all install uninstall clean
|
||||||
|
|||||||
@@ -1,61 +1,212 @@
|
|||||||
#include <r_asm.h>
|
/* ADSP-219x radare2 arch plugin - LGPL - OpenClaw / adsp219x-re project */
|
||||||
#include <r_lib.h>
|
/* Targets radare2 >= 5.8 (RArchPlugin API) */
|
||||||
|
|
||||||
/* ADSP-219x 24-bit Opcode Table (Basic definitions) */
|
#include <r_arch.h>
|
||||||
static const char *AXOP[] = {"AX0", "AX1", "AR", "MR0", "MR1", "MR2", "SR0", "SR1"};
|
|
||||||
static const char *AYOP[] = {"AY0", "AY1", "AF", "0"};
|
|
||||||
static const char *COND[] = {"EQ", "NE", "GT", "LE", "LT", "GE", "AV", "NAV", "AC", "NAC", "SWCOND", "NSWCOND", "MV", "NMV", "NCE", "TRUE"};
|
|
||||||
|
|
||||||
static int adsp219x_disassemble(RAsm *a, RAsmOp *op, const ut8 *buf, int len) {
|
/* ---- lookup tables ---- */
|
||||||
if (len < 3) return -1;
|
|
||||||
ut32 ins = (buf[0] << 16) | (buf[1] << 8) | buf[2];
|
|
||||||
op->size = 3;
|
|
||||||
|
|
||||||
/* Simplified decoding for the r2-Backend */
|
static const char *amf_alu[] = {
|
||||||
// Type 1: Compute (11xxxxxx)
|
"Y", "Y+1", "X+Y+C", "X+Y",
|
||||||
if ((ins >> 22) == 0b11) {
|
"NOT Y", "-Y", "X-Y+C-1", "X-Y",
|
||||||
ut32 amf = (ins >> 13) & 0x1F;
|
"Y-1", "Y-X", "Y-X+C-1", "NOT X",
|
||||||
ut32 xop = (ins >> 8) & 0x7;
|
"X AND Y", "X OR Y", "X XOR Y", "ABS X"
|
||||||
ut32 yop = (ins >> 11) & 0x3;
|
};
|
||||||
if (amf == 0) r_strbuf_setf(&op->buf_asm, "NOP");
|
static const char *amf_mac[] = {
|
||||||
else if (amf >= 0x10) r_strbuf_setf(&op->buf_asm, "COMPUTE (ALU:%d, %s, %s)", amf, AXOP[xop], AYOP[yop]);
|
"NOP", "X*Y (RND)", "MR+X*Y (RND)", "MR-X*Y (RND)",
|
||||||
else r_strbuf_setf(&op->buf_asm, "COMPUTE (MAC:%d, %s, %s)", amf, AXOP[xop], AYOP[yop]);
|
"X*Y (SS)", "X*Y (SU)", "X*Y (US)", "X*Y (UU)",
|
||||||
|
"MR+X*Y (SS)", "MR+X*Y (SU)", "MR+X*Y (US)", "MR+X*Y (UU)",
|
||||||
|
"MR-X*Y (SS)", "MR-X*Y (SU)", "MR-X*Y (US)", "MR-X*Y (UU)"
|
||||||
|
};
|
||||||
|
static const char *cond_str[] = {
|
||||||
|
"EQ", "NE", "GT", "LE", "LT", "GE", "AV", "NOT AV",
|
||||||
|
"AC", "NOT AC", "SWCOND", "NOT SWCOND", "MV", "NOT MV", "NOT CE", "TRUE"
|
||||||
|
};
|
||||||
|
static const char *reg0[] = {
|
||||||
|
"AX0", "AX1", "MX0", "MX1", "AY0", "AY1", "MY0", "MY1",
|
||||||
|
"MR2", "SR2", "AR", "SI", "MR1", "SR1", "MR0", "SR0"
|
||||||
|
};
|
||||||
|
static const char *dd_str[] = { "AX0", "AX1", "MX0", "MX1" };
|
||||||
|
static const char *pd_str[] = { "AY0", "AY1", "MY0", "MY1" };
|
||||||
|
|
||||||
|
/* ---- helpers ---- */
|
||||||
|
|
||||||
|
static const char *ireg(int g, int idx) {
|
||||||
|
static const char *names[2][4] = {
|
||||||
|
{"I0", "I1", "I2", "I3"},
|
||||||
|
{"I4", "I5", "I6", "I7"}
|
||||||
|
};
|
||||||
|
return names[g & 1][idx & 3];
|
||||||
}
|
}
|
||||||
// Type 6: Dreg = Imm16 (0100xxxx)
|
static const char *mreg(int g, int idx) {
|
||||||
else if ((ins >> 20) == 0b0100) {
|
static const char *names[2][4] = {
|
||||||
|
{"M0", "M1", "M2", "M3"},
|
||||||
|
{"M4", "M5", "M6", "M7"}
|
||||||
|
};
|
||||||
|
return names[g & 1][idx & 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *amf_str(int code) {
|
||||||
|
if (code < 16) {
|
||||||
|
return amf_mac[code];
|
||||||
|
}
|
||||||
|
return amf_alu[code - 16];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- decode ---- */
|
||||||
|
|
||||||
|
static bool decode(RArchSession *as, RAnalOp *op, RArchDecodeMask mask) {
|
||||||
|
const int len = op->size;
|
||||||
|
if (len < 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const ut8 *buf = op->bytes;
|
||||||
|
ut32 ins = ((ut32)buf[0] << 16) | ((ut32)buf[1] << 8) | (ut32)buf[2];
|
||||||
|
op->size = 3;
|
||||||
|
op->type = R_ANAL_OP_TYPE_UNK;
|
||||||
|
|
||||||
|
if (!(mask & R_ARCH_OP_MASK_DISASM)) {
|
||||||
|
/* lightweight decode: just set size + type */
|
||||||
|
if (ins == 0) {
|
||||||
|
op->type = R_ANAL_OP_TYPE_NOP;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Type 30: NOP (0x000000) ---- */
|
||||||
|
if (ins == 0) {
|
||||||
|
op->type = R_ANAL_OP_TYPE_NOP;
|
||||||
|
op->mnemonic = strdup ("NOP");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ut32 top2 = ins >> 22;
|
||||||
|
|
||||||
|
/* ---- Type 1: Compute | DM | PM (top2 == 11) ---- */
|
||||||
|
if (top2 == 3) {
|
||||||
|
ut32 amf = (ins >> 13) & 0x1F;
|
||||||
|
ut32 dmi = (ins >> 10) & 0x3;
|
||||||
|
ut32 dmm = (ins >> 8) & 0x3;
|
||||||
|
ut32 pmi = (ins >> 6) & 0x3;
|
||||||
|
ut32 pmm = (ins >> 4) & 0x3;
|
||||||
|
ut32 dd = (ins >> 2) & 0x3;
|
||||||
|
ut32 pd = (ins >> 0) & 0x3;
|
||||||
|
|
||||||
|
if (amf == 0 && (ins & 0x3FFFFF) == 0) {
|
||||||
|
op->type = R_ANAL_OP_TYPE_NOP;
|
||||||
|
op->mnemonic = strdup ("NOP /* Type 1 */");
|
||||||
|
} else {
|
||||||
|
op->type = R_ANAL_OP_TYPE_ADD;
|
||||||
|
op->mnemonic = r_str_newf ("%s, %s = DM(%s += %s), %s = PM(%s += %s)",
|
||||||
|
amf_str (amf),
|
||||||
|
dd_str[dd], ireg (0, dmi), mreg (0, dmm),
|
||||||
|
pd_str[pd], ireg (1, pmi), mreg (1, pmm));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Type 6: Dreg = Imm16 (bits 23-20 == 0100) ---- */
|
||||||
|
if ((ins >> 20) == 4) {
|
||||||
ut32 dreg = ins & 0xF;
|
ut32 dreg = ins & 0xF;
|
||||||
ut32 data = (ins >> 4) & 0xFFFF;
|
ut32 data = (ins >> 4) & 0xFFFF;
|
||||||
r_strbuf_setf(&op->buf_asm, "D%d = 0x%x", dreg, data);
|
op->type = R_ANAL_OP_TYPE_MOV;
|
||||||
|
op->val = data;
|
||||||
|
op->mnemonic = r_str_newf ("%s = 0x%04X", reg0[dreg], data);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
// Type 10: Jump (000110B xxxxx COND)
|
|
||||||
else if ((ins >> 19) == 0b00011) {
|
/* ---- Type 10: Direct Jump/Call (bits 23-19 == 00011) ---- */
|
||||||
|
if ((ins >> 19) == 3) {
|
||||||
|
ut32 b = (ins >> 18) & 1; /* delayed branch */
|
||||||
|
ut32 s = (ins >> 17) & 1; /* 0=jump, 1=call */
|
||||||
ut32 addr = (ins >> 4) & 0x1FFF;
|
ut32 addr = (ins >> 4) & 0x1FFF;
|
||||||
ut32 cond = ins & 0xF;
|
ut32 cond = ins & 0xF;
|
||||||
ut8 b = (ins >> 16) & 0x1;
|
|
||||||
const char *db = b ? " (DB)" : "";
|
const char *db = b ? " (DB)" : "";
|
||||||
if (cond == 0xF) r_strbuf_setf(&op->buf_asm, "JUMP 0x%x%s", addr, db);
|
const char *type = s ? "CALL" : "JUMP";
|
||||||
else r_strbuf_setf(&op->buf_asm, "IF %s JUMP 0x%x%s", COND[cond], addr, db);
|
op->jump = addr;
|
||||||
|
|
||||||
|
if (s) {
|
||||||
|
op->type = (cond == 0xF) ? R_ANAL_OP_TYPE_CALL : R_ANAL_OP_TYPE_CCALL;
|
||||||
|
} else {
|
||||||
|
op->type = (cond == 0xF) ? R_ANAL_OP_TYPE_JMP : R_ANAL_OP_TYPE_CJMP;
|
||||||
}
|
}
|
||||||
// Type 30: NOP (00000000)
|
if (cond != 0xF) {
|
||||||
else if (ins == 0) {
|
op->fail = op->addr + 3;
|
||||||
r_strbuf_setf(&op->buf_asm, "NOP");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
r_strbuf_setf(&op->buf_asm, "unknown (0x%06x)", ins);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return op->size;
|
if (cond == 0xF) {
|
||||||
|
op->mnemonic = r_str_newf ("%s 0x%04X%s", type, addr, db);
|
||||||
|
} else {
|
||||||
|
op->mnemonic = r_str_newf ("IF %s %s 0x%04X%s", cond_str[cond], type, addr, db);
|
||||||
|
}
|
||||||
|
if (b) {
|
||||||
|
op->delay = 1;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
RAsmPlugin r_asm_plugin_adsp219x = {
|
/* ---- Type 20: RTS / RTI (bits 23-19 == 00101) ---- */
|
||||||
|
if ((ins >> 19) == 5) {
|
||||||
|
ut32 t = (ins >> 18) & 1; /* 0=RTS, 1=RTI */
|
||||||
|
ut32 cond = ins & 0xF;
|
||||||
|
const char *type = t ? "RTI" : "RTS";
|
||||||
|
op->type = (cond == 0xF) ? R_ANAL_OP_TYPE_RET : R_ANAL_OP_TYPE_CRET;
|
||||||
|
op->eob = true;
|
||||||
|
if (cond == 0xF) {
|
||||||
|
op->mnemonic = r_str_newf ("%s", type);
|
||||||
|
} else {
|
||||||
|
op->mnemonic = r_str_newf ("IF %s %s", cond_str[cond], type);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Type 11: DO UNTIL (bits 23-20 == 0010) ---- */
|
||||||
|
if ((ins >> 20) == 2) {
|
||||||
|
ut32 addr = (ins >> 4) & 0x3FFF;
|
||||||
|
ut32 term = ins & 0xF;
|
||||||
|
op->type = R_ANAL_OP_TYPE_JMP;
|
||||||
|
op->jump = addr;
|
||||||
|
op->mnemonic = r_str_newf ("DO 0x%04X UNTIL %s", addr, cond_str[term]);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- fallback ---- */
|
||||||
|
op->mnemonic = r_str_newf ("unknown 0x%06X", ins);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int archinfo(RArchSession *s, ut32 q) {
|
||||||
|
switch (q) {
|
||||||
|
case R_ARCH_INFO_CODE_ALIGN:
|
||||||
|
return 3;
|
||||||
|
case R_ARCH_INFO_DATA_ALIGN:
|
||||||
|
return 1;
|
||||||
|
case R_ARCH_INFO_MINOP_SIZE:
|
||||||
|
return 3;
|
||||||
|
case R_ARCH_INFO_MAXOP_SIZE:
|
||||||
|
return 3;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const RArchPlugin r_arch_plugin_adsp219x = {
|
||||||
|
.meta = {
|
||||||
.name = "adsp219x",
|
.name = "adsp219x",
|
||||||
.desc = "Analog Devices ADSP-219x Disassembler (Air-Gapped RE)",
|
.author = "OpenClaw",
|
||||||
|
.desc = "Analog Devices ADSP-219x DSP",
|
||||||
|
.license = "LGPL-3.0-only",
|
||||||
|
},
|
||||||
.arch = "adsp219x",
|
.arch = "adsp219x",
|
||||||
.bits = 24,
|
.bits = R_SYS_BITS_PACK (24),
|
||||||
.endian = R_SYS_ENDIAN_BIG,
|
.endian = R_SYS_ENDIAN_BIG,
|
||||||
.disassemble = &adsp219x_disassemble
|
.info = archinfo,
|
||||||
|
.decode = decode,
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef R2_PLUGIN_INLINE
|
#ifndef R2_PLUGIN_INCORE
|
||||||
R_LIB_VERSION_SET(r_asm_plugin_adsp219x, r_asm);
|
R_API RLibStruct radare_plugin = {
|
||||||
|
.type = R_LIB_TYPE_ARCH,
|
||||||
|
.data = &r_arch_plugin_adsp219x,
|
||||||
|
.version = R2_VERSION
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
BIN
r2plugin/asm_adsp219x.so
Executable file
BIN
r2plugin/asm_adsp219x.so
Executable file
Binary file not shown.
13
r2plugin/r2_adsp219x.py
Normal file
13
r2plugin/r2_adsp219x.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import r2pipe
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def adsp_disasm(hex_str):
|
||||||
|
"""Simple bridge to our existing disasm logic"""
|
||||||
|
# For now, just return hex + architecture name as POC
|
||||||
|
return f"ADSP219x: {hex_str}"
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
r2 = r2pipe.open()
|
||||||
|
# Register architecture via r2pipe hook
|
||||||
|
# Note: Modern r2 supports python-based asm plugins via 'L' (plugins)
|
||||||
|
print("ADSP219x Plugin loaded via r2pipe")
|
||||||
Reference in New Issue
Block a user