| //===- COFFAutoImportGenerator.cpp - COFF dllimport auto-import ---------===// |
| // |
| // 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/COFFAutoImportGenerator.h" |
| #include "llvm/ADT/STLExtras.h" |
| #include "llvm/ADT/Twine.h" |
| #include "llvm/ExecutionEngine/JITLink/JITLink.h" |
| #include "llvm/ExecutionEngine/Orc/Shared/ExecutorSymbolDef.h" |
| |
| namespace llvm { |
| namespace orc { |
| |
| Expected<std::unique_ptr<COFFAutoImportGenerator>> |
| COFFAutoImportGenerator::Load(ExecutionSession &ES, ObjectLinkingLayer &L, |
| DylibManager &DylibMgr, const char *LibraryPath) { |
| Triple TT = ES.getTargetTriple(); |
| |
| auto CreatePointer = jitlink::getAnonymousPointerCreator(TT); |
| if (!CreatePointer) |
| return make_error<StringError>( |
| "COFFAutoImportGenerator: no pointer creator for " + TT.str(), |
| inconvertibleErrorCode()); |
| |
| auto CreateStub = jitlink::getPointerJumpStubCreator(TT); |
| if (!CreateStub) |
| return make_error<StringError>( |
| "COFFAutoImportGenerator: no stub creator for " + TT.str(), |
| inconvertibleErrorCode()); |
| |
| auto LibHandle = DylibMgr.loadDylib(LibraryPath); |
| if (!LibHandle) |
| return LibHandle.takeError(); |
| |
| return std::unique_ptr<COFFAutoImportGenerator>(new COFFAutoImportGenerator( |
| ES, L, DylibMgr, *LibHandle, std::move(CreatePointer), |
| std::move(CreateStub))); |
| } |
| |
| Error COFFAutoImportGenerator::tryToGenerate(LookupState &LS, LookupKind K, |
| JITDylib &JD, |
| JITDylibLookupFlags JDLookupFlags, |
| const SymbolLookupSet &Symbols) { |
| if (Symbols.empty()) |
| return Error::success(); |
| |
| // Weakly reference each symbol (minus any __imp_ prefix) so unexported names |
| // are left unresolved; de-dup __imp_X and X into one lookup. |
| SymbolLookupSet LookupSymbols; |
| DenseSet<SymbolStringPtr> Seen; |
| for (auto &KV : Symbols) { |
| StringRef Base = *KV.first; |
| if (Base.starts_with(getImpPrefix())) |
| Base = Base.drop_front(getImpPrefix().size()); |
| SymbolStringPtr BaseName = ES.intern(Base); |
| if (Seen.insert(BaseName).second) |
| LookupSymbols.add(BaseName, SymbolLookupFlags::WeaklyReferencedSymbol); |
| } |
| |
| DylibMgr.lookupSymbolsAsync( |
| LibHandle, LookupSymbols, |
| [this, &JD, LS = std::move(LS), LookupSymbols](auto Result) mutable { |
| if (!Result) |
| return LS.continueLookup(Result.takeError()); |
| |
| // Keep the exported (non-null) results. |
| SymbolMap Resolved; |
| for (auto [Sym, Addr] : llvm::zip_equal(LookupSymbols, *Result)) |
| if (Addr && *Addr) |
| Resolved[Sym.first] = {*Addr, JITSymbolFlags::Exported | |
| JITSymbolFlags::Callable}; |
| |
| if (Resolved.empty()) |
| return LS.continueLookup(Error::success()); |
| |
| auto G = createStubsGraph(Resolved); |
| if (!G) |
| return LS.continueLookup(G.takeError()); |
| |
| // One tracker owns all stubs so they can be reclaimed together. |
| if (!ImportStubsRT || ImportStubsRT->isDefunct()) |
| ImportStubsRT = JD.createResourceTracker(); |
| LS.continueLookup(L.add(ImportStubsRT, std::move(*G))); |
| }); |
| |
| return Error::success(); |
| } |
| |
| // FIXME: Pull this into a helper shared with |
| // DLLImportDefinitionGenerator::createStubsGraph (ExecutionUtils.cpp), which |
| // builds the same __imp_X + thunk stubs. Until then, fixes here may need to |
| // be mirrored there too. |
| Expected<std::unique_ptr<jitlink::LinkGraph>> |
| COFFAutoImportGenerator::createStubsGraph(const SymbolMap &Resolved) { |
| Triple TT = ES.getTargetTriple(); |
| |
| auto G = std::make_unique<jitlink::LinkGraph>( |
| "<AUTOIMPORT_STUBS>", ES.getSymbolStringPool(), TT, SubtargetFeatures(), |
| jitlink::getGenericEdgeKindName); |
| jitlink::Section &Sec = |
| G->createSection(getSectionName(), MemProt::Read | MemProt::Exec); |
| |
| for (auto &KV : Resolved) { |
| // X's address as a local absolute symbol, referenced only by __imp_ (so it |
| // can't collide with the X thunk below). |
| jitlink::Symbol &Target = G->addAbsoluteSymbol( |
| *KV.first, KV.second.getAddress(), G->getPointerSize(), |
| jitlink::Linkage::Strong, jitlink::Scope::Local, false); |
| |
| // __imp_X: pointer slot holding X's address. |
| jitlink::Symbol &Ptr = CreatePointer(*G, Sec, &Target, 0); |
| Ptr.setName(G->intern((Twine(getImpPrefix()) + *KV.first).str())); |
| // Weak: a later real definition overrides this fallback (link.exe-style). |
| Ptr.setLinkage(jitlink::Linkage::Weak); |
| Ptr.setScope(jitlink::Scope::Default); |
| |
| // X: thunk "jmpq *__imp_X(%rip)" so direct calls work too. |
| jitlink::Symbol &Stub = CreateStub(*G, Sec, Ptr); |
| Stub.setName(G->intern(*KV.first)); |
| Stub.setLinkage(jitlink::Linkage::Weak); |
| Stub.setScope(jitlink::Scope::Default); |
| } |
| |
| return std::move(G); |
| } |
| |
| } // end namespace orc |
| } // end namespace llvm |