1 //===- ObjectTransformLayerTest.cpp - Unit tests for ObjectTransformLayer -===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "llvm/ADT/STLExtras.h"
11 #include "llvm/ADT/SmallVector.h"
12 #include "llvm/ExecutionEngine/Orc/CompileUtils.h"
13 #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
14 #include "llvm/ExecutionEngine/Orc/NullResolver.h"
15 #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h"
16 #include "llvm/ExecutionEngine/Orc/ObjectTransformLayer.h"
17 #include "llvm/Object/ObjectFile.h"
18 #include "gtest/gtest.h"
19
20 using namespace llvm::orc;
21
22 namespace {
23
24 // Stand-in for RuntimeDyld::MemoryManager
25 typedef int MockMemoryManager;
26
27 // Stand-in for RuntimeDyld::SymbolResolver
28 typedef int MockSymbolResolver;
29
30 // stand-in for object::ObjectFile
31 typedef int MockObjectFile;
32
33 // stand-in for llvm::MemoryBuffer set
34 typedef int MockMemoryBufferSet;
35
36 // Mock transform that operates on unique pointers to object files, and
37 // allocates new object files rather than mutating the given ones.
38 struct AllocatingTransform {
39 std::unique_ptr<MockObjectFile>
operator ()__anon9106482a0111::AllocatingTransform40 operator()(std::unique_ptr<MockObjectFile> Obj) const {
41 return llvm::make_unique<MockObjectFile>(*Obj + 1);
42 }
43 };
44
45 // Mock base layer for verifying behavior of transform layer.
46 // Each method "T foo(args)" is accompanied by two auxiliary methods:
47 // - "void expectFoo(args)", to be called before calling foo on the transform
48 // layer; saves values of args, which mock layer foo then verifies against.
49 // - "void verifyFoo(T)", to be called after foo, which verifies that the
50 // transform layer called the base layer and forwarded any return value.
51 class MockBaseLayer {
52 public:
53 typedef int ObjSetHandleT;
54
MockBaseLayer()55 MockBaseLayer() : MockSymbol(nullptr) { resetExpectations(); }
56
57 template <typename ObjSetT, typename MemoryManagerPtrT,
58 typename SymbolResolverPtrT>
addObjectSet(ObjSetT Objects,MemoryManagerPtrT MemMgr,SymbolResolverPtrT Resolver)59 ObjSetHandleT addObjectSet(ObjSetT Objects, MemoryManagerPtrT MemMgr,
60 SymbolResolverPtrT Resolver) {
61 EXPECT_EQ(MockManager, *MemMgr) << "MM should pass through";
62 EXPECT_EQ(MockResolver, *Resolver) << "Resolver should pass through";
63 size_t I = 0;
64 for (auto &ObjPtr : Objects) {
65 EXPECT_EQ(MockObjects[I] + 1, *ObjPtr) << "Transform should be applied";
66 I++;
67 }
68 EXPECT_EQ(MockObjects.size(), I) << "Number of objects should match";
69 LastCalled = "addObjectSet";
70 MockObjSetHandle = 111;
71 return MockObjSetHandle;
72 }
73 template <typename ObjSetT>
expectAddObjectSet(ObjSetT & Objects,MockMemoryManager * MemMgr,MockSymbolResolver * Resolver)74 void expectAddObjectSet(ObjSetT &Objects, MockMemoryManager *MemMgr,
75 MockSymbolResolver *Resolver) {
76 MockManager = *MemMgr;
77 MockResolver = *Resolver;
78 for (auto &ObjPtr : Objects) {
79 MockObjects.push_back(*ObjPtr);
80 }
81 }
verifyAddObjectSet(ObjSetHandleT Returned)82 void verifyAddObjectSet(ObjSetHandleT Returned) {
83 EXPECT_EQ("addObjectSet", LastCalled);
84 EXPECT_EQ(MockObjSetHandle, Returned) << "Return should pass through";
85 resetExpectations();
86 }
87
removeObjectSet(ObjSetHandleT H)88 void removeObjectSet(ObjSetHandleT H) {
89 EXPECT_EQ(MockObjSetHandle, H);
90 LastCalled = "removeObjectSet";
91 }
expectRemoveObjectSet(ObjSetHandleT H)92 void expectRemoveObjectSet(ObjSetHandleT H) { MockObjSetHandle = H; }
verifyRemoveObjectSet()93 void verifyRemoveObjectSet() {
94 EXPECT_EQ("removeObjectSet", LastCalled);
95 resetExpectations();
96 }
97
findSymbol(const std::string & Name,bool ExportedSymbolsOnly)98 JITSymbol findSymbol(const std::string &Name, bool ExportedSymbolsOnly) {
99 EXPECT_EQ(MockName, Name) << "Name should pass through";
100 EXPECT_EQ(MockBool, ExportedSymbolsOnly) << "Flag should pass through";
101 LastCalled = "findSymbol";
102 MockSymbol = JITSymbol(122, llvm::JITSymbolFlags::None);
103 return MockSymbol;
104 }
expectFindSymbol(const std::string & Name,bool ExportedSymbolsOnly)105 void expectFindSymbol(const std::string &Name, bool ExportedSymbolsOnly) {
106 MockName = Name;
107 MockBool = ExportedSymbolsOnly;
108 }
verifyFindSymbol(llvm::orc::JITSymbol Returned)109 void verifyFindSymbol(llvm::orc::JITSymbol Returned) {
110 EXPECT_EQ("findSymbol", LastCalled);
111 EXPECT_EQ(MockSymbol.getAddress(), Returned.getAddress())
112 << "Return should pass through";
113 resetExpectations();
114 }
115
findSymbolIn(ObjSetHandleT H,const std::string & Name,bool ExportedSymbolsOnly)116 JITSymbol findSymbolIn(ObjSetHandleT H, const std::string &Name,
117 bool ExportedSymbolsOnly) {
118 EXPECT_EQ(MockObjSetHandle, H) << "Handle should pass through";
119 EXPECT_EQ(MockName, Name) << "Name should pass through";
120 EXPECT_EQ(MockBool, ExportedSymbolsOnly) << "Flag should pass through";
121 LastCalled = "findSymbolIn";
122 MockSymbol = JITSymbol(122, llvm::JITSymbolFlags::None);
123 return MockSymbol;
124 }
expectFindSymbolIn(ObjSetHandleT H,const std::string & Name,bool ExportedSymbolsOnly)125 void expectFindSymbolIn(ObjSetHandleT H, const std::string &Name,
126 bool ExportedSymbolsOnly) {
127 MockObjSetHandle = H;
128 MockName = Name;
129 MockBool = ExportedSymbolsOnly;
130 }
verifyFindSymbolIn(llvm::orc::JITSymbol Returned)131 void verifyFindSymbolIn(llvm::orc::JITSymbol Returned) {
132 EXPECT_EQ("findSymbolIn", LastCalled);
133 EXPECT_EQ(MockSymbol.getAddress(), Returned.getAddress())
134 << "Return should pass through";
135 resetExpectations();
136 }
137
emitAndFinalize(ObjSetHandleT H)138 void emitAndFinalize(ObjSetHandleT H) {
139 EXPECT_EQ(MockObjSetHandle, H) << "Handle should pass through";
140 LastCalled = "emitAndFinalize";
141 }
expectEmitAndFinalize(ObjSetHandleT H)142 void expectEmitAndFinalize(ObjSetHandleT H) { MockObjSetHandle = H; }
verifyEmitAndFinalize()143 void verifyEmitAndFinalize() {
144 EXPECT_EQ("emitAndFinalize", LastCalled);
145 resetExpectations();
146 }
147
mapSectionAddress(ObjSetHandleT H,const void * LocalAddress,TargetAddress TargetAddr)148 void mapSectionAddress(ObjSetHandleT H, const void *LocalAddress,
149 TargetAddress TargetAddr) {
150 EXPECT_EQ(MockObjSetHandle, H);
151 EXPECT_EQ(MockLocalAddress, LocalAddress);
152 EXPECT_EQ(MockTargetAddress, TargetAddr);
153 LastCalled = "mapSectionAddress";
154 }
expectMapSectionAddress(ObjSetHandleT H,const void * LocalAddress,TargetAddress TargetAddr)155 void expectMapSectionAddress(ObjSetHandleT H, const void *LocalAddress,
156 TargetAddress TargetAddr) {
157 MockObjSetHandle = H;
158 MockLocalAddress = LocalAddress;
159 MockTargetAddress = TargetAddr;
160 }
verifyMapSectionAddress()161 void verifyMapSectionAddress() {
162 EXPECT_EQ("mapSectionAddress", LastCalled);
163 resetExpectations();
164 }
165
166 private:
167 // Backing fields for remembering parameter/return values
168 std::string LastCalled;
169 MockMemoryManager MockManager;
170 MockSymbolResolver MockResolver;
171 std::vector<MockObjectFile> MockObjects;
172 ObjSetHandleT MockObjSetHandle;
173 std::string MockName;
174 bool MockBool;
175 JITSymbol MockSymbol;
176 const void *MockLocalAddress;
177 TargetAddress MockTargetAddress;
178 MockMemoryBufferSet MockBufferSet;
179
180 // Clear remembered parameters between calls
resetExpectations()181 void resetExpectations() {
182 LastCalled = "nothing";
183 MockManager = 0;
184 MockResolver = 0;
185 MockObjects.clear();
186 MockObjSetHandle = 0;
187 MockName = "bogus";
188 MockSymbol = JITSymbol(nullptr);
189 MockLocalAddress = nullptr;
190 MockTargetAddress = 0;
191 MockBufferSet = 0;
192 }
193 };
194
195 // Test each operation on ObjectTransformLayer.
TEST(ObjectTransformLayerTest,Main)196 TEST(ObjectTransformLayerTest, Main) {
197 MockBaseLayer M;
198
199 // Create one object transform layer using a transform (as a functor)
200 // that allocates new objects, and deals in unique pointers.
201 ObjectTransformLayer<MockBaseLayer, AllocatingTransform> T1(M);
202
203 // Create a second object transform layer using a transform (as a lambda)
204 // that mutates objects in place, and deals in naked pointers
205 ObjectTransformLayer<MockBaseLayer,
206 std::function<MockObjectFile *(MockObjectFile *)>>
207 T2(M, [](MockObjectFile *Obj) {
208 ++(*Obj);
209 return Obj;
210 });
211
212 // Instantiate some mock objects to use below
213 MockObjectFile MockObject1 = 211;
214 MockObjectFile MockObject2 = 222;
215 MockMemoryManager MockManager = 233;
216 MockSymbolResolver MockResolver = 244;
217
218 // Test addObjectSet with T1 (allocating, unique pointers)
219 std::vector<std::unique_ptr<MockObjectFile>> Objs1;
220 Objs1.push_back(llvm::make_unique<MockObjectFile>(MockObject1));
221 Objs1.push_back(llvm::make_unique<MockObjectFile>(MockObject2));
222 auto MM = llvm::make_unique<MockMemoryManager>(MockManager);
223 auto SR = llvm::make_unique<MockSymbolResolver>(MockResolver);
224 M.expectAddObjectSet(Objs1, MM.get(), SR.get());
225 auto H = T1.addObjectSet(std::move(Objs1), std::move(MM), std::move(SR));
226 M.verifyAddObjectSet(H);
227
228 // Test addObjectSet with T2 (mutating, naked pointers)
229 llvm::SmallVector<MockObjectFile *, 2> Objs2Vec;
230 Objs2Vec.push_back(&MockObject1);
231 Objs2Vec.push_back(&MockObject2);
232 llvm::MutableArrayRef<MockObjectFile *> Objs2(Objs2Vec);
233 M.expectAddObjectSet(Objs2, &MockManager, &MockResolver);
234 H = T2.addObjectSet(Objs2, &MockManager, &MockResolver);
235 M.verifyAddObjectSet(H);
236 EXPECT_EQ(212, MockObject1) << "Expected mutation";
237 EXPECT_EQ(223, MockObject2) << "Expected mutation";
238
239 // Test removeObjectSet
240 M.expectRemoveObjectSet(H);
241 T1.removeObjectSet(H);
242 M.verifyRemoveObjectSet();
243
244 // Test findSymbol
245 std::string Name = "foo";
246 bool ExportedOnly = true;
247 M.expectFindSymbol(Name, ExportedOnly);
248 JITSymbol Symbol = T2.findSymbol(Name, ExportedOnly);
249 M.verifyFindSymbol(Symbol);
250
251 // Test findSymbolIn
252 Name = "bar";
253 ExportedOnly = false;
254 M.expectFindSymbolIn(H, Name, ExportedOnly);
255 Symbol = T1.findSymbolIn(H, Name, ExportedOnly);
256 M.verifyFindSymbolIn(Symbol);
257
258 // Test emitAndFinalize
259 M.expectEmitAndFinalize(H);
260 T2.emitAndFinalize(H);
261 M.verifyEmitAndFinalize();
262
263 // Test mapSectionAddress
264 char Buffer[24];
265 TargetAddress MockAddress = 255;
266 M.expectMapSectionAddress(H, Buffer, MockAddress);
267 T1.mapSectionAddress(H, Buffer, MockAddress);
268 M.verifyMapSectionAddress();
269
270 // Verify transform getter (non-const)
271 MockObjectFile Mutatee = 277;
272 MockObjectFile *Out = T2.getTransform()(&Mutatee);
273 EXPECT_EQ(&Mutatee, Out) << "Expected in-place transform";
274 EXPECT_EQ(278, Mutatee) << "Expected incrementing transform";
275
276 // Verify transform getter (const)
277 auto OwnedObj = llvm::make_unique<MockObjectFile>(288);
278 const auto &T1C = T1;
279 OwnedObj = T1C.getTransform()(std::move(OwnedObj));
280 EXPECT_EQ(289, *OwnedObj) << "Expected incrementing transform";
281
282 volatile bool RunStaticChecks = false;
283 if (!RunStaticChecks)
284 return;
285
286 // Make sure that ObjectTransformLayer implements the object layer concept
287 // correctly by sandwitching one between an ObjectLinkingLayer and an
288 // IRCompileLayer, verifying that it compiles if we have a call to the
289 // IRComileLayer's addModuleSet that should call the transform layer's
290 // addObjectSet, and also calling the other public transform layer methods
291 // directly to make sure the methods they intend to forward to exist on
292 // the ObjectLinkingLayer.
293
294 // We'll need a concrete MemoryManager class.
295 class NullManager : public llvm::RuntimeDyld::MemoryManager {
296 public:
297 uint8_t *allocateCodeSection(uintptr_t, unsigned, unsigned,
298 llvm::StringRef) override {
299 return nullptr;
300 }
301 uint8_t *allocateDataSection(uintptr_t, unsigned, unsigned, llvm::StringRef,
302 bool) override {
303 return nullptr;
304 }
305 void registerEHFrames(uint8_t *, uint64_t, size_t) override {}
306 void deregisterEHFrames(uint8_t *, uint64_t, size_t) override {}
307 bool finalizeMemory(std::string *) override { return false; }
308 };
309
310 // Construct the jit layers.
311 ObjectLinkingLayer<> BaseLayer;
312 auto IdentityTransform = [](
313 std::unique_ptr<llvm::object::OwningBinary<llvm::object::ObjectFile>>
314 Obj) { return Obj; };
315 ObjectTransformLayer<decltype(BaseLayer), decltype(IdentityTransform)>
316 TransformLayer(BaseLayer, IdentityTransform);
317 auto NullCompiler = [](llvm::Module &) {
318 return llvm::object::OwningBinary<llvm::object::ObjectFile>();
319 };
320 IRCompileLayer<decltype(TransformLayer)> CompileLayer(TransformLayer,
321 NullCompiler);
322
323 // Make sure that the calls from IRCompileLayer to ObjectTransformLayer
324 // compile.
325 NullResolver Resolver;
326 NullManager Manager;
327 CompileLayer.addModuleSet(std::vector<llvm::Module *>(), &Manager, &Resolver);
328
329 // Make sure that the calls from ObjectTransformLayer to ObjectLinkingLayer
330 // compile.
331 decltype(TransformLayer)::ObjSetHandleT ObjSet;
332 TransformLayer.emitAndFinalize(ObjSet);
333 TransformLayer.findSymbolIn(ObjSet, Name, false);
334 TransformLayer.findSymbol(Name, true);
335 TransformLayer.mapSectionAddress(ObjSet, nullptr, 0);
336 TransformLayer.removeObjectSet(ObjSet);
337 }
338 }
339