示例#1
0
 public static StackSlot[] ModifyStack(StackSlot[] stack, int popCount, int pushCount, ByteCode pushDefinition)
 {
     var newStack = new StackSlot[stack.Length - popCount + pushCount];
     Array.Copy(stack, newStack, stack.Length - popCount);
     for (int i = stack.Length - popCount; i < newStack.Length; i++)
     {
         newStack[i] = new StackSlot(new[] { pushDefinition }, null);
     }
     return newStack;
 }
示例#2
0
 /// <summary>
 /// Find all targets the given bytecode can branch to.
 /// </summary>
 private static List<ByteCode> FindBranchTargets(ByteCode byteCode, Dictionary<Instruction, ByteCode> instrToByteCode, HashSet<ByteCode> exceptionHandlerStarts)
 {
     // Find all successors
     var branchTargets = new List<ByteCode>();
     if (!byteCode.Code.IsUnconditionalControlFlow())
     {
         if (exceptionHandlerStarts.Contains(byteCode.Next))
         {
             // Do not fall though down to exception handler
             // It is invalid IL as per ECMA-335 §12.4.2.8.1, but some obfuscators produce it
         }
         else
         {
             branchTargets.Add(byteCode.Next);
         }
     }
     var operandAsInstructionArray = byteCode.Operand as Instruction[];
     if (operandAsInstructionArray != null)
     {
         branchTargets.AddRange(operandAsInstructionArray.Select(inst => instrToByteCode[inst]));
     }
     else
     {
         var operandAsInstruction = byteCode.Operand as Instruction;
         if (operandAsInstruction != null)
         {
             var target = instrToByteCode[operandAsInstruction];
             branchTargets.Add(target);
         }
         else
         {
             var operandAsLookupSwitchData = byteCode.Operand as LookupSwitchData;
             if (operandAsLookupSwitchData != null)
             {
                 branchTargets.AddRange(operandAsLookupSwitchData.Pairs.Select(pair => instrToByteCode[pair.Target]));
                 // Default target is ignored here because a a Br follows.
             }
         }
     }
     return branchTargets;
 }
