1 //===---------- LazyReexports.cpp - Utilities for lazy reexports ----------===//
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/LazyReexports.h"
10 
11 #include "llvm/ADT/Triple.h"
12 #include "llvm/ExecutionEngine/Orc/OrcABISupport.h"
13 
14 #define DEBUG_TYPE "orc"
15 
16 namespace llvm {
17 namespace orc {
18 
19 LazyCallThroughManager::LazyCallThroughManager(
20     ExecutionSession &ES, JITTargetAddress ErrorHandlerAddr,
21     std::unique_ptr<TrampolinePool> TP)
22     : ES(ES), ErrorHandlerAddr(ErrorHandlerAddr), TP(std::move(TP)) {}
23 
24 Expected<JITTargetAddress> LazyCallThroughManager::getCallThroughTrampoline(
25     JITDylib &SourceJD, SymbolStringPtr SymbolName,
26     NotifyResolvedFunction NotifyResolved) {
27   std::lock_guard<std::mutex> Lock(LCTMMutex);
28   auto Trampoline = TP->getTrampoline();
29 
30   if (!Trampoline)
31     return Trampoline.takeError();
32 
33   Reexports[*Trampoline] = std::make_pair(&SourceJD, std::move(SymbolName));
34   Notifiers[*Trampoline] = std::move(NotifyResolved);
35   return *Trampoline;
36 }
37 
38 JITTargetAddress
39 LazyCallThroughManager::callThroughToSymbol(JITTargetAddress TrampolineAddr) {
40   JITDylib *SourceJD = nullptr;
41   SymbolStringPtr SymbolName;
42 
43   {
44     std::lock_guard<std::mutex> Lock(LCTMMutex);
45     auto I = Reexports.find(TrampolineAddr);
46     if (I == Reexports.end())
47       return ErrorHandlerAddr;
48     SourceJD = I->second.first;
49     SymbolName = I->second.second;
50   }
51 
52   auto LookupResult = ES.lookup(
53       makeJITDylibSearchOrder(SourceJD, JITDylibLookupFlags::MatchAllSymbols),
54       SymbolName);
55 
56   if (!LookupResult) {
57     ES.reportError(LookupResult.takeError());
58     return ErrorHandlerAddr;
59   }
60 
61   auto ResolvedAddr = LookupResult->getAddress();
62 
63   NotifyResolvedFunction NotifyResolved;
64   {
65     std::lock_guard<std::mutex> Lock(LCTMMutex);
66     auto I = Notifiers.find(TrampolineAddr);
67     if (I != Notifiers.end()) {
68       NotifyResolved = std::move(I->second);
69       Notifiers.erase(I);
70     }
71   }
72 
73   if (NotifyResolved) {
74     if (auto Err = NotifyResolved(ResolvedAddr)) {
75       ES.reportError(std::move(Err));
76       return ErrorHandlerAddr;
77     }
78   }
79 
80   return ResolvedAddr;
81 }
82 
83 Expected<std::unique_ptr<LazyCallThroughManager>>
84 createLocalLazyCallThroughManager(const Triple &T, ExecutionSession &ES,
85                                   JITTargetAddress ErrorHandlerAddr) {
86   switch (T.getArch()) {
87   default:
88     return make_error<StringError>(
89         std::string("No callback manager available for ") + T.str(),
90         inconvertibleErrorCode());
91 
92   case Triple::aarch64:
93   case Triple::aarch64_32:
94     return LocalLazyCallThroughManager::Create<OrcAArch64>(ES,
95                                                            ErrorHandlerAddr);
96 
97   case Triple::x86:
98     return LocalLazyCallThroughManager::Create<OrcI386>(ES, ErrorHandlerAddr);
99 
100   case Triple::mips:
101     return LocalLazyCallThroughManager::Create<OrcMips32Be>(ES,
102                                                             ErrorHandlerAddr);
103 
104   case Triple::mipsel:
105     return LocalLazyCallThroughManager::Create<OrcMips32Le>(ES,
106                                                             ErrorHandlerAddr);
107 
108   case Triple::mips64:
109   case Triple::mips64el:
110     return LocalLazyCallThroughManager::Create<OrcMips64>(ES, ErrorHandlerAddr);
111 
112   case Triple::x86_64:
113     if (T.getOS() == Triple::OSType::Win32)
114       return LocalLazyCallThroughManager::Create<OrcX86_64_Win32>(
115           ES, ErrorHandlerAddr);
116     else
117       return LocalLazyCallThroughManager::Create<OrcX86_64_SysV>(
118           ES, ErrorHandlerAddr);
119   }
120 }
121 
122 LazyReexportsMaterializationUnit::LazyReexportsMaterializationUnit(
123     LazyCallThroughManager &LCTManager, IndirectStubsManager &ISManager,
124     JITDylib &SourceJD, SymbolAliasMap CallableAliases, ImplSymbolMap *SrcJDLoc,
125     VModuleKey K)
126     : MaterializationUnit(extractFlags(CallableAliases), std::move(K)),
127       LCTManager(LCTManager), ISManager(ISManager), SourceJD(SourceJD),
128       CallableAliases(std::move(CallableAliases)),
129       AliaseeTable(SrcJDLoc) {}
130 
131 StringRef LazyReexportsMaterializationUnit::getName() const {
132   return "<Lazy Reexports>";
133 }
134 
135 void LazyReexportsMaterializationUnit::materialize(
136     MaterializationResponsibility R) {
137   auto RequestedSymbols = R.getRequestedSymbols();
138 
139   SymbolAliasMap RequestedAliases;
140   for (auto &RequestedSymbol : RequestedSymbols) {
141     auto I = CallableAliases.find(RequestedSymbol);
142     assert(I != CallableAliases.end() && "Symbol not found in alias map?");
143     RequestedAliases[I->first] = std::move(I->second);
144     CallableAliases.erase(I);
145   }
146 
147   if (!CallableAliases.empty())
148     R.replace(lazyReexports(LCTManager, ISManager, SourceJD,
149                             std::move(CallableAliases), AliaseeTable));
150 
151   IndirectStubsManager::StubInitsMap StubInits;
152   for (auto &Alias : RequestedAliases) {
153 
154     auto CallThroughTrampoline = LCTManager.getCallThroughTrampoline(
155         SourceJD, Alias.second.Aliasee,
156         [&ISManager = this->ISManager,
157          StubSym = Alias.first](JITTargetAddress ResolvedAddr) -> Error {
158           return ISManager.updatePointer(*StubSym, ResolvedAddr);
159         });
160 
161     if (!CallThroughTrampoline) {
162       SourceJD.getExecutionSession().reportError(
163           CallThroughTrampoline.takeError());
164       R.failMaterialization();
165       return;
166     }
167 
168     StubInits[*Alias.first] =
169         std::make_pair(*CallThroughTrampoline, Alias.second.AliasFlags);
170   }
171 
172   if (AliaseeTable != nullptr && !RequestedAliases.empty())
173     AliaseeTable->trackImpls(RequestedAliases, &SourceJD);
174 
175   if (auto Err = ISManager.createStubs(StubInits)) {
176     SourceJD.getExecutionSession().reportError(std::move(Err));
177     R.failMaterialization();
178     return;
179   }
180 
181   SymbolMap Stubs;
182   for (auto &Alias : RequestedAliases)
183     Stubs[Alias.first] = ISManager.findStub(*Alias.first, false);
184 
185   // No registered dependencies, so these calls cannot fail.
186   cantFail(R.notifyResolved(Stubs));
187   cantFail(R.notifyEmitted());
188 }
189 
190 void LazyReexportsMaterializationUnit::discard(const JITDylib &JD,
191                                                const SymbolStringPtr &Name) {
192   assert(CallableAliases.count(Name) &&
193          "Symbol not covered by this MaterializationUnit");
194   CallableAliases.erase(Name);
195 }
196 
197 SymbolFlagsMap
198 LazyReexportsMaterializationUnit::extractFlags(const SymbolAliasMap &Aliases) {
199   SymbolFlagsMap SymbolFlags;
200   for (auto &KV : Aliases) {
201     assert(KV.second.AliasFlags.isCallable() &&
202            "Lazy re-exports must be callable symbols");
203     SymbolFlags[KV.first] = KV.second.AliasFlags;
204   }
205   return SymbolFlags;
206 }
207 
208 } // End namespace orc.
209 } // End namespace llvm.
210