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