示例#3
0
        List<ByteCode> StackAnalysis(MethodDefinition methodDef)
        {
            // Create temporary structure for the stack analysis
            List<ByteCode> body = new List<ByteCode>(methodDef.Body.Instructions.Count);
            foreach(Instruction inst in methodDef.Body.Instructions) {
                OpCode opCode  = inst.OpCode;
                object operand = inst.Operand;
                MethodBodyRocks.ExpandMacro(ref opCode, ref operand, methodDef.Body);
                ByteCode byteCode = new ByteCode() {
                    Offset      = inst.Offset,
                    EndOffset   = inst.Next != null ? inst.Next.Offset : methodDef.Body.CodeSize,
                    OpCode      = opCode,
                    Operand     = operand,
                    PopCount    = inst.GetPopCount(),
                    PushCount   = inst.GetPushCount()
                };
                instrToByteCode[inst] = byteCode;
                body.Add(byteCode);
            }
            for (int i = 0; i < body.Count - 1; i++) {
                body[i].Next = body[i + 1];
            }

            Queue<ByteCode> agenda = new Queue<ByteCode>();

            // Add known states
            body[0].StackBefore = new List<StackSlot>();
            agenda.Enqueue(body[0]);

            if(methodDef.Body.HasExceptionHandlers) {
                foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) {
                    ByteCode tryStart = instrToByteCode[ex.TryStart];
                    tryStart.StackBefore = new List<StackSlot>();
                    agenda.Enqueue(tryStart);

                    ByteCode handlerStart = instrToByteCode[ex.HandlerType == ExceptionHandlerType.Filter ? ex.FilterStart : ex.HandlerStart];
                    handlerStart.StackBefore = new List<StackSlot>();
                    if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter) {
                        handlerStart.StackBefore.Add(new StackSlot(null));
                    }
                    agenda.Enqueue(handlerStart);

                    // Control flow is not required to reach endfilter
                    if (ex.HandlerType == ExceptionHandlerType.Filter) {
                        ByteCode endFilter = instrToByteCode[ex.FilterEnd.Previous];
                        endFilter.StackBefore = new List<StackSlot>();
                    }
                }
            }

            // Process agenda
            while(agenda.Count > 0) {
                ByteCode byteCode = agenda.Dequeue();

                // Calculate new stack
                List<StackSlot> newStack = byteCode.CloneStack(byteCode.PopCount);
                for (int i = 0; i < byteCode.PushCount; i++) {
                    newStack.Add(new StackSlot(byteCode));
                }

                // Apply the state to any successors
                List<ByteCode> branchTargets = new List<ByteCode>();
                if (byteCode.OpCode.CanFallThough()) {
                    branchTargets.Add(byteCode.Next);
                }
                if (byteCode.OpCode.IsBranch()) {
                    if (byteCode.Operand is Instruction[]) {
                        foreach(Instruction inst in (Instruction[])byteCode.Operand) {
                            ByteCode target = instrToByteCode[inst];
                            branchTargets.Add(target);
                            // The target of a branch must have label
                            if (target.Label == null) {
                                target.Label = new ILLabel() { Name = target.Name };
                            }
                        }
                    } else {
                        ByteCode target = instrToByteCode[(Instruction)byteCode.Operand];
                        branchTargets.Add(target);
                        // The target of a branch must have label
                        if (target.Label == null) {
                            target.Label = new ILLabel() { Name = target.Name };
                        }
                    }
                }
                foreach (ByteCode branchTarget in branchTargets) {
                    if (branchTarget.StackBefore == null) {
                        branchTarget.StackBefore = newStack;
                        // Do not share one stack for several bytecodes
                        if (branchTargets.Count > 1) {
                            branchTarget.StackBefore = branchTarget.CloneStack(0);
                        }
                        agenda.Enqueue(branchTarget);
                    } else {
                        if (branchTarget.StackBefore.Count != newStack.Count) {
                            throw new Exception("Inconsistent stack size at " + byteCode.Name);
                        }

                        // Merge stacks
                        bool modified = false;
                        for (int i = 0; i < newStack.Count; i++) {
                            List<ByteCode> oldPushedBy = branchTarget.StackBefore[i].PushedBy;
                            List<ByteCode> newPushedBy = oldPushedBy.Union(newStack[i].PushedBy).ToList();
                            if (newPushedBy.Count > oldPushedBy.Count) {
                                branchTarget.StackBefore[i].PushedBy = newPushedBy;
                                modified = true;
                            }
                        }

                        if (modified) {
                            agenda.Enqueue(branchTarget);
                        }
                    }
                }
            }

            // Genertate temporary variables to replace stack
            foreach(ByteCode byteCode in body) {
                int argIdx = 0;
                int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count;
                for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) {
                    StackSlot arg = byteCode.StackBefore[i];
                    ILVariable tmpVar = new ILVariable() { Name = string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), IsGenerated = true };
                    arg.LoadFrom = tmpVar;
                    foreach(ByteCode pushedBy in arg.PushedBy) {
                        // TODO: Handle exception variables
                        if (pushedBy != null) {
                            if (pushedBy.StoreTo == null) {
                                pushedBy.StoreTo = new List<ILVariable>(1);
                            }
                            pushedBy.StoreTo.Add(tmpVar);
                        }
                    }
                    if (arg.PushedBy.Count == 1) {
                        allowInline[tmpVar] = true;
                    }
                    argIdx++;
                }
            }

            // Convert local varibles
            Variables = methodDef.Body.Variables.Select(v => new ILVariable() { Name = string.IsNullOrEmpty(v.Name) ?  "var_" + v.Index : v.Name, Type = v.VariableType }).ToList();
            int[] numReads  = new int[Variables.Count];
            int[] numWrites = new int[Variables.Count];
            foreach(ByteCode byteCode in body) {
                if (byteCode.OpCode == OpCodes.Ldloc) {
                    int index = ((VariableDefinition)byteCode.Operand).Index;
                    byteCode.Operand = Variables[index];
                    numReads[index]++;
                }
                if (byteCode.OpCode == OpCodes.Stloc) {
                    int index = ((VariableDefinition)byteCode.Operand).Index;
                    byteCode.Operand = Variables[index];
                    numWrites[index]++;
                }
            }

            // Find which variables we can inline
            if (this.optimize) {
                for (int i = 0; i < Variables.Count; i++) {
                    if (numReads[i] == 1 && numWrites[i] == 1) {
                        allowInline[Variables[i]] = true;
                    }
                }
            }

            // Convert branch targets to labels
            foreach(ByteCode byteCode in body) {
                if (byteCode.Operand is Instruction[]) {
                    List<ILLabel> newOperand = new List<ILLabel>();
                    foreach(Instruction target in (Instruction[])byteCode.Operand) {
                        newOperand.Add(instrToByteCode[target].Label);
                    }
                    byteCode.Operand = newOperand.ToArray();
                } else if (byteCode.Operand is Instruction) {
                    byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label;
                }
            }

            return body;
        }
示例#4
0
 internal static NormalizedByteCode GetNormalizedByteCode(ByteCode bc)
 {
     return data[(int)bc].normbc;
 }
示例#5
0
 internal static int GetArg(ByteCode bc, int arg)
 {
     if((data[(int)bc].flags & ByteCodeFlags.FixedArg) != 0)
     {
         return data[(int)bc].arg;
     }
     return arg;
 }
示例#6
0
 private ByteCodeMetaData(ByteCode bc, ByteCodeMode reg, ByteCodeModeWide wide, bool cannotThrow)
 {
     this.reg = reg;
     this.wide = wide;
     this.normbc = (NormalizedByteCode)bc;
     this.arg = 0;
     this.flags = ByteCodeFlags.None;
     if(cannotThrow)
     {
         this.flags |= ByteCodeFlags.CannotThrow;
     }
     data[(int)bc] = this;
 }
