xref: /llvm-project/llvm/unittests/ExecutionEngine/Orc/ObjectLinkingLayerTest.cpp (revision 4eaff6c58ae2f130ac8d63cf2c87bbb483114876)
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