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