public static void Emit(ILGenerator ilg, ILInstruction il) { if (il.Operand == null) { ilg.Emit(il.Code); return; } var typeName = il.Operand.GetType().Name; switch (typeName) { case OperandTypeNames.Byte: ilg.Emit(il.Code, (byte)il.Operand); break; case OperandTypeNames.Float: ilg.Emit(il.Code, (float)il.Operand); break; case OperandTypeNames.Double: ilg.Emit(il.Code, (double)il.Operand); break; case OperandTypeNames.Short: ilg.Emit(il.Code, (short)il.Operand); break; case OperandTypeNames.Int: ilg.Emit(il.Code, (int)il.Operand); break; case OperandTypeNames.Long: ilg.Emit(il.Code, (long)il.Operand); break; case OperandTypeNames.String: ilg.Emit(il.Code, (string)il.Operand); break; case OperandTypeNames.Method: ilg.Emit(il.Code, (MethodInfo)il.Operand); break; case OperandTypeNames.Constructor: ilg.Emit(il.Code, (ConstructorInfo)il.Operand); break; case OperandTypeNames.Field: ilg.Emit(il.Code, (FieldInfo)il.Operand); break; case OperandTypeNames.Label: ilg.Emit(il.Code, (Label)il.Operand); break; default: throw new NotSupportedException(string.Format("OperandType: {0}", typeName)); } }
/// <summary> /// Constructs the array of ILInstructions according to the IL byte code. /// </summary> /// <param name="module"></param> private void ConstructInstructions(Module module) { byte[] il = this.il; int position = 0; instructions = new List<ILInstruction>(); while (position < il.Length) { ILInstruction instruction = new ILInstruction(); // get the operation code of the current instruction OpCode code = OpCodes.Nop; ushort value = il[position++]; if (value != 0xfe) { code = Globals.singleByteOpCodes[(int)value]; } else { value = il[position++]; code = Globals.multiByteOpCodes[(int)value]; value = (ushort)(value | 0xfe00); } instruction.Code = code; instruction.Offset = position - 1; int metadataToken = 0; // get the operand of the current operation switch (code.OperandType) { case OperandType.InlineBrTarget: metadataToken = ReadInt32(il, ref position); metadataToken += position; instruction.Operand = metadataToken; break; case OperandType.InlineField: metadataToken = ReadInt32(il, ref position); instruction.Operand = module.ResolveField(metadataToken); break; case OperandType.InlineMethod: metadataToken = ReadInt32(il, ref position); try { instruction.Operand = module.ResolveMethod(metadataToken); } catch { instruction.Operand = module.ResolveMember(metadataToken); } break; case OperandType.InlineSig: metadataToken = ReadInt32(il, ref position); instruction.Operand = module.ResolveSignature(metadataToken); break; case OperandType.InlineTok: metadataToken = ReadInt32(il, ref position); // SSS : see what to do here break; case OperandType.InlineType: metadataToken = ReadInt32(il, ref position); instruction.Operand = module.ResolveType(metadataToken); break; case OperandType.InlineI: { instruction.Operand = ReadInt32(il, ref position); break; } case OperandType.InlineI8: { instruction.Operand = ReadInt64(il, ref position); break; } case OperandType.InlineNone: { instruction.Operand = null; break; } case OperandType.InlineR: { instruction.Operand = ReadDouble(il, ref position); break; } case OperandType.InlineString: { metadataToken = ReadInt32(il, ref position); instruction.Operand = module.ResolveString(metadataToken); break; } case OperandType.InlineSwitch: { int count = ReadInt32(il, ref position); int[] casesAddresses = new int[count]; for (int i = 0; i < count; i++) { casesAddresses[i] = ReadInt32(il, ref position); } int[] cases = new int[count]; for (int i = 0; i < count; i++) { cases[i] = position + casesAddresses[i]; } break; } case OperandType.InlineVar: { instruction.Operand = ReadUInt16(il, ref position); break; } case OperandType.ShortInlineBrTarget: { instruction.Operand = ReadSByte(il, ref position) + position; break; } case OperandType.ShortInlineI: { instruction.Operand = ReadSByte(il, ref position); break; } case OperandType.ShortInlineR: { instruction.Operand = ReadSingle(il, ref position); break; } case OperandType.ShortInlineVar: { instruction.Operand = ReadByte(il, ref position); break; } default: { throw new Exception("Unknown operand type."); } } instructions.Add(instruction); } }
static void RedirectMethodInfo(ILInstruction il) { Type targetType = typeof(Console); var targetMethod = targetType.GetMethod("WriteLine", new Type[] { typeof(string) }); var redirect = typeof(Program).GetMethod("WriteLine"); var method = (MethodInfo)il.Operand; if (il.Operand.Equals(targetMethod)) { il.Operand = redirect; } }
public StringToInt(ILInstruction argument, string[] map) : base(OpCode.StringToInt) { this.Argument = argument; this.Map = map; }
public CodeThisReferenceExpression(ILInstruction inline) : base(inline) { }
/// <summary> /// if (logic.not(ldloc V_1)) Block IL_0149 { /// dynamic.setmember.compound B(target, dynamic.binary.operator AddAssign(ldloc V_2, value)) /// } else Block IL_0151 { /// dynamic.invokemember.invokespecial.discard add_B(target, value) /// } /// </summary> static bool MatchIsEventAssignmentIfInstruction(ILInstruction ifInst, DynamicIsEventInstruction isEvent, ILVariable flagVar, ILVariable getMemberVar, out DynamicSetMemberInstruction setMemberInst, out ILInstruction getMemberVarUse, out ILInstruction isEventConditionUse) { setMemberInst = null; getMemberVarUse = null; isEventConditionUse = null; if (!ifInst.MatchIfInstruction(out var condition, out var trueInst, out var falseInst)) { return(false); } if (MatchFlagEqualsZero(condition, flagVar)) { if (!condition.MatchCompEquals(out var left, out _)) { return(false); } isEventConditionUse = left; } else if (condition.MatchLdLoc(flagVar)) { var tmp = trueInst; trueInst = falseInst; falseInst = tmp; isEventConditionUse = condition; } else { return(false); } setMemberInst = Block.Unwrap(trueInst) as DynamicSetMemberInstruction; if (!(setMemberInst != null)) { return(false); } if (!isEvent.Argument.Match(setMemberInst.Target).Success) { return(false); } if (!(Block.Unwrap(falseInst) is DynamicInvokeMemberInstruction invokeMemberInst && invokeMemberInst.Arguments.Count == 2)) { return(false); } if (!isEvent.Argument.Match(invokeMemberInst.Arguments[0]).Success) { return(false); } if (!(setMemberInst.Value is DynamicBinaryOperatorInstruction binOp && binOp.Left.MatchLdLoc(getMemberVar))) { return(false); } getMemberVarUse = binOp.Left; return(true); }
static bool MatchFlagEqualsZero(ILInstruction condition, ILVariable flagVar) { return(condition.MatchCompEquals(out var left, out var right) && left.MatchLdLoc(flagVar) && right.MatchLdcI4(0)); }
public void EmulateInstruction(ILInstruction instruction) { // TODO: Perhaps include flag register updates? Registers[VMRegisters.IP] = new VMSlot() { U8 = (ulong)(instruction.Offset + instruction.Size) }; Registers[VMRegisters.SP] = new VMSlot() { U4 = (uint)instruction.ProgramState.Stack.Count }; switch (instruction.OpCode.Code) { case ILCode.PUSHR_OBJECT: { // TODO: This is definitely not accurate, but works for the purpose of this mini emulator (pushr_object sp). Stack.Push(new VMSlot { U8 = Registers[(VMRegisters)instruction.Operand].U8 }); break; } case ILCode.PUSHR_BYTE: Stack.Push(new VMSlot { U1 = Registers[(VMRegisters)instruction.Operand].U1 }); break; case ILCode.PUSHR_WORD: Stack.Push(new VMSlot { U2 = Registers[(VMRegisters)instruction.Operand].U2 }); break; case ILCode.PUSHR_DWORD: Stack.Push(new VMSlot { U4 = Registers[(VMRegisters)instruction.Operand].U4 }); break; case ILCode.PUSHR_QWORD: Stack.Push(new VMSlot { U8 = Registers[(VMRegisters)instruction.Operand].U8 }); break; case ILCode.PUSHI_DWORD: uint imm = Convert.ToUInt32(instruction.Operand); ulong sx = (imm & 0x80000000) != 0 ? 0xffffffffUL << 32 : 0; Stack.Push(new VMSlot { U8 = sx | imm }); break; case ILCode.PUSHI_QWORD: Stack.Push(new VMSlot { U8 = Convert.ToUInt64(instruction.Operand) }); break; case ILCode.ADD_DWORD: { var op2 = Stack.Pop(); var op1 = Stack.Pop(); Stack.Push(new VMSlot { U4 = op1.U4 + op2.U4 }); break; } case ILCode.ADD_QWORD: { var op2 = Stack.Pop(); var op1 = Stack.Pop(); Stack.Push(new VMSlot { U8 = op1.U8 + op2.U8 }); break; } case ILCode.POP: { Registers[(VMRegisters)instruction.Operand] = Stack.Pop(); break; } case ILCode.NOP: break; default: throw new EmulationException($"Failed to emulate the instruction {instruction}.", new NotSupportedException($"OpCode {instruction.OpCode.Code} not supported yet!")); } }
public void Run(ILFunction function, ILTransformContext context) { if (!context.Settings.Dynamic) { return; } this.context = context; Dictionary <IField, CallSiteInfo> callsites = new Dictionary <IField, CallSiteInfo>(); HashSet <BlockContainer> modifiedContainers = new HashSet <BlockContainer>(); foreach (var block in function.Descendants.OfType <Block>()) { if (block.Instructions.Count < 2) { continue; } // Check if, we deal with a callsite cache field null check: // if (comp(ldsfld <>p__3 == ldnull)) br IL_000c // br IL_002b if (!(block.Instructions.SecondToLastOrDefault() is IfInstruction ifInst)) { continue; } if (!(block.Instructions.LastOrDefault() is Branch branchAfterInit)) { continue; } if (!MatchCallSiteCacheNullCheck(ifInst.Condition, out var callSiteCacheField, out var callSiteDelegate, out bool invertBranches)) { continue; } if (!ifInst.TrueInst.MatchBranch(out var trueBlock)) { continue; } Block callSiteInitBlock, targetBlockAfterInit; if (invertBranches) { callSiteInitBlock = branchAfterInit.TargetBlock; targetBlockAfterInit = trueBlock; } else { callSiteInitBlock = trueBlock; targetBlockAfterInit = branchAfterInit.TargetBlock; } if (!ScanCallSiteInitBlock(callSiteInitBlock, callSiteCacheField, callSiteDelegate, out var callSiteInfo, out var blockAfterInit)) { continue; } if (targetBlockAfterInit != blockAfterInit) { continue; } callSiteInfo.DelegateType = callSiteDelegate; callSiteInfo.ConditionalJumpToInit = ifInst; callSiteInfo.Inverted = invertBranches; callSiteInfo.BranchAfterInit = branchAfterInit; callsites.Add(callSiteCacheField, callSiteInfo); } var storesToRemove = new List <StLoc>(); foreach (var invokeCall in function.Descendants.OfType <CallVirt>()) { if (invokeCall.Method.DeclaringType.Kind != TypeKind.Delegate || invokeCall.Method.Name != "Invoke" || invokeCall.Arguments.Count == 0) { continue; } var firstArgument = invokeCall.Arguments[0]; if (firstArgument.MatchLdLoc(out var stackSlot) && stackSlot.Kind == VariableKind.StackSlot && stackSlot.IsSingleDefinition) { firstArgument = ((StLoc)stackSlot.StoreInstructions[0]).Value; } if (!firstArgument.MatchLdFld(out var cacheFieldLoad, out var targetField)) { continue; } if (!cacheFieldLoad.MatchLdsFld(out var cacheField)) { continue; } if (!callsites.TryGetValue(cacheField, out var callsite)) { continue; } context.Stepper.Step("Transform callsite for " + callsite.MemberName); var deadArguments = new List <ILInstruction>(); ILInstruction replacement = MakeDynamicInstruction(callsite, invokeCall, deadArguments); if (replacement == null) { continue; } invokeCall.ReplaceWith(replacement); Debug.Assert(callsite.ConditionalJumpToInit?.Parent is Block); var block = ((Block)callsite.ConditionalJumpToInit.Parent); if (callsite.Inverted) { block.Instructions.Remove(callsite.ConditionalJumpToInit); callsite.BranchAfterInit.ReplaceWith(callsite.ConditionalJumpToInit.TrueInst); } else { block.Instructions.Remove(callsite.ConditionalJumpToInit); } foreach (var arg in deadArguments) { if (arg.MatchLdLoc(out var temporary) && temporary.Kind == VariableKind.StackSlot && temporary.IsSingleDefinition && temporary.LoadCount == 0) { StLoc stLoc = (StLoc)temporary.StoreInstructions[0]; if (stLoc.Parent is Block storeParentBlock) { var value = stLoc.Value; if (value.MatchLdsFld(out var cacheFieldCopy) && cacheFieldCopy.Equals(cacheField)) { storesToRemove.Add(stLoc); } if (value.MatchLdFld(out cacheFieldLoad, out var targetFieldCopy) && cacheFieldLoad.MatchLdsFld(out cacheFieldCopy) && cacheField.Equals(cacheFieldCopy) && targetField.Equals(targetFieldCopy)) { storesToRemove.Add(stLoc); } } } } modifiedContainers.Add((BlockContainer)block.Parent); } foreach (var inst in storesToRemove) { Block parentBlock = (Block)inst.Parent; parentBlock.Instructions.RemoveAt(inst.ChildIndex); } foreach (var container in modifiedContainers) { container.SortBlocks(deleteUnreachableBlocks: true); } }
private bool PatternMatchRefTypes(Block block, BlockContainer container, ILTransformContext context, ref ControlFlowGraph?cfg) { if (!block.MatchIfAtEndOfBlock(out var condition, out var trueInst, out var falseInst)) { return(false); } int pos = block.Instructions.Count - 3; if (condition.MatchLdLoc(out var conditionVar)) { // stloc conditionVar(comp.o(ldloc s == ldnull)) // if (logic.not(ldloc conditionVar)) br trueBlock if (pos < 0) { return(false); } if (!(conditionVar.IsSingleDefinition && conditionVar.LoadCount == 1 && conditionVar.Kind == VariableKind.StackSlot)) { return(false); } if (!block.Instructions[pos].MatchStLoc(conditionVar, out condition)) { return(false); } pos--; } if (condition.MatchCompEqualsNull(out var loadInNullCheck)) { ExtensionMethods.Swap(ref trueInst, ref falseInst); } else if (condition.MatchCompNotEqualsNull(out loadInNullCheck)) { // do nothing } else { return(false); } if (!loadInNullCheck.MatchLdLoc(out var s)) { return(false); } if (!s.IsSingleDefinition) { return(false); } if (s.Kind is not(VariableKind.Local or VariableKind.StackSlot)) { return(false); } if (pos < 0) { return(false); } // stloc V(isinst T(testedOperand)) ILInstruction storeToV = block.Instructions[pos]; if (!storeToV.MatchStLoc(out var v, out var value)) { return(false); } if (value.MatchLdLoc(s)) { // stloc v(ldloc s) pos--; if (!block.Instructions[pos].MatchStLoc(s, out value)) { return(false); } if (v.Kind is not(VariableKind.Local or VariableKind.StackSlot)) { return(false); } if (s.LoadCount != 2) { return(false); } } else { if (v != s) { return(false); } } IType?unboxType; if (value is UnboxAny unboxAny) { // stloc S(unbox.any T(isinst T(testedOperand))) unboxType = unboxAny.Type; value = unboxAny.Argument; } else { unboxType = null; } if (value is not IsInst { Argument : var testedOperand, Type : var type })