/// Lift transform a binary into a llvm module // // This file is distributed under the MIT License. See LICENSE.md for details. // #include "llvm/Support/Error.h" #include "revng/BasicAnalyses/GeneratedCodeBasicInfo.h" #include "revng/Lift/IRAnnotators.h" #include "revng/Lift/Lift.h" #include "revng/Lift/LiftPipe.h" #include "revng/Model/LoadModelPass.h" #include "revng/Pipeline/AllRegistries.h" #include "revng/Pipes/FileContainer.h" #include "revng/Pipes/Kinds.h" #include "revng/Pipes/ModelGlobal.h" #include "revng/Pipes/RootKind.h" #include "revng/Support/IRHelpers.h" #include "revng/Support/ResourceFinder.h" #include "PostLiftVerifyPass.h" using namespace llvm; using namespace pipeline; using namespace ::revng::pipes; void Lift::run(ExecutionContext &EC, const BinaryFileContainer &SourceBinary, LLVMContainer &Output) { if (not SourceBinary.exists()) return; const TupleTree &Model = getModelFromContext(EC); auto BufferOrError = MemoryBuffer::getFileOrSTDIN(*SourceBinary.path()); auto Buffer = cantFail(errorOrToExpected(std::move(BufferOrError))); RawBinaryView RawBinary(*Model, Buffer->getBuffer()); // Perform lifting llvm::legacy::PassManager PM; PM.add(new LoadModelWrapperPass(Model)); PM.add(new LoadExecutionContextPass(&EC, Output.name())); PM.add(new LoadBinaryWrapperPass(Buffer->getBuffer())); PM.add(new LiftPass); PM.add(new PostLiftVerifyPass); PM.run(Output.getModule()); EC.commitUniqueTarget(Output); } namespace revng::lift::internal { std::tuple> collectJumpTargets(const llvm::Module &Module) { const Function *Root = Module.getFunction("root"); const Function *NewPC = getIRHelper("newpc", Module); if (Root == nullptr) return { false, {} }; if (NewPC == nullptr) return { true, {} }; // Collect all jump targets by inspecting calls to newpc and record whether it // was found after adding entry addresses of functions std::map JumpTargets; for (const CallBase *Call : callers(NewPC)) { bool IsJumpTarget = getLimitedValue(Call->getArgOperand(2)) == 1; if (IsJumpTarget) { auto Address = MetaAddress::fromValue(Call->getArgOperand(0)); // Detect if this jump targets has been discovered *after* recording the // entry addresses of functions // Be conservative and assume it is, in absence of information bool DependsOnModelFunction = true; const Instruction *Terminator = Call->getParent()->getTerminator(); if (Terminator->hasMetadata(JTReasonMDName)) { uint32_t Reasons = GeneratedCodeBasicInfo::getJTReasons(Terminator); DependsOnModelFunction = hasReason(Reasons, JTReason::DependsOnModelFunction); } JumpTargets.emplace(Address, DependsOnModelFunction); } } return { true, JumpTargets }; } bool shouldInvalidateRoot(const std::map &JumpTargets, const TupleTreeDiff &Diff) { // Inspect the diff looking for newly added model::Functions using Fields = TupleLikeTraits::Fields; size_t FunctionsIndex = static_cast(Fields::Functions); for (const auto &Change : Diff.Changes) { bool IsAddition = not Change.Old.has_value() and Change.New.has_value(); bool IsRemoval = Change.Old.has_value() and not Change.New.has_value(); // Look for additions to /Functions auto &Path = Change.Path; if (Path.size() == 1 and Path[0].get() == FunctionsIndex) { // Check the Entry address of the newly added model::Function MetaAddress ChangedAddress; if (IsAddition) ChangedAddress = std::get(*Change.New).Entry(); else ChangedAddress = std::get(*Change.Old).Entry(); auto It = JumpTargets.find(ChangedAddress); bool IsJumpTarget = It != JumpTargets.end(); bool DependsOnModelFunction = IsJumpTarget and It->second; if (IsAddition and not IsJumpTarget) { // We're adding a function that was not a jump target return true; } else if (IsRemoval and DependsOnModelFunction) { // We're removing a function whose address was not discovered *before* // starting to take into account the entry addresses of model // functions return true; } } } return false; } } // namespace revng::lift::internal std::map Lift::invalidate(const BinaryFileContainer &SourceBinary, const pipeline::LLVMContainer &ModuleContainer, const GlobalTupleTreeDiff &Diff) const { // Prepare result in case of invalidation std::map InvalidateResult; InvalidateResult[&ModuleContainer].push_back(pipeline::Target({}, kinds::Root)); auto [HasRoot, JumpTargets] = lift::internal::collectJumpTargets(ModuleContainer .getModule()); if (not HasRoot) return InvalidateResult; auto *ModelDiff = Diff.getAs(); revng_assert(ModelDiff != nullptr); if (lift::internal::shouldInvalidateRoot(JumpTargets, *ModelDiff)) return InvalidateResult; else return {}; } namespace revng::lift::internal { llvm::Error checkPrecondition(const model::Binary &Model) { if (Model.Architecture() == model::Architecture::Invalid) { return revng::createError("Cannot lift binary with architecture invalid."); } if (Model.DefaultABI() == model::ABI::Invalid and Model.DefaultPrototype().isEmpty()) { return revng::createError("Cannot lift binary with neither `DefaultABI` " "nor `DefaultPrototype`."); } return llvm::Error::success(); } } // namespace revng::lift::internal llvm::Error Lift::checkPrecondition(const pipeline::Context &Context) const { const auto &Model = *getModelFromContext(Context); return lift::internal::checkPrecondition(Model); } static_assert(pipeline::HasInvalidate); static RegisterPipe E1;