llvm-project/llvm/unittests/ExecutionEngine/Orc/RTDyldObjectLinkingLayer2Test.cpp
Lang Hames 6d32002e2b [ORC] Add utilities to RTDyldObjectLinkingLayer2 to simplify symbol flag
management and materialization responsibility registration.

The setOverrideObjectFlagsWithResponsibilityFlags method instructs
RTDyldObjectlinkingLayer2 to override the symbol flags produced by RuntimeDyld with
the flags provided by the MaterializationResponsibility instance. This can be used
to enable symbol visibility (hidden/exported) for COFF object files, which do not
currently support the SF_Exported flag.

The setAutoClaimResponsibilityForObjectSymbols method instructs
RTDyldObjectLinkingLayer2 to claim responsibility for any symbols provided by a
given object file that were not already in the MaterializationResponsibility
instance. Setting this flag allows higher-level program representations (e.g.
LLVM IR) to be added based on only a subset of the symbols they provide, without
having to write intervening layers to scan and add the additional symbols. This
trades diagnostic quality for convenience however: If all symbols are enumerated
up-front then clashes can be detected and reported early. If this option is set,
clashes for the additional symbols may not be detected until late, and detection
may depend on the flow of control through JIT'd code.

llvm-svn: 341154
2018-08-31 00:53:17 +00:00

406 lines
14 KiB
C++

