1 //===-------- ObjectLinkingLayerTest.cpp - ObjectLinkingLayer tests -------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "llvm/ExecutionEngine/Orc/ObjectLinkingLayer.h" 10 #include "llvm/ExecutionEngine/JITLink/JITLink.h" 11 #include "llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h" 12 #include "llvm/ExecutionEngine/JITLink/x86_64.h" 13 #include "llvm/ExecutionEngine/JITSymbol.h" 14 #include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h" 15 #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" 16 #include "llvm/ExecutionEngine/Orc/Shared/ExecutorSymbolDef.h" 17 #include "llvm/ExecutionEngine/Orc/Shared/TargetProcessControlTypes.h" 18 #include "llvm/ExecutionEngine/Orc/SymbolStringPool.h" 19 #include "llvm/Testing/Support/Error.h" 20 #include "gtest/gtest.h" 21 22 using namespace llvm; 23 using namespace llvm::jitlink; 24 using namespace llvm::orc; 25 26 namespace { 27 28 const char BlockContentBytes[] = {0x01, 0x02, 0x03, 0x04, 29 0x05, 0x06, 0x07, 0x08}; 30 31 ArrayRef<char> BlockContent(BlockContentBytes); 32 33 class ObjectLinkingLayerTest : public testing::Test { 34 public: 35 ~ObjectLinkingLayerTest() { 36 if (auto Err = ES.endSession()) 37 ES.reportError(std::move(Err)); 38 } 39 40 protected: 41 ExecutionSession ES{std::make_unique<UnsupportedExecutorProcessControl>()}; 42 JITDylib &JD = ES.createBareJITDylib("main"); 43 ObjectLinkingLayer ObjLinkingLayer{ 44 ES, std::make_unique<InProcessMemoryManager>(4096)}; 45 }; 46 47 TEST_F(ObjectLinkingLayerTest, AddLinkGraph) { 48 auto G = std::make_unique<LinkGraph>( 49 "foo", ES.getSymbolStringPool(), Triple("x86_64-apple-darwin"), 50 SubtargetFeatures(), x86_64::getEdgeKindName); 51 52 auto &Sec1 = G->createSection("__data", MemProt::Read | MemProt::Write); 53 auto &B1 = G->createContentBlock(Sec1, BlockContent, 54 orc::ExecutorAddr(0x1000), 8, 0); 55 G->addDefinedSymbol(B1, 4, "_X", 4, Linkage::Strong, Scope::Default, false, 56 false); 57 G->addDefinedSymbol(B1, 4, "_Y", 4, Linkage::Weak, Scope::Default, false, 58 false); 59 G->addDefinedSymbol(B1, 4, "_Z", 4, Linkage::Strong, Scope::Hidden, false, 60 false); 61 G->addDefinedSymbol(B1, 4, "_W", 4, Linkage::Strong, Scope::Default, true, 62 false); 63 64 EXPECT_THAT_ERROR(ObjLinkingLayer.add(JD, std::move(G)), Succeeded()); 65 66 EXPECT_THAT_EXPECTED(ES.lookup(&JD, "_X"), Succeeded()); 67 } 68 69 TEST_F(ObjectLinkingLayerTest, ResourceTracker) { 70 // This test transfers allocations to previously unknown ResourceTrackers, 71 // while increasing the number of trackers in the ObjectLinkingLayer, which 72 // may invalidate some iterators internally. 73 std::vector<ResourceTrackerSP> Trackers; 74 for (unsigned I = 0; I < 64; I++) { 75 auto G = std::make_unique<LinkGraph>( 76 "foo", ES.getSymbolStringPool(), Triple("x86_64-apple-darwin"), 77 SubtargetFeatures(), x86_64::getEdgeKindName); 78 79 auto &Sec1 = G->createSection("__data", MemProt::Read | MemProt::Write); 80 auto &B1 = G->createContentBlock(Sec1, BlockContent, 81 orc::ExecutorAddr(0x1000), 8, 0); 82 llvm::SmallString<0> SymbolName; 83 SymbolName += "_X"; 84 SymbolName += std::to_string(I); 85 G->addDefinedSymbol(B1, 4, SymbolName, 4, Linkage::Strong, Scope::Default, 86 false, false); 87 88 auto RT1 = JD.createResourceTracker(); 89 EXPECT_THAT_ERROR(ObjLinkingLayer.add(RT1, std::move(G)), Succeeded()); 90 EXPECT_THAT_EXPECTED(ES.lookup(&JD, SymbolName), Succeeded()); 91 92 auto RT2 = JD.createResourceTracker(); 93 RT1->transferTo(*RT2); 94 95 Trackers.push_back(RT2); 96 } 97 } 98 99 TEST_F(ObjectLinkingLayerTest, ClaimLateDefinedWeakSymbols) { 100 // Check that claiming weak symbols works as expected. 101 // 102 // To do this we'll need a custom plugin to inject some new symbols during 103 // the link. 104 class TestPlugin : public ObjectLinkingLayer::Plugin { 105 public: 106 void modifyPassConfig(MaterializationResponsibility &MR, 107 jitlink::LinkGraph &G, 108 jitlink::PassConfiguration &Config) override { 109 Config.PrePrunePasses.insert( 110 Config.PrePrunePasses.begin(), [](LinkGraph &G) { 111 auto *DataSec = G.findSectionByName("__data"); 112 auto &DataBlock = G.createContentBlock( 113 *DataSec, BlockContent, orc::ExecutorAddr(0x2000), 8, 0); 114 G.addDefinedSymbol(DataBlock, 4, "_x", 4, Linkage::Weak, 115 Scope::Default, false, false); 116 117 auto &TextSec = 118 G.createSection("__text", MemProt::Read | MemProt::Write); 119 auto &FuncBlock = G.createContentBlock( 120 TextSec, BlockContent, orc::ExecutorAddr(0x3000), 8, 0); 121 G.addDefinedSymbol(FuncBlock, 4, "_f", 4, Linkage::Weak, 122 Scope::Default, true, false); 123 124 return Error::success(); 125 }); 126 } 127 128 Error notifyFailed(MaterializationResponsibility &MR) override { 129 llvm_unreachable("unexpected error"); 130 } 131 132 Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override { 133 return Error::success(); 134 } 135 void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey, 136 ResourceKey SrcKey) override { 137 llvm_unreachable("unexpected resource transfer"); 138 } 139 }; 140 141 ObjLinkingLayer.addPlugin(std::make_unique<TestPlugin>()); 142 auto G = std::make_unique<LinkGraph>( 143 "foo", ES.getSymbolStringPool(), Triple("x86_64-apple-darwin"), 144 SubtargetFeatures(), getGenericEdgeKindName); 145 146 auto &DataSec = G->createSection("__data", MemProt::Read | MemProt::Write); 147 auto &DataBlock = G->createContentBlock(DataSec, BlockContent, 148 orc::ExecutorAddr(0x1000), 8, 0); 149 G->addDefinedSymbol(DataBlock, 4, "_anchor", 4, Linkage::Weak, Scope::Default, 150 false, true); 151 152 EXPECT_THAT_ERROR(ObjLinkingLayer.add(JD, std::move(G)), Succeeded()); 153 154 EXPECT_THAT_EXPECTED(ES.lookup(&JD, "_anchor"), Succeeded()); 155 } 156 157 TEST_F(ObjectLinkingLayerTest, HandleErrorDuringPostAllocationPass) { 158 // We want to confirm that Errors in post allocation passes correctly 159 // abandon the in-flight allocation and report an error. 160 class TestPlugin : public ObjectLinkingLayer::Plugin { 161 public: 162 ~TestPlugin() { EXPECT_TRUE(ErrorReported); } 163 164 void modifyPassConfig(MaterializationResponsibility &MR, 165 jitlink::LinkGraph &G, 166 jitlink::PassConfiguration &Config) override { 167 Config.PostAllocationPasses.insert( 168 Config.PostAllocationPasses.begin(), [](LinkGraph &G) { 169 return make_error<StringError>("Kaboom", inconvertibleErrorCode()); 170 }); 171 } 172 173 Error notifyFailed(MaterializationResponsibility &MR) override { 174 ErrorReported = true; 175 return Error::success(); 176 } 177 178 Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override { 179 return Error::success(); 180 } 181 void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey, 182 ResourceKey SrcKey) override { 183 llvm_unreachable("unexpected resource transfer"); 184 } 185 186 private: 187 bool ErrorReported = false; 188 }; 189 190 // We expect this test to generate errors. Consume them so that we don't 191 // add noise to the test logs. 192 ES.setErrorReporter(consumeError); 193 194 ObjLinkingLayer.addPlugin(std::make_unique<TestPlugin>()); 195 auto G = std::make_unique<LinkGraph>( 196 "foo", ES.getSymbolStringPool(), Triple("x86_64-apple-darwin"), 197 SubtargetFeatures(), getGenericEdgeKindName); 198 199 auto &DataSec = G->createSection("__data", MemProt::Read | MemProt::Write); 200 auto &DataBlock = G->createContentBlock(DataSec, BlockContent, 201 orc::ExecutorAddr(0x1000), 8, 0); 202 G->addDefinedSymbol(DataBlock, 4, "_anchor", 4, Linkage::Weak, Scope::Default, 203 false, true); 204 205 EXPECT_THAT_ERROR(ObjLinkingLayer.add(JD, std::move(G)), Succeeded()); 206 207 EXPECT_THAT_EXPECTED(ES.lookup(&JD, "_anchor"), Failed()); 208 } 209 210 TEST_F(ObjectLinkingLayerTest, AddAndRemovePlugins) { 211 class TestPlugin : public ObjectLinkingLayer::Plugin { 212 public: 213 TestPlugin(size_t &ActivationCount, bool &PluginDestroyed) 214 : ActivationCount(ActivationCount), PluginDestroyed(PluginDestroyed) {} 215 216 ~TestPlugin() { PluginDestroyed = true; } 217 218 void modifyPassConfig(MaterializationResponsibility &MR, 219 jitlink::LinkGraph &G, 220 jitlink::PassConfiguration &Config) override { 221 ++ActivationCount; 222 } 223 224 Error notifyFailed(MaterializationResponsibility &MR) override { 225 ADD_FAILURE() << "TestPlugin::notifyFailed called unexpectedly"; 226 return Error::success(); 227 } 228 229 Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override { 230 return Error::success(); 231 } 232 233 void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey, 234 ResourceKey SrcKey) override {} 235 236 private: 237 size_t &ActivationCount; 238 bool &PluginDestroyed; 239 }; 240 241 size_t ActivationCount = 0; 242 bool PluginDestroyed = false; 243 244 auto P = std::make_shared<TestPlugin>(ActivationCount, PluginDestroyed); 245 246 ObjLinkingLayer.addPlugin(P); 247 248 { 249 auto G1 = std::make_unique<LinkGraph>( 250 "G1", ES.getSymbolStringPool(), Triple("x86_64-apple-darwin"), 251 SubtargetFeatures(), x86_64::getEdgeKindName); 252 253 auto &DataSec = G1->createSection("__data", MemProt::Read | MemProt::Write); 254 auto &DataBlock = G1->createContentBlock(DataSec, BlockContent, 255 orc::ExecutorAddr(0x1000), 8, 0); 256 G1->addDefinedSymbol(DataBlock, 4, "_anchor1", 4, Linkage::Weak, 257 Scope::Default, false, true); 258 259 EXPECT_THAT_ERROR(ObjLinkingLayer.add(JD, std::move(G1)), Succeeded()); 260 EXPECT_THAT_EXPECTED(ES.lookup(&JD, "_anchor1"), Succeeded()); 261 EXPECT_EQ(ActivationCount, 1U); 262 } 263 264 ObjLinkingLayer.removePlugin(*P); 265 266 { 267 auto G2 = std::make_unique<LinkGraph>( 268 "G2", ES.getSymbolStringPool(), Triple("x86_64-apple-darwin"), 269 SubtargetFeatures(), x86_64::getEdgeKindName); 270 271 auto &DataSec = G2->createSection("__data", MemProt::Read | MemProt::Write); 272 auto &DataBlock = G2->createContentBlock(DataSec, BlockContent, 273 orc::ExecutorAddr(0x1000), 8, 0); 274 G2->addDefinedSymbol(DataBlock, 4, "_anchor2", 4, Linkage::Weak, 275 Scope::Default, false, true); 276 277 EXPECT_THAT_ERROR(ObjLinkingLayer.add(JD, std::move(G2)), Succeeded()); 278 EXPECT_THAT_EXPECTED(ES.lookup(&JD, "_anchor2"), Succeeded()); 279 EXPECT_EQ(ActivationCount, 1U); 280 } 281 282 P.reset(); 283 EXPECT_TRUE(PluginDestroyed); 284 } 285 286 TEST(ObjectLinkingLayerSearchGeneratorTest, AbsoluteSymbolsObjectLayer) { 287 class TestEPC : public UnsupportedExecutorProcessControl, 288 public DylibManager { 289 public: 290 TestEPC() 291 : UnsupportedExecutorProcessControl(nullptr, nullptr, 292 "x86_64-apple-darwin") { 293 this->DylibMgr = this; 294 } 295 296 Expected<tpctypes::DylibHandle> loadDylib(const char *DylibPath) override { 297 return ExecutorAddr::fromPtr((void *)nullptr); 298 } 299 300 void lookupSymbolsAsync(ArrayRef<LookupRequest> Request, 301 SymbolLookupCompleteFn Complete) override { 302 std::vector<ExecutorSymbolDef> Result; 303 EXPECT_EQ(Request.size(), 1u); 304 for (auto &LR : Request) { 305 EXPECT_EQ(LR.Symbols.size(), 1u); 306 for (auto &Sym : LR.Symbols) { 307 if (*Sym.first == "_testFunc") { 308 ExecutorSymbolDef Def{ExecutorAddr::fromPtr((void *)0x1000), 309 JITSymbolFlags::Exported}; 310 Result.push_back(Def); 311 } else { 312 ADD_FAILURE() << "unexpected symbol request " << *Sym.first; 313 } 314 } 315 } 316 Complete(std::vector<tpctypes::LookupResult>{1, Result}); 317 } 318 }; 319 320 ExecutionSession ES{std::make_unique<TestEPC>()}; 321 JITDylib &JD = ES.createBareJITDylib("main"); 322 ObjectLinkingLayer ObjLinkingLayer{ 323 ES, std::make_unique<InProcessMemoryManager>(4096)}; 324 325 auto G = EPCDynamicLibrarySearchGenerator::GetForTargetProcess( 326 ES, {}, [&](JITDylib &JD, SymbolMap Syms) { 327 auto G = 328 absoluteSymbolsLinkGraph(Triple("x86_64-apple-darwin"), 329 ES.getSymbolStringPool(), std::move(Syms)); 330 return ObjLinkingLayer.add(JD, std::move(G)); 331 }); 332 ASSERT_THAT_EXPECTED(G, Succeeded()); 333 JD.addGenerator(std::move(*G)); 334 335 class CheckDefs : public ObjectLinkingLayer::Plugin { 336 public: 337 ~CheckDefs() { EXPECT_TRUE(SawSymbolDef); } 338 339 void modifyPassConfig(MaterializationResponsibility &MR, 340 jitlink::LinkGraph &G, 341 jitlink::PassConfiguration &Config) override { 342 Config.PostAllocationPasses.push_back([this](LinkGraph &G) { 343 unsigned SymCount = 0; 344 for (Symbol *Sym : G.absolute_symbols()) { 345 SymCount += 1; 346 if (!Sym->hasName()) { 347 ADD_FAILURE() << "unexpected unnamed symbol"; 348 continue; 349 } 350 if (*Sym->getName() == "_testFunc") 351 SawSymbolDef = true; 352 else 353 ADD_FAILURE() << "unexpected symbol " << Sym->getName(); 354 } 355 EXPECT_EQ(SymCount, 1u); 356 return Error::success(); 357 }); 358 } 359 360 Error notifyFailed(MaterializationResponsibility &MR) override { 361 return Error::success(); 362 } 363 364 Error notifyRemovingResources(JITDylib &JD, ResourceKey K) override { 365 return Error::success(); 366 } 367 void notifyTransferringResources(JITDylib &JD, ResourceKey DstKey, 368 ResourceKey SrcKey) override { 369 llvm_unreachable("unexpected resource transfer"); 370 } 371 372 private: 373 bool SawSymbolDef = false; 374 }; 375 376 ObjLinkingLayer.addPlugin(std::make_unique<CheckDefs>()); 377 378 EXPECT_THAT_EXPECTED(ES.lookup(&JD, "_testFunc"), Succeeded()); 379 EXPECT_THAT_ERROR(ES.endSession(), Succeeded()); 380 } 381 382 } // end anonymous namespace 383