| 1 | //===- COFFAutoImportGenerator.cpp - COFF dllimport auto-import ---------===// |
| 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/COFFAutoImportGenerator.h" |
| 10 | #include "llvm/ADT/STLExtras.h" |
| 11 | #include "llvm/ADT/Twine.h" |
| 12 | #include "llvm/ExecutionEngine/JITLink/JITLink.h" |
| 13 | #include "llvm/ExecutionEngine/Orc/Shared/ExecutorSymbolDef.h" |
| 14 | |
| 15 | namespace llvm { |
| 16 | namespace orc { |
| 17 | |
| 18 | Expected<std::unique_ptr<COFFAutoImportGenerator>> |
| 19 | COFFAutoImportGenerator::Load(ExecutionSession &ES, ObjectLinkingLayer &L, |
| 20 | DylibManager &DylibMgr, const char *LibraryPath) { |
| 21 | Triple TT = ES.getTargetTriple(); |
| 22 | |
| 23 | auto CreatePointer = jitlink::getAnonymousPointerCreator(TT); |
| 24 | if (!CreatePointer) |
| 25 | return make_error<StringError>( |
| 26 | Args: "COFFAutoImportGenerator: no pointer creator for " + TT.str(), |
| 27 | Args: inconvertibleErrorCode()); |
| 28 | |
| 29 | auto CreateStub = jitlink::getPointerJumpStubCreator(TT); |
| 30 | if (!CreateStub) |
| 31 | return make_error<StringError>( |
| 32 | Args: "COFFAutoImportGenerator: no stub creator for " + TT.str(), |
| 33 | Args: inconvertibleErrorCode()); |
| 34 | |
| 35 | auto LibHandle = DylibMgr.loadDylib(DylibPath: LibraryPath); |
| 36 | if (!LibHandle) |
| 37 | return LibHandle.takeError(); |
| 38 | |
| 39 | return std::unique_ptr<COFFAutoImportGenerator>(new COFFAutoImportGenerator( |
| 40 | ES, L, DylibMgr, *LibHandle, std::move(CreatePointer), |
| 41 | std::move(CreateStub))); |
| 42 | } |
| 43 | |
| 44 | Error COFFAutoImportGenerator::tryToGenerate(LookupState &LS, LookupKind K, |
| 45 | JITDylib &JD, |
| 46 | JITDylibLookupFlags JDLookupFlags, |
| 47 | const SymbolLookupSet &Symbols) { |
| 48 | if (Symbols.empty()) |
| 49 | return Error::success(); |
| 50 | |
| 51 | // Weakly reference each symbol (minus any __imp_ prefix) so unexported names |
| 52 | // are left unresolved; de-dup __imp_X and X into one lookup. |
| 53 | SymbolLookupSet LookupSymbols; |
| 54 | DenseSet<SymbolStringPtr> Seen; |
| 55 | for (auto &KV : Symbols) { |
| 56 | StringRef Base = *KV.first; |
| 57 | if (Base.starts_with(Prefix: getImpPrefix())) |
| 58 | Base = Base.drop_front(N: getImpPrefix().size()); |
| 59 | SymbolStringPtr BaseName = ES.intern(SymName: Base); |
| 60 | if (Seen.insert(V: BaseName).second) |
| 61 | LookupSymbols.add(Name: BaseName, Flags: SymbolLookupFlags::WeaklyReferencedSymbol); |
| 62 | } |
| 63 | |
| 64 | DylibMgr.lookupSymbolsAsync( |
| 65 | H: LibHandle, Symbols: LookupSymbols, |
| 66 | F: [this, &JD, LS = std::move(LS), LookupSymbols](auto Result) mutable { |
| 67 | if (!Result) |
| 68 | return LS.continueLookup(Err: Result.takeError()); |
| 69 | |
| 70 | // Keep the exported (non-null) results. |
| 71 | SymbolMap Resolved; |
| 72 | for (auto [Sym, Addr] : llvm::zip_equal(LookupSymbols, *Result)) |
| 73 | if (Addr && *Addr) |
| 74 | Resolved[Sym.first] = {*Addr, JITSymbolFlags::Exported | |
| 75 | JITSymbolFlags::Callable}; |
| 76 | |
| 77 | if (Resolved.empty()) |
| 78 | return LS.continueLookup(Err: Error::success()); |
| 79 | |
| 80 | auto G = createStubsGraph(Resolved); |
| 81 | if (!G) |
| 82 | return LS.continueLookup(Err: G.takeError()); |
| 83 | |
| 84 | // One tracker owns all stubs so they can be reclaimed together. |
| 85 | if (!ImportStubsRT || ImportStubsRT->isDefunct()) |
| 86 | ImportStubsRT = JD.createResourceTracker(); |
| 87 | LS.continueLookup(Err: L.add(RT: ImportStubsRT, G: std::move(*G))); |
| 88 | }); |
| 89 | |
| 90 | return Error::success(); |
| 91 | } |
| 92 | |
| 93 | // FIXME: Pull this into a helper shared with |
| 94 | // DLLImportDefinitionGenerator::createStubsGraph (ExecutionUtils.cpp), which |
| 95 | // builds the same __imp_X + thunk stubs. Until then, fixes here may need to |
| 96 | // be mirrored there too. |
| 97 | Expected<std::unique_ptr<jitlink::LinkGraph>> |
| 98 | COFFAutoImportGenerator::createStubsGraph(const SymbolMap &Resolved) { |
| 99 | Triple TT = ES.getTargetTriple(); |
| 100 | |
| 101 | auto G = std::make_unique<jitlink::LinkGraph>( |
| 102 | args: "<AUTOIMPORT_STUBS>" , args: ES.getSymbolStringPool(), args&: TT, args: SubtargetFeatures(), |
| 103 | args&: jitlink::getGenericEdgeKindName); |
| 104 | jitlink::Section &Sec = |
| 105 | G->createSection(Name: getSectionName(), Prot: MemProt::Read | MemProt::Exec); |
| 106 | |
| 107 | for (auto &KV : Resolved) { |
| 108 | // X's address as a local absolute symbol, referenced only by __imp_ (so it |
| 109 | // can't collide with the X thunk below). |
| 110 | jitlink::Symbol &Target = G->addAbsoluteSymbol( |
| 111 | Name: *KV.first, Address: KV.second.getAddress(), Size: G->getPointerSize(), |
| 112 | L: jitlink::Linkage::Strong, S: jitlink::Scope::Local, IsLive: false); |
| 113 | |
| 114 | // __imp_X: pointer slot holding X's address. |
| 115 | jitlink::Symbol &Ptr = CreatePointer(*G, Sec, &Target, 0); |
| 116 | Ptr.setName(G->intern(SymbolName: (Twine(getImpPrefix()) + *KV.first).str())); |
| 117 | // Weak: a later real definition overrides this fallback (link.exe-style). |
| 118 | Ptr.setLinkage(jitlink::Linkage::Weak); |
| 119 | Ptr.setScope(jitlink::Scope::Default); |
| 120 | |
| 121 | // X: thunk "jmpq *__imp_X(%rip)" so direct calls work too. |
| 122 | jitlink::Symbol &Stub = CreateStub(*G, Sec, Ptr); |
| 123 | Stub.setName(G->intern(SymbolName: *KV.first)); |
| 124 | Stub.setLinkage(jitlink::Linkage::Weak); |
| 125 | Stub.setScope(jitlink::Scope::Default); |
| 126 | } |
| 127 | |
| 128 | return std::move(G); |
| 129 | } |
| 130 | |
| 131 | } // end namespace orc |
| 132 | } // end namespace llvm |
| 133 | |