diff --git a/.gitignore b/.gitignore index e7693ce..473b5a4 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,9 @@ x64/ [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* +# Pre-built LLVM binaries. +llvm-15.0.3-win64/ + *_i.c *_p.c *.ilk diff --git a/Dna.Example/Program.cs b/Dna.Example/Program.cs index 35a015d..81295d1 100644 --- a/Dna.Example/Program.cs +++ b/Dna.Example/Program.cs @@ -26,14 +26,14 @@ using Dna.Decompilation; using Dna.Structuring.Stackify; // Load the 64 bit PE file. // Note: This file is automatically copied to the build directory. -var path = @"ProtectedExecutable.bin"; -var binary = new WindowsBinary(64, File.ReadAllBytes(path), 0x140000000); +var path = @"C:\Users\Public\Documents\VMProtect\Code Markers\MSVC\x64\Release\Project1.vmp.exe"; +var binary = new WindowsBinary(64, File.ReadAllBytes(path)); // Instantiate dna. var dna = new Dna.Dna(binary); // Parse a (virtualized) control flow graph from the binary. -ulong funcAddr = 0x1400F2947; +ulong funcAddr = 0x14000177E; var cfg = dna.RecursiveDescent.ReconstructCfg(funcAddr); // The VM entry spans across multiple routines. To avoid disassembling multiple @@ -92,31 +92,39 @@ prompt(); var dotGraph = GraphVisualizer.GetDotGraph(liftedCfg); File.WriteAllText("graph.dot", dotGraph.Compile(false, false)); -bool emulate = true; - +bool emulate = false; if(emulate) { // Load the binary into unicorn engine. var emulator = new UnicornEmulator(architecture); - PEMapper.MapBinary(emulator, binary); + //PEMapper.MapBinary(emulator, binary); ulong tebBase = 0x7fded000; ulong tebSize = 0x10000; emulator.MapMemory(tebBase, (int)tebSize); - emulator.MapMemory(0, (int)tebSize); + //emulator.MapMemory(0, (int)tebSize); - var igt = new GdtHelper(emulator.Emulator, 0x0, 0x1000); - igt.Setup(tebBase); + //var igt = new GdtHelper(emulator.Emulator, 0x0, 0x1000); + //igt.Setup(tebBase); // Setup the stack. ulong rsp = 0x100000000; emulator.MapMemory(rsp, 0x1000 * 1200); rsp += 0x20000; + ulong pebAddr = 0x9A67F99120; + emulator.MapMemory(pebAddr - 0x120, 0x1000); + UInt16 val = 0x4A63; + emulator.WriteMemory(pebAddr, BitConverter.GetBytes(val)); - //emulator.MapMemory(0, 0x1000 * 10); + int theSize = 0x1000 * 10; + emulator.MapMemory(0, theSize); + for(int i = 0; i < theSize; i++) + { + // emulator.WriteMemory((ulong)i, new byte[] { 0xFF }); + } //ulong gs = 0x1000; //emulator.MapMemory(0, 0x1000 * 1000); //emulator.SetRegister(register_e.ID_REG_X86_GS, 0); @@ -124,14 +132,14 @@ if(emulate) emulator.SetRegister(register_e.ID_REG_X86_RSP, rsp); emulator.SetRegister(register_e.ID_REG_X86_RBP, rsp); - emulator.SetRegister(register_e.ID_REG_X86_RIP, 0x14000177E); + //emulator.SetRegister(register_e.ID_REG_X86_RIP, 0x14000177E); // Execute the function. emulator.Start(0x14000177E); } -Thread.Sleep(100000); +Console.WriteLine("Started"); // Lift the control flow graph to LLVM IR. diff --git a/Dna.LLVMInterop/Dna.LLVMInterop.vcxproj b/Dna.LLVMInterop/Dna.LLVMInterop.vcxproj new file mode 100644 index 0000000..e906af6 --- /dev/null +++ b/Dna.LLVMInterop/Dna.LLVMInterop.vcxproj @@ -0,0 +1,172 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {e6a58095-9b8b-4830-bfea-2947c4f2681a} + DnaLLVMInterop + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + ClangCL + Unicode + + + DynamicLibrary + false + ClangCL + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + C:\Users\colton\source\repos\Dna\llvm-15.0.3-win64\include;$(IncludePath) + C:\Users\colton\source\repos\Dna\llvm-15.0.3-win64\lib;$(LibraryPath) + + + C:\Users\colton\source\repos\Dna\llvm-15.0.3-win64\include;$(IncludePath) + C:\Users\colton\source\repos\Dna\llvm-15.0.3-win64\lib;$(LibraryPath) + + + + Level3 + true + WIN32;_DEBUG;DNALLVMINTEROP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + WIN32;NDEBUG;DNALLVMINTEROP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + true + true + false + + + + + Level3 + true + _DEBUG;DNALLVMINTEROP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + stdcpplatest + stdc17 + + + Windows + DebugFastLink + false + true + LLVM-C.lib;LLVMAggressiveInstCombine.lib;LLVMAnalysis.lib;LLVMAsmParser.lib;LLVMAsmPrinter.lib;LLVMBinaryFormat.lib;LLVMBitReader.lib;LLVMBitstreamReader.lib;LLVMBitWriter.lib;LLVMCFGuard.lib;LLVMCFIVerify.lib;LLVMCodeGen.lib;LLVMCore.lib;LLVMCoroutines.lib;LLVMCoverage.lib;LLVMDebugInfoCodeView.lib;LLVMDebuginfod.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoGSYM.lib;LLVMDebugInfoMSF.lib;LLVMDebugInfoPDB.lib;LLVMDemangle.lib;LLVMDiff.lib;LLVMDlltoolDriver.lib;LLVMDWARFLinker.lib;LLVMDWP.lib;LLVMExecutionEngine.lib;LLVMExegesis.lib;LLVMExegesisAArch64.lib;LLVMExegesisMips.lib;LLVMExegesisPowerPC.lib;LLVMExegesisX86.lib;LLVMExtensions.lib;LLVMFileCheck.lib;LLVMFrontendOpenACC.lib;LLVMFrontendOpenMP.lib;LLVMFuzzerCLI.lib;LLVMFuzzMutate.lib;LLVMGlobalISel.lib;LLVMInstCombine.lib;LLVMInstrumentation.lib;LLVMInterfaceStub.lib;LLVMInterpreter.lib;LLVMipo.lib;LLVMIRReader.lib;LLVMJITLink.lib;LLVMLibDriver.lib;LLVMLineEditor.lib;LLVMLinker.lib;LLVMLTO.lib;LLVMMC.lib;LLVMMCA.lib;LLVMMCDisassembler.lib;LLVMMCJIT.lib;LLVMMCParser.lib;LLVMMIRParser.lib;LLVMNVPTXCodeGen.lib;LLVMNVPTXDesc.lib;LLVMNVPTXInfo.lib;LLVMObjCARCOpts.lib;LLVMObjCopy.lib;LLVMObject.lib;LLVMObjectYAML.lib;LLVMOption.lib;LLVMOrcJIT.lib;LLVMOrcShared.lib;LLVMOrcTargetProcess.lib;LLVMPasses.lib;LLVMProfileData.lib;LLVMRemarks.lib;LLVMRuntimeDyld.lib;LLVMScalarOpts.lib;LLVMSelectionDAG.lib;LLVMSupport.lib;LLVMSymbolize.lib;LLVMSystemZAsmParser.lib;LLVMSystemZCodeGen.lib;LLVMSystemZDesc.lib;LLVMSystemZDisassembler.lib;LLVMSystemZInfo.lib;LLVMTableGen.lib;LLVMTableGenGlobalISel.lib;LLVMTarget.lib;LLVMTextAPI.lib;LLVMTransformUtils.lib;LLVMVEAsmParser.lib;LLVMVECodeGen.lib;LLVMVectorize.lib;LLVMVEDesc.lib;LLVMVEDisassembler.lib;LLVMVEInfo.lib;LLVMWindowsDriver.lib;LLVMWindowsManifest.lib;LLVMX86AsmParser.lib;LLVMX86CodeGen.lib;LLVMX86Desc.lib;LLVMX86Disassembler.lib;LLVMX86Info.lib;LLVMX86TargetMCA.lib;LLVMXCoreCodeGen.lib;LLVMXCoreDesc.lib;LLVMXCoreDisassembler.lib;LLVMXCoreInfo.lib;LLVMXRay.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;DNALLVMINTEROP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + stdcpplatest + stdc17 + + + Windows + true + true + DebugFastLink + false + LLVM-C.lib;LLVMAggressiveInstCombine.lib;LLVMAnalysis.lib;LLVMAsmParser.lib;LLVMAsmPrinter.lib;LLVMBinaryFormat.lib;LLVMBitReader.lib;LLVMBitstreamReader.lib;LLVMBitWriter.lib;LLVMCFGuard.lib;LLVMCFIVerify.lib;LLVMCodeGen.lib;LLVMCore.lib;LLVMCoroutines.lib;LLVMCoverage.lib;LLVMDebugInfoCodeView.lib;LLVMDebuginfod.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoGSYM.lib;LLVMDebugInfoMSF.lib;LLVMDebugInfoPDB.lib;LLVMDemangle.lib;LLVMDiff.lib;LLVMDlltoolDriver.lib;LLVMDWARFLinker.lib;LLVMDWP.lib;LLVMExecutionEngine.lib;LLVMExegesis.lib;LLVMExegesisAArch64.lib;LLVMExegesisMips.lib;LLVMExegesisPowerPC.lib;LLVMExegesisX86.lib;LLVMExtensions.lib;LLVMFileCheck.lib;LLVMFrontendOpenACC.lib;LLVMFrontendOpenMP.lib;LLVMFuzzerCLI.lib;LLVMFuzzMutate.lib;LLVMGlobalISel.lib;LLVMInstCombine.lib;LLVMInstrumentation.lib;LLVMInterfaceStub.lib;LLVMInterpreter.lib;LLVMipo.lib;LLVMIRReader.lib;LLVMJITLink.lib;LLVMLibDriver.lib;LLVMLineEditor.lib;LLVMLinker.lib;LLVMLTO.lib;LLVMMC.lib;LLVMMCA.lib;LLVMMCDisassembler.lib;LLVMMCJIT.lib;LLVMMCParser.lib;LLVMMIRParser.lib;LLVMNVPTXCodeGen.lib;LLVMNVPTXDesc.lib;LLVMNVPTXInfo.lib;LLVMObjCARCOpts.lib;LLVMObjCopy.lib;LLVMObject.lib;LLVMObjectYAML.lib;LLVMOption.lib;LLVMOrcJIT.lib;LLVMOrcShared.lib;LLVMOrcTargetProcess.lib;LLVMPasses.lib;LLVMProfileData.lib;LLVMRemarks.lib;LLVMRuntimeDyld.lib;LLVMScalarOpts.lib;LLVMSelectionDAG.lib;LLVMSupport.lib;LLVMSymbolize.lib;LLVMSystemZAsmParser.lib;LLVMSystemZCodeGen.lib;LLVMSystemZDesc.lib;LLVMSystemZDisassembler.lib;LLVMSystemZInfo.lib;LLVMTableGen.lib;LLVMTableGenGlobalISel.lib;LLVMTarget.lib;LLVMTextAPI.lib;LLVMTransformUtils.lib;LLVMVEAsmParser.lib;LLVMVECodeGen.lib;LLVMVectorize.lib;LLVMVEDesc.lib;LLVMVEDisassembler.lib;LLVMVEInfo.lib;LLVMWindowsDriver.lib;LLVMWindowsManifest.lib;LLVMX86AsmParser.lib;LLVMX86CodeGen.lib;LLVMX86Desc.lib;LLVMX86Disassembler.lib;LLVMX86Info.lib;LLVMX86TargetMCA.lib;LLVMXCoreCodeGen.lib;LLVMXCoreDesc.lib;LLVMXCoreDisassembler.lib;LLVMXCoreInfo.lib;LLVMXRay.lib;%(AdditionalDependencies) + + + + + + + + + + + + true + + + + + + \ No newline at end of file diff --git a/Dna.LLVMInterop/Dna.LLVMInterop.vcxproj.filters b/Dna.LLVMInterop/Dna.LLVMInterop.vcxproj.filters new file mode 100644 index 0000000..fb1620b --- /dev/null +++ b/Dna.LLVMInterop/Dna.LLVMInterop.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + Passes + + + + + + Passes + + + + + {2aee77a2-525d-4f67-b255-9daecc2522a0} + + + {ce61e1b2-6444-4d20-b2ac-8d89cdc72726} + + + + + + \ No newline at end of file diff --git a/Dna.LLVMInterop/Passes/ControlledNodeSplittingPass.cpp b/Dna.LLVMInterop/Passes/ControlledNodeSplittingPass.cpp new file mode 100644 index 0000000..f62a356 --- /dev/null +++ b/Dna.LLVMInterop/Passes/ControlledNodeSplittingPass.cpp @@ -0,0 +1,1265 @@ +/****************************************************************************** +* Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of NVIDIA CORPORATION nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ControlledNodeSplittingPass.h" + +// #define DUMP_LIMITGRAPHS + +// This pass implements "Making Graphs Reducible with Controlled Node Splitting" +// by Johan Janssen and Henk Corporaal. + +namespace llvm { + namespace sl { + // forward + class CNSFunction; + class Node; + + // A node in the CNS function graph. + class Node : public ilist_node_with_parent { + friend class CNSLimitGraph; + friend class CNSFunction; + + public: + typedef std::vector NodeList; + typedef std::vector BB_list; + + /// Flags for SCC. + enum Flags { + ON_STACK = 1 << 0, + ON_LOOP = 1 << 1, + IS_HEADER = 1 << 2, + }; + + public: + /// Default constructor. + Node() + : m_parent(nullptr) + , m_id(0) + , m_copied_from(0) + , m_visit_count(0) + , m_dfs_num(0) + , m_low(0) + , m_flags(0) + { + } + + /// Constructor from a basic block. + /// + /// \param parent the owner function + /// \param id the unique identifier of this node + /// \param bb the basic block associated with this node + Node( + CNSFunction* parent, + size_t id, + BasicBlock* bb) + : m_parent(parent) + , m_id(id) + , m_copied_from(0) + , m_visit_count(0) + , m_dfs_num(0) + , m_low(0) + , m_flags(0) + { + add_bb(bb); + } + + /// Constructor (used by copy). + /// + /// \param parent the owner function + /// \param id the unique identifier of this node + /// \param copied_from the unique identifier from the node we copy + Node( + CNSFunction* parent, + size_t id, + size_t copied_from) + : m_parent(parent) + , m_id(id) + , m_copied_from(copied_from) + , m_visit_count(0) + , m_dfs_num(0) + , m_low(0) + , m_flags(0) + { + } + + public: + /// Get the parent function. + CNSFunction* getParent() { return m_parent; } + + /// Set the parent function. + void setParent(CNSFunction* parent) { m_parent = parent; } + + /// Get the unique ID of this node. + size_t get_id() const { return m_id; } + + /// Get the unique ID of the "original" node this node was copied from (if != zero). + size_t copied_from() const { return m_copied_from; } + + /// Add a basic block to the list of basic blocks. + /// + /// \param bb the block to add + void add_bb(BasicBlock* BB) { + m_blocks.push_back(BB); + } + + /// Add a predecessor edge to node pred. + /// + /// \param pred the predecessor node + void add_pred(Node* pred) { + if (pred != this) { + if (std::find(m_preds.begin(), m_preds.end(), pred) == m_preds.end()) { + m_preds.push_back(pred); + } + } + } + + /// Get all predecessors. + NodeList& preds() { return m_preds; } + + /// Get all predecessors. + NodeList const& preds() const { return m_preds; } + + /// Get the number of predecessors. + size_t pred_size() const { return m_preds.size(); } + + /// Get the predecessor at index i. + /// + /// \param i the index of the predecessor node + Node* get_pred(size_t i) { return m_preds[i]; } + + /// Erase the predecessor at index i. + /// + /// \param i the index of the predecessor node to erase + void pred_erase(size_t i) { + m_preds.erase(m_preds.begin() + i); + } + + /// Erase a given predecessor. + /// + /// \param pred the predecessor node to erase + void pred_erase(Node* pred) { + m_preds.erase(std::find(m_preds.begin(), m_preds.end(), pred)); + } + + /// Add a successor edge to node succ. + /// + /// \param succ the successor node to add + void add_succ(Node* succ) { + if (succ != this) { + if (std::find(m_succs.begin(), m_succs.end(), succ) == m_succs.end()) { + m_succs.push_back(succ); + } + } + } + + /// Get all successors. + NodeList& succs() { return m_succs; } + + /// Get all successors. + NodeList const& succs() const { return m_succs; } + + /// Get the number of successors. + size_t succ_size() const { return m_succs.size(); } + + /// Get the successor at index i. + /// + /// \param i the index of the successor node + Node* get_succ(size_t i) const { return m_succs[i]; } + + /// Erase the successor at index i. + /// + /// \param i the index of the successor node to erase + void succ_erase(size_t i) { + m_succs.erase(m_succs.begin() + i); + } + + /// Erase a given successor. + /// + /// \param succ the successor node to erase + void succ_erase(Node* succ) { + m_succs.erase(std::find(m_succs.begin(), m_succs.end(), succ)); + } + + /// Get an iterator to the first block of this node. + BB_list::iterator blocks_begin() { return m_blocks.begin(); } + + /// Get an iterator to the end block of this node. + BB_list::iterator blocks_end() { return m_blocks.end(); } + + /// Get the basic blocks. + BB_list& blocks() { return m_blocks; } + + /// Mark a node on the stack. + /// + /// \param flag if TRUE, mark this node laying on stack + void markOnStack(bool flag) { + if (flag) { + m_flags |= ON_STACK; + } + else { + m_flags &= ~ON_STACK; + } + } + + /// Check if we this node is inside the stack. + bool onStack() const { return (m_flags & ON_STACK) != 0; } + + /// Mark a node inside a loop. + /// + /// \param flag if TRUE, this node is inside a loop + void markLoop(bool flag) { + if (flag) { + m_flags |= ON_LOOP; + } + else { + m_flags &= ~ON_LOOP; + } + } + + /// Check if this node is inside a loop. + bool is_loop() const { return (m_flags & ON_LOOP) != 0; } + + /// Mark this node inside a loop as a header. + /// + /// \param flag if TRUE, this node is a loop header + void mark_loop_head(bool flag) { if (flag) m_flags |= IS_HEADER; else m_flags &= ~IS_HEADER; } + + /// Check if this node is a loop header. + bool is_loop_head() const { return (m_flags & IS_HEADER) != 0; } + + void printAsOperand(raw_ostream& OS, bool flag) {} + + private: + /// The (function) parent. + CNSFunction* m_parent; + + /// Unique ID of this node, != zero. + size_t m_id; + + /// If != zero, the ID of the original node that was copied into this one. + size_t m_copied_from; + + /// Visit count for traversal. + size_t m_visit_count; + + /// Predecessor edges (dependency). + NodeList m_preds; + + /// Successor edges (control flow). + NodeList m_succs; + + /// Set of basic blocks in this node. + BB_list m_blocks; + + // --------- SCC helper -------------- + + /// DFS number this node was reached first. + size_t m_dfs_num; + + /// Minimum DFS number over this node and successors. + size_t m_low; + + /// Node flags + unsigned m_flags; + }; + + // The dominator tree over the limit graph. + typedef DomTreeBase LGDominatorTree; + + /// Wrapper class for a function. + class CNSFunction { + public: + typedef std::list NodeList; + + public: + /// Constructor. + /// + /// \param func the associated LLVM function + CNSFunction(Function& func) + : m_func(func) + , m_node_id(0) + { + // create a Block node for every basic block + for (BasicBlock& BB : m_func) { + createNode(&BB); + } + + // now connect them + for (BasicBlock& BB : m_func) { + Node* curr = m_mapping[&BB]; + + for (BasicBlock* p_bb : predecessors(&BB)) { + Node* pred = m_mapping[p_bb]; + curr->add_pred(pred); + } + for (BasicBlock* s_bb : successors(&BB)) { + Node* succ = m_mapping[s_bb]; + curr->add_succ(succ); + } + } + + // the start block + m_root = m_mapping[&*m_func.begin()]; + } + + /// Destructor. + ~CNSFunction() { + while (!m_node_list.empty()) { + Node* n = m_node_list.front(); + m_node_list.pop_front(); + delete n; + } + } + + NodeList::iterator begin() { return m_node_list.begin(); } + NodeList::const_iterator begin() const { return m_node_list.begin(); } + + NodeList::iterator end() { return m_node_list.end(); } + NodeList::const_iterator end() const { return m_node_list.end(); } + + Node* front() { return *m_node_list.begin(); } + Node const* front() const { return *m_node_list.begin(); } + + size_t size() const { return m_node_list.size(); } + + /// Get the LLVM function object. + Function& getFunction() { + return m_func; + } + + /// Get the name of this function. + StringRef getName() { + return m_func.getName(); + } + + /// Create a new node in the graph. + /// + /// \param bb the basic block for which the node is created + Node* createNode(BasicBlock* bb); + + /// Create a new Node as a copy of another Node. + /// + /// \param other the other node + Node* createNode(Node const* other); + + /// Delete a node from this graph. + /// + /// \param n the node to delete + void dropNode(Node* n); + + /// Update the mapping of a basic block to a Node. + /// + /// \param bb the basic block + /// \param new_node the newly associated node + void updateMapping(BasicBlock* bb, Node* new_node) { + m_mapping[bb] = new_node; + } + + private: + /// The LLVM function. + Function& m_func; + + /// last used node ID. + size_t m_node_id; + + /// The top level node. + Node* m_root; + + /// The map from basic blocks to nodes. + std::map m_mapping; + + /// The list of all statement nodes. + NodeList m_node_list; + }; + + /// A Limit Graph. + class CNSLimitGraph + { + /// A backedge. + struct BackEdge { + BackEdge(Node const* S, Node const* D) : src(S), dst(D) {} + + Node const* src; + Node const* dst; + }; + + /// Less operator for Backedges. + struct EdgeLess { + bool operator()(BackEdge const& a, BackEdge const& b) const { + if (a.src < b.src) { + return true; + } + if (a.src == b.src) { + return a.dst < b.dst; + } + return false; + } + }; + + typedef std::set BackEdgeSet; + + public: + typedef std::list WorkList; + typedef std::set NodeSet; + + public: + /// Constructor. + /// + /// \param func the function for which the graph is constructed + CNSLimitGraph( + CNSFunction& func); + + /// Destructor. + ~CNSLimitGraph(); + + /// Remove irreducible control flow using Controlled Node Splitting. + bool removeIrreducibleControlFlow(); + + WorkList::iterator nodes_begin() { + return m_work_list.begin(); + } + + WorkList::iterator nodes_end() { + return m_work_list.end(); + } + + size_t nodes_size() const { + return m_work_list.size(); + } + + /// Get the start node of the limit graph. + Node* get_start() const { + return m_start; + } + + private: + /// Build the limit graph from a function. + void buildFromFunction(); + + /// Apply T2 transformations until limit graph is reached, T1 is done "automatically". + void applyT2(); + + /// Recompile the dominance info for the limit graph. + void recalculateDominance(CNSFunction& func); + + /// Helper for SCC calculation. + void doDFS(Node* node); + + /// Computes the split node candidate set using the CNS approach. + NodeSet computeCNSCandidates(); + + typedef std::set SCC; + + /// Process a strongly connected component. + /// + /// \param scc a strongly connected component of the graph + Node* processSCC(SCC const& scc); + + /// Calculate all SED-sets from one loop. + void calcSEDsForLoop(SCC const& scc); + + /// Dump the current graph. + /// + /// \param baseName base name of the graph + /// \param dumpID additional identifier of the dump + void dumpLimitGraph( + std::string const& baseName, + size_t dumpID); + + /// Check if the edge from S->D is a backedge. + /// + /// \param S the source node of the edge + /// \param D the destination node of the edge + bool isBackedge(Node const* S, Node const* D) { + return m_backedges.find(BackEdge(S, D)) != m_backedges.end(); + } + + /// Mark the edge from S->D as a backedge. + /// + /// \param S the source node of the edge + /// \param D the destination node of the edge + void mark_backedge(Node* S, Node* D) { + m_backedges.insert(BackEdge(S, D)); + } + + private: + /// Used for scc calculation. + size_t m_next_dfs_num; + + /// current visit count + size_t m_visit_count; + + /// The node stack. + std::stack m_stack; + + /// The processed function. + CNSFunction& m_func; + + /// The current work list. + WorkList m_work_list; + + /// The set of backedges. + BackEdgeSet m_backedges; + + Node* m_start; + + // --------- Dominance -------------- + LGDominatorTree* m_DT; + + // --------- SED-sets -------------- + typedef std::vector NodeVector; + + typedef std::vector CandidateSEDs; + CandidateSEDs m_candidate_sets; + + typedef std::vector Loops; + Loops m_all_loops; + }; + + } // namespace sl + + template <> + struct GraphTraits { + typedef sl::Node NodeType; + typedef sl::Node* NodeRef; + typedef sl::Node::NodeList::iterator ChildIteratorType; + + static NodeType* getEntryNode(NodeType* N) { return N; } + + static inline ChildIteratorType child_begin(NodeType* N) { + return N->succs().begin(); + } + static inline ChildIteratorType child_end(NodeType* N) { + return N->succs().end(); + } + }; + + template <> + struct GraphTraits > { + typedef sl::Node NodeType; + typedef sl::Node* NodeRef; + typedef sl::Node::NodeList::iterator ChildIteratorType; + + static NodeType* getEntryNode(NodeType* N) { return N; } + + static inline ChildIteratorType child_begin(NodeType* N) { + return N->preds().begin(); + } + static inline ChildIteratorType child_end(NodeType* N) { + return N->preds().end(); + } + }; + + template<> + struct GraphTraits : public GraphTraits { + typedef sl::CNSFunction GraphType; + typedef sl::Node NodeType; + typedef sl::CNSFunction::NodeList::iterator ChildIteratorType; + + // Return the entry node of the graph + static NodeType* getEntryNode(GraphType* G) { return G->front(); } + + // nodes_iterator/begin/end - Allow iteration over all nodes in the graph + typedef sl::CNSFunction::NodeList::iterator nodes_iterator; + + static nodes_iterator nodes_begin(GraphType* G) { + return G->begin(); + } + + static nodes_iterator nodes_end(GraphType* G) { + return G->end(); + } + + // Return total number of nodes in the graph + static size_t size(GraphType* G) { + return G->size(); + } + }; + + namespace sl { + + // ----------------------------------------------------------------------------------------------- + + // Constructor. + CNSLimitGraph::CNSLimitGraph( + CNSFunction& func) + : m_next_dfs_num(0) + , m_visit_count(0) + , m_func(func) + , m_start(NULL) + { + m_DT = new LGDominatorTree; + } + + // Destructor. + CNSLimitGraph::~CNSLimitGraph() + { + delete m_DT; + } + + // Create a new node in the graph. + Node* CNSFunction::createNode(BasicBlock* BB) + { + Node* n = new Node(this, ++m_node_id, BB); + m_node_list.push_back(n); + m_mapping[BB] = n; + + return n; + } + + // Create a new Node as a copy of another Node. + Node* CNSFunction::createNode(Node const* other) + { + size_t copied = other->m_copied_from; + if (copied == 0) { + copied = other->m_id; + } + + Node* n = new Node(this, ++m_node_id, copied); + m_node_list.push_back(n); + return n; + } + + // Delete a node. + void CNSFunction::dropNode(Node* n) + { + /* + new (n) Node(); + m_child_list.push_back(n); + */ + } + + // Build the limit graph from a function. + void CNSLimitGraph::buildFromFunction() + { + for (Node* node : m_func) { + m_work_list.push_back(node); + } + + // the start block + m_start = m_func.front(); + } + + /// Apply T2 transformations until limit graph is reached, T1 is done "automatically". + void CNSLimitGraph::applyT2() + { + for (;;) { + // dbgs() << "Nodes in graph: " << m_work_list.size() << '\n'; + size_t removed = 0; + + // process the worklist + for (WorkList::iterator it = m_work_list.begin(), end(m_work_list.end()); it != end;) { + Node* curr = *it; + // T2: Merge node into single predecessor + if (curr->pred_size() == 1) { + // this node has only ONE predecessor, merge them + Node* pred = curr->get_pred(0); + // dbgs() << " - " << pred->id << " -> " << cur->id << '\n'; + pred->succ_erase(curr); + + for (BasicBlock* BB : curr->blocks()) { + pred->add_bb(BB); + + // update mapping to one valid node (BB may be part of multiple nodes!) + m_func.updateMapping(BB, pred); + } + + for (Node* succ : curr->succs()) { + // dbgs() << " + " << pred->id << " -> " << succ->id << '\n'; + succ->pred_erase(curr); + succ->add_pred(pred); + pred->add_succ(succ); + } + + ++removed; + it = m_work_list.erase(it); + m_func.dropNode(curr); + } + else { + ++it; + } + } + + // dbgs() << "removed: " << removed << '\n'; + if (removed == 0) { + // stop if the complete list was processed and no node could be removed + break; + } + } + // dbgs() << "Remaining: " << m_work_list.size() << '\n'; + // dbgs() << "----------\n"; + } + + // Recompile the dominance info for the limit graph. + void CNSLimitGraph::recalculateDominance(CNSFunction& func) + { + m_DT->recalculate(func); + } + + // helper for SCC calculation. + void CNSLimitGraph::doDFS(Node* node) + { + if (m_visit_count <= node->m_visit_count) { + return; + } + node->m_visit_count = m_visit_count; + + node->m_dfs_num = m_next_dfs_num++; + node->m_low = node->m_dfs_num; + + m_stack.push(node); + node->markOnStack(true); + + for (Node* succ : node->succs()) { + if (isBackedge(node, succ)) { + continue; + } + + // visit the successor + doDFS(succ); + if (succ->onStack()) { + node->m_low = std::min(succ->m_low, node->m_low); + } + } + + if (node->m_low == node->m_dfs_num) { + Node* x = NULL; + + // found SCC + SCC scc; + do { + x = m_stack.top(); + m_stack.pop(); + scc.insert(x); + } while (x != node); + + size_t size = scc.size(); + if (size == 1) { + // single entry scc, no loop (keep in mind we ignore self loop edges) + x = *scc.begin(); + x->markLoop(false); + x->markOnStack(false); + } + else { + // found a scc of more than one node + Node* head = processSCC(scc); + + if (head != NULL) { + // search for inner loops + doDFS(head); + } + } + } + } + + // Calculate the strongly coupled components. + CNSLimitGraph::NodeSet CNSLimitGraph::computeCNSCandidates() + { + // clear the candidate sets + m_candidate_sets.clear(); + m_all_loops.clear(); + + ++m_visit_count; + m_next_dfs_num = 0; + + m_backedges.clear(); + + doDFS(m_start); + +#if 0 + for (auto& s : m_candidate_sets) { + dbgs() << "SET ["; + for (Node* n : s) { + dbgs() << " " << n->get_id(); + } + dbgs() << " ]\n"; + } +#endif + + NodeSet candidates; + + // put all nodes in the SED-sets into the candidate set + for (auto& s : m_candidate_sets) { + candidates.insert(s.begin(), s.end()); + } + + NodeSet result(candidates); + + for (Node* n : candidates) { + Node* idom = m_DT->getNode(n)->getIDom()->getBlock(); + + if (result.find(idom) != result.end()) { + // idom is still a valid candidate, search all loops + // if idom is in the same loop as n, then idom is an RC + // and must be removed + for (auto& loop : m_all_loops) { + if (loop.find(n) == loop.end()) { + continue; + } + if (loop.find(idom) == loop.end()) { + continue; + } + + result.erase(idom); + break; + } + } + } + +#if 0 + dbgs() << "Candidates for splitting are: ["; + for (Node* n : result) { + dbgs() << " " << n->get_id(); + } + dbgs() << " ]\n"; +#endif + + return result; + } + + // process a strongly connected component. + Node* CNSLimitGraph::processSCC(SCC const& scc) + { +#if 0 + dbgs() << "Loop"; + for (Node* node : scc) { + dbgs() << " " << unsigned(node->get_id()); + } + dbgs() << "\n"; +#endif + + Node* head = NULL; + for (Node* node : scc) { + node->markLoop(true); + + bool some_outside = false, some_inside = false; + + size_t max_dfs_num = 0; + Node* max_P = NULL; + + for (Node* P : node->preds()) { + if (isBackedge(P, node)) { + continue; + } + + if (scc.find(P) == scc.end()) { + some_outside = true; + } + else { + some_inside = true; + + size_t dfs_num = P->m_dfs_num; + if (dfs_num > max_dfs_num) { + max_dfs_num = dfs_num; + max_P = P; + } + } + } + if (some_inside && some_outside) { + // found one successor outside the loop, this is a head + node->mark_loop_head(true); + +#if 0 + dbgs() << "Head is " << node->get_id() << "\n"; + dbgs() << "Backedge " << max_P->get_id() << "==>" << node->get_id() << "\n"; +#endif + mark_backedge(max_P, node); + + head = node; + break; + } + } + + // clean up and mark + for (Node* node : scc) { + node->markLoop(true); + node->markOnStack(false); + + // set the visit count to zero here ensures that nodes on this loop are + // visited again to find inner loops + node->m_visit_count = 0; + } + + calcSEDsForLoop(scc); + + return head; + } + + // Calculate all SED-sets from one loop. + void CNSLimitGraph::calcSEDsForLoop(SCC const& scc) + { + typedef std::map SED_sets; + + SED_sets sets; + + for (Node* n : scc) { + Node* idom = m_DT->getNode(n)->getIDom()->getBlock(); + + if (scc.find(idom) == scc.end()) { + // this block has a dominator outside of this loop + if (sets.find(idom) == sets.end()) { + // first hit + NodeVector s; + sets[idom] = s; + } + sets[idom].push_back(n); + } + } + + // safe SED-sets ... + for (auto v : sets) { + m_candidate_sets.push_back(v.second); + } + + // ... and loops + m_all_loops.push_back(scc); + } + + // Remove irreducible control flow using Controlled Node Splitting. + bool CNSLimitGraph::removeIrreducibleControlFlow() + { + size_t numBlocks = 0; + size_t numInsts = 0; + + bool changed = false; + + // test reducibility +#if 0 + dbgs() << "Computing limit graph for " << m_func.getName() << "\n"; + dbgs() << "------------------------------------\n"; +#endif + buildFromFunction(); + + size_t dumpID = 0; + + for (;;) { + dumpLimitGraph(m_func.getName().str(), dumpID++); + + applyT2(); + if (m_work_list.size() == 1) { + // the whole graph was collapsed into one node, it IS reducible + break; + } + + changed = true; + + // for now, we recalculate the dominance at every run + // it *might* be possible to update it ... + recalculateDominance(m_func); + + NodeSet candidates = computeCNSCandidates(); + + dumpLimitGraph(m_func.getName().str(), dumpID++); + + // Select node for splitting: So far use the one with less number of instructions + Node* best = NULL; + size_t bestNum = 1 << 30; + // dbgs() << "Candidates:\n"; + for (Node* N : candidates) { + size_t num = 0; + for (BasicBlock* B : N->blocks()) { + num += B->size(); + } +#if 0 + dbgs() << " " << N->get_id() + << " : " << num + << " * " << (N->pred_size() - 1) + << " = " << num * (N->pred_size() - 1) << "\n"; +#endif + num *= (N->pred_size() - 1); + if (num < bestNum) { + best = N; + bestNum = num; + } + } + + // Split block + m_work_list.remove(best); + +#if 0 + dbgs() << "Splitting id = " << best->get_id() + << " into " << best->pred_size() + << " copies (" << bestNum + << " more instructions and " << best->blocks().size() * (best->pred_size() - 1) + << " more blocks)\n"; +#endif + numBlocks += best->blocks().size() * (best->pred_size() - 1); + numInsts += bestNum; + + for (Node* S : best->succs()) { + S->pred_erase(best); + } + + // Turn values which escape their blocks into variables to avoid problems during cloning + std::set to_demote; + for (BasicBlock* BB : best->blocks()) { + for (Instruction& inst : *BB) { + // A value escapes when a use is in another block or is a phi node + for (Instruction::use_iterator UI = inst.use_begin(), UE = inst.use_end(); + UI != UE; + ++UI) + { + Instruction* useI = cast(*UI); + if (useI->getParent() != BB || isa(useI)) { + // dbgs() << "Demoting value: " << inst << '\n'; + to_demote.insert(&inst); + break; + } + } + } + } + + for (Instruction* inst : to_demote) { + DemoteRegToStack(*inst, /*VolatileLoads=*/false); + } + + // Create one clone of the best node for each predecessor + for (Node* P : best->preds()) { + // Create a new node with P as only predecessor and all original successors + // dbgs() << "Creating clone for id: " << P->get_id() << '\n'; + Node* newBlock = m_func.createNode(best); + newBlock->add_pred(P); + newBlock->succs() = best->succs(); + + m_work_list.push_back(newBlock); + + // Update adjacent nodes + P->succ_erase(best); + P->add_succ(newBlock); + for (Node* S : best->succs()) { + S->add_pred(newBlock); + } + + // Clone all basic blocks + ValueToValueMapTy valueMap; + for (BasicBlock* BB : best->blocks()) { + // dbgs() << "Clone block: " << BB->getName() << '\n'; + BasicBlock* NB = CloneBasicBlock(BB, valueMap, "", &m_func.getFunction()); + valueMap[BB] = NB; + newBlock->add_bb(NB); + } + + // Remap operands of instructions in cloned blocks to cloned values, if available + for (BasicBlock* BB : best->blocks()) { + BasicBlock* clonedBlock = cast(valueMap[BB]); + for (Instruction& Instr : *clonedBlock) { + RemapInstruction( + &Instr, + valueMap, + RF_NoModuleLevelChanges | + RF_IgnoreMissingLocals | + RF_NullMapMissingGlobalValues); + } + } + + // Set the successors of the blocks in the predecessor node to the cloned blocks + // and collect the blocks where we potentially need to update phis + std::set phiUpdateBlocks; + // dbgs() << "Looking at " << P->blocks().size() << " predecessor blocks\n"; + for (BasicBlock* BB : P->blocks()) { + Instruction* term = BB->getTerminator(); + unsigned numSuccessors = term->getNumSuccessors(); + + for (unsigned idx = 0; idx < numSuccessors; ++idx) { + BasicBlock* successor = term->getSuccessor(idx); + Value* v = valueMap[successor]; + if (BasicBlock* clonedSuccessor = cast_or_null(v)) { + term->setSuccessor(idx, clonedSuccessor); + // dbgs() << " updated terminator: " << *term << "\n"; + + phiUpdateBlocks.insert(clonedSuccessor); + } + } + } + if (phiUpdateBlocks.empty()) { + dbgs() << "FAIL\n"; + abort(); + } + + // Cloning the blocks added new predecessors to non-cloned blocks without + // updating the phis. Do it now + for (BasicBlock* origBlock : best->blocks()) { + BasicBlock* clonedBlock = cast(valueMap[origBlock]); + for (auto BI = succ_begin(origBlock), BE = succ_end(origBlock); BI != BE; ++BI) { + if (valueMap[*BI] == NULL) { + // the successor of the original block has not been cloned, + // so the clone is a new predecessor + for (auto II = BI->begin(), IE = BI->end(); II != IE; ++II) { + if (PHINode* phi = dyn_cast(II)) { + int predIndex = phi->getBasicBlockIndex(origBlock); + + // use the cloned version of the phi value, if available, + // the original otherwise + Value* origPhiValue = phi->getIncomingValue(predIndex); + Value* newPhiValue = valueMap[origPhiValue]; + if (newPhiValue == NULL) { + newPhiValue = origPhiValue; + } + + phi->addIncoming(newPhiValue, clonedBlock); + } + else { + // no more phi nodes in the block + break; + } + } + } + } + } + + // Remove phi operands in cloned blocks for non-existing predecessors + for (BasicBlock* block : phiUpdateBlocks) { + PHINode* firstPHI = dyn_cast(block->begin()); + if (firstPHI == nullptr) { + continue; + } + + std::set preds(pred_begin(block), pred_end(block)); + if (preds.size() == firstPHI->getNumIncomingValues()) { + continue; + } + + for (PHINode& phi : block->phis()) { + SmallVector toRemove; + for (unsigned op = 0, opEnd = phi.getNumIncomingValues(); op != opEnd; ++op) { + BasicBlock* pred = phi.getIncomingBlock(op); + if (preds.count(pred) == 0) { + toRemove.push_back(op); + } + } + for (auto I = toRemove.rbegin(), E = toRemove.rend(); I != E; ++I) { + phi.removeIncomingValue(*I, false); + } + } + } + } + for (BasicBlock* BB : best->blocks()) { + BB->setName("dead"); + // dbgs() << "ERASE: " << *BB << '\n'; + //BB->eraseFromParent(); + } + m_func.dropNode(best); + } + + // dbgs() << "Added: blocks=" << numBlocks << ", insts=" << numInsts << '\n'; + + return changed; + } + + // Constructor. + ControlledNodeSplittingPass::ControlledNodeSplittingPass() + : FunctionPass(ID) + { + } + + // Run this pass on the given function. + bool ControlledNodeSplittingPass::runOnFunction(Function& function) + { + CNSFunction astFunction(function); + + CNSLimitGraph LG(astFunction); + + return LG.removeIrreducibleControlFlow(); + } + + char ControlledNodeSplittingPass::ID = 0; + + //------------------------------------------------------------------------------ + Pass* createControlledNodeSplittingPass() + { + return new ControlledNodeSplittingPass(); + } + + // Dump the current graph. + void CNSLimitGraph::dumpLimitGraph(std::string const& baseName, size_t dumpID) + { +#ifdef DUMP_LIMITGRAPHS + std::string filename = baseName + "-cns-" + std::to_string(dumpID) + ".gv"; + FILE* file = fopen(filename.c_str(), "wt"); + + fprintf(file, "digraph \"%s-%u\" {\n", baseName.c_str(), unsigned(dumpID)); + + for (Node const* node : m_work_list) { +#ifdef DUMP_BLOCKNAMES + fprintf(file, "n%u [label=\"%s", + unsigned(node->get_id()), std::string(node->blocks()[0]->getName()).c_str()); +#else + fprintf(file, "n%u [label=\"%u", + unsigned(node->get_id()), unsigned(node->get_id())); +#endif + if (size_t copied_from = node->copied_from()) { + fprintf(file, " (%u)", unsigned(copied_from)); + } + fprintf(file, "\""); + if (node->is_loop_head()) { + fprintf(file, " shape=box"); + } + else if (node->is_loop()) { + fprintf(file, " shape=diamond"); + } + fprintf(file, " ]\n"); + } + + for (Node const* node : m_work_list) { + unsigned node_id = unsigned(node->get_id()); + + for (Node const* succ : node->succs()) { + fprintf(file, "n%u -> n%u", node_id, unsigned(succ->get_id())); + + if (isBackedge(node, succ)) { + fprintf(file, " [ style=dashed ]"); + } + + fprintf(file, "\n"); + } + } + + fprintf(file, "}\n"); + + fclose(file); +#endif + } + + } // namespace sl +} // namespace llvm \ No newline at end of file diff --git a/Dna.LLVMInterop/Passes/ControlledNodeSplittingPass.h b/Dna.LLVMInterop/Passes/ControlledNodeSplittingPass.h new file mode 100644 index 0000000..bd60231 --- /dev/null +++ b/Dna.LLVMInterop/Passes/ControlledNodeSplittingPass.h @@ -0,0 +1,61 @@ +/****************************************************************************** +* Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of NVIDIA CORPORATION nor the names of its +* contributors may be used to endorse or promote products derived +* from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY +* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*****************************************************************************/ +#pragma once + +#include + +namespace llvm { + namespace sl { + + /// This pass computes a reducible AST above the control flow. + /// Irreducible control flow is removed using "Controlled Node Splitting". + class ControlledNodeSplittingPass : public FunctionPass + { + public: + static char ID; + + public: + /// Constructor. + ControlledNodeSplittingPass(); + + /// Run this pass on the given function. + /// + /// \param function the LLVM function + bool runOnFunction(Function& function) final; + + /// Get the name of the pass. + StringRef getPassName() const final { + return "Controlled Node Splitting"; + } + }; + + /// Create the "Controlled Node Splitting" pass. + Pass* createControlledNodeSplittingPass(); + + } // sl +} // llvm \ No newline at end of file diff --git a/Dna.LLVMInterop/dllmain.cpp b/Dna.LLVMInterop/dllmain.cpp new file mode 100644 index 0000000..5c3e3dc --- /dev/null +++ b/Dna.LLVMInterop/dllmain.cpp @@ -0,0 +1,9 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include +#include +#include +#include +#include +#include "llvm/Transforms/Scalar/SimpleLoopUnswitch.h" +#include "llvm/Transforms/Utils/LowerSwitch.h" +#include diff --git a/Dna.sln b/Dna.sln index 16960ed..ac3618d 100644 --- a/Dna.sln +++ b/Dna.sln @@ -26,6 +26,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Dna.LLVMInterop", "Dna.LLVMInterop\Dna.LLVMInterop.vcxproj", "{E6A58095-9B8B-4830-BFEA-2947C4F2681A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -258,6 +260,30 @@ Global {47D397E7-098D-4058-9B19-DC01124ED897}.RelWithDebInfo|x64.Build.0 = Release|Any CPU {47D397E7-098D-4058-9B19-DC01124ED897}.RelWithDebInfo|x86.ActiveCfg = Release|Any CPU {47D397E7-098D-4058-9B19-DC01124ED897}.RelWithDebInfo|x86.Build.0 = Release|Any CPU + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.Debug|Any CPU.ActiveCfg = Debug|x64 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.Debug|Any CPU.Build.0 = Debug|x64 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.Debug|x64.ActiveCfg = Release|x64 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.Debug|x64.Build.0 = Release|x64 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.Debug|x86.ActiveCfg = Debug|Win32 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.Debug|x86.Build.0 = Debug|Win32 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.MinSizeRel|Any CPU.ActiveCfg = Debug|x64 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.MinSizeRel|Any CPU.Build.0 = Debug|x64 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.MinSizeRel|x64.ActiveCfg = Debug|x64 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.MinSizeRel|x64.Build.0 = Debug|x64 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.MinSizeRel|x86.ActiveCfg = Debug|Win32 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.MinSizeRel|x86.Build.0 = Debug|Win32 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.Release|Any CPU.ActiveCfg = Release|x64 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.Release|Any CPU.Build.0 = Release|x64 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.Release|x64.ActiveCfg = Release|x64 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.Release|x64.Build.0 = Release|x64 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.Release|x86.ActiveCfg = Release|Win32 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.Release|x86.Build.0 = Release|Win32 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.RelWithDebInfo|Any CPU.ActiveCfg = Release|x64 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.RelWithDebInfo|Any CPU.Build.0 = Release|x64 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.RelWithDebInfo|x64.ActiveCfg = Release|x64 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.RelWithDebInfo|x64.Build.0 = Release|x64 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.RelWithDebInfo|x86.ActiveCfg = Release|Win32 + {E6A58095-9B8B-4830-BFEA-2947C4F2681A}.RelWithDebInfo|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Dna/Binary/ELF/LinuxBinary.cs b/Dna/Binary/ELF/LinuxBinary.cs new file mode 100644 index 0000000..f7284b0 --- /dev/null +++ b/Dna/Binary/ELF/LinuxBinary.cs @@ -0,0 +1,68 @@ +using AsmResolver.PE.File; +using ELFSharp.ELF; +using ELFSharp.ELF.Sections; +using ELFSharp.ELF.Segments; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Dna.Binary.Windows +{ + public class LinuxBinary : IBinary + { + public int Bitness { get; } + + public byte[] Bytes { get; set; } + + public ulong BaseAddress { get; } + + public IELF ELFFile { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The bitness of the binary(i.e 32 or 64). + /// The raw bytes of the PE file. + /// + /// The optional base address of the binary. + /// If no address is provided, then base address specified in the optional header is used. + /// + public LinuxBinary(int bitness, byte[] binaryBytes, ulong baseAddress) + { + Bitness = bitness; + Bytes = binaryBytes; + ELFFile = ELFReader.Load(new MemoryStream(binaryBytes), true); + BaseAddress = baseAddress; + } + + /// + public byte[] ReadBytes(ulong address, int count = 15) + { + // Compute a zero based offset for the address. + var offset = address - BaseAddress; + + // Compute the segment containing the address. + // TODO: Properly handle data split across multiple segments. + var section = ELFFile.Sections + .Where(x => x is Section) + .Cast>() + .Single(x => x.Offset <= offset && (x.Offset + x.Size) >= (offset + (ulong)count)); + + // Allocate a buffer to store the results in. + byte[] buffer = new byte[count]; + + // Read the raw data from the binary. + var segmentOffset = offset - section.Offset; + Array.Copy(section.GetContents(), (int)segmentOffset, buffer, 0, count); + return buffer; + } + + /// + public void WriteBytes(ulong address, byte[] bytes) + { + throw new NotImplementedException(); + } + } +} diff --git a/Dna/Dna.csproj b/Dna/Dna.csproj index 5388677..00b4682 100644 --- a/Dna/Dna.csproj +++ b/Dna/Dna.csproj @@ -1,4 +1,4 @@ - + net7.0 @@ -17,21 +17,24 @@ + + + - + + - + - @@ -41,8 +44,19 @@ + + ..\llvm-15.0.3-win64\bin\LLVM-C.dll + True + True + + + + PreserveNewest + + + diff --git a/Dna/Emulation/PEMapper.cs b/Dna/Emulation/BinaryMapper.cs similarity index 52% rename from Dna/Emulation/PEMapper.cs rename to Dna/Emulation/BinaryMapper.cs index 728e482..67c7edf 100644 --- a/Dna/Emulation/PEMapper.cs +++ b/Dna/Emulation/BinaryMapper.cs @@ -1,5 +1,6 @@ using Dna.Binary; using Dna.Binary.Windows; +using ELFSharp.ELF.Sections; using System; using System.Collections.Generic; using System.Linq; @@ -9,11 +10,29 @@ using System.Threading.Tasks; namespace Dna.Emulation { /// - /// Class for mapping windows binaries into an emulator's memory space. + /// Class for mapping binaries into an emulator's memory space. /// - public static class PEMapper + public static class BinaryMapper { - public static void MapBinary(ICpuEmulator state, WindowsBinary binary) + public static void MapELFFile(ICpuEmulator state, LinuxBinary binary) + { + // TODO: Actually map the correct amount of memory. + state.MapMemory(binary.BaseAddress, 0x1000 * 1000); + + // Get all mappable sections. + var elfFile = binary.ELFFile; + var sections = elfFile.Sections + .Where(x => x is Section) + .Cast>(); + + foreach(var section in sections) + { + // Compute the address to map the section at. + var address = section.Offset + binary.BaseAddress; + } + } + + public static void MapPEFile(ICpuEmulator state, WindowsBinary binary) { state.MapMemory(binary.BaseAddress, 0x1000 * 1000); var peFile = binary.PEFile; diff --git a/Dna/Emulation/Unicorn/GdtHelper.cs b/Dna/Emulation/Unicorn/GdtHelper.cs index 560fe52..fba491d 100644 --- a/Dna/Emulation/Unicorn/GdtHelper.cs +++ b/Dna/Emulation/Unicorn/GdtHelper.cs @@ -106,13 +106,12 @@ namespace Dna.Emulation.Unicorn gdts.Add(CreateGdtEntry(0, 0, 0, 0)); } - gdts[15] = CreateGdtEntry() var ds = CreateSegmentSelector(17, 0x0, 0xfffff, A_PRESENT | A_PRIV_0 | A_DATA | A_DATA_WRITABLE | A_DIRECTION_UP); var es = CreateSegmentSelector(28, 0x0, 0xfffff, A_PRESENT | A_PRIV_0 | A_DATA | A_DATA_WRITABLE | A_DIRECTION_UP); - var fs = CreateSegmentSelector(32, teb, 1, A_PRESENT | A_PRIV_0 | A_DATA | A_DATA_WRITABLE); // FIXME: Correct the limit - var gs = CreateSegmentSelector(33, 0x0, 0xfffff, A_PRESENT | A_PRIV_0 | A_DATA | A_DATA_WRITABLE | A_DIRECTION_UP); - var cs = CreateSegmentSelector(11, 0x0, 0xfffff, A_PRESENT | A_PRIV_0 | A_CODE | A_CODE_READABLE | A_CONFORMING); + // var fs = CreateSegmentSelector(32, teb, 1, A_PRESENT | A_PRIV_0 | A_DATA | A_DATA_WRITABLE); // FIXME: Correct the limit + //var gs = CreateSegmentSelector(33, 0x0, 0xfffff, A_PRESENT | A_PRIV_0 | A_DATA | A_DATA_WRITABLE | A_DIRECTION_UP); + //var cs = CreateSegmentSelector(11, 0x0, 0xfffff, A_PRESENT | A_PRIV_0 | A_CODE | A_CODE_READABLE | A_CONFORMING); } } } diff --git a/Dna/Emulation/Unicorn/UnicornEmulator.cs b/Dna/Emulation/Unicorn/UnicornEmulator.cs index 92a0f69..727b3ea 100644 --- a/Dna/Emulation/Unicorn/UnicornEmulator.cs +++ b/Dna/Emulation/Unicorn/UnicornEmulator.cs @@ -28,7 +28,7 @@ namespace Dna.Emulation.Unicorn this.architecture = architecture; // Insert a hook to throw an exception when unmapped memory usages occur. - //Emulator.Hooks.Code.Add(CodeHook, null); + Emulator.Hooks.Code.Add(CodeHook, null); Emulator.Hooks.Memory.Add(MemoryEventHookType.UnmappedFetch | MemoryEventHookType.UnmappedRead | MemoryEventHookType.UnmappedWrite, UnmappedMemoryHook, null); Emulator.Hooks.Memory.Add(MemoryHookType.Read, OnMemoryRead, null); Emulator.Hooks.Memory.Add(MemoryHookType.Write, OnMemoryWrite, null); @@ -104,9 +104,11 @@ namespace Dna.Emulation.Unicorn /// private bool UnmappedMemoryHook(Emulator emulator, MemoryType type, ulong address, int size, ulong value, object userData) { - + //Console.WriteLine("rax: 0x{0}", rax); Console.WriteLine("rip: 0x{0}", GetRegister(register_e.ID_REG_X86_RIP).ToString("X")); + Console.WriteLine("rdx: 0x{0}", GetRegister(register_e.ID_REG_X86_RDX).ToString("X")); + Console.WriteLine("r8: 0x{0}", GetRegister(register_e.ID_REG_X86_R8).ToString("X")); throw new InvalidOperationException($"Emulator accessed unmapped memory. (type: {type}), (address: {address})"); } @@ -127,10 +129,34 @@ namespace Dna.Emulation.Unicorn { Debugger.Break(); } - /* - Console.WriteLine("Code hook"); - Console.WriteLine("executing {0}:", ((ulong)Emulator.Registers.RIP).ToString("X")); + if(rax == 0x1400F17E1) + { + SetRegister(register_e.ID_REG_X86_RDI, 0x9A67F99000); + } + + if(rax == 0x1400F17DD) + { + //SetRegister(register_e.ID_REG_X86_RDI, 0x0000009A67F99000); + } + + // Cpuid + if(rax == 0x1400F27F1) + { + SetRegister(register_e.ID_REG_X86_RAX, 0x00000000000A0655); + SetRegister(register_e.ID_REG_X86_RBX, 0x000000000A200800); + SetRegister(register_e.ID_REG_X86_RCX, 0x00000000FEDAF387); + SetRegister(register_e.ID_REG_X86_RDX, 0x00000000BFEBFBFF); + } + + if(rax == 0x140001799) + { + Debugger.Break(); + } + + // Console.WriteLine("Code hook"); + // Console.WriteLine("executing {0}:", ((ulong)Emulator.Registers.RIP).ToString("X")); + /* if (Emulator.Registers.RIP == 0x140001753) { Console.WriteLine("RAX: 0x{0}", Emulator.Registers.RAX.ToString("X")); diff --git a/README.md b/README.md index 1513833..ec4ae8e 100644 --- a/README.md +++ b/README.md @@ -23,3 +23,9 @@ You can find an example usage [here](https://github.com/Colton1skees/Dna/blob/ma * [X] Decompilation to pseudo C * [ ] Jump table recovery * [ ] Compiler back to x86/x64 + + # Setup + +The .NET component of Dna is supported on Windows, Linux, and Mac OSX. The C++ component(LLVM.Interop) has been used exclusively on windows. + +To get the C++ component building, extract [this precompiled version of llvm](https://github.com/LLVMParty/REVIDE/releases/download/libraries/llvm-15.0.3-win64.7z) to the root directory of Dna. \ No newline at end of file diff --git a/TritonTranslator b/TritonTranslator index e63833d..c40bece 160000 --- a/TritonTranslator +++ b/TritonTranslator @@ -1 +1 @@ -Subproject commit e63833d1134f6c741cab4bc265d50e486ac579bb +Subproject commit c40bece7a47ac93f88af5fad0435208ec03337ae