示例#1
0
        public SubroutineGraph(List <Instruction> instructions, uint bytecodeBaseOffset) : this()
        {
            Dictionary <long, Node> nodeLocations = new Dictionary <long, Node>();
            //Dictionary<Node, long> nodeStarts = new Dictionary<Node, long>();
            Dictionary <Node, long> nodeLengths = new Dictionary <Node, long>();

            // For making jumps to nodes that might now exist yet
            List <Tuple <Node, long, Jump.JumpType> > delayedJumps = new List <Tuple <Node, long, Jump.JumpType> >();

            OSINode currentNode = new OSINode(bytecodeBaseOffset, new List <Instruction>());

            StartNode.CreateJumpTo(currentNode, Jump.JumpType.Always);


            uint offset = bytecodeBaseOffset;

            nodeLocations.Add(offset, currentNode);
            //nodeStarts.Add(currentNode, offset);
            Nodes.Add(currentNode);

            foreach (Instruction ins in instructions)
            {
                if (ins is BCLInstruction bcl)
                {
                    // Check whether we need to start a new block as a jump target
                    if (delayedJumps.Any((dj) => dj.Item2 == offset) && !nodeLocations.ContainsKey(offset))
                    {
                        OSINode nextNode = new OSINode(offset, new List <Instruction>());
                        Nodes.Add(nextNode);
                        nodeLocations.Add(offset, nextNode);
                        //nodeStarts.Add(nextNode, offset);
                        currentNode.CreateJumpTo(nextNode, Jump.JumpType.Always);
                        nodeLengths.Add(currentNode, offset + ins.Size - currentNode.StartOffset);

                        currentNode = nextNode;
                    }

                    // Check whether we need to start a new block after this instruction as a jump source
                    // HUUUUGE HACK: Don't make jumps to <End> for early returns, as this completely breaks control flow analysis.
                    if (bcl.Opcode == BCLOpcode.Return && ins == instructions[instructions.Count - 1])
                    {
                        currentNode.Instructions.Add(bcl);
                        nodeLengths.Add(currentNode, offset + ins.Size - currentNode.StartOffset);

                        currentNode.CreateJumpTo(EndNode, Jump.JumpType.Always);

                        currentNode = new OSINode(offset + ins.Size, new List <Instruction>());
                        Nodes.Add(currentNode);
                        nodeLocations.Add(offset + ins.Size, currentNode);
                        //nodeStarts.Add(currentNode, offset + ins.Size);
                    }
                    else if (bcl.Opcode == BCLOpcode.CompareAndBranchIfFalse)
                    {
                        //currentNode.Instructions.Add(bcl);

                        short delta = bcl.Arguments[0].GetValue <short>();

                        /*if (delta < 0)
                         * {
                         *  throw new NotImplementedException();
                         * }
                         * else */if (delta == 0)
                        {
                            // An empty node...
                            OSINode trueNode = new OSINode(offset + ins.Size, new List <Instruction>());
                            currentNode.CreateJumpTo(trueNode, Jump.JumpType.ConditionalTrue);
                            nodeLengths.Add(trueNode, 0);
                            Nodes.Add(trueNode);
                            // HACK: Don't add trueNode's location - it's empty, so nobody should want it. Let the false node own the location.

                            OSINode falseNode = new OSINode(offset + ins.Size, new List <Instruction>());
                            currentNode.CreateJumpTo(falseNode, Jump.JumpType.ConditionalFalse);
                            Nodes.Add(falseNode);
                            nodeLocations.Add(offset + ins.Size, falseNode);
                            //nodeStarts.Add(falseNode, offset + ins.Size);
                            nodeLengths.Add(currentNode, offset + ins.Size - currentNode.StartOffset);

                            trueNode.CreateJumpTo(falseNode, Jump.JumpType.Always);

                            currentNode = falseNode;
                        }
                        else
                        {
                            delayedJumps.Add(new Tuple <Node, long, Jump.JumpType>(currentNode, offset + delta + ins.Size, Jump.JumpType.ConditionalFalse));

                            OSINode trueNode = new OSINode(offset + ins.Size, new List <Instruction>());
                            nodeLengths.Add(currentNode, offset + ins.Size - currentNode.StartOffset);
                            Nodes.Add(trueNode);
                            nodeLocations.Add(offset + ins.Size, trueNode);
                            //nodeStarts.Add(trueNode, offset + ins.Size);
                            currentNode.CreateJumpTo(trueNode, Jump.JumpType.ConditionalTrue);

                            currentNode = trueNode;
                        }
                    }
                    else if (bcl.Opcode == BCLOpcode.BranchAlways)
                    {
                        //currentNode.Instructions.Add(bcl);

                        short delta = bcl.Arguments[0].GetValue <short>();

                        /*if (delta < 0)
                         * {
                         *  //throw new NotImplementedException();
                         *  nodeLengths.Add(currentNode, offset + ins.Size - nodeStarts[currentNode]);
                         *
                         *  // Continue reading instructions into a new node
                         *  currentNode = new OSINode(new List<Instruction>());
                         *  Nodes.Add(currentNode);
                         *  nodeLocations.Add(offset + ins.Size, currentNode);
                         *  nodeStarts.Add(currentNode, offset + ins.Size);
                         * }
                         * else */if (delta == 0)
                        {
                            //throw new NotImplementedException();
                            // Simply ignore the jump!
                        }
                        else
                        {
                            delayedJumps.Add(new Tuple <Node, long, Jump.JumpType>(currentNode, offset + delta + ins.Size, Jump.JumpType.Always));
                            nodeLengths.Add(currentNode, offset + ins.Size - currentNode.StartOffset);

                            // Continue reading instructions into a new node
                            currentNode = new OSINode(offset + ins.Size, new List <Instruction>());
                            Nodes.Add(currentNode);
                            nodeLocations.Add(offset + ins.Size, currentNode);
                            //nodeStarts.Add(currentNode, offset + ins.Size);
                        }
                    }
                    else
                    {
                        currentNode.Instructions.Add(bcl);
                    }
                }
                else
                {
                    currentNode.Instructions.Add(ins);
                }

                offset += ins.Size;
            }
            nodeLengths.Add(currentNode, offset - currentNode.StartOffset);

            foreach (Tuple <Node, long, Jump.JumpType> j in delayedJumps)
            {
                Node dest = null;
                if (nodeLocations.ContainsKey(j.Item2))
                {
                    // Jump to the beginning of the node! EZ!
                    dest = nodeLocations[j.Item2];
                }
                else
                {
                    // Find the containing node and split it
                    foreach (Node n in Nodes)
                    {
                        if (n is OSINode node)
                        {
                            if (j.Item2 >= node.StartOffset && j.Item2 < node.StartOffset + nodeLengths[n])
                            {
                                // Time to split j
                                long    targetLength = j.Item2 - node.StartOffset;
                                OSINode secondPart   = new OSINode(0, new List <Instruction>());
                                Nodes.Add(secondPart);

                                // Transfer instructions to secondPart
                                long l = nodeLengths[n];
                                while (l > targetLength)
                                {
                                    Instruction ins = node.Instructions[node.Instructions.Count - 1];
                                    node.Instructions.RemoveAt(node.Instructions.Count - 1);
                                    secondPart.Instructions.Insert(0, ins);
                                    l -= ins.Size;
                                }

                                // Transfer outjumps to secondPart
                                foreach (Jump outJump in n.OutJumps.Values)
                                {
                                    secondPart.CreateJumpTo(outJump.Destination, outJump.Type);
                                    outJump.Destination.InJumps.Remove(n);
                                }
                                n.OutJumps.Clear();
                                n.CreateJumpTo(secondPart, Jump.JumpType.Always);

                                nodeLengths.Add(secondPart, nodeLengths[n] - l);
                                nodeLengths[n] = l;
                                //nodeStarts.Add(secondPart, nodeStarts[n] + l);
                                secondPart.StartOffset = (uint)(node.StartOffset + l);

                                dest = secondPart;

                                break;
                            }
                        }
                    }
                }
                if (j.Item1.OutJumps.ContainsKey(dest))
                {
                    if (j.Item1.OutJumps[dest].Type != j.Item3)
                    {
                        throw new Exception("Cannot have two jumps of different types for the same jump pair");
                    }
                }
                else
                {
                    j.Item1.CreateJumpTo(dest, j.Item3);
                }
            }

            if (currentNode.InJumps.Count == 0 && currentNode.OutJumps.Count == 0)
            {
                Nodes.Remove(currentNode);
            }
        }