mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
Let FunctionIsolation and EnforceABI use the Model
This commit is contained in:
@@ -163,7 +163,6 @@ copy_to_build_and_install(FILES
|
||||
share/revng
|
||||
runtime/support.c
|
||||
runtime/support.h
|
||||
include/revng/Runtime/commonconstants.h
|
||||
scripts/clang-format-style-file)
|
||||
|
||||
configure_file(runtime/early-linked.c "${CMAKE_BINARY_DIR}/share/revng/early-linked.c" COPYONLY)
|
||||
|
||||
@@ -236,7 +236,7 @@ public:
|
||||
llvm::BasicBlock *getJumpTargetBlock(llvm::BasicBlock *BB);
|
||||
|
||||
MetaAddress getJumpTarget(llvm::BasicBlock *BB) {
|
||||
return getPCFromNewPC(&*getJumpTargetBlock(BB)->begin());
|
||||
return getPCFromNewPC(getJumpTargetBlock(BB));
|
||||
}
|
||||
|
||||
bool isJump(llvm::BasicBlock *BB) const {
|
||||
|
||||
@@ -22,6 +22,7 @@ public:
|
||||
|
||||
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
|
||||
AU.addRequired<GeneratedCodeBasicInfoWrapperPass>();
|
||||
AU.addRequired<LoadModelPass>();
|
||||
AU.setPreservesAll();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "llvm/Pass.h"
|
||||
|
||||
#include "revng/BasicAnalyses/GeneratedCodeBasicInfo.h"
|
||||
#include "revng/Model/LoadModelPass.h"
|
||||
|
||||
class IsolateFunctions : public llvm::ModulePass {
|
||||
public:
|
||||
@@ -22,5 +23,6 @@ public:
|
||||
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
|
||||
AU.setPreservesAll();
|
||||
AU.addRequired<GeneratedCodeBasicInfoWrapperPass>();
|
||||
AU.addRequired<LoadModelPass>();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// This file is distributed under the MIT License. See LICENSE.md for details.
|
||||
//
|
||||
|
||||
/// \brief Type of generated message for debugging exceptions at runtime when
|
||||
/// function isolation is applied
|
||||
typedef enum {
|
||||
/// Unexpected control flow at the end of a translated basic block
|
||||
StandardTranslatedBlock,
|
||||
/// Unexpected control flow at the end of a non-translated basic block (anypc
|
||||
/// or unexpectedpc)
|
||||
StandardNonTranslatedBlock,
|
||||
/// Expected and actual return address after function call not matching
|
||||
BadReturnAddress,
|
||||
/// Call to the function dispatcher with a PC not corresponding to any
|
||||
/// function entry block
|
||||
FunctionDispatcherFallBack,
|
||||
/// Execution has reached the return address of a noreturn function call
|
||||
ReturnFromNoReturn
|
||||
} Reason;
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "revng/BasicAnalyses/GeneratedCodeBasicInfo.h"
|
||||
#include "revng/FunctionCallIdentification/FunctionCallIdentification.h"
|
||||
#include "revng/Model/LoadModelPass.h"
|
||||
#include "revng/StackAnalysis/FunctionsSummary.h"
|
||||
|
||||
namespace StackAnalysis {
|
||||
@@ -29,6 +30,7 @@ public:
|
||||
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
|
||||
AU.setPreservesAll();
|
||||
AU.addRequired<GeneratedCodeBasicInfoWrapperPass>();
|
||||
AU.addRequired<LoadModelPass>();
|
||||
}
|
||||
|
||||
bool runOnModule(llvm::Module &M) override;
|
||||
|
||||
@@ -79,7 +79,7 @@ public:
|
||||
|
||||
public:
|
||||
std::array<llvm::GlobalVariable *, 4> pcCSVs() const {
|
||||
return { AddressCSV, EpochCSV, AddressSpaceCSV, TypeCSV };
|
||||
return { EpochCSV, AddressSpaceCSV, TypeCSV, AddressCSV };
|
||||
}
|
||||
|
||||
/// \brief Hook for the emission of a store to a CSV
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "llvm/ADT/Triple.h"
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
|
||||
#include "revng/Model/Binary.h"
|
||||
#include "revng/Support/IRHelpers.h"
|
||||
#include "revng/Support/MetaAddress.h"
|
||||
|
||||
@@ -23,32 +24,85 @@ class GlobalVariable;
|
||||
|
||||
class ABIRegister {
|
||||
private:
|
||||
llvm::StringRef Name;
|
||||
llvm::StringRef QemuName;
|
||||
model::Register::Values ID;
|
||||
unsigned MContextIndex;
|
||||
|
||||
public:
|
||||
static const unsigned NotInMContext = std::numeric_limits<unsigned>::max();
|
||||
|
||||
public:
|
||||
ABIRegister(llvm::StringRef Name, unsigned MContextIndex) :
|
||||
Name(Name), QemuName(Name), MContextIndex(MContextIndex) {}
|
||||
ABIRegister(model::Register::Values ID) :
|
||||
ID(ID), MContextIndex(NotInMContext) {}
|
||||
|
||||
ABIRegister(llvm::StringRef Name) :
|
||||
Name(Name), QemuName(Name), MContextIndex(NotInMContext) {}
|
||||
ABIRegister(model::Register::Values ID, unsigned MContextIndex) :
|
||||
ID(ID), MContextIndex(MContextIndex) {}
|
||||
|
||||
ABIRegister(llvm::StringRef Name, llvm::StringRef QemuName) :
|
||||
Name(Name), QemuName(QemuName), MContextIndex(NotInMContext) {}
|
||||
model::Register::Values id() const { return ID; }
|
||||
|
||||
llvm::StringRef name() const { return Name; }
|
||||
llvm::StringRef name() const { return model::Register::getRegisterName(ID); }
|
||||
|
||||
llvm::StringRef qemuName() const { return QemuName; }
|
||||
llvm::StringRef csvName() const { return toCSVName(ID); }
|
||||
|
||||
bool inMContext() const { return MContextIndex != NotInMContext; }
|
||||
unsigned mcontextIndex() const {
|
||||
revng_assert(inMContext());
|
||||
return MContextIndex;
|
||||
}
|
||||
|
||||
public:
|
||||
static llvm::StringRef toCSVName(model::Register::Values ID) {
|
||||
using namespace model::Register;
|
||||
|
||||
switch (ID) {
|
||||
case Invalid:
|
||||
revng_abort();
|
||||
case xmm0_x86_64:
|
||||
return "state_0x8558";
|
||||
case xmm1_x86_64:
|
||||
return "state_0x8598";
|
||||
case xmm2_x86_64:
|
||||
return "state_0x85d8";
|
||||
case xmm3_x86_64:
|
||||
return "state_0x8618";
|
||||
case xmm4_x86_64:
|
||||
return "state_0x8658";
|
||||
case xmm5_x86_64:
|
||||
return "state_0x8698";
|
||||
case xmm6_x86_64:
|
||||
return "state_0x86d8";
|
||||
case xmm7_x86_64:
|
||||
return "state_0x8718";
|
||||
default:
|
||||
return model::Register::getRegisterName(ID);
|
||||
}
|
||||
}
|
||||
|
||||
static model::Register::Values
|
||||
fromCSVName(llvm::StringRef Name, llvm::Triple::ArchType Arch) {
|
||||
using namespace model::Register;
|
||||
|
||||
if (Arch == llvm::Triple::x86_64) {
|
||||
if (Name == "state_0x8558") {
|
||||
return xmm0_x86_64;
|
||||
} else if (Name == "state_0x8598") {
|
||||
return xmm1_x86_64;
|
||||
} else if (Name == "state_0x85d8") {
|
||||
return xmm2_x86_64;
|
||||
} else if (Name == "state_0x8618") {
|
||||
return xmm3_x86_64;
|
||||
} else if (Name == "state_0x8658") {
|
||||
return xmm4_x86_64;
|
||||
} else if (Name == "state_0x8698") {
|
||||
return xmm5_x86_64;
|
||||
} else if (Name == "state_0x86d8") {
|
||||
return xmm6_x86_64;
|
||||
} else if (Name == "state_0x8718") {
|
||||
return xmm7_x86_64;
|
||||
}
|
||||
}
|
||||
|
||||
return model::Register::fromRegisterName(Name, Arch);
|
||||
}
|
||||
};
|
||||
|
||||
namespace JTReason {
|
||||
|
||||
@@ -7,5 +7,6 @@ revng_add_analyses_library_internal(revngFunctionIsolation
|
||||
IsolateFunctions.cpp)
|
||||
|
||||
target_link_libraries(revngFunctionIsolation
|
||||
revngModel
|
||||
revngStackAnalysis
|
||||
revngSupport)
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "revng/ADT/LazySmallBitVector.h"
|
||||
#include "revng/ADT/SmallMap.h"
|
||||
#include "revng/FunctionIsolation/EnforceABI.h"
|
||||
#include "revng/StackAnalysis/FunctionsSummary.h"
|
||||
#include "revng/Support/IRHelpers.h"
|
||||
#include "revng/Support/OpaqueFunctionsPool.h"
|
||||
|
||||
@@ -47,43 +46,55 @@ static cl::opt<bool> DisableSafetyChecks("disable-enforce-abi-safety-checks",
|
||||
cl::cat(MainCategory),
|
||||
cl::init(false));
|
||||
|
||||
static bool shouldEmit(FunctionRegisterArgument V) {
|
||||
switch (V.value()) {
|
||||
case FunctionRegisterArgument::Yes:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
static bool shouldEmit(model::RegisterState::Values V) {
|
||||
return (V == model::RegisterState::Yes or V == model::RegisterState::YesOrDead
|
||||
or V == model::RegisterState::Dead);
|
||||
}
|
||||
|
||||
static bool shouldEmit(FunctionCallRegisterArgument V) {
|
||||
switch (V.value()) {
|
||||
case FunctionCallRegisterArgument::Yes:
|
||||
case FunctionCallRegisterArgument::Dead:
|
||||
static bool areCompatible(model::RegisterState::Values LHS,
|
||||
model::RegisterState::Values RHS) {
|
||||
using namespace model::RegisterState;
|
||||
|
||||
if (LHS == RHS or LHS == Maybe or RHS == Maybe)
|
||||
return true;
|
||||
default:
|
||||
|
||||
switch (LHS) {
|
||||
case NoOrDead:
|
||||
return RHS == No or RHS == Dead;
|
||||
case YesOrDead:
|
||||
return RHS == Yes or RHS == Dead;
|
||||
case No:
|
||||
return RHS == NoOrDead;
|
||||
case Yes:
|
||||
return RHS == YesOrDead;
|
||||
case Dead:
|
||||
return RHS == NoOrDead or RHS == YesOrDead;
|
||||
case Contradiction:
|
||||
return false;
|
||||
case Invalid:
|
||||
default:
|
||||
revng_abort();
|
||||
}
|
||||
|
||||
revng_abort();
|
||||
}
|
||||
|
||||
static bool shouldEmit(FunctionReturnValue V) {
|
||||
switch (V.value()) {
|
||||
case FunctionReturnValue::YesOrDead:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
static bool areCompatible(const model::FunctionABIRegister &LHS,
|
||||
const model::FunctionABIRegister &RHS) {
|
||||
return areCompatible(LHS.Argument, RHS.Argument)
|
||||
and areCompatible(LHS.ReturnValue, RHS.ReturnValue);
|
||||
}
|
||||
|
||||
static bool shouldEmit(FunctionCallReturnValue V) {
|
||||
switch (V.value()) {
|
||||
case FunctionCallReturnValue::YesOrDead:
|
||||
case FunctionCallReturnValue::Yes:
|
||||
case FunctionCallReturnValue::Dead:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
static StringRef areCompatible(const model::Function &Callee,
|
||||
const model::FunctionEdge &CallSite) {
|
||||
for (const model::FunctionABIRegister &Register : Callee.Registers) {
|
||||
auto It = CallSite.Registers.find(Register.Register);
|
||||
if (It != CallSite.Registers.end() and not areCompatible(Register, *It)) {
|
||||
return model::Register::getName(Register.Register);
|
||||
}
|
||||
}
|
||||
|
||||
return StringRef();
|
||||
}
|
||||
|
||||
class HelperCallSite {
|
||||
@@ -132,30 +143,33 @@ private:
|
||||
|
||||
class EnforceABIImpl {
|
||||
public:
|
||||
EnforceABIImpl(Module &M, const GeneratedCodeBasicInfo &GCBI) :
|
||||
EnforceABIImpl(Module &M,
|
||||
GeneratedCodeBasicInfo &GCBI,
|
||||
const model::Binary &Binary) :
|
||||
M(M),
|
||||
GCBI(GCBI),
|
||||
FunctionDispatcher(M.getFunction("function_dispatcher")),
|
||||
Context(M.getContext()),
|
||||
IndirectPlaceholderPool(&M, false) {}
|
||||
IndirectPlaceholderPool(&M, false),
|
||||
Binary(Binary) {}
|
||||
|
||||
void run();
|
||||
|
||||
private:
|
||||
FunctionsSummary::FunctionDescription handleFunction(Function &F);
|
||||
Function *handleFunction(Function &F, const model::Function &FunctionModel);
|
||||
|
||||
void handleRegularFunctionCall(CallInst *Call);
|
||||
void handleHelperFunctionCall(CallInst *Call);
|
||||
void generateCall(IRBuilder<> &Builder,
|
||||
Function *Callee,
|
||||
FunctionsSummary::CallSiteDescription &CallSite);
|
||||
const model::FunctionEdge &CallSite);
|
||||
void replaceCSVsWithAlloca();
|
||||
void handleRoot();
|
||||
|
||||
private:
|
||||
Module &M;
|
||||
const GeneratedCodeBasicInfo &GCBI;
|
||||
std::map<Function *, FunctionsSummary::FunctionDescription> FunctionsMap;
|
||||
GeneratedCodeBasicInfo &GCBI;
|
||||
std::map<Function *, const model::Function *> FunctionsMap;
|
||||
std::map<Function *, Function *> OldToNew;
|
||||
Function *FunctionDispatcher;
|
||||
Function *OpaquePC;
|
||||
@@ -163,12 +177,14 @@ private:
|
||||
std::map<HelperCallSite, Function *> HelperCallSites;
|
||||
std::map<GlobalVariable *, unsigned> CSVToIndex;
|
||||
OpaqueFunctionsPool<FunctionType *> IndirectPlaceholderPool;
|
||||
const model::Binary &Binary;
|
||||
};
|
||||
|
||||
bool EnforceABI::runOnModule(Module &M) {
|
||||
auto &GCBI = getAnalysis<GeneratedCodeBasicInfoWrapperPass>().getGCBI();
|
||||
const model::Binary &Binary = getAnalysis<LoadModelPass>().getReadOnlyModel();
|
||||
|
||||
EnforceABIImpl Impl(M, GCBI);
|
||||
EnforceABIImpl Impl(M, GCBI, Binary);
|
||||
Impl.run();
|
||||
return false;
|
||||
}
|
||||
@@ -284,6 +300,12 @@ createHelperWrapper(Function *Helper,
|
||||
}
|
||||
|
||||
void EnforceABIImpl::run() {
|
||||
// Assign an index to each CSV
|
||||
unsigned I = 0;
|
||||
for (GlobalVariable *CSV : GCBI.csvs()) {
|
||||
CSVToIndex[CSV] = I;
|
||||
I++;
|
||||
}
|
||||
|
||||
// Declare an opaque function used later to obtain a value to store in the
|
||||
// local %pc alloca, so that we don't incur in error when removing the bad
|
||||
@@ -297,81 +319,45 @@ void EnforceABIImpl::run() {
|
||||
OpaquePC->addFnAttr(Attribute::NoUnwind);
|
||||
OpaquePC->addFnAttr(Attribute::ReadOnly);
|
||||
|
||||
// Collect functions we need to handle
|
||||
std::vector<Function *> Functions;
|
||||
for (Function &F : M)
|
||||
if (F.getName().startswith("bb."))
|
||||
Functions.push_back(&F);
|
||||
std::vector<Function *> OldFunctions;
|
||||
for (const model::Function &FunctionModel : Binary.Functions) {
|
||||
if (FunctionModel.Type == model::FunctionType::Fake)
|
||||
continue;
|
||||
|
||||
// Recreate the functions with the appropriate set of arguments
|
||||
for (Function *F : Functions) {
|
||||
const FunctionDescription &Descriptor = handleFunction(*F);
|
||||
auto *NewFunction = cast<Function>(Descriptor.Function);
|
||||
FunctionsMap[NewFunction] = Descriptor;
|
||||
OldToNew[F] = NewFunction;
|
||||
revng_assert(FunctionModel.Name.size() != 0);
|
||||
Function *OldFunction = M.getFunction(FunctionModel.Name);
|
||||
revng_assert(OldFunction != nullptr);
|
||||
OldFunctions.push_back(OldFunction);
|
||||
Function *NewFunction = handleFunction(*OldFunction, FunctionModel);
|
||||
FunctionsMap[NewFunction] = &FunctionModel;
|
||||
OldToNew[OldFunction] = NewFunction;
|
||||
}
|
||||
|
||||
unsigned I = 0;
|
||||
for (GlobalVariable *CSV : GCBI.csvs()) {
|
||||
CSVToIndex[CSV] = I;
|
||||
I++;
|
||||
}
|
||||
|
||||
// Collect the list of function calls we intend to handle
|
||||
std::vector<CallInst *> RegularFunctionCalls;
|
||||
std::vector<CallInst *> HelperFunctionCalls;
|
||||
for (auto &P : FunctionsMap) {
|
||||
Function &F = *cast<Function>(P.second.Function);
|
||||
|
||||
for (BasicBlock &BB : F) {
|
||||
for (Instruction &I : BB) {
|
||||
|
||||
auto *FuncCall = I.getMetadata("func.call");
|
||||
if (FuncCall != nullptr) {
|
||||
CallInst *Call = cast<CallInst>(&I);
|
||||
if (isCallToHelper(Call)) {
|
||||
HelperFunctionCalls.push_back(Call);
|
||||
} else {
|
||||
RegularFunctionCalls.push_back(Call);
|
||||
}
|
||||
}
|
||||
|
||||
auto *Call = dyn_cast<CallInst>(&I);
|
||||
|
||||
if (Call == nullptr or isMarker(Call))
|
||||
continue;
|
||||
|
||||
Value *CalledValue = Call->getCalledValue();
|
||||
Function *Callee = cast_or_null<Function>(skipCasts(CalledValue));
|
||||
|
||||
if (Callee != nullptr and Callee->isIntrinsic())
|
||||
continue;
|
||||
|
||||
if (FuncCall == nullptr) {
|
||||
BasicBlock *BB = Call->getParent();
|
||||
dbg << "Call " << getName(Call) << " ("
|
||||
<< "in function " << getName(BB->getParent())
|
||||
<< ", with unique predecessor "
|
||||
<< getName(BB->getUniquePredecessor()) << ")"
|
||||
<< " to " << getName(Callee) << " (now "
|
||||
<< getName(OldToNew[Callee]) << ")"
|
||||
<< " doesn't have func.call metadata\n";
|
||||
// revng_abort();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Callee == nullptr)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto IsInIsolatedFunction = [this](Instruction *I) -> bool {
|
||||
return FunctionsMap.count(I->getParent()->getParent()) != 0;
|
||||
};
|
||||
|
||||
// Handle function calls in isolated functions
|
||||
for (CallInst *Call : RegularFunctionCalls)
|
||||
std::vector<CallInst *> RegularCalls;
|
||||
for (auto *F : OldFunctions)
|
||||
for (User *U : F->users())
|
||||
if (auto *Call = dyn_cast<CallInst>(skipCasts(U)))
|
||||
if (IsInIsolatedFunction(Call))
|
||||
RegularCalls.push_back(Call);
|
||||
|
||||
for (CallInst *Call : RegularCalls)
|
||||
handleRegularFunctionCall(Call);
|
||||
|
||||
// Handle function calls to helpers in isolated functions
|
||||
for (CallInst *Call : HelperFunctionCalls)
|
||||
std::vector<CallInst *> HelperCalls;
|
||||
for (Function &F : M.functions())
|
||||
if (isHelper(&F))
|
||||
for (User *U : F.users())
|
||||
if (auto *Call = dyn_cast<CallInst>(skipCasts(U)))
|
||||
if (IsInIsolatedFunction(Call))
|
||||
HelperCalls.push_back(Call);
|
||||
|
||||
for (CallInst *Call : HelperCalls)
|
||||
handleHelperFunctionCall(Call);
|
||||
|
||||
// Handle invoke instructions in `root`
|
||||
@@ -387,9 +373,12 @@ void EnforceABIImpl::run() {
|
||||
BasicBlock::Create(Context, "", FunctionDispatcher));
|
||||
}
|
||||
|
||||
// Drop all the old functions
|
||||
for (Function *Function : Functions)
|
||||
Function->eraseFromParent();
|
||||
// Drop all the old functions, after we stole all of its blocks
|
||||
for (Function *OldFunction : OldFunctions) {
|
||||
for (User *U : OldFunction->users())
|
||||
cast<Instruction>(U)->getParent()->dump();
|
||||
OldFunction->eraseFromParent();
|
||||
}
|
||||
|
||||
// Quick and dirty DCE
|
||||
for (auto &P : FunctionsMap) {
|
||||
@@ -412,12 +401,10 @@ void EnforceABIImpl::run() {
|
||||
BB->eraseFromParent();
|
||||
}
|
||||
|
||||
bool Invalid;
|
||||
{
|
||||
if (VerifyLog.isEnabled()) {
|
||||
raw_os_ostream Stream(dbg);
|
||||
Invalid = verifyModule(M, &Stream);
|
||||
revng_assert(not verifyModule(M, &Stream));
|
||||
}
|
||||
revng_assert(not Invalid);
|
||||
}
|
||||
|
||||
void EnforceABIImpl::handleRoot() {
|
||||
@@ -427,26 +414,26 @@ void EnforceABIImpl::handleRoot() {
|
||||
|
||||
for (BasicBlock &BB : *Root) {
|
||||
// Find invoke instruction
|
||||
auto It = BB.begin();
|
||||
auto End = BB.end();
|
||||
if (It == End)
|
||||
continue;
|
||||
auto *Invoke = dyn_cast<InvokeInst>(&*It);
|
||||
It++;
|
||||
auto *Invoke = dyn_cast<InvokeInst>(BB.getTerminator());
|
||||
|
||||
if (It != End or Invoke == nullptr)
|
||||
if (Invoke == nullptr)
|
||||
continue;
|
||||
|
||||
revng_assert(BB.size() == 1);
|
||||
|
||||
Function *Callee = OldToNew.at(Invoke->getCalledFunction());
|
||||
FunctionsSummary::FunctionDescription &Function = FunctionsMap.at(Callee);
|
||||
const model::Function *Function = FunctionsMap.at(Callee);
|
||||
|
||||
// Collect arguments
|
||||
IRBuilder<> Builder(Invoke);
|
||||
std::vector<Value *> Arguments;
|
||||
for (auto &P : Function.RegisterSlots) {
|
||||
GlobalVariable *CSV = P.first;
|
||||
if (shouldEmit(P.second.Argument))
|
||||
for (const model::FunctionABIRegister &Register : Function->Registers) {
|
||||
if (shouldEmit(Register.Argument)) {
|
||||
auto Name = ABIRegister::toCSVName(Register.Register);
|
||||
GlobalVariable *CSV = M.getGlobalVariable(Name, true);
|
||||
revng_assert(CSV != nullptr);
|
||||
Arguments.push_back(Builder.CreateLoad(CSV));
|
||||
}
|
||||
}
|
||||
|
||||
// Create the new invoke with the appropriate arguments
|
||||
@@ -463,41 +450,25 @@ void EnforceABIImpl::handleRoot() {
|
||||
}
|
||||
}
|
||||
|
||||
FunctionsSummary::FunctionDescription
|
||||
EnforceABIImpl::handleFunction(Function &F) {
|
||||
QuickMetadata QMD(Context);
|
||||
|
||||
auto *Tuple = cast<MDTuple>(F.getMetadata("revng.func.entry"));
|
||||
|
||||
FunctionDescription Description;
|
||||
Function *EnforceABIImpl::handleFunction(Function &OldFunction,
|
||||
const model::Function &FunctionModel) {
|
||||
SmallVector<Type *, 8> ArgumentsTypes;
|
||||
SmallVector<GlobalVariable *, 8> ArgumentCSVs;
|
||||
SmallVector<Type *, 8> ReturnTypes;
|
||||
SmallVector<GlobalVariable *, 8> ReturnCSVs;
|
||||
|
||||
auto OperandsRange = QMD.extract<MDTuple *>(Tuple, 4)->operands();
|
||||
for (const MDOperand &Operand : OperandsRange) {
|
||||
auto *SlotTuple = cast<MDTuple>(Operand.get());
|
||||
|
||||
auto *CSV = cast<GlobalVariable>(QMD.extract<Constant *>(SlotTuple, 0));
|
||||
|
||||
StringRef ArgumentName = QMD.extract<StringRef>(SlotTuple, 1);
|
||||
auto Argument = FunctionRegisterArgument::fromName(ArgumentName);
|
||||
|
||||
StringRef ReturnValueName = QMD.extract<StringRef>(SlotTuple, 2);
|
||||
auto ReturnValue = FunctionReturnValue::fromName(ReturnValueName);
|
||||
|
||||
Description.RegisterSlots[CSV] = FunctionRegisterDescription{ Argument,
|
||||
ReturnValue };
|
||||
for (const model::FunctionABIRegister &Register : FunctionModel.Registers) {
|
||||
auto Name = ABIRegister::toCSVName(Register.Register);
|
||||
auto *CSV = cast<GlobalVariable>(M.getGlobalVariable(Name, true));
|
||||
|
||||
// Collect arguments
|
||||
if (shouldEmit(Argument)) {
|
||||
if (shouldEmit(Register.Argument)) {
|
||||
ArgumentsTypes.push_back(CSV->getType()->getPointerElementType());
|
||||
ArgumentCSVs.push_back(CSV);
|
||||
}
|
||||
|
||||
// Collect return values
|
||||
if (shouldEmit(ReturnValue)) {
|
||||
if (shouldEmit(Register.ReturnValue)) {
|
||||
ReturnTypes.push_back(CSV->getType()->getPointerElementType());
|
||||
ReturnCSVs.push_back(CSV);
|
||||
}
|
||||
@@ -517,22 +488,18 @@ EnforceABIImpl::handleFunction(Function &F) {
|
||||
auto *NewFunction = Function::Create(NewType,
|
||||
GlobalValue::ExternalLinkage,
|
||||
"",
|
||||
F.getParent());
|
||||
NewFunction->takeName(&F);
|
||||
NewFunction->copyAttributesFrom(&F);
|
||||
NewFunction->setMetadata("revng.func.entry",
|
||||
F.getMetadata("revng.func.entry"));
|
||||
Description.Function = NewFunction;
|
||||
OldFunction.getParent());
|
||||
NewFunction->takeName(&OldFunction);
|
||||
NewFunction->copyAttributesFrom(&OldFunction);
|
||||
|
||||
{
|
||||
unsigned I = 0;
|
||||
for (Argument &Argument : NewFunction->args())
|
||||
Argument.setName(ArgumentCSVs[I++]->getName());
|
||||
}
|
||||
// Set argument names
|
||||
unsigned I = 0;
|
||||
for (Argument &Argument : NewFunction->args())
|
||||
Argument.setName(ArgumentCSVs[I++]->getName());
|
||||
|
||||
// Steal body from the old function
|
||||
std::vector<BasicBlock *> Body;
|
||||
for (BasicBlock &BB : F)
|
||||
for (BasicBlock &BB : OldFunction)
|
||||
Body.push_back(&BB);
|
||||
auto &NewBody = NewFunction->getBasicBlockList();
|
||||
for (BasicBlock *BB : Body) {
|
||||
@@ -545,12 +512,8 @@ EnforceABIImpl::handleFunction(Function &F) {
|
||||
// Store arguments to CSVs
|
||||
BasicBlock &Entry = NewFunction->getEntryBlock();
|
||||
IRBuilder<> StoreBuilder(Entry.getTerminator());
|
||||
unsigned I = 0;
|
||||
for (Argument &TheArgument : NewFunction->args()) {
|
||||
auto *CSV = ArgumentCSVs[I];
|
||||
for (const auto &[TheArgument, CSV] : zip(NewFunction->args(), ArgumentCSVs))
|
||||
StoreBuilder.CreateStore(&TheArgument, CSV);
|
||||
I++;
|
||||
}
|
||||
|
||||
// Build the return value
|
||||
if (ReturnCSVs.size() != 0) {
|
||||
@@ -571,7 +534,7 @@ EnforceABIImpl::handleFunction(Function &F) {
|
||||
}
|
||||
}
|
||||
|
||||
return Description;
|
||||
return NewFunction;
|
||||
}
|
||||
|
||||
void EnforceABIImpl::handleHelperFunctionCall(CallInst *Call) {
|
||||
@@ -605,15 +568,10 @@ void EnforceABIImpl::handleHelperFunctionCall(CallInst *Call) {
|
||||
//
|
||||
IRBuilder<> Builder(Call);
|
||||
|
||||
SmallVector<Value *, 16> NewArguments;
|
||||
|
||||
// Initialize the new set of arguments with the old ones
|
||||
unsigned I = 0;
|
||||
for (Value *V : make_range(Call->arg_begin(), Call->arg_end())) {
|
||||
auto *Ty = HelperType->getParamType(I);
|
||||
NewArguments.push_back(Builder.CreateBitOrPointerCast(V, Ty));
|
||||
I++;
|
||||
}
|
||||
SmallVector<Value *, 16> NewArguments;
|
||||
for (auto [Argument, Type] : zip(Call->args(), HelperType->params()))
|
||||
NewArguments.push_back(Builder.CreateBitOrPointerCast(Argument, Type));
|
||||
|
||||
// Add arguments read
|
||||
for (GlobalVariable *CSV : UsedCSVs.Read)
|
||||
@@ -650,54 +608,38 @@ void EnforceABIImpl::handleHelperFunctionCall(CallInst *Call) {
|
||||
}
|
||||
|
||||
void EnforceABIImpl::handleRegularFunctionCall(CallInst *Call) {
|
||||
Function *Caller = Call->getParent()->getParent();
|
||||
const model::Function &FunctionModel = *FunctionsMap.at(Caller);
|
||||
|
||||
revng_assert(Call->getParent()->getParent()->getName() == FunctionModel.Name);
|
||||
|
||||
Function *Callee = cast<Function>(skipCasts(Call->getCalledValue()));
|
||||
bool IsDirect = (Callee != FunctionDispatcher);
|
||||
if (IsDirect)
|
||||
Callee = OldToNew.at(Callee);
|
||||
|
||||
using namespace StackAnalysis;
|
||||
QuickMetadata QMD(Context);
|
||||
|
||||
// Temporary variable we're going to populate using metadata
|
||||
CallSiteDescription CallSite(Call, nullptr);
|
||||
|
||||
auto *FuncCall = cast<MDTuple>(Call->getMetadata("func.call"));
|
||||
|
||||
//
|
||||
// Collect arguments and return values
|
||||
//
|
||||
auto *Slots = cast<MDTuple>(QMD.extract<MDTuple *>(FuncCall, 0));
|
||||
for (const MDOperand &Operand : Slots->operands()) {
|
||||
auto *SlotTuple = cast<MDTuple>(Operand.get());
|
||||
|
||||
auto *CSV = cast<GlobalVariable>(QMD.extract<Constant *>(SlotTuple, 0));
|
||||
|
||||
StringRef ArgumentName = QMD.extract<StringRef>(SlotTuple, 1);
|
||||
auto Argument = FunctionCallRegisterArgument::fromName(ArgumentName);
|
||||
|
||||
StringRef ReturnValueName = QMD.extract<StringRef>(SlotTuple, 2);
|
||||
auto ReturnValue = FunctionCallReturnValue::fromName(ReturnValueName);
|
||||
|
||||
CallSite.RegisterSlots[CSV] = FunctionCallRegisterDescription{
|
||||
Argument, ReturnValue
|
||||
};
|
||||
// Identify the corresponding call site in the model
|
||||
MetaAddress BasicBlockAddress = GCBI.getJumpTarget(Call->getParent());
|
||||
const model::BasicBlock &Block = FunctionModel.CFG.at(BasicBlockAddress);
|
||||
const model::FunctionEdge *CallSite = nullptr;
|
||||
for (const model::FunctionEdge &Edge : Block.Successors) {
|
||||
using namespace model::FunctionEdgeType;
|
||||
if (Edge.Type == FunctionCall or Edge.Type == IndirectCall
|
||||
or Edge.Type == IndirectTailCall) {
|
||||
CallSite = &Edge;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (DisableSafetyChecks or IsDirect) {
|
||||
// The callee is a well-known callee, generate a direct call
|
||||
IRBuilder<> Builder(Call);
|
||||
generateCall(Builder, Callee, CallSite);
|
||||
generateCall(Builder, Callee, *CallSite);
|
||||
|
||||
// Create an additional store to the local %pc, so that the optimizer cannot
|
||||
// do stuff with llvm.assume.
|
||||
revng_assert(OpaquePC != nullptr);
|
||||
auto *OpaqueValue = Builder.CreateCall(OpaquePC);
|
||||
|
||||
// The store here is done in the global CSV, since the
|
||||
// `replaceCSVsWithAlloca` method pass after us, and place the corresponding
|
||||
// alloca here.
|
||||
Value *PCCSV = GCBI.pcReg();
|
||||
Builder.CreateStore(OpaqueValue, PCCSV);
|
||||
Builder.CreateStore(Builder.CreateCall(OpaquePC), GCBI.pcReg());
|
||||
|
||||
} else {
|
||||
// If it's an indirect call, enumerate all the compatible callees and
|
||||
@@ -710,55 +652,56 @@ void EnforceABIImpl::handleRegularFunctionCall(CallInst *Call) {
|
||||
BeforeSplit->getTerminator()->eraseFromParent();
|
||||
|
||||
IRBuilder<> Builder(BeforeSplit);
|
||||
Value *PCCSV = GCBI.pcReg();
|
||||
Value *PC = Builder.CreateLoad(PCCSV);
|
||||
BasicBlock *UnexpectedPC = findByBlockType(AfterSplit->getParent(),
|
||||
BlockType::UnexpectedPCBlock);
|
||||
revng_assert(UnexpectedPC != nullptr);
|
||||
SwitchInst *Switch = Builder.CreateSwitch(PC, UnexpectedPC);
|
||||
|
||||
unsigned I = 0;
|
||||
ProgramCounterHandler::DispatcherTargets Targets;
|
||||
|
||||
unsigned Count = 0;
|
||||
for (auto &P : FunctionsMap) {
|
||||
Function *F = P.first;
|
||||
FunctionDescription &Description = P.second;
|
||||
|
||||
for (auto &[F, FunctionModel] : FunctionsMap) {
|
||||
EnforceABILog << " " << F->getName().data() << " ";
|
||||
if (GlobalVariable *CSV = CallSite.isCompatibleWith(Description)) {
|
||||
EnforceABILog << "[No: " << CSV->getName().data() << "]";
|
||||
|
||||
// Check compatibility
|
||||
StringRef IncompatibleCSV = areCompatible(*FunctionModel, *CallSite);
|
||||
bool Incompatible = not IncompatibleCSV.empty();
|
||||
if (Incompatible) {
|
||||
EnforceABILog << "[No: " << IncompatibleCSV.data() << "]";
|
||||
} else {
|
||||
EnforceABILog << "[Yes]";
|
||||
Count++;
|
||||
|
||||
auto *Tuple = cast<MDTuple>(F->getMetadata("revng.func.entry"));
|
||||
revng_assert(Tuple != nullptr);
|
||||
auto PC = MetaAddress::fromConstant(QMD.extract<Constant *>(Tuple, 1));
|
||||
|
||||
// Create the basic block containing the call
|
||||
auto *Case = BasicBlock::Create(Context,
|
||||
"",
|
||||
BeforeSplit->getParent(),
|
||||
AfterSplit);
|
||||
auto *Ty = cast<IntegerType>(PCCSV->getType()->getPointerElementType());
|
||||
Switch->addCase(ConstantInt::get(Ty, PC.asPC()), Case);
|
||||
|
||||
Builder.SetInsertPoint(Case);
|
||||
generateCall(Builder, F, CallSite);
|
||||
generateCall(Builder, F, *CallSite);
|
||||
Builder.CreateBr(AfterSplit);
|
||||
|
||||
// Record for inline dispatcher
|
||||
Targets.push_back({ FunctionModel->Entry, Case });
|
||||
}
|
||||
EnforceABILog << DoLog;
|
||||
|
||||
I++;
|
||||
}
|
||||
|
||||
// Actually create the inline dispatcher
|
||||
Builder.SetInsertPoint(BeforeSplit);
|
||||
GCBI.programCounterHandler()->buildDispatcher(Targets,
|
||||
Builder,
|
||||
UnexpectedPC,
|
||||
{});
|
||||
|
||||
EnforceABILog << Count << " functions" << DoLog;
|
||||
}
|
||||
|
||||
// Drop the original call
|
||||
Call->eraseFromParent();
|
||||
}
|
||||
|
||||
void EnforceABIImpl::generateCall(IRBuilder<> &Builder,
|
||||
Function *Callee,
|
||||
CallSiteDescription &CallSite) {
|
||||
const model::FunctionEdge &CallSite) {
|
||||
revng_assert(Callee != nullptr);
|
||||
|
||||
llvm::SmallVector<Type *, 8> ArgumentsTypes;
|
||||
@@ -771,15 +714,15 @@ void EnforceABIImpl::generateCall(IRBuilder<> &Builder,
|
||||
revng_assert(DisableSafetyChecks);
|
||||
|
||||
// Collect arguments, returns and their type.
|
||||
for (auto &P : CallSite.RegisterSlots) {
|
||||
GlobalVariable *CSV = P.first;
|
||||
|
||||
if (shouldEmit(P.second.Argument)) {
|
||||
for (const model::FunctionABIRegister &Register : CallSite.Registers) {
|
||||
auto Name = ABIRegister::toCSVName(Register.Register);
|
||||
GlobalVariable *CSV = M.getGlobalVariable(Name, true);
|
||||
if (shouldEmit(Register.Argument)) {
|
||||
ArgumentsTypes.push_back(CSV->getType()->getPointerElementType());
|
||||
Arguments.push_back(Builder.CreateLoad(CSV));
|
||||
}
|
||||
|
||||
if (shouldEmit(P.second.ReturnValue)) {
|
||||
if (shouldEmit(Register.ReturnValue)) {
|
||||
ReturnTypes.push_back(CSV->getType()->getPointerElementType());
|
||||
ReturnCSVs.push_back(CSV);
|
||||
}
|
||||
@@ -804,32 +747,34 @@ void EnforceABIImpl::generateCall(IRBuilder<> &Builder,
|
||||
} else {
|
||||
|
||||
// Additional debug checks if we are not emitting an indirect call.
|
||||
BasicBlock *InsertBlock = Builder.GetInsertPoint()->getParent();
|
||||
revng_log(EnforceABILog,
|
||||
"Emitting call to " << getName(Callee) << " from "
|
||||
<< getName(CallSite.Call));
|
||||
<< getName(InsertBlock));
|
||||
|
||||
FunctionDescription &Function = FunctionsMap.at(Callee);
|
||||
revng_assert(Function.Function != nullptr
|
||||
and Function.Function->getName().startswith("bb."));
|
||||
if (GlobalVariable *CSV = CallSite.isCompatibleWith(Function)) {
|
||||
dbg << (CallSite.Call == nullptr ? "nullptr" :
|
||||
getName(CallSite.Call).data())
|
||||
<< " -> "
|
||||
const model::Function *FunctionModel = FunctionsMap.at(Callee);
|
||||
revng_assert(Callee->getName().startswith("bb."));
|
||||
StringRef IncompatibleCSV = areCompatible(*FunctionModel, CallSite);
|
||||
bool Incompatible = not IncompatibleCSV.empty();
|
||||
if (Incompatible) {
|
||||
dbg << getName(InsertBlock) << " -> "
|
||||
<< (Callee == nullptr ? "nullptr" : Callee->getName().data()) << ": "
|
||||
<< CSV->getName().data() << "\n";
|
||||
<< IncompatibleCSV.data() << "\n";
|
||||
revng_abort();
|
||||
}
|
||||
|
||||
// Collect arguments, returns and their type.
|
||||
for (auto &P : Function.RegisterSlots) {
|
||||
GlobalVariable *CSV = P.first;
|
||||
for (const model::FunctionABIRegister &Register :
|
||||
FunctionModel->Registers) {
|
||||
auto Name = ABIRegister::toCSVName(Register.Register);
|
||||
GlobalVariable *CSV = M.getGlobalVariable(Name, true);
|
||||
|
||||
if (shouldEmit(P.second.Argument)) {
|
||||
if (shouldEmit(Register.Argument)) {
|
||||
ArgumentsTypes.push_back(CSV->getType()->getPointerElementType());
|
||||
Arguments.push_back(Builder.CreateLoad(CSV));
|
||||
}
|
||||
|
||||
if (shouldEmit(P.second.ReturnValue)) {
|
||||
if (shouldEmit(Register.ReturnValue)) {
|
||||
ReturnTypes.push_back(CSV->getType()->getPointerElementType());
|
||||
ReturnCSVs.push_back(CSV);
|
||||
}
|
||||
@@ -916,15 +861,15 @@ void EnforceABIImpl::replaceCSVsWithAlloca() {
|
||||
|
||||
// Ignore it if it's not a CSV
|
||||
auto CSVPosIt = CSVPosition.find(CSV);
|
||||
if (CSVPosIt == CSVPosition.end())
|
||||
continue;
|
||||
|
||||
auto CSVPos = CSVPosIt->second;
|
||||
CSVMaps[CSVPos][&F] = Alloca;
|
||||
if (CSVPosIt != CSVPosition.end()) {
|
||||
auto CSVPos = CSVPosIt->second;
|
||||
CSVMaps[CSVPos][&F] = Alloca;
|
||||
}
|
||||
}
|
||||
|
||||
Separator->eraseFromParent();
|
||||
}
|
||||
|
||||
// Substitute the uses of the GlobalVariables representing the CSVs with the
|
||||
// dedicate AllocaInst that were created in each Function.
|
||||
for (GlobalVariable *CSV : GCBI.csvs()) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -59,7 +59,8 @@ revng_add_analyses_library_internal(revngStackAnalysis
|
||||
|
||||
target_link_libraries(revngStackAnalysis
|
||||
revngBasicAnalyses
|
||||
revngSupport)
|
||||
revngSupport
|
||||
revngModel)
|
||||
|
||||
target_include_directories(revngStackAnalysis
|
||||
PRIVATE
|
||||
|
||||
@@ -12,9 +12,13 @@
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "llvm/ADT/DepthFirstIterator.h"
|
||||
#include "llvm/IR/Dominators.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/Pass.h"
|
||||
|
||||
#include "revng/BasicAnalyses/GeneratedCodeBasicInfo.h"
|
||||
#include "revng/Model/Binary.h"
|
||||
#include "revng/StackAnalysis/StackAnalysis.h"
|
||||
#include "revng/Support/CommandLine.h"
|
||||
#include "revng/Support/IRHelpers.h"
|
||||
@@ -65,6 +69,258 @@ static opt<std::string> ABIAnalysisOutputPath("abi-analysis-output",
|
||||
value_desc("path"),
|
||||
cat(MainCategory));
|
||||
|
||||
template<bool FunctionCall>
|
||||
static model::RegisterState::Values
|
||||
toRegisterState(RegisterArgument<FunctionCall> RA) {
|
||||
switch (RA.value()) {
|
||||
case RegisterArgument<FunctionCall>::NoOrDead:
|
||||
return model::RegisterState::NoOrDead;
|
||||
case RegisterArgument<FunctionCall>::Maybe:
|
||||
return model::RegisterState::Maybe;
|
||||
case RegisterArgument<FunctionCall>::Yes:
|
||||
return model::RegisterState::Yes;
|
||||
case RegisterArgument<FunctionCall>::Dead:
|
||||
return model::RegisterState::Dead;
|
||||
case RegisterArgument<FunctionCall>::Contradiction:
|
||||
return model::RegisterState::Contradiction;
|
||||
case RegisterArgument<FunctionCall>::No:
|
||||
return model::RegisterState::No;
|
||||
}
|
||||
|
||||
revng_abort();
|
||||
}
|
||||
|
||||
static model::RegisterState::Values toRegisterState(FunctionReturnValue RV) {
|
||||
switch (RV.value()) {
|
||||
case FunctionReturnValue::No:
|
||||
return model::RegisterState::No;
|
||||
case FunctionReturnValue::NoOrDead:
|
||||
return model::RegisterState::NoOrDead;
|
||||
case FunctionReturnValue::YesOrDead:
|
||||
return model::RegisterState::YesOrDead;
|
||||
case FunctionReturnValue::Maybe:
|
||||
return model::RegisterState::Maybe;
|
||||
case FunctionReturnValue::Contradiction:
|
||||
return model::RegisterState::Contradiction;
|
||||
}
|
||||
|
||||
revng_abort();
|
||||
}
|
||||
|
||||
static model::RegisterState::Values
|
||||
toRegisterState(FunctionCallReturnValue RV) {
|
||||
switch (RV.value()) {
|
||||
case FunctionCallReturnValue::No:
|
||||
return model::RegisterState::No;
|
||||
case FunctionCallReturnValue::NoOrDead:
|
||||
return model::RegisterState::NoOrDead;
|
||||
case FunctionCallReturnValue::YesOrDead:
|
||||
return model::RegisterState::YesOrDead;
|
||||
case FunctionCallReturnValue::Yes:
|
||||
return model::RegisterState::Yes;
|
||||
case FunctionCallReturnValue::Dead:
|
||||
return model::RegisterState::Dead;
|
||||
case FunctionCallReturnValue::Maybe:
|
||||
return model::RegisterState::Maybe;
|
||||
case FunctionCallReturnValue::Contradiction:
|
||||
return model::RegisterState::Contradiction;
|
||||
}
|
||||
|
||||
revng_abort();
|
||||
}
|
||||
|
||||
void commitToModel(GeneratedCodeBasicInfo &GCBI,
|
||||
Function *F,
|
||||
const FunctionsSummary &Summary,
|
||||
model::Binary &TheBinary);
|
||||
|
||||
void commitToModel(GeneratedCodeBasicInfo &GCBI,
|
||||
Function *F,
|
||||
const FunctionsSummary &Summary,
|
||||
model::Binary &TheBinary) {
|
||||
using namespace model;
|
||||
|
||||
for (const auto &[Entry, FunctionSummary] : Summary.Functions) {
|
||||
if (Entry == nullptr)
|
||||
continue;
|
||||
|
||||
//
|
||||
// Initialize model::Function
|
||||
//
|
||||
|
||||
// Get the entry point address
|
||||
MetaAddress EntryPC = getBasicBlockPC(Entry);
|
||||
revng_assert(EntryPC.isValid());
|
||||
|
||||
// Create the function
|
||||
revng_assert(TheBinary.Functions.count(EntryPC) == 0);
|
||||
model::Function &Function = TheBinary.Functions[EntryPC];
|
||||
|
||||
// Assign a name
|
||||
Function.Name = Entry->getName();
|
||||
revng_assert(Function.Name.size() != 0);
|
||||
|
||||
using FT = model::FunctionType::Values;
|
||||
Function.Type = static_cast<FT>(FunctionSummary.Type);
|
||||
|
||||
// Populate arguments and return values
|
||||
{
|
||||
auto Inserter = Function.Registers.batch_insert();
|
||||
for (auto &[CSV, FRD] : FunctionSummary.RegisterSlots) {
|
||||
auto ID = ABIRegister::fromCSVName(CSV->getName(), GCBI.arch());
|
||||
if (ID == model::Register::Invalid)
|
||||
continue;
|
||||
FunctionABIRegister TheRegister(ID);
|
||||
TheRegister.Argument = toRegisterState(FRD.Argument);
|
||||
TheRegister.ReturnValue = toRegisterState(FRD.ReturnValue);
|
||||
Inserter.insert(TheRegister);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &[BB, Branch] : FunctionSummary.BasicBlocks) {
|
||||
// Remap BranchType to FunctionEdgeType
|
||||
namespace FET = FunctionEdgeType;
|
||||
FET::Values EdgeType = FET::Invalid;
|
||||
bool IsCall = false;
|
||||
|
||||
switch (Branch) {
|
||||
case BranchType::Invalid:
|
||||
case BranchType::FakeFunction:
|
||||
case BranchType::RegularFunction:
|
||||
case BranchType::NoReturnFunction:
|
||||
case BranchType::UnhandledCall:
|
||||
revng_abort();
|
||||
break;
|
||||
|
||||
case BranchType::InstructionLocalCFG:
|
||||
EdgeType = FET::Invalid;
|
||||
break;
|
||||
|
||||
case BranchType::FunctionLocalCFG:
|
||||
EdgeType = FET::DirectBranch;
|
||||
break;
|
||||
|
||||
case BranchType::FakeFunctionCall:
|
||||
EdgeType = FET::FakeFunctionCall;
|
||||
break;
|
||||
|
||||
case BranchType::FakeFunctionReturn:
|
||||
EdgeType = FET::FakeFunctionReturn;
|
||||
break;
|
||||
|
||||
case BranchType::HandledCall:
|
||||
IsCall = true;
|
||||
EdgeType = FET::FunctionCall;
|
||||
break;
|
||||
|
||||
case BranchType::IndirectCall:
|
||||
IsCall = true;
|
||||
EdgeType = FET::IndirectCall;
|
||||
break;
|
||||
|
||||
case BranchType::Return:
|
||||
EdgeType = FET::Return;
|
||||
break;
|
||||
|
||||
case BranchType::BrokenReturn:
|
||||
EdgeType = FET::BrokenReturn;
|
||||
break;
|
||||
|
||||
case BranchType::IndirectTailCall:
|
||||
IsCall = true;
|
||||
EdgeType = FET::IndirectTailCall;
|
||||
break;
|
||||
|
||||
case BranchType::LongJmp:
|
||||
EdgeType = FET::LongJmp;
|
||||
break;
|
||||
|
||||
case BranchType::Killer:
|
||||
EdgeType = FET::Killer;
|
||||
break;
|
||||
|
||||
case BranchType::Unreachable:
|
||||
EdgeType = FET::Unreachable;
|
||||
break;
|
||||
}
|
||||
|
||||
if (EdgeType == FET::Invalid)
|
||||
continue;
|
||||
|
||||
// Identify Source address
|
||||
auto [Source, Size] = getPC(BB->getTerminator());
|
||||
Source += Size;
|
||||
revng_assert(Source.isValid());
|
||||
|
||||
// Identify Destination address
|
||||
llvm::BasicBlock *JumpTargetBB = GCBI.getJumpTargetBlock(BB);
|
||||
MetaAddress JumpTargetAddress = GCBI.getPCFromNewPC(JumpTargetBB);
|
||||
model::BasicBlock &CurrentBlock = Function.CFG[JumpTargetAddress];
|
||||
CurrentBlock.End = Source;
|
||||
CurrentBlock.Name = JumpTargetBB->getName();
|
||||
auto SuccessorsInserter = CurrentBlock.Successors.batch_insert();
|
||||
|
||||
if (EdgeType == FET::DirectBranch) {
|
||||
// Handle direct branch
|
||||
auto Successors = GCBI.getSuccessors(BB);
|
||||
for (const MetaAddress &Destination : Successors.Addresses)
|
||||
SuccessorsInserter.insert(FunctionEdge{ Destination, EdgeType });
|
||||
|
||||
} else if (EdgeType == FET::FakeFunctionReturn) {
|
||||
// Handle fake function return
|
||||
auto [First, Last] = FunctionSummary.FakeReturns.equal_range(BB);
|
||||
revng_assert(First != Last);
|
||||
for (const auto &[_, Destination] : make_range(First, Last))
|
||||
SuccessorsInserter.insert(FunctionEdge{ Destination, EdgeType });
|
||||
|
||||
} else if (IsCall) {
|
||||
// Handle call
|
||||
llvm::BasicBlock *Successor = BB->getSingleSuccessor();
|
||||
MetaAddress Destination = MetaAddress::invalid();
|
||||
if (Successor != nullptr)
|
||||
Destination = getBasicBlockPC(Successor);
|
||||
|
||||
// Record the edge in the CFG
|
||||
|
||||
auto &Edge = SuccessorsInserter.insert({ Destination, EdgeType });
|
||||
|
||||
bool Found = false;
|
||||
for (const FunctionsSummary::CallSiteDescription &CSD :
|
||||
FunctionSummary.CallSites) {
|
||||
if (not CSD.Call->isTerminator() or CSD.Call->getParent() != BB)
|
||||
continue;
|
||||
|
||||
revng_assert(not Found);
|
||||
Found = true;
|
||||
auto Inserter = Edge.Registers.batch_insert();
|
||||
for (auto &[CSV, FCRD] : CSD.RegisterSlots) {
|
||||
auto ID = ABIRegister::fromCSVName(CSV->getName(), GCBI.arch());
|
||||
if (ID == model::Register::Invalid)
|
||||
continue;
|
||||
FunctionABIRegister TheRegister(ID);
|
||||
TheRegister.Argument = toRegisterState(FCRD.Argument);
|
||||
TheRegister.ReturnValue = toRegisterState(FCRD.ReturnValue);
|
||||
Inserter.insert(TheRegister);
|
||||
}
|
||||
}
|
||||
revng_assert(Found);
|
||||
|
||||
} else {
|
||||
// Handle other successors
|
||||
llvm::BasicBlock *Successor = BB->getSingleSuccessor();
|
||||
MetaAddress Destination = MetaAddress::invalid();
|
||||
if (Successor != nullptr)
|
||||
Destination = getBasicBlockPC(Successor);
|
||||
|
||||
// Record the edge in the CFG
|
||||
SuccessorsInserter.insert(FunctionEdge{ Destination, EdgeType });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
revng_check(TheBinary.verify());
|
||||
}
|
||||
|
||||
template<bool AnalyzeABI>
|
||||
bool StackAnalysis<AnalyzeABI>::runOnModule(Module &M) {
|
||||
Function &F = *M.getFunction("root");
|
||||
@@ -73,6 +329,8 @@ bool StackAnalysis<AnalyzeABI>::runOnModule(Module &M) {
|
||||
|
||||
auto &GCBI = getAnalysis<GeneratedCodeBasicInfoWrapperPass>().getGCBI();
|
||||
|
||||
auto &LMP = getAnalysis<LoadModelPass>();
|
||||
|
||||
// The stack analysis works function-wise. We consider two sets of functions:
|
||||
// first (Force == true) those that are highly likely to be real functions
|
||||
// (i.e., they have a direct call) and then (Force == false) all the remaining
|
||||
@@ -177,10 +435,7 @@ bool StackAnalysis<AnalyzeABI>::runOnModule(Module &M) {
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream Output;
|
||||
GrandResult = Results.finalize(&M, &TheCache);
|
||||
GrandResult.dump(&M, Output);
|
||||
TextRepresentation = Output.str();
|
||||
|
||||
if (ClobberedLog.isEnabled()) {
|
||||
for (auto &P : GrandResult.Functions) {
|
||||
@@ -191,7 +446,12 @@ bool StackAnalysis<AnalyzeABI>::runOnModule(Module &M) {
|
||||
}
|
||||
}
|
||||
|
||||
revng_log(StackAnalysisLog, TextRepresentation);
|
||||
if (StackAnalysisLog.isEnabled()) {
|
||||
std::stringstream Output;
|
||||
GrandResult.dump(&M, Output);
|
||||
TextRepresentation = Output.str();
|
||||
revng_log(StackAnalysisLog, TextRepresentation);
|
||||
}
|
||||
|
||||
revng_log(PassesLog, "Ending StackAnalysis");
|
||||
|
||||
@@ -204,6 +464,8 @@ bool StackAnalysis<AnalyzeABI>::runOnModule(Module &M) {
|
||||
serialize(pathToStream(StackAnalysisOutputPath, Output));
|
||||
}
|
||||
|
||||
commitToModel(GCBI, &F, GrandResult, LMP.getWriteableModel());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+17
-54
@@ -28,7 +28,7 @@ int arch_prctl(int code, unsigned long *addr);
|
||||
|
||||
#endif
|
||||
|
||||
#include "revng/Runtime/commonconstants.h"
|
||||
#include "revng/Runtime/PrintPlainMetaAddress.h"
|
||||
|
||||
#include "support.h"
|
||||
|
||||
@@ -245,15 +245,18 @@ void g_free(void *memory) {
|
||||
return free(memory);
|
||||
}
|
||||
|
||||
void unknownPC() {
|
||||
void unknownPC(PlainMetaAddress *PC) {
|
||||
int arg;
|
||||
const char *error = "Unknown PC\n";
|
||||
write(2, error, strlen(error));
|
||||
fprintf(stderr, "Unknown PC:");
|
||||
fprint_metaaddress(stderr, PC);
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
for (arg = 0; arg < saved_argc; arg++) {
|
||||
write(2, saved_argv[arg], strlen(saved_argv[arg]));
|
||||
write(2, " ", 1);
|
||||
}
|
||||
write(2, "\n", 1);
|
||||
|
||||
abort();
|
||||
}
|
||||
|
||||
@@ -451,58 +454,18 @@ int main(int argc, char *argv[]) {
|
||||
root((target_reg) stack);
|
||||
}
|
||||
|
||||
// Helper function to debug informations when an exception is about to be
|
||||
// raised
|
||||
void exception_warning(Reason Code,
|
||||
target_reg Source,
|
||||
target_reg Target,
|
||||
target_reg ExpectedDestination) {
|
||||
switch (Code) {
|
||||
case StandardTranslatedBlock:
|
||||
fprintf(stderr,
|
||||
"Unexpected control-flow in isolated function: "
|
||||
"0x%" TARGET_REG_FORMAT " -> 0x%" TARGET_REG_FORMAT "\n",
|
||||
Source,
|
||||
Target);
|
||||
break;
|
||||
case StandardNonTranslatedBlock:
|
||||
fprintf(stderr,
|
||||
"Unexpected control-flow in isolated function after unexpectedpc "
|
||||
"or anypc block: 0x%" TARGET_REG_FORMAT "\n",
|
||||
Target);
|
||||
break;
|
||||
case BadReturnAddress:
|
||||
fprintf(stderr,
|
||||
"Expected and actual fallthrough after ret not corresponding: "
|
||||
"0x%" TARGET_REG_FORMAT " / 0x%" TARGET_REG_FORMAT "\n",
|
||||
Target,
|
||||
ExpectedDestination);
|
||||
break;
|
||||
case FunctionDispatcherFallBack:
|
||||
fprintf(stderr,
|
||||
"Erroneous call to function dispatcher: "
|
||||
"0x%" TARGET_REG_FORMAT "\n",
|
||||
Target);
|
||||
break;
|
||||
case ReturnFromNoReturn:
|
||||
fprintf(stderr,
|
||||
"Execution has return from a noreturn call: "
|
||||
"0x%" TARGET_REG_FORMAT "\n",
|
||||
Source);
|
||||
break;
|
||||
default:
|
||||
assert(0 && "Reason code not supported");
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function used to raise an exception
|
||||
noreturn void raise_exception_helper(Reason Code,
|
||||
target_reg Source,
|
||||
target_reg Target,
|
||||
target_reg ExpectedDestination) {
|
||||
noreturn void raise_exception_helper(const char *reason,
|
||||
PlainMetaAddress *source,
|
||||
PlainMetaAddress *destination) {
|
||||
|
||||
// Call the exception debug helper
|
||||
exception_warning(Code, Source, Target, ExpectedDestination);
|
||||
// Dump information about the exception
|
||||
fprintf(stderr, "Exception: %s", reason);
|
||||
fprintf(stderr, " (");
|
||||
fprint_metaaddress(stderr, source);
|
||||
fprintf(stderr, " -> ");
|
||||
fprint_metaaddress(stderr, source);
|
||||
fprintf(stderr, ")\n");
|
||||
|
||||
// Declare the exception object
|
||||
static struct _Unwind_Exception exc;
|
||||
|
||||
+2
-1
@@ -185,7 +185,8 @@ def build_opt_args(args):
|
||||
|
||||
return (prefix + [relative(get_command("opt"))]
|
||||
+ interleave(roots, "-load")
|
||||
+ args)
|
||||
+ args
|
||||
+ ["-serialize-model"])
|
||||
|
||||
def split_dash_dash(args):
|
||||
if not args:
|
||||
|
||||
@@ -52,7 +52,7 @@ macro(artifact_handler CATEGORY INPUT_FILE CONFIGURATION OUTPUT TARGET_NAME)
|
||||
|
||||
set(TEST_NAME test-lifted-${CATEGORY}-${ANALYSIS}-${TARGET_NAME})
|
||||
add_test(NAME ${TEST_NAME}
|
||||
COMMAND sh -c "./bin/revng opt --${ANALYSIS_OPT_${ANALYSIS}} --${ANALYSIS_OPT_${ANALYSIS}}-output=${ANALYSIS_OUTPUT} ${OUTPUT} -o /dev/null \
|
||||
COMMAND sh -c "./bin/revng opt --${ANALYSIS_OPT_${ANALYSIS}} --${ANALYSIS_OPT_${ANALYSIS}}-output=${ANALYSIS_OUTPUT} ${OUTPUT} --debug-log=stackanalysis -o /dev/null \
|
||||
&& ${ANALYSIS_DIFF_${ANALYSIS}} ${REFERENCE} ${ANALYSIS_OUTPUT}")
|
||||
set_tests_properties(${TEST_NAME} PROPERTIES LABELS "analysis;${CATEGORY};${CONFIGURATION};${ANALYSIS}")
|
||||
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
"slots" : [
|
||||
{
|
||||
"argument" : "Maybe",
|
||||
"slot" : "rax"
|
||||
"slot" : "rdx"
|
||||
},
|
||||
{
|
||||
"return_value" : "Maybe",
|
||||
"slot" : "rdx"
|
||||
"slot" : "rax"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -228,6 +228,7 @@ BinaryFile::BinaryFile(std::string FilePath, uint64_t PreferedBaseAddress) :
|
||||
|
||||
using RD = RelocationDescription;
|
||||
using namespace llvm::ELF;
|
||||
using namespace model::Register;
|
||||
Architecture::RelocationTypesMap RelocationTypes;
|
||||
|
||||
auto Arch = TheBinary->getArch();
|
||||
@@ -250,8 +251,8 @@ BinaryFile::BinaryFile(std::string FilePath, uint64_t PreferedBaseAddress) :
|
||||
RelocationTypes[R_386_GLOB_DAT] = RD(RD::SymbolRelative);
|
||||
RelocationTypes[R_386_COPY] = RD(RD::LabelOnly, RD::TargetValue);
|
||||
|
||||
ABIRegisters = { { "eax" }, { "ebx" }, { "ecx" }, { "edx" },
|
||||
{ "esi" }, { "edi" }, { "ebp" }, { "esp" } };
|
||||
ABIRegisters = { { eax_x86 }, { ebx_x86 }, { ecx_x86 }, { edx_x86 },
|
||||
{ esi_x86 }, { edi_x86 }, { ebp_x86 }, { esp_x86 } };
|
||||
|
||||
BasicBlockEndingPattern = "\xcc";
|
||||
|
||||
@@ -303,30 +304,16 @@ BinaryFile::BinaryFile(std::string FilePath, uint64_t PreferedBaseAddress) :
|
||||
// REGISTER_OFFSET(RIP);
|
||||
|
||||
// TODO: here we're hardcoding the offsets in the QEMU struct
|
||||
ABIRegisters = { { "rax", 0xD },
|
||||
{ "rbx", 0xB },
|
||||
{ "rcx", 0xE },
|
||||
{ "rdx", 0xC },
|
||||
{ "rbp", 0xA },
|
||||
{ "rsp", 0xF },
|
||||
{ "rsi", 0x9 },
|
||||
{ "rdi", 0x8 },
|
||||
{ "r8", 0x0 },
|
||||
{ "r9", 0x1 },
|
||||
{ "r10", 0x2 },
|
||||
{ "r11", 0x3 },
|
||||
{ "r12", 0x4 },
|
||||
{ "r13", 0x5 },
|
||||
{ "r14", 0x6 },
|
||||
{ "r15", 0x7 },
|
||||
{ "xmm0", "state_0x8558" },
|
||||
{ "xmm1", "state_0x8598" },
|
||||
{ "xmm2", "state_0x85d8" },
|
||||
{ "xmm3", "state_0x8618" },
|
||||
{ "xmm4", "state_0x8658" },
|
||||
{ "xmm5", "state_0x8698" },
|
||||
{ "xmm6", "state_0x86d8" },
|
||||
{ "xmm7", "state_0x8718" } };
|
||||
ABIRegisters = {
|
||||
{ rax_x86_64, 0xD }, { rbx_x86_64, 0xB }, { rcx_x86_64, 0xE },
|
||||
{ rdx_x86_64, 0xC }, { rbp_x86_64, 0xA }, { rsp_x86_64, 0xF },
|
||||
{ rsi_x86_64, 0x9 }, { rdi_x86_64, 0x8 }, { r8_x86_64, 0x0 },
|
||||
{ r9_x86_64, 0x1 }, { r10_x86_64, 0x2 }, { r11_x86_64, 0x3 },
|
||||
{ r12_x86_64, 0x4 }, { r13_x86_64, 0x5 }, { r14_x86_64, 0x6 },
|
||||
{ r15_x86_64, 0x7 }, { xmm0_x86_64 }, { xmm1_x86_64 },
|
||||
{ xmm2_x86_64 }, { xmm3_x86_64 }, { xmm4_x86_64 },
|
||||
{ xmm5_x86_64 }, { xmm6_x86_64 }, { xmm7_x86_64 }
|
||||
};
|
||||
WriteRegisterAsm = "movq $0, %REGISTER";
|
||||
ReadRegisterAsm = "movq %REGISTER, $0";
|
||||
JumpAsm = "jmpq *$0";
|
||||
@@ -355,9 +342,10 @@ BinaryFile::BinaryFile(std::string FilePath, uint64_t PreferedBaseAddress) :
|
||||
0x1, // exit
|
||||
0xb // execve
|
||||
};
|
||||
ABIRegisters = { { "r0" }, { "r1" }, { "r2" }, { "r3" }, { "r4" },
|
||||
{ "r5" }, { "r6" }, { "r7" }, { "r8" }, { "r9" },
|
||||
{ "r10" }, { "r11" }, { "r12" }, { "r13" }, { "r14" } };
|
||||
ABIRegisters = { { r0_arm }, { r1_arm }, { r2_arm }, { r3_arm },
|
||||
{ r4_arm }, { r5_arm }, { r6_arm }, { r7_arm },
|
||||
{ r8_arm }, { r9_arm }, { r10_arm }, { r11_arm },
|
||||
{ r12_arm }, { r13_arm }, { r14_arm } };
|
||||
PCMContextIndex = 18;
|
||||
|
||||
HasRelocationAddend = false;
|
||||
@@ -383,13 +371,16 @@ BinaryFile::BinaryFile(std::string FilePath, uint64_t PreferedBaseAddress) :
|
||||
0x5d, // exit
|
||||
0xdd // execve
|
||||
};
|
||||
ABIRegisters = { { "x0" }, { "x1" }, { "x2" }, { "x3" }, { "x4" },
|
||||
{ "x5" }, { "x6" }, { "x7" }, { "x8" }, { "x9" },
|
||||
{ "x10" }, { "x11" }, { "x12" }, { "x13" }, { "x14" },
|
||||
{ "x15" }, { "x16" }, { "x17" }, { "x18" }, { "x19" },
|
||||
{ "x20" }, { "x21" }, { "x22" }, { "x23" }, { "x24" },
|
||||
{ "x25" }, { "x26" }, { "x27" }, { "x28" }, { "x29" },
|
||||
{ "lr" }, { "sp" } };
|
||||
ABIRegisters = {
|
||||
{ x0_aarch64 }, { x1_aarch64 }, { x2_aarch64 }, { x3_aarch64 },
|
||||
{ x4_aarch64 }, { x5_aarch64 }, { x6_aarch64 }, { x7_aarch64 },
|
||||
{ x8_aarch64 }, { x9_aarch64 }, { x10_aarch64 }, { x11_aarch64 },
|
||||
{ x12_aarch64 }, { x13_aarch64 }, { x14_aarch64 }, { x15_aarch64 },
|
||||
{ x16_aarch64 }, { x17_aarch64 }, { x18_aarch64 }, { x19_aarch64 },
|
||||
{ x20_aarch64 }, { x21_aarch64 }, { x22_aarch64 }, { x23_aarch64 },
|
||||
{ x24_aarch64 }, { x25_aarch64 }, { x26_aarch64 }, { x27_aarch64 },
|
||||
{ x28_aarch64 }, { x29_aarch64 }, { lr_aarch64 }, { sp_aarch64 }
|
||||
};
|
||||
HasRelocationAddend = false;
|
||||
|
||||
// ret
|
||||
@@ -409,11 +400,11 @@ BinaryFile::BinaryFile(std::string FilePath, uint64_t PreferedBaseAddress) :
|
||||
0xfab // execve
|
||||
};
|
||||
DelaySlotSize = 1;
|
||||
ABIRegisters = {
|
||||
{ "v0" }, { "v1" }, { "a0" }, { "a1" }, { "a2" }, { "a3" },
|
||||
{ "s0" }, { "s1" }, { "s2" }, { "s3" }, { "s4" }, { "s5" },
|
||||
{ "s6" }, { "s7" }, { "gp" }, { "sp" }, { "fp" }, { "ra" }
|
||||
};
|
||||
ABIRegisters = { { v0_mips }, { v1_mips }, { a0_mips }, { a1_mips },
|
||||
{ a2_mips }, { a3_mips }, { s0_mips }, { s1_mips },
|
||||
{ s2_mips }, { s3_mips }, { s4_mips }, { s5_mips },
|
||||
{ s6_mips }, { s7_mips }, { gp_mips }, { sp_mips },
|
||||
{ fp_mips }, { ra_mips } };
|
||||
|
||||
HasRelocationAddend = false;
|
||||
|
||||
@@ -448,13 +439,16 @@ BinaryFile::BinaryFile(std::string FilePath, uint64_t PreferedBaseAddress) :
|
||||
RelocationTypes[R_390_GLOB_DAT] = RD(RD::SymbolRelative);
|
||||
RelocationTypes[R_390_COPY] = RD(RD::LabelOnly, RD::TargetValue);
|
||||
|
||||
ABIRegisters = { { "r0" }, { "r1" }, { "r2" }, { "r3" }, { "r4" },
|
||||
{ "r5" }, { "r6" }, { "r7" }, { "r8" }, { "r9" },
|
||||
{ "r10" }, { "r11" }, { "r12" }, { "r13" }, { "r14" },
|
||||
{ "r15" }, { "f0" }, { "f1" }, { "f2" }, { "f3" },
|
||||
{ "f4" }, { "f5" }, { "f6" }, { "f7" }, { "f8" },
|
||||
{ "f9" }, { "f10" }, { "f11" }, { "f12" }, { "f13" },
|
||||
{ "f14" }, { "f15" } };
|
||||
ABIRegisters = {
|
||||
{ r0_systemz }, { r1_systemz }, { r2_systemz }, { r3_systemz },
|
||||
{ r4_systemz }, { r5_systemz }, { r6_systemz }, { r7_systemz },
|
||||
{ r8_systemz }, { r9_systemz }, { r10_systemz }, { r11_systemz },
|
||||
{ r12_systemz }, { r13_systemz }, { r14_systemz }, { r15_systemz },
|
||||
{ f0_systemz }, { f1_systemz }, { f2_systemz }, { f3_systemz },
|
||||
{ f4_systemz }, { f5_systemz }, { f6_systemz }, { f7_systemz },
|
||||
{ f8_systemz }, { f9_systemz }, { f10_systemz }, { f11_systemz },
|
||||
{ f12_systemz }, { f13_systemz }, { f14_systemz }, { f15_systemz }
|
||||
};
|
||||
|
||||
break;
|
||||
|
||||
|
||||
@@ -955,7 +955,7 @@ void CodeGenerator::translate(Optional<uint64_t> RawVirtualAddress) {
|
||||
const SmallVector<ABIRegister, 20> &ABIRegisters = Arch.abiRegisters();
|
||||
SmallVector<Metadata *, 20> ABIRegMetadata;
|
||||
for (auto Register : ABIRegisters)
|
||||
ABIRegMetadata.push_back(MDString::get(Context, Register.name()));
|
||||
ABIRegMetadata.push_back(MDString::get(Context, Register.csvName()));
|
||||
|
||||
auto *Tuple = MDTuple::get(Context,
|
||||
{
|
||||
|
||||
@@ -55,7 +55,7 @@ BasicBlock *ExternalJumpsHandler::createReturnFromExternal() {
|
||||
|
||||
// Deserialize the ABI registers
|
||||
for (const ABIRegister &Register : Arch.abiRegisters()) {
|
||||
GlobalVariable *CSV = TheModule.getGlobalVariable(Register.qemuName());
|
||||
GlobalVariable *CSV = TheModule.getGlobalVariable(Register.csvName());
|
||||
// Not all the registers have a corresponding CSV
|
||||
if (CSV != nullptr) {
|
||||
|
||||
@@ -122,7 +122,7 @@ BasicBlock *ExternalJumpsHandler::createSerializeAndJumpOut() {
|
||||
|
||||
// Serialize ABI CSVs
|
||||
for (const ABIRegister &Register : Arch.abiRegisters()) {
|
||||
GlobalVariable *CSV = TheModule.getGlobalVariable(Register.qemuName());
|
||||
GlobalVariable *CSV = TheModule.getGlobalVariable(Register.csvName());
|
||||
|
||||
// Not all the registers have a corresponding CSV
|
||||
if (CSV == nullptr)
|
||||
|
||||
Reference in New Issue
Block a user