/// <summary> /// Process an unplugged IL chunk. /// </summary> /// <param name="aChunk">The chunk to process.</param> /// <returns>The resulting ASM chunk or null if processing failed.</returns> private ASMChunk ProcessUnpluggedILChunk(ILChunk aChunk) { ASMChunk result = new ASMChunk(); string methodSignature = Utils.GetMethodSignature(aChunk.Method); string MethodID = TheScannerState.GetMethodID(aChunk.Method); //Add the method to the debug database DB_Method dbMethod = null; if (TheSettings.DebugBuild) { dbMethod = new DB_Method(); dbMethod.Id = MethodID; dbMethod.MethodSignature = methodSignature; dbMethod.Plugged = false; dbMethod.ASMStartPos = -1; dbMethod.ASMEndPos = -1; result.DBMethod = dbMethod; DebugDatabase.AddMethod(dbMethod); } result.ASM.AppendLine("; IL Scanned Method"); //DEBUG INFO result.ASM.AppendLine("; " + methodSignature); //DEBUG INFO //Outputs the label that is the start of this method result.ASM.AppendLine(MethodID + ":"); //Construct the stack frame state for the start of this method StackFrame currFrame = new StackFrame(); TheScannerState.CurrentStackFrame = currFrame; TheScannerState.CurrentILChunk = aChunk; //Insert the method start op //See comments on method start op for what it does etc. int addASMLineNum = 0; { //TODO - Add DBILOpInfo for MethodStart op result.ASM.AppendLine("; MethodStart"); // DEBUG INFO //Get the ASM of the op string asm = MethodStartOp.Convert(null, TheScannerState); //Split the ASM into lines string[] asmLines = asm.Replace("\r", "").Split('\n'); //This code inserts the ASM line by line, outputting labels for // each line of ASM. //Start at any existing offset for the current op // - prevents duplication of labels int asmLineNum = addASMLineNum; //For each line of ASM: foreach (string asmLine in asmLines) { //If the line isn't already a label: if (!asmLine.Split(';')[0].Trim().EndsWith(":")) { //Output the ASM label result.ASM.AppendLine(string.Format("{0}.IL_{1}_{2}:", MethodID, 0, asmLineNum)); } //Append the ASM result.ASM.AppendLine(asmLine); //Increment the ASM line num asmLineNum++; } //Set the overall ASM line offset for current op to final // offset addASMLineNum = asmLineNum; } #region GC //If Garbage Collection code should be added to this method: if (aChunk.ApplyGC) { //Inc ref count of all args passed to the method // - see ILReader for GC cleanup / dec ref count (use search: "Dec ref count" exc. quotes) List<Type> allParams = aChunk.Method.GetParameters() .Select(x => x.ParameterType) .ToList(); //Non-static methods have first arg as instance reference if (!aChunk.Method.IsStatic) { allParams.Insert(0, aChunk.Method.DeclaringType); } //If the number of args for this method > 0 if (allParams.Count > 0) { //Store the new ASM to append afterwards string asm = ""; //For each arg: for (int i = 0; i < allParams.Count; i++) { Type aVarType = allParams[i]; //If the arg is of a type managed by the GC if (Utils.IsGCManaged(aVarType)) { //Load the arg asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Ldarg] .Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Ldarg, ValueBytes = BitConverter.GetBytes(i) }, TheScannerState); //Call increment ref count asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call] .Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.IncrementRefCountMethod }, TheScannerState); } } //Append the new ASM - see MethodStart for explanation above string[] asmLines = asm.Replace("\r", "").Split('\n'); int asmLineNum = addASMLineNum; foreach (string asmLine in asmLines) { if (!asmLine.Split(';')[0].Trim().EndsWith(":")) { result.ASM.AppendLine(string.Format("{0}.IL_{1}_{2}:", MethodID, 0, asmLineNum)); } result.ASM.AppendLine(asmLine); asmLineNum++; } addASMLineNum = asmLineNum; } } #endregion //Stores the label of the last IL op that was a debug NOP // - See documentation for use of Debug NOPs / INT3s string debugNopLabel = null; //Stores the start position of the current IL op in the ASM int ASMStartPos = 0; //Get a list of all the IL ops List<ILOpInfo> TheILOpInfos = aChunk.ILOpInfos; //For each IL op: foreach (ILOpInfo anILOpInfo in TheILOpInfos) { //We surround all this with a try-catch block // so that if processing the current IL op causes an exception, we don't abort processing // of the entire method. We will end up with invalid ASM code in the output, but at least // the developer will receive all the errors with their code not just one per build. try { #region Debug //Create the debug DB_ILOpInfo dbILOpInfo = null; if (TheSettings.DebugBuild) { dbILOpInfo = new DB_ILOpInfo(); dbILOpInfo.Id = Guid.NewGuid(); dbILOpInfo.MethodID = MethodID; dbILOpInfo.OpCode = anILOpInfo.opCode.Value; dbILOpInfo.CustomOpCode = 0; dbILOpInfo.NextPosition = anILOpInfo.NextPosition; dbILOpInfo.Position = anILOpInfo.Position; if (anILOpInfo.ValueBytes != null) { if (anILOpInfo.ValueBytes.Length < 8000) { dbILOpInfo.ValueBytes = new System.Data.Linq.Binary(anILOpInfo.ValueBytes); } else { OutputWarning(new Exception("ValueBytes not set because data too large. Op: " + anILOpInfo.opCode.Name + ", Op offset: " + anILOpInfo.Position.ToString("X2") + "\r\n" + methodSignature)); anILOpInfo.ValueBytes = null; } } dbILOpInfo.ASMInsertLabel = true; anILOpInfo.DBILOpInfo = dbILOpInfo; dbILOpInfo.ASMStartPos = anILOpInfo.ASMStartPos = ASMStartPos; } #endregion //Stores all the new ASM for this IL op // - This ASM gets appended to the result at the end of the try-section // thus it only gets appended if there are no processing errors. string asm = ""; #region Exception Handling //We needs to check if we are in try, catch or finally blocks (a.k.a critical sections): ExceptionHandledBlock exBlock = aChunk.GetExceptionHandledBlock(anILOpInfo.Position); //If we are in a critical section: if (exBlock != null) { //If this IL op is the first op of a try-section: if (exBlock.Offset == anILOpInfo.Position) { //Insert the start of try-block //Consists of adding a new ExceptionHandlerInfos // built from the info in exBlock so we: // - Add infos for all finally blocks first // - Then add infos for all catch blocks // Since finally code is always run after catch code in C#, // by adding catch handlers after finally handlers, they // appear as the inner-most exception handlers and so get // run before finally handlers. //To add a new ExceptionHandlerInfo we must set up args for // calling Exceptions.AddExceptionHandlerInfo: // 1. We load a pointer to the handler // - This is calculated from an offset from the start of the function to the handler // 2. We load a pointer to the filter // - This is calculated from an offset from the start of the function to the filter // Note: Filter has not been implemented as an actual filter. // At the moment, 0x00000000 indicates a finally handler, // 0xFFFFFFFF indicates no filter block // (i.e. an unfiltered catch handler) // 0xXXXXXXXX has undetermined behaviour! result.ASM.AppendLine("; Try-block start"); // DEBUG INFO //For each finally block: foreach (FinallyBlock finBlock in exBlock.FinallyBlocks) { // 1. Load the pointer to the handler code: asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Ldftn].Convert(new ILOpInfo() { //Load function pointer op opCode = System.Reflection.Emit.OpCodes.Ldftn, //Load a pointer to the current method MethodToCall = aChunk.Method, //At this offset: The first IL op of the finally block LoadAtILOffset = finBlock.Offset }, TheScannerState); // 2. Load the pointer to the filter code: asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Ldc_I4].Convert(new ILOpInfo() { //Load a constant value opCode = System.Reflection.Emit.OpCodes.Ldc_I4, //The value is 0x00000000 - since this is a finally handler ValueBytes = BitConverter.GetBytes(0x00000000) }, TheScannerState); // Call Exceptions.AddExceptionHandlerInfo asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.AddExceptionHandlerInfoMethod }, TheScannerState); } foreach (CatchBlock catchBlock in exBlock.CatchBlocks) { // 1. Load the pointer to the handler code: asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Ldftn].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Ldftn, MethodToCall = aChunk.Method, LoadAtILOffset = catchBlock.Offset }, TheScannerState); //TODO - We need to sort out a way of doing filter functions // 2. Load the pointer to the filter code: asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Ldc_I4].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Ldc_I4, //The value is 0xFFFFFFFF - since this is a catch handler (and filters aren't implemented yet!) ValueBytes = BitConverter.GetBytes(0xFFFFFFFF) }, TheScannerState); // Call Exceptions.AddExceptionHandlerInfo asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.AddExceptionHandlerInfoMethod }, TheScannerState); } } //We need to check if the current IL op is the first op // of a catch block. This is because C# catch blocks are // compiled with a "pop" instruction as the first op if // the catch block has no filter. Presumably, this is because // C# would be expecting the current exception to be on the // stack. //Get any catch blocks we are currently in. List<CatchBlock> potCatchBlocks = (from catchBlocks in exBlock.CatchBlocks where (catchBlocks.Offset <= anILOpInfo.Position && catchBlocks.Offset + catchBlocks.Length >= anILOpInfo.Position) select catchBlocks).ToList(); //If we are in a catch block: if (potCatchBlocks.Count > 0) { CatchBlock catchBlock = potCatchBlocks.First(); //If this is the first op of the catch block: if (catchBlock.Offset == anILOpInfo.Position) { result.ASM.AppendLine("; Catch-block start"); // DEBUG INFO //Ignore the first pop-op of the catch block if ((int)anILOpInfo.opCode.Value == (int)ILOps.ILOp.OpCodes.Pop) { //Do an immediate append rather than using the "asm" variable as we will be calling // "continue" - see below. //For debug, we must insert this op's label in to the ASM. string label = string.Format("{0}.IL_{1}_{2}", MethodID, anILOpInfo.Position, 0); result.ASM.AppendLine(label + ":"); result.ASM.AppendLine("; Skipped first pop of catch handler"); // DEBUG INFO //End processing of this op by skipping to the next! continue; } } } //We want to be able to output some debug info if we are starting a finally block // just so that our ASM is more intellgible / debuggable. List<FinallyBlock> potFinallyBlocks = (from finallyBlocks in exBlock.FinallyBlocks where (finallyBlocks.Offset <= anILOpInfo.Position && finallyBlocks.Offset + finallyBlocks.Length >= anILOpInfo.Position) select finallyBlocks).ToList(); if (potFinallyBlocks.Count > 0) { FinallyBlock finallyBlock = potFinallyBlocks.First(); if (finallyBlock.Offset == anILOpInfo.Position) { result.ASM.AppendLine("; Finally-block start"); // DEBUG INFO } } } #endregion #region Debug if (TheSettings.DebugBuild) { //If this chunk hasn't been marked as no-debug ops: if (!aChunk.NoDebugOps) { // Insert a debug nop just before the op // - This allows us to step an entire IL op at a time rather than just one // line of ASM at a time. result.ASM.AppendLine("; Debug Nop"); // DEBUG INFO // Insert the nop asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Nop].Convert(anILOpInfo, TheScannerState); //See above for how this append code works string[] asmLines = asm.Replace("\r", "").Split("\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); int asmLineNum = addASMLineNum; //Clear the current debug nop label so we can get the first label // of the new ASM and use it as the debug nop label for this IL op. debugNopLabel = null; foreach (string asmLine in asmLines) { if (!asmLine.Split(';')[0].Trim().EndsWith(":")) { string label = string.Format("{0}.IL_{1}_{2}", MethodID, anILOpInfo.Position, asmLineNum); //If we do not currently have a debug nop label for this IL op: if (debugNopLabel == null) { //Set the current debug nop label. debugNopLabel = label; } result.ASM.AppendLine(label + ":"); } result.ASM.AppendLine(asmLine); asmLineNum++; } addASMLineNum = asmLineNum; //We just added all the ASM for this op generated so far, so clean the "asm" variable asm = ""; } //Set the debug nop label for this IL op as the last inserted debug nop label dbILOpInfo.DebugOpMeta = "DebugNopLabel=" + debugNopLabel + ";"; } #endregion //Insert some method end code just before the ret op. if ((int)anILOpInfo.opCode.Value == (int)ILOps.ILOp.OpCodes.Ret) { //Method End op inserts code such as storing the return value // in the return argument and restoring the stack base pointer result.ASM.AppendLine("; MethodEnd"); // DEBUG INFO asm += "\r\n" + MethodEndOp.Convert(null, TheScannerState); } if ((int)anILOpInfo.opCode.Value == (int)ILOps.ILOp.OpCodes.Leave || (int)anILOpInfo.opCode.Value == (int)ILOps.ILOp.OpCodes.Leave_S) #region Exception Handling { //Leave is for leaving a critical section //We handle it by a higher-level implementation rather than // leaving it to each architecture to implement. //Leave is handled by inserting a call to the Exceptions.HandleLeave method //This value is an offset from the next IL op to the line to continue execution at // if there isno current exception and no finally handler. int ILOffset = 0; if ((int)anILOpInfo.opCode.Value == (int)ILOps.ILOp.OpCodes.Leave) { ILOffset = BitConverter.ToInt32(anILOpInfo.ValueBytes, 0); } else { ILOffset = (int)anILOpInfo.ValueBytes[0]; } //Get the IL number of the next op int startILNum = anILOpInfo.NextPosition; //Add the offset to get the IL op num to jump to int ILNumToGoTo = startILNum + ILOffset; // Load the address of the op to continue execution at if there is no exception and // no finally handler. asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Ldftn].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Ldftn, MethodToCall = aChunk.Method, LoadAtILOffset = ILNumToGoTo }, TheScannerState); // Call Exceptions.HandleLeave asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.ExceptionsHandleLeaveMethod }, TheScannerState); } else if ((int)anILOpInfo.opCode.Value == (int)ILOps.ILOp.OpCodes.Endfinally) { //Endfinally is for leaving a (critical) finally section //We handle it by a higher-level implementation rather than // leaving it to each architecture to implement. //Endfinally is handled by inserting a call to the Exceptions.HandleEndFinally method asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.ExceptionsHandleEndFinallyMethod }, TheScannerState); } #endregion else if ((int)anILOpInfo.opCode.Value == (int)ILOps.ILOp.OpCodes.Castclass) { //This IL op is ignored for now. We assume the the Microsoft compiler (csc.exe) //makes the correct casting checks etc //And even if it doesn't, at the kernel level it is useful to be able to play // tricks with converting objects to/from MS types and custom kernel types // e.g. System.String to Kernel.FOS_System.String } else { //Indicates whether the IL op should actually be processed or not. // - At this stage, there are a few cases when the Il op should not be // processed but the new ASM should still be appended. bool processOp = true; #region Special-case op handling //If the op is a call: if ((int)anILOpInfo.opCode.Value == (int)ILOps.ILOp.OpCodes.Call) { //If the method to call is actually in mscorlib: if (anILOpInfo.MethodToCall != null && anILOpInfo.MethodToCall.DeclaringType.AssemblyQualifiedName. Contains("mscorlib")) { //We do not want to process ops which attempt to call methods in mscorlib! processOp = false; //We do not allow calls to methods declared in MSCorLib. //Some of these calls can just be ignored (e.g. GetTypeFromHandle is // called by typeof operator). //Ones which can't be ignored, will result in an error...by virtue of // the fact that they were ignored when they were required. //But just to make sure we save ourselves a headache later when // runtime debugging, output a message saying we ignored the call. result.ASM.AppendLine("; Call to method defined in MSCorLib ignored:"); // DEBUG INFO result.ASM.AppendLine("; " + anILOpInfo.MethodToCall.DeclaringType.FullName + "." + anILOpInfo.MethodToCall.Name); // DEBUG INFO //If the method is a call to a constructor in MsCorLib: if (anILOpInfo.MethodToCall is ConstructorInfo) { //Then we can presume it was a call to a base-class constructor (e.g. the Object constructor) // and so we just need to remove any args that were loaded onto the stack. result.ASM.AppendLine("; Method to call was constructor so removing params"); // DEBUG INFO //Remove args from stack //If the constructor was non-static, then the first arg is the instance reference. if (!anILOpInfo.MethodToCall.IsStatic) { asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Pop].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Pop }, TheScannerState); } foreach (ParameterInfo anInfo in anILOpInfo.MethodToCall.GetParameters()) { asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Pop].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Pop }, TheScannerState); } } } //If the method to call wasn't to a method in MsCorLib, we may need to set the method to call: else if(anILOpInfo.SetToGCDecRefCountMethod) { anILOpInfo.MethodToCall = TheScannerState.DecrementRefCountMethod; } } #endregion //If the op should be processed: if(processOp) { #region GC //GC requires us to decrement ref count of any field/local/arg // that is about to be overwritten //NewILOps - Unimplemented and new IL Ops need checking and below // adding if necessary if (aChunk.ApplyGC) { bool IncRefCount = false; switch ((ILOps.ILOp.OpCodes)anILOpInfo.opCode.Value) { case ILOps.ILOp.OpCodes.Stsfld: { int metadataToken = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); FieldInfo theField = TheScannerState.CurrentILChunk.Method.Module.ResolveField(metadataToken); if (Utils.IsGCManaged(theField.FieldType)) { asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Ldsfld].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Ldsfld, ValueBytes = anILOpInfo.ValueBytes }, TheScannerState); asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.DecrementRefCountMethod }, TheScannerState); IncRefCount = true; } } break; case ILOps.ILOp.OpCodes.Stloc: { UInt16 localIndex = (UInt16)Utils.ReadInt16(anILOpInfo.ValueBytes, 0); if (Utils.IsGCManaged(aChunk.LocalVariables[localIndex].TheType)) { asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Ldloc].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Ldloc, ValueBytes = anILOpInfo.ValueBytes }, TheScannerState); asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.DecrementRefCountMethod }, TheScannerState); IncRefCount = true; } } break; case ILOps.ILOp.OpCodes.Stloc_0: { if (Utils.IsGCManaged(aChunk.LocalVariables[0].TheType)) { asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Ldloc_0].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Ldloc_0 }, TheScannerState); asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.DecrementRefCountMethod }, TheScannerState); IncRefCount = true; } } break; case ILOps.ILOp.OpCodes.Stloc_1: { if (Utils.IsGCManaged(aChunk.LocalVariables[1].TheType)) { asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Ldloc_1].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Ldloc_1 }, TheScannerState); asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.DecrementRefCountMethod }, TheScannerState); IncRefCount = true; } } break; case ILOps.ILOp.OpCodes.Stloc_2: { if (Utils.IsGCManaged(aChunk.LocalVariables[2].TheType)) { asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Ldloc_2].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Ldloc_2 }, TheScannerState); asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.DecrementRefCountMethod }, TheScannerState); IncRefCount = true; } } break; case ILOps.ILOp.OpCodes.Stloc_3: { if (Utils.IsGCManaged(aChunk.LocalVariables[3].TheType)) { asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Ldloc_3].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Ldloc_3 }, TheScannerState); asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.DecrementRefCountMethod }, TheScannerState); IncRefCount = true; } } break; case ILOps.ILOp.OpCodes.Stloc_S: { UInt16 localIndex = (UInt16)anILOpInfo.ValueBytes[0]; if (Utils.IsGCManaged(aChunk.LocalVariables[localIndex].TheType)) { asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Ldloc_S].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Ldloc_S, ValueBytes = anILOpInfo.ValueBytes }, TheScannerState); asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.DecrementRefCountMethod }, TheScannerState); IncRefCount = true; } } break; case ILOps.ILOp.OpCodes.Stfld: { int metadataToken = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); FieldInfo theField = aChunk.Method.Module.ResolveField(metadataToken); if (Utils.IsGCManaged(theField.FieldType)) { // Items on stack: // - Object reference // - (New) Value to store // // We want to load the current value of the field // for which we must duplicate the object ref // But first, we must remove the (new) value to store // off the stack, while also storing it to put back // on the stack after so the store can continue // // So: // 1. Switch value to store and object ref on stack // 3. Duplicate the object ref // 4. Load the field value // 5. Call dec ref count // 6. Switch value to store and object ref back again //USE A SWITCH STACK ITEMS OP!! asm += "\r\n" + StackSwitchOp.Convert(new ILOpInfo() { }, TheScannerState); StackItem switchItem1 = TheScannerState.CurrentStackFrame.Stack.Pop(); StackItem switchItem2 = TheScannerState.CurrentStackFrame.Stack.Pop(); TheScannerState.CurrentStackFrame.Stack.Push(switchItem1); TheScannerState.CurrentStackFrame.Stack.Push(switchItem2); asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Dup].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Dup }, TheScannerState); asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Ldfld].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Ldfld, ValueBytes = anILOpInfo.ValueBytes }, TheScannerState); asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.DecrementRefCountMethod }, TheScannerState); asm += "\r\n" + StackSwitchOp.Convert(new ILOpInfo() { }, TheScannerState); switchItem1 = TheScannerState.CurrentStackFrame.Stack.Pop(); switchItem2 = TheScannerState.CurrentStackFrame.Stack.Pop(); TheScannerState.CurrentStackFrame.Stack.Push(switchItem1); TheScannerState.CurrentStackFrame.Stack.Push(switchItem2); IncRefCount = true; } } break; case ILOps.ILOp.OpCodes.Stelem: case ILOps.ILOp.OpCodes.Stelem_Ref: { bool doDecrement = false; bool isRefOp = false; if ((ILOps.ILOp.OpCodes)anILOpInfo.opCode.Value == ILOps.ILOp.OpCodes.Stelem_Ref) { doDecrement = TheScannerState.CurrentStackFrame.Stack.Peek().isGCManaged; isRefOp = true; } else { int metadataToken = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); Type elementType = aChunk.Method.Module.ResolveType(metadataToken); doDecrement = Utils.IsGCManaged(elementType); } if (doDecrement) { // Items on stack: // - Array reference // - Index // - (New) Value to store // // We want to load the current value of the element at Index in the array // for which we must duplicate the array ref and index // But first, we must remove the (new) value to store // off the stack, while also storing it to put back // on the stack after so the store can continue // // So: // 1. Switch (rotate) 1 times the top 3 values so that index is on top // 2. Duplicate the index // 3. Switch (rotate) 2 times the top 4 values so that array ref is on top // 4. Duplicate the array ref // 5. Switch (rotate) 4 times the top 5 values so that duplicate array ref and index are on top // 6. Do LdElem op to load existing element value // 7. Call GC.DecrementRefCount // 8. Switch (rotate) 1 times the top 3 values so that the stack is in its original state // (9. Continue to increment ref count as normal) // // The following is a diagram of the stack manipulation occurring here: // Key: A=Array ref, I=Index, V=Value to store, E=Loaded element // Top-most stack item appears last // // 1) Rotate x 1 2) Duplicate 3) Rot x 2 4) Dup // A,I,V ---------> V,A,I ---------> V,A,I,I ---------> I,I,V,A ---------> I,I,V,A,A // // // 5) Rot x 4 6) Ldelem 7) Call GC (Dec) // I,I,V,A,A ---------> I,V,A,A,I ---------> I,V,A,E ---------> I,V,A // // // 8) Rot x 1 9) Dup 10) Call GC (Inc) // I,V,A ---------> A,I,V ---------> A,I,V,V ---------> A,I,V #region 1. asm += "\r\n" + StackSwitchOp.Convert(new ILOpInfo() { ValueBytes = BitConverter.GetBytes(3) }, TheScannerState); rotateStackItems(3, 1); #endregion #region 2. asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Dup].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Dup }, TheScannerState); #endregion #region 3. asm += "\r\n" + StackSwitchOp.Convert(new ILOpInfo() { ValueBytes = BitConverter.GetBytes(4) }, TheScannerState); asm += "\r\n" + StackSwitchOp.Convert(new ILOpInfo() { ValueBytes = BitConverter.GetBytes(4) }, TheScannerState); rotateStackItems(4, 2); #endregion #region 4. asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Dup].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Dup }, TheScannerState); #endregion #region 5. asm += "\r\n" + StackSwitchOp.Convert(new ILOpInfo() { ValueBytes = BitConverter.GetBytes(5) }, TheScannerState); asm += "\r\n" + StackSwitchOp.Convert(new ILOpInfo() { ValueBytes = BitConverter.GetBytes(5) }, TheScannerState); asm += "\r\n" + StackSwitchOp.Convert(new ILOpInfo() { ValueBytes = BitConverter.GetBytes(5) }, TheScannerState); asm += "\r\n" + StackSwitchOp.Convert(new ILOpInfo() { ValueBytes = BitConverter.GetBytes(5) }, TheScannerState); rotateStackItems(5, 4); #endregion #region 6. asm += "\r\n" + TargetILOps[isRefOp ? ILOps.ILOp.OpCodes.Ldelem_Ref : ILOps.ILOp.OpCodes.Ldelem].Convert(new ILOpInfo() { opCode = isRefOp ? System.Reflection.Emit.OpCodes.Ldelem_Ref : System.Reflection.Emit.OpCodes.Ldelem, ValueBytes = anILOpInfo.ValueBytes, Position = anILOpInfo.Position }, TheScannerState); #endregion #region 7. asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.DecrementRefCountMethod }, TheScannerState); #endregion #region 8. asm += "\r\n" + StackSwitchOp.Convert(new ILOpInfo() { ValueBytes = BitConverter.GetBytes(3) }, TheScannerState); rotateStackItems(3, 1); #endregion IncRefCount = true; } } break; case ILOps.ILOp.OpCodes.Starg: { Int16 index = Utils.ReadInt16(anILOpInfo.ValueBytes, 0); index -= (Int16)(!aChunk.Method.IsStatic ? 1 : 0); if (Utils.IsGCManaged(aChunk.Method.GetParameters()[index].ParameterType)) { asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Ldarg].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Ldarg, ValueBytes = anILOpInfo.ValueBytes }, TheScannerState); asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.DecrementRefCountMethod }, TheScannerState); IncRefCount = true; } } break; case ILOps.ILOp.OpCodes.Starg_S: { Int16 index = (Int16)anILOpInfo.ValueBytes[0]; index -= (Int16)(!aChunk.Method.IsStatic ? 1 : 0); if (Utils.IsGCManaged(aChunk.Method.GetParameters()[index].ParameterType)) { asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Ldarg_S].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Ldarg_S, ValueBytes = anILOpInfo.ValueBytes }, TheScannerState); asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.DecrementRefCountMethod }, TheScannerState); IncRefCount = true; } } break; } if(IncRefCount && !TheScannerState.CurrentStackFrame.Stack.Peek().isNewGCObject) { TheScannerState.CurrentStackFrame.Stack.Peek().isNewGCObject = false; asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Dup].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Dup }, TheScannerState); asm += "\r\n" + TargetILOps[ILOps.ILOp.OpCodes.Call].Convert(new ILOpInfo() { opCode = System.Reflection.Emit.OpCodes.Call, MethodToCall = TheScannerState.IncrementRefCountMethod }, TheScannerState); } } #endregion result.ASM.AppendLine("; " + anILOpInfo.opCode.Name); //DEBUG INFO ILOps.ILOp TheIlOp = TargetILOps[(ILOps.ILOp.OpCodes)anILOpInfo.opCode.Value]; #region Debug if (TheSettings.DebugBuild) { if (anILOpInfo.opCode.Name == "nop" && !aChunk.NoDebugOps) { anILOpInfo.IsDebugOp = dbILOpInfo.IsDebugOp = true; dbILOpInfo.DebugOpMeta += "breakpoint;"; } else { dbILOpInfo.IsDebugOp = false; } } #endregion // Convert the IL op to ASM! asm += "\r\n" + TheIlOp.Convert(anILOpInfo, TheScannerState); } } { // Append the new ASm to the result ASM :) // See above (MethodStart op) for thow this code works. string[] asmLines = asm.Replace("\r", "").Split("\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); int asmLineNum = addASMLineNum; foreach (string asmLine in asmLines) { if (!asmLine.Split(';')[0].Trim().EndsWith(":")) { result.ASM.AppendLine(string.Format("{0}.IL_{1}_{2}:", MethodID, anILOpInfo.Position, asmLineNum)); } result.ASM.AppendLine(asmLine); asmLineNum++; } addASMLineNum = asmLineNum; } #region Debug if (TheSettings.DebugBuild) { dbILOpInfo.ASMEndPos = anILOpInfo.ASMEndPos = result.ASM.Length; DebugDatabase.AddILOpInfo(dbILOpInfo); } #endregion } catch(KeyNotFoundException) { result.ASM.AppendLine("; ERROR! Target architecture does not support this IL op."); //DEBUG INFO OutputError(new Exception("Target architecture does not support ILOp! Op type: " + anILOpInfo.opCode.Name + ", Op offset: " + anILOpInfo.Position.ToString("X2") + "\r\n" + methodSignature)); } catch (Exception ex) { result.ASM.AppendLine("; ERROR! ILScanner failed to process."); //DEBUG INFO OutputError(new Exception("Could not process an ILOp! Op type: " + anILOpInfo.opCode.Name + ", Op offset: " + anILOpInfo.Position.ToString("X2") + "\r\n" + methodSignature, ex)); } ASMStartPos = result.ASM.Length; addASMLineNum = 0; } #region Debug if (TheSettings.DebugBuild) { //Add debug info for local variables of this method int locIndex = 0; foreach (LocalVariable localItem in aChunk.LocalVariables) { DB_LocalVariable dbLocalVar = new DB_LocalVariable(); dbLocalVar.BytesSize = localItem.sizeOnStackInBytes; dbLocalVar.Id = Guid.NewGuid(); dbLocalVar.Index = locIndex; //We always call ProcessType just in case we missed a type // when loading assemblies dbLocalVar.TypeID = ProcessType(localItem.TheType).Id; dbLocalVar.MethodID = dbMethod.Id; DebugDatabase.AddLocalVariable(dbLocalVar); locIndex++; } } #endregion return result; }
partial void DeleteDB_ILOpInfo(DB_ILOpInfo instance);
private void detach_DB_ILOpInfos(DB_ILOpInfo entity) { this.SendPropertyChanging(); entity.DB_Method = null; }
partial void InsertDB_ILOpInfo(DB_ILOpInfo instance);
partial void UpdateDB_ILOpInfo(DB_ILOpInfo instance);
/// <summary> /// Removes the specified IL op info from the database. /// <para>To Do's: See <see cref="RemoveMethod"/>'s to do's.</para> /// </summary> /// <param name="anILOpInfo">The entry to remove.</param> /// <remarks> /// <para> /// For the moment this method does no more than just directly remove /// the entry from the database. /// </para> /// <para> /// <see cref="SubmitChanges"/> must be called at some point after this /// method for changes to actually be submitted to the database. /// </para> /// </remarks> public static void RemoveILOpInfo(DB_ILOpInfo anILOpInfo) { DB.DB_ILOpInfos.DeleteOnSubmit(anILOpInfo); }
/// <summary> /// Adds the pre-created IL op info to the database. All the entries's /// required parameters (i.e. ones which cannot be null) should /// be set. /// <para>To Do's: See <see cref="AddMethod"/>'s to do's.</para> /// </summary> /// <param name="anILOpInfo">The entry to add.</param> /// <remarks> /// <para> /// For the moment this method does no more than just directly add /// the entry to the database. /// </para> /// <para> /// <see cref="SubmitChanges"/> must be called at some point after this /// method for changes to actually be submitted to the database. /// </para> /// </remarks> public static void AddILOpInfo(DB_ILOpInfo anILOpInfo) { anILOpInfo.ValueBytes = null; DB.DB_ILOpInfos.InsertOnSubmit(anILOpInfo); }
/// <summary> /// Loads the current line's IL Op info (assuming we aren't in a plugged method) /// </summary> private void LoadCurrentIlOp() { if(currentMethod != null && (!currentMethod.Plugged.HasValue || !currentMethod.Plugged.Value)) { string currLabel = currentNearestMethodBasedLabel; try { int pos = int.Parse(currLabel.Split('.')[1].Split('_')[1]); IEnumerable<DB_ILOpInfo> PotIlOps = (from infos in currentMethod.DB_ILOpInfos where (infos.Position == pos) select infos); if (PotIlOps.Count() > 0) { if (currentILOpInfo != null) { lastILOpInfo = currentILOpInfo; } currentILOpInfo = PotIlOps.First(); } } catch(IndexOutOfRangeException) { //Not an unplugged method (somehow?) so not a valid name //so splitting fails //Just ignore if (currentILOpInfo != null) { lastILOpInfo = currentILOpInfo; } currentILOpInfo = null; } } }
/// <summary> /// Sends a request for the brwak address. /// </summary> public void GetBreakAddress() { currentMethod = null; currentMethodASM = null; currentNearestLabels = null; currentNearestMethodBasedLabel = null; if (currentILOpInfo != null) { lastILOpInfo = currentILOpInfo; } currentILOpInfo = null; arguments = null; locals = null; currentCSLine = null; currentCSMethod = null; currentCSSymbol = null; TheSerial.Write((byte)DebugCommands.GetBreakAddress); WaitForCommand(); }
/// <summary> /// Gets the assembler text for the specified IL Op. /// </summary> /// <param name="anILOp">The IL Op to get the ASM for.</param> /// <returns>The ASM text.</returns> public string GetILOpASM(DB_ILOpInfo anILOp) { string result = null; string methodASM = GetMethodASM(anILOp.DB_Method); result = methodASM.Substring(anILOp.ASMStartPos, anILOp.ASMEndPos - anILOp.ASMStartPos); return result; }