#pragma once // // This file is distributed under the MIT License. See LICENSE.md for details. // #include #include #include #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallSet.h" #include "llvm/IR/BasicBlock.h" #include "llvm/Pass.h" #include "llvm/Support/DOTGraphTraits.h" #include "llvm/Support/GraphWriter.h" #include "revng/ADT/GenericGraph.h" #include "revng/BasicAnalyses/GeneratedCodeBasicInfo.h" #include "revng/FunctionCallIdentification/FunctionCallIdentification.h" #include "revng/Model/Binary.h" #include "revng/Model/LoadModelPass.h" #include "revng/StackAnalysis/FunctionsSummary.h" #include "revng/Support/OpaqueFunctionsPool.h" namespace StackAnalysis { extern const std::set EmptyCSVSet; class StackAnalysis : public llvm::ModulePass { friend class FunctionBoundariesDetectionPass; public: static char ID; public: StackAnalysis() : llvm::ModulePass(ID) {} void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { AU.setPreservesAll(); AU.addRequired(); AU.addRequired(); } bool runOnModule(llvm::Module &M) override; const std::set & getClobbered(llvm::BasicBlock *Function) const { auto It = GrandResult.Functions.find(Function); if (It == GrandResult.Functions.end()) return EmptyCSVSet; else return It->second.ClobberedRegisters; } void serialize(std::ostream &Output) { Output << TextRepresentation; } void serializeMetadata(llvm::Function &F, GeneratedCodeBasicInfo &GCBI); public: FunctionsSummary GrandResult; std::string TextRepresentation; }; struct FuncSummary { FuncSummary( model::FunctionType::Values Type, std::set ClobberedRegisters, llvm::SmallSet, 4> Result, std::optional ElectedFSO, llvm::Function *FakeFunction = nullptr) : Type(Type), ClobberedRegisters(ClobberedRegisters), Result(Result), ElectedFSO(ElectedFSO), FakeFunction(FakeFunction) {} FuncSummary() = delete; FuncSummary(const FuncSummary &) = delete; FuncSummary(FuncSummary &&) = default; FuncSummary &operator=(const FuncSummary &) = delete; FuncSummary &operator=(FuncSummary &&) = default; model::FunctionType::Values Type; std::set ClobberedRegisters; llvm::SmallSet, 4> Result; std::optional ElectedFSO; llvm::Function *FakeFunction; static bool compare(const FuncSummary &Old, const FuncSummary &New) { if (New.Type == Old.Type) return std::includes(Old.ClobberedRegisters.begin(), Old.ClobberedRegisters.end(), New.ClobberedRegisters.begin(), New.ClobberedRegisters.end()); return (New.Type <= Old.Type); } void dump() const debug_function { dump(dbg); } template void dump(T &Output) const { Output << "Dumping summary of the function. \n" << "Type: " << Type << "\n" << "Clobbered registers: "; for (auto *Reg : ClobberedRegisters) Output << ::getName(Reg); Output << "\n"; } }; class FunctionProperties { private: /// \brief Map from CFEP to its function description std::map Bucket; FuncSummary DefaultSummary; const FuncSummary &get(llvm::BasicBlock *BB) const { auto It = Bucket.find(BB); if (It != Bucket.end()) return It->second; return DefaultSummary; } public: FunctionProperties(FuncSummary DefaultSummary) : DefaultSummary(std::move(DefaultSummary)) {} model::FunctionType::Values getFunctionType(llvm::BasicBlock *BB) const { return get(BB).Type; } bool isFakeFunction(llvm::BasicBlock *BB) const { return getFunctionType(BB) == model::FunctionType::Values::Fake; } llvm::Function *getFakeFunction(llvm::BasicBlock *BB) const { return get(BB).FakeFunction; } const auto &getRegistersClobbered(llvm::BasicBlock *BB) const { return get(BB).ClobberedRegisters; } std::optional getElectedFSO(llvm::BasicBlock *BB) const { return get(BB).ElectedFSO; } bool registerFunc(llvm::BasicBlock *BB, FuncSummary &&F) { auto It = Bucket.find(BB); if (It != Bucket.end()) { bool Changed = FuncSummary::compare(It->second, F); It->second = std::move(F); return !(Changed); } Bucket.try_emplace(BB, std::move(F)); return true; } }; template class CFEPAnalyzer { llvm::Module &M; llvm::LLVMContext &Context; GeneratedCodeBasicInfo *GCBI; FunctionOracle &Oracle; llvm::Function *PreHookOpqF; llvm::Function *PostHookOpqF; llvm::Function *IndirectBranchInfoOpqF; OpaqueFunctionsPool OFPRegistersClobbered; public: CFEPAnalyzer(llvm::Module &M, GeneratedCodeBasicInfo *GCBI, FunctionOracle &Oracle, llvm::Function *PreHookOpqF, llvm::Function *PostHookOpqF) : M(M), Context(M.getContext()), GCBI(GCBI), Oracle(Oracle), PreHookOpqF(PreHookOpqF), PostHookOpqF(PostHookOpqF), OFPRegistersClobbered(&M, false) {} public: FuncSummary analyze(const std::vector &, llvm::BasicBlock *BB); private: llvm::Function *createDisposableFunction(llvm::BasicBlock *BB); llvm::BasicBlock *integrateFunctionCallee(llvm::BasicBlock *BB); void throwDisposableFunction(llvm::Function *F); FuncSummary milkResults(const std::vector &, llvm::Function *F); }; } // namespace StackAnalysis struct BasicBlockNodeData { BasicBlockNodeData(llvm::BasicBlock *BB = nullptr) : BB(BB){}; llvm::BasicBlock *BB; }; using BasicBlockNode = BidirectionalNode; using SmallCallGraph = GenericGraph; template<> struct llvm::DOTGraphTraits : public llvm::DefaultDOTGraphTraits { using EdgeIterator = llvm::GraphTraits::ChildIteratorType; DOTGraphTraits(bool IsSimple = false) : DefaultDOTGraphTraits(IsSimple) {} static std::string getNodeLabel(const BasicBlockNode *Node, const SmallCallGraph *Graph) { if (Node->BB == nullptr) return "null"; return Node->BB->getName(); } static std::string getEdgeAttributes(const BasicBlockNode *Node, const EdgeIterator EI, const SmallCallGraph *Graph) { return "color=black,style=dashed"; } };