示例#7
0
        /// <summary>
        /// Analyse the instructions in the method code and convert them to a ByteCode list.
        /// </summary>
        private List<ByteCode> StackAnalysis()
        {
            var instrToByteCode = new Dictionary<Instruction, ByteCode>();

            // Create temporary structure for the stack analysis
            var body = new List<ByteCode>(methodDef.Body.Instructions.Count);
            List<Instruction> prefixes = null;
            foreach (var inst in methodDef.Body.Instructions)
            {
                if (inst.OpCode.OpCodeType == OpCodeType.Prefix)
                {
                    if (prefixes == null)
                        prefixes = new List<Instruction>(1);
                    prefixes.Add(inst);
                    continue;
                }
                var code = (AstCode)inst.OpCode.Code;
                var operand = inst.Operand;
                AstCodeUtil.ExpandMacro(ref code, ref operand, methodDef.Body);
                var byteCode = new ByteCode
                {
                    Offset = inst.Offset,
                    EndOffset = inst.Next != null ? inst.Next.Offset : methodDef.Body.CodeSize,
                    Code = code,
                    Operand = operand,
                    PopCount = inst.GetPopDelta(methodDef),
                    PushCount = inst.GetPushDelta(),
                    SequencePoint = SequencePointWrapper.Wrap(inst.SequencePoint)
                };
                if (prefixes != null)
                {
                    instrToByteCode[prefixes[0]] = byteCode;
                    byteCode.Offset = prefixes[0].Offset;
                    byteCode.Prefixes = prefixes.ToArray();
                    prefixes = null;
                }
                else
                {
                    instrToByteCode[inst] = byteCode;
                }
                body.Add(byteCode);
            }
            for (int i = 0; i < body.Count - 1; i++)
            {
                body[i].Next = body[i + 1];
            }

            var agenda = new Stack<ByteCode>();
            var varCount = methodDef.Body.Variables.Count;

            var exceptionHandlerStarts = new HashSet<ByteCode>(methodDef.Body.ExceptionHandlers.Select(eh => instrToByteCode[eh.HandlerStart]));

            // Add known states
            if (methodDef.Body.HasExceptionHandlers)
            {
                foreach (var ex in methodDef.Body.ExceptionHandlers)
                {
                    var handlerStart = instrToByteCode[ex.HandlerStart];
                    handlerStart.StackBefore = new StackSlot[0];
                    handlerStart.VariablesBefore = VariableSlot.MakeUknownState(varCount);
                    if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter)
                    {
                        // Catch and Filter handlers start with the exeption on the stack
                        var ldexception = new ByteCode()
                        {
                            Code = AstCode.Ldexception,
                            Operand = ex.CatchType,
                            PopCount = 0,
                            PushCount = 1
                        };
                        ldexceptions[ex] = ldexception;
                        handlerStart.StackBefore = new[] { new StackSlot(new[] { ldexception }, null) };
                    }
                    agenda.Push(handlerStart);

                    if (ex.HandlerType == ExceptionHandlerType.Filter)
                    {
                        var filterStart = instrToByteCode[ex.FilterStart];
                        var ldexception = new ByteCode
                        {
                            Code = AstCode.Ldexception,
                            Operand = ex.CatchType,
                            PopCount = 0,
                            PushCount = 1
                        };
                        // TODO: ldexceptions[ex] = ldexception;
                        filterStart.StackBefore = new[] { new StackSlot(new[] { ldexception }, null) };
                        filterStart.VariablesBefore = VariableSlot.MakeUknownState(varCount);
                        agenda.Push(filterStart);
                    }
                }
            }

            body[0].StackBefore = new StackSlot[0];
            body[0].VariablesBefore = VariableSlot.MakeUknownState(varCount);
            agenda.Push(body[0]);

            // Process agenda
            while (agenda.Count > 0)
            {
                var byteCode = agenda.Pop();

                // Calculate new stack
                var newStack = StackSlot.ModifyStack(byteCode.StackBefore, byteCode.PopCount ?? byteCode.StackBefore.Length, byteCode.PushCount, byteCode);

                // Calculate new variable state
                var newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore);
                if (byteCode.IsVariableDefinition)
                {
                    newVariableState[((VariableReference)byteCode.Operand).Index] = new VariableSlot(new[] { byteCode }, false);
                }

                // After the leave, finally block might have touched the variables
                if (byteCode.Code == AstCode.Leave)
                {
                    newVariableState = VariableSlot.MakeUknownState(varCount);
                }

                // Find all successors
                var branchTargets = new List<ByteCode>();
                if (!byteCode.Code.IsUnconditionalControlFlow())
                {
                    if (exceptionHandlerStarts.Contains(byteCode.Next))
                    {
                        // Do not fall though down to exception handler
                        // It is invalid IL as per ECMA-335 §12.4.2.8.1, but some obfuscators produce it
                    }
                    else
                    {
                        branchTargets.Add(byteCode.Next);
                    }
                }
                if (byteCode.Operand is Instruction[])
                {
                    foreach (var inst in (Instruction[])byteCode.Operand)
                    {
                        var target = instrToByteCode[inst];
                        branchTargets.Add(target);
                        // The target of a branch must have label
                        if (target.Label == null)
                        {
                            target.Label = new AstLabel(target.SequencePoint, target.Name);
                        }
                    }
                }
                else if (byteCode.Operand is Instruction)
                {
                    var target = instrToByteCode[(Instruction)byteCode.Operand];
                    branchTargets.Add(target);
                    // The target of a branch must have label
                    if (target.Label == null)
                    {
                        target.Label = new AstLabel(target.SequencePoint, target.Name);
                    }
                }

                // Apply the state to successors
                foreach (var branchTarget in branchTargets)
                {
                    if (branchTarget.StackBefore == null && branchTarget.VariablesBefore == null)
                    {
                        if (branchTargets.Count == 1)
                        {
                            branchTarget.StackBefore = newStack;
                            branchTarget.VariablesBefore = newVariableState;
                        }
                        else
                        {
                            // Do not share data for several bytecodes
                            branchTarget.StackBefore = StackSlot.ModifyStack(newStack, 0, 0, null);
                            branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState);
                        }
                        agenda.Push(branchTarget);
                    }
                    else
                    {
                        if (branchTarget.StackBefore.Length != newStack.Length)
                        {
                            throw new Exception("Inconsistent stack size at " + byteCode.Name);
                        }

                        // Be careful not to change our new data - it might be reused for several branch targets.
                        // In general, be careful that two bytecodes never share data structures.

                        bool modified = false;

                        // Merge stacks - modify the target
                        for (int i = 0; i < newStack.Length; i++)
                        {
                            var oldDefs = branchTarget.StackBefore[i].Definitions;
                            var newDefs = oldDefs.Union(newStack[i].Definitions);
                            if (newDefs.Length > oldDefs.Length)
                            {
                                branchTarget.StackBefore[i] = new StackSlot(newDefs, null);
                                modified = true;
                            }
                        }

                        // Merge variables - modify the target
                        for (int i = 0; i < newVariableState.Length; i++)
                        {
                            var oldSlot = branchTarget.VariablesBefore[i];
                            var newSlot = newVariableState[i];
                            if (!oldSlot.UnknownDefinition)
                            {
                                if (newSlot.UnknownDefinition)
                                {
                                    branchTarget.VariablesBefore[i] = newSlot;
                                    modified = true;
                                }
                                else
                                {
                                    ByteCode[] oldDefs = oldSlot.Definitions;
                                    ByteCode[] newDefs = CollectionExtensions.Union(oldDefs, newSlot.Definitions);
                                    if (newDefs.Length > oldDefs.Length)
                                    {
                                        branchTarget.VariablesBefore[i] = new VariableSlot(newDefs, false);
                                        modified = true;
                                    }
                                }
                            }
                        }

                        if (modified)
                        {
                            agenda.Push(branchTarget);
                        }
                    }
                }
            }

            // Occasionally the compilers or obfuscators generate unreachable code (which might be intentonally invalid)
            // I belive it is safe to just remove it
            body.RemoveAll(b => b.StackBefore == null);

            // Genertate temporary variables to replace stack
            foreach (var byteCode in body)
            {
                int argIdx = 0;
                int popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
                for (int i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++)
                {
                    var tmpVar = new AstGeneratedVariable(string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), null);
                    byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].Definitions, tmpVar);
                    foreach (ByteCode pushedBy in byteCode.StackBefore[i].Definitions)
                    {
                        if (pushedBy.StoreTo == null)
                        {
                            pushedBy.StoreTo = new List<AstVariable>(1);
                        }
                        pushedBy.StoreTo.Add(tmpVar);
                    }
                    argIdx++;
                }
            }

            // Try to use single temporary variable insted of several if possilbe (especially useful for dup)
            // This has to be done after all temporary variables are assigned so we know about all loads
            foreach (var byteCode in body)
            {
                if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1)
                {
                    var locVars = byteCode.StoreTo;
                    // For each of the variables, find the location where it is loaded - there should be preciesly one
                    var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList();
                    // We now know that all the variables have a single load,
                    // Let's make sure that they have also a single store - us
                    if (loadedBy.All(slot => slot.Definitions.Length == 1 && slot.Definitions[0] == byteCode))
                    {
                        // Great - we can reduce everything into single variable
                        var tmpVar = new AstGeneratedVariable(string.Format("expr_{0:X2}", byteCode.Offset), locVars.Select(x => x.OriginalName).FirstOrDefault());
                        byteCode.StoreTo = new List<AstVariable>() { tmpVar };
                        foreach (var bc in body)
                        {
                            for (int i = 0; i < bc.StackBefore.Length; i++)
                            {
                                // Is it one of the variable to be merged?
                                if (locVars.Contains(bc.StackBefore[i].LoadFrom))
                                {
                                    // Replace with the new temp variable
                                    bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].Definitions, tmpVar);
                                }
                            }
                        }
                    }
                }
            }

            // Split and convert the normal local variables
            ConvertLocalVariables(body);

            // Convert branch targets to labels and references to xreferences
            foreach (var byteCode in body)
            {
                if (byteCode.Operand is Instruction[])
                {
                    byteCode.Operand = (from target in (Instruction[])byteCode.Operand select instrToByteCode[target].Label).ToArray();
                }
                else if (byteCode.Operand is Instruction)
                {
                    byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label;
                }
                else if (byteCode.Operand is FieldReference)
                {
                    byteCode.Operand = XBuilder.AsFieldReference(module, (FieldReference)byteCode.Operand);
                }
                else if (byteCode.Operand is MethodReference)
                {
                    byteCode.Operand = XBuilder.AsMethodReference(module, (MethodReference)byteCode.Operand);
                }
                else if (byteCode.Operand is TypeReference)
                {
                    byteCode.Operand = XBuilder.AsTypeReference(module, (TypeReference)byteCode.Operand);
                }
            }

            // Convert parameters to ILVariables
            ConvertParameters(body);

            return body;
        }
