winedbg: Introduce ARM disassembler

André Hentschel nerv at dawncrow.de
Mon Apr 9 16:15:31 CDT 2012


First step into a good ARM disassembler.
I choosed to code an disassembler for Wine because there is no lgpl compatible option. (Only bsd with displaying the authors name in binaries and gplv3).
This disassembler is not complete yet, but the most important/common commands are included. Thumb disassembling is coming soon.
---
 programs/winedbg/Makefile.in     |    1 +
 programs/winedbg/be_arm.c        |    8 +-
 programs/winedbg/db_disasm_arm.c |  325 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 329 insertions(+), 5 deletions(-)
 create mode 100644 programs/winedbg/db_disasm_arm.c

diff --git a/programs/winedbg/Makefile.in b/programs/winedbg/Makefile.in
index 6b1a6cf..2638dbd 100644
--- a/programs/winedbg/Makefile.in
+++ b/programs/winedbg/Makefile.in
@@ -13,6 +13,7 @@ C_SRCS = \
 	break.c \
 	crashdlg.c \
 	db_disasm.c \
+	db_disasm_arm.c \
 	db_disasm64.c \
 	display.c \
 	expr.c \
diff --git a/programs/winedbg/be_arm.c b/programs/winedbg/be_arm.c
index e36c969..7fd3359 100644
--- a/programs/winedbg/be_arm.c
+++ b/programs/winedbg/be_arm.c
@@ -24,6 +24,9 @@
 
 #if defined(__arm__) && !defined(__ARMEB__)
 
+  /* db_disasm_arm.c */
+extern void be_arm_disasm_one_insn(ADDRESS64* addr, int display);
+
 static unsigned be_arm_get_addr(HANDLE hThread, const CONTEXT* ctx,
                                 enum be_cpu_addr bca, ADDRESS64* addr)
 {
@@ -150,11 +153,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)
diff --git a/programs/winedbg/db_disasm_arm.c b/programs/winedbg/db_disasm_arm.c
new file mode 100644
index 0000000..7d7edeb
--- /dev/null
+++ b/programs/winedbg/db_disasm_arm.c
@@ -0,0 +1,325 @@
+/*
+ * ARM Instruction disassembler
+ *
+ * Copyright 2011-2012 André Hentschel
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "debugger.h"
+
+#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");
+}
+#endif  /* __arm__ */
-- 

Best Regards, André Hentschel


More information about the wine-patches mailing list