summaryrefslogtreecommitdiff
path: root/src/debug/ee/amd64/amd64walker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug/ee/amd64/amd64walker.cpp')
-rw-r--r--src/debug/ee/amd64/amd64walker.cpp1181
1 files changed, 1181 insertions, 0 deletions
diff --git a/src/debug/ee/amd64/amd64walker.cpp b/src/debug/ee/amd64/amd64walker.cpp
new file mode 100644
index 0000000000..836d21486e
--- /dev/null
+++ b/src/debug/ee/amd64/amd64walker.cpp
@@ -0,0 +1,1181 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+//*****************************************************************************
+// File: Amd64walker.cpp
+//
+
+//
+// AMD64 instruction decoding/stepping logic
+//
+//*****************************************************************************
+
+#include "stdafx.h"
+
+#include "walker.h"
+
+#include "frames.h"
+#include "openum.h"
+
+#ifdef _TARGET_AMD64_
+
+//
+// The AMD64 walker is currently pretty minimal. It only recognizes call and return opcodes, plus a few jumps. The rest
+// is treated as unknown.
+//
+void NativeWalker::Decode()
+{
+ const BYTE *ip = m_ip;
+
+ m_type = WALK_UNKNOWN;
+ m_skipIP = NULL;
+ m_nextIP = NULL;
+
+ BYTE rex = NULL;
+
+ LOG((LF_CORDB, LL_INFO100000, "NW:Decode: m_ip 0x%x\n", m_ip));
+
+ BYTE prefix = *ip;
+ if (prefix == 0xcc)
+ {
+ prefix = (BYTE)DebuggerController::GetPatchedOpcode(m_ip);
+ LOG((LF_CORDB, LL_INFO100000, "NW:Decode 1st byte was patched, might have been prefix\n"));
+ }
+
+ //
+ // Skip instruction prefixes
+ //
+ do
+ {
+ switch (prefix)
+ {
+ // Segment overrides
+ case 0x26: // ES
+ case 0x2E: // CS
+ case 0x36: // SS
+ case 0x3E: // DS
+ case 0x64: // FS
+ case 0x65: // GS
+
+ // Size overrides
+ case 0x66: // Operand-Size
+ case 0x67: // Address-Size
+
+ // Lock
+ case 0xf0:
+
+ // String REP prefixes
+ case 0xf2: // REPNE/REPNZ
+ case 0xf3:
+ LOG((LF_CORDB, LL_INFO10000, "NW:Decode: prefix:%0.2x ", prefix));
+ ip++;
+ continue;
+
+ // REX register extension prefixes
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4a:
+ case 0x4b:
+ case 0x4c:
+ case 0x4d:
+ case 0x4e:
+ case 0x4f:
+ LOG((LF_CORDB, LL_INFO10000, "NW:Decode: REX prefix:%0.2x ", prefix));
+ // make sure to set rex to prefix, not *ip because *ip still represents the
+ // codestream which has a 0xcc in it.
+ rex = prefix;
+ ip++;
+ continue;
+
+ default:
+ break;
+ }
+ } while (0);
+
+ // Read the opcode
+ m_opcode = *ip++;
+
+ LOG((LF_CORDB, LL_INFO100000, "NW:Decode: ip 0x%x, m_opcode:%0.2x\n", ip, m_opcode));
+
+ // Don't remove this, when we did the check above for the prefix we didn't modify the codestream
+ // and since m_opcode was just taken directly from the code stream it will be patched if we
+ // didn't have a prefix
+ if (m_opcode == 0xcc)
+ {
+ m_opcode = (BYTE)DebuggerController::GetPatchedOpcode(m_ip);
+ LOG((LF_CORDB, LL_INFO100000, "NW:Decode after patch look up: m_opcode:%0.2x\n", m_opcode));
+ }
+
+ // Setup rex bits if needed
+ BYTE rex_b = 0;
+ BYTE rex_x = 0;
+ BYTE rex_r = 0;
+
+ if (rex != NULL)
+ {
+ rex_b = (rex & 0x1); // high bit to modrm r/m field or SIB base field or OPCODE reg field -- Hmm, when which?
+ rex_x = (rex & 0x2) >> 1; // high bit to sib index field
+ rex_r = (rex & 0x4) >> 2; // high bit to modrm reg field
+ }
+
+ // Analyze what we can of the opcode
+ switch (m_opcode)
+ {
+ case 0xff:
+ {
+
+ BYTE modrm = *ip++;
+
+ _ASSERT(modrm != NULL);
+
+ BYTE mod = (modrm & 0xC0) >> 6;
+ BYTE reg = (modrm & 0x38) >> 3;
+ BYTE rm = (modrm & 0x07);
+
+ reg |= (rex_r << 3);
+ rm |= (rex_b << 3);
+
+ if ((reg < 2) || (reg > 5 && reg < 8) || (reg > 15)) {
+ // not a valid register for a CALL or BRANCH
+ return;
+ }
+
+ BYTE *result;
+ WORD displace;
+
+ // See: Tables A-15,16,17 in AMD Dev Manual 3 for information
+ // about how the ModRM/SIB/REX bytes interact.
+
+ switch (mod)
+ {
+ case 0:
+ case 1:
+ case 2:
+ if ((rm & 0x07) == 4) // we have an SIB byte following
+ {
+ //
+ // Get values from the SIB byte
+ //
+ BYTE sib = *ip;
+
+ _ASSERT(sib != NULL);
+
+ BYTE ss = (sib & 0xC0) >> 6;
+ BYTE index = (sib & 0x38) >> 3;
+ BYTE base = (sib & 0x07);
+
+ index |= (rex_x << 3);
+ base |= (rex_b << 3);
+
+ ip++;
+
+ //
+ // Get starting value
+ //
+ if ((mod == 0) && ((base & 0x07) == 5))
+ {
+ result = 0;
+ }
+ else
+ {
+ result = (BYTE *)(size_t)GetRegisterValue(base);
+ }
+
+ //
+ // Add in the [index]
+ //
+ if (index != 0x4)
+ {
+ result = result + (GetRegisterValue(index) << ss);
+ }
+
+ //
+ // Finally add in the offset
+ //
+ if (mod == 0)
+ {
+ if ((base & 0x07) == 5)
+ {
+ result = result + *((INT32*)ip);
+ displace = 7;
+ }
+ else
+ {
+ displace = 3;
+ }
+ }
+ else if (mod == 1)
+ {
+ result = result + *((INT8*)ip);
+ displace = 4;
+ }
+ else // mod == 2
+ {
+ result = result + *((INT32*)ip);
+ displace = 7;
+ }
+
+ }
+ else
+ {
+ //
+ // Get the value we need from the register.
+ //
+
+ // Check for RIP-relative addressing mode.
+ if ((mod == 0) && ((rm & 0x07) == 5))
+ {
+ displace = 6; // 1 byte opcode + 1 byte modrm + 4 byte displacement (signed)
+ result = const_cast<BYTE *>(m_ip) + displace + *(reinterpret_cast<const INT32*>(ip));
+ }
+ else
+ {
+ result = (BYTE *)GetRegisterValue(rm);
+
+ if (mod == 0)
+ {
+ displace = 2;
+ }
+ else if (mod == 1)
+ {
+ result = result + *((INT8*)ip);
+ displace = 3;
+ }
+ else // mod == 2
+ {
+ result = result + *((INT32*)ip);
+ displace = 6;
+ }
+ }
+ }
+
+ //
+ // Now dereference thru the result to get the resulting IP.
+ //
+ result = (BYTE *)(*((UINT64*)result));
+
+ break;
+
+ case 3:
+ default:
+ // The operand is stored in a register.
+ result = (BYTE *)GetRegisterValue(rm);
+ displace = 2;
+
+ break;
+
+ }
+
+ // the instruction uses r8-r15, add in the extra byte to the displacement
+ // for the REX prefix which was used to specify the extended register
+ if (rex != NULL)
+ {
+ displace++;
+ }
+
+ // because we already checked register validity for CALL/BRANCH
+ // instructions above we can assume that there is no other option
+ if ((reg == 4) || (reg == 5))
+ {
+ m_type = WALK_BRANCH;
+ }
+ else
+ {
+ m_type = WALK_CALL;
+ }
+ m_nextIP = result;
+ m_skipIP = m_ip + displace;
+ break;
+ }
+ case 0xe8:
+ {
+ m_type = WALK_CALL;
+
+ // Sign-extend the displacement is necessary.
+ INT32 disp = *((INT32*)ip);
+ m_nextIP = ip + 4 + (disp < 0 ? (disp | 0xffffffff00000000) : disp);
+ m_skipIP = ip + 4;
+
+ break;
+ }
+ case 0xe9:
+ {
+ m_type = WALK_BRANCH;
+
+ // Sign-extend the displacement is necessary.
+ INT32 disp = *((INT32*)ip);
+ m_nextIP = ip + 4 + (disp < 0 ? (disp | 0xffffffff00000000) : disp);
+ m_skipIP = ip + 4;
+
+ break;
+ }
+ case 0xc2:
+ case 0xc3:
+ case 0xca:
+ case 0xcb:
+ {
+ m_type = WALK_RETURN;
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+
+//
+// Given a regdisplay and a register number, return the value of the register.
+//
+
+UINT64 NativeWalker::GetRegisterValue(int registerNumber)
+{
+ if (m_registers == NULL) {
+ return 0;
+ }
+
+ switch (registerNumber)
+ {
+ case 0:
+ return m_registers->pCurrentContext->Rax;
+ break;
+ case 1:
+ return m_registers->pCurrentContext->Rcx;
+ break;
+ case 2:
+ return m_registers->pCurrentContext->Rdx;
+ break;
+ case 3:
+ return m_registers->pCurrentContext->Rbx;
+ break;
+ case 4:
+ return m_registers->pCurrentContext->Rsp;
+ break;
+ case 5:
+ return m_registers->pCurrentContext->Rbp;
+ break;
+ case 6:
+ return m_registers->pCurrentContext->Rsi;
+ break;
+ case 7:
+ return m_registers->pCurrentContext->Rdi;
+ break;
+ case 8:
+ return m_registers->pCurrentContext->R8;
+ break;
+ case 9:
+ return m_registers->pCurrentContext->R9;
+ break;
+ case 10:
+ return m_registers->pCurrentContext->R10;
+ break;
+ case 11:
+ return m_registers->pCurrentContext->R11;
+ break;
+ case 12:
+ return m_registers->pCurrentContext->R12;
+ break;
+ case 13:
+ return m_registers->pCurrentContext->R13;
+ break;
+ case 14:
+ return m_registers->pCurrentContext->R14;
+ break;
+ case 15:
+ return m_registers->pCurrentContext->R15;
+ break;
+ default:
+ _ASSERTE(!"Invalid register number!");
+ }
+
+ return 0;
+}
+
+
+// mod reg r/m
+// bits 7-6 5-3 2-0
+struct ModRMByte
+{
+ BYTE rm :3;
+ BYTE reg:3;
+ BYTE mod:2;
+};
+
+// fixed W R X B
+// bits 7-4 3 2 1 0
+struct RexByte
+{
+ BYTE b:1;
+ BYTE x:1;
+ BYTE r:1;
+ BYTE w:1;
+ BYTE fixed:4;
+};
+
+// static
+void NativeWalker::DecodeInstructionForPatchSkip(const BYTE *address, InstructionAttribute * pInstrAttrib)
+{
+ //
+ // Skip instruction prefixes
+ //
+
+ LOG((LF_CORDB, LL_INFO10000, "Patch decode: "));
+
+ // for reads and writes where the destination is a RIP-relative address pInstrAttrib->m_cOperandSize will contain the size in bytes of the pointee; in all other
+ // cases it will be zero. if the RIP-relative address is being written to then pInstrAttrib->m_fIsWrite will be true; in all other cases it will be false.
+ // similar to cbImmedSize in some cases we'll set pInstrAttrib->m_cOperandSize to 0x3 meaning that the prefix will determine the size if one is specified.
+ pInstrAttrib->m_cOperandSize = 0;
+ pInstrAttrib->m_fIsWrite = false;
+
+ if (pInstrAttrib == NULL)
+ {
+ return;
+ }
+
+ // These three legacy prefixes are used to modify some of the two-byte opcodes.
+ bool fPrefix66 = false;
+ bool fPrefixF2 = false;
+ bool fPrefixF3 = false;
+
+ bool fRex = false;
+ bool fModRM = false;
+
+ RexByte rex = {0};
+ ModRMByte modrm = {0};
+
+ // We use 0x3 to indicate that we need to look at the operand-size override and the rex byte
+ // to determine whether the immediate size is 2 bytes or 4 bytes.
+ BYTE cbImmedSize = 0;
+
+ const BYTE* originalAddr = address;
+
+ do
+ {
+ switch (*address)
+ {
+ // Operand-Size override
+ case 0x66:
+ fPrefix66 = true;
+ goto LLegacyPrefix;
+
+ // Repeat (REP/REPE/REPZ)
+ case 0xf2:
+ fPrefixF2 = true;
+ goto LLegacyPrefix;
+
+ // Repeat (REPNE/REPNZ)
+ case 0xf3:
+ fPrefixF3 = true;
+ goto LLegacyPrefix;
+
+ // Address-Size override
+ case 0x67: // fall through
+
+ // Segment overrides
+ case 0x26: // ES
+ case 0x2E: // CS
+ case 0x36: // SS
+ case 0x3E: // DS
+ case 0x64: // FS
+ case 0x65: // GS // fall through
+
+ // Lock
+ case 0xf0:
+LLegacyPrefix:
+ LOG((LF_CORDB, LL_INFO10000, "prefix:%0.2x ", *address));
+ address++;
+ continue;
+
+ // REX register extension prefixes
+ case 0x40:
+ case 0x41:
+ case 0x42:
+ case 0x43:
+ case 0x44:
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4a:
+ case 0x4b:
+ case 0x4c:
+ case 0x4d:
+ case 0x4e:
+ case 0x4f:
+ LOG((LF_CORDB, LL_INFO10000, "prefix:%0.2x ", *address));
+ fRex = true;
+ rex = *(RexByte*)address;
+ address++;
+ continue;
+
+ default:
+ break;
+ }
+ } while (0);
+
+ pInstrAttrib->Reset();
+
+ BYTE opcode0 = *address;
+ BYTE opcode1 = *(address + 1); // this is only valid if the first opcode byte is 0x0F
+
+ // Handle AVX encodings. Note that these can mostly be handled as if they are aliases
+ // for a corresponding SSE encoding.
+ // See Figure 2-9 in "Intel 64 and IA-32 Architectures Software Developer's Manual".
+
+ if (opcode0 == 0xC4 || opcode0 == 0xC5)
+ {
+ BYTE pp;
+ if (opcode0 == 0xC4)
+ {
+ BYTE opcode2 = *(address + 2);
+ address++;
+
+ // REX bits are encoded in inverted form.
+ // R,X, and B are the top bits (in that order) of opcode1.
+ // W is the top bit of opcode2.
+ if ((opcode1 & 0x80) != 0)
+ {
+ rex.b = 1;
+ fRex = true;
+ }
+ if ((opcode1 & 0x40) == 0)
+ {
+ rex.x = 1;
+ fRex = true;
+ }
+ if ((opcode1 & 0x20) == 0)
+ {
+ rex.b = 1;
+ fRex = true;
+ }
+ if ((opcode2 & 0x80) != 0)
+ {
+ rex.w = 1;
+ fRex = true;
+ }
+
+ pp = opcode2 & 0x3;
+
+ BYTE mmBits = opcode1 & 0x1f;
+ BYTE impliedOpcode1 = 0;
+ switch(mmBits)
+ {
+ case 1: break; // No implied leading byte.
+ case 2: impliedOpcode1 = 0x38; break;
+ case 3: impliedOpcode1 = 0x3A; break;
+ default: _ASSERTE(!"NW::DIFPS - invalid opcode"); break;
+ }
+
+ if (impliedOpcode1 != 0)
+ {
+ opcode1 = impliedOpcode1;
+ }
+ else
+ {
+ opcode1 = *address;
+ address++;
+ }
+ }
+ else
+ {
+ pp = opcode1 & 0x3;
+ if ((opcode1 & 0x80) == 0)
+ {
+ // The two-byte VEX encoding only encodes the 'R' bit.
+ fRex = true;
+ rex.r = 1;
+ }
+ opcode1 = *address;
+ address++;
+ }
+ opcode0 = 0x0f;
+ switch (pp)
+ {
+ case 1: fPrefix66 = true; break;
+ case 2: fPrefixF3 = true; break;
+ case 3: fPrefixF2 = true; break;
+ }
+ }
+
+ // The following opcode decoding follows the tables in "Appendix A Opcode and Operand Encodings" of
+ // "AMD64 Architecture Programmer's Manual Volume 3"
+
+ // one-byte opcodes
+ if (opcode0 != 0x0F)
+ {
+ BYTE highNibble = (opcode0 & 0xF0) >> 4;
+ BYTE lowNibble = (opcode0 & 0x0F);
+
+ switch (highNibble)
+ {
+ case 0x0:
+ case 0x1:
+ case 0x2:
+ case 0x3:
+ if ((lowNibble == 0x6) || (lowNibble == 0x7) || (lowNibble == 0xE) || (lowNibble == 0xF))
+ {
+ _ASSERTE(!"NW::DIFPS - invalid opcode");
+ }
+
+ // CMP
+ if ( (lowNibble <= 0x3) ||
+ ((lowNibble >= 0x8) && (lowNibble <= 0xB)) )
+ {
+ fModRM = true;
+ }
+
+ // ADD/XOR reg/mem, reg
+ if (lowNibble == 0x0)
+ {
+ pInstrAttrib->m_cOperandSize = 0x1;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ else if (lowNibble == 0x1)
+ {
+ pInstrAttrib->m_cOperandSize = 0x3;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ // XOR reg, reg/mem
+ else if (lowNibble == 0x2)
+ {
+ pInstrAttrib->m_cOperandSize = 0x1;
+ }
+ else if (lowNibble == 0x3)
+ {
+ pInstrAttrib->m_cOperandSize = 0x3;
+ }
+
+ break;
+
+ case 0x4:
+ case 0x5:
+ break;
+
+ case 0x6:
+ // IMUL
+ if (lowNibble == 0x9)
+ {
+ fModRM = true;
+ cbImmedSize = 0x3;
+ }
+ else if (lowNibble == 0xB)
+ {
+ fModRM = true;
+ cbImmedSize = 0x1;
+ }
+ else if (lowNibble == 0x3)
+ {
+ if (fRex)
+ {
+ // MOVSXD
+ fModRM = true;
+ }
+ }
+ break;
+
+ case 0x7:
+ break;
+
+ case 0x8:
+ fModRM = true;
+
+ // Group 1: lowNibble in [0x0, 0x3]
+ _ASSERTE(lowNibble != 0x2);
+
+ // ADD/XOR reg/mem, imm
+ if (lowNibble == 0x0)
+ {
+ cbImmedSize = 1;
+ pInstrAttrib->m_cOperandSize = 1;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ else if (lowNibble == 0x1)
+ {
+ cbImmedSize = 3;
+ pInstrAttrib->m_cOperandSize = 3;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ else if (lowNibble == 0x3)
+ {
+ cbImmedSize = 1;
+ pInstrAttrib->m_cOperandSize = 3;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ // MOV reg/mem, reg
+ else if (lowNibble == 0x8)
+ {
+ pInstrAttrib->m_cOperandSize = 0x1;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ else if (lowNibble == 0x9)
+ {
+ pInstrAttrib->m_cOperandSize = 0x3;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ // MOV reg, reg/mem
+ else if (lowNibble == 0xA)
+ {
+ pInstrAttrib->m_cOperandSize = 0x1;
+ }
+ else if (lowNibble == 0xB)
+ {
+ pInstrAttrib->m_cOperandSize = 0x3;
+ }
+
+ break;
+
+ case 0x9:
+ case 0xA:
+ case 0xB:
+ break;
+
+ case 0xC:
+ if ((lowNibble == 0x4) || (lowNibble == 0x5) || (lowNibble == 0xE))
+ {
+ _ASSERTE(!"NW::DIFPS - invalid opcode");
+ }
+
+ // RET
+ if ((lowNibble == 0x2) || (lowNibble == 0x3))
+ {
+ break;
+ }
+
+ // Group 2 (part 1): lowNibble in [0x0, 0x1]
+ // RCL reg/mem, imm
+ if (lowNibble == 0x0)
+ {
+ fModRM = true;
+ cbImmedSize = 0x1;
+ pInstrAttrib->m_cOperandSize = 0x1;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ else if (lowNibble == 0x1)
+ {
+ fModRM = true;
+ cbImmedSize = 0x1;
+ pInstrAttrib->m_cOperandSize = 0x3;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ // Group 11: lowNibble in [0x6, 0x7]
+ // MOV reg/mem, imm
+ else if (lowNibble == 0x6)
+ {
+ fModRM = true;
+ cbImmedSize = 1;
+ pInstrAttrib->m_cOperandSize = 1;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ else if (lowNibble == 0x7)
+ {
+ fModRM = true;
+ cbImmedSize = 3;
+ pInstrAttrib->m_cOperandSize = 3;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ break;
+
+ case 0xD:
+ // Group 2 (part 2): lowNibble in [0x0, 0x3]
+ // RCL reg/mem, 1/reg
+ if (lowNibble == 0x0 || lowNibble == 0x2)
+ {
+ fModRM = true;
+ pInstrAttrib->m_cOperandSize = 0x1;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ else if (lowNibble == 0x1 || lowNibble == 0x3)
+ {
+ fModRM = true;
+ pInstrAttrib->m_cOperandSize = 0x3;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+
+ // x87 instructions: lowNibble in [0x8, 0xF]
+ // - the entire ModRM byte is used to modify the opcode,
+ // so the ModRM byte cannot be used in RIP-relative addressing
+ break;
+
+ case 0xE:
+ break;
+
+ case 0xF:
+ // Group 3: lowNibble in [0x6, 0x7]
+ // TEST
+ if ((lowNibble == 0x6) || (lowNibble == 0x7))
+ {
+ fModRM = true;
+
+ modrm = *(ModRMByte*)(address + 1);
+ if ((modrm.reg == 0x0) || (modrm.reg == 0x1))
+ {
+ if (lowNibble == 0x6)
+ {
+ cbImmedSize = 0x1;
+ }
+ else
+ {
+ cbImmedSize = 0x3;
+ }
+ }
+ }
+ // Group 4: lowNibble == 0xE
+ // INC reg/mem
+ else if (lowNibble == 0xE)
+ {
+ fModRM = true;
+ pInstrAttrib->m_cOperandSize = 1;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ // Group 5: lowNibble == 0xF
+ else if (lowNibble == 0xF)
+ {
+ fModRM = true;
+ pInstrAttrib->m_cOperandSize = 3;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ break;
+ }
+
+ address += 1;
+ if (fModRM)
+ {
+ modrm = *(ModRMByte*)address;
+ address += 1;
+ }
+ }
+ // two-byte opcodes
+ else
+ {
+ BYTE highNibble = (opcode1 & 0xF0) >> 4;
+ BYTE lowNibble = (opcode1 & 0x0F);
+
+ switch (highNibble)
+ {
+ case 0x0:
+ // Group 6: lowNibble == 0x0
+ if (lowNibble == 0x0)
+ {
+ fModRM = true;
+ }
+ // Group 7: lowNibble == 0x1
+ else if (lowNibble == 0x1)
+ {
+ fModRM = true;
+ }
+ else if ((lowNibble == 0x2) || (lowNibble == 0x3))
+ {
+ fModRM = true;
+ }
+ // Group p: lowNibble == 0xD
+ else if (lowNibble == 0xD)
+ {
+ fModRM = true;
+ }
+ // 3DNow! instructions: lowNibble == 0xF
+ // - all 3DNow! instructions use the ModRM byte
+ else if (lowNibble == 0xF)
+ {
+ fModRM = true;
+ cbImmedSize = 0x1;
+ }
+ break;
+
+ case 0x1: // Group 16: lowNibble == 0x8
+ // MOVSS xmm, xmm/mem (low nibble 0x0)
+ // MOVSS xmm/mem, xmm (low nibble 0x1)
+ if (lowNibble <= 0x1)
+ {
+ fModRM = true;
+ if (fPrefixF2 || fPrefixF3)
+ pInstrAttrib->m_cOperandSize = 0x8;
+ else
+ pInstrAttrib->m_cOperandSize = 0x10;
+
+ if (lowNibble == 0x1)
+ pInstrAttrib->m_fIsWrite = true;
+
+ break;
+ }
+ case 0x2: // fall through
+ fModRM = true;
+ if (lowNibble == 0x8 || lowNibble == 0x9)
+ {
+ pInstrAttrib->m_cOperandSize = 0x10;
+
+ if (lowNibble == 0x9)
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ break;
+
+ case 0x3:
+ break;
+
+ case 0x4:
+ case 0x5:
+ case 0x6: // fall through
+ fModRM = true;
+ break;
+
+ case 0x7:
+ if (lowNibble == 0x0)
+ {
+ fModRM = true;
+ cbImmedSize = 0x1;
+ }
+ else if ((lowNibble >= 0x1) && (lowNibble <= 0x3))
+ {
+ _ASSERTE(!fPrefixF2 && !fPrefixF3);
+
+ // Group 12: lowNibble == 0x1
+ // Group 13: lowNibble == 0x2
+ // Group 14: lowNibble == 0x3
+ fModRM = true;
+ cbImmedSize = 0x1;
+ }
+ else if ((lowNibble >= 0x4) && (lowNibble <= 0x6))
+ {
+ fModRM = true;
+ }
+ // MOVD reg/mem, mmx for 0F 7E
+ else if ((lowNibble == 0xE) || (lowNibble == 0xF))
+ {
+ _ASSERTE(!fPrefixF2);
+
+ fModRM = true;
+ }
+ break;
+
+ case 0x8:
+ break;
+
+ case 0x9:
+ fModRM = true;
+ break;
+
+ case 0xA:
+ if ((lowNibble >= 0x3) && (lowNibble <= 0x5))
+ {
+ // BT reg/mem, reg
+ fModRM = true;
+ if (lowNibble == 0x3)
+ {
+ pInstrAttrib->m_cOperandSize = 0x3;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ // SHLD reg/mem, imm
+ else if (lowNibble == 0x4)
+ {
+ cbImmedSize = 0x1;
+ }
+ }
+ else if (lowNibble >= 0xB)
+ {
+ fModRM = true;
+ // BTS reg/mem, reg
+ if (lowNibble == 0xB)
+ {
+ pInstrAttrib->m_cOperandSize = 0x3;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ // SHRD reg/mem, imm
+ else if (lowNibble == 0xC)
+ {
+ cbImmedSize = 0x1;
+ }
+ // Group 15: lowNibble == 0xE
+ }
+ break;
+
+ case 0xB:
+ // Group 10: lowNibble == 0x9
+ // - this entire group is invalid
+ _ASSERTE((lowNibble != 0x8) && (lowNibble != 0x9));
+
+ fModRM = true;
+ // CMPXCHG reg/mem, reg
+ if (lowNibble == 0x0)
+ {
+ pInstrAttrib->m_cOperandSize = 0x1;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ else if (lowNibble == 0x1)
+ {
+ pInstrAttrib->m_cOperandSize = 0x3;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ // Group 8: lowNibble == 0xA
+ // BTS reg/mem, imm
+ else if (lowNibble == 0xA)
+ {
+ cbImmedSize = 0x1;
+ pInstrAttrib->m_cOperandSize = 0x3;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ // MOVSX reg, reg/mem
+ else if (lowNibble == 0xE)
+ {
+ pInstrAttrib->m_cOperandSize = 1;
+ }
+ else if (lowNibble == 0xF)
+ {
+ pInstrAttrib->m_cOperandSize = 2;
+ }
+ break;
+
+ case 0xC:
+ if (lowNibble <= 0x7)
+ {
+ fModRM = true;
+ // XADD reg/mem, reg
+ if (lowNibble == 0x0)
+ {
+ pInstrAttrib->m_cOperandSize = 0x1;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ else if (lowNibble == 0x1)
+ {
+ pInstrAttrib->m_cOperandSize = 0x3;
+ pInstrAttrib->m_fIsWrite = true;
+ }
+ else if ( (lowNibble == 0x2) ||
+ ((lowNibble >= 0x4) && (lowNibble <= 0x6)) )
+ {
+ cbImmedSize = 0x1;
+ }
+ }
+ break;
+
+ case 0xD:
+ case 0xE:
+ case 0xF: // fall through
+ fModRM = true;
+ break;
+ }
+
+ address += 2;
+ if (fModRM)
+ {
+ modrm = *(ModRMByte*)address;
+ address += 1;
+ }
+ }
+
+ // Check for RIP-relative addressing
+ if (fModRM && (modrm.mod == 0x0) && (modrm.rm == 0x5))
+ {
+ // SIB byte cannot be present with RIP-relative addressing.
+
+ pInstrAttrib->m_dwOffsetToDisp = (DWORD)(address - originalAddr);
+ _ASSERTE(pInstrAttrib->m_dwOffsetToDisp <= MAX_INSTRUCTION_LENGTH);
+
+ // Add 4 to the address for the displacement.
+ address += 4;
+
+ // Further adjust the address by the size of the cbImmedSize (if any).
+ if (cbImmedSize == 0x3)
+ {
+ // The size of the cbImmedSizeiate depends on the effective operand size:
+ // 2 bytes if the effective operand size is 16-bit, or
+ // 4 bytes if the effective operand size is 32- or 64-bit.
+ if (fPrefix66)
+ {
+ cbImmedSize = 0x2;
+ }
+ else
+ {
+ cbImmedSize = 0x4;
+ }
+ }
+ address += cbImmedSize;
+
+ // if this is a read or write to a RIP-relative address then update pInstrAttrib->m_cOperandSize with the size of the pointee.
+ if (pInstrAttrib->m_cOperandSize == 0x3)
+ {
+ if (fPrefix66)
+ pInstrAttrib->m_cOperandSize = 0x2; // WORD*
+ else
+ pInstrAttrib->m_cOperandSize = 0x4; // DWORD*
+
+ if (fRex && rex.w == 0x1)
+ {
+ _ASSERTE(pInstrAttrib->m_cOperandSize == 0x4);
+ pInstrAttrib->m_cOperandSize = 0x8; // QWORD*
+ }
+ }
+
+ pInstrAttrib->m_cbInstr = (DWORD)(address - originalAddr);
+ _ASSERTE(pInstrAttrib->m_cbInstr <= MAX_INSTRUCTION_LENGTH);
+ }
+ else
+ {
+ // not a RIP-relative address so set to default values
+ pInstrAttrib->m_cOperandSize = 0;
+ pInstrAttrib->m_fIsWrite = false;
+ }
+
+ //
+ // Look at opcode to tell if it's a call or an
+ // absolute branch.
+ //
+ switch (opcode0)
+ {
+ case 0xC2: // RET
+ case 0xC3: // RET N
+ pInstrAttrib->m_fIsAbsBranch = true;
+ LOG((LF_CORDB, LL_INFO10000, "ABS:%0.2x\n", opcode0));
+ break;
+
+ case 0xE8: // CALL relative
+ pInstrAttrib->m_fIsCall = true;
+ LOG((LF_CORDB, LL_INFO10000, "CALL REL:%0.2x\n", opcode0));
+ break;
+
+ case 0xC8: // ENTER
+ pInstrAttrib->m_fIsCall = true;
+ pInstrAttrib->m_fIsAbsBranch = true;
+ LOG((LF_CORDB, LL_INFO10000, "CALL ABS:%0.2x\n", opcode0));
+ break;
+
+ case 0xFF: // CALL/JMP modr/m
+ //
+ // Read opcode modifier from modr/m
+ //
+
+ _ASSERTE(fModRM);
+ switch (modrm.reg)
+ {
+ case 2:
+ case 3:
+ pInstrAttrib->m_fIsCall = true;
+ // fall through
+ case 4:
+ case 5:
+ pInstrAttrib->m_fIsAbsBranch = true;
+ }
+ LOG((LF_CORDB, LL_INFO10000, "CALL/JMP modr/m:%0.2x\n", opcode0));
+ break;
+
+ default:
+ LOG((LF_CORDB, LL_INFO10000, "NORMAL:%0.2x\n", opcode0));
+ }
+
+ if (pInstrAttrib->m_cOperandSize == 0x0)
+ {
+ // if an operand size wasn't computed (likely because the decoder didn't understand the instruction) then set
+ // the size to the max buffer size. this is a fall-back to the dev10 behavior and is applicable for reads only.
+ _ASSERTE(!pInstrAttrib->m_fIsWrite);
+ pInstrAttrib->m_cOperandSize = SharedPatchBypassBuffer::cbBufferBypass;
+ }
+}
+
+
+#endif