示例#8
0
文件: Opcode.cs 项目: CalebJ2/KOS
 /// <summary>
 /// Given a string value of Code, find the Opcode Type that uses that as its CodeName.
 /// </summary>
 /// <param name="code">ByteCode to look up</param>
 /// <returns>Type, one of the subclasses of Opcode, or PseudoNull if there was no match</returns>
 public static Type TypeFromCode(ByteCode code)
 {
     Type returnValue;
     if (! mapCodeToType.TryGetValue(code, out returnValue))
     {
         returnValue = typeof(PseudoNull); // flag telling the caller "not found".
     }
     return returnValue;
 }
示例#9
0
			internal void SetTermNop(ushort pc)
			{
				// TODO what happens if we already have exactly the maximum number of instructions?
				this.pc = pc;
				this.opcode = ByteCode.__nop;
			}
 public ByteCodeBlock(ByteCode first, ByteCode last)
 {
     this.first = first;
     this.last = last;
 }
示例#11
0
            public readonly AstVariable LoadFrom;     // Variable used for storage of the value

            public StackSlot(ByteCode[] definitions, AstVariable loadFrom)
            {
                Definitions = definitions;
                LoadFrom = loadFrom;
            }
示例#12
0
 /// <summary>
 /// Gets all variables that are defined in the given range of bytes (start - (exclusive) end).
 /// </summary>
 private static void GetDefinedVariables(ByteCode start, ByteCode end, HashSet<LocalVariableReference> defs)
 {
     while ((start != null) && (start != end))
     {
         if (start.IsVariableDefinition)
         {
             var varDef = start.Operand as LocalVariableReference;
             if (varDef != null) defs.Add(varDef);
         }
         start = start.Next;
     }
 }
