using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; namespace Cosmos.IL2CPU { public class ILReader { // We split this into two arrays since we have to read // a byte at a time anways. In the future if we need to // back to a unifed array, instead of 64k entries // we can change it to a signed int, and then add x0200 to the value. // This will reduce array size down to 768 entries. protected OpCode[] mOpCodesLo = new OpCode[256]; protected OpCode[] mOpCodesHi = new OpCode[256]; public ILReader() { LoadOpCodes(); } protected void LoadOpCodes() { foreach (var xField in typeof(OpCodes).GetFields(BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public)) { var xOpCode = (OpCode)xField.GetValue(null); var xValue = (ushort)xOpCode.Value; if (xValue <= 0xFF) { mOpCodesLo[xValue] = xOpCode; } else { mOpCodesHi[xValue & 0xFF] = xOpCode; } } } protected void CheckBranch(int aTarget, int aMethodSize) { // this method is a safety-measure. Should never occur if (aTarget < 0 || aTarget >= aMethodSize) { throw new Exception("Branch jumps outside method."); } } public List ProcessMethod(MethodBase aMethod) { var xResult = new List(); var xBody = aMethod.GetMethodBody(); // Cache for use in field and method resolution Type[] xTypeGenArgs = null; Type[] xMethodGenArgs = null; if (aMethod.DeclaringType.IsGenericType) { xTypeGenArgs = aMethod.DeclaringType.GetGenericArguments(); } if (aMethod.IsGenericMethod) { xMethodGenArgs = aMethod.GetGenericArguments(); } // Some methods return no body. Not sure why.. have to investigate // They arent abstracts or icalls... if (xBody == null) { return null; } var xIL = xBody.GetILAsByteArray(); int xPos = 0; while (xPos < xIL.Length) { ExceptionHandlingClause xCurrentHandler = null; #region Determine current handler // todo: add support for nested handlers using a stack or so.. foreach (ExceptionHandlingClause xHandler in xBody.ExceptionHandlingClauses) { if (xHandler.TryOffset > 0) { if (xHandler.TryOffset <= xPos && (xHandler.TryLength + xHandler.TryOffset ) > xPos) { if (xCurrentHandler == null) { xCurrentHandler = xHandler; continue; } else if (xHandler.TryOffset > xCurrentHandler.TryOffset && (xHandler.TryLength + xHandler.TryOffset) < (xCurrentHandler.TryLength + xCurrentHandler.TryOffset)) { // only replace if the current found handler is narrower xCurrentHandler = xHandler; continue; } } } if (xHandler.HandlerOffset > 0) { if (xHandler.HandlerOffset <= xPos && (xHandler.HandlerOffset + xHandler.HandlerLength) > xPos) { if (xCurrentHandler == null) { xCurrentHandler = xHandler; continue; } else if (xHandler.HandlerOffset > xCurrentHandler.HandlerOffset && (xHandler.HandlerOffset + xHandler.HandlerLength) < (xCurrentHandler.HandlerOffset + xCurrentHandler.HandlerLength)) { // only replace if the current found handler is narrower xCurrentHandler = xHandler; continue; } } } if ((xHandler.Flags & ExceptionHandlingClauseOptions.Filter) > 0) { if (xHandler.FilterOffset > 0) { if (xHandler.FilterOffset <= xPos) { if (xCurrentHandler == null) { xCurrentHandler = xHandler; continue; } else if (xHandler.FilterOffset > xCurrentHandler.FilterOffset) { // only replace if the current found handler is narrower xCurrentHandler = xHandler; continue; } } } } } #endregion ILOpCode.Code xOpCodeVal; OpCode xOpCode; int xOpPos = xPos; if (xIL[xPos] == 0xFE) { xOpCodeVal = (ILOpCode.Code)(0xFE00 | xIL[xPos + 1]); xOpCode = mOpCodesHi[xIL[xPos + 1]]; xPos = xPos + 2; } else { xOpCodeVal = (ILOpCode.Code)xIL[xPos]; xOpCode = mOpCodesLo[xIL[xPos]]; xPos++; } ILOpCode xILOpCode = null; switch (xOpCode.OperandType) { // No operand. case OperandType.InlineNone: { #region Inline none options // These shortcut translation regions expand shortcut ops into full ops // This elminates the amount of code required in the assemblers // by allowing them to ignore the shortcuts switch (xOpCodeVal) { case ILOpCode.Code.Ldarg_0: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos, 0, xCurrentHandler); break; case ILOpCode.Code.Ldarg_1: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos, 1, xCurrentHandler); break; case ILOpCode.Code.Ldarg_2: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos, 2, xCurrentHandler); break; case ILOpCode.Code.Ldarg_3: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos, 3, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_0: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 0, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_1: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 1, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_2: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 2, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_3: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 3, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_4: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 4, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_5: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 5, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_6: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 6, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_7: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 7, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_8: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, 8, xCurrentHandler); break; case ILOpCode.Code.Ldc_I4_M1: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos, -1, xCurrentHandler); break; case ILOpCode.Code.Ldloc_0: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos, 0, xCurrentHandler); break; case ILOpCode.Code.Ldloc_1: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos, 1, xCurrentHandler); break; case ILOpCode.Code.Ldloc_2: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos, 2, xCurrentHandler); break; case ILOpCode.Code.Ldloc_3: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos, 3, xCurrentHandler); break; case ILOpCode.Code.Stloc_0: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos, 0, xCurrentHandler); break; case ILOpCode.Code.Stloc_1: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos, 1, xCurrentHandler); break; case ILOpCode.Code.Stloc_2: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos, 2, xCurrentHandler); break; case ILOpCode.Code.Stloc_3: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos, 3, xCurrentHandler); break; default: xILOpCode = new ILOpCodes.OpNone(xOpCodeVal, xOpPos, xPos, xCurrentHandler); break; } #endregion break; } case OperandType.ShortInlineBrTarget: { #region Inline branch // By calculating target, we assume all branches are within a method // So far at least wtih csc, its true. We check it with CheckBranch // just in case. int xTarget = xPos + 1 + (sbyte)xIL[xPos]; CheckBranch(xTarget, xIL.Length); switch (xOpCodeVal) { case ILOpCode.Code.Beq_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Beq, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Bge_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bge, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Bge_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bge_Un, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Bgt_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bgt, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Bgt_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bgt_Un, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Ble_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Ble, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Ble_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Ble_Un, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Blt_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Blt, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Blt_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Blt_Un, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Bne_Un_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Bne_Un, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Br_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Br, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Brfalse_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Brfalse, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Brtrue_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Brtrue, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; case ILOpCode.Code.Leave_S: xILOpCode = new ILOpCodes.OpBranch(ILOpCode.Code.Leave, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; default: xILOpCode = new ILOpCodes.OpBranch(xOpCodeVal, xOpPos, xPos + 1, xTarget, xCurrentHandler); break; } xPos = xPos + 1; break; #endregion } case OperandType.InlineBrTarget: { int xTarget = xPos + 4 + ReadInt32(xIL, xPos); CheckBranch(xTarget, xIL.Length); xILOpCode = new ILOpCodes.OpBranch(xOpCodeVal, xOpPos, xPos + 4, xTarget, xCurrentHandler); xPos = xPos + 4; break; } case OperandType.ShortInlineI: switch (xOpCodeVal) { case ILOpCode.Code.Ldc_I4_S: xILOpCode = new ILOpCodes.OpInt(ILOpCode.Code.Ldc_I4, xOpPos, xPos + 1, ((sbyte)xIL[xPos]), xCurrentHandler); break; default: xILOpCode = new ILOpCodes.OpInt(xOpCodeVal, xOpPos, xPos + 1, ((sbyte)xIL[xPos]), xCurrentHandler); break; } xPos = xPos + 1; break; case OperandType.InlineI: xILOpCode = new ILOpCodes.OpInt(xOpCodeVal, xOpPos, xPos + 4, ReadInt32(xIL, xPos), xCurrentHandler); xPos = xPos + 4; break; case OperandType.InlineI8: xILOpCode = new ILOpCodes.OpInt64(xOpCodeVal, xOpPos, xPos + 8, ReadUInt64(xIL, xPos), xCurrentHandler); xPos = xPos + 8; break; case OperandType.ShortInlineR: xILOpCode = new ILOpCodes.OpSingle(xOpCodeVal, xOpPos, xPos + 4, BitConverter.ToSingle(xIL, xPos), xCurrentHandler); xPos = xPos + 4; break; case OperandType.InlineR: xILOpCode = new ILOpCodes.OpDouble(xOpCodeVal, xOpPos, xPos + 8, BitConverter.ToDouble(xIL, xPos), xCurrentHandler); xPos = xPos + 8; break; // The operand is a 32-bit metadata token. case OperandType.InlineField: { var xValue = aMethod.Module.ResolveField(ReadInt32(xIL, xPos), xTypeGenArgs, xMethodGenArgs); xILOpCode = new ILOpCodes.OpField(xOpCodeVal, xOpPos, xPos + 4, xValue, xCurrentHandler); xPos = xPos + 4; break; } // The operand is a 32-bit metadata token. case OperandType.InlineMethod: { var xValue = aMethod.Module.ResolveMethod(ReadInt32(xIL, xPos), xTypeGenArgs, xMethodGenArgs); xILOpCode = new ILOpCodes.OpMethod(xOpCodeVal, xOpPos, xPos + 4, xValue, xCurrentHandler); xPos = xPos + 4; break; } // 32-bit metadata signature token. case OperandType.InlineSig: xILOpCode = new ILOpCodes.OpSig(xOpCodeVal, xOpPos, xPos + 4, ReadInt32(xIL, xPos), xCurrentHandler); xPos = xPos + 4; break; case OperandType.InlineString: xILOpCode = new ILOpCodes.OpString(xOpCodeVal, xOpPos, xPos + 4, aMethod.Module.ResolveString(ReadInt32(xIL, xPos)), xCurrentHandler); xPos = xPos + 4; break; case OperandType.InlineSwitch: { int xCount = ReadInt32(xIL, xPos); xPos = xPos + 4; int xNextOpPos = xPos + xCount * 4; var xBranchLocations = new int[xCount]; for (int i = 0; i < xCount; i++) { xBranchLocations[i] = xNextOpPos + ReadInt32(xIL, xPos + i * 4); CheckBranch(xBranchLocations[i], xIL.Length); } xILOpCode = new ILOpCodes.OpSwitch(xOpCodeVal, xOpPos, xNextOpPos, xBranchLocations, xCurrentHandler); xPos = xNextOpPos; break; } // The operand is a FieldRef, MethodRef, or TypeRef token. case OperandType.InlineTok: xILOpCode = new ILOpCodes.OpToken(xOpCodeVal, xOpPos, xPos + 4, ReadInt32(xIL, xPos), aMethod.Module, xTypeGenArgs, xMethodGenArgs, xCurrentHandler); xPos = xPos + 4; break; // 32-bit metadata token. case OperandType.InlineType: { var xValue = aMethod.Module.ResolveType(ReadInt32(xIL, xPos), xTypeGenArgs, xMethodGenArgs); xILOpCode = new ILOpCodes.OpType(xOpCodeVal, xOpPos, xPos + 4, xValue, xCurrentHandler); xPos = xPos + 4; break; } case OperandType.ShortInlineVar: switch (xOpCodeVal) { case ILOpCode.Code.Ldloc_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloc, xOpPos, xPos + 1, xIL[xPos], xCurrentHandler); break; case ILOpCode.Code.Ldloca_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldloca, xOpPos, xPos + 1, xIL[xPos], xCurrentHandler); break; case ILOpCode.Code.Ldarg_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarg, xOpPos, xPos + 1, xIL[xPos], xCurrentHandler); break; case ILOpCode.Code.Ldarga_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Ldarga, xOpPos, xPos + 1, xIL[xPos], xCurrentHandler); break; case ILOpCode.Code.Starg_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Starg, xOpPos, xPos + 1, xIL[xPos], xCurrentHandler); break; case ILOpCode.Code.Stloc_S: xILOpCode = new ILOpCodes.OpVar(ILOpCode.Code.Stloc, xOpPos, xPos + 1, xIL[xPos], xCurrentHandler); break; default: xILOpCode = new ILOpCodes.OpVar(xOpCodeVal, xOpPos, xPos + 1, xIL[xPos], xCurrentHandler); break; } xPos = xPos + 1; break; case OperandType.InlineVar: xILOpCode = new ILOpCodes.OpVar(xOpCodeVal, xOpPos, xPos + 2, ReadUInt16(xIL, xPos), xCurrentHandler); xPos = xPos + 2; break; default: throw new Exception("Unknown OperandType"); } xILOpCode.InitStackAnalysis(aMethod); xResult.Add(xILOpCode); } return xResult; } // We could use BitConvertor, unfortuantely they "hardcoded" endianness. Its fine for reading IL now... // but they essentially do the same as we do, just a bit slower. private UInt16 ReadUInt16(byte[] aBytes, int aPos) { return (UInt16)(aBytes[aPos + 1] << 8 | aBytes[aPos]); } private Int32 ReadInt32(byte[] aBytes, int aPos) { return aBytes[aPos + 3] << 24 | aBytes[aPos + 2] << 16 | aBytes[aPos + 1] << 8 | aBytes[aPos]; } private UInt64 ReadUInt64(byte[] aBytes, int aPos) { //return (UInt64)( // aBytes[aPos + 7] << 56 | aBytes[aPos + 6] << 48 | aBytes[aPos + 5] << 40 | aBytes[aPos + 4] << 32 // | aBytes[aPos + 3] << 24 | aBytes[aPos + 2] << 16 | aBytes[aPos + 1] << 8 | aBytes[aPos]); return BitConverter.ToUInt64(aBytes, aPos); } } }