diff --git a/Cpp2IL.Core/Graphs/BasicBlock.cs b/Cpp2IL.Core/Graphs/BasicBlock.cs deleted file mode 100644 index d1ff4330..00000000 --- a/Cpp2IL.Core/Graphs/BasicBlock.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; - -namespace Cpp2IL.Core.Graphs; - -public struct BasicBlock -{ - public BlockType BlockType; - public List Predecessors; - public List Successors; - - - public BasicBlock(BlockType blockType) - { - BlockType = blockType; - Predecessors = new(); - Successors = new(); - } -} \ No newline at end of file diff --git a/Cpp2IL.Core/Graphs/Block.cs b/Cpp2IL.Core/Graphs/Block.cs new file mode 100644 index 00000000..9ec22211 --- /dev/null +++ b/Cpp2IL.Core/Graphs/Block.cs @@ -0,0 +1,83 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Cpp2IL.Core.ISIL; + +namespace Cpp2IL.Core.Graphs; + +public class Block +{ + public BlockType BlockType { get; set; } + public List Predecessors; + public List Successors; + + public List isilInstructions; + + public int ID { get; set; } + + public bool Dirty { get; set; } + public bool Visited = false; + + + + public override string ToString() { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.AppendLine("Type: " + BlockType); + stringBuilder.AppendLine(); + foreach(var instruction in isilInstructions) + { + stringBuilder.AppendLine(instruction.ToString()); + } + return stringBuilder.ToString(); + } + + public Block() + { + BlockType = BlockType.Unknown; + Predecessors = new(); + Successors = new(); + isilInstructions = new(); + ID = -1; + } + + public void AddInstruction(InstructionSetIndependentInstruction instruction) + { + isilInstructions.Add(instruction); + } + + public void CaculateBlockType() + { + // This enum is kind of redundant, can be possibly swapped for IsilFlowControl and no need for BlockType? + if (isilInstructions.Count > 0) { + var instruction = isilInstructions.Last(); + switch (instruction.FlowControl) + { + case IsilFlowControl.UnconditionalJump: + BlockType = BlockType.OneWay; + break; + case IsilFlowControl.ConditionalJump: + BlockType = BlockType.TwoWay; + break; + case IsilFlowControl.IndexedJump: + BlockType = BlockType.NWay; + break; + case IsilFlowControl.MethodCall: + BlockType = BlockType.Call; + break; + case IsilFlowControl.MethodReturn: + BlockType = BlockType.Return; + break; + case IsilFlowControl.Interrupt: + BlockType = BlockType.Interrupt; + break; + case IsilFlowControl.Continue: + BlockType = BlockType.Fall; + break; + default: + BlockType = BlockType.Unknown; + break; + } + + } + } +} diff --git a/Cpp2IL.Core/Graphs/BlockType.cs b/Cpp2IL.Core/Graphs/BlockType.cs index 06f22458..0a3bd4e9 100644 --- a/Cpp2IL.Core/Graphs/BlockType.cs +++ b/Cpp2IL.Core/Graphs/BlockType.cs @@ -1,11 +1,29 @@ -namespace Cpp2IL.Core.Graphs; +namespace Cpp2IL.Core.Graphs; public enum BlockType : byte { - OneWay, - TwoWay, - NWay, - Call, - Return, - Fall -} \ No newline at end of file + OneWay, // etc. Jumps to another block + TwoWay, // etc. Jumps conditionally to two blocks + NWay, // switch statement nonsense I think + Call, // Block finishes with call + Return, // Block finishes with return + // we fall into next block, for example block A has + // mov reg1, reg2 + // mov reg2, reg3 + // + // block b has + // mov reg3, reg4 + // mov reg2, reg4 + // and another block finishes with a jump to start instruction of block b meaning block a falls into b (bad explanation) + Fall, + + // Block type is not known yet + Unknown, + + // Exception or something raised + Interrupt, + + // Empty blocks that serve as entry and exit markers + Entry, + Exit, +} diff --git a/Cpp2IL.Core/Graphs/ISILControlFlowGraph.cs b/Cpp2IL.Core/Graphs/ISILControlFlowGraph.cs new file mode 100644 index 00000000..d2fe77b7 --- /dev/null +++ b/Cpp2IL.Core/Graphs/ISILControlFlowGraph.cs @@ -0,0 +1,317 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using Cpp2IL.Core.ISIL; + +namespace Cpp2IL.Core.Graphs +{ + public class ISILControlFlowGraph + { + public Block EntryBlock => entryBlock; + public Block ExitBlock => exitBlock; + public int Count => blockSet != null ? blockSet.Count : 0; + public Collection Blocks => blockSet; + + + private int idCounter = 0; + private Collection blockSet; + private Block exitBlock; + private Block entryBlock; + + public ISILControlFlowGraph() + { + entryBlock = new Block() { ID = idCounter++ }; + entryBlock.BlockType = BlockType.Entry; + exitBlock = new Block() { ID = idCounter++ }; + exitBlock.BlockType = BlockType.Exit; + blockSet = new Collection(); + blockSet.Add(entryBlock); + blockSet.Add(exitBlock); + } + + private bool TryGetTargetJumpInstructionIndex(InstructionSetIndependentInstruction instruction, out uint jumpInstructionIndex) + { + jumpInstructionIndex = 0; + try + { + jumpInstructionIndex = ((InstructionSetIndependentInstruction)instruction.Operands[0].Data).InstructionIndex; + return true; + } + catch { } + return false; + } + + + public void Build(List instructions) + { + if (instructions == null) + throw new ArgumentNullException(nameof(instructions)); + + + + var currentBlock = new Block() { ID = idCounter++ }; + AddNode(currentBlock); + AddDirectedEdge(entryBlock, currentBlock); + for (var i = 0; i < instructions.Count; i++) + { + + var isLast = i == instructions.Count - 1; + switch (instructions[i].FlowControl) + { + case IsilFlowControl.UnconditionalJump: + currentBlock.AddInstruction(instructions[i]); + if (!isLast) + { + var newNodeFromJmp = new Block() { ID = idCounter++ }; + AddNode(newNodeFromJmp); + if (TryGetTargetJumpInstructionIndex(instructions[i], out uint jumpTargetIndex)) + { + var result = instructions.Any(instruction => + instruction.InstructionIndex == jumpTargetIndex); + currentBlock.Dirty = true; + } else + { + AddDirectedEdge(currentBlock, exitBlock); + } + + currentBlock.CaculateBlockType(); + currentBlock = newNodeFromJmp; + } + else + { + AddDirectedEdge(currentBlock, exitBlock); + currentBlock.Dirty = true; + } + break; + case IsilFlowControl.MethodCall: + currentBlock.AddInstruction(instructions[i]); + if (!isLast) + { + var newNodeFromCall = new Block() { ID = idCounter++ }; + AddNode(newNodeFromCall); + AddDirectedEdge(currentBlock, newNodeFromCall); + currentBlock.CaculateBlockType(); + currentBlock = newNodeFromCall; + } + else + { + AddDirectedEdge(currentBlock, exitBlock); + currentBlock.CaculateBlockType(); + } + break; + case IsilFlowControl.Continue: + currentBlock.AddInstruction(instructions[i]); + if (isLast) { + // TODO: Investiage + /* This shouldn't happen, we've either smashed into another method or random data such as a jump table */ + } + break; + case IsilFlowControl.MethodReturn: + currentBlock.AddInstruction(instructions[i]); + if (!isLast) { + var newNodeFromReturn = new Block() { ID = idCounter++ }; + AddNode(newNodeFromReturn); + AddDirectedEdge(currentBlock, exitBlock); + currentBlock.CaculateBlockType(); + currentBlock = newNodeFromReturn; + } else + { + AddDirectedEdge(currentBlock, exitBlock); + currentBlock.CaculateBlockType(); + } + + break; + case IsilFlowControl.ConditionalJump: + currentBlock.AddInstruction(instructions[i]); + if (!isLast) + { + var newNodeFromConditionalBranch = new Block() { ID = idCounter++ }; + AddNode(newNodeFromConditionalBranch); + AddDirectedEdge(currentBlock, newNodeFromConditionalBranch); + currentBlock.CaculateBlockType(); + currentBlock.Dirty = true; + currentBlock = newNodeFromConditionalBranch; + } + else + { + AddDirectedEdge(currentBlock, exitBlock); + } + break; + case IsilFlowControl.Interrupt: + currentBlock.AddInstruction(instructions[i]); + var newNodeFromInterrupt = new Block() { ID = idCounter++ }; + AddNode(newNodeFromInterrupt); + AddDirectedEdge(currentBlock, exitBlock); + currentBlock.CaculateBlockType(); + currentBlock = newNodeFromInterrupt; + break; + case IsilFlowControl.IndexedJump: + // This could be a part of either 2 things, a jmp to a jump table (switch statement) or a tail call to another function maybe? I dunno + throw new NotImplementedException("Indirect branch not implemented currently"); + default: + throw new NotImplementedException(instructions[i].ToString() + " " + instructions[i].FlowControl); + } + } + + + + for (var index = 0; index < blockSet.Count; index++) + { + + var node = blockSet[index]; + if (node.Dirty) + FixBlock(node, false); + } + + + } + + public void CalculateDominations() + { + foreach (var block in blockSet) + { + + throw new NotImplementedException(); + } + } + + private void FixBlock(Block block, bool removeJmp = false) + { + if (block.BlockType is BlockType.Fall) + return; + + var jump = block.isilInstructions.Last(); + + var targetInstruction = jump.Operands[0].Data as InstructionSetIndependentInstruction; + + var destination = FindNodeByInstruction(targetInstruction); + + if (destination == null) + { + //We assume that we're tail calling another method somewhere. Need to verify if this breaks anywhere but it shouldn't in general + block.BlockType = BlockType.Call; + return; + } + + + int index = destination.isilInstructions.FindIndex(instruction => instruction == targetInstruction); + + var targetNode = SplitAndCreate(destination, index); + + AddDirectedEdge(block, targetNode); + block.Dirty = false; + + if (removeJmp) + block.isilInstructions.Remove(jump); + } + + protected Block? FindNodeByInstruction(InstructionSetIndependentInstruction instruction) + { + if (instruction == null) + return null; + foreach (var block in blockSet) + { + if (block.isilInstructions.Any(instr => instr == instruction)) + { + return block; + } + } + return null; + } + + private Block SplitAndCreate(Block target, int index) + { + if (index < 0 || index >= target.isilInstructions.Count) + throw new ArgumentOutOfRangeException(nameof(index)); + + // Don't need to split... + if (index == 0) + return target; + + var newNode = new Block() { ID = idCounter++ }; + + // target split in two + // targetFirstPart -> targetSecondPart aka newNode + + // Take the instructions for the secondPart + var instructions = target.isilInstructions.GetRange(index, target.isilInstructions.Count - index); + target.isilInstructions.RemoveRange(index, target.isilInstructions.Count - index); + + // Add those to the newNode + newNode.isilInstructions.AddRange(instructions); + // Transfer control flow + newNode.BlockType = target.BlockType; + target.BlockType = BlockType.Fall; + + // Transfer successors + newNode.Successors = target.Successors; + if (target.Dirty) + newNode.Dirty = true; + target.Dirty = false; + target.Successors = new(); + + // Correct the predecessors for all the successors + foreach (var successor in newNode.Successors) + { + for (int i = 0; i < successor.Predecessors.Count; i++) + { + if (successor.Predecessors[i].ID == target.ID) + successor.Predecessors[i] = newNode; + } + } + + // Add newNode and connect it + AddNode(newNode); + AddDirectedEdge(target, newNode); + + return newNode; + } + + private void AddDirectedEdge(Block from, Block to) + { + from.Successors.Add(to); + to.Predecessors.Add(from); + } + + protected void AddNode(Block block) => blockSet.Add(block); + + private void TraverseAndPreExecute(Block block, Action action) + { + block.Visited = true; + action(block); + foreach (var successor in block.Successors) + if (!successor.Visited) + TraverseAndPreExecute(successor, action); + } + + public string Print(bool instructions = false) + { + var sb = new StringBuilder(); + foreach (var node in blockSet) + { + /* + sb.Append("=========================\n"); + sb.Append( + $"ID: {node.ID}, FC: {node.FlowControl}, Successors:{string.Join(",", node.Successors.Select(i => i.ID))}, Predecessors:{string.Join(",", node.Predecessors.Select(i => i.ID))}"); + if (node.IsConditionalBranch) + sb.Append($", Condition: {node.Condition?.ConditionString ?? "Null"}"); + if (node.Instructions.Count > 0) + sb.Append($", Address {node.GetFormattedInstructionAddress(node.Instructions.First())}"); + sb.Append($", Number of Statements: {node.Statements.Count}"); + sb.Append("\n"); + if (instructions) + foreach (var instruction in node.Instructions) + sb.AppendLine(instruction?.ToString()); + else + foreach (var v in node.Statements) + sb.Append(v.GetTextDump(0)); + sb.Append('\n'); + */ + } + + return sb.ToString(); + } + } +} diff --git a/Cpp2IL.Core/Graphs/Processors/IBlockProcessor.cs b/Cpp2IL.Core/Graphs/Processors/IBlockProcessor.cs new file mode 100644 index 00000000..9c262c92 --- /dev/null +++ b/Cpp2IL.Core/Graphs/Processors/IBlockProcessor.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Cpp2IL.Core.ISIL; + +namespace Cpp2IL.Core.Graphs.Processors +{ + internal interface IBlockProcessor + { + public void Process(Block block); + } +} diff --git a/Cpp2IL.Core/Graphs/Processors/StringProcessor.cs b/Cpp2IL.Core/Graphs/Processors/StringProcessor.cs new file mode 100644 index 00000000..0cf2add1 --- /dev/null +++ b/Cpp2IL.Core/Graphs/Processors/StringProcessor.cs @@ -0,0 +1,33 @@ +using Cpp2IL.Core.ISIL; +using LibCpp2IL; + +namespace Cpp2IL.Core.Graphs.Processors +{ + internal class StringProcessor : IBlockProcessor + { + public void Process(Block block) + { + foreach (var instruction in block.isilInstructions) + { + // TODO: Check if it shows up in any other + if (instruction.OpCode != InstructionSetIndependentOpCode.Move) + { + return; + } + if (instruction.Operands[0].Type != InstructionSetIndependentOperand.OperandType.Register || instruction.Operands[1].Type != InstructionSetIndependentOperand.OperandType.Memory) + { + return; + } + var memoryOp = (IsilMemoryOperand)instruction.Operands[1].Data; + if (memoryOp.Base == null && memoryOp.Index == null && memoryOp.Scale == 0) + { + var val = LibCpp2IlMain.GetLiteralByAddress((ulong)memoryOp.Addend); + if (val == null) + return; + + instruction.Operands[1] = InstructionSetIndependentOperand.MakeImmediate(val); + } + } + } + } +} diff --git a/Cpp2IL.Core/HLIL/BinaryArithmeticOperator.cs b/Cpp2IL.Core/HLIL/BinaryArithmeticOperator.cs deleted file mode 100644 index 0e8f6ed9..00000000 --- a/Cpp2IL.Core/HLIL/BinaryArithmeticOperator.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Cpp2IL.Core.HLIL; - -public enum BinaryArithmeticOperator -{ - Subtract, - Add -} \ No newline at end of file diff --git a/Cpp2IL.Core/HLIL/HLILBuilder.cs b/Cpp2IL.Core/HLIL/HLILBuilder.cs deleted file mode 100644 index b41151d6..00000000 --- a/Cpp2IL.Core/HLIL/HLILBuilder.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System; -using Cpp2IL.Core.ISIL; - -namespace Cpp2IL.Core.HLIL; - -public class HLILBuilder -{ - public HLILMnemonic Mnemonic; - public InstructionSetIndependentOperand[] Operands = Array.Empty(); -} \ No newline at end of file diff --git a/Cpp2IL.Core/HLIL/HLILMnemonic.cs b/Cpp2IL.Core/HLIL/HLILMnemonic.cs deleted file mode 100644 index 60e05813..00000000 --- a/Cpp2IL.Core/HLIL/HLILMnemonic.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Cpp2IL.Core.HLIL; - -public enum HLILMnemonic -{ - Assign, - Goto, - JCond, - Call, - Ret, - Push, - Pop -} \ No newline at end of file diff --git a/Cpp2IL.Core/HLIL/HLILOperand.cs b/Cpp2IL.Core/HLIL/HLILOperand.cs deleted file mode 100644 index 08098718..00000000 --- a/Cpp2IL.Core/HLIL/HLILOperand.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Cpp2IL.Core.HLIL; - -public class HLILOperand -{ - -} \ No newline at end of file diff --git a/Cpp2IL.Core/HLIL/HlilInstruction.cs b/Cpp2IL.Core/HLIL/HlilInstruction.cs deleted file mode 100644 index 5260c05a..00000000 --- a/Cpp2IL.Core/HLIL/HlilInstruction.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Cpp2IL.Core.HLIL; - -public class HlilInstruction -{ - public HlilInstruction() - { - - } -} \ No newline at end of file diff --git a/Cpp2IL.Core/HLIL/UnaryArithmeticOperator.cs b/Cpp2IL.Core/HLIL/UnaryArithmeticOperator.cs deleted file mode 100644 index 4c96dc5a..00000000 --- a/Cpp2IL.Core/HLIL/UnaryArithmeticOperator.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Cpp2IL.Core.HLIL; - -public enum UnaryArithmeticOperator -{ - Not, - Or, - Xor - // TODO whatever else -} \ No newline at end of file diff --git a/Cpp2IL.Core/ISIL/IsilImmediateOperand.cs b/Cpp2IL.Core/ISIL/IsilImmediateOperand.cs index c0d10d1c..0a0c6095 100644 --- a/Cpp2IL.Core/ISIL/IsilImmediateOperand.cs +++ b/Cpp2IL.Core/ISIL/IsilImmediateOperand.cs @@ -23,6 +23,11 @@ public override string ToString() { //Ignore } + + if (Value is string) + { + return "\"" + Value + "\""; + } return Value.ToString(CultureInfo.InvariantCulture); } diff --git a/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs b/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs index 6faee022..306f0ab4 100644 --- a/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs +++ b/Cpp2IL.Core/InstructionSets/Arm64InstructionSet.cs @@ -14,11 +14,6 @@ namespace Cpp2IL.Core.InstructionSets; public class Arm64InstructionSet : Cpp2IlInstructionSet { - public virtual IControlFlowGraph BuildGraphForMethod(MethodAnalysisContext context) - { - return null!; - } - public override Memory GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator) { //Avoid use of capstone where possible diff --git a/Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs b/Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs index a3208e57..c23efda1 100644 --- a/Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs +++ b/Cpp2IL.Core/InstructionSets/ArmV7InstructionSet.cs @@ -13,11 +13,6 @@ namespace Cpp2IL.Core.InstructionSets; public class ArmV7InstructionSet : Cpp2IlInstructionSet { - public virtual IControlFlowGraph BuildGraphForMethod(MethodAnalysisContext context) - { - return null!; - } - public override Memory GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator) { if (ArmV7Utils.TryGetMethodBodyBytesFast(context.UnderlyingPointer, context is AttributeGeneratorMethodAnalysisContext) is { } ret) diff --git a/Cpp2IL.Core/InstructionSets/WasmInstructionSet.cs b/Cpp2IL.Core/InstructionSets/WasmInstructionSet.cs index 3b25c7cf..f6ba1841 100644 --- a/Cpp2IL.Core/InstructionSets/WasmInstructionSet.cs +++ b/Cpp2IL.Core/InstructionSets/WasmInstructionSet.cs @@ -13,11 +13,6 @@ namespace Cpp2IL.Core.InstructionSets; public class WasmInstructionSet : Cpp2IlInstructionSet { - public virtual IControlFlowGraph BuildGraphForMethod(MethodAnalysisContext context) - { - return null!; - } - public override Memory GetRawBytesForMethod(MethodAnalysisContext context, bool isAttributeGenerator) { if (context.Definition is { } methodDefinition) diff --git a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs index 4e77925b..26035d34 100644 --- a/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs +++ b/Cpp2IL.Core/Model/Contexts/MethodAnalysisContext.cs @@ -1,14 +1,13 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Reflection; using Cpp2IL.Core.Graphs; using Cpp2IL.Core.ISIL; +using Cpp2IL.Core.Graphs.Processors; using Cpp2IL.Core.Logging; using Cpp2IL.Core.Utils; using LibCpp2IL; -using LibCpp2IL.BinaryStructures; using LibCpp2IL.Metadata; using StableNameDotNet.Providers; @@ -51,7 +50,7 @@ public class MethodAnalysisContext : HasCustomAttributesAndName, IMethodInfoProv /// /// The control flow graph for this method, if one is built. /// - public IControlFlowGraph? ControlFlowGraph; + public ISILControlFlowGraph? ControlFlowGraph; public List Parameters = new(); @@ -77,6 +76,12 @@ public class MethodAnalysisContext : HasCustomAttributesAndName, IMethodInfoProv //TODO Support custom attributes on return types (v31 feature) public TypeAnalysisContext ReturnTypeContext => InjectedReturnType ?? DeclaringType!.DeclaringAssembly.ResolveIl2CppType(Definition!.RawReturnType!); + + private static List blockProcessors = new List() + { + new StringProcessor() + }; + public MethodAnalysisContext(Il2CppMethodDefinition? definition, TypeAnalysisContext parent) : base(definition?.token ?? 0, parent.AppContext) { DeclaringType = parent; @@ -132,20 +137,17 @@ public void Analyze() if (ConvertedIsil.Count == 0) return; //Nothing to do, empty function - // Intermediate step to convert metadata usage. Ldstr Opcodes etc. - - //TODO: Build control flow graph from ISIL - - + ControlFlowGraph = new ISILControlFlowGraph(); + ControlFlowGraph.Build(ConvertedIsil); - - // ControlFlowGraph = AppContext.InstructionSet.BuildGraphForMethod(this); - // - // if (ControlFlowGraph == null) - // return; - // - // ControlFlowGraph.Run(); - // InstructionSetIndependentNodes = AppContext.InstructionSet.ControlFlowGraphToISIL(ControlFlowGraph, this); + // Post step to convert metadata usage. Ldstr Opcodes etc. + foreach (var block in ControlFlowGraph.Blocks) + { + foreach (var converter in blockProcessors) + { + converter.Process(block); + } + } } public void ReleaseAnalysisData() diff --git a/Cpp2IL.Core/OldGraphs/IControlFlowGraph.cs b/Cpp2IL.Core/OldGraphs/IControlFlowGraph.cs deleted file mode 100644 index 11815bbf..00000000 --- a/Cpp2IL.Core/OldGraphs/IControlFlowGraph.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace Cpp2IL.Core.Graphs; - -public interface IControlFlowGraph -{ - public void Run(bool print = false); - - public List INodes { get; } - - public void TraverseEntireGraphPreOrder(Action action); -} \ No newline at end of file diff --git a/Cpp2IL.Core/OldGraphs/IControlFlowNode.cs b/Cpp2IL.Core/OldGraphs/IControlFlowNode.cs deleted file mode 100644 index c236cf21..00000000 --- a/Cpp2IL.Core/OldGraphs/IControlFlowNode.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Cpp2IL.Core.Graphs; - -public interface IControlFlowNode -{ - -} \ No newline at end of file diff --git a/Cpp2IL.Core/OldGraphs/IStatement.cs b/Cpp2IL.Core/OldGraphs/IStatement.cs deleted file mode 100644 index a005fab0..00000000 --- a/Cpp2IL.Core/OldGraphs/IStatement.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Cpp2IL.Core.Graphs; - -public interface IStatement -{ - string GetTextDump(int indent); -} \ No newline at end of file diff --git a/Cpp2IL.Core/OldGraphs/IfStatement.cs b/Cpp2IL.Core/OldGraphs/IfStatement.cs deleted file mode 100644 index ca531e39..00000000 --- a/Cpp2IL.Core/OldGraphs/IfStatement.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections.Generic; -using System.Text; - -namespace Cpp2IL.Core.Graphs; - -public class IfStatement : IStatement -{ - public List IfBlock; - public List ElseBlock; - public InstructionGraphCondition Condition; - - public IfStatement(InstructionGraphCondition condition, List @if, List @else) - { - Condition = condition; - IfBlock = @if; - ElseBlock = @else; - } - - public IfStatement(InstructionGraphCondition condition, List @if) : this(condition, @if, new List()) {} - - public string GetTextDump(int indent) - { - StringBuilder stringBuilder = new StringBuilder(); - var space = new string(' ', indent); - stringBuilder.Append(space).Append($"if({Condition.ConditionString})\n"); - stringBuilder.Append(space).Append("{\n"); - - foreach (var statement in IfBlock) - stringBuilder.Append(statement.GetTextDump(indent + 4)); - - stringBuilder.Append(space).Append("}\n"); - if (ElseBlock.Count == 0) - return stringBuilder.ToString(); - - stringBuilder.Append(space).Append("else\n"); - stringBuilder.Append(space).Append("{\n"); - - foreach (var statement in ElseBlock) - stringBuilder.Append(statement.GetTextDump(indent + 4)); - - stringBuilder.Append(space).Append("}\n"); - - return stringBuilder.ToString(); - } -} \ No newline at end of file diff --git a/Cpp2IL.Core/OldGraphs/InstructionGraph.cs b/Cpp2IL.Core/OldGraphs/InstructionGraph.cs deleted file mode 100644 index 3551611d..00000000 --- a/Cpp2IL.Core/OldGraphs/InstructionGraph.cs +++ /dev/null @@ -1,512 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using Cpp2IL.Core.Extensions; -using Cpp2IL.Core.Il2CppApiFunctions; -using Iced.Intel; - -namespace Cpp2IL.Core.Graphs; - -// #define MERGE_DEBUG_PRINTS - -public class AbstractControlFlowGraph : IControlFlowGraph where TNode : InstructionGraphNode, new() -{ - protected List Instructions; - protected BaseKeyFunctionAddresses KeyFunctionAddresses; - - protected TNode ExitNode; - - Dictionary InstructionsByAddress; - protected int idCounter = 0; - - protected AbstractControlFlowGraph(List instructions, BaseKeyFunctionAddresses keyFunctionAddresses) - { - if (instructions == null) - throw new ArgumentNullException(nameof(instructions)); - - - var startNode = new TNode() {ID = idCounter++, FlowControl = InstructionGraphNodeFlowControl.Entry}; - ExitNode = new TNode() {ID = idCounter++, FlowControl = InstructionGraphNodeFlowControl.Exit}; - Instructions = instructions; - KeyFunctionAddresses = keyFunctionAddresses; - InstructionsByAddress = new Dictionary(); - Root = startNode; - nodeSet = new Collection(); - - foreach (var instruction in Instructions) - InstructionsByAddress.Add(GetAddressOfInstruction(instruction), instruction); - - } - - protected virtual ulong GetAddressOfInstruction(TInstruction instruction) - { - throw new NotImplementedException(); - } - - protected TNode SplitAndCreate(TNode target, int index) - { - if (index < 0 || index >= target.Instructions.Count) - throw new ArgumentOutOfRangeException(nameof(index)); - - // Don't need to split... - if (index == 0) - return target; - - var newNode = new TNode() {ID = idCounter++}; - - // target split in two - // targetFirstPart -> targetSecondPart aka newNode - - // Take the instructions for the secondPart - var instructions = target.Instructions.GetRange(index, target.Instructions.Count - index); - target.Instructions.RemoveRange(index, target.Instructions.Count - index); - - // Add those to the newNode - newNode.Instructions.AddRange(instructions); - // Transfer control flow - newNode.FlowControl = target.FlowControl; - target.FlowControl = InstructionGraphNodeFlowControl.Continue; - - // Transfer successors - newNode.Successors = target.Successors; - newNode.HasProcessedSuccessors = target.HasProcessedSuccessors; - newNode.NeedsCorrectingDueToJump = target.NeedsCorrectingDueToJump; - target.NeedsCorrectingDueToJump = false; //We've split, so this no longer ends with a jump - target.Successors = new(); - - // Correct the predecessors for all the successors - foreach (var successor in newNode.Successors) - { - for (int i = 0; i < successor.Predecessors.Count; i++) - { - if (successor.Predecessors[i] == target) - successor.Predecessors[i] = newNode; - } - } - - // Add newNode and connect it - AddNode(newNode); - AddDirectedEdge(target, newNode); - - return newNode; - } - - private static Dictionary UsageAndDefinitions = new(); - - - public void Run(bool print = false) - { - AddNode(Root); - if (Instructions.Count == 0) - { - AddNode(ExitNode); - AddDirectedEdge(Root, ExitNode); - return; - } - - BuildInitialGraph(); - AddNode(ExitNode); - SegmentGraph(); - ConstructConditions(); - SquashGraph(); - ComputeDominators(); - IdentifyLoops(); - CalculateUseDefs(); - DetermineLocals(); - if (print) - Console.WriteLine(Print()); - } - - - private void SquashGraph() - { - // We can optimize this later - foreach (var node in Nodes) - node.Visited = false; - TraverseAndPostExecute(Root, node => - { - foreach (var instruction in node.Instructions) - node.Statements.Add(new InstructionStatement(instruction)); - }); - foreach (var node in Nodes) - node.Visited = false; - TraverseAndPostExecute(Root, node => - { - bool success = false; - do - { - success = TrySquash(node); - } while (success); - }); - } - - public void TraverseEntireGraphPreOrder(Action action) => TraversePreOrder(Root, action); - - public void TraversePreOrder(TNode node, Action action) - { - action(node); - foreach (var successor in node.Successors) - TraversePreOrder((TNode) successor, action); - } - - private void TraverseAndPostExecute(InstructionGraphNode node, Action> action) - { - node.Visited = true; - foreach (var successor in node.Successors) - if(!successor.Visited) - TraverseAndPostExecute(successor, action); - action(node); - } - - private bool TrySquash(InstructionGraphNode node) - { - if(node == ExitNode || node == Root) - return false; - - if (node.Successors.Count == 1) - { - if (node.Successors[0].Predecessors.Count == 1) - { - - var succ = (TNode) node.Successors[0]; - if(succ == ExitNode) - return false; - -#if MERGE_DEBUG_PRINTS - Console.WriteLine($"Merging sequence node {succ.ID} --> {node.ID}"); -#endif - - node.FlowControl = succ.FlowControl; - //node.Instructions.AddRange(succ.Instructions); - node.Successors = succ.Successors; - foreach (var succSuccessor in succ.Successors) - { - succSuccessor.Predecessors.Remove(succ); - succSuccessor.Predecessors.Add(node); - } - node.Condition = succ.Condition; - node.Statements.AddRange(succ.Statements); - DirectedEdgeRemove(node, succ); - nodeSet.Remove(succ); - return true; - } - } - else if (node.Successors.Count == 2) - { - return TrySquashConditional(node); - } - return false; - } - - protected void DirectedEdgeRemove(InstructionGraphNode from, InstructionGraphNode to) - { - from.Successors.Remove((TNode)to); - to.Predecessors.Remove((TNode)from); - } - - private bool TrySquashConditional(InstructionGraphNode node) - { - if (!node.IsConditionalBranch) - return false; - var condition = node.Condition; - var truePath = node.Successors[1]; - var falsePath = node.Successors[0]; - if (truePath == null || falsePath == null) - throw new NullReferenceException("True or false path is null, this shouldn't happen ever"); - var truePathSuccessor = GetSingleSucc(truePath); - var falsePathSuccessor = GetSingleSucc(falsePath); - if (falsePathSuccessor == truePath) - { - if (falsePath.Predecessors.Count != 1) - return false; - var ifstatement = new IfStatement(condition!, falsePath.Statements); - -#if MERGE_DEBUG_PRINTS - Console.WriteLine($"Merging if node {succ.ID} --> {node.ID}"); -#endif - node.FlowControl = InstructionGraphNodeFlowControl.Continue; - node.Condition = null; - DirectedEdgeRemove(falsePath, truePath); - DirectedEdgeRemove(node, falsePath); - Nodes.Remove((TNode)falsePath); - node.Statements.Add(ifstatement); - return true; - } - else - { - if (truePathSuccessor == falsePath) - { - if (truePath.Predecessors.Count != 1) - return false; - condition!.FlipCondition(); - var ifstatement = new IfStatement(condition, truePath.Statements); - -#if MERGE_DEBUG_PRINTS - Console.WriteLine($"Merging if node {succ.ID} --> {node.ID}"); -#endif - node.FlowControl = InstructionGraphNodeFlowControl.Continue; - node.Condition = null; - DirectedEdgeRemove(truePath, falsePath); - DirectedEdgeRemove(node, truePath); - Nodes.Remove((TNode)truePath); - node.Statements.Add(ifstatement); - return true; - } - if (truePathSuccessor is not null && falsePathSuccessor is not null && truePathSuccessor == falsePathSuccessor) - { - if (falsePath.Predecessors.Count != 1 || truePath.Predecessors.Count != 1) - return false; - - var ifstatement = new IfStatement(condition!, falsePath.Statements, truePath.Statements); -#if MERGE_DEBUG_PRINTS - Console.WriteLine($"Merging if else block nodes {truePath.ID} and {falsePath.ID} --> {node.ID}"); -#endif - node.FlowControl = InstructionGraphNodeFlowControl.Continue; - node.Condition = null; - DirectedEdgeRemove(node, truePath); - DirectedEdgeRemove(node, falsePath); - DirectedEdgeRemove(truePath, truePathSuccessor); - DirectedEdgeRemove(falsePath, falsePathSuccessor); - AddDirectedEdge((TNode)node, (TNode)falsePathSuccessor); - Nodes.Remove((TNode)truePath); - Nodes.Remove((TNode)falsePath); - node.Statements.Add(ifstatement); - return true; - } - } - - - return false; - } - - // // Haha, yes very funny.... not! - private InstructionGraphNode? GetSingleSucc(InstructionGraphNode node) - { - if (node.Successors.Count != 1) - return null; - - return node.Successors[0]; - } - - - protected virtual void DetermineLocals() - { - throw new NotImplementedException(); - } - - private void ConstructConditions() - { - foreach (var graphNode in Nodes) - { - if (graphNode.IsConditionalBranch) - { - graphNode.CheckCondition(); - } - } - } - - private void CalculateUseDefs() - { - foreach (var node in Nodes) - { - foreach (var instruction in node.Instructions) - { - var useDef = new InstructionGraphUseDef(); - GetUseDefsForInstruction(instruction, useDef); - } - } - } - - protected virtual void GetUseDefsForInstruction(TInstruction instruction, InstructionGraphUseDef instructionGraphUseDef) - { - throw new NotImplementedException(); - } - - protected virtual void SegmentGraph() - { - throw new NotImplementedException(); - } - - protected virtual void BuildInitialGraph() - { - throw new NotImplementedException(); - } - - // Highly recommend reading https://www.backerstreet.com/decompiler/loop_analysis.php - private void ComputeDominators() - { - for (int i = 0; i < nodeSet.Count; i++) - { - nodeSet[i].Dominators = new BitArray(nodeSet.Count); - nodeSet[i].Dominators!.SetAll(true); - nodeSet[i].ID = i; - } - - Root.Dominators!.SetAll(false); - - Root.Dominators.Set(Root.ID, true); - - BitArray temp = new BitArray(nodeSet.Count); - - bool changed = false; - do - { - changed = false; - foreach (var node in nodeSet) - { - if (node == Root) - continue; - - foreach (var predecessor in node.Predecessors) - { - temp.SetAll(false); - temp.Or(node.Dominators!); - node.Dominators!.And(predecessor.Dominators!); - node.Dominators.Set(node.ID, true); - if (!node.Dominators.BitsAreEqual(temp)) - changed = true; - } - } - } while (changed); - } - - private void IdentifyLoops() - { - List>> identifiedLoops = new(); - foreach (var node in nodeSet) - { - if (node == Root) - continue; - foreach (var succ in node.Successors) - { - if (node.Dominators!.Get(succ.ID)) - { - identifiedLoops.Add(GetLoopForEdge(succ, node)); - } - } - } - - identifiedLoops = SortLoops(identifiedLoops); - foreach (var loop in identifiedLoops) - { - // All loops can be represented as doWhiles for example https://i.imgur.com/fCzStwX.png - throw new NotImplementedException("Loops aren't currently implemented"); - // var doWhileStatement = new InstructionGraphStatement(InstructionGraphStatementType.DoWhile); - // if (loop.Nodes.Count == 1) - // { - // doWhileStatement.Expression = loop.Nodes[0].Condition; - // //doWhileStatement.Nodes - // } - //doWhileStatement.Expression = loop.Nodes[1].Condition; - //doWhileStatement.Nodes = loop.Nodes; - //doWhileStatement.ContinueNode = loop.Nodes[1]; - //doWhileStatement. - - //TODO: Extract break and continue statement - } - } - - - private List>> SortLoops( - List>> loops) - { - // We'll just assume for now theres only one loop - // TODO: Return the loops in a list from innermost to outermost - return loops; - } - - private InstructionGraphLoop> GetLoopForEdge( - InstructionGraphNode header, InstructionGraphNode tail) - { - Stack> stack = new(); - InstructionGraphLoop> loop = new(header); - if (header != tail) - { - loop.Nodes.Add(tail); - stack.Push(tail); - } - - while (stack.Count != 0) - { - var node = stack.Pop(); - foreach (var predecessor in node.Predecessors) - { - if (!loop.Nodes.Contains(predecessor)) - { - loop.Nodes.Add(predecessor); - stack.Push(predecessor); - } - } - } - - return loop; - } - - - protected TNode? FindNodeByAddress(ulong address) - { - if (InstructionsByAddress.TryGetValue(address, out var instruction)) - { - foreach (var node in Nodes) - { - if (node.Instructions.Any(instr => GetAddressOfInstruction(instr) == address)) - { - return node; - } - } - } - - return null; - } - - public TNode Root { get; } - - private Collection nodeSet; - - protected void AddDirectedEdge(TNode from, TNode to) - { - from.Successors.Add(to); - to.Predecessors.Add(from); - } - - protected void AddNode(TNode node) => nodeSet.Add(node); - - - public string Print(bool instructions = false) - { - var sb = new StringBuilder(); - foreach (var node in nodeSet) - { - sb.Append("=========================\n"); - sb.Append( - $"ID: {node.ID}, FC: {node.FlowControl}, Successors:{string.Join(",", node.Successors.Select(i => i.ID))}, Predecessors:{string.Join(",", node.Predecessors.Select(i => i.ID))}"); - if (node.IsConditionalBranch) - sb.Append($", Condition: {node.Condition?.ConditionString ?? "Null"}"); - if (node.Instructions.Count > 0) - sb.Append($", Address {node.GetFormattedInstructionAddress(node.Instructions.First())}"); - sb.Append($", Number of Statements: {node.Statements.Count}"); - sb.Append("\n"); - if (instructions) - foreach (var instruction in node.Instructions) - sb.AppendLine(instruction?.ToString()); - else - foreach (var v in node.Statements) - sb.Append(v.GetTextDump(0)); - - - - sb.Append('\n'); - } - - return sb.ToString(); - } - - protected Collection Nodes => nodeSet; - - public List INodes => Nodes.Cast().ToList(); - - public int Count => nodeSet.Count; -} diff --git a/Cpp2IL.Core/OldGraphs/InstructionGraphCondition.cs b/Cpp2IL.Core/OldGraphs/InstructionGraphCondition.cs deleted file mode 100644 index ed5c66b0..00000000 --- a/Cpp2IL.Core/OldGraphs/InstructionGraphCondition.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; - -namespace Cpp2IL.Core.Graphs; - -public class InstructionGraphCondition -{ - public TInstruction Comparison { get; } - public TInstruction Jump { get; } - public InstructionGraphCondition(TInstruction comparison, TInstruction conditionalJump) - { - Comparison = comparison; - Jump = conditionalJump; - ConditionString = GetCondition(); - } - - public string ConditionString { get; set; } - - public virtual string GetCondition(bool invert = false) => throw new NotImplementedException(); - - public virtual void FlipCondition() => throw new NotImplementedException(); - - - public virtual string GetConditionOperator(bool invert = false) => throw new NotImplementedException(); -} \ No newline at end of file diff --git a/Cpp2IL.Core/OldGraphs/InstructionGraphLoop.cs b/Cpp2IL.Core/OldGraphs/InstructionGraphLoop.cs deleted file mode 100644 index 25667135..00000000 --- a/Cpp2IL.Core/OldGraphs/InstructionGraphLoop.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; - -namespace Cpp2IL.Core.Graphs; - -public class InstructionGraphLoop -{ - public TNode Header; - public List Nodes; - - public InstructionGraphLoop(TNode header) - { - Header = header; - Nodes = new List(); - Nodes.Add(header); - } -} \ No newline at end of file diff --git a/Cpp2IL.Core/OldGraphs/InstructionGraphNode.cs b/Cpp2IL.Core/OldGraphs/InstructionGraphNode.cs deleted file mode 100644 index a9a3c754..00000000 --- a/Cpp2IL.Core/OldGraphs/InstructionGraphNode.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Cpp2IL.Core.Exceptions; -using Cpp2IL.Core.ISIL; - -namespace Cpp2IL.Core.Graphs; - -public class InstructionGraphNode : IControlFlowNode -{ - public int ID { get; set; } - - public bool IsConditionalBranch => _flowControl == InstructionGraphNodeFlowControl.ConditionalJump; - - public InstructionGraphCondition? Condition { get; protected internal set; } - - public InstructionGraphNode() - { - Instructions = new(); - Successors = new(); - Predecessors = new(); - Statements = new(); - } - - public InstructionGraphNodeSet Successors { get; set; } - public InstructionGraphNodeSet Predecessors { get; set; } - - public List Statements { get;} - - private InstructionGraphNodeFlowControl? _flowControl; - - public bool HasProcessedSuccessors = false; - - public bool NeedsCorrectingDueToJump = false; - - public bool Visited = false; - - public BitArray? Dominators; - - public InstructionGraphNodeFlowControl? FlowControl - { - get => _flowControl; - set => _flowControl = value; - } - - public void AddInstruction(TInstruction instruction) => Instructions.Add(instruction); - - public void CheckCondition() - { - if (Successors.Count != 2) - { - throw new Exception($"Node didn't have 2 neighbours, instead had {Successors.Count}, aborting...\n\nNode Dump:\n{GetTextDump()}"); - } - - var node = this; - while(!node.ThisNodeHasComparison()) - node = node.Predecessors.Count == 1 ? node.Predecessors.Single() : throw new NodeConditionCalculationException("Don't have a comparison and don't have a single predecessor line to a node which has one"); - - var lastComparison = node.GetLastComparison(); - - CreateCondition(lastComparison); - - if (Condition is not null) - { - Instructions.Remove(Condition.Jump); - node.Instructions.Remove(lastComparison); - } - } - - protected virtual void CreateCondition(TInstruction comparison) - { - throw new NotImplementedException(); - } - - protected virtual TInstruction GetLastComparison() => throw new NotImplementedException(); - - protected string GetTextDump() - { - StringBuilder stringBuilder = new(); - stringBuilder.Append($"ID: {ID}, FlowControl: {_flowControl}"); - if (IsConditionalBranch) - stringBuilder.Append($", Condition: {Condition?.ConditionString}"); - stringBuilder.Append('\n'); - foreach (var instruction in Instructions) - { - stringBuilder.AppendLine(GetFormattedInstructionAddress(instruction) + " " + instruction); - } - return stringBuilder.ToString(); - } - - public virtual string GetFormattedInstructionAddress(TInstruction instruction) => throw new NotImplementedException(); - public virtual bool ThisNodeHasComparison() - { - throw new NotImplementedException(); - } - - public List Instructions { get; } = new(); - public List TranslatedInstructions = new(); -} \ No newline at end of file diff --git a/Cpp2IL.Core/OldGraphs/InstructionGraphNodeFlowControl.cs b/Cpp2IL.Core/OldGraphs/InstructionGraphNodeFlowControl.cs deleted file mode 100644 index 07e56841..00000000 --- a/Cpp2IL.Core/OldGraphs/InstructionGraphNodeFlowControl.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Cpp2IL.Core.Graphs; - -public enum InstructionGraphNodeFlowControl -{ - Continue, - UnconditionalJump, - ConditionalJump, - IndirectJump, - Call, - IndirectCall, - Return, - NoReturn, - Entry, - Exit, -} \ No newline at end of file diff --git a/Cpp2IL.Core/OldGraphs/InstructionGraphNodeSet.cs b/Cpp2IL.Core/OldGraphs/InstructionGraphNodeSet.cs deleted file mode 100644 index 19608f8a..00000000 --- a/Cpp2IL.Core/OldGraphs/InstructionGraphNodeSet.cs +++ /dev/null @@ -1,5 +0,0 @@ -using System.Collections.ObjectModel; - -namespace Cpp2IL.Core.Graphs; - -public class InstructionGraphNodeSet : Collection> { } \ No newline at end of file diff --git a/Cpp2IL.Core/OldGraphs/InstructionGraphStatement.cs b/Cpp2IL.Core/OldGraphs/InstructionGraphStatement.cs deleted file mode 100644 index 18db8fac..00000000 --- a/Cpp2IL.Core/OldGraphs/InstructionGraphStatement.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; - -namespace Cpp2IL.Core.Graphs; - -public class InstructionGraphStatement -{ - public InstructionGraphStatementType Type { get; } - public InstructionGraphCondition? Expression; - public List>? Nodes; - public InstructionGraphNode? ContinueNode; - public InstructionGraphNode? BreakNode; - public InstructionGraphStatement(InstructionGraphStatementType type) - { - Type = type; - } -} \ No newline at end of file diff --git a/Cpp2IL.Core/OldGraphs/InstructionGraphStatementType.cs b/Cpp2IL.Core/OldGraphs/InstructionGraphStatementType.cs deleted file mode 100644 index ce6bd696..00000000 --- a/Cpp2IL.Core/OldGraphs/InstructionGraphStatementType.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Cpp2IL.Core.Graphs; - -public enum InstructionGraphStatementType -{ - Goto, - If, - DoWhile -} \ No newline at end of file diff --git a/Cpp2IL.Core/OldGraphs/InstructionGraphUseDef.cs b/Cpp2IL.Core/OldGraphs/InstructionGraphUseDef.cs deleted file mode 100644 index d32ceeec..00000000 --- a/Cpp2IL.Core/OldGraphs/InstructionGraphUseDef.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; - -namespace Cpp2IL.Core.Graphs; - -public struct InstructionGraphUseDef -{ - public List Uses; - public List Definitions; - - public InstructionGraphUseDef() - { - Uses = new(); - Definitions = new(); - } -} \ No newline at end of file diff --git a/Cpp2IL.Core/OldGraphs/InstructionStatement.cs b/Cpp2IL.Core/OldGraphs/InstructionStatement.cs deleted file mode 100644 index 045604a6..00000000 --- a/Cpp2IL.Core/OldGraphs/InstructionStatement.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Cpp2IL.Core.Graphs; - -public class InstructionStatement : IStatement -{ - public TInstruction Instruction { get; } - public InstructionStatement(TInstruction instruction) - { - Instruction = instruction; - } - public string GetTextDump(int indent) - { - return new string(' ', indent) + Instruction+"\n"; - } -} \ No newline at end of file diff --git a/Cpp2IL.Core/OldGraphs/X86ControlFlowGraphCondition.cs b/Cpp2IL.Core/OldGraphs/X86ControlFlowGraphCondition.cs deleted file mode 100644 index 6ed3e081..00000000 --- a/Cpp2IL.Core/OldGraphs/X86ControlFlowGraphCondition.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using Iced.Intel; - -namespace Cpp2IL.Core.Graphs; - -public class X86InstructionGraphCondition : InstructionGraphCondition -{ - private static MasmFormatter _formatter = new(); - private static StringOutput _output = new(); - public X86InstructionGraphCondition(Instruction comparison, Instruction conditionalJump) : base(comparison, conditionalJump) - { - } - - public override string GetCondition(bool invert = false) - { - if (Comparison.Mnemonic == Mnemonic.Test) - { - if (Comparison.Op0Kind == OpKind.Register && Comparison.Op1Kind == OpKind.Register && Comparison.Op0Register == Comparison.Op1Register) - { - _formatter.FormatOperand(Comparison, _output, 0); - return $"{_output.ToStringAndReset()} {GetConditionOperator(invert)} 0"; - } - _formatter.FormatOperand(Comparison, _output, 0); - var argumentOne = _output.ToStringAndReset(); - _formatter.FormatOperand(Comparison, _output, 1); - var argumentTwo = _output.ToStringAndReset(); - return $"({argumentOne} & {argumentTwo}) {GetConditionOperator(invert)} 0"; - } - if(Comparison.Mnemonic == Mnemonic.Cmp) - { - _formatter.FormatOperand(Comparison, _output, 0); - var argumentOne = _output.ToStringAndReset(); - _formatter.FormatOperand(Comparison, _output, 1); - var argumentTwo = _output.ToStringAndReset(); - return $"{argumentOne} {GetConditionOperator(invert)} {argumentTwo}"; - } - throw new Exception($"Don't know what to do with {Comparison.Mnemonic}"); - } - - public override void FlipCondition() - { - ConditionString = GetCondition(true); - } - - public override string GetConditionOperator(bool invert = false) - { - switch (Jump.Mnemonic) - { - case Mnemonic.Je: - return invert ? "==": "!="; - case Mnemonic.Jne: - return invert ? "!=": "=="; - case Mnemonic.Jg: - return invert ? ">": "<"; - case Mnemonic.Jge: - return invert ? ">=": "<="; - case Mnemonic.Jl: - case Mnemonic.Js: - return invert ? "<": ">"; - case Mnemonic.Jle: - return invert ? "<=": ">="; - case Mnemonic.Ja: - return invert ? ">": "<"; - case Mnemonic.Jae: - case Mnemonic.Jns: - return invert ? ">=": "<="; - case Mnemonic.Jb: - return invert ? "<": ">"; - case Mnemonic.Jbe: - return invert ? "<=": ">="; - case Mnemonic.Jp: - return "has parity idk todo"; //"low-order eight bits of result contain an even number of 1 bits" - default: - throw new Exception($"{Jump.Mnemonic} isn't supported currently"); - } - } -} \ No newline at end of file diff --git a/Cpp2IL.Core/OldGraphs/x86ControlFlowGraph.cs b/Cpp2IL.Core/OldGraphs/x86ControlFlowGraph.cs deleted file mode 100644 index 35c2a7dc..00000000 --- a/Cpp2IL.Core/OldGraphs/x86ControlFlowGraph.cs +++ /dev/null @@ -1,313 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Cpp2IL.Core.Il2CppApiFunctions; -using Iced.Intel; - -namespace Cpp2IL.Core.Graphs; - -public class X86ControlFlowGraph : AbstractControlFlowGraph -{ - public bool Is32Bit; - public X86ControlFlowGraph(List instructions, bool is32Bit, BaseKeyFunctionAddresses keyFunctionAddresses) : base(instructions, keyFunctionAddresses) - { - Is32Bit = is32Bit; - } - - protected override ulong GetAddressOfInstruction(Instruction instruction) => instruction.IP; - - protected override void SegmentGraph() - { - for (var i = 0; i < Nodes.Count; i++) - { - var node = Nodes[i]; - - if(node.HasProcessedSuccessors) - continue; - - if (node.FlowControl is InstructionGraphNodeFlowControl.ConditionalJump) - FixNode(node); - - node.HasProcessedSuccessors = true; - } - } - - private void FixNode(X86ControlFlowGraphNode node, bool removeJmp = false) - { - if(node.FlowControl is InstructionGraphNodeFlowControl.Continue) - return; //Can happen if we split this node during BuildInitialGraph jumpNodesToCorrect processing. - - var jump = node.Instructions.Last(); - - var destination = FindNodeByAddress(jump.NearBranchTarget); - - if (destination == null) - { - //We assume that we're tail calling another method somewhere. Need to verify if this breaks anywhere but it shouldn't in general - node.FlowControl = InstructionGraphNodeFlowControl.Call; - return; - // throw new($"While fixing conditional jump node {node.ID}, couldn't find destination node at 0x{jump.NearBranchTarget:X}, near branch from 0x{jump.IP:X}"); - } - - int index = destination.Instructions.FindIndex(instruction => instruction.IP == jump.NearBranchTarget); - - var targetNode = SplitAndCreate(destination, index); - - AddDirectedEdge(node, targetNode); - - node.NeedsCorrectingDueToJump = false; - - if (removeJmp) - node.Instructions.Remove(jump); - } - - private static HashSet _volatileRegisters = new() - { - Register.RCX, - Register.RDX, - Register.R8, - Register.R9, - Register.R10, - Register.R11, - Register.XMM0, - Register.XMM1, - Register.XMM2, - Register.XMM3, - Register.XMM4, - Register.XMM5, - }; - - protected override void DetermineLocals() - { - TraverseNode(Root); - } - - private static InstructionInfoFactory _instructionInfoFactory = new(); - private void TraverseNode(InstructionGraphNode node) - { - node.Visited = true; - - // Pre visit - - foreach (var succ in node.Successors) - { - if (!succ.Visited) - TraverseNode(succ); - } - - // Post visit - - for (int i = 0; i < node.Instructions.Count; i++) - { - var nodeInstruction = node.Instructions[i]; - /*switch (nodeInstruction.Mnemonic) - { - case Mnemonic.Mov when nodeInstruction.Op0Kind == OpKind.Register && nodeInstruction.Op1Kind == OpKind.Register: - var op0 = InstructionSetIndependentOperand.MakeRegister(nodeInstruction.Op0Register.GetFullRegister().ToString()); - var op1 = InstructionSetIndependentOperand.MakeRegister(nodeInstruction.Op1Register.GetFullRegister().ToString()); - node.TranslatedInstructions.Add(new InstructionSetIndependentInstruction(InstructionSetIndependentOpCode.Move, op0, op1)); - break; - case Mnemonic.Mov when nodeInstruction.Op0Kind == OpKind.Register && nodeInstruction.Op1Kind == OpKind.Memory: - var op0Reg = InstructionSetIndependentOperand.MakeRegister(nodeInstruction.Op0Register.GetFullRegister().ToString()); - var op1Mem = InstructionSetIndependentOperand.MakeMemory( - new IsilMemoryOperand( - InstructionSetIndependentOperand.MakeRegister(nodeInstruction.MemoryBase.GetFullRegister().ToString()), - InstructionSetIndependentOperand.MakeRegister(nodeInstruction.MemoryIndex.GetFullRegister().ToString()), - (long)nodeInstruction.MemoryDisplacement64, - nodeInstruction.MemoryIndexScale - ) - ); - node.TranslatedInstructions.Add(new InstructionSetIndependentInstruction(InstructionSetIndependentOpCode.Move, op0Reg, op1Mem)); - break; - case Mnemonic.Mov when nodeInstruction.Op0Kind == OpKind.Memory && nodeInstruction.Op1Kind.IsImmediate(): - var op0Mem = InstructionSetIndependentOperand.MakeMemory( - new IsilMemoryOperand( - InstructionSetIndependentOperand.MakeRegister(nodeInstruction.MemoryBase.GetFullRegister().ToString()), - InstructionSetIndependentOperand.MakeRegister(nodeInstruction.MemoryIndex.GetFullRegister().ToString()), - (long)nodeInstruction.MemoryDisplacement64, - nodeInstruction.MemoryIndexScale - ) - ); - node.TranslatedInstructions.Add(new InstructionSetIndependentInstruction(InstructionSetIndependentOpCode.Move, op0Mem, InstructionSetIndependentOperand.MakeImmediate(nodeInstruction.GetImmediate(1)))); - break; - case Mnemonic.Call: - node.TranslatedInstructions.Add(new InstructionSetIndependentInstruction(InstructionSetIndependentOpCode.Call)); - break; - }*/ - - } - } - - protected override void GetUseDefsForInstruction(Instruction instruction, InstructionGraphUseDef instructionGraphUseDef) - { - var info = _instructionInfoFactory.GetInfo(instruction); - foreach (var usedRegister in info.GetUsedRegisters()) - { - switch (usedRegister.Access) - { - case OpAccess.CondRead: - case OpAccess.Read: - instructionGraphUseDef.Uses.Add(usedRegister.Register.GetFullRegister().ToString()); - break; - case OpAccess.Write: - case OpAccess.CondWrite: - instructionGraphUseDef.Definitions.Add(usedRegister.Register.GetFullRegister().ToString()); - break; - case OpAccess.ReadCondWrite: - case OpAccess.ReadWrite: - var item = usedRegister.Register.GetFullRegister().ToString(); - instructionGraphUseDef.Uses.Add(item); - instructionGraphUseDef.Definitions.Add(usedRegister.Register.GetFullRegister().ToString()); - break; - } - } - } - - private InstructionGraphNodeFlowControl GetAbstractControlFlow(FlowControl flowControl) - { - switch (flowControl) - { - case FlowControl.Call: - return InstructionGraphNodeFlowControl.Call; - case FlowControl.UnconditionalBranch: - return InstructionGraphNodeFlowControl.UnconditionalJump; - case FlowControl.IndirectCall: - return InstructionGraphNodeFlowControl.IndirectCall; - case FlowControl.Return: - return InstructionGraphNodeFlowControl.Return; - case FlowControl.Next: - return InstructionGraphNodeFlowControl.Continue; - case FlowControl.Interrupt: - return InstructionGraphNodeFlowControl.NoReturn; - case FlowControl.ConditionalBranch: - return InstructionGraphNodeFlowControl.ConditionalJump; - case FlowControl.IndirectBranch: - return InstructionGraphNodeFlowControl.IndirectJump; - default: - throw new NotImplementedException($"Flow control {flowControl} not supported"); - - } - } - - protected override void BuildInitialGraph() - { - var currentNode = new X86ControlFlowGraphNode() {ID = idCounter++}; - AddNode(currentNode); - AddDirectedEdge(Root, currentNode); - for (var i = 0; i < Instructions.Count; i++) - { - - var isLast = i == Instructions.Count - 1; - switch (Instructions[i].FlowControl) - { - case FlowControl.UnconditionalBranch: - currentNode.AddInstruction(Instructions[i]); - if (!isLast) - { - var newNodeFromJmp = new X86ControlFlowGraphNode() {ID = idCounter++}; - AddNode(newNodeFromJmp); - var result = Instructions.Any(instruction => - instruction.IP == Instructions[i].NearBranchTarget); - if (!result) - { - //AddDirectedEdge(currentNode, newNodeFromJmp); // This is a jmp outside of this method, presumably a noreturn method or a tail call probably - AddDirectedEdge(currentNode, ExitNode); - } - else - currentNode.NeedsCorrectingDueToJump = true; - - currentNode.FlowControl = GetAbstractControlFlow(Instructions[i].FlowControl); - currentNode = newNodeFromJmp; - } - else - { - AddDirectedEdge(currentNode, ExitNode); - currentNode.NeedsCorrectingDueToJump = true; - } - break; - case FlowControl.IndirectCall: - case FlowControl.Call: - currentNode.AddInstruction(Instructions[i]); - if (!isLast) - { - var newNodeFromCall = new X86ControlFlowGraphNode() {ID = idCounter++}; - AddNode(newNodeFromCall); - AddDirectedEdge(currentNode, newNodeFromCall); - currentNode.FlowControl = GetAbstractControlFlow(Instructions[i].FlowControl); - currentNode = newNodeFromCall; - } - else - { - AddDirectedEdge(currentNode, ExitNode); - } - break; - case FlowControl.Next: - currentNode.AddInstruction(Instructions[i]); - if (isLast) { /* This shouldn't happen */} - break; - case FlowControl.Return: - currentNode.AddInstruction(Instructions[i]); - var newNodeFromReturn = new X86ControlFlowGraphNode() {ID = idCounter++}; - AddNode(newNodeFromReturn); - AddDirectedEdge(currentNode, ExitNode); - currentNode.FlowControl = GetAbstractControlFlow(Instructions[i].FlowControl); - currentNode = newNodeFromReturn; - break; - case FlowControl.ConditionalBranch: - currentNode.AddInstruction(Instructions[i]); - if (!isLast) - { - var newNodeFromConditionalBranch = new X86ControlFlowGraphNode() {ID = idCounter++}; - AddNode(newNodeFromConditionalBranch); - AddDirectedEdge(currentNode, newNodeFromConditionalBranch); - currentNode.FlowControl = GetAbstractControlFlow(Instructions[i].FlowControl); - currentNode = newNodeFromConditionalBranch; - } - else - { - AddDirectedEdge(currentNode, ExitNode); - } - - break; - case FlowControl.Interrupt: - currentNode.AddInstruction(Instructions[i]); - var newNodeFromInterrupt = new X86ControlFlowGraphNode() {ID = idCounter++}; - AddNode(newNodeFromInterrupt); - AddDirectedEdge(currentNode, ExitNode); - currentNode.FlowControl = GetAbstractControlFlow(Instructions[i].FlowControl); - currentNode = newNodeFromInterrupt; - break; - case FlowControl.IndirectBranch: - // This could be a part of either 2 things, a jmp to a jump table (switch statement) or a tail call to another function maybe? I dunno - throw new NotImplementedException("Indirect branch not implemented currently"); - default: - throw new NotImplementedException(Instructions[i].ToString() + " " + Instructions[i].FlowControl); - } - } - - - - for (var index = 0; index < Nodes.Count; index++) - { - var node = Nodes[index]; - if (node.NeedsCorrectingDueToJump) - FixNode(node, true); - } - - //CleanUp(); - } - - private void CleanUp() - { - var nodesToRemove = new List(); - foreach (var node in Nodes) - { - if (node.Successors.Count == 0 && node.Predecessors.Count == 0) - nodesToRemove.Add(node); - } - foreach (var node in nodesToRemove) - { - Nodes.Remove(node); - } - } -} diff --git a/Cpp2IL.Core/OldGraphs/x86ControlFlowGraphNode.cs b/Cpp2IL.Core/OldGraphs/x86ControlFlowGraphNode.cs deleted file mode 100644 index 810e3b3c..00000000 --- a/Cpp2IL.Core/OldGraphs/x86ControlFlowGraphNode.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Linq; -using Iced.Intel; - -namespace Cpp2IL.Core.Graphs; - -public class X86ControlFlowGraphNode : InstructionGraphNode -{ - public override string GetFormattedInstructionAddress(Instruction instruction) - { - return "0x" + instruction.IP.ToString("X8").ToUpperInvariant(); - } - - public override bool ThisNodeHasComparison() - { - return Instructions.Any(instruction => instruction.Mnemonic is Mnemonic.Cmp or Mnemonic.Test); - } - - protected override Instruction GetLastComparison() => Instructions.Last(instruction => instruction.Mnemonic is Mnemonic.Test or Mnemonic.Cmp); - - protected override void CreateCondition(Instruction comparison) - { - var lastInstruction = Instructions.Last(); - - Condition = new X86InstructionGraphCondition(comparison, lastInstruction); - - } -} \ No newline at end of file diff --git a/Cpp2IL.Core/OutputFormats/IsilDumpOutputFormat.cs b/Cpp2IL.Core/OutputFormats/IsilDumpOutputFormat.cs index 3148bcfa..7fff88b3 100644 --- a/Cpp2IL.Core/OutputFormats/IsilDumpOutputFormat.cs +++ b/Cpp2IL.Core/OutputFormats/IsilDumpOutputFormat.cs @@ -20,7 +20,7 @@ public override void DoOutput(ApplicationAnalysisContext context, string outputR outputRoot = Path.Combine(outputRoot, "IsilDump"); var numAssemblies = context.Assemblies.Count; - var i = 0; + var i = 1; foreach (var assembly in context.Assemblies) { Logger.InfoNewline($"Processing assembly {i++} of {numAssemblies}: {assembly.Definition.AssemblyName.Name}", "IsilOutputFormat"); diff --git a/Cpp2IL.Plugin.ControlFlowGraph/ControlFlowGraphOutputFormat.cs b/Cpp2IL.Plugin.ControlFlowGraph/ControlFlowGraphOutputFormat.cs new file mode 100644 index 00000000..41e5655c --- /dev/null +++ b/Cpp2IL.Plugin.ControlFlowGraph/ControlFlowGraphOutputFormat.cs @@ -0,0 +1,222 @@ +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Model.Contexts; +using Cpp2IL.Core.Logging; +using Cpp2IL.Core.Utils; +using Cpp2IL.Core.Extensions; +using System.Text; +using Cpp2IL.Core.Graphs; +using DotNetGraph.Core; +using DotNetGraph.Extensions; +using DotNetGraph.Compilation; + +namespace Cpp2IL.Plugin.ControlFlowGraph; + +public class ControlFlowGraphOutputFormat : Cpp2IlOutputFormat +{ + public override string OutputFormatId => "cfg"; + public override string OutputFormatName => "CFG Dump"; + public override void DoOutput(ApplicationAnalysisContext context, string outputRoot) + { + outputRoot = Path.Combine(outputRoot, "CFGDump"); + + var numAssemblies = context.Assemblies.Count; + var i = 1; + foreach (var assembly in context.Assemblies) + { + Logger.InfoNewline($"Processing assembly {i++} of {numAssemblies}: {assembly.Definition.AssemblyName.Name}", "ControlFlowGraphOutputFormat"); + + var assemblyNameClean = assembly.CleanAssemblyName; + + MiscUtils.ExecuteParallel(assembly.Types, type => + { + if (type is InjectedTypeAnalysisContext) + return; + + if (type.Methods.Count == 0) + return; + + foreach (var method in type.Methods) + { + if (method is InjectedMethodAnalysisContext) + continue; + + try + { + method.Analyze(); + + if (method.ControlFlowGraph == null) + { + continue; + } + + WriteMethodGraph(outputRoot, method, GenerateGraph(method.ControlFlowGraph, method), assemblyNameClean).Wait(); + + method.ReleaseAnalysisData(); + } + catch (Exception e) + { + Logger.ErrorNewline("Method threw an exception while dumping - " + e.ToString()); + } + } + }); + } + } + + private string GenerateGraphTitle(MethodAnalysisContext context) + { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.Append("Type: "); + stringBuilder.Append(context.DeclaringType?.FullName); + stringBuilder.Append("\n"); + stringBuilder.Append("Method: "); + stringBuilder.Append(context.DefaultName); + stringBuilder.Append("\n"); + return stringBuilder.ToString(); + } + + public DotGraph GenerateGraph(ISILControlFlowGraph graph, MethodAnalysisContext method) + { + var directedGraph = new DotGraph() + .WithIdentifier("Graph") + .Directed() + .WithLabel(GenerateGraphTitle(method)); + + var nodeCache = new Dictionary(); + var edgeCache = new List(); + DotNode GetOrAddNode(int id) { + if (nodeCache.TryGetValue(id, out var node)) { + return node; + } + var newNode = new DotNode().WithIdentifier(id.ToString()); + directedGraph.Add(newNode); + nodeCache[id] = newNode; + return newNode; + } + DotEdge GetOrAddEdge(DotNode from, DotNode to) { + foreach (var edge in edgeCache) + { + if (edge.From == from.Identifier && edge.To == to.Identifier) { return edge; } + } + var newEdge = new DotEdge() + .From(from) + .To(to); + edgeCache.Add(newEdge); + directedGraph.Add(newEdge); + return newEdge; + } + + + foreach (var block in graph.Blocks) + { + var node = GetOrAddNode(block.ID); + if (block.BlockType == BlockType.Entry) + { + node.WithColor("green"); + node.WithLabel("Entry point"); + } + else if (block.BlockType == BlockType.Exit) + { + node.WithColor("red"); + node.WithLabel("Exit point"); + } + else + { + node.WithShape("box"); + node.WithLabel(block.ToString()); + } + foreach (var succ in block.Successors) + { + var target = GetOrAddNode(succ.ID); + GetOrAddEdge(node, target); + } + } + return directedGraph; + } + + private static string GetFilePathForMethod(string outputRoot, MethodAnalysisContext method, string assemblyNameClean) + { + TypeAnalysisContext type = method.DeclaringType; + + + + //Get root assembly directory + var assemblyDir = Path.Combine(outputRoot, assemblyNameClean); + + //If type is nested, we should use namespace of ultimate declaring type, which could be an arbitrary depth + //E.g. rewired has a type Rewired.Data.Mapping.HardwareJoystickMap, which contains a nested class Platform_Linux_Base, which contains MatchingCriteria, which contains ElementCount. + var ultimateDeclaringType = type; + while (ultimateDeclaringType.DeclaringType != null) + ultimateDeclaringType = ultimateDeclaringType.DeclaringType; + + var namespaceSplit = ultimateDeclaringType.Namespace!.Split('.'); + namespaceSplit = namespaceSplit + .Peek(n => MiscUtils.InvalidPathChars.ForEach(c => n = n.Replace(c, '_'))) + .Select(n => MiscUtils.InvalidPathElements.Contains(n) ? $"__illegalwin32name_{n}__" : n) + .ToArray(); + + //Ok so we have the namespace directory. Now we need to join all the declaring type hierarchy together for a filename. + var declaringTypeHierarchy = new List(); + var declaringType = type.DeclaringType; + while (declaringType != null) + { + declaringTypeHierarchy.Add(declaringType.Name!); + declaringType = declaringType.DeclaringType; + } + + //Reverse so we have top-level type first. + declaringTypeHierarchy.Reverse(); + + //Join the hierarchy together with _NestedType_ separators + string filename; + if (declaringTypeHierarchy.Count > 0) + filename = $"{string.Join("_NestedType_", declaringTypeHierarchy)}_NestedType_{type.Name}"; + else + filename = $"{type.Name}"; + + //Get directory from assembly root + namespace + var directory = Path.Combine(namespaceSplit.Prepend(assemblyDir).ToArray()); + if (!Directory.Exists(directory)) + Directory.CreateDirectory(directory); + + //Clean up the filename + MiscUtils.InvalidPathChars.ForEach(c => filename = filename.Replace(c, '_')); + + filename = Path.Combine(directory, filename); + + if (!Directory.Exists(filename)) + Directory.CreateDirectory(filename); + + + + var methodFileName = method.DefaultName; + + var parameters = string.Join("_", method.Parameters.Select(p => p.ParameterTypeContext.Name)); + if (parameters.Length > 0) + { + methodFileName += "_"; + methodFileName += parameters; + } + methodFileName += ".dot"; + + MiscUtils.InvalidPathChars.ForEach(c => methodFileName = methodFileName.Replace(c, '_')); + + + + //Combine the directory and filename + return Path.Combine(filename, methodFileName); + } + + private static async Task WriteMethodGraph(string outputRoot, MethodAnalysisContext method, DotGraph graph, string assemblyNameClean) + { + var file = GetFilePathForMethod(outputRoot, method, assemblyNameClean); + + // Library doesn't provide a non async option :( + await using var writer = new StringWriter(); + var context = new CompilationContext(writer, new CompilationOptions()); + await graph.CompileAsync(context); + var result = writer.GetStringBuilder().ToString(); + + // Save it to a file + File.WriteAllText(file, result); + } +} diff --git a/Cpp2IL.Plugin.ControlFlowGraph/ControlFlowGraphPlugin.cs b/Cpp2IL.Plugin.ControlFlowGraph/ControlFlowGraphPlugin.cs new file mode 100644 index 00000000..99db0876 --- /dev/null +++ b/Cpp2IL.Plugin.ControlFlowGraph/ControlFlowGraphPlugin.cs @@ -0,0 +1,18 @@ +using Cpp2IL.Plugin.ControlFlowGraph; +using Cpp2IL.Core.Api; +using Cpp2IL.Core.Attributes; + +[assembly: RegisterCpp2IlPlugin(typeof(ControlFlowGraphPlugin))] + +namespace Cpp2IL.Plugin.ControlFlowGraph; + +public class ControlFlowGraphPlugin : Cpp2IlPlugin +{ + public override string Name => "Control Flow Graph Plugin"; + public override string Description => "Adds an output format which generates control flow graph dot files for methods"; + public override void OnLoad() + { + OutputFormatRegistry.Register(); + Logger.VerboseNewline("Control Flow Graph Plugin loaded and output format registered."); + } +} diff --git a/Cpp2IL.Plugin.ControlFlowGraph/Cpp2IL.Plugin.ControlFlowGraph.csproj b/Cpp2IL.Plugin.ControlFlowGraph/Cpp2IL.Plugin.ControlFlowGraph.csproj new file mode 100644 index 00000000..fbddfdea --- /dev/null +++ b/Cpp2IL.Plugin.ControlFlowGraph/Cpp2IL.Plugin.ControlFlowGraph.csproj @@ -0,0 +1,22 @@ + + + + net7.0 + enable + enable + embedded + + + + + all + compile; build + runtime + + + + + + + + diff --git a/Cpp2IL.sln b/Cpp2IL.sln index 2fa414e0..612d5ed4 100644 --- a/Cpp2IL.sln +++ b/Cpp2IL.sln @@ -41,6 +41,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cpp2IL.Core.Tests", "Cpp2IL EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cpp2IL.Plugin.StrippedCodeRegSupport", "Cpp2IL.Plugin.StrippedCodeRegSupport\Cpp2IL.Plugin.StrippedCodeRegSupport.csproj", "{B2425628-0D69-44FA-AD34-997595512785}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cpp2IL.Plugin.ControlFlowGraph", "Cpp2IL.Plugin.ControlFlowGraph\Cpp2IL.Plugin.ControlFlowGraph.csproj", "{29683659-D4B0-444B-8D35-F35309535EE6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -87,6 +89,10 @@ Global {B2425628-0D69-44FA-AD34-997595512785}.Debug|Any CPU.Build.0 = Debug|Any CPU {B2425628-0D69-44FA-AD34-997595512785}.Release|Any CPU.ActiveCfg = Release|Any CPU {B2425628-0D69-44FA-AD34-997595512785}.Release|Any CPU.Build.0 = Release|Any CPU + {29683659-D4B0-444B-8D35-F35309535EE6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {29683659-D4B0-444B-8D35-F35309535EE6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29683659-D4B0-444B-8D35-F35309535EE6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29683659-D4B0-444B-8D35-F35309535EE6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE