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/ExecutionEngine/Orc/OrcABISupport.h"
12 #include "llvm/TargetParser/Triple.h"
13 
14 #define DEBUG_TYPE "orc"
15 
16 namespace llvm {
17 namespace orc {
18 
19 LazyCallThroughManager::LazyCallThroughManager(ExecutionSession &ES,
20                                                ExecutorAddr ErrorHandlerAddr,
21                                                TrampolinePool *TP)
22     : ES(ES), ErrorHandlerAddr(ErrorHandlerAddr), TP(TP) {}
23 
24 Expected<ExecutorAddr> LazyCallThroughManager::getCallThroughTrampoline(
25     JITDylib &SourceJD, SymbolStringPtr SymbolName,
26     NotifyResolvedFunction NotifyResolved) {
27   assert(TP && "TrampolinePool not set");
28 
29   std::lock_guard<std::mutex> Lock(LCTMMutex);
30   auto Trampoline = TP->getTrampoline();
31 
32   if (!Trampoline)
33     return Trampoline.takeError();
34 
35   Reexports[*Trampoline] = ReexportsEntry{&SourceJD, std::move(SymbolName)};
36   Notifiers[*Trampoline] = std::move(NotifyResolved);
37   return *Trampoline;
38 }
39 
40 ExecutorAddr LazyCallThroughManager::reportCallThroughError(Error Err) {
41   ES.reportError(std::move(Err));
42   return ErrorHandlerAddr;
43 }
44 
45 Expected<LazyCallThroughManager::ReexportsEntry>
46 LazyCallThroughManager::findReexport(ExecutorAddr TrampolineAddr) {
47   std::lock_guard<std::mutex> Lock(LCTMMutex);
48   auto I = Reexports.find(TrampolineAddr);
49   if (I == Reexports.end())
50     return createStringError(inconvertibleErrorCode(),
51                              "Missing reexport for trampoline address %p" +
52                                  formatv("{0:x}", TrampolineAddr));
53   return I->second;
54 }
55 
56 Error LazyCallThroughManager::notifyResolved(ExecutorAddr TrampolineAddr,
57                                              ExecutorAddr ResolvedAddr) {
58   NotifyResolvedFunction NotifyResolved;
59   {
60     std::lock_guard<std::mutex> Lock(LCTMMutex);
61     auto I = Notifiers.find(TrampolineAddr);
62     if (I != Notifiers.end()) {
63       NotifyResolved = std::move(I->second);
64       Notifiers.erase(I);
65     }
66   }
67 
68   return NotifyResolved ? NotifyResolved(ResolvedAddr) : Error::success();
69 }
70 
71 void LazyCallThroughManager::resolveTrampolineLandingAddress(
72     ExecutorAddr TrampolineAddr,
73     NotifyLandingResolvedFunction NotifyLandingResolved) {
74 
75   auto Entry = findReexport(TrampolineAddr);
76   if (!Entry)
77     return NotifyLandingResolved(reportCallThroughError(Entry.takeError()));
78 
79   // Declaring SLS and the callback outside of the call to ES.lookup is a
80   // workaround to fix build failures on AIX and on z/OS platforms.
81   SymbolLookupSet SLS({Entry->SymbolName});
82   auto Callback = [this, TrampolineAddr, SymbolName = Entry->SymbolName,
83                    NotifyLandingResolved = std::move(NotifyLandingResolved)](
84                       Expected<SymbolMap> Result) mutable {
85     if (Result) {
86       assert(Result->size() == 1 && "Unexpected result size");
87       assert(Result->count(SymbolName) && "Unexpected result value");
88       ExecutorAddr LandingAddr = (*Result)[SymbolName].getAddress();
89 
90       if (auto Err = notifyResolved(TrampolineAddr, LandingAddr))
91         NotifyLandingResolved(reportCallThroughError(std::move(Err)));
92       else
93         NotifyLandingResolved(LandingAddr);
94     } else {
95       NotifyLandingResolved(reportCallThroughError(Result.takeError()));
96     }
97   };
98 
99   ES.lookup(LookupKind::Static,
100             makeJITDylibSearchOrder(Entry->SourceJD,
101                                     JITDylibLookupFlags::MatchAllSymbols),
102             std::move(SLS), SymbolState::Ready, std::move(Callback),
103             NoDependenciesToRegister);
104 }
105 
106 Expected<std::unique_ptr<LazyCallThroughManager>>
107 createLocalLazyCallThroughManager(const Triple &T, ExecutionSession &ES,
108                                   ExecutorAddr ErrorHandlerAddr) {
109   switch (T.getArch()) {
110   default:
111     return make_error<StringError>(
112         std::string("No callback manager available for ") + T.str(),
113         inconvertibleErrorCode());
114 
115   case Triple::aarch64:
116   case Triple::aarch64_32:
117     return LocalLazyCallThroughManager::Create<OrcAArch64>(ES,
118                                                            ErrorHandlerAddr);
119 
120   case Triple::x86:
121     return LocalLazyCallThroughManager::Create<OrcI386>(ES, ErrorHandlerAddr);
122 
123   case Triple::loongarch64:
124     return LocalLazyCallThroughManager::Create<OrcLoongArch64>(
125         ES, ErrorHandlerAddr);
126 
127   case Triple::mips:
128     return LocalLazyCallThroughManager::Create<OrcMips32Be>(ES,
129                                                             ErrorHandlerAddr);
130 
131   case Triple::mipsel:
132     return LocalLazyCallThroughManager::Create<OrcMips32Le>(ES,
133                                                             ErrorHandlerAddr);
134 
135   case Triple::mips64:
136   case Triple::mips64el:
137     return LocalLazyCallThroughManager::Create<OrcMips64>(ES, ErrorHandlerAddr);
138 
139   case Triple::riscv64:
140     return LocalLazyCallThroughManager::Create<OrcRiscv64>(ES,
141                                                            ErrorHandlerAddr);
142 
143   case Triple::x86_64:
144     if (T.getOS() == Triple::OSType::Win32)
145       return LocalLazyCallThroughManager::Create<OrcX86_64_Win32>(
146           ES, ErrorHandlerAddr);
147     else
148       return LocalLazyCallThroughManager::Create<OrcX86_64_SysV>(
149           ES, ErrorHandlerAddr);
150   }
151 }
152 
153 LazyReexportsMaterializationUnit::LazyReexportsMaterializationUnit(
154     LazyCallThroughManager &LCTManager, IndirectStubsManager &ISManager,
155     JITDylib &SourceJD, SymbolAliasMap CallableAliases, ImplSymbolMap *SrcJDLoc)
156     : MaterializationUnit(extractFlags(CallableAliases)),
157       LCTManager(LCTManager), ISManager(ISManager), SourceJD(SourceJD),
158       CallableAliases(std::move(CallableAliases)), AliaseeTable(SrcJDLoc) {}
159 
160 StringRef LazyReexportsMaterializationUnit::getName() const {
161   return "<Lazy Reexports>";
162 }
163 
164 void LazyReexportsMaterializationUnit::materialize(
165     std::unique_ptr<MaterializationResponsibility> R) {
166   auto RequestedSymbols = R->getRequestedSymbols();
167 
168   SymbolAliasMap RequestedAliases;
169   for (auto &RequestedSymbol : RequestedSymbols) {
170     auto I = CallableAliases.find(RequestedSymbol);
171     assert(I != CallableAliases.end() && "Symbol not found in alias map?");
172     RequestedAliases[I->first] = std::move(I->second);
173     CallableAliases.erase(I);
174   }
175 
176   if (!CallableAliases.empty())
177     if (auto Err = R->replace(lazyReexports(LCTManager, ISManager, SourceJD,
178                                             std::move(CallableAliases),
179                                             AliaseeTable))) {
180       R->getExecutionSession().reportError(std::move(Err));
181       R->failMaterialization();
182       return;
183     }
184 
185   IndirectStubsManager::StubInitsMap StubInits;
186   for (auto &Alias : RequestedAliases) {
187 
188     auto CallThroughTrampoline = LCTManager.getCallThroughTrampoline(
189         SourceJD, Alias.second.Aliasee,
190         [&ISManager = this->ISManager,
191          StubSym = Alias.first](ExecutorAddr ResolvedAddr) -> Error {
192           return ISManager.updatePointer(*StubSym, ResolvedAddr);
193         });
194 
195     if (!CallThroughTrampoline) {
196       SourceJD.getExecutionSession().reportError(
197           CallThroughTrampoline.takeError());
198       R->failMaterialization();
199       return;
200     }
201 
202     StubInits[*Alias.first] =
203         std::make_pair(*CallThroughTrampoline, Alias.second.AliasFlags);
204   }
205 
206   if (AliaseeTable != nullptr && !RequestedAliases.empty())
207     AliaseeTable->trackImpls(RequestedAliases, &SourceJD);
208 
209   if (auto Err = ISManager.createStubs(StubInits)) {
210     SourceJD.getExecutionSession().reportError(std::move(Err));
211     R->failMaterialization();
212     return;
213   }
214 
215   SymbolMap Stubs;
216   for (auto &Alias : RequestedAliases)
217     Stubs[Alias.first] = ISManager.findStub(*Alias.first, false);
218 
219   // No registered dependencies, so these calls cannot fail.
220   cantFail(R->notifyResolved(Stubs));
221   cantFail(R->notifyEmitted());
222 }
223 
224 void LazyReexportsMaterializationUnit::discard(const JITDylib &JD,
225                                                const SymbolStringPtr &Name) {
226   assert(CallableAliases.count(Name) &&
227          "Symbol not covered by this MaterializationUnit");
228   CallableAliases.erase(Name);
229 }
230 
231 MaterializationUnit::Interface
232 LazyReexportsMaterializationUnit::extractFlags(const SymbolAliasMap &Aliases) {
233   SymbolFlagsMap SymbolFlags;
234   for (auto &KV : Aliases) {
235     assert(KV.second.AliasFlags.isCallable() &&
236            "Lazy re-exports must be callable symbols");
237     SymbolFlags[KV.first] = KV.second.AliasFlags;
238   }
239   return MaterializationUnit::Interface(std::move(SymbolFlags), nullptr);
240 }
241 
242 } // End namespace orc.
243 } // End namespace llvm.
244