示例#13
0
        /// <summary>
        /// Update the variable state of handlers for the exception handlers that start at the given branch target.
        /// </summary>
        private void UpdateTryStartBranchTarget(ByteCode branchTarget, IEnumerable<ExceptionHandler> exceptionHandlers, Dictionary<Instruction, ByteCode> instrToByteCode, VariableSlot[] newVariableState, Stack<ByteCode> agenda)
        {
            // The branch target is the start of a try block.
            // Collection all exception handler that share the same handler as this try block.
            var ehs = new HashSet<ExceptionHandler>(exceptionHandlers);
            foreach (var eh in validExceptionHandlers.Where(x => ehs.Any(y => y.HandlerPc == x.HandlerPc)))
            {
                ehs.Add(eh);
            }

            // Collection the variables defined in all try block's of the collected exception handlers.
            var defs = new HashSet<LocalVariableReference>();
            foreach (var eh in ehs)
            {
                GetDefinedVariables(instrToByteCode[eh.Start], instrToByteCode[eh.End], defs);
            }

            // Merge variables of handlers - modify the target
            foreach (var handler in ehs.Select(x => instrToByteCode[x.Handler]))
            {
                var modified = false;
                for (var i = 0; i < newVariableState.Length; i++)
                {
                    // Update variables unless it is defined
                    if (defs.Any(x => x.Index == i))
                        continue;

                    var oldSlot = handler.VariablesBefore[i];
                    var newSlot = newVariableState[i];

                    if (newSlot.UnknownDefinition)
                    {
                        if (!oldSlot.UnknownDefinition)
                        {
                            handler.VariablesBefore[i] = newSlot;
                            modified = true;
                        }
                    }
                    else
                    {
                        var oldDefs = oldSlot.Definitions;
                        var newDefs = oldDefs.Union(newSlot.Definitions);
                        if (newDefs.Length > oldDefs.Length)
                        {
                            handler.VariablesBefore[i] = new VariableSlot(newDefs, false);
                            modified = true;
                        }
                    }
                }
                if (modified)
                {
                    agenda.Push(handler);
                }
            }
        }
示例#14
0
        /// <summary>
        /// Update the state of the given branch targets (of the given bytecode).
        /// Add modified branch targets to the given agenda.
        /// </summary>
        private static void UpdateBranchTarget(ByteCode byteCode, ByteCode branchTarget, bool canShareNewState, StackSlot[] newStack, VariableSlot[] newVariableState, Stack<ByteCode> agenda)
        {
            if ((branchTarget.StackBefore == null) && (branchTarget.VariablesBefore == null))
            {
                // Branch target has not been processed at all
                if (canShareNewState)
                {
                    branchTarget.StackBefore = newStack;
                    branchTarget.VariablesBefore = newVariableState;
                }
                else
                {
                    // Do not share data for several bytecodes
                    branchTarget.StackBefore = StackSlot.ModifyStack(newStack, 0, 0, null);
                    branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState);
                }
                agenda.Push(branchTarget);
                return;
            }

            // If we get here, the branch target has been processed before.
            // See the the stack size is the same and merge the state where needed.
            if ((branchTarget.StackBefore == null) || (branchTarget.StackBefore.Length != newStack.Length))
            {
                throw new Exception("Inconsistent stack size at " + byteCode.Name);
            }

            // Be careful not to change our new data - it might be reused for several branch targets.
            // In general, be careful that two bytecodes never share data structures.
            var modified = false;

            // Merge stacks - modify the target
            for (var i = 0; i < newStack.Length; i++)
            {
                var oldDefs = branchTarget.StackBefore[i].Definitions;
                var newDefs = oldDefs.Union(newStack[i].Definitions);
                if (newDefs.Length > oldDefs.Length)
                {
                    branchTarget.StackBefore[i] = new StackSlot(newDefs, null);
                    modified = true;
                }
            }

            // Merge variables - modify the target
            for (var i = 0; i < newVariableState.Length; i++)
            {
                var oldSlot = branchTarget.VariablesBefore[i];
                var newSlot = newVariableState[i];
                if (oldSlot.UnknownDefinition)
                    continue;

                if (newSlot.UnknownDefinition)
                {
                    branchTarget.VariablesBefore[i] = newSlot;
                    modified = true;
                }
                else
                {
                    var oldDefs = oldSlot.Definitions;
                    var newDefs = oldDefs.Union(newSlot.Definitions);
                    if (newDefs.Length > oldDefs.Length)
                    {
                        branchTarget.VariablesBefore[i] = new VariableSlot(newDefs, false);
                        modified = true;
                    }
                }
            }

            if (modified)
            {
                agenda.Push(branchTarget);
            }
        }
示例#15
0
文件: Block.cs 项目: ajlopez/AjTalk
 public void CompileJumpByteCode(ByteCode b, short jump)
 {
     this.CompileByte((byte)b);
     this.CompileByte((byte)(jump >> 8));
     this.CompileByte((byte)(jump & 0xff));
 }