//===--- RTDyldObjectLinkingLayer2Test.cpp - RTDyld linking layer tests ---===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "OrcTestCommon.h"
#include "llvm/ExecutionEngine/ExecutionEngine.h"
#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
#include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
#include "llvm/ExecutionEngine/Orc/Legacy.h"
#include "llvm/ExecutionEngine/Orc/NullResolver.h"
#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/LLVMContext.h"
#include "gtest/gtest.h"
using namespace llvm;
using namespace llvm::orc;
namespace {
class RTDyldObjectLinkingLayer2ExecutionTest : public testing::Test,
public OrcExecutionTest {};
class SectionMemoryManagerWrapper : public SectionMemoryManager {
public:
int FinalizationCount = 0;
int NeedsToReserveAllocationSpaceCount = 0;
bool needsToReserveAllocationSpace() override {
++NeedsToReserveAllocationSpaceCount;
return SectionMemoryManager::needsToReserveAllocationSpace();
}
bool finalizeMemory(std::string *ErrMsg = nullptr) override {
++FinalizationCount;
return SectionMemoryManager::finalizeMemory(ErrMsg);
}
};
// Adds an object with a debug section to RuntimeDyld and then returns whether
// the debug section was passed to the memory manager.
static bool testSetProcessAllSections(std::unique_ptr<MemoryBuffer> Obj,
bool ProcessAllSections) {
class MemoryManagerWrapper : public SectionMemoryManager {
public:
MemoryManagerWrapper(bool &DebugSeen) : DebugSeen(DebugSeen) {}
uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment,
unsigned SectionID, StringRef SectionName,
bool IsReadOnly) override {
if (SectionName == ".debug_str")
DebugSeen = true;
return SectionMemoryManager::allocateDataSection(
Size, Alignment, SectionID, SectionName, IsReadOnly);
}
private:
bool &DebugSeen;
};
bool DebugSectionSeen = false;
auto MM = std::make_shared<MemoryManagerWrapper>(DebugSectionSeen);
ExecutionSession ES(std::make_shared<SymbolStringPool>());
auto &JD = ES.createJITDylib("main");
auto Foo = ES.getSymbolStringPool().intern("foo");
RTDyldObjectLinkingLayer2 ObjLayer(ES, [&MM](VModuleKey) { return MM; });
auto OnResolveDoNothing = [](Expected<SymbolMap> R) {
cantFail(std::move(R));
};
auto OnReadyDoNothing = [](Error Err) { cantFail(std::move(Err)); };
ObjLayer.setProcessAllSections(ProcessAllSections);
auto K = ES.allocateVModule();
cantFail(ObjLayer.add(JD, K, std::move(Obj)));
ES.lookup({&JD}, {Foo}, OnResolveDoNothing, OnReadyDoNothing,
NoDependenciesToRegister);
return DebugSectionSeen;
}
TEST(RTDyldObjectLinkingLayer2Test, TestSetProcessAllSections) {
LLVMContext Context;
auto M = llvm::make_unique<Module>("", Context);
M->setTargetTriple("x86_64-unknown-linux-gnu");
Type *Int32Ty = IntegerType::get(Context, 32);
GlobalVariable *GV =
new GlobalVariable(*M, Int32Ty, false, GlobalValue::ExternalLinkage,
ConstantInt::get(Int32Ty, 42), "foo");
GV->setSection(".debug_str");
// Initialize the native target in case this is the first unit test
// to try to build a TM.
OrcNativeTarget::initialize();
std::unique_ptr<TargetMachine> TM(EngineBuilder().selectTarget(
Triple(M->getTargetTriple()), "", "", SmallVector<std::string, 1>()));
if (!TM)
return;
auto Obj = SimpleCompiler(*TM)(*M);
EXPECT_FALSE(testSetProcessAllSections(
MemoryBuffer::getMemBufferCopy(Obj->getBuffer()), false))
<< "Debug section seen despite ProcessAllSections being false";
EXPECT_TRUE(testSetProcessAllSections(std::move(Obj), true))
<< "Expected to see debug section when ProcessAllSections is true";
}
TEST(RTDyldObjectLinkingLayer2Test, TestOverrideObjectFlags) {
OrcNativeTarget::initialize();
std::unique_ptr<TargetMachine> TM(
EngineBuilder().selectTarget(Triple("x86_64-unknown-linux-gnu"), "", "",
SmallVector<std::string, 1>()));
if (!TM)
return;
// Our compiler is going to modify symbol visibility settings without telling
// ORC. This will test our ability to override the flags later.
class FunkySimpleCompiler : public SimpleCompiler {
public:
FunkySimpleCompiler(TargetMachine &TM) : SimpleCompiler(TM) {}
CompileResult operator()(Module &M) {
auto *Foo = M.getFunction("foo");
assert(Foo && "Expected function Foo not found");
Foo->setVisibility(GlobalValue::HiddenVisibility);
return SimpleCompiler::operator()(M);
}
};
// Create a module with two void() functions: foo and bar.
LLVMContext Context;
std::unique_ptr<Module> M;
{
ModuleBuilder MB(Context, TM->getTargetTriple().str(), "dummy");
MB.getModule()->setDataLayout(TM->createDataLayout());
Function *FooImpl = MB.createFunctionDecl<void()>("foo");
BasicBlock *FooEntry = BasicBlock::Create(Context, "entry", FooImpl);
IRBuilder<> B1(FooEntry);
B1.CreateRetVoid();
Function *BarImpl = MB.createFunctionDecl<void()>("bar");
BasicBlock *BarEntry = BasicBlock::Create(Context, "entry", BarImpl);
IRBuilder<> B2(BarEntry);
B2.CreateRetVoid();
M = MB.takeModule();
}
// Create a simple stack and set the override flags option.
ExecutionSession ES;
auto &JD = ES.createJITDylib("main");
auto Foo = ES.getSymbolStringPool().intern("foo");
RTDyldObjectLinkingLayer2 ObjLayer(
ES, [](VModuleKey) { return std::make_shared<SectionMemoryManager>(); });
IRCompileLayer2 CompileLayer(ES, ObjLayer, FunkySimpleCompiler(*TM));
ObjLayer.setOverrideObjectFlagsWithResponsibilityFlags(true);
cantFail(CompileLayer.add(JD, ES.allocateVModule(), std::move(M)));
ES.lookup({&JD}, {Foo}, [](Expected<SymbolMap> R) { cantFail(std::move(R)); },
[](Error Err) { cantFail(std::move(Err)); },
NoDependenciesToRegister);
}
TEST(RTDyldObjectLinkingLayer2Test, TestAutoClaimResponsibilityForSymbols) {
OrcNativeTarget::initialize();
std::unique_ptr<TargetMachine> TM(
EngineBuilder().selectTarget(Triple("x86_64-unknown-linux-gnu"), "", "",
SmallVector<std::string, 1>()));
if (!TM)
return;
// Our compiler is going to add a new symbol without telling ORC.
// This will test our ability to auto-claim responsibility later.
class FunkySimpleCompiler : public SimpleCompiler {
public:
FunkySimpleCompiler(TargetMachine &TM) : SimpleCompiler(TM) {}
CompileResult operator()(Module &M) {
Function *BarImpl =
Function::Create(TypeBuilder<void(), false>::get(M.getContext()),
GlobalValue::ExternalLinkage, "bar", &M);
BasicBlock *BarEntry =
BasicBlock::Create(M.getContext(), "entry", BarImpl);
IRBuilder<> B(BarEntry);
B.CreateRetVoid();
return SimpleCompiler::operator()(M);
}
};
// Create a module with two void() functions: foo and bar.
LLVMContext Context;
std::unique_ptr<Module> M;
{
ModuleBuilder MB(Context, TM->getTargetTriple().str(), "dummy");
MB.getModule()->setDataLayout(TM->createDataLayout());
Function *FooImpl = MB.createFunctionDecl<void()>("foo");
BasicBlock *FooEntry = BasicBlock::Create(Context, "entry", FooImpl);
IRBuilder<> B(FooEntry);
B.CreateRetVoid();
M = MB.takeModule();
}
// Create a simple stack and set the override flags option.
ExecutionSession ES;
auto &JD = ES.createJITDylib("main");
auto Foo = ES.getSymbolStringPool().intern("foo");
RTDyldObjectLinkingLayer2 ObjLayer(
ES, [](VModuleKey) { return std::make_shared<SectionMemoryManager>(); });
IRCompileLayer2 CompileLayer(ES, ObjLayer, FunkySimpleCompiler(*TM));
ObjLayer.setAutoClaimResponsibilityForObjectSymbols(true);
cantFail(CompileLayer.add(JD, ES.allocateVModule(), std::move(M)));
ES.lookup({&JD}, {Foo}, [](Expected<SymbolMap> R) { cantFail(std::move(R)); },
[](Error Err) { cantFail(std::move(Err)); },
NoDependenciesToRegister);
}
TEST(RTDyldObjectLinkingLayer2Test, NoDuplicateFinalization) {
// Create a pair of modules that will trigger recursive finalization:
// Module 1:
// int bar() { return 42; }
// Module 2:
// int bar();
// int foo() { return bar(); }
//
// Verify that the memory manager is only finalized once (for Module 2).
// Failure suggests that finalize is being called on the inner RTDyld
// instance (for Module 1) which is unsafe, as it will prevent relocation of
// Module 2.
// Initialize the native target in case this is the first unit test
// to try to build a TM.
OrcNativeTarget::initialize();
std::unique_ptr<TargetMachine> TM(
EngineBuilder().selectTarget(Triple("x86_64-unknown-linux-gnu"), "", "",
SmallVector<std::string, 1>()));
if (!TM)
return;
LLVMContext Context;
ExecutionSession ES(std::make_shared<SymbolStringPool>());
auto &JD = ES.createJITDylib("main");
auto Foo = ES.getSymbolStringPool().intern("foo");
auto MM = std::make_shared<SectionMemoryManagerWrapper>();
RTDyldObjectLinkingLayer2 ObjLayer(ES, [&](VModuleKey K) { return MM; });
SimpleCompiler Compile(*TM);
ModuleBuilder MB1(Context, TM->getTargetTriple().str(), "dummy");
{
MB1.getModule()->setDataLayout(TM->createDataLayout());
Function *BarImpl = MB1.createFunctionDecl<int32_t(void)>("bar");
BasicBlock *BarEntry = BasicBlock::Create(Context, "entry", BarImpl);
IRBuilder<> Builder(BarEntry);
IntegerType *Int32Ty = IntegerType::get(Context, 32);
Value *FourtyTwo = ConstantInt::getSigned(Int32Ty, 42);
Builder.CreateRet(FourtyTwo);
}
auto Obj1 = Compile(*MB1.getModule());
ModuleBuilder MB2(Context, TM->getTargetTriple().str(), "dummy");
{
MB2.getModule()->setDataLayout(TM->createDataLayout());
Function *BarDecl = MB2.createFunctionDecl<int32_t(void)>("bar");
Function *FooImpl = MB2.createFunctionDecl<int32_t(void)>("foo");
BasicBlock *FooEntry = BasicBlock::Create(Context, "entry", FooImpl);
IRBuilder<> Builder(FooEntry);
Builder.CreateRet(Builder.CreateCall(BarDecl));
}
auto Obj2 = Compile(*MB2.getModule());
auto K1 = ES.allocateVModule();
cantFail(ObjLayer.add(JD, K1, std::move(Obj1)));
auto K2 = ES.allocateVModule();
cantFail(ObjLayer.add(JD, K2, std::move(Obj2)));
auto OnResolve = [](Expected<SymbolMap> Symbols) {
cantFail(std::move(Symbols));
};
auto OnReady = [](Error Err) { cantFail(std::move(Err)); };
ES.lookup({&JD}, {Foo}, OnResolve, OnReady, NoDependenciesToRegister);
// Finalization of module 2 should trigger finalization of module 1.
// Verify that finalize on SMMW is only called once.
EXPECT_EQ(MM->FinalizationCount, 1) << "Extra call to finalize";
}
TEST(RTDyldObjectLinkingLayer2Test, NoPrematureAllocation) {
// Create a pair of unrelated modules:
//
// Module 1:
// int foo() { return 42; }
// Module 2:
// int bar() { return 7; }
//
// Both modules will share a memory manager. We want to verify that the
// second object is not loaded before the first one is finalized. To do this
// in a portable way, we abuse the
// RuntimeDyld::MemoryManager::needsToReserveAllocationSpace hook, which is
// called once per object before any sections are allocated.
// Initialize the native target in case this is the first unit test
// to try to build a TM.
OrcNativeTarget::initialize();
std::unique_ptr<TargetMachine> TM(
EngineBuilder().selectTarget(Triple("x86_64-unknown-linux-gnu"), "", "",
SmallVector<std::string, 1>()));
if (!TM)
return;
ExecutionSession ES(std::make_shared<SymbolStringPool>());
auto &JD = ES.createJITDylib("main");
auto Foo = ES.getSymbolStringPool().intern("foo");
auto MM = std::make_shared<SectionMemoryManagerWrapper>();
RTDyldObjectLinkingLayer2 ObjLayer(ES, [&MM](VModuleKey K) { return MM; });
SimpleCompiler Compile(*TM);
LLVMContext Context;
ModuleBuilder MB1(Context, TM->getTargetTriple().str(), "dummy");
{
MB1.getModule()->setDataLayout(TM->createDataLayout());
Function *BarImpl = MB1.createFunctionDecl<int32_t(void)>("foo");
BasicBlock *BarEntry = BasicBlock::Create(Context, "entry", BarImpl);
IRBuilder<> Builder(BarEntry);
IntegerType *Int32Ty = IntegerType::get(Context, 32);
Value *FourtyTwo = ConstantInt::getSigned(Int32Ty, 42);
Builder.CreateRet(FourtyTwo);
}
auto Obj1 = Compile(*MB1.getModule());
ModuleBuilder MB2(Context, TM->getTargetTriple().str(), "dummy");
{
MB2.getModule()->setDataLayout(TM->createDataLayout());
Function *BarImpl = MB2.createFunctionDecl<int32_t(void)>("bar");
BasicBlock *BarEntry = BasicBlock::Create(Context, "entry", BarImpl);
IRBuilder<> Builder(BarEntry);
IntegerType *Int32Ty = IntegerType::get(Context, 32);
Value *Seven = ConstantInt::getSigned(Int32Ty, 7);
Builder.CreateRet(Seven);
}
auto Obj2 = Compile(*MB2.getModule());
cantFail(ObjLayer.add(JD, ES.allocateVModule(), std::move(Obj1)));
cantFail(ObjLayer.add(JD, ES.allocateVModule(), std::move(Obj2)));
auto OnResolve = [](Expected<SymbolMap> Result) {
cantFail(std::move(Result));
};
auto OnReady = [](Error Err) { cantFail(std::move(Err)); };
ES.lookup({&JD}, {Foo}, OnResolve, OnReady, NoDependenciesToRegister);
// Only one call to needsToReserveAllocationSpace should have been made.
EXPECT_EQ(MM->NeedsToReserveAllocationSpaceCount, 1)
<< "More than one call to needsToReserveAllocationSpace "
"(multiple unrelated objects loaded prior to finalization)";
}
TEST(RTDyldObjectLinkingLayer2Test, TestNotifyLoadedSignature) {
ExecutionSession ES(std::make_shared<SymbolStringPool>());
RTDyldObjectLinkingLayer2 ObjLayer(
ES,
[](VModuleKey) -> std::shared_ptr<RuntimeDyld::MemoryManager> {
return nullptr;
},
[](VModuleKey, const object::ObjectFile &obj,
const RuntimeDyld::LoadedObjectInfo &info) {});
}
} // end anonymous namespace