1 #include "OrcTestCommon.h" 2 #include "llvm/ExecutionEngine/Orc/AbsoluteSymbols.h" 3 #include "llvm/ExecutionEngine/Orc/JITLinkRedirectableSymbolManager.h" 4 #include "llvm/ExecutionEngine/Orc/JITLinkReentryTrampolines.h" 5 #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" 6 #include "llvm/ExecutionEngine/Orc/LLJIT.h" 7 #include "llvm/ExecutionEngine/Orc/LazyReexports.h" 8 #include "llvm/Testing/Support/Error.h" 9 #include "gtest/gtest.h" 10 11 using namespace llvm; 12 using namespace llvm::orc; 13 14 class LazyReexportsTest : public CoreAPIsBasedStandardTest {}; 15 16 static int dummyTarget() { return 42; } 17 18 TEST_F(LazyReexportsTest, BasicLocalCallThroughManagerOperation) { 19 // Create a callthrough manager for the host (if possible) and verify that 20 // a call to the lazy call-through: 21 // (1) Materializes the MU. This verifies that the symbol was looked up, and 22 // that we didn't arrive at the target via some other path 23 // (2) Returns the expected value (which we take as proof that the call 24 // reached the target). 25 26 auto JTMB = JITTargetMachineBuilder::detectHost(); 27 28 // Bail out if we can not detect the host. 29 if (!JTMB) { 30 consumeError(JTMB.takeError()); 31 GTEST_SKIP(); 32 } 33 34 // Bail out if we can not build a local call-through manager. 35 auto LCTM = createLocalLazyCallThroughManager(JTMB->getTargetTriple(), ES, 36 ExecutorAddr()); 37 if (!LCTM) { 38 consumeError(LCTM.takeError()); 39 GTEST_SKIP(); 40 } 41 42 auto DummyTarget = ES.intern("DummyTarget"); 43 44 bool DummyTargetMaterialized = false; 45 46 cantFail(JD.define(std::make_unique<SimpleMaterializationUnit>( 47 SymbolFlagsMap({{DummyTarget, JITSymbolFlags::Exported}}), 48 [&](std::unique_ptr<MaterializationResponsibility> R) { 49 DummyTargetMaterialized = true; 50 // No dependencies registered, can't fail. 51 cantFail(R->notifyResolved({{DummyTarget, 52 {ExecutorAddr::fromPtr(&dummyTarget), 53 JITSymbolFlags::Exported}}})); 54 cantFail(R->notifyEmitted({})); 55 }))); 56 57 unsigned NotifyResolvedCount = 0; 58 auto NotifyResolved = [&](ExecutorAddr ResolvedAddr) { 59 ++NotifyResolvedCount; 60 return Error::success(); 61 }; 62 63 auto CallThroughTrampoline = cantFail((*LCTM)->getCallThroughTrampoline( 64 JD, DummyTarget, std::move(NotifyResolved))); 65 66 auto CTTPtr = CallThroughTrampoline.toPtr<int (*)()>(); 67 68 // Call twice to verify nothing unexpected happens on redundant calls. 69 auto Result = CTTPtr(); 70 (void)CTTPtr(); 71 72 EXPECT_TRUE(DummyTargetMaterialized) 73 << "CallThrough did not materialize target"; 74 EXPECT_EQ(NotifyResolvedCount, 1U) 75 << "CallThrough should have generated exactly one 'NotifyResolved' call"; 76 EXPECT_EQ(Result, 42) << "Failed to call through to target"; 77 } 78 79 static void *noReentry(void *) { abort(); } 80 81 TEST(JITLinkLazyReexportsTest, Basics) { 82 OrcNativeTarget::initialize(); 83 84 auto J = LLJITBuilder().create(); 85 if (!J) { 86 dbgs() << toString(J.takeError()) << "\n"; 87 // consumeError(J.takeError()); 88 GTEST_SKIP(); 89 } 90 if (!isa<ObjectLinkingLayer>((*J)->getObjLinkingLayer())) 91 GTEST_SKIP(); 92 93 auto &OLL = cast<ObjectLinkingLayer>((*J)->getObjLinkingLayer()); 94 95 auto RSMgr = JITLinkRedirectableSymbolManager::Create(OLL); 96 if (!RSMgr) { 97 dbgs() << "Boom for RSMgr\n"; 98 consumeError(RSMgr.takeError()); 99 GTEST_SKIP(); 100 } 101 102 auto &ES = (*J)->getExecutionSession(); 103 104 auto &JD = ES.createBareJITDylib("JD"); 105 cantFail(JD.define(absoluteSymbols( 106 {{ES.intern("__orc_rt_reentry"), 107 {ExecutorAddr::fromPtr(&noReentry), 108 JITSymbolFlags::Exported | JITSymbolFlags::Callable}}}))); 109 110 auto LRMgr = createJITLinkLazyReexportsManager(OLL, **RSMgr, JD); 111 if (!LRMgr) { 112 dbgs() << "Boom for LRMgr\n"; 113 consumeError(LRMgr.takeError()); 114 GTEST_SKIP(); 115 } 116 117 auto Foo = ES.intern("foo"); 118 auto Bar = ES.intern("bar"); 119 120 auto RT = JD.createResourceTracker(); 121 cantFail(JD.define( 122 lazyReexports( 123 **LRMgr, 124 {{Foo, {Bar, JITSymbolFlags::Exported | JITSymbolFlags::Callable}}}), 125 RT)); 126 127 // Check flags after adding Foo -> Bar lazy reexport. 128 auto SF = cantFail( 129 ES.lookupFlags(LookupKind::Static, makeJITDylibSearchOrder(&JD), 130 {{Foo, SymbolLookupFlags::WeaklyReferencedSymbol}})); 131 EXPECT_EQ(SF.size(), 1U); 132 EXPECT_TRUE(SF.count(Foo)); 133 EXPECT_EQ(SF[Foo], JITSymbolFlags::Exported | JITSymbolFlags::Callable); 134 135 // Remove reexport without running it. 136 if (auto Err = RT->remove()) { 137 EXPECT_THAT_ERROR(std::move(Err), Succeeded()); 138 return; 139 } 140 141 // Check flags after adding Foo -> Bar lazy reexport. 142 SF = cantFail( 143 ES.lookupFlags(LookupKind::Static, makeJITDylibSearchOrder(&JD), 144 {{Foo, SymbolLookupFlags::WeaklyReferencedSymbol}})); 145 EXPECT_EQ(SF.size(), 0U); 146 } 147