示例#16
0
			internal void Read(ushort pc, BigEndianBinaryReader br)
			{
				this.pc = pc;
				ByteCode bc = (ByteCode) br.ReadByte();
				switch (ByteCodeMetaData.GetMode(bc))
				{
					case ByteCodeMode.Simple:
						break;
					case ByteCodeMode.Constant_1:
					case ByteCodeMode.Local_1:
						arg1 = br.ReadByte();
						break;
					case ByteCodeMode.Constant_2:
						arg1 = br.ReadUInt16();
						break;
					case ByteCodeMode.Branch_2:
						arg1 = br.ReadInt16();
						break;
					case ByteCodeMode.Branch_4:
						arg1 = br.ReadInt32();
						break;
					case ByteCodeMode.Constant_2_1_1:
						arg1 = br.ReadUInt16();
						arg2 = br.ReadByte();
						if (br.ReadByte() != 0)
						{
							throw new ClassFormatError("invokeinterface filler must be zero");
						}
						break;
					case ByteCodeMode.Immediate_1:
						arg1 = br.ReadSByte();
						break;
					case ByteCodeMode.Immediate_2:
						arg1 = br.ReadInt16();
						break;
					case ByteCodeMode.Local_1_Immediate_1:
						arg1 = br.ReadByte();
						arg2 = br.ReadSByte();
						break;
					case ByteCodeMode.Constant_2_Immediate_1:
						arg1 = br.ReadUInt16();
						arg2 = br.ReadSByte();
						break;
					case ByteCodeMode.Tableswitch:
						{
							// skip the padding
							uint p = pc + 1u;
							uint align = ((p + 3) & 0x7ffffffc) - p;
							br.Skip(align);
							int default_offset = br.ReadInt32();
							this.arg1 = default_offset;
							int low = br.ReadInt32();
							int high = br.ReadInt32();
							if (low > high || high > 16384L + low)
							{
								throw new ClassFormatError("Incorrect tableswitch");
							}
							SwitchEntry[] entries = new SwitchEntry[high - low + 1];
							for (int i = low; i <= high; i++)
							{
								entries[i - low].value = i;
								entries[i - low].target_offset = br.ReadInt32();
							}
							this.switch_entries = entries;
							break;
						}
					case ByteCodeMode.Lookupswitch:
						{
							// skip the padding
							uint p = pc + 1u;
							uint align = ((p + 3) & 0x7ffffffc) - p;
							br.Skip(align);
							int default_offset = br.ReadInt32();
							this.arg1 = default_offset;
							int count = br.ReadInt32();
							if (count < 0 || count > 16384)
							{
								throw new ClassFormatError("Incorrect lookupswitch");
							}
							SwitchEntry[] entries = new SwitchEntry[count];
							for (int i = 0; i < count; i++)
							{
								entries[i].value = br.ReadInt32();
								entries[i].target_offset = br.ReadInt32();
							}
							this.switch_entries = entries;
							break;
						}
					case ByteCodeMode.WidePrefix:
						bc = (ByteCode) br.ReadByte();
						// NOTE the PC of a wide instruction is actually the PC of the
						// wide prefix, not the following instruction (vmspec 4.9.2)
						switch (ByteCodeMetaData.GetWideMode(bc))
						{
							case ByteCodeModeWide.Local_2:
								arg1 = br.ReadUInt16();
								break;
							case ByteCodeModeWide.Local_2_Immediate_2:
								arg1 = br.ReadUInt16();
								arg2 = br.ReadInt16();
								break;
							default:
								throw new ClassFormatError("Invalid wide prefix on opcode: {0}", bc);
						}
						break;
					default:
						throw new ClassFormatError("Invalid opcode: {0}", bc);
				}
				this.opcode = bc;
				this.normopcode = ByteCodeMetaData.GetNormalizedByteCode(bc);
				arg1 = ByteCodeMetaData.GetArg(opcode, arg1);
			}
示例#17
0
文件: Block.cs 项目: ajlopez/AjTalk
 public void CompileJumpByteCodeAt(ByteCode b, short jump, int position)
 {
     this.bytecodes[position] = (byte)b;
     this.bytecodes[position + 1] = (byte)(jump >> 8);
     this.bytecodes[position + 2] = (byte)(jump & 0xff);
 }
 /// <summary>
 /// Create a single bytecode.
 /// </summary>
 private ByteCode CreateByteCode(Instruction inst, AstCode code, object operand, int popCount, int pushCount, Category category, XTypeReference type = null)
 {
     var next = codeAttr.GetNext(inst);
     var byteCode = new ByteCode
     {
         Category = category,
         Offset = inst.Offset,
         EndOffset = (next != null) ? next.Offset : codeAttr.Code.Length,
         Code = code,
         Operand = operand,
         PopCount = popCount,
         PushCount = pushCount,
         SourceLocation = new SourceLocation(codeAttr, inst),
         Type = type
     };
     return byteCode;
 }
示例#19
0
        static bool IsDeterministicLdloca(ByteCode b)
        {
            var v = b.Operand;
            b = b.Next;
            if (b.Code == AstCode.Initobj) return true;

            // instance method calls on value types use the variable ref deterministically
            int stack = 1;
            while (true)
            {
                if (b.PopCount == null) return false;
                stack -= b.PopCount.GetValueOrDefault();
                if (stack == 0) break;
                if (stack < 0) return false;
                if (b.Code.IsConditionalControlFlow() || b.Code.IsUnconditionalControlFlow()) return false;
                switch (b.Code)
                {
                    case AstCode.Ldloc:
                    case AstCode.Ldloca:
                    case AstCode.Stloc:
                        if (b.Operand == v) return false;
                        break;
                }
                stack += b.PushCount;
                b = b.Next;
                if (b == null) return false;
            }
            if (b.Code == AstCode.Ldfld || b.Code == AstCode.Stfld)
                return true;
            return (b.Code == AstCode.Call || b.Code == AstCode.Callvirt) && ((MethodReference)b.Operand).HasThis;
        }
