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
LazyCallThroughManager(ExecutionSession & ES,ExecutorAddr ErrorHandlerAddr,TrampolinePool * TP)19 LazyCallThroughManager::LazyCallThroughManager(ExecutionSession &ES,
20 ExecutorAddr ErrorHandlerAddr,
21 TrampolinePool *TP)
22 : ES(ES), ErrorHandlerAddr(ErrorHandlerAddr), TP(TP) {}
23
getCallThroughTrampoline(JITDylib & SourceJD,SymbolStringPtr SymbolName,NotifyResolvedFunction NotifyResolved)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
reportCallThroughError(Error Err)40 ExecutorAddr LazyCallThroughManager::reportCallThroughError(Error Err) {
41 ES.reportError(std::move(Err));
42 return ErrorHandlerAddr;
43 }
44
45 Expected<LazyCallThroughManager::ReexportsEntry>
findReexport(ExecutorAddr TrampolineAddr)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
notifyResolved(ExecutorAddr TrampolineAddr,ExecutorAddr ResolvedAddr)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
resolveTrampolineLandingAddress(ExecutorAddr TrampolineAddr,NotifyLandingResolvedFunction NotifyLandingResolved)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>>
createLocalLazyCallThroughManager(const Triple & T,ExecutionSession & ES,ExecutorAddr ErrorHandlerAddr)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
LazyReexportsMaterializationUnit(LazyCallThroughManager & LCTManager,IndirectStubsManager & ISManager,JITDylib & SourceJD,SymbolAliasMap CallableAliases,ImplSymbolMap * SrcJDLoc)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
getName() const160 StringRef LazyReexportsMaterializationUnit::getName() const {
161 return "<Lazy Reexports>";
162 }
163
materialize(std::unique_ptr<MaterializationResponsibility> R)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
discard(const JITDylib & JD,const SymbolStringPtr & Name)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
extractFlags(const SymbolAliasMap & Aliases)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