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
15namespace llvm {
16namespace orc {
17
18Expected<std::unique_ptr<COFFAutoImportGenerator>>
19COFFAutoImportGenerator::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
44Error 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.
97Expected<std::unique_ptr<jitlink::LinkGraph>>
98COFFAutoImportGenerator::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