示例#20
0
        void Emit(Node node, ByteCode code, object operandA = null, object operandB = null)
        {
            var instruction = new Instruction();
            instruction.operation = code;
            instruction.operandA = operandA;
            instruction.operandB = operandB;

            node.instructions.Add (instruction);

            if (code == ByteCode.Label) {
                // Add this label to the label table
                node.labels.Add ((string)instruction.operandA, node.instructions.Count - 1);
            }
        }
示例#21
0
 public VariableSlot(ByteCode[] definitions, bool unknownDefinition)
 {
     Definitions = definitions;
     UnknownDefinition = unknownDefinition;
 }
示例#22
0
 internal static bool CanThrowException(ByteCode bc)
 {
     return (data[(int) bc].flags & ByteCodeFlags.CannotThrow) == 0;
 }
示例#23
0
 private ByteCodeMetaData(ByteCode bc, NormalizedByteCode normbc, int arg, ByteCodeMode reg, ByteCodeModeWide wide, bool cannotThrow)
 {
     this.reg = reg;
     this.wide = wide;
     this.normbc = normbc;
     this.arg = arg;
     this.flags = ByteCodeFlags.FixedArg;
     if(cannotThrow)
     {
         this.flags |= ByteCodeFlags.CannotThrow;
     }
     data[(int)bc] = this;
 }
示例#24
0
文件: Block.cs 项目: ajlopez/AjTalk
 public void CompileByteCode(ByteCode b)
 {
     this.CompileByte((byte)b);
 }
示例#25
0
 internal static ByteCodeMode GetMode(ByteCode bc)
 {
     return data[(int)bc].reg;
 }
示例#26
0
文件: Block.cs 项目: ajlopez/AjTalk
 public void CompileByteCode(ByteCode b, byte arg1, byte arg2)
 {
     this.CompileByteCode(b);
     this.CompileByte(arg1);
     this.CompileByte(arg2);
 }
示例#27
0
 internal static ByteCodeModeWide GetWideMode(ByteCode bc)
 {
     return data[(int)bc].wide;
 }
示例#28
0
文件: Block.cs 项目: ajlopez/AjTalk
 public void CompileByteCodeAt(ByteCode b, int position)
 {
     this.bytecodes[position] = (byte)b;
 }
示例#29
0
 public StackSlot(ByteCode pushedBy)
 {
     this.PushedBy = new List<ByteCode>(1);
     this.PushedBy.Add(pushedBy);
 }
