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: &SourceJD, .SymbolName: std::move(SymbolName)}; |
36 | Notifiers[*Trampoline] = std::move(NotifyResolved); |
37 | return *Trampoline; |
38 | } |
39 | |
40 | ExecutorAddr LazyCallThroughManager::reportCallThroughError(Error Err) { |
41 | ES.reportError(Err: 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(x: TrampolineAddr); |
49 | if (I == Reexports.end()) |
50 | return createStringError(EC: inconvertibleErrorCode(), |
51 | S: "Missing reexport for trampoline address %p" + |
52 | formatv(Fmt: "{0:x}" , Vals&: 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(x: TrampolineAddr); |
62 | if (I != Notifiers.end()) { |
63 | NotifyResolved = std::move(I->second); |
64 | Notifiers.erase(position: 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(Err: 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, ResolvedAddr: LandingAddr)) |
91 | NotifyLandingResolved(reportCallThroughError(Err: std::move(Err))); |
92 | else |
93 | NotifyLandingResolved(LandingAddr); |
94 | } else { |
95 | NotifyLandingResolved(reportCallThroughError(Err: Result.takeError())); |
96 | } |
97 | }; |
98 | |
99 | ES.lookup(K: LookupKind::Static, |
100 | SearchOrder: makeJITDylibSearchOrder(JDs: Entry->SourceJD, |
101 | Flags: JITDylibLookupFlags::MatchAllSymbols), |
102 | Symbols: std::move(SLS), RequiredState: SymbolState::Ready, NotifyComplete: std::move(Callback), |
103 | RegisterDependencies: 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 | Args: std::string("No callback manager available for " ) + T.str(), |
113 | Args: 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(Aliases: 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(Val: 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(MU: lazyReexports(LCTManager, ISManager, SourceJD, |
178 | CallableAliases: std::move(CallableAliases), |
179 | SrcJDLoc: AliaseeTable))) { |
180 | R->getExecutionSession().reportError(Err: 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, SymbolName: Alias.second.Aliasee, |
190 | NotifyResolved: [&ISManager = this->ISManager, |
191 | StubSym = Alias.first](ExecutorAddr ResolvedAddr) -> Error { |
192 | return ISManager.updatePointer(Name: *StubSym, NewAddr: ResolvedAddr); |
193 | }); |
194 | |
195 | if (!CallThroughTrampoline) { |
196 | SourceJD.getExecutionSession().reportError( |
197 | Err: CallThroughTrampoline.takeError()); |
198 | R->failMaterialization(); |
199 | return; |
200 | } |
201 | |
202 | StubInits[*Alias.first] = |
203 | std::make_pair(x&: *CallThroughTrampoline, y&: Alias.second.AliasFlags); |
204 | } |
205 | |
206 | if (AliaseeTable != nullptr && !RequestedAliases.empty()) |
207 | AliaseeTable->trackImpls(ImplMaps: RequestedAliases, SrcJD: &SourceJD); |
208 | |
209 | if (auto Err = ISManager.createStubs(StubInits)) { |
210 | SourceJD.getExecutionSession().reportError(Err: std::move(Err)); |
211 | R->failMaterialization(); |
212 | return; |
213 | } |
214 | |
215 | SymbolMap Stubs; |
216 | for (auto &Alias : RequestedAliases) |
217 | Stubs[Alias.first] = ISManager.findStub(Name: *Alias.first, ExportedStubsOnly: false); |
218 | |
219 | // No registered dependencies, so these calls cannot fail. |
220 | cantFail(Err: R->notifyResolved(Symbols: Stubs)); |
221 | cantFail(Err: R->notifyEmitted(EmittedDeps: {})); |
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(Val: Name); |
229 | } |
230 | |
231 | MaterializationUnit::Interface |
232 | LazyReexportsMaterializationUnit::(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 | |