You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1832 lines
48 KiB
1832 lines
48 KiB
//----------------------------------------------------------------------------
|
|
//
|
|
// Disassembly portions of ARM machine implementation.
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 2001-2002.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "ntsdp.hpp"
|
|
|
|
#include "arminst.h"
|
|
|
|
PSTR g_ArmCond[] =
|
|
{
|
|
"eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
|
|
"hi", "ls", "ge", "lt", "gt", "le", "al", "nv",
|
|
};
|
|
|
|
PSTR g_ArmShift[] =
|
|
{
|
|
"lsl", "lsr", "asr", "ror", "rrx",
|
|
};
|
|
|
|
enum ARM_DECODE
|
|
{
|
|
ARMDEC_INVALID,
|
|
ARMDEC_ARTH_IMM,
|
|
ARMDEC_ARTH_ISHF,
|
|
ARMDEC_ARTH_RSHF,
|
|
ARMDEC_BI,
|
|
ARMDEC_BKPT,
|
|
ARMDEC_BXI,
|
|
ARMDEC_BXR,
|
|
ARMDEC_CDP,
|
|
ARMDEC_CLZ,
|
|
ARMDEC_CMP_IMM,
|
|
ARMDEC_CMP_ISHF,
|
|
ARMDEC_CMP_RSHF,
|
|
ARMDEC_LDC,
|
|
ARMDEC_LDM,
|
|
ARMDEC_LDR_IMM,
|
|
ARMDEC_LDR_RSHF,
|
|
ARMDEC_LDRH_IMM,
|
|
ARMDEC_LDRH_REG,
|
|
ARMDEC_MCR,
|
|
ARMDEC_MCRR,
|
|
ARMDEC_MOV_IMM,
|
|
ARMDEC_MOV_ISHF,
|
|
ARMDEC_MOV_RSHF,
|
|
ARMDEC_MRC,
|
|
ARMDEC_MRRC,
|
|
ARMDEC_MRS,
|
|
ARMDEC_MSR_IMM,
|
|
ARMDEC_MSR_REG,
|
|
ARMDEC_MUL,
|
|
ARMDEC_QADD,
|
|
ARMDEC_SMLA,
|
|
ARMDEC_SMLAL,
|
|
ARMDEC_SMUL,
|
|
ARMDEC_SWI,
|
|
ARMDEC_SWP,
|
|
ARMDEC_UNDEF,
|
|
};
|
|
|
|
struct ArmDecode
|
|
{
|
|
ULONG Mask;
|
|
ULONG Encoded;
|
|
ARM_DECODE Op;
|
|
PSTR Name;
|
|
};
|
|
|
|
#define ARTH_OP(Op, Name) \
|
|
0xffe00000, 0x02000000 | ((Op) << 21), ARMDEC_ARTH_IMM, Name, \
|
|
0xffe00010, 0x00000000 | ((Op) << 21), ARMDEC_ARTH_ISHF, Name, \
|
|
0xffe00090, 0x00000010 | ((Op) << 21), ARMDEC_ARTH_RSHF, Name
|
|
|
|
#define MOV_OP(Op, Name) \
|
|
0xffef0000, 0x02000000 | ((Op) << 21), ARMDEC_MOV_IMM, Name, \
|
|
0xffef0010, 0x00000000 | ((Op) << 21), ARMDEC_MOV_ISHF, Name, \
|
|
0xffef0090, 0x00000010 | ((Op) << 21), ARMDEC_MOV_RSHF, Name
|
|
|
|
#define CMP_OP(Op, Name) \
|
|
0xfff0f000, 0x02100000 | ((Op) << 21), ARMDEC_CMP_IMM, Name, \
|
|
0xfff0f010, 0x00100000 | ((Op) << 21), ARMDEC_CMP_ISHF, Name, \
|
|
0xfff0f090, 0x00100010 | ((Op) << 21), ARMDEC_CMP_RSHF, Name
|
|
|
|
#define LDC_OP(Op, Name) \
|
|
0xfe100000, 0x0c000000 | ((Op) << 20), ARMDEC_LDC, Name, \
|
|
0xfe100000, 0xfc000000 | ((Op) << 20), ARMDEC_LDC, Name ## "2"
|
|
|
|
#define LDM_OP(Op, Name) \
|
|
0xfe500000, 0x08000000 | ((Op) << 20), ARMDEC_LDM, Name, \
|
|
0xfe700000, 0x08400000 | ((Op) << 20), ARMDEC_LDM, Name
|
|
|
|
#define LDR_OP(Op, Name) \
|
|
0xfe100000, 0x04000000 | ((Op) << 20), ARMDEC_LDR_IMM, Name, \
|
|
0xfe100000, 0x06000000 | ((Op) << 20), ARMDEC_LDR_RSHF, Name
|
|
|
|
ArmDecode g_ArmDecode[] =
|
|
{
|
|
// Undefined instruction space.
|
|
0xf6000010, 0x06000010, ARMDEC_UNDEF, "???",
|
|
|
|
ARTH_OP(OP_ADC, "adc"),
|
|
ARTH_OP(OP_ADD, "add"),
|
|
ARTH_OP(OP_AND, "and"),
|
|
0xff000000, 0x0a000000, ARMDEC_BI, "b",
|
|
0xff000000, 0x0b000000, ARMDEC_BI, "bl",
|
|
ARTH_OP(OP_BIC, "bic"),
|
|
0xfff000f0, 0x01200070, ARMDEC_BKPT, "bkpt",
|
|
0xfe000000, 0xfa000000, ARMDEC_BXI, "blx",
|
|
0xfffffff0, 0x012fff30, ARMDEC_BXR, "blx",
|
|
0xfffffff0, 0x012fff10, ARMDEC_BXR, "bx",
|
|
0xff000010, 0x0e000000, ARMDEC_CDP, "cdp",
|
|
0xff000010, 0xfe000000, ARMDEC_CDP, "cdp2",
|
|
0xffff0ff0, 0x016f0f10, ARMDEC_CLZ, "clz",
|
|
CMP_OP(OP_CMN, "cmn"),
|
|
CMP_OP(OP_CMP, "cmp"),
|
|
ARTH_OP(OP_EOR, "eor"),
|
|
LDC_OP(1, "ldc"),
|
|
LDM_OP(1, "ldm"),
|
|
LDR_OP(1, "ldr"),
|
|
0xfe5000f0, 0x004000d0, ARMDEC_LDRH_IMM, "ldr",
|
|
0xfe500ff0, 0x000000d0, ARMDEC_LDRH_REG, "ldr",
|
|
0xfe5000f0, 0x005000b0, ARMDEC_LDRH_IMM, "ldr",
|
|
0xfe500ff0, 0x001000b0, ARMDEC_LDRH_REG, "ldr",
|
|
0xfe5000f0, 0x005000d0, ARMDEC_LDRH_IMM, "ldr",
|
|
0xfe500ff0, 0x001000d0, ARMDEC_LDRH_REG, "ldr",
|
|
0xfe5000f0, 0x005000f0, ARMDEC_LDRH_IMM, "ldr",
|
|
0xfe500ff0, 0x001000f0, ARMDEC_LDRH_REG, "ldr",
|
|
0xff100010, 0x0e000010, ARMDEC_MCR, "mcr",
|
|
0xff100010, 0xfe000010, ARMDEC_MCR, "mcr2",
|
|
0xfff00000, 0x0e400000, ARMDEC_MCRR, "mcrr",
|
|
0xffe000f0, 0x00200090, ARMDEC_MUL, "mla",
|
|
MOV_OP(OP_MOV, "mov"),
|
|
0xff100010, 0x0e100010, ARMDEC_MRC, "mrc",
|
|
0xff100010, 0xfe100010, ARMDEC_MRC, "mrc2",
|
|
0xfff00000, 0x0e500000, ARMDEC_MRRC, "mrrc",
|
|
0xffbf0fff, 0x010f0000, ARMDEC_MRS, "mrs",
|
|
0xffb0f000, 0x0320f000, ARMDEC_MSR_IMM, "msr",
|
|
0xffb0f0f0, 0x0120f000, ARMDEC_MSR_REG, "msr",
|
|
0xffe0f0f0, 0x00000090, ARMDEC_MUL, "mul",
|
|
MOV_OP(OP_MVN, "mvn"),
|
|
ARTH_OP(OP_ORR, "orr"),
|
|
ARTH_OP(OP_RSB, "rsb"),
|
|
ARTH_OP(OP_RSC, "rsc"),
|
|
ARTH_OP(OP_SBC, "sbc"),
|
|
0xfff00090, 0x01000080, ARMDEC_SMLA, "smla",
|
|
0xfff00090, 0x01400080, ARMDEC_SMLAL, "smlal",
|
|
0xffe000f0, 0x00e00090, ARMDEC_MUL, "smlal",
|
|
0xfff000b0, 0x01200080, ARMDEC_SMLA, "smlaw",
|
|
0xfff0f090, 0x01600080, ARMDEC_SMUL, "smulb",
|
|
0xffe000f0, 0x00c00090, ARMDEC_MUL, "smull",
|
|
0xfff0f0b0, 0x016000a0, ARMDEC_SMUL, "smulw",
|
|
0xff70f000, 0xf750f000, ARMDEC_LDR_IMM, "pld",
|
|
0xff70f000, 0xf550f000, ARMDEC_LDR_RSHF, "pld",
|
|
0xfff00ff0, 0x01000050, ARMDEC_QADD, "qadd",
|
|
0xfff00ff0, 0x01400050, ARMDEC_QADD, "qdadd",
|
|
0xfff00ff0, 0x01600050, ARMDEC_QADD, "qdsub",
|
|
0xfff00ff0, 0x01200050, ARMDEC_QADD, "qsub",
|
|
LDC_OP(0, "stc"),
|
|
LDM_OP(0, "stm"),
|
|
LDR_OP(0, "str"),
|
|
0xfe5000f0, 0x004000f0, ARMDEC_LDRH_IMM, "str",
|
|
0xfe500ff0, 0x000000f0, ARMDEC_LDRH_REG, "str",
|
|
0xfe5000f0, 0x004000b0, ARMDEC_LDRH_IMM, "str",
|
|
0xfe500ff0, 0x000000b0, ARMDEC_LDRH_REG, "str",
|
|
ARTH_OP(OP_SUB, "sub"),
|
|
0xff000000, 0x0f000000, ARMDEC_SWI, "swi",
|
|
0xffb00ff0, 0x01000090, ARMDEC_SWP, "swp",
|
|
CMP_OP(OP_TEQ, "teq"),
|
|
CMP_OP(OP_TST, "tst"),
|
|
0xffe000f0, 0x00a00090, ARMDEC_MUL, "umlal",
|
|
0xffe000f0, 0x00800090, ARMDEC_MUL, "umull",
|
|
|
|
// End marker.
|
|
0x00000000, 0x00000000, ARMDEC_INVALID, NULL,
|
|
};
|
|
|
|
BOOL
|
|
ArmMachineInfo::Disassemble(ProcessInfo* Process,
|
|
PADDR Addr, PSTR Buffer, BOOL EffAddr)
|
|
{
|
|
ARMI Instr;
|
|
|
|
m_DisStart = *Addr;
|
|
|
|
ADDRFLAT(&m_EffAddr, 0);
|
|
m_EaSize = 0;
|
|
|
|
m_BufStart = m_Buf = Buffer;
|
|
sprintAddr(&m_Buf, Addr);
|
|
*m_Buf++ = ' ';
|
|
|
|
if (m_Target->
|
|
ReadAllVirtual(Process, Flat(*Addr), &Instr, sizeof(Instr)) != S_OK)
|
|
{
|
|
AddrAdd(Addr, 4);
|
|
if (!(g_AsmOptions & DEBUG_ASMOPT_NO_CODE_BYTES))
|
|
{
|
|
BufferString("???????? ");
|
|
}
|
|
BufferString("???\n");
|
|
*m_Buf = 0;
|
|
return FALSE;
|
|
}
|
|
|
|
m_ArgCol = 19;
|
|
|
|
if (!(g_AsmOptions & DEBUG_ASMOPT_NO_CODE_BYTES))
|
|
{
|
|
BufferHex(Instr.instruction, 8, FALSE);
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = ' ';
|
|
m_ArgCol += 10;
|
|
}
|
|
|
|
//
|
|
// All of the condition bits are AND'ed before being
|
|
// used to search the decode table. This allows simple
|
|
// matching and filtering for unconditional instructions.
|
|
//
|
|
|
|
ULONG InstrBits = Instr.instruction;
|
|
if ((InstrBits & COND_MASK) != COND_NV)
|
|
{
|
|
InstrBits &= ~COND_MASK;
|
|
}
|
|
|
|
ArmDecode* Decode = g_ArmDecode;
|
|
while (Decode->Mask)
|
|
{
|
|
if ((InstrBits & Decode->Mask) == Decode->Encoded)
|
|
{
|
|
break;
|
|
}
|
|
|
|
Decode++;
|
|
}
|
|
|
|
if (!Decode->Mask || Decode->Op == ARMDEC_UNDEF)
|
|
{
|
|
BufferString("???");
|
|
}
|
|
else
|
|
{
|
|
BufferString(Decode->Name);
|
|
BufferCond(Instr.instruction & COND_MASK);
|
|
|
|
switch(Decode->Op)
|
|
{
|
|
case ARMDEC_ARTH_IMM:
|
|
case ARMDEC_ARTH_ISHF:
|
|
case ARMDEC_ARTH_RSHF:
|
|
DisArmArth(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_BI:
|
|
DisArmBi(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_BKPT:
|
|
DisArmBkpt(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_BXI:
|
|
DisArmBxi(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_BXR:
|
|
DisArmBxr(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_CDP:
|
|
DisArmCdp(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_CLZ:
|
|
DisArmClz(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_CMP_IMM:
|
|
case ARMDEC_CMP_ISHF:
|
|
case ARMDEC_CMP_RSHF:
|
|
DisArmCmp(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_LDC:
|
|
DisArmLdc(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_LDM:
|
|
DisArmLdm(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_LDR_IMM:
|
|
case ARMDEC_LDR_RSHF:
|
|
DisArmLdr(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_LDRH_IMM:
|
|
case ARMDEC_LDRH_REG:
|
|
DisArmLdrh(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_MCR:
|
|
case ARMDEC_MRC:
|
|
DisArmMcr(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_MCRR:
|
|
case ARMDEC_MRRC:
|
|
DisArmMcrr(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_MOV_IMM:
|
|
case ARMDEC_MOV_ISHF:
|
|
case ARMDEC_MOV_RSHF:
|
|
DisArmMov(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_MRS:
|
|
DisArmMrs(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_MSR_IMM:
|
|
case ARMDEC_MSR_REG:
|
|
DisArmMsr(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_MUL:
|
|
DisArmMul(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_QADD:
|
|
DisArmQadd(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_SMLA:
|
|
DisArmSmla(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_SMLAL:
|
|
DisArmSmlal(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_SMUL:
|
|
DisArmSmul(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_SWI:
|
|
DisArmSwi(Decode, &Instr);
|
|
break;
|
|
case ARMDEC_SWP:
|
|
DisArmSwp(Decode, &Instr);
|
|
break;
|
|
default:
|
|
BufferString("** ARM diassembly bug **");
|
|
break;
|
|
}
|
|
}
|
|
|
|
*m_Buf++ = '\n';
|
|
*m_Buf = 0;
|
|
AddrAdd(Addr, 4);
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmArth(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
if (Instr->dataproc.s)
|
|
{
|
|
*m_Buf++ = 's';
|
|
}
|
|
BufferBlanks(m_ArgCol);
|
|
BufferRegName(Instr->dataproc.rd);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->dataproc.rn);
|
|
BufferArmDpArg(Instr);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmBi(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
BufferBlanks(m_ArgCol);
|
|
BufferEffectiveAddress(Flat(m_DisStart) + 8 +
|
|
((LONG)Instr->bl.offset << 2), 4);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmBkpt(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
BufferBlanks(m_ArgCol);
|
|
*m_Buf++ = '#';
|
|
*m_Buf++ = '0';
|
|
*m_Buf++ = 'x';
|
|
BufferHex((Instr->bkpt.immed2 << 4) || Instr->bkpt.immed1,
|
|
4, FALSE);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmBxi(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
BufferBlanks(m_ArgCol);
|
|
BufferEffectiveAddress(Flat(m_DisStart) + 8 +
|
|
((LONG)Instr->blxi.offset << 2) +
|
|
(Instr->blxi.h << 1), 4);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmBxr(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
BufferBlanks(m_ArgCol);
|
|
BufferRegName(Instr->bx.rn);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmCdp(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
BufferBlanks(m_ArgCol);
|
|
*m_Buf++ = 'p';
|
|
BufferInt(Instr->cpdo.cpn, 0, FALSE);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferInt(Instr->cpdo.cpop, 0, FALSE);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = 'c';
|
|
BufferInt(Instr->cpdo.crd, 0, FALSE);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = 'c';
|
|
BufferInt(Instr->cpdo.crn, 0, FALSE);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = 'c';
|
|
BufferInt(Instr->cpdo.crm, 0, FALSE);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferInt(Instr->cpdo.cp, 0, FALSE);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmClz(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
BufferBlanks(m_ArgCol);
|
|
BufferRegName(Instr->clz.rd);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->clz.rm);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmCmp(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
BufferBlanks(m_ArgCol);
|
|
BufferRegName(Instr->dataproc.rn);
|
|
BufferArmDpArg(Instr);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmLdc(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
if (Instr->cpdt.n)
|
|
{
|
|
*m_Buf++ = 'l';
|
|
}
|
|
BufferBlanks(m_ArgCol);
|
|
*m_Buf++ = 'p';
|
|
BufferInt(Instr->cpdt.cpn, 0, FALSE);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = 'c';
|
|
BufferInt(Instr->cpdt.crd, 0, FALSE);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = '[';
|
|
BufferRegName(Instr->cpdt.rn);
|
|
if (!Instr->cpdt.p)
|
|
{
|
|
*m_Buf++ = ']';
|
|
}
|
|
if (Instr->cpdt.offset)
|
|
{
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = '#';
|
|
*m_Buf++ = '0';
|
|
*m_Buf++ = 'x';
|
|
BufferHex(Instr->cpdt.offset, 2, FALSE);
|
|
}
|
|
if (Instr->cpdt.p)
|
|
{
|
|
*m_Buf++ = ']';
|
|
}
|
|
if (Instr->cpdt.w)
|
|
{
|
|
*m_Buf++ = '!';
|
|
}
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmLdm(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
ULONG i;
|
|
BOOL Separate = FALSE;
|
|
|
|
if (Instr->ldm.rn == ARM_SP)
|
|
{
|
|
*m_Buf++ = Instr->ldm.p ? 'e' : 'f';
|
|
*m_Buf++ = Instr->ldm.u ? 'd' : 'a';
|
|
}
|
|
else
|
|
{
|
|
*m_Buf++ = Instr->ldm.u ? 'i' : 'd';
|
|
*m_Buf++ = Instr->ldm.p ? 'b' : 'a';
|
|
}
|
|
BufferBlanks(m_ArgCol);
|
|
BufferRegName(Instr->ldm.rn);
|
|
if (Instr->ldm.w)
|
|
{
|
|
*m_Buf++ = '!';
|
|
}
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = '{';
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
if (Instr->ldm.reglist & (1 << i))
|
|
{
|
|
ULONG Len = 0;
|
|
|
|
if (Separate)
|
|
{
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
}
|
|
else
|
|
{
|
|
Separate = TRUE;
|
|
}
|
|
|
|
BufferRegName(i);
|
|
|
|
do
|
|
{
|
|
Len++;
|
|
i++;
|
|
} while (i < 16 && (Instr->ldm.reglist & (1 << i)));
|
|
|
|
if (Len > 1)
|
|
{
|
|
if (Len > 2)
|
|
{
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = '-';
|
|
}
|
|
else
|
|
{
|
|
*m_Buf++ = ',';
|
|
}
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(i - 1);
|
|
}
|
|
}
|
|
}
|
|
*m_Buf++ = '}';
|
|
if (Instr->ldm.s)
|
|
{
|
|
*m_Buf++ = '^';
|
|
}
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmLdr(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
if (Instr->ldr.b)
|
|
{
|
|
*m_Buf++ = 'b';
|
|
}
|
|
if (!Instr->ldr.p && Instr->ldr.w)
|
|
{
|
|
*m_Buf++ = 't';
|
|
}
|
|
BufferBlanks(m_ArgCol);
|
|
BufferRegName(Instr->ldr.rd);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = '[';
|
|
BufferRegName(Instr->ldr.rn);
|
|
if (Instr->ldr.p)
|
|
{
|
|
if (Instr->ldr.offset)
|
|
{
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
if (!Instr->ldr.i)
|
|
{
|
|
*m_Buf++ = '#';
|
|
if (!Instr->ldr.u)
|
|
{
|
|
*m_Buf++ = '-';
|
|
}
|
|
*m_Buf++ = '0';
|
|
*m_Buf++ = 'x';
|
|
BufferHex(Instr->ldr.offset, 3, FALSE);
|
|
}
|
|
else
|
|
{
|
|
*m_Buf++ = Instr->ldr.u ? '+' : '-';
|
|
BufferArmShift(Instr->ldr.offset);
|
|
}
|
|
}
|
|
*m_Buf++ = ']';
|
|
}
|
|
else
|
|
{
|
|
*m_Buf++ = ']';
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
if (!Instr->ldr.i)
|
|
{
|
|
*m_Buf++ = '#';
|
|
if (!Instr->ldr.u)
|
|
{
|
|
*m_Buf++ = '-';
|
|
}
|
|
*m_Buf++ = '0';
|
|
*m_Buf++ = 'x';
|
|
BufferHex(Instr->ldr.offset, 3, FALSE);
|
|
}
|
|
else
|
|
{
|
|
*m_Buf++ = Instr->ldr.u ? '+' : '-';
|
|
BufferArmShift(Instr->ldr.offset);
|
|
}
|
|
}
|
|
if (Instr->ldr.w)
|
|
{
|
|
*m_Buf++ = '!';
|
|
}
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmLdrh(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
if (!Instr->miscdt.l && Instr->miscdt.s)
|
|
{
|
|
*m_Buf++ = 'd';
|
|
}
|
|
else
|
|
{
|
|
if (Instr->miscdt.s)
|
|
{
|
|
*m_Buf++ = 's';
|
|
}
|
|
*m_Buf++ = Instr->miscdt.h ? 'h' : 'b';
|
|
}
|
|
BufferBlanks(m_ArgCol);
|
|
BufferRegName(Instr->miscdt.rd);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = '[';
|
|
BufferRegName(Instr->miscdt.rn);
|
|
if (!Instr->miscdt.p)
|
|
{
|
|
*m_Buf++ = ']';
|
|
}
|
|
if (Instr->miscdt.i)
|
|
{
|
|
ULONG Offset =
|
|
(Instr->miscdt.operand2 << 4) | Instr->miscdt.operand1;
|
|
if (Offset)
|
|
{
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = '#';
|
|
if (!Instr->miscdt.u)
|
|
{
|
|
*m_Buf++ = '-';
|
|
}
|
|
*m_Buf++ = '0';
|
|
*m_Buf++ = 'x';
|
|
BufferHex(Offset, 2, FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = Instr->miscdt.u ? '+' : '-';
|
|
BufferRegName(Instr->miscdt.operand1);
|
|
}
|
|
if (Instr->miscdt.p)
|
|
{
|
|
*m_Buf++ = ']';
|
|
if (Instr->miscdt.w)
|
|
{
|
|
*m_Buf++ = '!';
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmMcr(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
BufferBlanks(m_ArgCol);
|
|
*m_Buf++ = 'p';
|
|
BufferInt(Instr->cprt.cpn, 0, FALSE);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferInt(Instr->cprt.cpop, 0, FALSE);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->cprt.rd);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = 'c';
|
|
BufferInt(Instr->cprt.crn, 0, FALSE);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = 'c';
|
|
BufferInt(Instr->cprt.crm, 0, FALSE);
|
|
if (Instr->cprt.cp)
|
|
{
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferInt(Instr->cprt.cp, 0, FALSE);
|
|
}
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmMcrr(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
BufferBlanks(m_ArgCol);
|
|
*m_Buf++ = 'p';
|
|
BufferInt(Instr->mcrr.cpn, 0, FALSE);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferInt(Instr->mcrr.cpop, 0, FALSE);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->mcrr.rd);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferInt(Instr->mcrr.rn, 0, FALSE);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = 'c';
|
|
BufferInt(Instr->mcrr.crm, 0, FALSE);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmMov(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
if (Instr->dataproc.s)
|
|
{
|
|
*m_Buf++ = 's';
|
|
}
|
|
BufferBlanks(m_ArgCol);
|
|
BufferRegName(Instr->dataproc.rd);
|
|
BufferArmDpArg(Instr);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmMrs(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
BufferBlanks(m_ArgCol);
|
|
BufferRegName(Instr->dpmrs.rd);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferString("psr");
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmMsr(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
BufferBlanks(m_ArgCol);
|
|
BufferString("psr_");
|
|
if (Instr->dpmsr.fc)
|
|
{
|
|
*m_Buf++ = 'c';
|
|
}
|
|
if (Instr->dpmsr.fx)
|
|
{
|
|
*m_Buf++ = 'x';
|
|
}
|
|
if (Instr->dpmsr.fs)
|
|
{
|
|
*m_Buf++ = 's';
|
|
}
|
|
if (Instr->dpmsr.ff)
|
|
{
|
|
*m_Buf++ = 'f';
|
|
}
|
|
if (Instr->dpmsr.i)
|
|
{
|
|
BufferArmDpArg(Instr);
|
|
}
|
|
else
|
|
{
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->dpmsr.operand & 0xf);
|
|
}
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmMul(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
if (Instr->mul.s)
|
|
{
|
|
*m_Buf++ = 's';
|
|
}
|
|
BufferBlanks(m_ArgCol);
|
|
if (Instr->mul.lng)
|
|
{
|
|
BufferRegName(Instr->mul.rn);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
}
|
|
BufferRegName(Instr->mul.rd);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->mul.rm);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->mul.rs);
|
|
if (Instr->mul.a && !Instr->mul.lng)
|
|
{
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->mul.rn);
|
|
}
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmQadd(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
BufferBlanks(m_ArgCol);
|
|
BufferRegName(Instr->qadd.rd);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->qadd.rm);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->qadd.rn);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmSmla(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
if (strcmp(Decode->Name, "smlaw"))
|
|
{
|
|
*m_Buf++ = Instr->smla.x ? 't' : 'b';
|
|
}
|
|
*m_Buf++ = Instr->smla.y ? 't' : 'b';
|
|
BufferBlanks(m_ArgCol);
|
|
BufferRegName(Instr->smla.rd);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->smla.rm);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->smla.rs);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->smla.rn);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmSmlal(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
*m_Buf++ = Instr->smla.x ? 't' : 'b';
|
|
*m_Buf++ = Instr->smla.y ? 't' : 'b';
|
|
BufferBlanks(m_ArgCol);
|
|
BufferRegName(Instr->smlal.rdlo);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->smlal.rdhi);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->smlal.rm);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->smlal.rs);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmSmul(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
if (strcmp(Decode->Name, "smulw"))
|
|
{
|
|
*m_Buf++ = Instr->smul.x ? 't' : 'b';
|
|
}
|
|
*m_Buf++ = Instr->smul.y ? 't' : 'b';
|
|
BufferBlanks(m_ArgCol);
|
|
BufferRegName(Instr->smul.rd);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->smul.rm);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->smlal.rs);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmSwi(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
BufferBlanks(m_ArgCol);
|
|
*m_Buf++ = '#';
|
|
*m_Buf++ = '0';
|
|
*m_Buf++ = 'x';
|
|
BufferHex(Instr->swi.comment, 6, FALSE);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DisArmSwp(ArmDecode* Decode, PARMI Instr)
|
|
{
|
|
if (Instr->swp.b)
|
|
{
|
|
*m_Buf++ = 'b';
|
|
}
|
|
BufferBlanks(m_ArgCol);
|
|
BufferRegName(Instr->swp.rd);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferRegName(Instr->swp.rm);
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = '[';
|
|
BufferRegName(Instr->swp.rn);
|
|
*m_Buf++ = ']';
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::BufferEffectiveAddress(ULONG64 Offset, ULONG Size)
|
|
{
|
|
CHAR Symbol[MAX_SYMBOL_LEN];
|
|
ULONG64 Disp;
|
|
|
|
GetSymbol(Offset, Symbol, sizeof(Symbol), &Disp);
|
|
if (Symbol[0])
|
|
{
|
|
BufferString(Symbol);
|
|
if (Disp)
|
|
{
|
|
*m_Buf++ = '+';
|
|
*m_Buf++ = '0';
|
|
*m_Buf++ = 'x';
|
|
BufferHex(Disp, 8, TRUE);
|
|
}
|
|
*m_Buf++ = ' ';
|
|
*m_Buf++ = '(';
|
|
BufferHex(Offset, 8, FALSE);
|
|
*m_Buf++ = ')';
|
|
}
|
|
else
|
|
{
|
|
BufferHex(Offset, 8, FALSE);
|
|
}
|
|
|
|
// Save EA.
|
|
ADDRFLAT(&m_EffAddr, Offset);
|
|
m_EaSize = Size;
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::BufferArmDpArg(PARMI Instr)
|
|
{
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
if (Instr->dataproc.bits & 1)
|
|
{
|
|
ULONG64 Immed;
|
|
|
|
*m_Buf++ = '#';
|
|
*m_Buf++ = '0';
|
|
*m_Buf++ = 'x';
|
|
// The immediate forms takes the low 8 bits as an unsigned
|
|
// value and rotates it right by twice the upper 4 bits to
|
|
// form a 32-bit immediate.
|
|
// Emulate the rotate by shifting a 64-bit value and piecing
|
|
// together the appropriate parts.
|
|
Immed = (((ULONG64)(Instr->dataproc.operand2 & 0xff)) << 32) >>
|
|
((Instr->dataproc.operand2 >> 7) & 0x1e);
|
|
BufferHex((ULONG)((Immed & 0xffffffff) | (Immed >> 32)), 8, FALSE);
|
|
}
|
|
else
|
|
{
|
|
BufferArmShift(Instr->dataproc.operand2);
|
|
}
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::BufferArmShift(ULONG Shift)
|
|
{
|
|
ULONG Op = (Shift >> 5) & 3;
|
|
ULONG Amount = (Shift >> 7) & 0x1f;
|
|
|
|
if (Op == 3 && !(Shift & 0x10) && Amount == 0)
|
|
{
|
|
// ror #0 is replaced by rrx.
|
|
Op = 4;
|
|
}
|
|
|
|
BufferRegName(Shift & 0xf);
|
|
|
|
if (Op == 0 && Amount == 0)
|
|
{
|
|
// No shift.
|
|
return;
|
|
}
|
|
|
|
*m_Buf++ = ',';
|
|
*m_Buf++ = ' ';
|
|
BufferString(g_ArmShift[Op]);
|
|
*m_Buf++ = ' ';
|
|
if (Shift & 0x10)
|
|
{
|
|
DBG_ASSERT(!(Shift & 0x80));
|
|
BufferRegName((Shift >> 8) & 0xf);
|
|
}
|
|
else
|
|
{
|
|
if ((Op == 1 || Op == 2) && Amount == 0)
|
|
{
|
|
// lsr #0 and asr #0 are actually [la]sr #32.
|
|
Amount = 32;
|
|
}
|
|
|
|
*m_Buf++ = '#';
|
|
BufferInt(Amount, 0, FALSE);
|
|
}
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::BufferRegName(ULONG Reg)
|
|
{
|
|
PCSTR Name = RegNameFromIndex(Reg + ARM_INT_FIRST);
|
|
if (Name)
|
|
{
|
|
BufferString(Name);
|
|
}
|
|
else
|
|
{
|
|
BufferString("ArmMachineInfo::BufferRegName invalid arg");
|
|
}
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::BufferCond(ULONG Cond)
|
|
{
|
|
if (Cond != COND_AL && Cond != COND_NV)
|
|
{
|
|
BufferString(g_ArmCond[Cond >> COND_SHIFT]);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
ArmMachineInfo::IsBreakpointInstruction(ProcessInfo* Process, PADDR Addr)
|
|
{
|
|
// XXX drewb - Presumably some form of BKPT, but what?
|
|
return FALSE;
|
|
}
|
|
|
|
HRESULT
|
|
ArmMachineInfo::InsertBreakpointInstruction(PUSER_DEBUG_SERVICES Services,
|
|
ULONG64 Process,
|
|
ULONG64 Offset,
|
|
ULONG Flags,
|
|
PUCHAR SaveInstr,
|
|
PULONG64 ChangeStart,
|
|
PULONG ChangeLen)
|
|
{
|
|
if (Flags != IBI_DEFAULT)
|
|
{
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
HRESULT
|
|
ArmMachineInfo::RemoveBreakpointInstruction(PUSER_DEBUG_SERVICES Services,
|
|
ULONG64 Process,
|
|
ULONG64 Offset,
|
|
PUCHAR SaveInstr,
|
|
PULONG64 ChangeStart,
|
|
PULONG ChangeLen)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::AdjustPCPastBreakpointInstruction(PADDR Addr,
|
|
ULONG BreakType)
|
|
{
|
|
DBG_ASSERT(BreakType == DEBUG_BREAKPOINT_CODE);
|
|
|
|
AddrAdd(Addr, 4);
|
|
SetPC(Addr);
|
|
}
|
|
|
|
BOOL
|
|
ArmMachineInfo::IsCallDisasm(PCSTR Disasm)
|
|
{
|
|
// XXX.
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
ArmMachineInfo::IsReturnDisasm(PCSTR Disasm)
|
|
{
|
|
// XXX.
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
ArmMachineInfo::IsSystemCallDisasm(PCSTR Disasm)
|
|
{
|
|
// XXX.
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL
|
|
ArmMachineInfo::IsDelayInstruction(PADDR Addr)
|
|
{
|
|
// ARM does not have delay slots.
|
|
return FALSE;
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::GetEffectiveAddr(PADDR Addr, PULONG Size)
|
|
{
|
|
*Addr = m_EffAddr;
|
|
*Size = m_EaSize;
|
|
}
|
|
|
|
BOOL
|
|
ArmCheckConditionCodes(
|
|
PARM_CONTEXT Context,
|
|
DWORD instr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks the condition codes of the instruction and the values of the
|
|
condition flags in the current program status register, and determines
|
|
whether or not the instruction will be executed.
|
|
|
|
Return Value:
|
|
|
|
TRUE if the instruction will be executed, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
BOOL Execute = FALSE;
|
|
BOOL Nset = (Context->Psr & 0x80000000L) == 0x80000000L;
|
|
BOOL Zset = (Context->Psr & 0x40000000L) == 0x40000000L;
|
|
BOOL Cset = (Context->Psr & 0x20000000L) == 0x20000000L;
|
|
BOOL Vset = (Context->Psr & 0x10000000L) == 0x10000000L;
|
|
|
|
instr &= COND_MASK;
|
|
|
|
switch( instr )
|
|
{
|
|
case COND_EQ: // Z set
|
|
if ( Zset ) Execute = TRUE;
|
|
break;
|
|
|
|
case COND_NE: // Z clear
|
|
if ( !Zset ) Execute = TRUE;
|
|
break;
|
|
|
|
case COND_CS: // C set
|
|
if ( Cset ) Execute = TRUE;
|
|
break;
|
|
|
|
case COND_CC: // C clear
|
|
if ( !Cset ) Execute = TRUE;
|
|
break;
|
|
|
|
case COND_MI: // N set
|
|
if ( Nset ) Execute = TRUE;
|
|
break;
|
|
|
|
case COND_PL: // N clear
|
|
if ( !Nset ) Execute = TRUE;
|
|
break;
|
|
|
|
case COND_VS: // V set
|
|
if ( Vset ) Execute = TRUE;
|
|
break;
|
|
|
|
case COND_VC: // V clear
|
|
if ( !Vset ) Execute = TRUE;
|
|
break;
|
|
|
|
case COND_HI: // C set and Z clear
|
|
if ( Cset && !Zset ) Execute = TRUE;
|
|
break;
|
|
|
|
case COND_LS: // C clear or Z set
|
|
if ( !Cset || Zset ) Execute = TRUE;
|
|
break;
|
|
|
|
case COND_GE: // N == V
|
|
if (( Nset && Vset ) || ( !Nset && !Vset )) Execute = TRUE;
|
|
break;
|
|
|
|
case COND_LT: // N != V
|
|
if (( Nset && !Vset ) || ( !Nset && Vset )) Execute = TRUE;
|
|
break;
|
|
|
|
case COND_GT: // Z clear, and N == V
|
|
if ( !Zset &&
|
|
(( Nset && Vset ) || ( !Nset && !Vset ))) Execute = TRUE;
|
|
break;
|
|
|
|
case COND_LE: // Z set, and N != V
|
|
if ( Zset &&
|
|
(( Nset && !Vset ) || ( !Nset && Vset ))) Execute = TRUE;
|
|
break;
|
|
|
|
case COND_AL: // Always execute
|
|
case COND_NV:
|
|
Execute = TRUE;
|
|
break;
|
|
|
|
default:
|
|
DBG_ASSERT(FALSE);
|
|
break;
|
|
}
|
|
|
|
return Execute;
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::GetNextOffset(ProcessInfo* Process,
|
|
BOOL StepOver,
|
|
PADDR NextAddr, PULONG NextMachine)
|
|
{
|
|
ARMI instr;
|
|
PULONG Register = &m_Context.ArmContext.R0;
|
|
ULONG returnvalue;
|
|
BOOL QualifyReturnAddress = FALSE; // ADDED for ARM WINCE, fixes up LR
|
|
BOOL Ldm_instr = FALSE;
|
|
|
|
*NextMachine = m_ExecTypes[0];
|
|
|
|
GetPC(NextAddr);
|
|
|
|
if (m_Target->ReadAllVirtual(Process, Flat(*NextAddr), &instr.instruction,
|
|
sizeof(instr.instruction)) != S_OK)
|
|
{
|
|
// Couldn't read the instruction so just return the
|
|
// next offset.
|
|
AddrAdd(NextAddr, 4);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We are only testing for data processing, load multiple, bx and bl
|
|
// instructions. We might have to check regular loads and stores that
|
|
// have the PC as the destination.
|
|
//
|
|
|
|
if (!ArmCheckConditionCodes(&m_Context.ArmContext, instr.instruction))
|
|
{
|
|
//
|
|
// Instruction will not be executed. Bump PC normally.
|
|
//
|
|
|
|
AddrAdd(NextAddr, 4);
|
|
return;
|
|
}
|
|
|
|
if (( instr.instruction & BX_MASK ) == BX_INSTR )
|
|
{
|
|
ULONG Rn;
|
|
|
|
//
|
|
// Check Rn (lower 4 bits). To compute the target address:
|
|
// Mask out the T bit. We don't care if we're transferring to Thumb
|
|
// Shift bits 31-1 left 1 bit.
|
|
//
|
|
|
|
Rn = Register[ instr.bx.rn ];
|
|
Rn &= 0xfffffffe;
|
|
returnvalue = Rn << 1;
|
|
}
|
|
else if (( instr.instruction & DATA_PROC_MASK ) == DP_PC_INSTR )
|
|
{
|
|
ULONG Op1, Op2;
|
|
ULONG Cflag = (m_Context.ArmContext.Psr & 0x20000000L) == 0x20000000L;
|
|
ULONG shift;
|
|
|
|
//
|
|
// We are not making sure that data processing instructions are not the
|
|
// multiply instructions, because we are checking to make sure that the
|
|
// PC is the destination register. The PC is not a legal destination
|
|
// register on multiply instructions.
|
|
//
|
|
// Currently only the MOV instruction (returns, branches) and the ADDLS
|
|
// instruction (switch statement) are used. Both of these instructions
|
|
// use the addressing mode "Register, Logical shift left by immediate."
|
|
// I'm leaving the other cases in case they are used in the future.
|
|
//
|
|
|
|
//
|
|
// Figure out the addressing mode (there are 11 of them), and get the
|
|
// operands.
|
|
//
|
|
|
|
Op1 = Register[ instr.dataproc.rn ];
|
|
|
|
if ( instr.dataproc.rn == 15 )
|
|
{
|
|
//
|
|
// If this is the PC, add 8.
|
|
//
|
|
|
|
Op1 += 8;
|
|
}
|
|
|
|
if ( instr.dataproc.bits == 0x1 )
|
|
{
|
|
//
|
|
// Immediate addressing - Type 1
|
|
//
|
|
|
|
Op2 = _lrotr( instr.dpi.immediate, instr.dpi.rotate * 2 );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Register addressing - start by getting the value of Rm.
|
|
//
|
|
|
|
Op2 = Register[ instr.dpshi.rm ];
|
|
|
|
if ( instr.dpshi.rm == 15 )
|
|
{
|
|
//
|
|
// If this is the PC, add 8.
|
|
//
|
|
|
|
Op2 += 8;
|
|
}
|
|
|
|
if ( instr.dprre.bits == 0x6 )
|
|
{
|
|
//
|
|
// Rotate right with extended - Type 11
|
|
//
|
|
|
|
Op2 = ( Cflag << 31 ) | ( Op2 >> 1 );
|
|
}
|
|
else if ( instr.dataproc.operand2 & 0x10 )
|
|
{
|
|
//
|
|
// Register shifts. Types 4, 6, 8, and 10
|
|
//
|
|
|
|
//
|
|
// Get the shift value from the least-significant byte of the
|
|
// shift register.
|
|
//
|
|
|
|
shift = Register[ instr.dpshr.rs ];
|
|
|
|
shift &= 0xff;
|
|
|
|
switch( instr.dpshr.bits )
|
|
{
|
|
case 0x1: // 4 Logical shift left by register
|
|
if ( shift >= 32 )
|
|
{
|
|
Op2 = 0;
|
|
}
|
|
else
|
|
{
|
|
Op2 = Op2 << shift;
|
|
}
|
|
break;
|
|
|
|
case 0x3: // 6 Logical shift right by register
|
|
if ( shift >= 32 )
|
|
{
|
|
Op2 = 0;
|
|
}
|
|
else
|
|
{
|
|
Op2 = Op2 >> shift;
|
|
}
|
|
break;
|
|
|
|
case 0x5: // 8 Arithmetic shift right by register
|
|
if ( shift >= 32 )
|
|
{
|
|
if ( Op2 & 0x80000000 )
|
|
{
|
|
Op2 = 0xffffffff;
|
|
}
|
|
else
|
|
{
|
|
Op2 = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Op2 = (LONG)Op2 >> shift;
|
|
}
|
|
break;
|
|
|
|
case 0x7: // 10 Rotate right by register
|
|
if ( !( shift == 0 ) && !(( shift & 0xf ) == 0 ) )
|
|
{
|
|
Op2 = _lrotl( Op2, shift );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Immediate shifts. Types 2, 3, 5, 7, and 9
|
|
//
|
|
|
|
//
|
|
// Get the shift value from the instruction.
|
|
//
|
|
|
|
shift = instr.dpshi.shift;
|
|
|
|
switch( instr.dpshi.bits )
|
|
{
|
|
case 0x0: // 2,3 Register, Logical shift left by immediate
|
|
if ( shift != 0 )
|
|
{
|
|
Op2 = Op2 << shift;
|
|
}
|
|
break;
|
|
|
|
case 0x2: // 5 Logical shift right by immediate
|
|
if ( shift == 0 )
|
|
{
|
|
Op2 = 0;
|
|
}
|
|
else
|
|
{
|
|
Op2 = Op2 >> shift;
|
|
}
|
|
break;
|
|
|
|
case 0x4: // 7 Arithmetic shift right by immediate
|
|
if ( shift == 0 )
|
|
{
|
|
Op2 = 0;
|
|
}
|
|
else
|
|
{
|
|
Op2 = (LONG)Op2 >> shift;
|
|
}
|
|
break;
|
|
|
|
case 0x6: // 9 Rotate right by immediate
|
|
Op2 = _lrotl( Op2, shift );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine the result (the new PC), based on the opcode.
|
|
//
|
|
|
|
switch( instr.dataproc.opcode )
|
|
{
|
|
case OP_AND:
|
|
returnvalue = Op1 & Op2;
|
|
break;
|
|
|
|
case OP_EOR:
|
|
returnvalue = Op1 ^ Op2;
|
|
break;
|
|
|
|
case OP_SUB:
|
|
returnvalue = Op1 - Op2;
|
|
break;
|
|
|
|
case OP_RSB:
|
|
returnvalue = Op2 - Op1;
|
|
break;
|
|
|
|
case OP_ADD:
|
|
returnvalue = Op1 + Op2;
|
|
break;
|
|
|
|
case OP_ADC:
|
|
returnvalue = (Op1 + Op2) + Cflag;
|
|
break;
|
|
|
|
case OP_SBC:
|
|
returnvalue = (Op1 - Op2) - ~Cflag;
|
|
break;
|
|
|
|
case OP_RSC:
|
|
returnvalue = (Op2 - Op1) - ~Cflag;
|
|
break;
|
|
|
|
case OP_ORR:
|
|
returnvalue = Op1 | Op2;
|
|
break;
|
|
|
|
case OP_MOV:
|
|
if (( instr.dataproc.operand2 != 0xe ) && StepOver )
|
|
{
|
|
//
|
|
// A move from any register but LR to the PC is a call.
|
|
// We are stepping over, so bump the PC normally.
|
|
//
|
|
|
|
returnvalue = (ULONG)Flat(*NextAddr) + sizeof(ARMI);
|
|
}
|
|
else
|
|
{
|
|
//ie: mov pc, lr
|
|
returnvalue = Op2;
|
|
//[Moonshot 6841]: fix up the LR reg.
|
|
QualifyReturnAddress = TRUE;
|
|
}
|
|
break;
|
|
|
|
case OP_BIC:
|
|
returnvalue = Op1 & ~Op2;
|
|
break;
|
|
|
|
case OP_MVN:
|
|
returnvalue = ~Op2;
|
|
break;
|
|
|
|
case OP_TST:
|
|
case OP_TEQ:
|
|
case OP_CMP:
|
|
case OP_CMN:
|
|
default:
|
|
//
|
|
// This really isn't a branch. Bump the PC normally.
|
|
//
|
|
|
|
returnvalue = (ULONG)Flat(*NextAddr) + sizeof(ARMI);
|
|
break;
|
|
}
|
|
}
|
|
else if (( instr.instruction & LDM_PC_MASK ) == LDM_PC_INSTR )
|
|
{
|
|
// ie: ldmia sp!, {pc}
|
|
// Load multiple with the PC bit set. We don't need to check the
|
|
// step over flag in this case, because a load multiple is never a
|
|
// call, only a return.
|
|
//
|
|
|
|
ULONG RegList, i, count = 0, Rn;
|
|
|
|
//
|
|
// Get the address from Rn.
|
|
//
|
|
|
|
Rn = Register[ instr.ldm.rn ];
|
|
|
|
if ( instr.ldm.u )
|
|
{
|
|
//
|
|
// Increment the address. Check to see how many other registers
|
|
// are to be read.
|
|
//
|
|
|
|
RegList = instr.ldm.reglist;
|
|
|
|
for ( i = 0; i < 15; i++ )
|
|
{
|
|
if ( RegList & 0x1 ) count++;
|
|
RegList = RegList >> 1;
|
|
}
|
|
|
|
//
|
|
// Check the p bit to see how big to make the offset to the PC.
|
|
//
|
|
|
|
if ( instr.ldm.p )
|
|
{
|
|
// Before
|
|
count = (count + 1) * sizeof(ARMI);
|
|
}
|
|
else
|
|
{
|
|
// After
|
|
count = count * sizeof(ARMI);
|
|
}
|
|
|
|
Rn += count;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Decrement the address. If we decrement before, we need to
|
|
// subract the instruction size now. Otherwise, do nothing.
|
|
//
|
|
|
|
if ( instr.ldm.p )
|
|
{
|
|
// Before
|
|
Rn -= sizeof(ARMI);
|
|
}
|
|
}
|
|
|
|
// reading values from the stack
|
|
if (m_Target->ReadAllVirtual(Process, EXTEND64(Rn), &returnvalue,
|
|
sizeof(returnvalue)) != S_OK)
|
|
{
|
|
// Unable to read, so what should be returned?
|
|
returnvalue = (ULONG)Flat(*NextAddr) + sizeof(ARMI);
|
|
}
|
|
|
|
//[Moonshot 6838]: fix up the LR reg.
|
|
QualifyReturnAddress = TRUE;
|
|
Ldm_instr = TRUE;
|
|
}
|
|
else if ((( instr.instruction & B_BL_MASK ) == B_INSTR ) ||
|
|
(( instr.instruction & B_BL_MASK ) == BL_INSTR ))
|
|
{
|
|
//
|
|
// If this is a call (branch and link), and we are stepping over, the
|
|
// next offset is the addr + 8.
|
|
//
|
|
|
|
if ( instr.bl.link && StepOver )
|
|
{
|
|
returnvalue = (ULONG)Flat(*NextAddr) + sizeof(ARMI);
|
|
}
|
|
else
|
|
{
|
|
LONG BranchOffset;
|
|
|
|
//
|
|
// To calculate the branch target:
|
|
// Shift the 24-bit offset left 2 bits
|
|
// Sign-extend it to 32 bits
|
|
// Add it to the contents of the PC
|
|
// (Which would be the current address + 8);
|
|
//
|
|
|
|
BranchOffset = instr.bl.offset;
|
|
BranchOffset <<= 2;
|
|
if( BranchOffset & 0x2000000 )
|
|
{
|
|
BranchOffset |= 0xfc000000;
|
|
}
|
|
returnvalue = (ULONG)Flat(*NextAddr) + BranchOffset;
|
|
returnvalue += 8;
|
|
}
|
|
}
|
|
else if ( instr.instruction == LDR_THUNK_2 )
|
|
{
|
|
//
|
|
// Need to handle import DLL thunk type branches to destination func Foo
|
|
//
|
|
// 0001ACA0: ldr r12, [pc] ; pc+8+0 = 0x0001ACA8
|
|
// 0001ACA4: ldr pc, [r12]
|
|
// 0001ACA8: DCD 0x0001C020 ; This memory location holds the address, of the address, of Foo
|
|
//
|
|
// 0001C020: DCD Foo ; This memory location holds the address of Foo
|
|
//
|
|
|
|
//
|
|
// Get the address of Foo from Rn.
|
|
// simple register indirect. no offsets, no scaling, no indexing addressing mode
|
|
//
|
|
if (m_Target->ReadAllVirtual(Process, EXTEND64(Register[instr.ldr.rn]),
|
|
&returnvalue,
|
|
sizeof(returnvalue)) != S_OK)
|
|
{
|
|
// Unable to read, so what should be returned?
|
|
returnvalue = (ULONG)Flat(*NextAddr) + sizeof(ARMI);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Bump PC normally
|
|
returnvalue = (ULONG)Flat(*NextAddr) + sizeof(ARMI);
|
|
}
|
|
|
|
#if 0
|
|
//jvp
|
|
//[Moonshot 6838, 6841]: fix up the LR register.
|
|
// Taken from the same function in ppcmach.c
|
|
// The value we get from the LR register is not necessarily fully
|
|
// qualified. In WINCE, when a process is running it is mapped into the
|
|
// ZERO slot, meaning that the upper 6 bits contain 0. To fully
|
|
// qualify an address, the upper 6 bits contain the current process. The
|
|
// LR regs is set at runtime so we need to fix it up so breakpoint
|
|
// comparisons make sense.
|
|
if (QualifyReturnAddress == TRUE)
|
|
{
|
|
ULONG Status, NextOffset;
|
|
ARMI I1;
|
|
ADDR addr;
|
|
|
|
NextOffset = returnvalue;
|
|
|
|
ZeroMemory(&addr, sizeof(ADDR));
|
|
GetAddrOff(addr) = NextOffset;
|
|
TranslateAddress(hthd->hprc, 0, &addr, TRUE);
|
|
NextOffset = GetAddrOff(addr);
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Because the next offset translation is meant for NK, the
|
|
// stub and simulator will return STATUS_UNSUCCESSFUL.
|
|
//
|
|
// On a successful transaction our new address will be fully qualified
|
|
// otherwise, just leave.
|
|
if (Status == STATUS_SUCCESS)
|
|
{
|
|
if (!Ldm_instr)
|
|
{
|
|
// DP_PC_INSTR; Moonshot 6841
|
|
returnvalue = NextOffset; // fully qualified.
|
|
}
|
|
|
|
//since this is a ldm instruction, it's reading values from the stack;
|
|
//so do error checking to make sure that these values must be a valid
|
|
//instruction. At this moment, we know that NextOffset contains the address
|
|
//of the instruction after the branch instruction (also contains the value
|
|
//of LR). So make sure that the previous instruction (from the NextOffset)
|
|
//must be a branch instruction.
|
|
else if (NextOffset && (!(NextOffset % 4)))
|
|
{
|
|
AddrInit( &memaddr, 0, 0, NextOffset-4, TRUE, TRUE, FALSE, FALSE );
|
|
AddrReadMemory( hthd->hprc, //read previous instruction.
|
|
hthd,
|
|
&memaddr,
|
|
&I1.instruction,
|
|
sizeof(ARMI),
|
|
&cBytes );
|
|
|
|
//previous instruction must be a branch instruction.
|
|
if ( ((I1.instruction & BX_MASK ) == BX_INSTR ) ||
|
|
((I1.instruction & B_BL_MASK ) == B_INSTR) ||
|
|
((I1.instruction & B_BL_MASK ) == BL_INSTR) )
|
|
{
|
|
returnvalue = NextOffset; //fully qualified.
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
// make sure a fully fixed up offset is returned
|
|
ADDR addr;
|
|
DWORD dwTemp;
|
|
DWORD dwPC;
|
|
BOOL bReturn;
|
|
|
|
ZeroMemory(&addr, sizeof(ADDR));
|
|
GetAddrOff(addr) = returnvalue;
|
|
TranslateAddress(hthd->hprc, 0, &addr, TRUE);
|
|
|
|
if (!StepOver)
|
|
{
|
|
cBytes = 0;
|
|
dwTemp = 0;
|
|
returnvalue = GetAddrOff(addr);
|
|
bReturn = DbgReadMemory(hthd->hprc,
|
|
(LPVOID)returnvalue,
|
|
&dwTemp,
|
|
sizeof(DWORD),
|
|
&cBytes);
|
|
if (!bReturn || cBytes != sizeof(DWORD) || dwTemp == 0 )
|
|
{
|
|
returnvalue = pcaddr.addr.off + sizeof(ARMI);
|
|
GetAddrOff(addr) = returnvalue;
|
|
TranslateAddress(hthd->hprc, 0, &addr, TRUE);
|
|
dwPC = 0;
|
|
}
|
|
else
|
|
{
|
|
dwPC = returnvalue;
|
|
}
|
|
// Now need to find if its a thunk, if yes, we need to
|
|
// probe the thunk destination
|
|
if ( dwPC )
|
|
{
|
|
while (IsThunk(hthd, dwPC, NULL, &dwPC, NULL ) && dwPC)
|
|
{
|
|
GetAddrOff(addr) = dwPC;
|
|
TranslateAddress(hthd->hprc, 0, &addr, FALSE);
|
|
dwPC = GetAddrOff(addr);
|
|
cBytes = 0;
|
|
dwTemp = 0;
|
|
bReturn = DbgReadMemory(hthd->hprc,
|
|
(LPVOID)dwPC,
|
|
&dwTemp,
|
|
sizeof(DWORD),
|
|
&cBytes);
|
|
if (!bReturn || cBytes != sizeof(DWORD) || dwTemp == 0 )
|
|
{
|
|
returnvalue = pcaddr.addr.off + sizeof(ARMI);
|
|
GetAddrOff(addr) = returnvalue;
|
|
TranslateAddress(hthd->hprc, 0, &addr, TRUE);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
returnvalue = GetAddrOff(addr);
|
|
}
|
|
#endif
|
|
|
|
ADDRFLAT(NextAddr, EXTEND64(returnvalue));
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::IncrementBySmallestInstruction(PADDR Addr)
|
|
{
|
|
AddrAdd(Addr, 4);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::DecrementBySmallestInstruction(PADDR Addr)
|
|
{
|
|
AddrSub(Addr, 4);
|
|
}
|
|
|
|
void
|
|
ArmMachineInfo::Assemble(ProcessInfo* Process,
|
|
PADDR Address, PSTR Input)
|
|
{
|
|
// Not going to implement assemble command at this time
|
|
ErrOut("No assemble support for ARM\n");
|
|
}
|