/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> /// <exception cref="System.NotSupportedException"> /// Thrown if field to load is a floating value or the field to load /// is not of size 4 or 8 bytes. /// </exception> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); aScannerState.CurrentStackFrame.Stack.Pop(); DB_Type arrayDBType = DebugDatabase.GetType(aScannerState.GetTypeID(aScannerState.ArrayClass)); int lengthOffset = aScannerState.GetFieldOffset(arrayDBType, "length"); // 1. Check array reference is not null // 1.1. Move array ref into eax // 1.2. Compare eax (array ref) to 0 // 1.3. If not zero, jump to continue execution further down // 1.4. Otherwise, call Exceptions.ThrowNullReferenceException // 2. Load array length string ContinueExecutionLabelBase = string.Format("{0}.IL_{1}_ContinueExecution", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string ContinueExecutionLabel1 = ContinueExecutionLabelBase + "1"; // 1.1. Move array ref into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov eax, [esp]"); // 1.2. Compare eax (array ref) to 0 result.AppendLine("cmp eax, 0"); // 1.3. If not zero, jump to continue execution further down result.AppendLine("jnz " + ContinueExecutionLabel1); // 1.4. Otherwise, call Exceptions.ThrowNullReferenceException result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowNullReferenceExceptionMethod))); result.AppendLine(ContinueExecutionLabel1 + ":"); //2. Load array length // - Pop array ref result.AppendLine("pop dword ecx"); // - Load length from array ref GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ecx", lengthOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [ecx+{0}]", lengthOffset)); // - Push array length result.AppendLine("push dword eax"); aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { isFloat = false, sizeOnStackInBytes = 4, isGCManaged = false }); return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> /// <exception cref="System.NotSupportedException"> /// Thrown if the value to store is floating point or /// if the value is not 4 or 8 bytes in size. /// </exception> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); int metadataToken = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); FieldInfo theField = aScannerState.CurrentILChunk.Method.Module.ResolveField(metadataToken); DB_Type objDBType = DebugDatabase.GetType(aScannerState.GetTypeID(theField.DeclaringType)); int offset = aScannerState.GetFieldOffset(objDBType, theField.Name); int stackSize = Utils.GetNumBytesForType(theField.FieldType); int memSize = theField.FieldType.IsValueType ? Utils.GetSizeForType(theField.FieldType) : stackSize; StackItem value = aScannerState.CurrentStackFrame.Stack.Pop(); StackItem objPointer = aScannerState.CurrentStackFrame.Stack.Pop(); if (value.isFloat) { //SUPPORT - floats throw new NotSupportedException("Storing fields of type float not supported yet!"); } //Get object pointer GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", stackSize, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov ecx, [esp+{0}]", stackSize)); //Pop and mov value for (int i = 0; i < memSize; i += 2) { if (memSize - i == 1) { result.AppendLine("pop word ax"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ecx", offset + i, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov byte [ecx+{0}], al", offset + i)); } else { result.AppendLine("pop word ax"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ecx", offset + i, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov word [ecx+{0}], ax", offset + i)); } } result.AppendLine(string.Format("add esp, {0}", ((stackSize - memSize) / 2) * 2)); //Rounds down result.AppendLine("add esp, 4");//Pop object pointer return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> /// <exception cref="System.NotSupportedException"> /// Thrown if any argument or the return value is a floating point number. /// </exception> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); MethodBase methodToCall = anILOpInfo.MethodToCall; //The method to call is a method base //A method base can be either a method info i.e. a normal method //or a constructor method. The two types are treated separately. if(methodToCall is MethodInfo) { //Need to do callvirt related stuff to load address of method to call // - Check for invoke of a delegate - if so, treat rather differently from normal callvirt string call_Label = string.Format("{0}.IL_{1}_Call", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); if (typeof(Delegate).IsAssignableFrom(((MethodInfo)methodToCall).DeclaringType)) { //Callvirt to delegate method // - We only support calls to Invoke at the moment if (methodToCall.Name != "Invoke") { throw new NotSupportedException("Callvirt to Delegate method not supported! Method name: " + methodToCall.Name); } int bytesForAllParams = ((MethodInfo)methodToCall).GetParameters().Select(x => Utils.GetNumBytesForType(x.ParameterType)).Sum(); // - Move into eax address of function to call from stack - delegate reference is function pointer //All the parameters for the method that was called List<Type> allParams = ((MethodInfo)methodToCall).GetParameters().Select(x => x.ParameterType).ToList(); int bytesForParams = allParams.Select(x => Utils.GetNumBytesForType(x)).Sum(); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", bytesForParams, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov dword eax, [esp+{0}]", bytesForParams)); //Allocate space on the stack for the return value as necessary Type retType = ((MethodInfo)methodToCall).ReturnType; StackItem returnItem = new StackItem() { isFloat = Utils.IsFloat(retType), sizeOnStackInBytes = Utils.GetNumBytesForType(retType), isGCManaged = Utils.IsGCManaged(retType) }; //We do not push the return value onto the stack unless it has size > 0 //We do not push the return value onto our stack at this point - it is pushed after the call is done if (returnItem.sizeOnStackInBytes != 0) { if (returnItem.isFloat) { //SUPPORT - floats throw new NotSupportedException("Cannot handle float return values!"); } else if (returnItem.sizeOnStackInBytes == 4) { result.AppendLine("push dword 0"); } else if (returnItem.sizeOnStackInBytes == 8) { result.AppendLine("push dword 0"); result.AppendLine("push dword 0"); } else { throw new NotSupportedException("Invalid return stack operand size!"); } } //Append the actual call result.AppendLine("call eax"); //After a call, we need to remove the return value and parameters from the stack //This is most easily done by just adding the total number of bytes for params and //return value to the stack pointer (ESP register). //Stores the number of bytes to add // - Initially at least 4 for the delegate (method) ref/pointer int bytesToAdd = 4; //Go through all params that must be removed foreach (Type aParam in allParams) { //Pop the paramter off our stack //(Note: Return value was never pushed onto our stack. See above) aScannerState.CurrentStackFrame.Stack.Pop(); //Add the size of the paramter to the total number of bytes to pop bytesToAdd += Utils.GetNumBytesForType(aParam); } //If there is a return value on the stack if (returnItem.sizeOnStackInBytes != 0) { //We need to store the return value then pop all the params //We now push the return value onto our stack as, //after all is said and done below, it will be the //top item on the stack aScannerState.CurrentStackFrame.Stack.Push(returnItem); //SUPPORT - floats (with above) //Pop the return value into the eax register //We will push it back on after params are skipped over. if (returnItem.sizeOnStackInBytes == 4) { result.AppendLine("pop dword eax"); } else if (returnItem.sizeOnStackInBytes == 8) { result.AppendLine("pop dword eax"); result.AppendLine("pop dword edx"); } } //Skip over the params result.AppendLine(string.Format("add esp, {0}", bytesToAdd)); //If necessary, push the return value onto the stack. if (returnItem.sizeOnStackInBytes != 0) { //SUPPORT - floats (with above) //The return value was stored in eax //So push it back onto the stack if (returnItem.sizeOnStackInBytes == 4) { result.AppendLine("push dword eax"); } else if (returnItem.sizeOnStackInBytes == 8) { result.AppendLine("push dword edx"); result.AppendLine("push dword eax"); } } } else { //Normal callvirt // - Get object ref from loaded args // - Check object ref not null // - Get type table entry from object ref // - Get method table from type table entry // - Scan method table for the method we want // - If found, load method address // - Else, check for parent type method table // - If no parent type method table, throw exception // - Else, scan parent type method table string methodIDValueWanted = aScannerState.GetMethodIDValue((MethodInfo)methodToCall); string loopTableEntries_Label = string.Format("{0}.IL_{1}_LoopMethodTable", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string notEqual_Label = string.Format("{0}.IL_{1}_NotEqual", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string endOfTable_Label = string.Format("{0}.IL_{1}_EndOfTable", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string notFound_Label = string.Format("{0}.IL_{1}_NotFound", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string notNull_Label = string.Format("{0}.IL_{1}_NotNullMem", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); DB_Type declaringDBType = DebugDatabase.GetType(aScannerState.GetTypeID(methodToCall.DeclaringType)); //Get object ref int bytesForAllParams = ((MethodInfo)methodToCall).GetParameters().Select(x => Utils.GetNumBytesForType(x.ParameterType)).Sum(); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", bytesForAllParams, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov dword eax, [esp+{0}]", bytesForAllParams)); //Check object ref result.AppendLine("cmp eax, 0"); result.AppendLine(string.Format("jnz {0}", notNull_Label)); result.AppendLine("call GetEIP"); result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.HaltMethod))); result.AppendLine(notNull_Label + ":"); //Get type ref int typeOffset = aScannerState.GetFieldOffset(declaringDBType, "_Type"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", typeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [eax+{0}]", typeOffset)); //Get method table ref int methodTablePtrOffset = aScannerState.GetTypeFieldOffset("MethodTablePtr"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", methodTablePtrOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [eax+{0}]", methodTablePtrOffset)); //Loop through entries result.AppendLine(loopTableEntries_Label + ":"); //Load ID Val for current entry GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov ebx, [eax]"); //Compare to wanted ID value result.AppendLine("cmp ebx, " + methodIDValueWanted); //If equal, load method address into eax result.AppendLine("jne " + notEqual_Label); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov eax, [eax+4]"); result.AppendLine("jmp " + call_Label); result.AppendLine(notEqual_Label + ":"); //Else, compare to 0 to check for end of table result.AppendLine("cmp ebx, 0"); result.AppendLine("jz " + endOfTable_Label); //Not 0? Move to next entry then loop again result.AppendLine("add eax, 8"); result.AppendLine("jmp " + loopTableEntries_Label); result.AppendLine(endOfTable_Label + ":"); //Compare address value to 0 //If not zero, there is a parent method table to check GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov ebx, [eax+4]"); result.AppendLine("cmp ebx, 0"); result.AppendLine("jz " + notFound_Label); //Load parent method table and loop result.AppendLine("mov eax, ebx"); result.AppendLine("jmp " + loopTableEntries_Label); result.AppendLine(notFound_Label + ":"); //Throw exception! result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowNullReferenceExceptionMethod))); result.AppendLine(call_Label + ":"); //Allocate space on the stack for the return value as necessary Type retType = ((MethodInfo)methodToCall).ReturnType; StackItem returnItem = new StackItem() { isFloat = Utils.IsFloat(retType), sizeOnStackInBytes = Utils.GetNumBytesForType(retType), isGCManaged = Utils.IsGCManaged(retType) }; //We do not push the return value onto the stack unless it has size > 0 //We do not push the return value onto our stack at this point - it is pushed after the call is done if (returnItem.sizeOnStackInBytes != 0) { if (returnItem.isFloat) { //SUPPORT - floats throw new NotSupportedException("Cannot handle float return values!"); } else if (returnItem.sizeOnStackInBytes == 4) { result.AppendLine("push dword 0"); } else if (returnItem.sizeOnStackInBytes == 8) { result.AppendLine("push dword 0"); result.AppendLine("push dword 0"); } else { throw new NotSupportedException("Invalid return stack operand size!"); } } //Append the actual call result.AppendLine("call eax"); //After a call, we need to remove the return value and parameters from the stack //This is most easily done by just adding the total number of bytes for params and //return value to the stack pointer (ESP register). //Stores the number of bytes to add int bytesToAdd = 0; //All the parameters for the method that was called List<Type> allParams = ((MethodInfo)methodToCall).GetParameters().Select(x => x.ParameterType).ToList(); //Go through each one if (!methodToCall.IsStatic) { allParams.Insert(0, methodToCall.DeclaringType); } foreach (Type aParam in allParams) { //Pop the paramter off our stack //(Note: Return value was never pushed onto our stack. See above) aScannerState.CurrentStackFrame.Stack.Pop(); //Add the size of the paramter to the total number of bytes to pop bytesToAdd += Utils.GetNumBytesForType(aParam); } //If the number of bytes to add to skip over params is > 0 if (bytesToAdd > 0) { //If there is a return value on the stack if (returnItem.sizeOnStackInBytes != 0) { //We need to store the return value then pop all the params //We now push the return value onto our stack as, //after all is said and done below, it will be the //top item on the stack aScannerState.CurrentStackFrame.Stack.Push(returnItem); //SUPPORT - floats (with above) //Pop the return value into the eax register //We will push it back on after params are skipped over. if (returnItem.sizeOnStackInBytes == 4) { result.AppendLine("pop dword eax"); } else if (returnItem.sizeOnStackInBytes == 8) { result.AppendLine("pop dword eax"); result.AppendLine("pop dword edx"); } } //Skip over the params result.AppendLine(string.Format("add esp, {0}", bytesToAdd)); //If necessary, push the return value onto the stack. if (returnItem.sizeOnStackInBytes != 0) { //SUPPORT - floats (with above) //The return value was stored in eax //So push it back onto the stack if (returnItem.sizeOnStackInBytes == 4) { result.AppendLine("push dword eax"); } else if (returnItem.sizeOnStackInBytes == 8) { result.AppendLine("push dword edx"); result.AppendLine("push dword eax"); } } } //No params to skip over but we might still need to store return value else if (returnItem.sizeOnStackInBytes != 0) { //The return value will be the top item on the stack. //So all we need to do is push the return item onto our stack. aScannerState.CurrentStackFrame.Stack.Push(returnItem); } } } else if(methodToCall is ConstructorInfo) { throw new NotSupportedException("How the hell are we getting callvirts to constructor methods?!"); } return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> /// <exception cref="System.NotSupportedException"> /// Thrown if constant is a floating point number. /// </exception> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); Type elementType = null; bool pushValue = true; int sizeToPush = 4; bool signExtend = true; bool isFloat = false; switch ((OpCodes)anILOpInfo.opCode.Value) { case OpCodes.Ldelem: { signExtend = false; //Load the metadata token used to get the type info int metadataToken = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Get the type info for the element type elementType = aScannerState.CurrentILChunk.Method.Module.ResolveType(metadataToken); } break; case OpCodes.Ldelema: { signExtend = false; //Load the metadata token used to get the type info int metadataToken = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Get the type info for the element type elementType = aScannerState.CurrentILChunk.Method.Module.ResolveType(metadataToken); } break; case OpCodes.Ldelem_R4: case OpCodes.Ldelem_R8: //TODO - Add more LdElem op variants support throw new NotSupportedException("Ldelem op variant not supported yet!"); case OpCodes.Ldelem_I1: sizeToPush = 1; elementType = typeof(sbyte); break; case OpCodes.Ldelem_I2: sizeToPush = 2; elementType = typeof(Int16); break; case OpCodes.Ldelem_U1: sizeToPush = 1; signExtend = false; elementType = typeof(byte); break; case OpCodes.Ldelem_U2: sizeToPush = 2; signExtend = false; elementType = typeof(UInt16); break; case OpCodes.Ldelem_Ref: signExtend = false; elementType = null; break; case OpCodes.Ldelem_U4: signExtend = false; elementType = typeof(UInt32); break; case OpCodes.Ldelem_I4: elementType = typeof(Int32); break; case OpCodes.Ldelem_I8: elementType = typeof(Int64); break; } if (isFloat) { //TODO - Support floats throw new NotSupportedException("LdElem for floats not supported yet!"); } //Get element from array and push the value onto the stack // (or for LdElemA push the address of the value) //This involves: // 1. Check array reference is not null // - If it is, throw NullReferenceException // 2. Check array element type is correct // - If not, throw ArrayTypeMismatchException // 3. Check index to get is > -1 and < array length // - If not, throw IndexOutOfRangeException // 4. Calculate address of element // 5. Push the element onto the stack //Stack setup upon entering this op: (top-most downwards) // 0. Index of element to get as Int32 (dword) // 1. Array object reference as address (dword) string ContinueExecutionLabelBase = string.Format("{0}.IL_{1}_Load_ContinueExecution", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); DB_Type arrayDBType = DebugDatabase.GetType(aScannerState.GetTypeID(aScannerState.ArrayClass)); // 1. Check array reference is not null // 1.1. Move array ref into eax // 1.2. Compare eax (array ref) to 0 // 1.3. If not zero, jump to continue execution further down // 1.4. Otherwise, call Exceptions.ThrowNullReferenceException string ContinueExecutionLabel1 = ContinueExecutionLabelBase + "1"; // 1.1. Move array ref into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov eax, [esp+4]"); // 1.2. Compare eax (array ref) to 0 result.AppendLine("cmp eax, 0"); // 1.3. If not zero, jump to continue execution further down result.AppendLine("jnz " + ContinueExecutionLabel1); // 1.4. Otherwise, call Exceptions.ThrowNullReferenceException result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowNullReferenceExceptionMethod))); result.AppendLine(ContinueExecutionLabel1 + ":"); // 2. Check array element type is correct // 2.1. Move element type ref into eax // 2.2. Move element type ref from array object into ebx // 2.3. Compare eax to ebx // 2.4. If the same, jump to continue execution further down // 2.5. Otherwise, call Exceptions.ThrowArrayTypeMismatchException //string ContinueExecutionLabel2 = ContinueExecutionLabelBase + "2"; //// 2.1. Move element type ref into eax int elemTypeOffset = aScannerState.GetFieldOffset(arrayDBType, "elemType"); //if (elementType != null) //{ // result.AppendLine(string.Format("mov eax, {0}", aScannerState.GetTypeIdString(aScannerState.GetTypeID(elementType)))); // // 2.2. Move element type ref from array object into ebx // // - Calculate the offset of the field from the start of the array object // // - Move array ref into ebx //GlobalMethods.CheckAddrFromRegister(result, aScannerState, "esp", 4); // result.AppendLine("mov ebx, [esp+4]"); // // - Move elemType ref ([ebx+offset]) into ebx // GlobalMethods.CheckAddrFromRegister(result, aScannerState, "ebx", elemTypeOffset); // result.AppendLine(string.Format("mov ebx, [ebx+{0}]", elemTypeOffset)); // // 2.3. Compare eax to ebx // result.AppendLine("cmp eax, ebx"); // // 2.4. If the same, jump to continue execution further down // result.AppendLine("je " + ContinueExecutionLabel2); // // 2.5. Otherwise, call Exceptions.ThrowArrayTypeMismatchException // result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowArrayTypeMismatchExceptionMethod))); // result.AppendLine(ContinueExecutionLabel2 + ":"); //} // 3. Check index to get is > -1 and < array length // 3.1. Move index into eax // 3.2. Move array length into ebx // 3.2. Compare eax to 0 // 3.3. Jump if greater than to next test condition (3.5) // 3.4. Otherwise, call Exceptions.ThrowIndexOutOfRangeException // 3.5. Compare eax to ebx // 3.6. Jump if less than to continue execution further down // 3.7. Otherwise, call Exceptions.ThrowIndexOutOfRangeException string ContinueExecutionLabel3_1 = ContinueExecutionLabelBase + "3_1"; string ContinueExecutionLabel3_2 = ContinueExecutionLabelBase + "3_2"; // 3.1. Move index into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov eax, [esp]"); // 3.2. Move array length into ecx // - Calculate the offset of the field from the start of the array object int lengthOffset = aScannerState.GetFieldOffset(arrayDBType, "length"); // - Move array ref into ebx GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov ebx, [esp+4]"); // - Move length value ([ebx+offset]) into ebx GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ebx", lengthOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov ebx, [ebx+{0}]", lengthOffset)); // 3.2. Compare eax to 0 result.AppendLine("cmp eax, 0"); // 3.3. Jump if greater than to next test condition (3.5) result.AppendLine("jge " + ContinueExecutionLabel3_1); // 3.4. Otherwise, call Exceptions.ThrowIndexOutOfRangeException result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowIndexOutOfRangeExceptionMethod))); result.AppendLine(ContinueExecutionLabel3_1 + ":"); // 3.5. Compare eax to ebx result.AppendLine("cmp eax, ebx"); // 3.6. Jump if less than to continue execution further down result.AppendLine("jl " + ContinueExecutionLabel3_2); // 3.7. Otherwise, call Exceptions.ThrowIndexOutOfRangeException result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowIndexOutOfRangeExceptionMethod))); result.AppendLine(ContinueExecutionLabel3_2 + ":"); // 4. Calculate address of element // 4.1. Pop index into ebx // 4.2. Pop array ref into eax // 4.3. Move element type ref (from array ref) into eax // 4.4. Move IsValueType (from element ref type) into ecx // 4.5. If IsValueType, continue to 4.6., else goto 4.8. // 4.6. Move Size (from element type ref) into eax // 4.7. Skip over 4.8. // 4.8. Move StackSize (from element type ref) into eax // 4.9. Mulitply eax by ebx (index by element size) // 4.10. Move array ref into ebx // 4.11. Add enough to go past Kernel.FOS_System.Array fields // 4.12. Add eax and ebx (array ref + fields + (index * element size)) string ContinueExecutionLabel4_1 = ContinueExecutionLabelBase + "4_1"; string ContinueExecutionLabel4_2 = ContinueExecutionLabelBase + "4_2"; // 4.1. Pop index into ebx result.AppendLine("pop ebx"); // 4.2. Move array ref into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov eax, [esp]"); // 4.3. Move element type ref (from array ref) into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", elemTypeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [eax+{0}]", elemTypeOffset)); // 4.4. Move IsValueType (from element ref type) into ecx int isValueTypeOffset = aScannerState.GetTypeFieldOffset("IsValueType"); result.AppendLine("mov ecx, 0"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", isValueTypeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov byte cl, [eax+{0}]", isValueTypeOffset)); // 4.5. If IsValueType, continue to 4.6., else goto 4.8. result.AppendLine("cmp ecx, 0"); result.AppendLine("jz " + ContinueExecutionLabel4_1); // 4.6. Move Size (from element type ref) into eax int sizeOffset = aScannerState.GetTypeFieldOffset("Size"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", sizeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [eax+{0}]", sizeOffset)); // 4.7. Skip over 4.8. result.AppendLine("jmp " + ContinueExecutionLabel4_2); // 4.8. Move StackSize (from element type ref) into eax result.AppendLine(ContinueExecutionLabel4_1 + ":"); int stackSizeOffset = aScannerState.GetTypeFieldOffset("StackSize"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", stackSizeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [eax+{0}]", stackSizeOffset)); // 4.9. Mulitply eax by ebx (index by element size) result.AppendLine(ContinueExecutionLabel4_2 + ":"); result.AppendLine("mul ebx"); // 4.10. Pop array ref into ebx result.AppendLine("pop ebx"); // 4.11. Add enough to go past Kernel.FOS_System.Array fields int allFieldsOffset = 0; #region Offset calculation { //Get the child links of the type (i.e. the fields of the type) List<DB_ComplexTypeLink> allChildLinks = arrayDBType.ChildTypes.ToList(); //Calculate the offset //We use StackBytesSize since fields that are reference types are only stored as a pointer allFieldsOffset = allChildLinks.Sum(x => x.ChildType.IsValueType ? x.ChildType.BytesSize : x.ChildType.StackBytesSize); } #endregion result.AppendLine(string.Format("add ebx, {0}", allFieldsOffset)); // 4.12. Add eax and ebx (array ref + fields + (index * element size)) result.AppendLine("add eax, ebx"); // 5. Push the element onto the stack // 5.1. Push value at [eax] (except for LdElemA op in which case just push address) if (pushValue) { switch (sizeToPush) { case 1: result.AppendLine("mov dword ebx, 0"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov byte bl, [eax]"); if (signExtend) { throw new NotSupportedException("Sign extend byte to 4 bytes in LdElem not supported!"); } break; case 2: result.AppendLine("mov dword ebx, 0"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov word bx, [eax]"); if (signExtend) { result.AppendLine("cwde"); } break; case 4: GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword ebx, [eax]"); break; case 8: GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov word ebx, [eax]"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov word ecx, [eax+4]"); break; } if (sizeToPush == 8) { result.AppendLine("push ecx"); } result.AppendLine("push ebx"); } else { result.AppendLine("push eax"); } // 5.2. Pop index and array ref from our stack aScannerState.CurrentStackFrame.Stack.Pop(); aScannerState.CurrentStackFrame.Stack.Pop(); // 5.3. Push element onto our stack aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { sizeOnStackInBytes = sizeToPush > 4 ? 8 : 4, isFloat = isFloat, isNewGCObject = false, isGCManaged = pushValue ? Utils.IsGCManaged(elementType) : false }); return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); MethodBase constructorMethod = anILOpInfo.MethodToCall; Type objectType = constructorMethod.DeclaringType; //New obj must: // - Ignore for creation of Delegates // - Allocate memory on the heap for the object // - If no memory is left, throw a panic attack because we're out of memory... // - Call the specified constructor if (typeof(Delegate).IsAssignableFrom(objectType)) { result.AppendLine("; Ignore newobj calls for Delegates"); //Still need to: // - Remove the "object" param but preserve the "function pointer" GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword eax, [esp]"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword [esp+4], eax"); result.AppendLine("add esp, 4"); return result.ToString().Trim(); } //The label to jump to if allocated memory isn't null //i.e. not out of memory. string NotNullLabel = string.Format("{0}.IL_{1}_NotNullMem", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); //Attempt to allocate memory on the heap for the new object //This involves: // - Pushing the type reference onto the stack // - Calling GC NewObj method // - Check the pointer == 0, if so, out of memory //Push type reference string typeIdStr = aScannerState.GetTypeIdString(aScannerState.GetTypeID(objectType)); result.AppendLine(string.Format("push dword {0}", typeIdStr)); //Push a dword for return value (i.e. new object pointer) result.AppendLine("push dword 0"); //Get the GC.NewObj method ID (i.e. ASM label) string methodLabel = aScannerState.GetMethodID(aScannerState.NewObjMethod); //Call GC.NewObj result.AppendLine(string.Format("call {0}", methodLabel)); //Pop the return value (i.e. new object pointer) result.AppendLine("pop dword eax"); //Remove arg 0 from stack result.AppendLine("add esp, 4"); //Check if pointer == 0? result.AppendLine("cmp eax, 0"); //If it isn't 0, not out of memory so continue execution result.AppendLine(string.Format("jnz {0}", NotNullLabel)); //If we are out of memory, we have a massive problem //Because it means we don't have space to create a new exception object //So ultimately we just have to throw a kernel panic //Throw a panic attack... ( :/ ) by calling kernel Halt(uint lastAddress) //result.AppendLine("call GetEIP"); //result.AppendLine("push dword esp"); //result.AppendLine("push dword ebp"); //result.AppendLine("pushad"); //result.AppendLine("mov dword eax, 0xDEADBEEF"); //result.AppendLine("mov dword ebx, 0x1"); //result.AppendLine("mov dword ecx, 1"); //result.AppendLine("mov dword [staticfield_System_Boolean_Kernel_FOS_System_GC_Enabled], 1"); //result.AppendLine("mov dword [staticfield_System_Boolean_Kernel_FOS_System_Heap_PreventAllocation], 0"); //result.AppendLine("jmp method_System_Void_RETEND_Kernel_PreReqs_DECLEND_PageFaultDetection_NAMEEND___Fail"); result.AppendLine("call GetEIP"); result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.HaltMethod))); //Insert the not null label result.AppendLine(NotNullLabel + ":"); //Call the specified constructor //This involves: // - Push empty dword onto stack // - Move all args down by one dword // - Move object reference into dword as first arg // - Call constructor result.AppendLine("push dword 0"); int sizeOfArgs = 0; ParameterInfo[] allParams = constructorMethod.GetParameters(); foreach(ParameterInfo aParam in allParams) { sizeOfArgs += Utils.GetNumBytesForType(aParam.ParameterType); aScannerState.CurrentStackFrame.Stack.Pop(); } result.AppendLine("mov dword ebx, esp"); if (sizeOfArgs > 0) { if (sizeOfArgs % 4 != 0) { throw new InvalidOperationException("sizeOfArgs not exact multiple of 4!"); } result.AppendLine(string.Format("mov dword ecx, {0}", sizeOfArgs / 4)); string ShiftArgsLoopLabel = string.Format("{0}.IL_{1}_ShiftArgsLoop", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); result.AppendLine(ShiftArgsLoopLabel + ":"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ebx", 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword edx, [ebx+4]"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ebx", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword [ebx], edx"); result.AppendLine("add ebx, 4"); result.AppendLine(string.Format("loop {0}", ShiftArgsLoopLabel)); } GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ebx", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword [ebx], eax"); result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(constructorMethod))); //Only remove args from stack - we want the object pointer to remain on the stack result.AppendLine(string.Format("add esp, {0}", sizeOfArgs)); aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { isFloat = false, sizeOnStackInBytes = 4, isNewGCObject = true, isGCManaged = true }); return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); //Load the metadata token used to get the type info int metadataToken = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Get the type info for the element type Type elementType = aScannerState.CurrentILChunk.Method.Module.ResolveType(metadataToken); //New array must: // - Allocate memory on the heap for the object // - If no memory is left, throw a panic attack because we're out of memory... // - Call the specified constructor //The label to jump to if allocated memory isn't null //i.e. not out of memory. string NotNullLabel = string.Format("{0}.IL_{1}_NotNullMem", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); //Attempt to allocate memory on the heap for the new array //This involves: // - (Number of elements is already on the stack) // - Pushing the element type reference onto the stack // - Calling GC NewArr method // - Check the pointer == 0, if so, out of memory //Push type reference string typeIdStr = aScannerState.GetTypeIdString(aScannerState.GetTypeID(elementType)); result.AppendLine(string.Format("push dword {0}", typeIdStr)); //Push a dword for return value (i.e. new array pointer) result.AppendLine("push dword 0"); //Get the GC.NewArr method ID (i.e. ASM label) string methodLabel = aScannerState.GetMethodID(aScannerState.NewArrMethod); //Call GC.NewArr result.AppendLine(string.Format("call {0}", methodLabel)); //Pop the return value (i.e. new array pointer) result.AppendLine("pop dword eax"); //Remove args from stack result.AppendLine("add esp, 8"); //Check if pointer == 0? result.AppendLine("cmp eax, 0"); //If it isn't 0, not out of memory so continue execution result.AppendLine(string.Format("jnz {0}", NotNullLabel)); //If we are out of memory, we have a massive problem //Because it means we don't have space to create a new exception object //So ultimately we just have to throw a kernel panic //Throw a panic attack... ( :/ ) by calling kernel Halt(uint lastAddress) //result.AppendLine("call GetEIP"); //result.AppendLine("push dword esp"); //result.AppendLine("push dword ebp"); //result.AppendLine("pushad"); //result.AppendLine("mov dword eax, 0xDEADBEEF"); //result.AppendLine("mov dword ebx, 0x2"); //result.AppendLine("mov dword ecx, 1"); //result.AppendLine("mov dword [staticfield_System_Boolean_Kernel_FOS_System_GC_Enabled], 1"); //result.AppendLine("mov dword [staticfield_System_Boolean_Kernel_FOS_System_Heap_PreventAllocation], 0"); //result.AppendLine("jmp method_System_Void_RETEND_Kernel_PreReqs_DECLEND_PageFaultDetection_NAMEEND___Fail"); result.AppendLine("call GetEIP"); result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.HaltMethod))); //Insert the not null label result.AppendLine(NotNullLabel + ":"); //Push new array pointer result.AppendLine("push dword eax"); aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { isFloat = false, sizeOnStackInBytes = 4, isNewGCObject = true, isGCManaged = true }); return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> /// <exception cref="System.NotSupportedException"> /// Thrown if constant is a floating point number. /// </exception> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); Type elementType = null; //bool pushValue = true; int sizeToPop = 4; bool isFloat = false; switch ((OpCodes)anILOpInfo.opCode.Value) { case OpCodes.Stelem: { //Load the metadata token used to get the type info int metadataToken = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Get the type info for the element type elementType = aScannerState.CurrentILChunk.Method.Module.ResolveType(metadataToken); } break; case OpCodes.Stelem_R4: case OpCodes.Stelem_R8: //TODO - Add more StElem op variants support throw new NotSupportedException("Stelem op variant not supported yet!"); case OpCodes.Stelem_I1: sizeToPop = 1; elementType = typeof(sbyte); break; case OpCodes.Stelem_I2: sizeToPop = 2; elementType = typeof(Int16); break; case OpCodes.Stelem_Ref: elementType = null; break; case OpCodes.Stelem_I4: elementType = typeof(Int32); break; case OpCodes.Stelem_I8: elementType = typeof(Int64); break; } if (isFloat) { //TODO - Support floats throw new NotSupportedException("StElem for floats not supported yet!"); } //Get element from array and push the value onto the stack // (or for LdElemA push the address of the value) //This involves: // 1. Check array reference is not null // - If it is, throw NullReferenceException // 2. Check array element type is correct // - If not, throw ArrayTypeMismatchException // 3. Check index to get is > -1 and < array length // - If not, throw IndexOutOfRangeException // 4. Calculate address of element // 5. Pop the value from the stack into the element //Stack setup upon entering this op: (top-most downwards) // 0. Value to store (dword or 2 dwords) // 1. Index of element to get as Int32 (dword) // 2. Array object reference as address (dword) string ContinueExecutionLabelBase = string.Format("{0}.IL_{1}_Store_ContinueExecution", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); DB_Type arrayDBType = DebugDatabase.GetType(aScannerState.GetTypeID(aScannerState.ArrayClass)); // 1. Check array reference is not null // 1.1. Move array ref into eax // 1.2. Compare eax (array ref) to 0 // 1.3. If not zero, jump to continue execution further down // 1.4. Otherwise, call Exceptions.ThrowNullReferenceException string ContinueExecutionLabel1 = ContinueExecutionLabelBase + "1"; // 1.1. Move array ref into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", sizeToPop == 8 ? 12 : 8, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [esp+{0}]", sizeToPop == 8 ? 12 : 8)); // 1.2. Compare eax (array ref) to 0 result.AppendLine("cmp eax, 0"); // 1.3. If not zero, jump to continue execution further down result.AppendLine("jnz " + ContinueExecutionLabel1); // 1.4. Otherwise, call Exceptions.ThrowNullReferenceException result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowNullReferenceExceptionMethod))); result.AppendLine(ContinueExecutionLabel1 + ":"); // 2. Check array element type is correct // 2.1. Move element type ref into eax // 2.2. Move element type ref from array object into ebx // 2.3. Compare eax to ebx // 2.4. If the same, jump to continue execution further down // 2.5. Otherwise, call Exceptions.ThrowArrayTypeMismatchException //string ContinueExecutionLabel2 = ContinueExecutionLabelBase + "2"; //// 2.1. Move element type ref into eax //if (elementType != null) //{ // result.AppendLine(string.Format("mov eax, {0}", aScannerState.GetTypeIdString(aScannerState.GetTypeID(elementType)))); //} //else //{ // //Should be the same for all classes since they are (indirectly) derived from ObjectWithType // int typeOffset = aScannerState.GetFieldOffset(arrayDBType, "_Type"); // // - Move value (which is a ref) into eax // GlobalMethods.CheckAddrFromRegister(result, aScannerState, "esp", 0); // result.AppendLine("mov eax, [esp]"); // // - Move value type ref (from value (ref)) into eax // GlobalMethods.CheckAddrFromRegister(result, aScannerState, "eax", typeOffset); // result.AppendLine(string.Format("mov eax, [eax+{0}]", typeOffset)); //} //// 2.2. Move element type ref from array object into ebx //// - Move array ref into ebx //GlobalMethods.CheckAddrFromRegister(result, aScannerState, "esp", sizeToPop == 8 ? 12 : 8); //result.AppendLine(string.Format("mov ebx, [esp+{0}]", sizeToPop == 8 ? 12 : 8)); //// - Move elemType ref ([ebx+offset]) into ebx int elemTypeOffset = aScannerState.GetFieldOffset(arrayDBType, "elemType"); //GlobalMethods.CheckAddrFromRegister(result, aScannerState, "ebx", elemTypeOffset); //result.AppendLine(string.Format("mov ebx, [ebx+{0}]", elemTypeOffset)); //// 2.3. Compare eax to ebx //result.AppendLine("cmp eax, ebx"); //// 2.4. If the same, jump to continue execution further down //result.AppendLine("je " + ContinueExecutionLabel2); //// 2.5. Otherwise, call Exceptions.ThrowArrayTypeMismatchException //result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowArrayTypeMismatchExceptionMethod))); //result.AppendLine(ContinueExecutionLabel2 + ":"); // 3. Check index to get is > -1 and < array length // 3.1. Move index into eax // 3.2. Move array length into ebx // 3.2. Compare eax to 0 // 3.3. Jump if greater than to next test condition (3.5) // 3.4. Otherwise, call Exceptions.ThrowIndexOutOfRangeException // 3.5. Compare eax to ebx // 3.6. Jump if less than to continue execution further down // 3.7. Otherwise, call Exceptions.ThrowIndexOutOfRangeException string ContinueExecutionLabel3_1 = ContinueExecutionLabelBase + "3_1"; string ContinueExecutionLabel3_2 = ContinueExecutionLabelBase + "3_2"; // 3.1. Move index into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", sizeToPop == 8 ? 8 : 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [esp+{0}]", sizeToPop == 8 ? 8 : 4)); // 3.2. Move array length into ecx // - Calculate the offset of the field from the start of the array object int lengthOffset = aScannerState.GetFieldOffset(arrayDBType, "length"); // - Move array ref into ebx GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", sizeToPop == 8 ? 12 : 8, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov ebx, [esp+{0}]", sizeToPop == 8 ? 12 : 8)); // - Move length value ([ebx+offset]) into ebx GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ebx", lengthOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov ebx, [ebx+{0}]", lengthOffset)); // 3.2. Compare eax to 0 result.AppendLine("cmp eax, 0"); // 3.3. Jump if greater than to next test condition (3.5) result.AppendLine("jge " + ContinueExecutionLabel3_1); // 3.4. Otherwise, call Exceptions.ThrowIndexOutOfRangeException result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowIndexOutOfRangeExceptionMethod))); result.AppendLine(ContinueExecutionLabel3_1 + ":"); // 3.5. Compare eax to ebx result.AppendLine("cmp eax, ebx"); // 3.6. Jump if less than to continue execution further down result.AppendLine("jl " + ContinueExecutionLabel3_2); // 3.7. Otherwise, call Exceptions.ThrowIndexOutOfRangeException result.AppendLine(string.Format("call {0}", aScannerState.GetMethodID(aScannerState.ThrowIndexOutOfRangeExceptionMethod))); result.AppendLine(ContinueExecutionLabel3_2 + ":"); // 4. Calculate address of element // 4.0. Pop value into ecx:edx // 4.1. Pop index into ebx // 4.2. Pop array ref into eax // 4.3. Move element type ref (from array ref) into eax // 4.4. Push eax // 4.5. Move IsValueType (from element ref type) into eax // 4.6. If IsValueType, continue to 4.6., else goto 4.9. // 4.7. Pop eax // 4.8. Move Size (from element type ref) into eax // 4.9. Skip over 4.9. and 4.10. // 4.10. Pop eax // 4.11. Move StackSize (from element type ref) into eax // 4.12. Mulitply eax by ebx (index by element size) // 4.13. Move array ref into ebx // 4.14. Add enough to go past Kernel.FOS_System.Array fields // 4.15. Add eax and ebx (array ref + fields + (index * element size)) string ContinueExecutionLabel4_1 = ContinueExecutionLabelBase + "4_1"; string ContinueExecutionLabel4_2 = ContinueExecutionLabelBase + "4_2"; // 4.0. Pop value into ecx:edx result.AppendLine("pop ecx"); if (sizeToPop == 8) { result.AppendLine("pop edx"); } // 4.1. Pop index into ebx result.AppendLine("pop ebx"); // 4.2. Move array ref into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "esp", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov eax, [esp]"); // 4.3. Move element type ref (from array ref) into eax GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", elemTypeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [eax+{0}]", elemTypeOffset)); // 4.4. Push eax result.AppendLine("push eax"); // 4.5. Move IsValueType (from element ref type) into eax int isValueTypeOffset = aScannerState.GetTypeFieldOffset("IsValueType"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", isValueTypeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov byte al, [eax+{0}]", isValueTypeOffset)); // Zero-out the rest of eax result.AppendLine("and eax, 1"); // 4.6. If IsValueType, continue to 4.7., else goto 4.9. result.AppendLine("cmp eax, 0"); result.AppendLine("jz " + ContinueExecutionLabel4_1); // 4.7. Pop eax result.AppendLine("pop eax"); // 4.8. Move Size (from element type ref) into eax int sizeOffset = aScannerState.GetTypeFieldOffset("Size"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", sizeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [eax+{0}]", sizeOffset)); // 4.9. Skip over 4.9. and 4.10. result.AppendLine("jmp " + ContinueExecutionLabel4_2); // 4.10. Pop eax result.AppendLine(ContinueExecutionLabel4_1 + ":"); result.AppendLine("pop eax"); // 4.11. Move StackSize (from element type ref) into eax int stackSizeOffset = aScannerState.GetTypeFieldOffset("StackSize"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", stackSizeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov eax, [eax+{0}]", stackSizeOffset)); // 4.12. Mulitply eax by ebx (index by element size) result.AppendLine(ContinueExecutionLabel4_2 + ":"); result.AppendLine("mul ebx"); // 4.13. Pop array ref into ebx result.AppendLine("pop ebx"); // 4.14. Add enough to go past Kernel.FOS_System.Array fields int allFieldsOffset = 0; #region Offset calculation { //Get the child links of the type (i.e. the fields of the type) List<DB_ComplexTypeLink> allChildLinks = arrayDBType.ChildTypes.ToList(); //Calculate the offset //We use StackBytesSize since fields that are reference types are only stored as a pointer allFieldsOffset = allChildLinks.Sum(x => x.ChildType.IsValueType ? x.ChildType.BytesSize : x.ChildType.StackBytesSize); } #endregion result.AppendLine(string.Format("add ebx, {0}", allFieldsOffset)); // 4.15. Add eax and ebx (array ref + fields + (index * element size)) result.AppendLine("add eax, ebx"); // 5. Pop the element from the stack to array // 5.1. Move value in edx:ecx to [eax] if (sizeToPop == 8) { GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword [eax], ecx"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 4, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword [eax+4], edx"); } else if(sizeToPop == 4) { GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword [eax], ecx"); } else if (sizeToPop == 2) { GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov word [eax], cx"); } else if (sizeToPop == 1) { GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov byte [eax], cl"); } // 5.2. Pop index, array ref and value from our stack aScannerState.CurrentStackFrame.Stack.Pop(); aScannerState.CurrentStackFrame.Stack.Pop(); aScannerState.CurrentStackFrame.Stack.Pop(); return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> /// <exception cref="System.NotSupportedException"> /// Thrown if field to load is a floating value or the field to load /// is not of size 4 or 8 bytes. /// </exception> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); //Get the field's token that is used to get FieldInfo from the assembly int metadataToken = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); //Get the field info from the referencing assembly FieldInfo theField = aScannerState.CurrentILChunk.Method.Module.ResolveField(metadataToken); //Get the database type information about the object that contains the field DB_Type objDBType = DebugDatabase.GetType(aScannerState.GetTypeID(theField.DeclaringType)); int offset = aScannerState.GetFieldOffset(objDBType, theField.Name); //Is the value to load a floating pointer number? bool valueisFloat = Utils.IsFloat(theField.FieldType); //Get the size of the value to load (in bytes, as it will appear on the stack) int stackSize = Utils.GetNumBytesForType(theField.FieldType); int memSize = theField.FieldType.IsValueType ? Utils.GetSizeForType(theField.FieldType) : stackSize; //Pop the object pointer from our stack StackItem objPointer = aScannerState.CurrentStackFrame.Stack.Pop(); //If the value to load is a float, erm...abort... if (valueisFloat) { //SUPPORT - floats throw new NotSupportedException("Loading fields of type float not supported yet!"); } //Pop object pointer result.AppendLine("pop dword ecx"); if ((OpCodes)anILOpInfo.opCode.Value == OpCodes.Ldflda) { result.AppendLine(string.Format("add ecx, {0}", offset)); result.AppendLine("push dword ecx"); aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { isFloat = false, sizeOnStackInBytes = 4, isGCManaged = false }); } else { //Push value at pointer+offset int sizeNotInMem = stackSize - memSize; int sizeToSub = (sizeNotInMem / 2) * 2; //Rounds down for (int i = 0; i < sizeToSub; i += 2) { result.AppendLine("push word 0"); } for (int i = memSize + (memSize % 2); i > 0; i -= 2) { if (sizeToSub != sizeNotInMem) { result.AppendLine("mov ax, 0"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ecx", offset + i - 2, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov byte al, [ecx+{0}]", offset + i - 2)); result.AppendLine("push word ax"); } else { GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ecx", offset + i - 2, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov word ax, [ecx+{0}]", offset + i - 2)); result.AppendLine("push word ax"); } } aScannerState.CurrentStackFrame.Stack.Push(new StackItem() { isFloat = valueisFloat, sizeOnStackInBytes = stackSize, isGCManaged = Utils.IsGCManaged(theField.FieldType) }); } return result.ToString().Trim(); }
/// <summary> /// See base class documentation. /// </summary> /// <param name="anILOpInfo">See base class documentation.</param> /// <param name="aScannerState">See base class documentation.</param> /// <returns>See base class documentation.</returns> public override string Convert(ILOpInfo anILOpInfo, ILScannerState aScannerState) { StringBuilder result = new StringBuilder(); string Label_3 = string.Format("{0}.IL_{1}_Point3", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string Label_False1 = string.Format("{0}.IL_{1}_False1", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string Label_False2 = string.Format("{0}.IL_{1}_False2", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); string Label_End = string.Format("{0}.IL_{1}_End", aScannerState.GetMethodID(aScannerState.CurrentILChunk.Method), anILOpInfo.Position); // Test if the object provided inherits from the specified class // 1. Pop object ref // 1.1. Test if object ref is null: // 1.1.1 True: Push null and continue // 1.1.2 False: Go to 2 // 2. Load object type // 3. Test if object type == provided type: // 3.1 True: Push object ref and continue // 3.2 False: // 3.2.1. Move to base type // 3.2.2. Test if base type null: // 3.2.2.1 True: Push null and continue // 3.2.2.2 False: Jump back to (3) // 1. Pop object ref result.AppendLine("pop dword eax"); // 1.1. Test if object ref is null: result.AppendLine("cmp eax, 0"); result.AppendLine("jne " + Label_False1); // 1.1.1 True: Push null and continue result.AppendLine("push dword 0"); result.AppendLine("jmp " + Label_End); // 1.1.2 False: Go to 2 result.AppendLine(Label_False1 + ":"); // 2. Load object type GlobalMethods.InsertPageFaultDetection(result, aScannerState, "eax", 0, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine("mov dword ebx, [eax]"); // 3. Test if object type == provided type: int metadataToken = Utils.ReadInt32(anILOpInfo.ValueBytes, 0); Type theType = aScannerState.CurrentILChunk.Method.Module.ResolveType(metadataToken); string TestTypeId = aScannerState.GetTypeIdString(aScannerState.GetTypeID(theType)); result.AppendLine("mov dword ecx, " + TestTypeId); result.AppendLine(Label_3 + ":"); result.AppendLine("cmp ebx, ecx"); result.AppendLine("jne " + Label_False2); // 3.1 True: Push object ref and continue result.AppendLine("push dword eax"); result.AppendLine("jmp " + Label_End); // 3.2 False: result.AppendLine(Label_False2 + ":"); // 3.2.1. Move to base type int baseTypeOffset = aScannerState.GetTypeFieldOffset("TheBaseType"); GlobalMethods.InsertPageFaultDetection(result, aScannerState, "ebx", baseTypeOffset, (OpCodes)anILOpInfo.opCode.Value); result.AppendLine(string.Format("mov dword ebx, [ebx+{0}]", baseTypeOffset)); // 3.2.2. Test if base type null: result.AppendLine("cmp ebx, 0"); // 3.2.2.2 False: Jump back to (3) result.AppendLine("jne " + Label_3); // 3.2.2.1 True: Push null and continue result.AppendLine("push dword 0"); result.AppendLine(Label_End + ":"); return result.ToString().Trim(); }