/* 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_INSTR_INFO using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using Iced.Intel.InstructionInfoInternal; namespace Iced.Intel { /// /// Contains information about an instruction, eg. read/written registers, read/written RFLAGS bits, CPUID feature bit, etc /// public readonly struct InstructionInfo { readonly UsedRegister[] usedRegisters; readonly UsedMemory[] usedMemoryLocations; readonly ushort usedRegistersLength; readonly ushort usedMemoryLocationsLength; readonly ushort opMaskFlags; readonly byte cpuidFeature; readonly byte flowControl; readonly byte encoding; readonly byte rflagsInfo; readonly byte flags; [Flags] internal enum OpMaskFlags : ushort { OpAccessMask = 7, Op1AccessShift = 3, Op2AccessShift = 6, Op3AccessShift = 9, Op4AccessShift = 12, } [Flags] enum Flags : byte { SaveRestore = 0x01, StackInstruction = 0x02, ProtectedMode = 0x04, Privileged = 0x08, } internal InstructionInfo(ref SimpleList usedRegisters, ref SimpleList usedMemoryLocations, uint opMaskFlags, RflagsInfo rflagsInfo, uint flags1, uint flags2) { Debug.Assert(DecoderConstants.MaxOpCount == 5); Debug.Assert(usedRegisters.Array != null); this.usedRegisters = usedRegisters.Array; Debug.Assert(usedMemoryLocations.Array != null); this.usedMemoryLocations = usedMemoryLocations.Array; Debug.Assert((uint)usedRegisters.ValidLength <= ushort.MaxValue); usedRegistersLength = (ushort)usedRegisters.ValidLength; Debug.Assert((uint)usedMemoryLocations.ValidLength <= ushort.MaxValue); usedMemoryLocationsLength = (ushort)usedMemoryLocations.ValidLength; this.opMaskFlags = (ushort)opMaskFlags; Debug.Assert(((flags2 >> (int)InfoFlags2.CpuidFeatureShift) & (uint)InfoFlags2.CpuidFeatureMask) <= byte.MaxValue); cpuidFeature = (byte)((flags2 >> (int)InfoFlags2.CpuidFeatureShift) & (uint)InfoFlags2.CpuidFeatureMask); Debug.Assert(((flags2 >> (int)InfoFlags2.FlowControlShift) & (uint)InfoFlags2.FlowControlMask) <= byte.MaxValue); flowControl = (byte)((flags2 >> (int)InfoFlags2.FlowControlShift) & (uint)InfoFlags2.FlowControlMask); Debug.Assert(((flags2 >> (int)InfoFlags2.EncodingShift) & (uint)InfoFlags2.EncodingMask) <= byte.MaxValue); encoding = (byte)((flags2 >> (int)InfoFlags2.EncodingShift) & (uint)InfoFlags2.EncodingMask); Debug.Assert((uint)rflagsInfo <= byte.MaxValue); Debug.Assert((uint)rflagsInfo < (uint)RflagsInfo.Last); this.rflagsInfo = (byte)rflagsInfo; Debug.Assert((uint)InfoFlags1.SaveRestore == 0x08000000); Debug.Assert((uint)InfoFlags1.StackInstruction == 0x10000000); Debug.Assert((uint)InfoFlags1.ProtectedMode == 0x20000000); Debug.Assert((uint)InfoFlags1.Privileged == 0x40000000); Debug.Assert((uint)Flags.SaveRestore == 0x01); Debug.Assert((uint)Flags.StackInstruction == 0x02); Debug.Assert((uint)Flags.ProtectedMode == 0x04); Debug.Assert((uint)Flags.Privileged == 0x08); // Bit 4 could be set but we don't use it so we don't need to mask it out flags = (byte)(flags1 >> 27); } void ThrowArgumentOutOfRangeException(string paramName) => throw new ArgumentOutOfRangeException(paramName); /// /// Gets a struct iterator that returns all read and written registers. There are some exceptions, this method doesn't return all used registers: /// /// 1) If is true, or /// /// 2) If it's a or instruction (call, sysenter, int n etc), it can read and write any register (including RFLAGS). /// /// public UsedRegisterIterator GetUsedRegisters() => new UsedRegisterIterator(usedRegisters, usedRegistersLength); /// /// Gets a struct iterator that returns all read and written memory locations /// /// public UsedMemoryIterator GetUsedMemory() => new UsedMemoryIterator(usedMemoryLocations, usedMemoryLocationsLength); #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member public struct UsedRegisterIterator : IEnumerable, IEnumerator { readonly UsedRegister[] usedRegisters; readonly uint length; int index; internal UsedRegisterIterator(UsedRegister[] usedRegisters, uint length) { this.usedRegisters = usedRegisters; this.length = length; index = -1; } public UsedRegisterIterator GetEnumerator() => this; public UsedRegister Current => usedRegisters[index]; public bool MoveNext() { index++; return (uint)index < length; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); UsedRegister IEnumerator.Current => Current; object IEnumerator.Current => Current; bool IEnumerator.MoveNext() => MoveNext(); void IEnumerator.Reset() => throw new NotSupportedException(); public void Dispose() { } } public struct UsedMemoryIterator : IEnumerable, IEnumerator { readonly UsedMemory[] usedMemoryLocations; readonly uint length; int index; internal UsedMemoryIterator(UsedMemory[] usedMemoryLocations, uint length) { this.usedMemoryLocations = usedMemoryLocations; this.length = length; index = -1; } public UsedMemoryIterator GetEnumerator() => this; public UsedMemory Current => usedMemoryLocations[index]; public bool MoveNext() { index++; return (uint)index < length; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); UsedMemory IEnumerator.Current => Current; object IEnumerator.Current => Current; bool IEnumerator.MoveNext() => MoveNext(); void IEnumerator.Reset() => throw new NotSupportedException(); public void Dispose() { } } #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member /// /// true if the instruction isn't available in real mode or virtual 8086 mode /// public bool ProtectedMode => (flags & (uint)Flags.ProtectedMode) != 0; /// /// true if this is a privileged instruction /// public bool Privileged => (flags & (uint)Flags.Privileged) != 0; /// /// true if this is an instruction that implicitly uses the stack pointer (SP/ESP/RSP), eg. call, push, pop, ret, etc. /// See also /// public bool StackInstruction => (flags & (uint)Flags.StackInstruction) != 0; /// /// true if it's an instruction that saves or restores too many registers (eg. fxrstor, xsave, etc). /// won't return all read/written registers. /// public bool SaveRestoreInstruction => (flags & (uint)Flags.SaveRestore) != 0; /// /// Instruction encoding, eg. legacy, VEX, EVEX, ... /// public EncodingKind Encoding => (EncodingKind)encoding; /// /// CPU or CPUID feature flag /// public CpuidFeature CpuidFeature => (CpuidFeature)cpuidFeature; /// /// Flow control info /// public FlowControl FlowControl => (FlowControl)flowControl; /// /// Operand #0 access /// public OpAccess Op0Access => (OpAccess)(opMaskFlags & (uint)OpMaskFlags.OpAccessMask); /// /// Operand #1 access /// public OpAccess Op1Access => (OpAccess)(((uint)opMaskFlags >> (int)OpMaskFlags.Op1AccessShift) & (uint)OpMaskFlags.OpAccessMask); /// /// Operand #2 access /// public OpAccess Op2Access => (OpAccess)(((uint)opMaskFlags >> (int)OpMaskFlags.Op2AccessShift) & (uint)OpMaskFlags.OpAccessMask); /// /// Operand #3 access /// public OpAccess Op3Access => (OpAccess)(((uint)opMaskFlags >> (int)OpMaskFlags.Op3AccessShift) & (uint)OpMaskFlags.OpAccessMask); /// /// Operand #4 access /// public OpAccess Op4Access => (OpAccess)(((uint)opMaskFlags >> (int)OpMaskFlags.Op4AccessShift) & (uint)OpMaskFlags.OpAccessMask); /// /// Gets operand access /// /// Operand number, 0-4 /// public OpAccess GetOpAccess(int operand) { switch (operand) { case 0: return Op0Access; case 1: return Op1Access; case 2: return Op2Access; case 3: return Op3Access; case 4: return Op4Access; default: ThrowArgumentOutOfRangeException(nameof(operand)); return 0; } } /// /// All flags that are read by the CPU when executing the instruction /// public RflagsBits RflagsRead => (RflagsBits)RflagsInfoConstants.flagsRead[rflagsInfo]; /// /// All flags that are written by the CPU, except those flags that are known to be undefined, always set or always cleared. See also /// public RflagsBits RflagsWritten => (RflagsBits)RflagsInfoConstants.flagsWritten[rflagsInfo]; /// /// All flags that are always cleared by the CPU /// public RflagsBits RflagsCleared => (RflagsBits)RflagsInfoConstants.flagsCleared[rflagsInfo]; /// /// All flags that are always set by the CPU /// public RflagsBits RflagsSet => (RflagsBits)RflagsInfoConstants.flagsSet[rflagsInfo]; /// /// All flags that are undefined after executing the instruction /// public RflagsBits RflagsUndefined => (RflagsBits)RflagsInfoConstants.flagsUndefined[rflagsInfo]; /// /// All flags that are modified by the CPU. This is + + + /// public RflagsBits RflagsModified => (RflagsBits)RflagsInfoConstants.flagsModified[rflagsInfo]; } } #endif