/*
Copyright (C) 2018 de4dot@gmail.com
This file is part of Iced.
Iced is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Iced is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with Iced. If not, see .
*/
#if (!NO_DECODER32 || !NO_DECODER64) && !NO_DECODER
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Iced.Intel.DecoderInternal;
namespace Iced.Intel {
enum OpSize : byte {
Size16 = 0,
Size32 = 1,
Size64 = 2,
}
[Flags]
enum StateFlags : uint {
EncodingMask = 7,
HasRex = 0x00000008,
b = 0x00000010,
z = 0x00000020,
IsInvalid = 0x00000040,
W = 0x00000080,
NoImm = 0x00000100,
Addr64 = 0x00000200,
SpecialImm = 0x00000400,
}
///
/// Decodes 16/32/64-bit x86 instructions
///
public sealed partial class Decoder {
readonly CodeReader reader;
internal readonly bool is64Mode;
internal readonly CodeSize defaultCodeSize;
readonly OpCodeHandler[] handlers_XX;
readonly OpCodeHandler[] handlers_0FXX_VEX;
readonly OpCodeHandler[] handlers_0F38XX_VEX;
readonly OpCodeHandler[] handlers_0F3AXX_VEX;
readonly OpCodeHandler[] handlers_0FXX_EVEX;
readonly OpCodeHandler[] handlers_0F38XX_EVEX;
readonly OpCodeHandler[] handlers_0F3AXX_EVEX;
readonly OpCodeHandler[] handlers_XOP8;
readonly OpCodeHandler[] handlers_XOP9;
readonly OpCodeHandler[] handlers_XOPA;
readonly OpSize defaultOperandSize, defaultInvertedOperandSize;
readonly OpSize defaultAddressSize, defaultInvertedAddressSize;
ulong instructionPointer;
internal struct State {
public uint instructionLength;
public uint modrm, mod, reg, rm;
public uint extraRegisterBase; // R << 3
public uint extraIndexRegisterBase; // X << 3
public uint extraBaseRegisterBase; // B << 3
public uint vvvv;// V`vvvv. Not stored in inverted form. If 16/32-bit, bits [4:3] are cleared
public uint aaa;
public uint extraRegisterBaseEVEX;
public uint extraBaseRegisterBaseEVEX;
public uint extraIndexRegisterBaseVSIB;
public StateFlags flags;
public byte defaultDsSegment;
public VectorLength vectorLength;
public MandatoryPrefix mandatoryPrefix;
public OpSize operandSize;
public OpSize addressSize;
public EncodingKind Encoding => (EncodingKind)(flags & StateFlags.EncodingMask);
}
internal State state;
internal uint displIndex;
///
/// Current IP/EIP/RIP value
///
public ulong InstructionPointer {
get => instructionPointer;
set => instructionPointer = value;
}
///
/// Gets the bitness (16, 32 or 64)
///
public int Bitness { get; }
Decoder(CodeReader reader, OpSize defaultOpSize) {
this.reader = reader ?? throw new ArgumentNullException(nameof(reader));
if (defaultOpSize == OpSize.Size64) {
#if !NO_DECODER64
is64Mode = true;
Bitness = 64;
defaultCodeSize = CodeSize.Code64;
defaultOperandSize = OpSize.Size32;
defaultInvertedOperandSize = OpSize.Size16;
defaultAddressSize = OpSize.Size64;
defaultInvertedAddressSize = OpSize.Size32;
handlers_XX = DecoderInternal.OpCodeHandlers64.OpCodeHandlers64Tables.OneByteHandlers;
handlers_0FXX_VEX = DecoderInternal.OpCodeHandlers64.OpCodeHandlers64Tables_VEX.TwoByteHandlers_0FXX;
handlers_0F38XX_VEX = DecoderInternal.OpCodeHandlers64.OpCodeHandlers64Tables_VEX.ThreeByteHandlers_0F38XX;
handlers_0F3AXX_VEX = DecoderInternal.OpCodeHandlers64.OpCodeHandlers64Tables_VEX.ThreeByteHandlers_0F3AXX;
handlers_0FXX_EVEX = DecoderInternal.OpCodeHandlers64.OpCodeHandlers64Tables_EVEX.TwoByteHandlers_0FXX;
handlers_0F38XX_EVEX = DecoderInternal.OpCodeHandlers64.OpCodeHandlers64Tables_EVEX.ThreeByteHandlers_0F38XX;
handlers_0F3AXX_EVEX = DecoderInternal.OpCodeHandlers64.OpCodeHandlers64Tables_EVEX.ThreeByteHandlers_0F3AXX;
handlers_XOP8 = DecoderInternal.OpCodeHandlers64.OpCodeHandlers64Tables_XOP.XOP8;
handlers_XOP9 = DecoderInternal.OpCodeHandlers64.OpCodeHandlers64Tables_XOP.XOP9;
handlers_XOPA = DecoderInternal.OpCodeHandlers64.OpCodeHandlers64Tables_XOP.XOPA;
#else
throw new ArgumentException("64-bit decoder isn't present");
#endif
}
else {
#if !NO_DECODER32
is64Mode = false;
Bitness = defaultOpSize == OpSize.Size32 ? 32 : 16;
defaultCodeSize = defaultOpSize == OpSize.Size32 ? CodeSize.Code32 : CodeSize.Code16;
var inverted = defaultOpSize == OpSize.Size32 ? OpSize.Size16 : OpSize.Size32;
defaultOperandSize = defaultOpSize;
defaultInvertedOperandSize = inverted;
defaultAddressSize = defaultOpSize;
defaultInvertedAddressSize = inverted;
handlers_XX = DecoderInternal.OpCodeHandlers32.OpCodeHandlers32Tables.OneByteHandlers;
handlers_0FXX_VEX = DecoderInternal.OpCodeHandlers32.OpCodeHandlers32Tables_VEX.TwoByteHandlers_0FXX;
handlers_0F38XX_VEX = DecoderInternal.OpCodeHandlers32.OpCodeHandlers32Tables_VEX.ThreeByteHandlers_0F38XX;
handlers_0F3AXX_VEX = DecoderInternal.OpCodeHandlers32.OpCodeHandlers32Tables_VEX.ThreeByteHandlers_0F3AXX;
handlers_0FXX_EVEX = DecoderInternal.OpCodeHandlers32.OpCodeHandlers32Tables_EVEX.TwoByteHandlers_0FXX;
handlers_0F38XX_EVEX = DecoderInternal.OpCodeHandlers32.OpCodeHandlers32Tables_EVEX.ThreeByteHandlers_0F38XX;
handlers_0F3AXX_EVEX = DecoderInternal.OpCodeHandlers32.OpCodeHandlers32Tables_EVEX.ThreeByteHandlers_0F3AXX;
handlers_XOP8 = DecoderInternal.OpCodeHandlers32.OpCodeHandlers32Tables_XOP.XOP8;
handlers_XOP9 = DecoderInternal.OpCodeHandlers32.OpCodeHandlers32Tables_XOP.XOP9;
handlers_XOPA = DecoderInternal.OpCodeHandlers32.OpCodeHandlers32Tables_XOP.XOPA;
#else
throw new ArgumentException("16-bit and 32-bit decoders aren't present");
#endif
}
}
sealed class DelegateCodeReader : CodeReader {
readonly Func readByte;
public DelegateCodeReader(Func readByte) => this.readByte = readByte ?? throw new ArgumentNullException(nameof(readByte));
public override int ReadByte() => readByte();
}
///
/// Creates a decoder
///
/// 16, 32 or 64
/// Code reader
///
public static Decoder Create(int bitness, CodeReader reader) {
switch (bitness) {
case 16: return Create16(reader);
case 32: return Create32(reader);
case 64: return Create64(reader);
default: throw new ArgumentOutOfRangeException(nameof(bitness));
}
}
///
/// Creates a decoder that decodes 16-bit code
///
/// Code reader
///
public static Decoder Create16(CodeReader reader) => new Decoder(reader, OpSize.Size16);
///
/// Creates a decoder that decodes 32-bit code
///
/// Code reader
///
public static Decoder Create32(CodeReader reader) => new Decoder(reader, OpSize.Size32);
///
/// Creates a decoder that decodes 64-bit code
///
/// Code reader
///
public static Decoder Create64(CodeReader reader) => new Decoder(reader, OpSize.Size64);
///
/// Creates a decoder
///
/// 16, 32 or 64
/// Reads the next byte or returns less than 0 if there are no more bytes
///
public static Decoder Create(int bitness, Func readByte) => Create(bitness, new DelegateCodeReader(readByte));
///
/// Creates a decoder that decodes 16-bit code
///
/// Reads the next byte or returns less than 0 if there are no more bytes
///
public static Decoder Create16(Func readByte) => Create16(new DelegateCodeReader(readByte));
///
/// Creates a decoder that decodes 32-bit code
///
/// Reads the next byte or returns less than 0 if there are no more bytes
///
public static Decoder Create32(Func readByte) => Create32(new DelegateCodeReader(readByte));
///
/// Creates a decoder that decodes 64-bit code
///
/// Reads the next byte or returns less than 0 if there are no more bytes
///
public static Decoder Create64(Func readByte) => Create64(new DelegateCodeReader(readByte));
internal uint ReadByte() {
uint instrLen = state.instructionLength;
if (instrLen >= DecoderConstants.MaxInstructionLength) {
state.flags |= StateFlags.IsInvalid;
return 0;
}
uint b = (uint)reader.ReadByte();
Debug.Assert(b <= byte.MaxValue || b > int.MaxValue);
if (b > byte.MaxValue) {
state.flags |= StateFlags.IsInvalid;
return 0;
}
state.instructionLength = instrLen + 1;
return b;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal uint ReadUInt16() => ReadByte() | (ReadByte() << 8);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal uint ReadUInt32() => ReadByte() | (ReadByte() << 8) | (ReadByte() << 16) | (ReadByte() << 24);
///
/// Decodes the next instruction, see also which is faster
/// if you already have an local, array element or field.
///
///
public Instruction Decode() {
Decode(out var instr);
return instr;
}
///
/// Decodes the next instruction
///
/// Decoded instruction
public void Decode(out Instruction instruction) {
instruction = default;
// JIT32: it's 9% slower decoding instructions if we clear the whole 'state'
// 32-bit RyuJIT: not tested
// 64-bit RyuJIT: diff is too small to care about
#if truex
state = default;
#else
state.instructionLength = 0;
state.extraRegisterBase = 0;
state.extraIndexRegisterBase = 0;
state.extraBaseRegisterBase = 0;
state.flags = 0;
state.mandatoryPrefix = 0;
state.extraIndexRegisterBaseVSIB = 0;
#endif
state.defaultDsSegment = (byte)Register.DS;
state.operandSize = defaultOperandSize;
state.addressSize = defaultAddressSize;
uint rexPrefix = 0;
uint b;
for (;;) {
b = ReadByte();
// Converting these prefixes to opcode handlers instead of a switch results in slightly worse perf
// with JIT32, and about the same speed with 64-bit RyuJIT.
switch (b) {
case 0x26:
if (!is64Mode || (state.defaultDsSegment != (byte)Register.FS && state.defaultDsSegment != (byte)Register.GS)) {
instruction.PrefixSegment = Register.ES;
state.defaultDsSegment = (byte)Register.ES;
}
rexPrefix = 0;
break;
case 0x2E:
if (!is64Mode || (state.defaultDsSegment != (byte)Register.FS && state.defaultDsSegment != (byte)Register.GS)) {
instruction.PrefixSegment = Register.CS;
state.defaultDsSegment = (byte)Register.CS;
}
rexPrefix = 0;
break;
case 0x36:
if (!is64Mode || (state.defaultDsSegment != (byte)Register.FS && state.defaultDsSegment != (byte)Register.GS)) {
instruction.PrefixSegment = Register.SS;
state.defaultDsSegment = (byte)Register.SS;
}
rexPrefix = 0;
break;
case 0x3E:
if (!is64Mode || (state.defaultDsSegment != (byte)Register.FS && state.defaultDsSegment != (byte)Register.GS)) {
instruction.PrefixSegment = Register.DS;
state.defaultDsSegment = (byte)Register.DS;
}
rexPrefix = 0;
break;
case 0x64:
instruction.PrefixSegment = Register.FS;
state.defaultDsSegment = (byte)Register.FS;
rexPrefix = 0;
break;
case 0x65:
instruction.PrefixSegment = Register.GS;
state.defaultDsSegment = (byte)Register.GS;
rexPrefix = 0;
break;
case 0x66:
state.operandSize = defaultInvertedOperandSize;
rexPrefix = 0;
if (state.mandatoryPrefix == MandatoryPrefix.None)
state.mandatoryPrefix = MandatoryPrefix.P66;
break;
case 0x67:
state.addressSize = defaultInvertedAddressSize;
rexPrefix = 0;
break;
case 0xF0:
instruction.InternalSetHasPrefixLock();
rexPrefix = 0;
break;
case 0xF2:
instruction.InternalSetHasPrefixRepne();
rexPrefix = 0;
state.mandatoryPrefix = MandatoryPrefix.PF2;
break;
case 0xF3:
instruction.InternalSetHasPrefixRepe();
rexPrefix = 0;
state.mandatoryPrefix = MandatoryPrefix.PF3;
break;
default:
if (is64Mode && (b & 0xF0) == 0x40) {
rexPrefix = b;
break;
}
goto after_read_prefixes;
}
}
after_read_prefixes:
if (rexPrefix != 0) {
state.flags |= StateFlags.HasRex;
if ((rexPrefix & 8) != 0) {
state.operandSize = OpSize.Size64;
state.flags |= StateFlags.W;
}
state.extraRegisterBase = (rexPrefix & 4) << 1;
state.extraIndexRegisterBase = (rexPrefix & 2) << 2;
state.extraBaseRegisterBase = (rexPrefix & 1) << 3;
}
DecodeTable(handlers_XX[b], ref instruction);
if ((state.flags & StateFlags.IsInvalid) != 0) {
instruction = default;
Debug.Assert(Code.INVALID == 0);
//instruction.InternalCode = Code.INVALID;
}
uint instrLen = state.instructionLength;
Debug.Assert(0 <= instrLen && instrLen <= DecoderConstants.MaxInstructionLength);// Could be 0 if there were no bytes available
instruction.InternalByteLength = instrLen;
instruction.InternalCodeSize = defaultCodeSize;
var ip = instructionPointer;
ip += instrLen;
instructionPointer = ip;
instruction.NextIP64 = ip;
}
internal uint GetCurrentInstructionPointer32() => (uint)instructionPointer + state.instructionLength;
internal ulong GetCurrentInstructionPointer64() => instructionPointer + state.instructionLength;
internal void ClearMandatoryPrefix(ref Instruction instruction) {
Debug.Assert(state.Encoding == EncodingKind.Legacy);
switch (state.mandatoryPrefix) {
case MandatoryPrefix.P66:
state.operandSize = defaultOperandSize;
break;
case MandatoryPrefix.PF3:
instruction.InternalClearHasPrefixRepe();
break;
case MandatoryPrefix.PF2:
instruction.InternalClearHasPrefixRepne();
break;
}
}
internal void SetXacquireRelease(ref Instruction instruction, HandlerFlags flags) {
if ((flags & HandlerFlags.XacquireReleaseNoLock) == 0 && !instruction.HasPrefixLock)
return;
switch (state.mandatoryPrefix) {
case MandatoryPrefix.PF2:
if ((flags & HandlerFlags.Xacquire) != 0) {
ClearMandatoryPrefixF2(ref instruction);
instruction.InternalSetHasPrefixXacquire();
}
break;
case MandatoryPrefix.PF3:
if ((flags & HandlerFlags.Xrelease) != 0) {
ClearMandatoryPrefixF3(ref instruction);
instruction.InternalSetHasPrefixXrelease();
}
break;
}
}
internal void ClearMandatoryPrefixF3(ref Instruction instruction) {
Debug.Assert(state.Encoding == EncodingKind.Legacy);
Debug.Assert(state.mandatoryPrefix == MandatoryPrefix.PF3);
instruction.InternalClearHasPrefixRepe();
}
internal void ClearMandatoryPrefixF2(ref Instruction instruction) {
Debug.Assert(state.Encoding == EncodingKind.Legacy);
Debug.Assert(state.mandatoryPrefix == MandatoryPrefix.PF2);
instruction.InternalClearHasPrefixRepne();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void SetInvalidInstruction() => state.flags |= StateFlags.IsInvalid;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void DecodeTable(OpCodeHandler[] table, ref Instruction instruction) => DecodeTable(table[(int)ReadByte()], ref instruction);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void DecodeTable(OpCodeHandler handler, ref Instruction instruction) {
if (handler.HasModRM) {
uint m = ReadByte();
state.modrm = m;
state.mod = m >> 6;
state.reg = (m >> 3) & 7;
state.rm = m & 7;
}
handler.Decode(this, ref instruction);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal void ReadModRM() {
uint m = ReadByte();
state.modrm = m;
state.mod = m >> 6;
state.reg = (m >> 3) & 7;
state.rm = m & 7;
}
internal void VEX2(ref Instruction instruction) {
if ((state.flags & StateFlags.HasRex) != 0 || state.mandatoryPrefix != MandatoryPrefix.None)
SetInvalidInstruction();
state.flags |= (StateFlags)EncodingKind.VEX;
uint b = state.modrm;
if (is64Mode && (b & 0x80) == 0)
state.extraRegisterBase = 8;
// Bit 6 can only be 1 if it's 16/32-bit mode, so we don't need to change the mask
state.vvvv = (~b >> 3) & 0x0F;
Debug.Assert((int)VectorLength.L128 == 0);
Debug.Assert((int)VectorLength.L256 == 1);
state.vectorLength = (VectorLength)((b >> 2) & 1);
Debug.Assert((int)MandatoryPrefix.None == 0);
Debug.Assert((int)MandatoryPrefix.P66 == 1);
Debug.Assert((int)MandatoryPrefix.PF3 == 2);
Debug.Assert((int)MandatoryPrefix.PF2 == 3);
state.mandatoryPrefix = (MandatoryPrefix)(b & 3);
DecodeTable(handlers_0FXX_VEX, ref instruction);
}
internal void VEX3(ref Instruction instruction) {
if ((state.flags & StateFlags.HasRex) != 0 || state.mandatoryPrefix != MandatoryPrefix.None)
SetInvalidInstruction();
state.flags |= (StateFlags)EncodingKind.VEX;
uint b1 = state.modrm;
uint b2 = ReadByte();
Debug.Assert((int)StateFlags.W == 0x80);
state.flags |= (StateFlags)(b2 & 0x80);
Debug.Assert((int)VectorLength.L128 == 0);
Debug.Assert((int)VectorLength.L256 == 1);
state.vectorLength = (VectorLength)((b2 >> 2) & 1);
Debug.Assert((int)MandatoryPrefix.None == 0);
Debug.Assert((int)MandatoryPrefix.P66 == 1);
Debug.Assert((int)MandatoryPrefix.PF3 == 2);
Debug.Assert((int)MandatoryPrefix.PF2 == 3);
state.mandatoryPrefix = (MandatoryPrefix)(b2 & 3);
if (is64Mode) {
if ((b2 & 0x80) != 0)
state.operandSize = OpSize.Size64;
state.vvvv = (~b2 >> 3) & 0x0F;
uint b1x = ~b1;
state.extraRegisterBase = (b1x >> 4) & 8;
state.extraIndexRegisterBase = (b1x >> 3) & 8;
b1x >>= 2;
b1x &= 8;
state.extraBaseRegisterBase = b1x;
}
else
state.vvvv = (~b2 >> 3) & 0x07;
int table = (int)(b1 & 0x1F);
if (table == 1)
DecodeTable(handlers_0FXX_VEX, ref instruction);
else if (table == 2)
DecodeTable(handlers_0F38XX_VEX, ref instruction);
else if (table == 3)
DecodeTable(handlers_0F3AXX_VEX, ref instruction);
else
SetInvalidInstruction();
}
internal void XOP(ref Instruction instruction) {
if ((state.flags & StateFlags.HasRex) != 0 || state.mandatoryPrefix != MandatoryPrefix.None)
SetInvalidInstruction();
state.flags |= (StateFlags)EncodingKind.XOP;
uint b1 = state.modrm;
uint b2 = ReadByte();
Debug.Assert((int)StateFlags.W == 0x80);
state.flags |= (StateFlags)(b2 & 0x80);
Debug.Assert((int)VectorLength.L128 == 0);
Debug.Assert((int)VectorLength.L256 == 1);
state.vectorLength = (VectorLength)((b2 >> 2) & 1);
Debug.Assert((int)MandatoryPrefix.None == 0);
Debug.Assert((int)MandatoryPrefix.P66 == 1);
Debug.Assert((int)MandatoryPrefix.PF3 == 2);
Debug.Assert((int)MandatoryPrefix.PF2 == 3);
state.mandatoryPrefix = (MandatoryPrefix)(b2 & 3);
if (is64Mode) {
if ((b2 & 0x80) != 0)
state.operandSize = OpSize.Size64;
state.vvvv = (~b2 >> 3) & 0x0F;
uint b1x = ~b1;
state.extraRegisterBase = (b1x >> 4) & 8;
state.extraIndexRegisterBase = (b1x >> 3) & 8;
state.extraBaseRegisterBase = (b1x >> 2) & 8;
}
else
state.vvvv = (~b2 >> 3) & 0x07;
int table = (int)(b1 & 0x1F);
if (table == 8)
DecodeTable(handlers_XOP8, ref instruction);
else if (table == 9)
DecodeTable(handlers_XOP9, ref instruction);
else if (table == 10)
DecodeTable(handlers_XOPA, ref instruction);
else
SetInvalidInstruction();
}
internal void EVEX_MVEX(ref Instruction instruction) {
if ((state.flags & StateFlags.HasRex) != 0 || state.mandatoryPrefix != MandatoryPrefix.None)
SetInvalidInstruction();
uint p0 = state.modrm;
uint p1 = ReadByte();
uint p2 = ReadByte();
if ((p1 & 4) == 0) {
//TODO: Support MVEX instructions
SetInvalidInstruction();
}
else {
if ((p0 & 0x0C) != 0) {
SetInvalidInstruction();
return;
}
state.flags |= (StateFlags)EncodingKind.EVEX;
Debug.Assert((int)MandatoryPrefix.None == 0);
Debug.Assert((int)MandatoryPrefix.P66 == 1);
Debug.Assert((int)MandatoryPrefix.PF3 == 2);
Debug.Assert((int)MandatoryPrefix.PF2 == 3);
state.mandatoryPrefix = (MandatoryPrefix)(p1 & 3);
Debug.Assert((int)StateFlags.W == 0x80);
state.flags |= (StateFlags)(p1 & 0x80);
uint aaa = p2 & 7;
state.aaa = aaa;
Debug.Assert((int)StateFlags.z == 0x20);
state.flags |= (StateFlags)((p2 & 0x80) >> 2);
if (aaa == 0 && (state.flags & StateFlags.z) != 0)
SetInvalidInstruction();
Debug.Assert((int)StateFlags.b == 0x10);
state.flags |= (StateFlags)(p2 & 0x10);
Debug.Assert((int)VectorLength.L128 == 0);
Debug.Assert((int)VectorLength.L256 == 1);
Debug.Assert((int)VectorLength.L512 == 2);
Debug.Assert((int)VectorLength.Unknown == 3);
state.vectorLength = (VectorLength)((p2 >> 5) & 3);
if (is64Mode) {
state.vvvv = (~p1 >> 3) & 0x0F;
uint tmp = (~p2 & 8) << 1;
state.vvvv += tmp;
state.extraIndexRegisterBaseVSIB = tmp;
if ((p1 & 0x80) != 0)
state.operandSize = OpSize.Size64;
uint p0x = ~p0;
state.extraRegisterBase = (p0x >> 4) & 8;
state.extraIndexRegisterBase = (p0x & 0x40) >> 3;
state.extraBaseRegisterBaseEVEX = (p0x & 0x40) >> 2;
state.extraBaseRegisterBase = (p0x >> 2) & 8;
p0x &= 0x10;
state.extraRegisterBaseEVEX = p0x;
}
else
state.vvvv = (~p1 >> 3) & 0x07;
int table = (int)(p0 & 3);
if (table == 1)
DecodeTable(handlers_0FXX_EVEX, ref instruction);
else if (table == 2)
DecodeTable(handlers_0F38XX_EVEX, ref instruction);
else if (table == 3)
DecodeTable(handlers_0F3AXX_EVEX, ref instruction);
else
SetInvalidInstruction();
}
}
// Return type is uint since caller will write to a uint field
internal uint ReadIb() => ReadByte();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal Register ReadOpSw() {
uint reg = state.reg;
if (reg >= 6) {
state.flags |= StateFlags.IsInvalid;
return Register.None;
}
else
return Register.ES + (int)reg;
}
static readonly (Register baseReg, Register indexReg)[] memRegs16 = new(Register baseReg, Register indexReg)[8] {
(Register.BX, Register.SI),
(Register.BX, Register.DI),
(Register.BP, Register.SI),
(Register.BP, Register.DI),
(Register.SI, Register.None),
(Register.DI, Register.None),
(Register.BP, Register.None),
(Register.BX, Register.None),
};
void ReadOpMem16(ref Instruction instruction, TupleType tupleType) {
Debug.Assert(state.addressSize == OpSize.Size16);
var (baseReg, indexReg) = memRegs16[(int)state.rm];
switch ((int)state.mod) {
case 0:
if (state.rm == 6) {
instruction.InternalSetMemoryDisplSize(2);
displIndex = state.instructionLength;
instruction.MemoryDisplacement = ReadUInt16();
baseReg = Register.None;
Debug.Assert(indexReg == Register.None);
}
break;
case 1:
instruction.InternalSetMemoryDisplSize(1);
displIndex = state.instructionLength;
if (tupleType == TupleType.None)
instruction.MemoryDisplacement = (ushort)(sbyte)ReadByte();
else
instruction.MemoryDisplacement = (ushort)(GetDisp8N(tupleType) * (uint)(sbyte)ReadByte());
break;
default:
Debug.Assert(state.mod == 2);
instruction.InternalSetMemoryDisplSize(2);
displIndex = state.instructionLength;
instruction.MemoryDisplacement = ReadUInt16();
break;
}
instruction.InternalMemoryBase = baseReg;
instruction.InternalMemoryIndex = indexReg;
}
// Returns true if the SIB byte was read
bool ReadOpMem32Or64(ref Instruction instruction, Register baseReg, Register indexReg, TupleType tupleType, bool isVsib) {
Debug.Assert(state.addressSize == OpSize.Size32 || state.addressSize == OpSize.Size64);
uint sib;
uint displSizeScale, displ;
switch ((int)state.mod) {
case 0:
if (state.rm == 4) {
sib = ReadByte();
displSizeScale = 0;
displ = 0;
break;
}
else if (state.rm == 5) {
if (state.addressSize == OpSize.Size64)
instruction.InternalSetMemoryDisplSize(4);
else
instruction.InternalSetMemoryDisplSize(3);
displIndex = state.instructionLength;
instruction.MemoryDisplacement = ReadUInt32();
if (is64Mode) {
if (state.addressSize == OpSize.Size64)
instruction.InternalMemoryBase = Register.RIP;
else
instruction.InternalMemoryBase = Register.EIP;
}
return false;
}
else {
Debug.Assert(0 <= state.rm && state.rm <= 7 && state.rm != 4 && state.rm != 5);
instruction.InternalMemoryBase = (int)(state.extraBaseRegisterBase + state.rm) + baseReg;
return false;
}
case 1:
if (state.rm == 4) {
sib = ReadByte();
displSizeScale = 1;
displIndex = state.instructionLength;
if (tupleType == TupleType.None)
displ = (uint)(sbyte)ReadByte();
else
displ = GetDisp8N(tupleType) * (uint)(sbyte)ReadByte();
break;
}
else {
Debug.Assert(0 <= state.rm && state.rm <= 7 && state.rm != 4);
instruction.InternalSetMemoryDisplSize(1);
displIndex = state.instructionLength;
if (tupleType == TupleType.None)
instruction.MemoryDisplacement = (uint)(sbyte)ReadByte();
else
instruction.MemoryDisplacement = GetDisp8N(tupleType) * (uint)(sbyte)ReadByte();
instruction.InternalMemoryBase = (int)(state.extraBaseRegisterBase + state.rm) + baseReg;
return false;
}
case 2:
if (state.rm == 4) {
sib = ReadByte();
displSizeScale = state.addressSize == OpSize.Size64 ? 4U : 3;
displIndex = state.instructionLength;
displ = ReadUInt32();
break;
}
else {
Debug.Assert(0 <= state.rm && state.rm <= 7 && state.rm != 4);
if (state.addressSize == OpSize.Size64)
instruction.InternalSetMemoryDisplSize(4);
else
instruction.InternalSetMemoryDisplSize(3);
displIndex = state.instructionLength;
instruction.MemoryDisplacement = ReadUInt32();
instruction.InternalMemoryBase = (int)(state.extraBaseRegisterBase + state.rm) + baseReg;
return false;
}
default:
Debug.Fail("Not reachable");
return false;
}
uint index = ((sib >> 3) & 7) + state.extraIndexRegisterBase;
uint @base = sib & 7;
instruction.InternalMemoryIndexScale = (int)(sib >> 6);
if (!isVsib) {
if (index != 4)
instruction.InternalMemoryIndex = (int)index + indexReg;
}
else
instruction.InternalMemoryIndex = (int)(index + state.extraIndexRegisterBaseVSIB) + indexReg;
if (@base == 5 && state.mod == 0) {
if (state.addressSize == OpSize.Size64)
instruction.InternalSetMemoryDisplSize(4);
else
instruction.InternalSetMemoryDisplSize(3);
displIndex = state.instructionLength;
instruction.MemoryDisplacement = ReadUInt32();
}
else {
instruction.InternalMemoryBase = (int)(@base + state.extraBaseRegisterBase) + baseReg;
instruction.InternalSetMemoryDisplSize(displSizeScale);
instruction.MemoryDisplacement = displ;
}
return true;
}
uint GetDisp8N(TupleType tupleType) {
switch (tupleType) {
case TupleType.None:
return 1;
case TupleType.Full_128:
if ((state.flags & StateFlags.b) != 0)
return (state.flags & StateFlags.W) != 0 ? 8U : 4;
return 16;
case TupleType.Full_256:
if ((state.flags & StateFlags.b) != 0)
return (state.flags & StateFlags.W) != 0 ? 8U : 4;
return 32;
case TupleType.Full_512:
if ((state.flags & StateFlags.b) != 0)
return (state.flags & StateFlags.W) != 0 ? 8U : 4;
return 64;
case TupleType.Half_128:
return (state.flags & StateFlags.b) != 0 ? 4U : 8;
case TupleType.Half_256:
return (state.flags & StateFlags.b) != 0 ? 4U : 16;
case TupleType.Half_512:
return (state.flags & StateFlags.b) != 0 ? 4U : 32;
case TupleType.Full_Mem_128:
return 16;
case TupleType.Full_Mem_256:
return 32;
case TupleType.Full_Mem_512:
return 64;
case TupleType.Tuple1_Scalar:
return (state.flags & StateFlags.W) != 0 ? 8U : 4;
case TupleType.Tuple1_Scalar_1:
return 1;
case TupleType.Tuple1_Scalar_2:
return 2;
case TupleType.Tuple1_Scalar_4:
return 4;
case TupleType.Tuple1_Scalar_8:
return 8;
case TupleType.Tuple1_Fixed:
return (state.flags & StateFlags.W) != 0 ? 8U : 4;
case TupleType.Tuple1_Fixed_4:
return 4;
case TupleType.Tuple1_Fixed_8:
return 8;
case TupleType.Tuple2:
return (state.flags & StateFlags.W) != 0 ? 16U : 8;
case TupleType.Tuple4:
return (state.flags & StateFlags.W) != 0 ? 32U : 16;
case TupleType.Tuple8:
Debug.Assert((state.flags & StateFlags.W) == 0);
return 32;
case TupleType.Tuple1_4X:
return 16;
case TupleType.Half_Mem_128:
return 8;
case TupleType.Half_Mem_256:
return 16;
case TupleType.Half_Mem_512:
return 32;
case TupleType.Quarter_Mem_128:
return 4;
case TupleType.Quarter_Mem_256:
return 8;
case TupleType.Quarter_Mem_512:
return 16;
case TupleType.Eighth_Mem_128:
return 2;
case TupleType.Eighth_Mem_256:
return 4;
case TupleType.Eighth_Mem_512:
return 8;
case TupleType.Mem128:
return 16;
case TupleType.MOVDDUP_128:
return 8;
case TupleType.MOVDDUP_256:
return 32;
case TupleType.MOVDDUP_512:
return 64;
default:
Debug.Fail($"Unreachable code");
return 0;
}
}
///
/// Gets the offsets of the constants (memory displacement and immediate) in the decoded instruction.
/// The caller can check if there are any relocations at those addresses.
///
/// The latest instruction that was decoded by this decoder
///
public ConstantOffsets GetConstantOffsets(ref Instruction instruction) {
ConstantOffsets constantOffsets = default;
int displSize = instruction.MemoryDisplSize;
if (displSize != 0) {
constantOffsets.DisplacementOffset = (byte)displIndex;
if (displSize == 8 && (state.flags & StateFlags.Addr64) == 0)
constantOffsets.DisplacementSize = 4;
else
constantOffsets.DisplacementSize = (byte)displSize;
}
if ((state.flags & StateFlags.NoImm) == 0) {
uint extraImmSub = 0;
int opCount = instruction.OpCount;
for (int i = opCount - 1; i >= 0; i--) {
switch (instruction.GetOpKind(i)) {
case OpKind.Immediate8:
case OpKind.Immediate8to16:
case OpKind.Immediate8to32:
case OpKind.Immediate8to64:
constantOffsets.ImmediateOffset = (byte)(instruction.ByteLength - (int)extraImmSub - 1);
constantOffsets.ImmediateSize = 1;
goto after_imm_loop;
case OpKind.Immediate16:
constantOffsets.ImmediateOffset = (byte)(instruction.ByteLength - (int)extraImmSub - 2);
constantOffsets.ImmediateSize = 2;
goto after_imm_loop;
case OpKind.Immediate32:
case OpKind.Immediate32to64:
constantOffsets.ImmediateOffset = (byte)(instruction.ByteLength - (int)extraImmSub - 4);
constantOffsets.ImmediateSize = 4;
goto after_imm_loop;
case OpKind.Immediate64:
constantOffsets.ImmediateOffset = (byte)(instruction.ByteLength - (int)extraImmSub - 8);
constantOffsets.ImmediateSize = 8;
goto after_imm_loop;
case OpKind.Immediate8_2nd:
extraImmSub = 1;
break;
}
}
if ((state.flags & StateFlags.SpecialImm) != 0) {
var code = instruction.Code;
if (code == Code.Call_ptr3216 || code == Code.Jmp_ptr3216) {
constantOffsets.ImmediateOffset = (byte)(instruction.ByteLength - (4 + 2));
constantOffsets.ImmediateSize = 4;
}
else {
Debug.Assert(code == Code.Call_ptr1616 || code == Code.Jmp_ptr1616);
constantOffsets.ImmediateOffset = (byte)(instruction.ByteLength - (2 + 2));
constantOffsets.ImmediateSize = 2;
}
}
}
after_imm_loop:
return constantOffsets;
}
#if !NO_ENCODER
///
/// Creates an encoder
///
/// Destination
///
public Encoder CreateEncoder(CodeWriter writer) {
switch (defaultCodeSize) {
case CodeSize.Code16: return Encoder.Create16(writer);
case CodeSize.Code32: return Encoder.Create32(writer);
case CodeSize.Code64: return Encoder.Create64(writer);
case CodeSize.Unknown:
default:
throw new InvalidOperationException();
}
}
#endif
}
}
#endif