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