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> 40 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 55 MockBaseLayer() : MockSymbol(nullptr) { resetExpectations(); } 56 57 template <typename ObjSetT, typename MemoryManagerPtrT, 58 typename SymbolResolverPtrT> 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> 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 } 82 void verifyAddObjectSet(ObjSetHandleT Returned) { 83 EXPECT_EQ("addObjectSet", LastCalled); 84 EXPECT_EQ(MockObjSetHandle, Returned) << "Return should pass through"; 85 resetExpectations(); 86 } 87 88 void removeObjectSet(ObjSetHandleT H) { 89 EXPECT_EQ(MockObjSetHandle, H); 90 LastCalled = "removeObjectSet"; 91 } 92 void expectRemoveObjectSet(ObjSetHandleT H) { MockObjSetHandle = H; } 93 void verifyRemoveObjectSet() { 94 EXPECT_EQ("removeObjectSet", LastCalled); 95 resetExpectations(); 96 } 97 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 } 105 void expectFindSymbol(const std::string &Name, bool ExportedSymbolsOnly) { 106 MockName = Name; 107 MockBool = ExportedSymbolsOnly; 108 } 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 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 } 125 void expectFindSymbolIn(ObjSetHandleT H, const std::string &Name, 126 bool ExportedSymbolsOnly) { 127 MockObjSetHandle = H; 128 MockName = Name; 129 MockBool = ExportedSymbolsOnly; 130 } 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 138 void emitAndFinalize(ObjSetHandleT H) { 139 EXPECT_EQ(MockObjSetHandle, H) << "Handle should pass through"; 140 LastCalled = "emitAndFinalize"; 141 } 142 void expectEmitAndFinalize(ObjSetHandleT H) { MockObjSetHandle = H; } 143 void verifyEmitAndFinalize() { 144 EXPECT_EQ("emitAndFinalize", LastCalled); 145 resetExpectations(); 146 } 147 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 } 155 void expectMapSectionAddress(ObjSetHandleT H, const void *LocalAddress, 156 TargetAddress TargetAddr) { 157 MockObjSetHandle = H; 158 MockLocalAddress = LocalAddress; 159 MockTargetAddress = TargetAddr; 160 } 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 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. 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