[llvm] [ORC] Add AutoImportGenerator for COFF dllimport auto-import (PR #203914)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jul 6 08:58:56 PDT 2026
================
@@ -583,5 +583,134 @@ DLLImportDefinitionGenerator::createStubsGraph(const SymbolMap &Resolved) {
return std::move(G);
}
+Expected<std::unique_ptr<AutoImportGenerator>>
+AutoImportGenerator::Load(ExecutionSession &ES, ObjectLinkingLayer &L,
+ DylibManager &DylibMgr, const char *LibraryPath) {
+ // x86_64-only for now: createStubsGraph emits x86_64 pointer slots / stubs.
+ if (ES.getTargetTriple().getArch() != Triple::x86_64)
+ return make_error<StringError>(
+ "AutoImportGenerator currently only supports x86_64",
+ inconvertibleErrorCode());
+
+ // Load the library in the executor. Symbols are later resolved via
+ // DylibMgr.lookupSymbols, which only returns exported symbols -- so the
+ // library's export table is the authority on what may be synthesized.
+ auto LibHandle = DylibMgr.loadDylib(LibraryPath);
+ if (!LibHandle)
+ return LibHandle.takeError();
+
+ return std::unique_ptr<AutoImportGenerator>(
+ new AutoImportGenerator(ES, L, DylibMgr, *LibHandle));
+}
+
+Error AutoImportGenerator::tryToGenerate(LookupState &LS, LookupKind K,
+ JITDylib &JD,
+ JITDylibLookupFlags JDLookupFlags,
+ const SymbolLookupSet &Symbols) {
+ if (Symbols.empty())
+ return Error::success();
+
+ // The library's export table is the authority on what may be synthesized:
+ // look each requested symbol up in the executor (with any __imp_ prefix
+ // stripped) as a weak reference, so names the library does not export come
+ // back null and are simply left unresolved -- the link then fails as a static
+ // link would. De-duplicate so __imp_X and X collapse to a single 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());
+
+ assert(Result->size() == LookupSymbols.size() &&
+ "Result has incorrect number of elements");
+
+ // Keep only the symbols the library actually exports (non-null
+ // addresses); anything else stays unresolved.
+ SymbolMap Resolved;
+ auto ResultIt = Result->begin();
+ for (auto &[Name, Flags] : LookupSymbols) {
+ const auto &Addr = *ResultIt++;
+ if (Addr && *Addr)
+ Resolved[Name] = {*Addr, JITSymbolFlags::Exported |
+ JITSymbolFlags::Callable};
+ }
+
+ if (Resolved.empty())
+ return LS.continueLookup(Error::success());
+
+ auto G = createStubsGraph(Resolved);
+ if (!G)
+ return LS.continueLookup(G.takeError());
+
+ // Add the synthesized stubs under a generator-owned ResourceTracker
+ // (created lazily, recreated if the client has reclaimed it) so that
+ // all synthesized __imp_ slots and thunks can be released in one step
+ // via getImportStubsResourceTracker()->remove(), without tearing down
+ // JD.
+ if (!ImportStubsRT || ImportStubsRT->isDefunct())
+ ImportStubsRT = JD.createResourceTracker();
+ LS.continueLookup(L.add(ImportStubsRT, std::move(*G)));
+ });
+
+ return Error::success();
+}
+
+Expected<std::unique_ptr<jitlink::LinkGraph>>
+AutoImportGenerator::createStubsGraph(const SymbolMap &Resolved) {
+ Triple TT = ES.getTargetTriple();
+
+ auto CreatePointer = jitlink::getAnonymousPointerCreator(TT);
+ if (!CreatePointer)
+ return make_error<StringError>(
+ "AutoImportGenerator: no pointer creator for " + TT.str(),
+ inconvertibleErrorCode());
+
+ auto CreateStub = jitlink::getPointerJumpStubCreator(TT);
+ if (!CreateStub)
+ return make_error<StringError>("AutoImportGenerator: no stub creator for " +
+ TT.str(),
+ inconvertibleErrorCode());
+
+ 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) {
+ // The real implementation in the library, frozen as an absolute symbol.
+ // It is referenced only by the __imp_ slot's pointer edge, so it is local
+ // and never collides with the same-named thunk below.
+ jitlink::Symbol &Target = G->addAbsoluteSymbol(
+ *KV.first, KV.second.getAddress(), G->getPointerSize(),
+ jitlink::Linkage::Strong, jitlink::Scope::Local, false);
+
+ // __imp_X: an 8-byte pointer slot holding X's address in the library.
+ jitlink::Symbol &Ptr = CreatePointer(*G, Sec, &Target, 0);
+ Ptr.setName(G->intern((Twine(getImpPrefix()) + *KV.first).str()));
+ Ptr.setLinkage(jitlink::Linkage::Strong);
+ Ptr.setScope(jitlink::Scope::Default);
----------------
mkovacevic99 wrote:
Agreed — switched both __imp_X and the X thunk to Linkage::Weak, matching link.exe treating an import as an overridable fallback (warn/override rather than a hard duplicate error) if a real definition coexists. Note ORC only calls tryToGenerate for unresolved symbols, so we never synthesize over an existing definition in the first place — weak just makes the "real definition added afterward" case resolve in favor of the real one.
https://github.com/llvm/llvm-project/pull/203914
More information about the llvm-commits
mailing list