diff --git a/CMakeLists.txt b/CMakeLists.txt index 0dba2e27d..a1b8936df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,7 @@ include_directories(argparse/) add_executable(revamb ptcdump.cpp main.cpp debughelper.cpp variablemanager.cpp jumptargetmanager.cpp instructiontranslator.cpp codegenerator.cpp debug.cpp osra.cpp set.cpp simplifycomparisons.cpp reachingdefinitions.cpp - argparse/argparse.c) + functionboundariesdetection.cpp argparse/argparse.c) target_link_libraries(revamb dl m ${LLVM_LIBRARIES}) # Remove -rdynamic diff --git a/codegenerator.cpp b/codegenerator.cpp index f14ee2bb1..717b2c4f9 100644 --- a/codegenerator.cpp +++ b/codegenerator.cpp @@ -3,8 +3,6 @@ /// assembly to LLVM IR. // Standard includes -#include -#include #include #include #include @@ -35,6 +33,7 @@ #include "codegenerator.h" #include "debug.h" #include "debughelper.h" +#include "functionboundariesdetection.h" #include "instructiontranslator.h" #include "jumptargetmanager.h" #include "ptcinterface.h" @@ -918,7 +917,7 @@ void CodeGenerator::translate(uint64_t VirtualAddress, replaceFunctionWithRet(HelpersModule->getFunction("page_get_flags"), 0xffffffff); - // HACK: the LLVM linker does not import non-static funcitons anymore if + // HACK: the LLVM linker does not import non-static functions anymore if // LinkOnlyNeeded is specified. We don't want this so mark all the // non-static symbols not directly imported as static. { @@ -967,6 +966,11 @@ void CodeGenerator::translate(uint64_t VirtualAddress, JumpTargets.finalizeJumpTargets(); purgeDeadBlocks(MainFunction); + + legacy::FunctionPassManager FPM(&*TheModule); + FPM.add(new FunctionBoundariesDetectionPass(&JumpTargets, "")); + FPM.run(*MainFunction); + Translator.finalizeNewPCMarkers(CoveragePath, EnableTracing); Debug->generateDebugInfo(); diff --git a/codegenerator.h b/codegenerator.h index c5616afd3..879621820 100644 --- a/codegenerator.h +++ b/codegenerator.h @@ -119,6 +119,7 @@ private: bool EnableOSRA; bool EnableTracing; std::string BBSummaryPath; + std::string FunctionListPath; }; #endif // _CODEGENERATOR_H diff --git a/functionboundariesdetection.cpp b/functionboundariesdetection.cpp new file mode 100644 index 000000000..274879e12 --- /dev/null +++ b/functionboundariesdetection.cpp @@ -0,0 +1,669 @@ +/// \file functionboundariesdetection.cpp +/// \brief + +// Standard includes +#include +#include +#include +#include +#include +#include + +// Boost includes +#include +#include +#include + +// LLVM includes +#include "llvm/ADT/iterator_range.h" +#include "llvm/ADT/ilist.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" + +// Local includes +#include "debug.h" +#include "datastructures.h" +#include "functionboundariesdetection.h" +#include "ir-helpers.h" +#include "jumptargetmanager.h" + +using namespace llvm; + +using std::map; +using std::vector; + +class FunctionBoundariesDetectionImpl; + +using FBDP = FunctionBoundariesDetectionPass; +using FBD = FunctionBoundariesDetectionImpl; +using interval_set = boost::icl::interval_set; +using interval = boost::icl::interval; + +char FBDP::ID = 0; +static RegisterPass X("fbdp", + "Function Boundaries Detection Pass", + true, + true); + +class FunctionBoundariesDetectionImpl { +public: + FunctionBoundariesDetectionImpl(Function &F, + JumpTargetManager *JTM) : F(F), JTM(JTM) { } + + map> run(); + +private: + enum RelationType { + UnknownRelation = 0, + Head = 1, + Fallthrough = 2, + Jump = 4, + Return = 8 + }; + + enum CFEPReason { + UnknownReason = 0, + Callee = 1, + GlobalData = 2, + InCode = 4, + SkippingJump = 8 + }; + + class CFEPRelation { + public: + CFEPRelation(BasicBlock *CFEP) : CFEP(CFEP), Distance(0), Type(0) { } + + void setType(RelationType T) { Type |= T; } + bool hasType(RelationType T) const { return Type & T; } + + void setDistance(uint32_t New) { Distance = std::max(Distance, New); } + bool isSkippingJump() const { return Distance > 0 && hasType(Jump); } + bool isNonSkippingJump() const { return Distance == 0 && hasType(Jump); } + + BasicBlock *cfep() const { return CFEP; } + + std::string describe() const; + + private: + BasicBlock *CFEP; + uint32_t Distance; + uint32_t Type; + }; + + class CFEP { + public: + CFEP() : Reasons(0) { } + + void setReason(CFEPReason Reason) { Reasons |= Reason; } + bool hasReason(CFEPReason Reason) { return Reasons & Reason; } + + private: + uint32_t Reasons; + }; + +private: + void initPostDispatcherIt(); + void collectFunctionCalls(); + void collectReturnInstructions(); + void initNormalizedAddressSpace(); + interval_set findCoverage(BasicBlock *BB); + + // CFEP related methods + void collectInitialCFEPSet(); + void cfepProcessPhase1(); + void cfepProcessPhase2(); + void serialize(); + + void setRelation(BasicBlock *CFEP, BasicBlock *Affected, RelationType T) { + assert(CFEP != nullptr); + CFEPRelation &Relation = getRelation(CFEP, Affected); + Relation.setType(T); + } + + void setDistance(BasicBlock *CFEP, BasicBlock *Affected, uint64_t Distance) { + assert(CFEP != nullptr); + const uint64_t Max = std::numeric_limits::max(); + Distance = std::min(Distance, Max); + CFEPRelation &Relation = getRelation(CFEP, Affected); + Relation.setDistance(Distance); + } + + bool isCFEP(BasicBlock *BB) const { return CFEPs.count(BB); } + void registerCFEP(BasicBlock *BB, CFEPReason Reason) { + assert(BB != nullptr); + CFEPs[BB].setReason(Reason); + setRelation(BB, BB, Head); + } + + void filterCFEPs(); + + CFEPRelation &getRelation(BasicBlock *CFEP, BasicBlock *Affected) { + SmallVector &BBRelations = Relations[Affected]; + auto It = std::find_if(BBRelations.begin(), + BBRelations.end(), + [CFEP] (CFEPRelation &R) { + return R.cfep() == CFEP; + }); + if (It != BBRelations.end()) { + return *It; + } else { + BBRelations.emplace_back(CFEP); + return BBRelations.back(); + } + } + + std::vector cfeps() const { + std::vector Result; + Result.reserve(CFEPs.size()); + for (auto &P : CFEPs) + Result.push_back(P.first); + + return Result; + } + +private: + Function &F; + JumpTargetManager *JTM; + + std::map FunctionCalls; + std::map> CallPredecessors; + std::set ReturnPCs; + std::set Returns; + ilist_iterator PostDispatcherIt; + std::map Coverage; + + // CFEP related data + std::map CFEPs; + std::map> Relations; + OnceQueue CFEPWorkList; + interval_set Callees; + interval_set NormalizedReadInterval; + std::map> Functions; +}; + + +void FBD::initPostDispatcherIt() { + // Skip dispatcher and friends + auto It = F.begin(); + for (; It != F.end(); It++) { + if (!It->empty()) { + if (auto *Call = dyn_cast(&*It->begin())) { + Function *Callee = Call->getCalledFunction(); + if (Callee != nullptr && Callee->getName() == "newpc") + break; + } + } + } + PostDispatcherIt = It; +} + +void FBD::collectFunctionCalls() { + // Collect function calls + for (BasicBlock &BB : make_range(PostDispatcherIt, F.end())) { + auto *Terminator = BB.getTerminator(); + if (!JTM->isJump(Terminator) || isa(Terminator)) + continue; + + // To be a function call we need to find: + // + // * a call to "newpc" + // * a store of the next PC + // * a store to the PC + // + // TODO: the function call detection criteria in reachingdefinitions.cpp is + // probably more elegant, import it. + bool NewPCFound = false; + bool SaveRAFound = false; + bool StorePCFound = false; + uint64_t ReturnPC = JTM->getNextPC(Terminator); + + auto Visitor = [this, + &NewPCFound, + &SaveRAFound, + ReturnPC, + &StorePCFound] (RBasicBlockRange R) { + for (Instruction &I : R) { + if (auto *Store = dyn_cast(&I)) { + Value *V = Store->getValueOperand(); + if (Store->getPointerOperand() == JTM->pcReg()) { + StorePCFound = true; + } else if (auto *Constant = dyn_cast(V)) { + // Note that we willingly ignore stores to the PC here + if (Constant->getLimitedValue() == ReturnPC) { + assert(!SaveRAFound); + SaveRAFound = true; + } + } + } else if (auto *Call = dyn_cast(&I)) { + auto *Callee = Call->getCalledFunction(); + if (Callee != nullptr && Callee->getName() == "newpc") { + assert(!NewPCFound); + NewPCFound = true; + return true; + } + } + } + + return false; + }; + + // TODO: adapt visitPredecessors from visitSuccessors + visitPredecessors(Terminator, Visitor, JTM->dispatcher()); + + if (SaveRAFound && StorePCFound) { + BasicBlock *ReturnBB = JTM->getBlockAt(ReturnPC); + assert(ReturnBB != nullptr); + FunctionCalls[Terminator] = ReturnBB; + CallPredecessors[ReturnBB].push_back(&BB); + ReturnPCs.insert(ReturnPC); + } + } + + // Mark all the callee basic blocks as such + for (auto P : FunctionCalls) + for (BasicBlock *S : P.first->successors()) + if (S != JTM->dispatcher()) + JTM->registerJT(S, JumpTargetManager::Callee); +} + +void FBD::collectReturnInstructions() { + // Detect return instructions + + // TODO: there is a remote possibility that we're mishandling some case here, + // in the future we should perform a stack analysis to prove that a + // register has not been touched since the function entry. + for (BasicBlock &BB : make_range(PostDispatcherIt, F.end())) { + auto *Terminator = BB.getTerminator(); + + if (FunctionCalls.count(Terminator) != 0) + continue; + + bool JumpsToDispatcher = false; + bool IsReturn = true; + + for (BasicBlock *Successor : Terminator->successors()) { + + if (Successor == JTM->dispatcher()) + JumpsToDispatcher = true; + + if (!(Successor == JTM->dispatcher() + || Successor == JTM->dispatcherFail() + || ReturnPCs.count(JTM->getPC(&*Successor->begin()).first) != 0)) { + IsReturn = false; + break; + } + + } + + IsReturn &= JumpsToDispatcher; + + if (IsReturn) { + // TODO: assert that the destnation is the content of a register, or a + // load from a memory location at a constant offset from the content + // of a register + Returns.insert(Terminator); + } + + } +} + +/// \brief Address space normalization +/// Assign the lowest address to 0 and skip any holes in the translated +/// address space. +void FBD::initNormalizedAddressSpace() { + // Sort all the basic blocks by their starting address + std::map> SortedPCs; + for (User *U : F.getParent()->getFunction("newpc")->users()) { + auto *Call = dyn_cast(U); + if (Call == nullptr) + continue; + + uint64_t Address = getLimitedValue(Call->getOperand(0)); + uint64_t Size = getLimitedValue(Call->getOperand(1)); + + SortedPCs[Address] = { Call->getParent(), Size }; + } + + // Assign addresses in the normalized address space + uint64_t CurrentAddress = 0; + for (auto &P : SortedPCs) { + BasicBlock *BB = P.second.first; + uint64_t Size = P.second.second; + uint64_t StartAddress = P.first; + uint64_t EndAddress = StartAddress + Size; + + interval_set VirtualInterval; + VirtualInterval += interval::right_open(StartAddress, EndAddress); + + // Move the read range into the normalized address space and merge the + // result into NormalizedReadInterval + interval_set ReadInterval = JTM->readRange() & VirtualInterval; + for (auto Interval : ReadInterval) { + uint64_t Lower = (Interval.lower() - StartAddress) + CurrentAddress; + uint64_t Upper = Lower + Interval.upper() - Interval.lower(); + NormalizedReadInterval += interval::right_open(Lower, Upper); + } + + // Associate each basic block with its interval in the normalized address + // space + Coverage[BB] += interval::right_open(CurrentAddress, CurrentAddress + Size); + CurrentAddress += Size; + } +} + +interval_set FBD::findCoverage(BasicBlock *BB) { + auto It = Coverage.find(BB); + if (It != Coverage.end()) + return It->second; + + OnceQueue WorkList; + WorkList.insert(BB); + + while (!WorkList.empty()) { + BB = WorkList.pop(); + + It = Coverage.find(BB); + if (It != Coverage.end()) + return It->second; + + for (BasicBlock *Predecessor : predecessors(BB)) + if (Predecessor != JTM->dispatcher() + && Predecessor != JTM->dispatcherFail()) + WorkList.insert(Predecessor); + } + + assert(false); +} + +void FBD::collectInitialCFEPSet() { + // TODO: handle entry points + // registerCFEP(JTM->getBlockAt(EntryPoint), Callee); + + // Collect initial set of CFEPs + for (auto &P : *JTM) { + if (contains(JTM->readRange(), P.first)) + continue; + + const JumpTargetManager::JumpTarget &JT = P.second; + + BasicBlock *CFEPHead = JT.head(); + bool Insert = false; + + DBG("functions", dbg << JT.describe() << "\n"); + + if (JT.hasReason(JumpTargetManager::Callee)) { + registerCFEP(CFEPHead, Callee); + Callees += Coverage[CFEPHead]; + Insert = true; + } + + if (JT.hasReason(JumpTargetManager::UnusedGlobalData)) { + registerCFEP(CFEPHead, GlobalData); + Insert = true; + } + + if (JT.hasReason(JumpTargetManager::SETNotToPC) + && !JT.hasReason(JumpTargetManager::SETToPC)) { + registerCFEP(CFEPHead, InCode); + Insert = true; + } + + if (Insert) + CFEPWorkList.insert(CFEPHead); + } +} + +void FBD::cfepProcessPhase1() { + // For each CFEP record which basic block it can reach and how. Then also + // detect skipping jumps. + while (!CFEPWorkList.empty()) { + BasicBlock *CFEP = CFEPWorkList.pop(); + + interval_set Covered; + + // Find all the basic block it can reach + OnceQueue WorkList; + WorkList.insert(CFEP); + + while (!WorkList.empty()) { + BasicBlock *RelatedBB = WorkList.pop(); + + Covered += Coverage[RelatedBB]; + + auto FCIt = FunctionCalls.find(RelatedBB->getTerminator()); + if (FCIt != FunctionCalls.end()) { + // This basic block ends with a function call, proceed with the return + // address + BasicBlock *ReturnBB = FCIt->second; + setRelation(CFEP, ReturnBB, Return); + WorkList.insert(ReturnBB); + } else if (Returns.count(RelatedBB->getTerminator()) == 0) { + // It's not a return, it's not a function call, it must be a branch part + // of the ordinary control flow of the function. + for (BasicBlock *S : successors(RelatedBB)) { + if (S == JTM->dispatcher() + || S == JTM->dispatcherFail()) + continue; + + // TODO: track fallthrough + setRelation(CFEP, S, Jump); + WorkList.insert(S); + } + + } + } + + // Compute distance of jumps + + // For each basic block look at his Jump successors + for (BasicBlock *BB : WorkList.visited()) { + TerminatorInst *T = BB->getTerminator(); + if (FunctionCalls.count(T) != 0 || Returns.count(T) != 0) + continue; + + uint64_t StartAddress = findCoverage(BB).begin()->lower(); + + for (BasicBlock *S : successors(BB)) { + if (S == JTM->dispatcher() + || S == JTM->dispatcherFail() + || Coverage.count(S) == 0) + continue; + + interval_set &BBInterval = Coverage[S]; + // TODO: why this? + if (BBInterval.size() == 0) + continue; + + uint64_t DestinationAddress = BBInterval.begin()->lower(); + + interval_set JumpInterval; + if (StartAddress <= DestinationAddress) + JumpInterval += interval::closed(StartAddress, DestinationAddress); + else + JumpInterval += interval::closed(DestinationAddress, StartAddress); + + JumpInterval -= Covered; + JumpInterval -= NormalizedReadInterval; + JumpInterval &= Callees; + uint64_t Distance = JumpInterval.size(); + + if (Distance > 0) { + setDistance(CFEP, S, Distance); + registerCFEP(S, SkippingJump); + CFEPWorkList.insert(S); + } + + } + + } + } +} + +void FBD::filterCFEPs() { + std::map::iterator It = CFEPs.begin(); + while (It != CFEPs.end()) { + BasicBlock *CFEPHead = It->first; + assert(CFEPHead != nullptr); + CFEP &C = It->second; + assert(!C.hasReason(UnknownReason)); + + // Keep a CFEP only if its address is taken, it's a callee or all the + // paths leading there are skipping jumps + bool Keep = C.hasReason(Callee); + bool AddressTaken = C.hasReason(GlobalData) || C.hasReason(InCode); + + // if (getBasicBlockPC(Head) == 0x18a70) + // dbg << "here\n"; + if (!Keep && AddressTaken) { + Keep = true; + // Check no relation of Jump type and 0-distance exist + for (CFEPRelation &Relation : Relations[CFEPHead]) + Keep = Keep + && !Relation.isNonSkippingJump() + && !Relation.hasType(Return); + } + + if (!Keep && !AddressTaken) { + auto &CFEPRelations = Relations[CFEPHead]; + Keep = Relations.size() > 1; + if (Keep) + for (CFEPRelation &Relation : CFEPRelations) + Keep = Keep && (Relation.hasType(Head) + || Relation.isSkippingJump()); + } + + if (Keep) { + DBG("functions", { + dbg << std::hex << "0x" << getBasicBlockPC(CFEPHead) + << " is a FEP: " + << " Callee? " << C.hasReason(Callee) + << " GlobalData? " << C.hasReason(GlobalData) + << " InCode? " << C.hasReason(InCode) + << " SkippingJump? " << C.hasReason(SkippingJump) + << "\n"; + }); + It++; + } else { + DBG("functions", { + dbg << std::hex << "0x" << getBasicBlockPC(CFEPHead) + << " is a not a FEP:"; + for (CFEPRelation &Relation : Relations[CFEPHead]) + dbg << " {" << Relation.describe() << "}"; + dbg << "\n"; + }); + It = CFEPs.erase(It); + } + } + + Relations.clear(); +} + +void FBD::cfepProcessPhase2() { + // Find all the basic block it can reach + for (BasicBlock *CFEP : cfeps()) { + OnceQueue WorkList; + WorkList.insert(CFEP); + + while (!WorkList.empty()) { + BasicBlock *RelatedBB = WorkList.pop(); + assert(RelatedBB != JTM->dispatcher()); + + auto FCIt = FunctionCalls.find(RelatedBB->getTerminator()); + if (FCIt != FunctionCalls.end()) { + BasicBlock *ReturnBB = FCIt->second; + if (!isCFEP(ReturnBB)) + WorkList.insert(ReturnBB); + } else if (Returns.count(RelatedBB->getTerminator()) == 0) { + for (BasicBlock *S : successors(RelatedBB)) { + if (S == JTM->dispatcher() + || S == JTM->dispatcherFail()) + continue; + + // TODO: doesn't handle the div in div case + if (!isCFEP(S)) + WorkList.insert(S); + } + + } + } + + for (BasicBlock *Member : WorkList.visited()) + Functions[CFEP].push_back(Member); + } +} + +map> FBD::run() { + assert(JTM != nullptr); + + initPostDispatcherIt(); + + collectFunctionCalls(); + + collectReturnInstructions(); + + initNormalizedAddressSpace(); + + collectInitialCFEPSet(); + + cfepProcessPhase1(); + + filterCFEPs(); + + cfepProcessPhase2(); + + return std::move(Functions); +} + +std::string FBD::CFEPRelation::describe() const { + std::stringstream SS; + SS << getName(CFEP) + << " Distance: " << Distance; + + if (hasType(UnknownRelation)) + SS << " UnknownRelation"; + if (hasType(Head)) + SS << " Head"; + if (hasType(Fallthrough)) + SS << " Fallthrough"; + if (hasType(Jump)) + SS << " Jump"; + if (hasType(Return)) + SS << " Return"; + + return SS.str(); +} + +bool FBDP::runOnFunction(Function &F) { + FBD Impl(F, JTM); + Functions = Impl.run(); + serialize(); + return false; +} + +void FBDP::serialize() const { + if (SerializePath.size() == 0) + return; + + // Emit results + std::ofstream Output(SerializePath); + Output << "index,start,end\n"; + for (auto &P : Functions) { + for (BasicBlock *BB : P.second) { + for (Instruction &I : *BB) { + if (auto *Call = dyn_cast(&I)) { + Function *Callee = Call->getCalledFunction(); + if (Callee != nullptr && Callee->getName() == "newpc") { + uint64_t StartPC = getLimitedValue(Call->getArgOperandUse(0)); + uint64_t Size = getLimitedValue(Call->getArgOperandUse(1)); + uint64_t EndPC = StartPC + Size; + Output << std::dec << getName(P.first) << "," + << "0x" << std::hex << StartPC << "," + << "0x" << std::hex << EndPC << "\n"; + } + } + } + } + } +} diff --git a/functionboundariesdetection.h b/functionboundariesdetection.h new file mode 100644 index 000000000..5f1ed38b0 --- /dev/null +++ b/functionboundariesdetection.h @@ -0,0 +1,42 @@ +#ifndef _FUNCTIONBOUNDARIESDETECTION_H +#define _FUNCTIONBOUNDARIESDETECTION_H + +// Standard includes +#include +#include + +// LLVM includes +#include "llvm/Pass.h" + +namespace llvm { +class BasicBlock; +} + +class JumpTargetManager; + +class FunctionBoundariesDetectionPass : public llvm::FunctionPass { +public: + static char ID; + +public: + FunctionBoundariesDetectionPass() : llvm::FunctionPass(ID), JTM(nullptr) { } + FunctionBoundariesDetectionPass(JumpTargetManager *JTM, + std::string SerializePath) : + llvm::FunctionPass(ID), JTM(JTM), SerializePath(SerializePath) { } + + void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } + + bool runOnFunction(llvm::Function &F) override; + +private: + void serialize() const; + +private: + JumpTargetManager *JTM; + std::string SerializePath; + std::map> Functions; +}; + +#endif // _FUNCTIONBOUNDARIESDETECTION_H diff --git a/ir-helpers.h b/ir-helpers.h index 275ca4487..7d363b873 100644 --- a/ir-helpers.h +++ b/ir-helpers.h @@ -230,6 +230,47 @@ static inline void visitSuccessors(llvm::Instruction *I, visitSuccessors(I, IgnoreSet, Visitor); } +using RBasicBlockRange = + llvm::iterator_range; +using RVisitorFunction = std::function; + +// TODO: factor with visitSuccessors +static inline void visitPredecessors(llvm::Instruction *I, + RVisitorFunction Visitor, + llvm::BasicBlock *Ignore) { + llvm::BasicBlock *Parent = I->getParent(); + std::set Visited; + Visited.insert(Parent); + + llvm::BasicBlock::reverse_iterator It(make_reverse_iterator(I)); + if (It == Parent->rend()) + return; + // It++; + + std::queue> Queue; + Queue.push(llvm::make_range(It, Parent->rend())); + bool Stop = false; + + while (!Queue.empty()) { + auto Range = Queue.front(); + Queue.pop(); + auto *lol = Range.begin()->getParent(); + + if (Visitor(Range)) + Stop = true; + + for (auto *Predecessor : predecessors(lol)) { + if (Visited.count(Predecessor) == 0 && Predecessor != Ignore) { + Visited.insert(Predecessor); + if (!Stop && !Predecessor->empty()) + Queue.push(make_range(Predecessor->rbegin(), Predecessor->rend())); + } + } + + } + +} + /// \brief Return a sensible name for the given basic block /// \return the name of the basic block, if available, its pointer value /// otherwise. @@ -270,4 +311,17 @@ static inline std::string getName(const llvm::Value *V) { return SS.str(); } +// TODO: this function assumes 0 is not a valid PC +static inline uint64_t getBasicBlockPC(llvm::BasicBlock *BB) { + auto It = BB->begin(); + assert(It != BB->end()); + if (auto *Call = llvm::dyn_cast(&*It)) { + auto *Callee = Call->getCalledFunction(); + if (Callee && Callee->getName() == "newpc") + return getLimitedValue(Call->getOperand(0)); + } + + return 0; +} + #endif // _IRHELPERS_H diff --git a/jumptargetmanager.h b/jumptargetmanager.h index 1b491a7b3..3c43781c9 100644 --- a/jumptargetmanager.h +++ b/jumptargetmanager.h @@ -15,6 +15,7 @@ // Local includes #include "datastructures.h" #include "ir-helpers.h" +#include "revamb.h" // Forward declarations namespace llvm { @@ -320,6 +321,15 @@ public: return JumpTargets.end(); } + void registerJT(llvm::BasicBlock *BB, JTReason Reason) { + assert(!BB->empty()); + auto *CallNewPC = llvm::dyn_cast(&*BB->begin()); + assert(CallNewPC != nullptr); + llvm::Function *Callee = CallNewPC->getCalledFunction(); + assert(Callee != nullptr && Callee->getName() == "newpc"); + registerJT(getLimitedValue(CallNewPC->getArgOperand(0)), Reason); + } + /// \brief Removes a `BasicBlock` from the SET's visited list void unvisit(llvm::BasicBlock *BB); @@ -456,6 +466,17 @@ public: /// one llvm::CallInst *findNextExitTB(llvm::Instruction *I); + bool isJump(llvm::TerminatorInst *T) const { + for (llvm::BasicBlock *Successor : T->successors()) { + if (!(Successor == Dispatcher + || Successor == DispatcherFail + || isJumpTarget(getBasicBlockPC(Successor)))) + return false; + } + + return true; + } + void registerReadRange(uint64_t Address, uint64_t Size); const interval_set &readRange() const { return ReadIntervalSet; }