|
|
//----------------------------------------------------------------------------
//
// Disassembly portions of X86 machine implementation.
//
// Copyright (C) Microsoft Corporation, 2000-2002.
//
//----------------------------------------------------------------------------
#include "ntsdp.hpp"
#include "i386_dis.h"
UCHAR g_X86Int3[] = { 0xcc };
//----------------------------------------------------------------------------
//
// BaseX86MachineInfo methods.
//
//----------------------------------------------------------------------------
/***** macros and defines *****/
#define X86_CS_OVR 0x2e
#define BIT20(b) ((b) & 0x07)
#define BIT53(b) (((b) >> 3) & 0x07)
#define BIT76(b) (((b) >> 6) & 0x03)
#define MAXOPLEN 10
/***** static tables and variables *****/
char* g_X86Reg8[] = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" }; char* g_Amd64ExtendedReg8[] = { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil" }; char* g_X86RegBase[] = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", "8", "9", "10", "11", "12", "13", "14", "15" }; char *g_X86Mrm16[] = { "bx+si", "bx+di", "bp+si", "bp+di", "si", "di", "bp", "bx", "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" }; char *g_X86Mrm32[] = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi", "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" }; char *g_X86Mrm64[] = { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" };
UCHAR g_X86Reg16Idx[] = { X86_NBX, X86_NBX, X86_NBP, X86_NBP, X86_NSI, X86_NDI, X86_NBP, X86_NBX, }; UCHAR g_X86Reg16Idx2[] = { X86_NSI, X86_NDI, X86_NSI, X86_NDI }; UCHAR g_X86RegIdx[] = { X86_NAX, X86_NCX, X86_NDX, X86_NBX, X86_NSP, X86_NBP, X86_NSI, X86_NDI, AMD64_R8, AMD64_R9, AMD64_R10, AMD64_R11, AMD64_R12, AMD64_R13, AMD64_R14, AMD64_R15 };
static char sregtab[] = "ecsdfg"; // first letter of ES, CS, SS, DS, FS, GS
char* g_CompareIb[] = { "eq", "lt", "le", "unord", "ne", "nlt", "nle", "ord" };
char hexdigit[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
static int g_MrmMod; /* mod of mod/rm byte */ static int g_MrmRm; /* rm of mod/rm byte */ static int g_MrmArg; /* return reg value (of mod/rm) */ static unsigned char *g_InstrMem; /* current position in instruction */
ADDR EAaddr[2]; // offset of effective address
static int EAsize[2]; // size of effective address item
static char *g_EaSegNames[2]; // normal segment for operand
#define IPREL_MARKER "<-IPREL->"
BOOL g_X86ModrmHasIpRelOffset; LONG g_X86IpRelOffset;
int g_SegAddrMode; /* global address size in bits */ int g_SegOpSize; /* global operand size in bits */ int g_AddrMode; /* local address size in bits */ int g_OpSize; /* operand size in bits */
int g_ExtendOpCode; int g_ExtendAny; int g_ExtendMrmReg; int g_ExtendSibIndex; int g_ExtendRm;
BOOL g_MovX; // Indicates a MOVSX or MOVZX.
BOOL g_MovSXD; BOOL g_ForceMrmReg32; // M/RM register is always 32-bit.
BOOL g_MmRegEa; // Use mm? registers in reg-only EA.
BOOL g_XmmRegEa; // Use xmm? registers in reg-only EA.
BOOL g_ControlFlow; // Control flow instruction.
int g_RepPrefix;
enum { XMM_SS, XMM_SD, XMM_PS, XMM_PD, };
int g_XmmOpSize;
enum { JCC_EA_NONE, // Branch must be no-branch + 1.
JCC_EA_NO_BRANCH, JCC_EA_BRANCH, };
// First entry are bits that must be zero, second
// and third entries are bit shifts for bits that must match.
ULONG g_JccCheckTable[][3] = { X86_BIT_FLAGOF, 0, 0, // JNO
X86_BIT_FLAGCF, 0, 0, // JNB
X86_BIT_FLAGZF, 0, 0, // JNZ
X86_BIT_FLAGCF | X86_BIT_FLAGZF, 0, 0, // JNBE
X86_BIT_FLAGSF, 0, 0, // JNS
X86_BIT_FLAGPF, 0, 0, // JNP
0, 7, 11, // JNL
X86_BIT_FLAGZF, 7, 11, // JNLE
};
// internal function definitions
void OutputHexString(char **, PUCHAR, int); void OutputHexValue(char **, PUCHAR, int, int); void OutputExHexValue(char **, PUCHAR, int, int); void OutputHexCode(char **, PUCHAR, int); void X86OutputString(char **, char *);
void OutputHexAddr(PSTR *, PADDR);
#define FormSelAddress(Addr, Sel, Off) \
FormAddr(Sel, Off, 0, Addr) #define FormSegRegAddress(Addr, SegReg, Off) \
FormAddr(SegReg, Off, FORM_SEGREG, Addr)
void GetSegAddrOpSizes(MachineInfo* Machine, PADDR Addr) { if ((Type(*Addr) & ADDR_1664) || ((Type(*Addr) & ADDR_FLAT) && Machine->m_ExecTypes[0] == IMAGE_FILE_MACHINE_AMD64)) { g_SegAddrMode = 64; // X86-64 defaults to 32-bit operand sizes even in 64-bit code.
// Only the address size changes. An operand size prefix
// switches from 32- to 64-bit.
g_SegOpSize = 32; } else if (Type(*Addr) & (ADDR_V86 | ADDR_16)) { g_SegAddrMode = 16; g_SegOpSize = 16; } else { g_SegAddrMode = 32; g_SegOpSize = 32; }
g_AddrMode = g_SegAddrMode; g_OpSize = g_SegOpSize; }
void OverrideAddrMode(void) { switch(g_SegAddrMode) { case 16: g_AddrMode = 32; break; case 32: g_AddrMode = 16; break; case 64: g_AddrMode = 32; break; default: DBG_ASSERT(FALSE); break; } }
void OverrideOpSize(int OverrideOp) { switch(g_SegAddrMode) { case 16: g_OpSize = 32; break; case 32: g_OpSize = 16; break; case 64: // X86-64 defaults to 32-bit operand sizes even in 64-bit code.
// Only the address size changes. A REX operand size prefix
// switches from 32- to 64-bit.
if (OverrideOp == 0x66) { g_OpSize = 16; } else if (OverrideOp & 8) { g_OpSize = 64; } break; default: DBG_ASSERT(FALSE); break; } }
void ExtendOps(int opcode) { // x86-64 uses these opcodes as the REX override.
OverrideOpSize(opcode);
g_ExtendOpCode = opcode; g_ExtendAny = 8; if (opcode & 1) { g_ExtendRm = 8; } if (opcode & 2) { g_ExtendSibIndex = 8; } if (opcode & 4) { g_ExtendMrmReg = 8; } }
void IgnoreExtend(BOOL Verbose) { //
// Resets any extensions that may have happened.
// The REX prefix must be the last
// prefix of an instruction and is ignored otherwise,
// so this reset is done when any prefix is encountered
// after the REX prefix. This should normally never
// happen but technically it's valid code so we should handle it.
//
if (g_ExtendOpCode) { if (Verbose) { WarnOut("REX prefix ignored\n"); }
if (g_ExtendOpCode & 8) { // Op size was changed so put it back. This
// is tricky since in theory an op size override
// prefix could also be present, but let's not
// worry about that for now.
g_OpSize = g_SegOpSize; }
g_ExtendOpCode = 0; g_ExtendAny = 0; g_ExtendRm = 0; g_ExtendSibIndex = 0; g_ExtendMrmReg = 0; } }
struct AMD_3DNOW_OPSTR { PSTR Str; UCHAR Opcode; };
AMD_3DNOW_OPSTR g_Amd3DNowOpStr[] = { "pavgusb", 0xBF, "pfadd", 0x9E, "pfsub", 0x9A, "pfsubr", 0xAA, "pfacc", 0xAE, "pfcmpge", 0x90, "pfcmpgt", 0xA0, "pfcmpeq", 0xB0, "pfmin", 0x94, "pfmax", 0xA4, "pi2fd", 0x0D, "pf2id", 0x1D, "pfrcp", 0x96, "pfrsqrt", 0x97, "pfmul", 0xB4, "pfrcpit1", 0xA6, "pfrsqit1", 0xA7, "pfrcpit2", 0xB6, "pmulhrw", 0xB7, "pf2iw", 0x1C, "pfnacc", 0x8A, "pfpnacc", 0x8E, "pi2fw", 0x0C, "pswapd", 0xBB, };
PSTR GetAmd3DNowOpString(UCHAR Opcode) { UCHAR i;
for (i = 0; i < sizeof(g_Amd3DNowOpStr) / sizeof(g_Amd3DNowOpStr[0]); i++) { if (g_Amd3DNowOpStr[i].Opcode == Opcode) { return g_Amd3DNowOpStr[i].Str; } }
return NULL; }
BOOL BaseX86MachineInfo::Disassemble(ProcessInfo* Process, PADDR paddr, PSTR pchDst, BOOL fEAout) { ULONG64 Offset = Off(*paddr); int opcode; /* current opcode */ int olen = 2; /* operand length */ int alen = 2; /* address length */ int end = FALSE; /* end of instruction flag */ int mrm = FALSE; /* indicator that modrm is generated*/ unsigned char *action; /* action for operand interpretation*/ int indx; /* temporary index */ int action2; /* secondary action */ ULONG instlen; /* instruction length */ ULONG cBytes; // bytes read into instr buffer
int segOvr = 0; /* segment override opcode */ UCHAR membuf[X86_MAX_INSTRUCTION_LEN]; /* current instruction buffer */ char *pEAlabel = ""; // optional label for operand
char *pchResultBuf = pchDst; // working copy of pchDst pointer
char RepPrefixBuffer[32]; // rep prefix buffer
char *pchRepPrefixBuf = RepPrefixBuffer; // pointer to prefix buffer
char OpcodeBuffer[16]; // opcode buffer
char *pchOpcodeBuf = OpcodeBuffer; // pointer to opcode buffer
char OperandBuffer[MAX_SYMBOL_LEN + 20]; // operand buffer
char *pchOperandBuf = OperandBuffer; // pointer to operand buffer
char ModrmBuffer[MAX_SYMBOL_LEN + 20]; // modRM buffer
char *pchModrmBuf = ModrmBuffer; // pointer to modRM buffer
char EABuffer[128]; // effective address buffer
char *pchEABuf = EABuffer; // pointer to EA buffer
unsigned char BOPaction; int subcode; /* bop subcode */ int JccEa; LONGLONG Branch;
g_X86ModrmHasIpRelOffset = FALSE; g_MovX = FALSE; g_MovSXD = FALSE; g_ForceMrmReg32 = FALSE; g_MmRegEa = FALSE; g_XmmRegEa = FALSE; g_ControlFlow = FALSE; EAsize[0] = EAsize[1] = 0; // no effective address
g_EaSegNames[0] = dszDS_; g_EaSegNames[1] = dszES_; g_RepPrefix = 0; g_XmmOpSize = XMM_PS; g_ExtendOpCode = 0; g_ExtendAny = 0; g_ExtendMrmReg = 0; g_ExtendSibIndex = 0; g_ExtendRm = 0; JccEa = JCC_EA_NONE;
GetSegAddrOpSizes(this, paddr); alen = g_AddrMode / 8; olen = g_OpSize / 8;
OutputHexAddr(&pchResultBuf, paddr);
*pchResultBuf++ = ' ';
if (fnotFlat(*paddr) || m_Target->ReadVirtual(Process, Flat(*paddr), membuf, X86_MAX_INSTRUCTION_LEN, &cBytes) != S_OK) { ZeroMemory(membuf, X86_MAX_INSTRUCTION_LEN); cBytes = 0; }
g_InstrMem = membuf; /* point to begin of instruction */ opcode = *g_InstrMem++; /* get opcode */
if ( opcode == 0xc4 && *g_InstrMem == 0xC4 ) { g_InstrMem++; X86OutputString(&pchOpcodeBuf,"BOP"); action = &BOPaction; BOPaction = IB | END; subcode = *g_InstrMem; if ( subcode == 0x50 || subcode == 0x52 || subcode == 0x53 || subcode == 0x54 || subcode == 0x57 || subcode == 0x58 || subcode == 0x58 ) { BOPaction = IW | END; } } else { X86OutputString(&pchOpcodeBuf, distbl[opcode].instruct); action = actiontbl + distbl[opcode].opr; /* get operand action */ }
/***** loop through all operand actions *****/
do { action2 = (*action) & 0xc0; switch((*action++) & 0x3f) { case ALT: /* alter the opcode if not 16-bit */ if (g_OpSize > 16) { indx = *action++; pchOpcodeBuf = &OpcodeBuffer[indx]; if (indx == 0) { X86OutputString(&pchOpcodeBuf, g_OpSize == 32 ? dszCWDE : dszCDQE); } else if (g_OpSize == 64) { *pchOpcodeBuf++ = 'q'; if (indx == 1) { *pchOpcodeBuf++ = 'o'; } } else { *pchOpcodeBuf++ = 'd'; if (indx == 1) { *pchOpcodeBuf++ = 'q'; } } } break;
case XMMSD: /* SSE-style opcode rewriting */ { char ScalarOrPacked, SingleOrDouble; char* DquOrQ, *DqOrQ, *SsdOrUpsd, *CvtPd, *CvtPs; char* MovQD6, *Shuf; char* Scan;
g_MmRegEa = TRUE; DquOrQ = "q"; DqOrQ = "q"; SsdOrUpsd = "s?"; CvtPd = NULL; CvtPs = NULL; MovQD6 = NULL; switch(g_RepPrefix) { case X86_REPN: // Scalar double operation.
ScalarOrPacked = 's'; SingleOrDouble = 'd'; CvtPd = "pd2dq"; MovQD6 = "dq2q"; Shuf = "lw"; g_XmmOpSize = XMM_SD; // Assume there was no other lock/rep/etc.
pchRepPrefixBuf = RepPrefixBuffer; break; case X86_REP: // Scalar single operation.
ScalarOrPacked = 's'; SingleOrDouble = 's'; CvtPd = "dq2pd"; CvtPs = "tps2dq"; MovQD6 = "q2dq"; Shuf = "hw"; g_XmmOpSize = XMM_SS; // Assume there was no other lock/rep/etc.
pchRepPrefixBuf = RepPrefixBuffer; break; default: // No rep prefix means packed single or double
// depending on operand size.
ScalarOrPacked = 'p'; SsdOrUpsd = "up?"; if (g_OpSize == g_SegOpSize) { SingleOrDouble = 's'; CvtPs = "dq2ps"; Shuf = "w"; g_XmmOpSize = XMM_PS; } else { SingleOrDouble = 'd'; DqOrQ = "dq"; DquOrQ = "dqu"; CvtPd = "tpd2dq"; CvtPs = "ps2dq"; MovQD6 = "q"; Shuf = "d"; g_XmmRegEa = TRUE; g_XmmOpSize = XMM_PD; } break; }
pchOpcodeBuf = OpcodeBuffer; while (*pchOpcodeBuf && *pchOpcodeBuf != ' ') { switch(*pchOpcodeBuf) { case ':': *pchOpcodeBuf = ScalarOrPacked; break; case '?': *pchOpcodeBuf = SingleOrDouble; break; case ',': *pchOpcodeBuf = SingleOrDouble == 's' ? 'd' : 's'; break; }
pchOpcodeBuf++; }
switch(opcode) { case X86_MOVFREGMEM: case X86_MOVFMEMREG: // Append characters for MOVS[SD] and MOVUP[SD].
strcpy(pchOpcodeBuf, SsdOrUpsd); if ((Scan = strchr(pchOpcodeBuf, '?')) != NULL) { *Scan = SingleOrDouble; } pchOpcodeBuf += strlen(pchOpcodeBuf); break; case X86_MOVNT: // Append characters for MOVNTQ and MOVNTDQ.
X86OutputString(&pchOpcodeBuf, DqOrQ); break; case X86_MASKMOV: // Append characters for MASKMOVQ and MASKMOVDQU.
X86OutputString(&pchOpcodeBuf, DquOrQ); break; case X86_CVTPD: if (CvtPd == NULL) { // Invalid opcode.
pchOpcodeBuf = OpcodeBuffer; X86OutputString(&pchOpcodeBuf, dszRESERVED); action2 = END; } else { // Append characters for CVT<PD>.
X86OutputString(&pchOpcodeBuf, CvtPd); } break; case X86_CVTPS: if (CvtPs == NULL) { // Invalid opcode.
pchOpcodeBuf = OpcodeBuffer; X86OutputString(&pchOpcodeBuf, dszRESERVED); action2 = END; } else { // Append characters for CVT<PS>.
X86OutputString(&pchOpcodeBuf, CvtPs); } break; case X86_MOVQ_D6: if (MovQD6 == NULL) { // Invalid opcode.
pchOpcodeBuf = OpcodeBuffer; X86OutputString(&pchOpcodeBuf, dszRESERVED); action2 = END; } else { // Append characters for MOVQ D6 family.
X86OutputString(&pchOpcodeBuf, MovQD6); } break; case X86_PSHUF: // Append characters for PSHUF variants.
X86OutputString(&pchOpcodeBuf, Shuf); break; } } break;
case AMD3DNOW: /* AMD 3DNow post-instruction byte */ { PSTR OpStr;
// Get the trailing byte and look up
// the opcode string.
OpStr = GetAmd3DNowOpString(*g_InstrMem++); if (OpStr == NULL) { // Not a defined 3DNow instruction.
// Leave the ??? in the opstring.
break; }
// Update opstring to real text.
pchOpcodeBuf = OpcodeBuffer; X86OutputString(&pchOpcodeBuf, OpStr); } break;
case STROP: // compute size of operands in indx
// also if dword operands, change fifth
// opcode letter from 'w' to 'd'.
if (opcode & 1) { if (g_OpSize == 64) { indx = 8; OpcodeBuffer[4] = 'q'; } else if (g_OpSize == 32) { indx = 4; OpcodeBuffer[4] = 'd'; } else { indx = 2; } } else { indx = 1; }
if (*action & 1) { if (fEAout) { if (g_AddrMode > 16) { FormSelAddress(&EAaddr[0], 0, GetReg64(X86_NSI)); } else { FormSegRegAddress(&EAaddr[0], SEGREG_DATA, GetReg16(X86_NSI)); } EAsize[0] = indx; } } if (*action++ & 2) { if (fEAout) { if (g_AddrMode > 16) { FormSelAddress(&EAaddr[1], 0, GetReg64(X86_NDI)); } else { FormSegRegAddress(&EAaddr[1], SEGREG_ES, GetReg16(X86_NDI)); } EAsize[1] = indx; } } break;
case CHR: /* insert a character */ *pchOperandBuf++ = *action++; break;
case CREG: /* set debug, test or control reg */ if (opcode & 0x04) { *pchOperandBuf++ = 't'; } else if (opcode & 0x01) { *pchOperandBuf++ = 'd'; } else { *pchOperandBuf++ = 'c'; } *pchOperandBuf++ = 'r'; if (g_MrmArg >= 10) { *pchOperandBuf++ = (char)('0' + g_MrmArg / 10); g_MrmArg %= 10; } *pchOperandBuf++ = (char)('0' + g_MrmArg); break;
case SREG2: /* segment register */ g_MrmArg = BIT53(opcode); // set value to fall through
case SREG3: /* segment register */ *pchOperandBuf++ = sregtab[g_MrmArg]; // reg is part of modrm
*pchOperandBuf++ = 's'; break;
case BRSTR: /* get index to register string */ g_MrmArg = *action++; /* from action table */ goto BREGlabel;
case BOREG: /* byte register (in opcode) */ g_MrmArg = BIT20(opcode); /* register is part of opcode */ goto BREGlabel;
case ALSTR: g_MrmArg = 0; /* point to AL register */ BREGlabel: case BREG: /* general register */ if (g_ExtendAny && g_MrmArg < 8) { X86OutputString(&pchOperandBuf, g_Amd64ExtendedReg8[g_MrmArg]); } else { X86OutputString(&pchOperandBuf, g_X86Reg8[g_MrmArg]); } break;
case WRSTR: /* get index to register string */ g_MrmArg = *action++; /* from action table */ goto WREGlabel;
case VOREG: /* register is part of opcode */ if (m_ExecTypes[0] == IMAGE_FILE_MACHINE_AMD64 && opcode >= 0x40 && opcode <= 0x4f) { // Get rid of the inc/dec text as this
// isn't really an inc/dec.
pchOpcodeBuf = OpcodeBuffer;
// Process the REX override.
ExtendOps(opcode); olen = g_OpSize / 8; action2 = 0; goto getNxtByte; }
g_MrmArg = BIT20(opcode) + g_ExtendRm; goto VREGlabel;
case AXSTR: g_MrmArg = 0; /* point to eAX register */ VREGlabel: case VREG: /* general register */ if ((g_SegAddrMode == 64 && opcode >= 0x50 && opcode <= 0x5f) || g_MrmArg >= 8) { // Push/pops are always 64-bit in 64-bit segments.
*pchOperandBuf++ = 'r'; } else if (g_OpSize == 32 || opcode == X86_PEXTRW || opcode == X86_PMOVMSKB) { *pchOperandBuf++ = 'e'; } else if (g_OpSize == 64) { *pchOperandBuf++ = 'r'; } WREGlabel: case WREG: /* register is word size */ X86OutputString(&pchOperandBuf, g_X86RegBase[g_MrmArg]); if (g_MrmArg >= 8) { if (g_OpSize == 32) { *pchOperandBuf++ = 'd'; } else if (g_OpSize == 16) { *pchOperandBuf++ = 'w'; } } break;
case MMORWREG: if (g_XmmOpSize == XMM_SS || g_XmmOpSize == XMM_SD) { goto VREGlabel; } // Fall through.
MMWREGlabel: case MMWREG: if ((g_OpSize != g_SegOpSize && opcode != X86_CVTSPSD2SPI) || (g_RepPrefix == X86_REP && (opcode == X86_MOVDQAU_MR || opcode == X86_MOVDQAU_RM))) { *pchOperandBuf++ = 'x'; } *pchOperandBuf++ = 'm'; *pchOperandBuf++ = 'm'; if (g_MrmArg >= 10) { *pchOperandBuf++ = (char)('0' + g_MrmArg / 10); g_MrmArg %= 10; } *pchOperandBuf++ = g_MrmArg + '0'; break;
case XORMMREG: if (g_OpSize == g_SegOpSize) { goto MMWREGlabel; } // Fall through.
case XMMWREG: if (opcode != X86_PSHUF || g_XmmOpSize != XMM_PS) { *pchOperandBuf++ = 'x'; } *pchOperandBuf++ = 'm'; *pchOperandBuf++ = 'm'; if (g_MrmArg >= 10) { *pchOperandBuf++ = (char)('0' + g_MrmArg / 10); g_MrmArg %= 10; } *pchOperandBuf++ = g_MrmArg + '0'; break;
case IST_ST: X86OutputString(&pchOperandBuf, "st(0),st"); *(pchOperandBuf - 5) += (char)g_MrmRm; break;
case ST_IST: X86OutputString(&pchOperandBuf, "st,"); case IST: X86OutputString(&pchOperandBuf, "st(0)"); *(pchOperandBuf - 2) += (char)g_MrmRm; break;
case xBYTE: /* set instruction to byte only */ EAsize[0] = 1; pEAlabel = "byte ptr "; break;
case VAR: if ((g_SegAddrMode == 64 || g_ExtendAny > 0) && opcode == 0x63) { // In AMD64 REX32 and 64-bit modes this instruction
// is MOVSXD r64, r/m32 instead of ARPL r/m, reg.
pchOpcodeBuf = OpcodeBuffer; X86OutputString(&pchOpcodeBuf, dszMOVSXD); action = &actiontbl[O_Reg_Modrm] + 1; g_OpSize = 64; g_MovSXD = TRUE; goto DWORDlabel; } else if (opcode == 0xff) { UCHAR Extra = BIT53(*g_InstrMem); if (Extra >= 2 && Extra <= 5) { g_ControlFlow = TRUE;
// On x86-64 control-flow operations default to
// 64-bit opsize.
if (g_SegAddrMode == 64) { if (g_OpSize == 32) { g_OpSize = 64; } } } else if (g_SegAddrMode == 64 && Extra == 6) { // Push/pops are always 64-bit in 64-bit segments.
g_OpSize = 64; } } else if (g_SegAddrMode == 64 && opcode == 0x8f) { // Push/pops are always 64-bit in 64-bit segments.
g_OpSize = 64; } olen = g_OpSize / 8;
if (g_OpSize == 64) { goto QWORDlabel; } else if (g_OpSize == 32) { goto DWORDlabel; }
case xWORD: if (opcode == X86_PINSRW) { g_ForceMrmReg32 = TRUE; } EAsize[0] = 2; pEAlabel = "word ptr "; break;
case EDWORD: // Control register opsize is mode-independent.
g_OpSize = g_SegAddrMode; if (g_OpSize == 64) { goto QWORDlabel; } case xDWORD: if (opcode == X86_MOVDQ_7E && g_RepPrefix == X86_REP) { // Switch to MOVQ xmm1, xmm2/m64.
pchRepPrefixBuf = RepPrefixBuffer; *(pchOpcodeBuf - 1) = 'q'; EAsize[0] = 8; pEAlabel = "qword ptr "; g_XmmRegEa = TRUE; action = &actiontbl[O_Sd_XmmReg_qModrm] + 2; break; } // Fall through.
DWORDlabel: EAsize[0] = 4; pEAlabel = "dword ptr "; break;
case XMMOWORD: if (opcode == X86_PSHUF) { if (g_XmmOpSize == XMM_PS) { g_MmRegEa = TRUE; goto QWORDlabel; } else { EAsize[0] = 16; pEAlabel = "oword ptr "; break; } }
g_XmmRegEa = TRUE; if (opcode == X86_CVTPD) { if (g_XmmOpSize == XMM_SS) { EAsize[0] = 8; pEAlabel = "qword ptr "; } else { EAsize[0] = 16; pEAlabel = "oword ptr "; } break; } else if (opcode == X86_CVTPS) { EAsize[0] = 16; pEAlabel = "oword ptr "; break; } else if (opcode == X86_MOVQ_D6) { if (g_XmmOpSize == XMM_SD) { // Switch to MOVDQ2Q mm, xmm.
EAsize[0] = 16; pEAlabel = "oword ptr "; action = &actiontbl[O_MmReg_qModrm] + 1; break; } } else if (opcode == X86_MOVHLPS && g_XmmOpSize == XMM_PS && BIT76(*g_InstrMem) == 3) { // reg-reg form of MOVLPS is called MOVHLPS.
pchOpcodeBuf = OpcodeBuffer; X86OutputString(&pchOpcodeBuf, dszMOVHLPS); } else if (opcode == X86_MOVLHPS && g_XmmOpSize == XMM_PS && BIT76(*g_InstrMem) == 3) { // reg-reg form of MOVHPS is called MOVLHPS.
pchOpcodeBuf = OpcodeBuffer; X86OutputString(&pchOpcodeBuf, dszMOVLHPS); }
// Fall through.
OWORDlabel: case OWORD: switch(g_XmmOpSize) { case XMM_SS: EAsize[0] = 4; pEAlabel = "dword ptr "; if (opcode == X86_MOVQ_D6) { // Switch to MOVQ xmm1, xmm2/m64.
g_XmmRegEa = FALSE; action = &actiontbl[O_Sd_XmmReg_qModrm] + 1; } break; case XMM_SD: EAsize[0] = 8; pEAlabel = "qword ptr "; break; default: if (opcode == 0x112 || opcode == 0x113 || opcode == 0x116 || opcode == 0x117 || opcode == X86_MOVQ_D6 || (g_OpSize == g_SegOpSize && (opcode == 0x12c || opcode == X86_CVTSPSD2SPI || opcode == X86_CVTSPSD2SPSD))) { EAsize[0] = 8; pEAlabel = "qword ptr "; } else { EAsize[0] = 16; pEAlabel = "oword ptr "; } break; } break;
case XMMXWORD: g_XmmRegEa = TRUE; if (g_OpSize == g_SegOpSize) { if (opcode == X86_MOVNT) { EAsize[0] = 8; pEAlabel = "qword ptr "; } else { EAsize[0] = 4; pEAlabel = "dword ptr "; } } else { if (opcode == X86_MOVNT) { EAsize[0] = 16; pEAlabel = "oword ptr "; } else { EAsize[0] = 8; pEAlabel = "qword ptr "; } } break;
case MMQWORD: // The REX prefix is ignored in front of most
// FP and MM operations. The only affect it
// has is to allow extended register selection.
// Reset the opsize as the 64-bit opsize behavior
// of the REX prefix is ignored.
if (g_ExtendOpCode & 8) { g_OpSize = g_SegOpSize; }
if (g_OpSize != g_SegOpSize && (opcode == X86_MOVDQAU_MR || opcode == X86_MOVDQAU_RM)) { pchOpcodeBuf = OpcodeBuffer; X86OutputString(&pchOpcodeBuf, dszMOVDQA); } else if (g_RepPrefix == X86_REP && (opcode == X86_MOVDQAU_MR || opcode == X86_MOVDQAU_RM)) { pchRepPrefixBuf = RepPrefixBuffer; pchOpcodeBuf = OpcodeBuffer; X86OutputString(&pchOpcodeBuf, dszMOVDQU); g_XmmRegEa = TRUE; goto OWORDlabel; }
if (opcode == X86_CVTSPI2SPSD) { g_XmmRegEa = FALSE; if (g_XmmOpSize == XMM_SS || g_XmmOpSize == XMM_SD) { g_MmRegEa = FALSE; goto DWORDlabel; } } else if (g_OpSize != g_SegOpSize) { g_XmmRegEa = TRUE; goto OWORDlabel; } g_MmRegEa = TRUE; QWORDlabel: case QWORD: EAsize[0] = 8; pEAlabel = "qword ptr "; break;
case TBYTE: EAsize[0] = 10; pEAlabel = "tbyte ptr "; break;
case FARPTR: g_ControlFlow = TRUE;
// On x86-64 control-flow operations default to
// 64-bit opsize.
if (g_SegAddrMode == 64) { if (g_OpSize == 32) { g_OpSize = 64; } }
switch(g_OpSize) { case 16: EAsize[0] = 4; pEAlabel = "dword ptr "; break; default: EAsize[0] = 6; pEAlabel = "fword ptr "; break; } break;
case LMODRM: // output modRM data type
if (g_MrmMod != 3) { X86OutputString(&pchOperandBuf, pEAlabel); } else { EAsize[0] = 0; }
case MODRM: /* output modrm string */ if (segOvr) /* in case of segment override */ { X86OutputString(&pchOperandBuf, distbl[segOvr].instruct); } *pchModrmBuf = '\0'; X86OutputString(&pchOperandBuf, ModrmBuffer); break;
case ADDRP: /* address pointer */ // segment
OutputHexString(&pchOperandBuf, g_InstrMem + olen, 2); *pchOperandBuf++ = ':'; // offset
OutputSymbol(&pchOperandBuf, g_InstrMem, olen, segOvr); g_InstrMem += olen + 2; break;
case JCC8: JccEa = ComputeJccEa(opcode, fEAout); // Fall through.
case REL8: /* relative address 8-bit */ if (opcode == 0xe3 && g_AddrMode > 16) { pchOpcodeBuf = OpcodeBuffer; X86OutputString(&pchOpcodeBuf, g_AddrMode == 64 ? dszJRCXZ : dszJECXZ); } Branch = *(char *)g_InstrMem++; /* get the 8-bit rel offset */ goto DoRelDispl;
case JCCX: JccEa = ComputeJccEa(opcode, fEAout); // Fall through.
case REL16: /* relative address 16-/32-bit */ switch(g_AddrMode) { case 16: Branch = *(short UNALIGNED *)g_InstrMem; g_InstrMem += 2; break; default: Branch = *(long UNALIGNED *)g_InstrMem; g_InstrMem += 4; break; } DoRelDispl: /* calculate address */ Branch += Offset + (g_InstrMem - membuf); // rel8 and rel16 are only used in control-flow
// instructions so the target is always relative
// to CS. Pass in the CS override to force this.
OutputSymbol(&pchOperandBuf, (PUCHAR)&Branch, alen, X86_CS_OVR); break;
case UBYTE: // unsigned byte for int/in/out
OutputHexString(&pchOperandBuf, g_InstrMem, 1); // ubyte
g_InstrMem++; break;
case CMPIB: // Immediate byte comparison encoding for CMP[SP][SD].
if (*g_InstrMem < 8) { X86OutputString(&pchOperandBuf, g_CompareIb[*g_InstrMem]); g_InstrMem++; } else { olen = 1; goto DoImmed; } break;
case IB: /* operand is immediate byte */ // postop for AAD/AAM is 0x0a
if ((opcode & ~1) == 0xd4) { // test post-opcode byte
if (*g_InstrMem++ != 0x0a) { X86OutputString(&pchOperandBuf, dszRESERVED); } break; } olen = 1; /* set operand length */ goto DoImmed;
case IW: /* operand is immediate word */ olen = 2; /* set operand length */
case IV: /* operand is word or dword */ DoImmed: // AMD64 immediates are only 64-bit in the case of
// mov reg, immed. All other operations involving
// immediates stay 32-bit.
if (olen == 8 && (opcode < 0xb8 || opcode > 0xbf)) { olen = 4; } OutputHexValue(&pchOperandBuf, g_InstrMem, olen, FALSE); g_InstrMem += olen; break;
case XB: OutputExHexValue(&pchOperandBuf, g_InstrMem, 1, g_OpSize / 8); g_InstrMem++; break;
case OFFS: /* operand is offset */ EAsize[0] = (opcode & 1) ? olen : 1;
if (segOvr) /* in case of segment override */ { X86OutputString(&pchOperandBuf, distbl[segOvr].instruct); }
*pchOperandBuf++ = '['; // offset
OutputSymbol(&pchOperandBuf, g_InstrMem, alen, segOvr); g_InstrMem += alen; *pchOperandBuf++ = ']'; break;
case X86_GROUP: /* operand is of group 1,2,4,6 or 8 */ /* output opcode symbol */ X86OutputString(&pchOpcodeBuf, group[*action++][g_MrmArg]); break;
case GROUPT: /* operand is of group 3,5 or 7 */ indx = *action; /* get indx into group from action */ goto doGroupT;
case EGROUPT: /* x87 ESC (D8-DF) group index */ indx = BIT20(opcode) * 2; /* get group index from opcode */ /* some operand variations exist */ if (g_MrmMod == 3) { /* for x87 and mod == 3 */ ++indx; /* take the next group table entry */ if (indx == 3) { /* for x87 ESC==D9 and mod==3 */ if (g_MrmArg > 3) { /* for those D9 instructions */ indx = 12 + g_MrmArg; /* offset index to table by 12 */ g_MrmArg = g_MrmRm; /* set secondary index to rm */ } } else if (indx == 7) { /* for x87 ESC==DB and mod==3 */ if (g_MrmArg == 4) { /* if g_MrmArg==4 */ g_MrmArg = g_MrmRm; /* set secondary group table index */ } else if ((g_MrmArg < 4) || (g_MrmArg > 4 && g_MrmArg < 7)) { // adjust for pentium pro opcodes
indx = 24; /* offset index to table by 24*/ } } } doGroupT: /* handle group with different types of operands */
X86OutputString(&pchOpcodeBuf, groupt[indx][g_MrmArg].instruct); action = actiontbl + groupt[indx][g_MrmArg].opr; /* get new action */ break;
case OPC0F: /* secondary opcode table (opcode 0F) */ opcode = *g_InstrMem++; /* get real opcode */ g_MovX = (BOOL)(opcode == 0xBF || opcode == 0xB7); // Point opcode into secondary opcode portion of table.
opcode += 256; goto getNxtByte1;
case ADR_OVR: /* address override */ IgnoreExtend(TRUE); olen = g_OpSize / 8; OverrideAddrMode(); alen = g_AddrMode / 8; goto getNxtByte;
case OPR_OVR: /* operand size override */ IgnoreExtend(TRUE); OverrideOpSize(opcode); olen = g_OpSize / 8; goto getNxtByte;
case SEG_OVR: /* handle segment override */ IgnoreExtend(TRUE); olen = g_OpSize / 8; segOvr = opcode; /* save segment override opcode */ pchOpcodeBuf = OpcodeBuffer; // restart the opcode string
goto getNxtByte;
case REP: /* handle rep/lock prefixes */ IgnoreExtend(TRUE); olen = g_OpSize / 8; g_RepPrefix = opcode; *pchOpcodeBuf = '\0'; if (pchRepPrefixBuf != RepPrefixBuffer) { *pchRepPrefixBuf++ = ' '; } X86OutputString(&pchRepPrefixBuf, OpcodeBuffer); pchOpcodeBuf = OpcodeBuffer; getNxtByte: opcode = *g_InstrMem++; /* next byte is opcode */ getNxtByte1: action = actiontbl + distbl[opcode].opr; X86OutputString(&pchOpcodeBuf, distbl[opcode].instruct); break;
case NOP: if (opcode == X86_PAUSE && g_RepPrefix == X86_REP) { pchRepPrefixBuf = RepPrefixBuffer; pchOpcodeBuf = OpcodeBuffer; X86OutputString(&pchOpcodeBuf, dszPAUSE); } // Fall through.
default: /* opcode has no operand */ break; }
/* secondary action */ switch (action2) { case MRM: /* generate modrm for later use */ /* ignore if it has been generated */ if (!mrm) { /* generate modrm */ DIdoModrm(Process, &pchModrmBuf, segOvr, fEAout); mrm = TRUE; /* remember its generation */ } break;
case COM: /* insert a comma after operand */ *pchOperandBuf++ = ','; break;
case END: /* end of instruction */ end = TRUE; break; } } while (!end); /* loop til end of instruction */
/***** prepare disassembled instruction for output *****/
// dprintf("EAaddr[] = %08lx\n", (ULONG)Flat(EAaddr[0]));
instlen = (ULONG)(g_InstrMem - membuf);
if (instlen < cBytes) { cBytes = instlen; }
int obOpcode = m_Ptr64 ? 35 : 26; int obOpcodeMin; int obOpcodeMax;
int obOperand = m_Ptr64 ? 43 : 34; int obOperandMin; int obOperandMax;
int cbOpcode; int cbOperand; int cbOffset; int cbEAddr;
int obLineEnd = g_OutputWidth - 3;
if (g_AsmOptions & DEBUG_ASMOPT_IGNORE_OUTPUT_WIDTH) { // Extend the line end out a long way but
// not so much that overflows may occur.
obLineEnd = 0x7fffff; }
if (!(g_AsmOptions & DEBUG_ASMOPT_NO_CODE_BYTES)) { OutputHexCode(&pchResultBuf, membuf, cBytes); } else { obOpcode -= 16; obOperand -= 16; }
if (instlen > cBytes) { *pchResultBuf++ = '?'; *pchResultBuf++ = '?'; // point past unread byte
AddrAdd(paddr, 1); do { *pchResultBuf++ = ' '; } while (pchResultBuf < pchDst + obOpcode); X86OutputString(&pchResultBuf, "???\n"); *pchResultBuf++ = '\0'; return FALSE; }
AddrAdd(paddr, instlen);
PSTR Mark;
// Now that we know the complete size of the instruction
// we can correctly compute IP-relative absolute addresses.
*pchOperandBuf = 0; if (g_X86ModrmHasIpRelOffset && (Mark = strstr(OperandBuffer, IPREL_MARKER)) != NULL) { PSTR TailFrom, TailTo; ULONG64 IpRelAddr; size_t TailLen;
// Move the tail of the string to the end of the buffer
// to make space.
TailFrom = Mark + sizeof(IPREL_MARKER) - 1; TailLen = pchOperandBuf - TailFrom; TailTo = OperandBuffer + (sizeof(OperandBuffer) - 1 - TailLen); memmove(TailTo, TailFrom, TailLen);
// Compute the absolute address from the new IP
// and the offset and format it into the buffer.
IpRelAddr = Flat(*paddr) + g_X86IpRelOffset; OutputSymbol(&Mark, (PUCHAR)&IpRelAddr, g_SegAddrMode == 64 ? 8 : 4, X86_CS_OVR); if (Mark < TailTo) { memmove(Mark, TailTo, TailLen); pchOperandBuf = Mark + TailLen; } else if (Mark >= TailTo + TailLen) { pchOperandBuf = Mark; } else { pchOperandBuf = Mark + (TailLen - (Mark - TailTo)); } }
// if fEAout is set, build each EA with trailing space in EABuf
// point back over final trailing space if buffer nonnull
if (fEAout) { for (indx = 0; indx < 2; indx++) { if (EAsize[indx]) { X86OutputString(&pchEABuf, segOvr ? distbl[segOvr].instruct : g_EaSegNames[indx]); OutputHexAddr(&pchEABuf, &EAaddr[indx]); *pchEABuf++ = '='; if (fFlat(EAaddr[indx]) && m_Target->ReadAllVirtual(Process, Flat(EAaddr[indx]), membuf, EAsize[indx]) == S_OK) { OutputHexString(&pchEABuf, membuf, EAsize[indx]); } else { while (EAsize[indx]--) { *pchEABuf++ = '?'; *pchEABuf++ = '?'; } } *pchEABuf++ = ' '; } }
if (pchEABuf != EABuffer) { pchEABuf--; }
switch(JccEa) { case JCC_EA_NO_BRANCH: X86OutputString(&pchEABuf, "[br=0]"); break; case JCC_EA_BRANCH: X86OutputString(&pchEABuf, "[br=1]"); break; } }
// compute lengths of component strings.
// if the rep string is nonnull,
// add the opcode string length to the operand
// make the rep string the opcode string
cbOffset = (int)(pchResultBuf - pchDst); cbOperand = (int)(pchOperandBuf - OperandBuffer); cbOpcode = (int)(pchOpcodeBuf - OpcodeBuffer); if (pchRepPrefixBuf != RepPrefixBuffer) { cbOperand += cbOpcode + (cbOperand != 0); cbOpcode = (int)(pchRepPrefixBuf - RepPrefixBuffer); } cbEAddr = (int)(pchEABuf - EABuffer);
// compute the minimum and maximum offset values for
// opcode and operand strings.
// if strings are nonnull, add extra for separating space
obOpcodeMin = cbOffset + 1; obOperandMin = obOpcodeMin + cbOpcode + 1; obOperandMax = obLineEnd - cbEAddr - (cbEAddr != 0) - cbOperand; obOpcodeMax = obOperandMax - (cbOperand != 0) - cbOpcode;
// compute the opcode and operand offsets. set offset as
// close to the default values as possible.
if (obOpcodeMin > obOpcode) { obOpcode = obOpcodeMin; } else if (obOpcodeMax < obOpcode) { obOpcode = obOpcodeMax; }
obOperandMin = obOpcode + cbOpcode + 1;
if (obOperandMin > obOperand) { obOperand = obOperandMin; } else if (obOperandMax < obOperand) { obOperand = obOperandMax; }
// build the resultant string with the offsets computed
// output rep, opcode, and operand strings
do { *pchResultBuf++ = ' '; } while (pchResultBuf < pchDst + obOpcode);
if (pchRepPrefixBuf != RepPrefixBuffer) { *pchRepPrefixBuf = '\0'; X86OutputString(&pchResultBuf, RepPrefixBuffer); do { *pchResultBuf++ = ' '; } while (pchResultBuf < pchDst + obOperand); }
*pchOpcodeBuf = '\0'; X86OutputString(&pchResultBuf, OpcodeBuffer);
if (pchOperandBuf != OperandBuffer) { do { *pchResultBuf++ = ' '; } while (pchResultBuf < pchDst + obOperand); *pchOperandBuf = '\0'; X86OutputString(&pchResultBuf, OperandBuffer); }
// append the EAddr string
if (pchEABuf != EABuffer) { *pchEABuf = '\0'; do { *pchResultBuf++ = ' '; } while (pchResultBuf < pchDst + obLineEnd - cbEAddr); X86OutputString(&pchResultBuf, EABuffer); }
*pchResultBuf++ = '\n'; *pchResultBuf = '\0'; return TRUE; }
void BaseX86MachineInfo::GetNextOffset(ProcessInfo* Process, BOOL StepOver, PADDR NextAddr, PULONG NextMachine) { ULONG cBytes; UCHAR membuf[X86_MAX_INSTRUCTION_LEN]; // current instruction buffer
UCHAR *InstrMem; UCHAR opcode; int fPrefix = TRUE; int fRepPrefix = FALSE; int MrmMod; int MrmArg; int MrmRm; ULONG64 instroffset; int subcode;
// NextMachine is always the same.
*NextMachine = m_ExecTypes[0];
// read instruction stream bytes into membuf and set mode and
// opcode size flags
GetPC(NextAddr); instroffset = Flat(*NextAddr); GetSegAddrOpSizes(this, NextAddr);
/* move full inst to local buffer */ if (fnotFlat(*NextAddr) || m_Target->ReadVirtual(Process, Flat(*NextAddr), membuf, X86_MAX_INSTRUCTION_LEN, &cBytes) != S_OK) { cBytes = 0; }
// Ensure that membuf is padded with innocuous bytes in
// the section that wasn't read.
if (cBytes < X86_MAX_INSTRUCTION_LEN) { memset(membuf + cBytes, 0xcc, X86_MAX_INSTRUCTION_LEN - cBytes); }
/* point to begin of instruction */ InstrMem = membuf;
// read and process any prefixes first
do { opcode = *InstrMem++; /* get opcode */ if (opcode == 0x66) { OverrideOpSize(opcode); } else if (m_ExecTypes[0] == IMAGE_FILE_MACHINE_AMD64 && opcode >= 0x40 && opcode <= 0x4f) { ExtendOps(opcode); } else if (opcode == 0x67) { OverrideAddrMode(); } else if ((opcode & ~1) == 0xf2) { fRepPrefix = TRUE; } else if (opcode != 0xf0 && (opcode & ~0x18) != 0x26 && (opcode & ~1) != 0x64) { fPrefix = FALSE; } } while (fPrefix);
// for instructions that alter the TF (trace flag), return the
// offset of the next instruction despite the flag of StepOver
if (((opcode & ~0x3) == 0x9c) && !g_WatchTrace) { // 9c-9f, pushf, popf, sahf, lahf
; } else if (opcode == 0xcf) { ULONG64 RetAddr[2]; ADDR Sp; ULONG Seg;
// cf - iret - get RA from stack
FormSegRegAddress(&Sp, SEGREG_STACK, GetReg64(X86_NSP));
if (fnotFlat(Sp) || m_Target->ReadAllVirtual(Process, Flat(Sp), RetAddr, g_SegAddrMode / 4) != S_OK) { instroffset = OFFSET_TRACE; goto Exit; }
Seg = *(PUSHORT)((PUCHAR)RetAddr + g_SegAddrMode / 8); switch(g_SegAddrMode) { case 16: instroffset = EXTEND64(*(PUSHORT)RetAddr); break; case 32: instroffset = EXTEND64(*(PULONG)RetAddr); break; case 64: instroffset = RetAddr[0]; break; }
FormSelAddress(NextAddr, Seg, instroffset); ComputeFlatAddress(NextAddr, NULL); return; } else if (opcode == 0xc4 && *InstrMem == 0xc4) { subcode = *(InstrMem+1); if ( subcode == 0x50 || subcode == 0x52 || subcode == 0x53 || subcode == 0x54 || subcode == 0x57 || subcode == 0x58 || subcode == 0x5D ) { InstrMem += 3; } else { InstrMem += 2; } } else if (!StepOver) { // if tracing just return OFFSET_TRACE to trace
instroffset = OFFSET_TRACE; } else if (opcode == 0xe8) { // near direct jump
InstrMem += g_OpSize > 16 ? 4 : 2; } else if (opcode == 0x9a) { // far direct jump
InstrMem += g_OpSize > 16 ? 6 : 4; } else if (opcode == 0xcd || (opcode >= 0xe0 && opcode <= 0xe2)) { // loop / int nn instrs
InstrMem++; } else if (opcode == 0xff) { // indirect call - compute length
opcode = *InstrMem++; // get modRM
MrmArg = BIT53(opcode); if ((MrmArg & ~1) == 2) { MrmMod = BIT76(opcode); if (MrmMod != 3) { // nonregister operand
MrmRm = BIT20(opcode); if (g_AddrMode > 16) { if (MrmRm == 4) { MrmRm = BIT20(*InstrMem++); // get base from SIB
} if (MrmMod == 0) { if (MrmRm == 5) { InstrMem += 4; // long direct address
} // else register
} else if (MrmMod == 1) { InstrMem++; // register with byte offset
} else { InstrMem += 4; // register with long offset
} } else { // 16-bit mode
if (MrmMod == 0) { if (MrmRm == 6) { InstrMem += 2; // short direct address
} } else { InstrMem += MrmMod; // reg, byte, word offset
} } } } else { instroffset = OFFSET_TRACE; // 0xff, but not call
} } else if (!((fRepPrefix && ((opcode & ~3) == 0x6c || (opcode & ~3) == 0xa4 || (opcode & ~1) == 0xaa || (opcode & ~3) == 0xac)) || opcode == 0xcc || opcode == 0xce)) { instroffset = OFFSET_TRACE; // not repeated string op
} // or int 3 / into
// if not enough bytes were read for instruction parse,
// just give up and trace the instruction
if (cBytes < (ULONG)(InstrMem - (PUCHAR)membuf)) { instroffset = OFFSET_TRACE; }
Exit: // if not tracing, compute the new instruction offset
if (instroffset != OFFSET_TRACE) { instroffset += InstrMem - (PUCHAR)membuf; }
Flat(*NextAddr) = instroffset; ComputeNativeAddress(NextAddr); }
/*...........................internal function..............................*/ /* */ /* generate a mod/rm string */ /* */
void BaseX86MachineInfo::DIdoModrm(ProcessInfo* Process, char **ppchBuf, int segOvr, BOOL fEAout) { int mrm; /* modrm byte */ char *src; /* source string */ int sib; int ss; int ind; int oldrm;
mrm = *g_InstrMem++; /* get the mrm byte from instruction */ g_MrmMod = BIT76(mrm); /* get mod */ g_MrmArg = BIT53(mrm) + g_ExtendMrmReg; /* get reg - used outside routine */ g_MrmRm = BIT20(mrm); /* get rm */
if (g_MrmMod == 3) { g_MrmRm += g_ExtendRm;
/* register only mode */ if (g_XmmRegEa) { *(*ppchBuf)++ = 'x'; *(*ppchBuf)++ = 'm'; *(*ppchBuf)++ = 'm'; if (g_MrmRm >= 10) { *(*ppchBuf)++ = (char)('0' + g_MrmRm / 10); g_MrmRm %= 10; } *(*ppchBuf)++ = g_MrmRm + '0'; } else if (g_MmRegEa) { *(*ppchBuf)++ = 'm'; *(*ppchBuf)++ = 'm'; *(*ppchBuf)++ = g_MrmRm + '0'; } else { if (EAsize[0] == 1) { /* point to 8-bit register */ if (g_ExtendAny && g_MrmRm < 8) { src = g_Amd64ExtendedReg8[g_MrmRm]; } else { src = g_X86Reg8[g_MrmRm]; } X86OutputString(ppchBuf, src); } else { src = g_X86RegBase[g_MrmRm]; if (g_ForceMrmReg32) { *(*ppchBuf)++ = 'e'; } else if (g_OpSize > 16 && (!g_MovX || g_MrmRm >= 8)) { /* make it a 32- or 64-bit register */ *(*ppchBuf)++ = (g_MrmRm >= 8 || g_OpSize == 64 && !g_MovSXD) ? 'r' : 'e'; } X86OutputString(ppchBuf, src); if (g_MrmRm >= 8) { if (g_OpSize == 32) { *(*ppchBuf)++ = 'd'; } else if (g_OpSize == 16) { *(*ppchBuf)++ = 'w'; } }
if (g_ControlFlow && fEAout) { // This is a call/jmp through a register.
// Output a code symbol for the target.
ULONG64 Target = GetReg64(g_X86RegIdx[g_MrmRm]); *(*ppchBuf)++ = ' '; *(*ppchBuf)++ = '{'; OutputSymbol(ppchBuf, (PUCHAR)&Target, g_OpSize / 8, X86_CS_OVR); *(*ppchBuf)++ = '}'; } } } EAsize[0] = 0; // no EA value to output
return; }
if (g_AddrMode == 64) { oldrm = g_MrmRm; if (g_MrmRm == 4) { /* g_MrmRm == 4 implies sib byte */ sib = *g_InstrMem++; /* get s_i_b byte */ g_MrmRm = BIT20(sib); }
*(*ppchBuf)++ = '['; if (g_MrmMod == 0 && g_MrmRm == 5) { if (g_SegAddrMode == 64 && oldrm == 5) { // IP-relative 32-bit displacement. The
// displacement is relative to the IP of the
// next instruction, which can't be computed
// yet so just put in a marker for post-processing.
g_X86ModrmHasIpRelOffset = TRUE; g_X86IpRelOffset = *(LONG UNALIGNED *)g_InstrMem; X86OutputString(ppchBuf, IPREL_MARKER); } else { // Absolute 32-bit displacement.
OutputSymbol(ppchBuf, g_InstrMem, 4, segOvr); }
g_InstrMem += 4; } else { g_MrmRm += g_ExtendRm;
if (fEAout) { if (segOvr) { FormSegRegAddress(&EAaddr[0], GetSegReg(segOvr), GetReg64(g_X86RegIdx[g_MrmRm])); g_EaSegNames[0] = distbl[segOvr].instruct; } else if (g_X86RegIdx[g_MrmRm] == X86_NBP || g_X86RegIdx[g_MrmRm] == X86_NSP) { FormSegRegAddress(&EAaddr[0], SEGREG_STACK, GetReg64(g_X86RegIdx[g_MrmRm])); g_EaSegNames[0] = dszSS_; } else { FormSegRegAddress(&EAaddr[0], SEGREG_DATA, GetReg64(g_X86RegIdx[g_MrmRm])); } } X86OutputString(ppchBuf, g_X86Mrm64[g_MrmRm]); }
if (oldrm == 4) { // finish processing sib
ind = BIT53(sib); if (ind != 4) { ind += g_ExtendSibIndex; *(*ppchBuf)++ = '+'; X86OutputString(ppchBuf, g_X86Mrm64[ind]); ss = 1 << BIT76(sib); if (ss != 1) { *(*ppchBuf)++ = '*'; *(*ppchBuf)++ = (char)(ss + '0'); } if (fEAout) { AddrAdd(&EAaddr[0], GetReg64(g_X86RegIdx[ind]) * ss); } } } } else if (g_AddrMode == 32) { oldrm = g_MrmRm; if (g_MrmRm == 4) { /* g_MrmRm == 4 implies sib byte */ sib = *g_InstrMem++; /* get s_i_b byte */ g_MrmRm = BIT20(sib); }
*(*ppchBuf)++ = '['; if (g_MrmMod == 0 && g_MrmRm == 5) { if (g_SegAddrMode == 64 && oldrm == 5) { // IP-relative 32-bit displacement. The
// displacement is relative to the IP of the
// next instruction, which can't be computed
// yet so just put in a marker for post-processing.
g_X86ModrmHasIpRelOffset = TRUE; g_X86IpRelOffset = *(LONG UNALIGNED *)g_InstrMem; X86OutputString(ppchBuf, IPREL_MARKER); } else { // Absolute 32-bit displacement.
OutputSymbol(ppchBuf, g_InstrMem, 4, segOvr); }
g_InstrMem += 4; } else { g_MrmRm += g_ExtendRm;
if (fEAout) { if (segOvr) { FormSegRegAddress(&EAaddr[0], GetSegReg(segOvr), EXTEND64(GetReg32(g_X86RegIdx[g_MrmRm]))); g_EaSegNames[0] = distbl[segOvr].instruct; } else if (g_X86RegIdx[g_MrmRm] == X86_NBP || g_X86RegIdx[g_MrmRm] == X86_NSP) { FormSegRegAddress(&EAaddr[0], SEGREG_STACK, EXTEND64(GetReg32(g_X86RegIdx[g_MrmRm]))); g_EaSegNames[0] = dszSS_; } else { FormSegRegAddress(&EAaddr[0], SEGREG_DATA, EXTEND64(GetReg32(g_X86RegIdx[g_MrmRm]))); } } X86OutputString(ppchBuf, g_X86Mrm32[g_MrmRm]); }
if (oldrm == 4) { // finish processing sib
ind = BIT53(sib); if (ind != 4) { ind += g_ExtendSibIndex; *(*ppchBuf)++ = '+'; X86OutputString(ppchBuf, g_X86Mrm32[ind]); ss = 1 << BIT76(sib); if (ss != 1) { *(*ppchBuf)++ = '*'; *(*ppchBuf)++ = (char)(ss + '0'); } if (fEAout) { AddrAdd(&EAaddr[0], EXTEND64(GetReg32(g_X86RegIdx[ind])) * ss); } } } } else { // 16-bit addressing mode
*(*ppchBuf)++ = '['; if (g_MrmMod == 0 && g_MrmRm == 6) { OutputSymbol(ppchBuf, g_InstrMem, 2, segOvr); // 16-bit offset
g_InstrMem += 2; } else { if (fEAout) { if (segOvr) { FormSegRegAddress(&EAaddr[0], GetSegReg(segOvr), GetReg16(g_X86Reg16Idx[g_MrmRm])); g_EaSegNames[0] = distbl[segOvr].instruct; } else if (g_X86Reg16Idx[g_MrmRm] == X86_NBP) { FormSegRegAddress(&EAaddr[0], SEGREG_STACK, GetReg16(g_X86Reg16Idx[g_MrmRm])); g_EaSegNames[0] = dszSS_; } else { FormSegRegAddress(&EAaddr[0], SEGREG_DATA, GetReg16(g_X86Reg16Idx[g_MrmRm])); } if (g_MrmRm < 4) { AddrAdd(&EAaddr[0], GetReg16(g_X86Reg16Idx2[g_MrmRm])); } } X86OutputString(ppchBuf, g_X86Mrm16[g_MrmRm]); } }
// output any displacement
if (g_MrmMod == 1) { if (fEAout) { AddrAdd(&EAaddr[0], (long)*(char *)g_InstrMem); } OutputHexValue(ppchBuf, g_InstrMem, 1, TRUE); g_InstrMem++; } else if (g_MrmMod == 2) { long tmp = 0; if (g_AddrMode > 16) { memmove(&tmp, g_InstrMem, sizeof(long)); if (fEAout) { AddrAdd(&EAaddr[0], tmp); } OutputHexValue(ppchBuf, g_InstrMem, 4, TRUE); g_InstrMem += 4; } else { memmove(&tmp,g_InstrMem,sizeof(short)); if (fEAout) { AddrAdd(&EAaddr[0], tmp); } OutputHexValue(ppchBuf, g_InstrMem, 2, TRUE); g_InstrMem += 2; } }
if (g_AddrMode == 16 && fEAout) { Off(EAaddr[0]) &= 0xffff; NotFlat(EAaddr[0]); Off(EAaddr[1]) &= 0xffff; NotFlat(EAaddr[1]); ComputeFlatAddress(&EAaddr[0], NULL); ComputeFlatAddress(&EAaddr[1], NULL); }
*(*ppchBuf)++ = ']';
// The value at the effective address may be pointing to an interesting
// symbol, as with indirect jumps or memory operations.
// If there's an EA and an exact symbol match, display
// the extra symbol.
if (fEAout) { DWORD64 symbol;
if (m_Target-> ReadPointer(Process, this, Flat(EAaddr[0]), &symbol) == S_OK) { char* pchBuf = *ppchBuf;
(*ppchBuf)++; if (OutputExactSymbol(ppchBuf, (PUCHAR)&symbol, m_Ptr64 ? 8 : 4, segOvr)) { *pchBuf = '{'; *(*ppchBuf)++ = '}'; } else { (*ppchBuf)--; } } } }
LONGLONG GetSignExtendedValue(int OpLen, PUCHAR Mem) { switch(OpLen) { case 1: return *(char *)Mem; case 2: return *(short UNALIGNED *)Mem; case 4: return *(long UNALIGNED *)Mem; case 8: return *(LONGLONG UNALIGNED *)Mem; }
DBG_ASSERT(FALSE); return 0; }
/*** OutputHexValue - output hex value
* 07-Jun-1999 -by- Andre Vachon * Purpose: * Output the value pointed by *ppchBuf of the specified * length. The value is treated as signed and leading * zeroes are not printed. The string is prefaced by a * '+' or '-' sign as appropriate. * * Input: * *ppchBuf - pointer to text buffer to fill * *pchMemBuf - pointer to memory buffer to extract value * length - length in bytes of value (1, 2, and 4 supported) * fDisp - set if displacement to output '+' * * Output: * *ppchBuf - pointer updated to next text character * *************************************************************************/
void OutputHexValue (char **ppchBuf, PUCHAR pchMemBuf, int length, int fDisp) { LONGLONG value; int index; char digit[32];
value = GetSignExtendedValue(length, pchMemBuf);
length <<= 1; // shift once to get hex length
if (value != 0 || !fDisp) { if (fDisp) { // use neg value for byte displacement
// assume very large DWORDs are negative too
if (value < 0 && (length == 2 || ((unsigned long)value & 0xff000000) == 0xff000000)) { value = -value; *(*ppchBuf)++ = '-'; } else { *(*ppchBuf)++ = '+'; } }
*(*ppchBuf)++ = '0'; *(*ppchBuf)++ = 'x'; for (index = length - 1; index != -1; index--) { digit[index] = (char)(value & 0xf); value >>= 4; } index = 0; while (digit[index] == 0 && index < length - 1) { index++; } while (index < length) { *(*ppchBuf)++ = hexdigit[digit[index++]]; } } }
void OutputExHexValue(char **ppchBuf, PUCHAR pchMemBuf, int MemLen, int OpLen) { LONGLONG Value = GetSignExtendedValue(MemLen, pchMemBuf); OutputHexValue(ppchBuf, (PUCHAR)&Value, OpLen, FALSE); }
/*** OutputHexString - output hex string
* * Purpose: * Output the value pointed by *ppchMemBuf of the specified * length. The value is treated as unsigned and leading * zeroes are printed. * * Input: * *ppchBuf - pointer to text buffer to fill * *pchValue - pointer to memory buffer to extract value * length - length in bytes of value * * Output: * *ppchBuf - pointer updated to next text character * *ppchMemBuf - pointer update to next memory byte * *************************************************************************/
void OutputHexString (char **ppchBuf, PUCHAR pchValue, int length) { UCHAR chMem;
pchValue += length; while (length--) { chMem = *--pchValue; *(*ppchBuf)++ = hexdigit[chMem >> 4]; *(*ppchBuf)++ = hexdigit[chMem & 0x0f]; } }
/*** OutputHexCode - output hex code
* * Purpose: * Output the code pointed by pchMemBuf of the specified * length. The value is treated as unsigned and leading * zeroes are printed. This differs from OutputHexString * in that bytes are printed from low to high addresses. * * Input: * *ppchBuf - pointer to text buffer to fill * pchMemBuf - pointer to memory buffer to extract value * length - length in bytes of value * * Output: * *ppchBuf - pointer updated to next text character * *************************************************************************/
void OutputHexCode (char **ppchBuf, PUCHAR pchMemBuf, int length) { UCHAR chMem;
while (length--) { chMem = *pchMemBuf++; *(*ppchBuf)++ = hexdigit[chMem >> 4]; *(*ppchBuf)++ = hexdigit[chMem & 0x0f]; } }
/*** X86OutputString - output string
* * Purpose: * Copy the string into the buffer pointed by *ppBuf. * * Input: * *pStr - pointer to string * * Output: * *ppBuf points to next character in buffer. * *************************************************************************/
void X86OutputString ( char **ppBuf, char *pStr ) { while (*pStr) { *(*ppBuf)++ = *pStr++; } }
/*** OutputSymbol - output symbolic value
* * Purpose: * Output the value in outvalue into the buffer * pointed by *pBuf. Express the value as a * symbol plus displacment, if possible. * * Input: * *ppBuf - pointer to text buffer to fill * *pValue - pointer to memory buffer to extract value * length - length in bytes of value * * Output: * *ppBuf - pointer updated to next text character * *************************************************************************/
void BaseX86MachineInfo::OutputSymbol ( char **ppBuf, PUCHAR pValue, int length, int segOvr ) { CHAR chSymbol[MAX_SYMBOL_LEN]; ULONG64 displacement; ULONG64 value;
value = 0; memcpy(&value, pValue, length); if (length == 4) { value = EXTEND64(value); }
if (IS_CONTEXT_POSSIBLE(m_Target)) { FormSegRegAddress(&EAaddr[0], GetSegReg(segOvr), value); value = Flat(EAaddr[0]); }
GetSymbol(value, chSymbol, sizeof(chSymbol), &displacement); if (chSymbol[0]) { X86OutputString(ppBuf, chSymbol); OutputHexValue(ppBuf, (PUCHAR)&displacement, length, TRUE); *(*ppBuf)++ = ' '; *(*ppBuf)++ = '('; OutputHexString(ppBuf, pValue, length); *(*ppBuf)++ = ')'; } else { OutputHexString(ppBuf, pValue, length); } }
/*** OutputExactSymbol - Output symbolic value only for exact symbol
* matches. * *************************************************************************/
BOOL BaseX86MachineInfo::OutputExactSymbol ( char **ppBuf, PUCHAR pValue, int length, int segOvr ) { CHAR chSymbol[MAX_SYMBOL_LEN]; ULONG64 displacement; ULONG64 value;
value = 0; memcpy(&value, pValue, length); if (length == 4) { value = EXTEND64(value); }
GetSymbol(value, chSymbol, sizeof(chSymbol), &displacement); if (chSymbol[0] && displacement == 0) { X86OutputString(ppBuf, chSymbol); OutputHexValue(ppBuf, (PUCHAR)&displacement, length, TRUE); *(*ppBuf)++ = ' '; *(*ppBuf)++ = '('; OutputHexString(ppBuf, pValue, length); *(*ppBuf)++ = ')'; return TRUE; } else { return FALSE; } }
void OutputHexAddr(PSTR *ppBuffer, PADDR paddr) { sprintAddr(ppBuffer, paddr); // Remove trailing space.
(*ppBuffer)--; **ppBuffer = 0; }
ULONG BaseX86MachineInfo::GetSegReg(int SegOpcode) { switch(SegOpcode) { case 0x26: return SEGREG_ES; case X86_CS_OVR: return SEGREG_CODE; case 0x36: return SEGREG_STACK; case 0x64: return SEGREG_FS; case 0x65: return SEGREG_GS; case 0x3e: default: return SEGREG_DATA; } }
int BaseX86MachineInfo::ComputeJccEa(int Opcode, BOOL EaOut) { if (!EaOut) { return JCC_EA_NONE; }
ULONG Flags; int Branch;
if ((Opcode >= 0x70 && Opcode <= 0x7f) || (Opcode >= 0x180 && Opcode <= 0x18f)) { int Table = (Opcode >> 1) & 7;
Flags = GetReg32(X86_NFL); Branch = Opcode & 1; if ((Flags & g_JccCheckTable[Table][0]) != 0 || ((Flags >> g_JccCheckTable[Table][1]) & 1) != ((Flags >> g_JccCheckTable[Table][2]) & 1)) { Branch ^= 1; }
return JCC_EA_NO_BRANCH + Branch; } else { ULONG64 Cx = GetReg64(X86_NCX); switch(g_OpSize) { case 16: Cx &= 0xffff; break; case 32: Cx &= 0xffffffff; break; }
switch(Opcode) { case 0xe0: // LOOPNE.
Flags = GetReg32(X86_NFL); Branch = (Flags & X86_BIT_FLAGZF) == 0 && Cx != 1 ? JCC_EA_BRANCH : JCC_EA_NO_BRANCH; break; case 0xe1: // LOOPE.
Flags = GetReg32(X86_NFL); Branch = (Flags & X86_BIT_FLAGZF) != 0 && Cx != 1 ? JCC_EA_BRANCH : JCC_EA_NO_BRANCH; break; case 0xe2: // LOOP.
Branch = Cx == 1 ? JCC_EA_NO_BRANCH : JCC_EA_BRANCH; break; case 0xe3: // J*CXZ.
Branch = Cx == 0 ? JCC_EA_BRANCH : JCC_EA_NO_BRANCH; break; default: DBG_ASSERT(FALSE); Branch = JCC_EA_NONE; break; }
return Branch; } }
BOOL BaseX86MachineInfo::IsBreakpointInstruction(ProcessInfo* Process, PADDR Addr) { UCHAR Instr[X86_INT3_LEN];
if (fnotFlat(*Addr) || m_Target->ReadAllVirtual(Process, Flat(*Addr), Instr, X86_INT3_LEN) != S_OK) { return FALSE; }
return !memcmp(Instr, g_X86Int3, X86_INT3_LEN); }
HRESULT BaseX86MachineInfo::InsertBreakpointInstruction(PUSER_DEBUG_SERVICES Services, ULONG64 Process, ULONG64 Offset, ULONG Flags, PUCHAR SaveInstr, PULONG64 ChangeStart, PULONG ChangeLen) { if (Flags != IBI_DEFAULT) { return E_INVALIDARG; }
if ((m_Target->m_MachineType != IMAGE_FILE_MACHINE_I386) && (g_Wow64exts != NULL)) { ProcessInfo* ProcInfo = m_Target->FindProcessByHandle(Process); if (ProcInfo != NULL) { (*g_Wow64exts)(WOW64EXTS_FLUSH_CACHE_WITH_HANDLE, ProcInfo->m_SysHandle, Offset, X86_INT3_LEN); } }
*ChangeStart = Offset; *ChangeLen = X86_INT3_LEN;
ULONG Done; HRESULT Status;
Status = Services->ReadVirtual(Process, Offset, SaveInstr, X86_INT3_LEN, &Done); if (Status == S_OK && Done != X86_INT3_LEN) { Status = HRESULT_FROM_WIN32(ERROR_READ_FAULT); }
if (Status == S_OK) { Status = Services->WriteVirtual(Process, Offset, g_X86Int3, X86_INT3_LEN, &Done); if (Status == S_OK && Done != X86_INT3_LEN) { Status = HRESULT_FROM_WIN32(ERROR_WRITE_FAULT); } }
return Status; }
HRESULT BaseX86MachineInfo::RemoveBreakpointInstruction(PUSER_DEBUG_SERVICES Services, ULONG64 Process, ULONG64 Offset, PUCHAR SaveInstr, PULONG64 ChangeStart, PULONG ChangeLen) { if ((m_Target->m_MachineType != IMAGE_FILE_MACHINE_I386) && (g_Wow64exts != NULL)) { ProcessInfo* ProcInfo = m_Target->FindProcessByHandle(Process); if (ProcInfo != NULL) { (*g_Wow64exts)(WOW64EXTS_FLUSH_CACHE_WITH_HANDLE, ProcInfo->m_SysHandle, Offset, X86_INT3_LEN); } }
*ChangeStart = Offset; *ChangeLen = X86_INT3_LEN;
ULONG Done; HRESULT Status;
Status = Services->WriteVirtual(Process, Offset, SaveInstr, X86_INT3_LEN, &Done); if (Status == S_OK && Done != X86_INT3_LEN) { Status = HRESULT_FROM_WIN32(ERROR_WRITE_FAULT); } return Status; }
void BaseX86MachineInfo::AdjustPCPastBreakpointInstruction(PADDR Addr, ULONG BreakType) { if (BreakType == DEBUG_BREAKPOINT_CODE) { AddrAdd(Addr, X86_INT3_LEN); SetPC(Addr); } }
BOOL BaseX86MachineInfo::IsCallDisasm(PCSTR Disasm) { return strstr(Disasm, " call") != NULL; }
BOOL BaseX86MachineInfo::IsReturnDisasm(PCSTR Disasm) { return strstr(Disasm, " ret") != NULL || (IS_KERNEL_TARGET(m_Target) && strstr(Disasm, " iretd") != NULL); }
BOOL BaseX86MachineInfo::IsSystemCallDisasm(PCSTR Disasm) { return (strstr(Disasm, " int ") != NULL && strstr(Disasm, " 2e") != NULL) || strstr(Disasm, " sysenter") != NULL || strstr(Disasm, " syscall") != NULL; }
BOOL BaseX86MachineInfo::IsDelayInstruction(PADDR Addr) { // X86 does not have delay slots.
return FALSE; }
void BaseX86MachineInfo::GetEffectiveAddr(PADDR Addr, PULONG Size) { *Addr = EAaddr[0]; *Size = EAsize[0]; }
void BaseX86MachineInfo::IncrementBySmallestInstruction(PADDR Addr) { AddrAdd(Addr, 1); }
void BaseX86MachineInfo::DecrementBySmallestInstruction(PADDR Addr) { AddrSub(Addr, 1); }
//----------------------------------------------------------------------------
//
// X86MachineInfo methods.
//
//----------------------------------------------------------------------------
HRESULT X86MachineInfo::NewBreakpoint(DebugClient* Client, ULONG Type, ULONG Id, Breakpoint** RetBp) { HRESULT Status;
switch(Type & (DEBUG_BREAKPOINT_CODE | DEBUG_BREAKPOINT_DATA)) { case DEBUG_BREAKPOINT_CODE: *RetBp = new CodeBreakpoint(Client, Id, IMAGE_FILE_MACHINE_I386); Status = (*RetBp) ? S_OK : E_OUTOFMEMORY; break; case DEBUG_BREAKPOINT_DATA: *RetBp = new X86DataBreakpoint(Client, Id, X86_CR4, X86_DR6, IMAGE_FILE_MACHINE_I386); Status = (*RetBp) ? S_OK : E_OUTOFMEMORY; break; default: // Unknown breakpoint type.
Status = E_NOINTERFACE; }
return Status; }
void X86MachineInfo::InsertThreadDataBreakpoints(void) { ULONG Dr7Value;
BpOut("Thread %d data breaks %d\n", g_Thread->m_UserId, g_Thread->m_NumDataBreaks);
// Start with all breaks turned off.
Dr7Value = GetIntReg(X86_DR7) & ~X86_DR7_CTRL_03_MASK;
if (g_Thread->m_NumDataBreaks > 0) { ULONG i;
for (i = 0; i < g_Thread->m_NumDataBreaks; i++) { X86DataBreakpoint* Bp = (X86DataBreakpoint *)g_Thread->m_DataBreakBps[i];
ULONG64 Addr = Flat(*Bp->GetAddr()); BpOut(" dbp %d at %p\n", i, Addr); if (g_DataBreakpointsChanged) { SetReg32(X86_DR0 + i, (ULONG)Addr); } // There are two enable bits per breakpoint
// and four len/rw bits so split up enables
// and len/rw when shifting into place.
Dr7Value |= ((Bp->m_Dr7Bits & 0xffff0000) << (i * 4)) | ((Bp->m_Dr7Bits & X86_DR7_ALL_ENABLES) << (i * 2)); }
// The kernel automatically clears DR6 when it
// processes a DBGKD_CONTROL_SET.
if (IS_USER_TARGET(m_Target)) { SetReg32(X86_DR6, 0); }
// Set local exact match, which is effectively global on NT.
Dr7Value |= X86_DR7_LOCAL_EXACT_ENABLE; }
BpOut(" thread %d DR7 %X\n", g_Thread->m_UserId, Dr7Value); SetReg32(X86_DR7, Dr7Value); }
void X86MachineInfo::RemoveThreadDataBreakpoints(void) { SetReg32(X86_DR7, 0); }
ULONG X86MachineInfo::IsBreakpointOrStepException(PEXCEPTION_RECORD64 Record, ULONG FirstChance, PADDR BpAddr, PADDR RelAddr) { if (Record->ExceptionCode == STATUS_BREAKPOINT || Record->ExceptionCode == STATUS_WX86_BREAKPOINT) { // Data breakpoints hit as STATUS_SINGLE_STEP so
// this can only be a code breakpoint.
if (IS_USER_TARGET(m_Target) && FirstChance) { // Back up to the actual breakpoint instruction.
AddrSub(BpAddr, X86_INT3_LEN); SetPC(BpAddr); } return EXBS_BREAKPOINT_CODE; } else if (Record->ExceptionCode == STATUS_SINGLE_STEP || Record->ExceptionCode == STATUS_WX86_SINGLE_STEP) { // XXX t-tcheng - Conversion for Dr6, Dr7 not implemented yet...
ULONG Dr6 = GetIntReg(X86_DR6); ULONG Dr7 = GetIntReg(X86_DR7);
BpOut("X86 step: DR6 %X, DR7 %X\n", Dr6, Dr7);
// The single step bit should always be clear if a data breakpoint
// is hit but also check the DR7 enables just in case.
// We've also seen cases where DR6 shows no hits, so consider
// that a single step also.
if ((Dr6 & X86_DR6_SINGLE_STEP) || (Dr7 & X86_DR7_ALL_ENABLES) == 0 || (Dr6 & X86_DR6_BREAK_03) == 0) { // There's no way to tell if this particular
// step was a branch step or not so only
// try to look up the branch source if we're
// in branch-trace mode.
if (m_SupportsBranchTrace && g_CmdState == 'b') { HRESULT Status; ULONG64 LastIp;
// The Pentium IV handles last-branch tracking
// differently from the P6.
if (m_Target->m_FirstProcessorId.X86.Family >= 15) { ULONG64 LbrTos;
Status = m_Target->ReadMsr(X86_MSR_LAST_BRANCH_TOS, &LbrTos); if (Status == S_OK) { Status = m_Target->ReadMsr(X86_MSR_LAST_BRANCH_0 + (ULONG)LbrTos, &LastIp); // The result is a 64-bit value with the
// from address in the upper 32-bits.
LastIp >>= 32; } } else { Status = m_Target->ReadMsr(X86_MSR_LAST_BRANCH_FROM_IP, &LastIp); }
if (Status == S_OK) { // The branch may have come from a different
// segment. We could try and determine what
// code segment it was by reading the stack to
// get the saved CS value but it's not worth
// it right now.
FormAddr(SEGREG_CODE, EXTEND64(LastIp), FORM_CODE | FORM_SEGREG | X86_FORM_VM86(GetIntReg(X86_EFL)), RelAddr); } }
// This is a true single step exception, not
// a data breakpoint.
return EXBS_STEP_INSTRUCTION; } else { // Some data breakpoint must be hit.
// There doesn't appear to be any way to get the
// faulting instruction address so just leave the PC.
return EXBS_BREAKPOINT_DATA; } }
return EXBS_NONE; }
void X86MachineInfo::PrintStackFrameAddressesTitle(ULONG Flags) { PrintMultiPtrTitle("ChildEBP", 1); PrintMultiPtrTitle("RetAddr", 1); }
void X86MachineInfo::PrintStackFrameAddresses(ULONG Flags, PDEBUG_STACK_FRAME StackFrame) { dprintf("%s %s ", FormatAddr64(StackFrame->FrameOffset), FormatAddr64(StackFrame->ReturnOffset)); }
void X86MachineInfo::PrintStackArgumentsTitle(ULONG Flags) { PrintMultiPtrTitle("Args to Child", 3); }
void X86MachineInfo::PrintStackArguments(ULONG Flags, PDEBUG_STACK_FRAME StackFrame) { dprintf("%s %s %s ", FormatAddr64(StackFrame->Params[0]), FormatAddr64(StackFrame->Params[1]), FormatAddr64(StackFrame->Params[2])); }
void X86MachineInfo::PrintStackCallSiteTitle(ULONG Flags) { }
void X86MachineInfo::PrintStackCallSite(ULONG Flags, PDEBUG_STACK_FRAME StackFrame, PSYMBOL_INFO SiteSymbol, PSTR SymName, DWORD64 Displacement) { // Truncate the displacement to 32 bits since it can never be
// greater than 32 bit for X86, and we don't want addresses with no
// symbols to show up with the leading 0xfffffff
MachineInfo::PrintStackCallSite(Flags, StackFrame, SiteSymbol, SymName, (DWORD64)(DWORD)Displacement);
if (!(Flags & DEBUG_STACK_FUNCTION_INFO)) { return; }
if (StackFrame->FuncTableEntry) { PFPO_DATA FpoData = (PFPO_DATA)StackFrame->FuncTableEntry; switch(FpoData->cbFrame) { case FRAME_FPO: if (FpoData->fHasSEH) { dprintf(" (FPO: [SEH])"); } else { dprintf(" (FPO:"); if (FpoData->fUseBP) { dprintf(" [EBP 0x%s]", FormatAddr64(SAVE_EBP(StackFrame))); } dprintf(" [%d,%d,%d])", FpoData->cdwParams, FpoData->cdwLocals, FpoData->cbRegs); } break;
case FRAME_NONFPO: dprintf(" (FPO: [Non-Fpo])" ); break;
case FRAME_TRAP: if (!IS_KERNEL_TARGET(m_Target)) { goto UnknownFpo; }
dprintf(" (FPO: [%d,%d] TrapFrame%s @ %s)", FpoData->cdwParams, FpoData->cdwLocals, TRAP_EDITED(StackFrame) ? "" : "-EDITED", FormatAddr64(SAVE_TRAP(StackFrame))); break;
case FRAME_TSS: if (!IS_KERNEL_TARGET(m_Target)) { goto UnknownFpo; }
dprintf(" (FPO: TaskGate %lx:0)", (ULONG)TRAP_TSS(StackFrame)); break;
default: UnknownFpo: dprintf(" (UNKNOWN FPO TYPE)"); break; } }
if (SiteSymbol->Tag == SymTagFunction) { ULONG CallConv;
// Look up the type symbol for the function.
if (SymGetTypeInfo(g_Process->m_SymHandle, SiteSymbol->ModBase, SiteSymbol->TypeIndex, TI_GET_CALLING_CONVENTION, &CallConv) && CallConv < CV_CALL_RESERVED) { dprintf(" (CONV: %s)", g_CallConv[CallConv]); } } }
void X86MachineInfo::PrintStackFrameMemoryUsage(PDEBUG_STACK_FRAME CurFrame, PDEBUG_STACK_FRAME PrevFrame) { if (CurFrame->FrameOffset >= PrevFrame->FrameOffset) { dprintf(" %6x ", (ULONG)(CurFrame->FrameOffset - PrevFrame->FrameOffset)); } else { dprintf(" "); } }
|