示例#30
0
        /// <summary>
        /// Analyse the instructions in the method code and convert them to a ByteCode list.
        /// </summary>
        private List<ByteCode> StackAnalysis()
        {
            // Map from instruction to bytecode.
            var instrToByteCode = new Dictionary<Instruction, ByteCode>();

            // Create temporary structure for the stack analysis
            var body = new List<ByteCode>(codeAttr.Code.Length);
            foreach (var inst in codeAttr.Instructions)
            {
                var first = true;
                foreach (var byteCode in Create(inst, module))
                {
                    if (first)
                    {
                        instrToByteCode[inst] = byteCode;
                        first = false;
                    }
                    body.Add(byteCode);
                }
            }
            // Connect bytecodes to the next
            for (var i = 0; i < body.Count - 1; i++)
            {
                body[i].Next = body[i + 1];
            }

            var agenda = new Stack<ByteCode>();
            var localVarsCount = codeAttr.MaxLocals; // methodDef.GetParametersLocalVariableSlots();

            // All bytecodes that are the start of an exception handler.
            var exceptionHandlerStarts = new HashSet<ByteCode>(validExceptionHandlers.Select(eh => instrToByteCode[eh.Handler]));
            var exceptionTryStarts = new DefaultDictionary<ByteCode, List<ExceptionHandler>>(x => new List<ExceptionHandler>());
            foreach (var eh in validExceptionHandlers)
            {
                exceptionTryStarts[instrToByteCode[eh.Start]].Add(eh);
            }

            // Add known states
            var ldExceptionByHandlerPc = new Dictionary<int, ByteCode>();
            foreach (var ex in validExceptionHandlers)
            {
                ByteCode ldexception;
                if (ldExceptionByHandlerPc.TryGetValue(ex.HandlerPc, out ldexception))
                {
                    // Re-use ldexception (that we've created for the same handler PC for another exception handler before)
                }
                else
                {
                    // No handler at handlerPc processed before, do that now
                    var handlerStart = instrToByteCode[ex.Handler];
                    handlerStart.StackBefore = new StackSlot[0];
                    handlerStart.VariablesBefore = VariableSlot.MakeUnknownState(localVarsCount);
                    {
                        // Catch handlers start with the exeption on the stack
                        ldexception = new ByteCode {
                            Code = AstCode.Ldexception,
                            Operand = ex.CatchType,
                            PopCount = 0,
                            PushCount = 1,
                            Offset = handlerStart.Offset,
                            Next = handlerStart,
                            StackBefore = new StackSlot[0],
                            VariablesBefore = handlerStart.VariablesBefore
                        };
                        handlerStart.StackBefore = new[] { new StackSlot(new[] { ldexception }, null) };
                    }
                    ldExceptionByHandlerPc[ex.HandlerPc] = ldexception;
                    agenda.Push(handlerStart);
                }
                // Store ldexception by exception handler
                ldexceptions[ex] = ldexception;
            }

            // At the start of the method the stack is empty and all local variables have unknown state
            body[0].StackBefore = new StackSlot[0];
            body[0].VariablesBefore = VariableSlot.MakeUnknownState(localVarsCount);
            agenda.Push(body[0]);

            // Process agenda
            while (agenda.Count > 0)
            {
                var byteCode = agenda.Pop();

                // Calculate new stack
                var newStack = byteCode.CreateNewStack();

                // Calculate new variable state
                var newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore);
                if (byteCode.IsVariableDefinition)
                {
                    newVariableState[((LocalVariableReference)byteCode.Operand).Index] = new VariableSlot(new[] { byteCode }, false);
                }

                // After the leave, finally block might have touched the variables
                if (byteCode.Code == AstCode.Leave)
                {
                    newVariableState = VariableSlot.MakeUnknownState(localVarsCount);
                }

                // Find all successors
                var branchTargets = FindBranchTargets(byteCode, instrToByteCode, exceptionHandlerStarts);

                // Apply the state to successors
                foreach (var branchTarget in branchTargets)
                {
                    UpdateBranchTarget(byteCode, branchTarget, (branchTargets.Count == 1), newStack, newVariableState, agenda);
                }

                // Apply state to handlers when a branch target is the start of an exception handler
                foreach (var branchTarget in branchTargets.Where(exceptionTryStarts.ContainsKey))
                {
                    // The branch target is the start of a try block.
                    UpdateTryStartBranchTarget(branchTarget, exceptionTryStarts[branchTarget], instrToByteCode, newVariableState, agenda);
                }
            }

            // Occasionally the compilers or obfuscators generate unreachable code (which might be intentonally invalid)
            // I believe it is safe to just remove it
            body.RemoveAll(b => b.StackBefore == null);

            // Generate temporary variables to replace stack
            foreach (var byteCode in body)
            {
                var argIdx = 0;
                var popCount = byteCode.PopCount ?? byteCode.StackBefore.Length;
                for (var i = byteCode.StackBefore.Length - popCount; i < byteCode.StackBefore.Length; i++)
                {
                    var tmpVar = new AstGeneratedVariable(string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), null);
                    byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].Definitions, tmpVar);
                    foreach (var pushedBy in byteCode.StackBefore[i].Definitions)
                    {
                        if (pushedBy.StoreTo == null)
                        {
                            pushedBy.StoreTo = new List<AstVariable>(1);
                        }
                        pushedBy.StoreTo.Add(tmpVar);
                    }
                    argIdx++;
                }
            }

            // Try to use single temporary variable insted of several if possible (especially useful for dup)
            // This has to be done after all temporary variables are assigned so we know about all loads
            foreach (var byteCode in body)
            {
                if ((byteCode.StoreTo == null) || (byteCode.StoreTo.Count <= 1)) 
                    continue;

                var locVars = byteCode.StoreTo;
                // For each of the variables, find the location where it is loaded - there should be preciesly one
                var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList();
                // We now know that all the variables have a single load,
                // Let's make sure that they have also a single store - us
                if (loadedBy.All(slot => (slot.Definitions.Length == 1) && (slot.Definitions[0] == byteCode)))
                {
                    // Great - we can reduce everything into single variable
                    var tmpVar = new AstGeneratedVariable(string.Format("expr_{0:X2}", byteCode.Offset), locVars.Select(x => x.OriginalName).FirstOrDefault());
                    byteCode.StoreTo = new List<AstVariable> { tmpVar };
                    foreach (var bc in body)
                    {
                        for (var i = 0; i < bc.StackBefore.Length; i++)
                        {
                            // Is it one of the variable to be merged?
                            if (locVars.Contains(bc.StackBefore[i].LoadFrom))
                            {
                                // Replace with the new temp variable
                                bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].Definitions, tmpVar);
                            }
                        }
                    }
                }
            }

            // Split and convert the normal local variables
            ConvertLocalVariables(body);

            // Convert branch targets to labels
            foreach (var byteCode in body)
            {
                if (byteCode.Operand is Instruction[])
                {
                    byteCode.Operand = (from target in (Instruction[])byteCode.Operand select instrToByteCode[target].Label(true)).ToArray();
                }
                else if (byteCode.Operand is Instruction)
                {
                    byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label(true);
                }
                else if (byteCode.Operand is LookupSwitchData)
                {
                    var data = (LookupSwitchData) byteCode.Operand;
                    byteCode.Operand = data.Pairs.Select(x => new AstLabelKeyPair(instrToByteCode[x.Target].Label(true), x.Match)).ToArray();
                }
            }

            // Convert parameters to ILVariables
            ConvertParameters(body);

            // Replace temporary opcodes
            foreach (var byteCode in body)
            {
                switch (byteCode.Code)
                {
                    case AstCode.Dup_x1:
                    case AstCode.Dup_x2:
                    case AstCode.Dup2:
                    case AstCode.Dup2_x1:
                    case AstCode.Dup2_x2:
                    case AstCode.Swap:
                        byteCode.Code = AstCode.Dup;
                        break;
                    case AstCode.Pop2:
                        byteCode.Code = AstCode.Pop;
                        break;
                }
            }

            return body;
        }