/// \file Lift.cpp /// Lift transform a binary into a llvm module // // This file is distributed under the MIT License. See LICENSE.md for details. // extern "C" { #include "dlfcn.h" } #include "llvm/Support/Error.h" #include "revng/BasicAnalyses/GeneratedCodeBasicInfo.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/IRAnnotators.h" #include "revng/Support/IRHelpers.h" #include "revng/Support/ResourceFinder.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.run(Output.getModule()); EC.commitUniqueTarget(Output); } 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)); Function *Root = ModuleContainer.getModule().getFunction("root"); Function *NewPC = ModuleContainer.getModule().getFunction("newpc"); if (Root == nullptr or NewPC == nullptr) return InvalidateResult; // 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 (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; Instruction *Terminator = Call->getParent()->getTerminator(); if (Terminator->hasMetadata(JTReasonMDName)) { uint32_t Reasons = GeneratedCodeBasicInfo::getJTReasons(Terminator); DependsOnModelFunction = hasReason(Reasons, JTReason::DependsOnModelFunction); } JumpTargets.emplace(Address, DependsOnModelFunction); } } // Inspect the diff looking for newly added model::Functions auto *ModelDiff = Diff.getAs(); revng_assert(ModelDiff != nullptr); using Fields = TupleLikeTraits::Fields; size_t FunctionsIndex = static_cast(Fields::Functions); for (const auto &Change : ModelDiff->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) { if (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 InvalidateResult; } 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 InvalidateResult; } } } } return {}; } llvm::Error Lift::checkPrecondition(const pipeline::Context &Context) const { const auto &Model = *getModelFromContext(Context); 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 without either a DefaultABI " "or a DefaultPrototype."); } return llvm::Error::success(); } static_assert(pipeline::HasInvalidate); static RegisterPipe E1;