| //===---------- LazyReexports.cpp - Utilities for lazy reexports ----------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/ExecutionEngine/Orc/LazyReexports.h" |
| |
| #include "llvm/ADT/Triple.h" |
| #include "llvm/ExecutionEngine/Orc/OrcABISupport.h" |
| |
| #define DEBUG_TYPE "orc" |
| |
| namespace llvm { |
| namespace orc { |
| |
| LazyCallThroughManager::LazyCallThroughManager( |
| ExecutionSession &ES, JITTargetAddress ErrorHandlerAddr, TrampolinePool *TP) |
| : ES(ES), ErrorHandlerAddr(ErrorHandlerAddr), TP(TP) {} |
| |
| Expected<JITTargetAddress> LazyCallThroughManager::getCallThroughTrampoline( |
| JITDylib &SourceJD, SymbolStringPtr SymbolName, |
| NotifyResolvedFunction NotifyResolved) { |
| assert(TP && "TrampolinePool not set"); |
| |
| std::lock_guard<std::mutex> Lock(LCTMMutex); |
| auto Trampoline = TP->getTrampoline(); |
| |
| if (!Trampoline) |
| return Trampoline.takeError(); |
| |
| Reexports[*Trampoline] = ReexportsEntry{&SourceJD, std::move(SymbolName)}; |
| Notifiers[*Trampoline] = std::move(NotifyResolved); |
| return *Trampoline; |
| } |
| |
| JITTargetAddress LazyCallThroughManager::reportCallThroughError(Error Err) { |
| ES.reportError(std::move(Err)); |
| return ErrorHandlerAddr; |
| } |
| |
| Expected<LazyCallThroughManager::ReexportsEntry> |
| LazyCallThroughManager::findReexport(JITTargetAddress TrampolineAddr) { |
| std::lock_guard<std::mutex> Lock(LCTMMutex); |
| auto I = Reexports.find(TrampolineAddr); |
| if (I == Reexports.end()) |
| return createStringError(inconvertibleErrorCode(), |
| "Missing reexport for trampoline address %p", |
| TrampolineAddr); |
| return I->second; |
| } |
| |
| Error LazyCallThroughManager::notifyResolved(JITTargetAddress TrampolineAddr, |
| JITTargetAddress ResolvedAddr) { |
| NotifyResolvedFunction NotifyResolved; |
| { |
| std::lock_guard<std::mutex> Lock(LCTMMutex); |
| auto I = Notifiers.find(TrampolineAddr); |
| if (I != Notifiers.end()) { |
| NotifyResolved = std::move(I->second); |
| Notifiers.erase(I); |
| } |
| } |
| |
| return NotifyResolved ? NotifyResolved(ResolvedAddr) : Error::success(); |
| } |
| |
| void LazyCallThroughManager::resolveTrampolineLandingAddress( |
| JITTargetAddress TrampolineAddr, |
| NotifyLandingResolvedFunction NotifyLandingResolved) { |
| |
| auto Entry = findReexport(TrampolineAddr); |
| if (!Entry) |
| return NotifyLandingResolved(reportCallThroughError(Entry.takeError())); |
| |
| // Declaring SLS and the callback outside of the call to ES.lookup is a |
| // workaround to fix build failures on AIX and on z/OS platforms. |
| SymbolLookupSet SLS({Entry->SymbolName}); |
| auto Callback = [this, TrampolineAddr, SymbolName = Entry->SymbolName, |
| NotifyLandingResolved = std::move(NotifyLandingResolved)]( |
| Expected<SymbolMap> Result) mutable { |
| if (Result) { |
| assert(Result->size() == 1 && "Unexpected result size"); |
| assert(Result->count(SymbolName) && "Unexpected result value"); |
| JITTargetAddress LandingAddr = (*Result)[SymbolName].getAddress(); |
| |
| if (auto Err = notifyResolved(TrampolineAddr, LandingAddr)) |
| NotifyLandingResolved(reportCallThroughError(std::move(Err))); |
| else |
| NotifyLandingResolved(LandingAddr); |
| } else { |
| NotifyLandingResolved(reportCallThroughError(Result.takeError())); |
| } |
| }; |
| |
| ES.lookup(LookupKind::Static, |
| makeJITDylibSearchOrder(Entry->SourceJD, |
| JITDylibLookupFlags::MatchAllSymbols), |
| std::move(SLS), SymbolState::Ready, std::move(Callback), |
| NoDependenciesToRegister); |
| } |
| |
| Expected<std::unique_ptr<LazyCallThroughManager>> |
| createLocalLazyCallThroughManager(const Triple &T, ExecutionSession &ES, |
| JITTargetAddress ErrorHandlerAddr) { |
| switch (T.getArch()) { |
| default: |
| return make_error<StringError>( |
| std::string("No callback manager available for ") + T.str(), |
| inconvertibleErrorCode()); |
| |
| case Triple::aarch64: |
| case Triple::aarch64_32: |
| return LocalLazyCallThroughManager::Create<OrcAArch64>(ES, |
| ErrorHandlerAddr); |
| |
| case Triple::x86: |
| return LocalLazyCallThroughManager::Create<OrcI386>(ES, ErrorHandlerAddr); |
| |
| case Triple::loongarch64: |
| return LocalLazyCallThroughManager::Create<OrcLoongArch64>( |
| ES, ErrorHandlerAddr); |
| |
| case Triple::mips: |
| return LocalLazyCallThroughManager::Create<OrcMips32Be>(ES, |
| ErrorHandlerAddr); |
| |
| case Triple::mipsel: |
| return LocalLazyCallThroughManager::Create<OrcMips32Le>(ES, |
| ErrorHandlerAddr); |
| |
| case Triple::mips64: |
| case Triple::mips64el: |
| return LocalLazyCallThroughManager::Create<OrcMips64>(ES, ErrorHandlerAddr); |
| |
| case Triple::riscv64: |
| return LocalLazyCallThroughManager::Create<OrcRiscv64>(ES, |
| ErrorHandlerAddr); |
| |
| case Triple::x86_64: |
| if (T.getOS() == Triple::OSType::Win32) |
| return LocalLazyCallThroughManager::Create<OrcX86_64_Win32>( |
| ES, ErrorHandlerAddr); |
| else |
| return LocalLazyCallThroughManager::Create<OrcX86_64_SysV>( |
| ES, ErrorHandlerAddr); |
| } |
| } |
| |
| LazyReexportsMaterializationUnit::LazyReexportsMaterializationUnit( |
| LazyCallThroughManager &LCTManager, IndirectStubsManager &ISManager, |
| JITDylib &SourceJD, SymbolAliasMap CallableAliases, ImplSymbolMap *SrcJDLoc) |
| : MaterializationUnit(extractFlags(CallableAliases)), |
| LCTManager(LCTManager), ISManager(ISManager), SourceJD(SourceJD), |
| CallableAliases(std::move(CallableAliases)), AliaseeTable(SrcJDLoc) {} |
| |
| StringRef LazyReexportsMaterializationUnit::getName() const { |
| return "<Lazy Reexports>"; |
| } |
| |
| void LazyReexportsMaterializationUnit::materialize( |
| std::unique_ptr<MaterializationResponsibility> R) { |
| auto RequestedSymbols = R->getRequestedSymbols(); |
| |
| SymbolAliasMap RequestedAliases; |
| for (auto &RequestedSymbol : RequestedSymbols) { |
| auto I = CallableAliases.find(RequestedSymbol); |
| assert(I != CallableAliases.end() && "Symbol not found in alias map?"); |
| RequestedAliases[I->first] = std::move(I->second); |
| CallableAliases.erase(I); |
| } |
| |
| if (!CallableAliases.empty()) |
| if (auto Err = R->replace(lazyReexports(LCTManager, ISManager, SourceJD, |
| std::move(CallableAliases), |
| AliaseeTable))) { |
| R->getExecutionSession().reportError(std::move(Err)); |
| R->failMaterialization(); |
| return; |
| } |
| |
| IndirectStubsManager::StubInitsMap StubInits; |
| for (auto &Alias : RequestedAliases) { |
| |
| auto CallThroughTrampoline = LCTManager.getCallThroughTrampoline( |
| SourceJD, Alias.second.Aliasee, |
| [&ISManager = this->ISManager, |
| StubSym = Alias.first](JITTargetAddress ResolvedAddr) -> Error { |
| return ISManager.updatePointer(*StubSym, ResolvedAddr); |
| }); |
| |
| if (!CallThroughTrampoline) { |
| SourceJD.getExecutionSession().reportError( |
| CallThroughTrampoline.takeError()); |
| R->failMaterialization(); |
| return; |
| } |
| |
| StubInits[*Alias.first] = |
| std::make_pair(*CallThroughTrampoline, Alias.second.AliasFlags); |
| } |
| |
| if (AliaseeTable != nullptr && !RequestedAliases.empty()) |
| AliaseeTable->trackImpls(RequestedAliases, &SourceJD); |
| |
| if (auto Err = ISManager.createStubs(StubInits)) { |
| SourceJD.getExecutionSession().reportError(std::move(Err)); |
| R->failMaterialization(); |
| return; |
| } |
| |
| SymbolMap Stubs; |
| for (auto &Alias : RequestedAliases) |
| Stubs[Alias.first] = ISManager.findStub(*Alias.first, false); |
| |
| // No registered dependencies, so these calls cannot fail. |
| cantFail(R->notifyResolved(Stubs)); |
| cantFail(R->notifyEmitted()); |
| } |
| |
| void LazyReexportsMaterializationUnit::discard(const JITDylib &JD, |
| const SymbolStringPtr &Name) { |
| assert(CallableAliases.count(Name) && |
| "Symbol not covered by this MaterializationUnit"); |
| CallableAliases.erase(Name); |
| } |
| |
| MaterializationUnit::Interface |
| LazyReexportsMaterializationUnit::extractFlags(const SymbolAliasMap &Aliases) { |
| SymbolFlagsMap SymbolFlags; |
| for (auto &KV : Aliases) { |
| assert(KV.second.AliasFlags.isCallable() && |
| "Lazy re-exports must be callable symbols"); |
| SymbolFlags[KV.first] = KV.second.AliasFlags; |
| } |
| return MaterializationUnit::Interface(std::move(SymbolFlags), nullptr); |
| } |
| |
| } // End namespace orc. |
| } // End namespace llvm. |