winedbg: Add partial ARM disassembler
André Hentschel
nerv at dawncrow.de
Wed Apr 11 13:28:30 CDT 2012
---
programs/winedbg/be_arm.c | 309 ++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 304 insertions(+), 5 deletions(-)
diff --git a/programs/winedbg/be_arm.c b/programs/winedbg/be_arm.c
index e36c969..4cee5d2 100644
--- a/programs/winedbg/be_arm.c
+++ b/programs/winedbg/be_arm.c
@@ -24,6 +24,310 @@
#if defined(__arm__) && !defined(__ARMEB__)
+/*
+ * Switch to disassemble Thumb code.
+ */
+static BOOL db_disasm_thumb = FALSE;
+
+/*
+ * Flag to indicate whether we need to display instruction,
+ * or whether we just need to know the address of the next
+ * instruction.
+ */
+static BOOL db_display = FALSE;
+
+#define ARM_INSN_SIZE 4
+#define THUMB_INSN_SIZE 2
+
+#define ROR32(n, r) (((n) >> (r)) | ((n) << (32 - (r))))
+
+#define get_cond(ins) tbl_cond[(ins >> 28) & 0x0f]
+#define get_nibble(ins, num) ((ins >> (num * 4)) & 0x0f)
+
+static char const tbl_addrmode[][3] = {
+ "da", "ia", "db", "ib"
+};
+
+static char const tbl_cond[][3] = {
+ "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", "hi", "ls", "ge", "lt", "gt", "le", "", ""
+};
+
+static char const tbl_dataops[][4] = {
+ "and", "eor", "sub", "rsb", "add", "adc", "sbc", "rsc", "tst", "teq", "cmp", "cmn", "orr",
+ "mov", "bic", "mvn"
+};
+
+static UINT db_get_inst(void* addr, int size)
+{
+ UINT result = 0;
+ char buffer[4];
+
+ if (dbg_read_memory(addr, buffer, size))
+ {
+ switch (size)
+ {
+ case 4:
+ result = *(UINT*)buffer;
+ break;
+ case 2:
+ result = *(WORD*)buffer;
+ break;
+ }
+ }
+ return result;
+}
+
+static UINT arm_disasm_branch(UINT inst)
+{
+ short link = (inst >> 24) & 0x01;
+ int offset = (inst << 2) & 0x03ffffff;
+
+ if (offset & 0x02000000) offset |= 0xfc000000;
+ offset += 8;
+
+ dbg_printf("\n\tb%s%s\t#%d/0x%08x", link ? "l" : "", get_cond(inst), offset, offset);
+ return 0;
+}
+
+static UINT arm_disasm_dataprocessing(UINT inst)
+{
+ short condcodes = (inst >> 20) & 0x01;
+ short opcode = (inst >> 21) & 0x0f;
+ short immediate = (inst >> 25) & 0x01;
+ short no_op1 = (opcode & 0x0d) == 0x0d;
+
+ /* check for nop */
+ if (get_nibble(inst, 3) == 15 /* r15 */ && condcodes == 0 &&
+ opcode >= 8 /* tst */ && opcode <= 11 /* cmn */)
+ {
+ dbg_printf("\n\tnop");
+ return 0;
+ }
+
+ dbg_printf("\n\t%s%s%s", tbl_dataops[opcode], condcodes ? "s" : "", get_cond(inst));
+ if (no_op1)
+ {
+ if (immediate)
+ dbg_printf("\tr%u, #%u", get_nibble(inst, 3),
+ ROR32(inst & 0xff, 2 * get_nibble(inst, 2)));
+ else
+ dbg_printf("\tr%u, r%u", get_nibble(inst, 3), get_nibble(inst, 0));
+ }
+ else
+ {
+ if (immediate)
+ dbg_printf("\tr%u, r%u, #%u", get_nibble(inst, 3), get_nibble(inst, 4),
+ ROR32(inst & 0xff, 2 * get_nibble(inst, 2)));
+ else
+ dbg_printf("\tr%u, r%u, r%u", get_nibble(inst, 3), get_nibble(inst, 4),
+ get_nibble(inst, 0));
+ }
+ return 0;
+}
+
+static UINT arm_disasm_singletrans(UINT inst)
+{
+ short load = (inst >> 20) & 0x01;
+ short writeback = (inst >> 21) & 0x01;
+ short byte = (inst >> 22) & 0x01;
+ short direction = (inst >> 23) & 0x01;
+ /* FIXME: what to do with bit 24 (indexing) */
+ short immediate = !((inst >> 25) & 0x01);
+ short offset = inst & 0x0fff;
+
+ if (!direction) offset *= -1;
+
+ dbg_printf("\n\t%s%s%s%s", load ? "ldr" : "str", byte ? "b" : "", writeback ? "t" : "",
+ get_cond(inst));
+ if (immediate)
+ dbg_printf("\tr%u, [r%u, #%d]", get_nibble(inst, 3), get_nibble(inst, 4), offset);
+ else
+ dbg_printf("\tr%u, r%u, r%u", get_nibble(inst, 3), get_nibble(inst, 4),
+ get_nibble(inst, 0));
+ return 0;
+}
+
+static UINT arm_disasm_halfwordtrans(UINT inst)
+{
+ short halfword = (inst >> 5) & 0x01;
+ short sign = (inst >> 6) & 0x01;
+ short load = (inst >> 20) & 0x01;
+ short writeback = (inst >> 21) & 0x01;
+ short immediate = (inst >> 22) & 0x01;
+ short direction = (inst >> 23) & 0x01;
+ /* FIXME: what to do with bit 24 (indexing) */
+ short offset = ((inst >> 4) & 0xf0) + (inst & 0x0f);
+
+ if (!direction) offset *= -1;
+
+ dbg_printf("\n\t%s%s%s%s%s", load ? "ldr" : "str", sign ? "s" : "",
+ halfword ? "h" : (sign ? "b" : ""), writeback ? "t" : "", get_cond(inst));
+ if (immediate)
+ dbg_printf("\tr%u, r%u, #%d", get_nibble(inst, 3), get_nibble(inst, 4), offset);
+ else
+ dbg_printf("\tr%u, r%u, r%u", get_nibble(inst, 3), get_nibble(inst, 4), get_nibble(inst, 0));
+ return 0;
+}
+
+static UINT arm_disasm_blocktrans(UINT inst)
+{
+ short load = (inst >> 20) & 0x01;
+ short writeback = (inst >> 21) & 0x01;
+ short psr = (inst >> 22) & 0x01;
+ short addrmode = (inst >> 23) & 0x03;
+ short i;
+ short last=15;
+ for (i=15;i>=0;i--)
+ if ((inst>>i) & 1)
+ {
+ last = i;
+ break;
+ }
+
+ dbg_printf("\n\t%s%s%s\tr%u%s, {", load ? "ldm" : "stm", tbl_addrmode[addrmode], get_cond(inst),
+ get_nibble(inst, 4), writeback ? "!" : "");
+ for (i=0;i<=15;i++)
+ if ((inst>>i) & 1)
+ {
+ if (i == last) dbg_printf("r%u", i);
+ else dbg_printf("r%u, ", i);
+ }
+ dbg_printf("}%s", psr ? "^" : "");
+ return 0;
+}
+
+static UINT arm_disasm_swi(UINT inst)
+{
+ UINT comment = inst & 0x00ffffff;
+ dbg_printf("\n\tswi%s\t#%d/0x%08x", get_cond(inst), comment, comment);
+ return 0;
+}
+
+static UINT arm_disasm_coproctrans(UINT inst)
+{
+ WORD CRm = inst & 0x0f;
+ WORD CP = (inst >> 5) & 0x07;
+ WORD CPnum = (inst >> 8) & 0x0f;
+ WORD CRn = (inst >> 16) & 0x0f;
+ WORD load = (inst >> 20) & 0x01;
+ WORD CP_Opc = (inst >> 21) & 0x07;
+
+ dbg_printf("\n\t%s%s\t%u, %u, r%u, cr%u, cr%u, {%u}", load ? "mrc" : "mcr", get_cond(inst), CPnum,
+ CP, get_nibble(inst, 3), CRn, CRm, CP_Opc);
+ return 0;
+}
+
+static UINT arm_disasm_coprocdataop(UINT inst)
+{
+ WORD CRm = inst & 0x0f;
+ WORD CP = (inst >> 5) & 0x07;
+ WORD CPnum = (inst >> 8) & 0x0f;
+ WORD CRd = (inst >> 12) & 0x0f;
+ WORD CRn = (inst >> 16) & 0x0f;
+ WORD CP_Opc = (inst >> 20) & 0x0f;
+
+ dbg_printf("\n\tcdp%s\t%u, %u, cr%u, cr%u, cr%u, {%u}", get_cond(inst),
+ CPnum, CP, CRd, CRn, CRm, CP_Opc);
+ return 0;
+}
+
+static UINT arm_disasm_coprocdatatrans(UINT inst)
+{
+ WORD CPnum = (inst >> 8) & 0x0f;
+ WORD CRd = (inst >> 12) & 0x0f;
+ WORD load = (inst >> 20) & 0x01;
+ /* FIXME: what to do with bit 21 (writeback) */
+ WORD translen = (inst >> 22) & 0x01;
+ WORD direction = (inst >> 23) & 0x01;
+ /* FIXME: what to do with bit 24 (indexing) */
+ short offset = (inst & 0xff) << 2;
+
+ if (!direction) offset *= -1;
+
+ dbg_printf("\n\t%s%s%s", load ? "ldc" : "stc", translen ? "l" : "", get_cond(inst));
+ dbg_printf("\t%u, cr%u, [r%u, #%d]", CPnum, CRd, get_nibble(inst, 4), offset);
+ return 0;
+}
+
+struct inst_arm
+{
+ UINT mask;
+ UINT pattern;
+ UINT (*func)(UINT);
+};
+
+static const struct inst_arm tbl_arm[] = {
+ { 0x0e000000, 0x0a000000, arm_disasm_branch },
+ { 0x0c000000, 0x00000000, arm_disasm_dataprocessing },
+ { 0x0c000000, 0x04000000, arm_disasm_singletrans },
+ { 0x0e000090, 0x00000090, arm_disasm_halfwordtrans },
+ { 0x0e000000, 0x08000000, arm_disasm_blocktrans },
+ { 0x0f000000, 0x0f000000, arm_disasm_swi },
+ { 0x0f000010, 0x0e000010, arm_disasm_coproctrans },
+ { 0x0f000010, 0x0e000000, arm_disasm_coprocdataop },
+ { 0x0e000000, 0x0c000000, arm_disasm_coprocdatatrans },
+ { 0x00000000, 0x00000000, NULL }
+};
+
+/***********************************************************************
+ * disasm_one_insn
+ *
+ * Disassemble instruction at 'addr'. addr is changed to point to the
+ * start of the next instruction.
+ */
+void be_arm_disasm_one_insn(ADDRESS64 *addr, int display)
+{
+ struct inst_arm *a_ptr = (struct inst_arm *)&tbl_arm;
+ UINT inst;
+ int size;
+ int matched = 0;
+
+ char tmp[64];
+ DWORD_PTR* pval;
+
+ if (!memory_get_register(CV_ARM_CPSR, &pval, tmp, sizeof(tmp)))
+ dbg_printf("\n\tmemory_get_register failed: %s\n",tmp);
+ else
+ db_disasm_thumb=(*pval & 0x20)?TRUE:FALSE;
+
+ if (db_disasm_thumb) size = THUMB_INSN_SIZE;
+ else size = ARM_INSN_SIZE;
+
+ db_display = display;
+ inst = db_get_inst( memory_to_linear_addr(addr), size );
+
+ if (!db_disasm_thumb)
+ {
+ while (a_ptr->func) {
+ if ((inst & a_ptr->mask) == a_ptr->pattern) {
+ matched = 1;
+ break;
+ }
+ a_ptr++;
+ }
+
+ if (!matched) {
+ dbg_printf("\n\tUnknown Instruction: %08x\n", inst);
+ addr->Offset += size;
+ return;
+ }
+ else
+ {
+ if (!a_ptr->func(inst))
+ {
+ dbg_printf("\n");
+ addr->Offset += size;
+ }
+ return;
+ }
+ }
+ else
+ {
+ dbg_printf("\n\tThumb disassembling not yet implemented\n");
+ addr->Offset += size;
+ }
+}
+
static unsigned be_arm_get_addr(HANDLE hThread, const CONTEXT* ctx,
enum be_cpu_addr bca, ADDRESS64* addr)
{
@@ -150,11 +454,6 @@ static unsigned be_arm_is_jump(const void* insn, ADDRESS64* jumpee)
return FALSE;
}
-static void be_arm_disasm_one_insn(ADDRESS64* addr, int display)
-{
- dbg_printf("Disasm NIY\n");
-}
-
static unsigned be_arm_insert_Xpoint(HANDLE hProcess, const struct be_process_io* pio,
CONTEXT* ctx, enum be_xpoint_type type,
void* addr, unsigned long* val, unsigned size)
--
Best Regards, André Hentschel
More information about the wine-patches
mailing list