Skip to content

Commit 253e116

Browse files
committed
[ORC][llvm-jitlink] Add support for emulating ld64 -weak-lx / -weak_library.
Linking libraries in ld64 with -weak-lx / -weak_library causes all references to symbols in those libraries to be made weak, allowing the librarie to be missing at runtime. This patch extends EPCDynamicLibrarySearchGenerator with support for emulating this behavior: If an instance is constructed with an Allow predicate but no dylib handle then all symbols matching the predicate are immediately resolved to null. The llvm-jitlink tool is updated with -weak-lx / -weak_library options for testing. Unlike their ld64 counterparts these options take a TBD file as input, and always resolve all exports in the TBD file to null.
1 parent 6880644 commit 253e116

File tree

9 files changed

+241
-6
lines changed

9 files changed

+241
-6
lines changed

llvm/include/llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@ class EPCDynamicLibrarySearchGenerator : public DefinitionGenerator {
4343
: EPC(ES.getExecutorProcessControl()), H(H), Allow(std::move(Allow)),
4444
AddAbsoluteSymbols(std::move(AddAbsoluteSymbols)) {}
4545

46+
/// Create an EPCDynamicLibrarySearchGenerator that resolves all symbols
47+
/// matching the Allow predicate to null. This can be used to emulate linker
48+
/// options like -weak-l / -weak_library where the library is missing at
49+
/// runtime. (Note: here we're explicitly returning null for these symbols,
50+
/// rather than returning no value at all for them, which is the usual
51+
/// "missing symbol" behavior in ORC. This distinction shouldn't matter for
52+
/// most use-cases).
53+
EPCDynamicLibrarySearchGenerator(
54+
ExecutionSession &ES, SymbolPredicate Allow,
55+
AddAbsoluteSymbolsFn AddAbsoluteSymbols = nullptr)
56+
: EPC(ES.getExecutorProcessControl()), Allow(std::move(Allow)),
57+
AddAbsoluteSymbols(std::move(AddAbsoluteSymbols)) {}
58+
4659
/// Permanently loads the library at the given path and, on success, returns
4760
/// an EPCDynamicLibrarySearchGenerator that will search it for symbol
4861
/// definitions in the library. On failure returns the reason the library
@@ -66,8 +79,10 @@ class EPCDynamicLibrarySearchGenerator : public DefinitionGenerator {
6679
const SymbolLookupSet &Symbols) override;
6780

6881
private:
82+
Error addAbsolutes(JITDylib &JD, SymbolMap Symbols);
83+
6984
ExecutorProcessControl &EPC;
70-
tpctypes::DylibHandle H;
85+
std::optional<tpctypes::DylibHandle> H;
7186
SymbolPredicate Allow;
7287
AddAbsoluteSymbolsFn AddAbsoluteSymbols;
7388
};
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===---- GetTapiInterface.h -- Get interface from TAPI file ----*- C++ -*-===//
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+
// Get symbol interface from TAPI file.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_EXECUTIONENGINE_ORC_GETTAPIINTERFACE_H
14+
#define LLVM_EXECUTIONENGINE_ORC_GETTAPIINTERFACE_H
15+
16+
#include "llvm/ExecutionEngine/Orc/Core.h"
17+
#include "llvm/Object/TapiUniversal.h"
18+
19+
namespace llvm::orc {
20+
21+
/// Returns a SymbolNameSet containing the exported symbols defined in the
22+
/// relevant slice of the TapiUniversal file.
23+
Expected<SymbolNameSet> getInterfaceFromTapiFile(ExecutionSession &ES,
24+
object::TapiUniversal &TU);
25+
26+
} // namespace llvm::orc
27+
28+
#endif // LLVM_EXECUTIONENGINE_ORC_GETTAPIINTERFACE_H

llvm/lib/ExecutionEngine/Orc/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ add_llvm_component_library(LLVMOrcJIT
2525
EPCIndirectionUtils.cpp
2626
ExecutionUtils.cpp
2727
ObjectFileInterface.cpp
28+
GetTapiInterface.cpp
2829
IndirectionUtils.cpp
2930
IRCompileLayer.cpp
3031
IRTransformLayer.cpp

llvm/lib/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ Error EPCDynamicLibrarySearchGenerator::tryToGenerate(
4242
<< Symbols << "\n";
4343
});
4444

45+
// If there's no handle then resolve all requested symbols to null.
46+
if (!H) {
47+
assert(Allow && "No handle or filter?");
48+
SymbolMap Nulls;
49+
for (auto &[Name, LookupFlags] : Symbols) {
50+
if (Allow(Name))
51+
Nulls[Name] = {};
52+
}
53+
return addAbsolutes(JD, std::move(Nulls));
54+
}
55+
56+
// Otherwise proceed with lookup in the remote.
4557
SymbolLookupSet LookupSymbols;
4658

4759
for (auto &KV : Symbols) {
@@ -51,7 +63,7 @@ Error EPCDynamicLibrarySearchGenerator::tryToGenerate(
5163
LookupSymbols.add(KV.first, SymbolLookupFlags::WeaklyReferencedSymbol);
5264
}
5365

54-
DylibManager::LookupRequest Request(H, LookupSymbols);
66+
DylibManager::LookupRequest Request(*H, LookupSymbols);
5567
// Copy-capture LookupSymbols, since LookupRequest keeps a reference.
5668
EPC.getDylibMgr().lookupSymbolsAsync(Request, [this, &JD, LS = std::move(LS),
5769
LookupSymbols](
@@ -85,15 +97,19 @@ Error EPCDynamicLibrarySearchGenerator::tryToGenerate(
8597
return LS.continueLookup(Error::success());
8698

8799
// Define resolved symbols.
88-
Error Err = AddAbsoluteSymbols
89-
? AddAbsoluteSymbols(JD, std::move(NewSymbols))
90-
: JD.define(absoluteSymbols(std::move(NewSymbols)));
100+
Error Err = addAbsolutes(JD, std::move(NewSymbols));
91101

92102
LS.continueLookup(std::move(Err));
93103
});
94104

95105
return Error::success();
96106
}
97107

108+
Error EPCDynamicLibrarySearchGenerator::addAbsolutes(JITDylib &JD,
109+
SymbolMap Symbols) {
110+
return AddAbsoluteSymbols ? AddAbsoluteSymbols(JD, std::move(Symbols))
111+
: JD.define(absoluteSymbols(std::move(Symbols)));
112+
}
113+
98114
} // end namespace orc
99115
} // end namespace llvm
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//===--------- GetTapiInterface.cpp - Get interface from TAPI file --------===//
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/GetTapiInterface.h"
10+
11+
#define DEBUG_TYPE "orc"
12+
13+
namespace llvm::orc {
14+
15+
Expected<SymbolNameSet> getInterfaceFromTapiFile(ExecutionSession &ES,
16+
object::TapiUniversal &TU) {
17+
SymbolNameSet Symbols;
18+
19+
auto CPUType = MachO::getCPUType(ES.getTargetTriple());
20+
if (!CPUType)
21+
return CPUType.takeError();
22+
23+
auto CPUSubType = MachO::getCPUSubType(ES.getTargetTriple());
24+
if (!CPUSubType)
25+
return CPUSubType.takeError();
26+
27+
auto &TUIF = TU.getInterfaceFile();
28+
auto ArchInterface =
29+
TUIF.extract(MachO::getArchitectureFromCpuType(*CPUType, *CPUSubType));
30+
if (!ArchInterface)
31+
return ArchInterface.takeError();
32+
33+
for (auto *Sym : (*ArchInterface)->exports())
34+
Symbols.insert(ES.intern(Sym->getName()));
35+
36+
return Symbols;
37+
}
38+
39+
} // namespace llvm::orc
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
--- !tapi-tbd
2+
tbd-version: 4
3+
targets: [ arm64-macos ]
4+
uuids:
5+
- target: arm64-macos
6+
value: 00000000-0000-0000-0000-000000000000
7+
flags: [ installapi ]
8+
install-name: Foo.framework/Foo
9+
current-version: 1.2.3
10+
compatibility-version: 1.2
11+
swift-abi-version: 5
12+
parent-umbrella:
13+
- targets: [ arm64-macos ]
14+
umbrella: System
15+
exports:
16+
- targets: [ arm64-macos ]
17+
symbols: [ _foo ]
18+
objc-classes: []
19+
objc-eh-types: []
20+
objc-ivars: []
21+
weak-symbols: []
22+
thread-local-symbols: []
23+
...
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
.section __TEXT,__text,regular,pure_instructions
2+
.globl _main
3+
.p2align 2
4+
_main:
5+
.cfi_startproc
6+
Lloh0:
7+
adrp x0, _foo@GOTPAGE
8+
Lloh1:
9+
ldr x0, [x0, _foo@GOTPAGEOFF]
10+
11+
ret
12+
.loh AdrpLdrGot Lloh0, Lloh1
13+
.cfi_endproc
14+
15+
.subsections_via_symbols
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# RUN: rm -rf %t && mkdir -p %t
2+
# RUN: llvm-mc -triple=arm64-apple-darwin19 -filetype=obj -o %t/main.o \
3+
# RUN: %S/Inputs/MachO_main_ret_foo.s
4+
# RUN: llvm-jitlink -noexec %t/main.o -weak_library %S/Inputs/MachO_Foo.tbd
5+
6+
# Check that we can load main.o, which unconditionally uses symbol foo, by
7+
# using -weak_library on a TBD file to emulate forced weak linking against
8+
# a library that supplies foo, but is missing at runtime.

llvm/tools/llvm-jitlink/llvm-jitlink.cpp

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h"
3030
#include "llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h"
3131
#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
32+
#include "llvm/ExecutionEngine/Orc/GetTapiInterface.h"
3233
#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
3334
#include "llvm/ExecutionEngine/Orc/JITLinkRedirectableSymbolManager.h"
3435
#include "llvm/ExecutionEngine/Orc/JITLinkReentryTrampolines.h"
@@ -58,6 +59,7 @@
5859
#include "llvm/Object/COFF.h"
5960
#include "llvm/Object/MachO.h"
6061
#include "llvm/Object/ObjectFile.h"
62+
#include "llvm/Object/TapiUniversal.h"
6163
#include "llvm/Support/CommandLine.h"
6264
#include "llvm/Support/Debug.h"
6365
#include "llvm/Support/InitLLVM.h"
@@ -140,6 +142,20 @@ static cl::list<std::string>
140142
cl::desc("Link against library X with hidden visibility"),
141143
cl::cat(JITLinkCategory));
142144

145+
static cl::list<std::string>
146+
LibrariesWeak("weak-l",
147+
cl::desc("Emulate weak link against library X. Must resolve "
148+
"to a TextAPI file, and all symbols in the "
149+
"interface will resolve to null."),
150+
cl::Prefix, cl::cat(JITLinkCategory));
151+
152+
static cl::list<std::string> WeakLibraries(
153+
"weak_library",
154+
cl::desc("Emulate weak link against library X. X must point to a "
155+
"TextAPI file, and all symbols in the interface will "
156+
"resolve to null"),
157+
cl::cat(JITLinkCategory));
158+
143159
static cl::opt<bool> SearchSystemLibrary(
144160
"search-sys-lib",
145161
cl::desc("Add system library paths to library search paths"),
@@ -2100,6 +2116,27 @@ static SmallVector<StringRef, 5> getSearchPathsFromEnvVar(Session &S) {
21002116
return PathVec;
21012117
}
21022118

2119+
static Expected<std::unique_ptr<DefinitionGenerator>>
2120+
LoadLibraryWeak(Session &S, StringRef InterfacePath) {
2121+
auto TapiFileBuffer = getFile(InterfacePath);
2122+
if (!TapiFileBuffer)
2123+
return TapiFileBuffer.takeError();
2124+
2125+
auto Tapi =
2126+
object::TapiUniversal::create((*TapiFileBuffer)->getMemBufferRef());
2127+
if (!Tapi)
2128+
return Tapi.takeError();
2129+
2130+
auto Symbols = getInterfaceFromTapiFile(S.ES, **Tapi);
2131+
if (!Symbols)
2132+
return Symbols.takeError();
2133+
2134+
return std::make_unique<EPCDynamicLibrarySearchGenerator>(
2135+
S.ES, [Symbols = std::move(*Symbols)](const SymbolStringPtr &Sym) {
2136+
return Symbols.count(Sym);
2137+
});
2138+
}
2139+
21032140
static Error addLibraries(Session &S,
21042141
const std::map<unsigned, JITDylib *> &IdxToJD,
21052142
const DenseSet<unsigned> &LazyLinkIdxs) {
@@ -2142,11 +2179,12 @@ static Error addLibraries(Session &S,
21422179
bool IsPath = false;
21432180
unsigned Position;
21442181
ArrayRef<StringRef> CandidateExtensions;
2145-
enum { Standard, Hidden } Modifier;
2182+
enum { Standard, Hidden, Weak } Modifier;
21462183
};
21472184

21482185
// Queue to load library as in the order as it appears in the argument list.
21492186
std::deque<LibraryLoad> LibraryLoadQueue;
2187+
21502188
// Add archive files from the inputs to LibraryLoads.
21512189
for (auto InputFileItr = InputFiles.begin(), InputFileEnd = InputFiles.end();
21522190
InputFileItr != InputFileEnd; ++InputFileItr) {
@@ -2173,9 +2211,23 @@ static Error addLibraries(Session &S,
21732211
LL.Modifier = LibraryLoad::Hidden;
21742212
LibraryLoadQueue.push_back(std::move(LL));
21752213
}
2214+
2215+
// Add -weak_library arguments to LibraryLoads.
2216+
for (auto LibItr = WeakLibraries.begin(), LibEnd = WeakLibraries.end();
2217+
LibItr != LibEnd; ++LibItr) {
2218+
LibraryLoad LL;
2219+
LL.LibName = *LibItr;
2220+
LL.IsPath = true;
2221+
LL.Position = WeakLibraries.getPosition(LibItr - WeakLibraries.begin());
2222+
LL.CandidateExtensions = {};
2223+
LL.Modifier = LibraryLoad::Weak;
2224+
LibraryLoadQueue.push_back(std::move(LL));
2225+
}
2226+
21762227
StringRef StandardExtensions[] = {".so", ".dylib", ".dll", ".a", ".lib"};
21772228
StringRef DynLibExtensionsOnly[] = {".so", ".dylib", ".dll"};
21782229
StringRef ArchiveExtensionsOnly[] = {".a", ".lib"};
2230+
StringRef InterfaceExtensionsOnly = {".tbd"};
21792231

21802232
// Add -lx arguments to LibraryLoads.
21812233
for (auto LibItr = Libraries.begin(), LibEnd = Libraries.end();
@@ -2201,6 +2253,18 @@ static Error addLibraries(Session &S,
22012253
LibraryLoadQueue.push_back(std::move(LL));
22022254
}
22032255

2256+
// Add -weak-lx arguments to LibraryLoads.
2257+
for (auto LibWeakItr = LibrariesWeak.begin(),
2258+
LibWeakEnd = LibrariesWeak.end();
2259+
LibWeakItr != LibWeakEnd; ++LibWeakItr) {
2260+
LibraryLoad LL;
2261+
LL.LibName = *LibWeakItr;
2262+
LL.Position = LibrariesWeak.getPosition(LibWeakItr - LibrariesWeak.begin());
2263+
LL.CandidateExtensions = InterfaceExtensionsOnly;
2264+
LL.Modifier = LibraryLoad::Weak;
2265+
LibraryLoadQueue.push_back(std::move(LL));
2266+
}
2267+
22042268
// Sort library loads by position in the argument list.
22052269
llvm::sort(LibraryLoadQueue,
22062270
[](const LibraryLoad &LHS, const LibraryLoad &RHS) {
@@ -2220,6 +2284,9 @@ static Error addLibraries(Session &S,
22202284
GetObjFileInterface = getObjectFileInterfaceHidden;
22212285
S.HiddenArchives.insert(Path);
22222286
break;
2287+
case LibraryLoad::Weak:
2288+
llvm_unreachable("Unsupported");
2289+
break;
22232290
}
22242291

22252292
auto &LinkLayer = S.getLinkLayer(LazyLinkIdxs.count(LL.Position));
@@ -2266,11 +2333,26 @@ static Error addLibraries(Session &S,
22662333

22672334
// If this is the name of a JITDylib then link against that.
22682335
if (auto *LJD = S.ES.getJITDylibByName(LL.LibName)) {
2336+
if (LL.Modifier == LibraryLoad::Weak)
2337+
return make_error<StringError>(
2338+
"Can't use -weak-lx or -weak_library to load JITDylib " +
2339+
LL.LibName,
2340+
inconvertibleErrorCode());
22692341
JD.addToLinkOrder(*LJD);
22702342
continue;
22712343
}
22722344

22732345
if (LL.IsPath) {
2346+
// Must be -weak_library.
2347+
if (LL.Modifier == LibraryLoad::Weak) {
2348+
if (auto G = LoadLibraryWeak(S, LL.LibName)) {
2349+
JD.addGenerator(std::move(*G));
2350+
continue;
2351+
} else
2352+
return G.takeError();
2353+
}
2354+
2355+
// Otherwise handle archive.
22742356
auto G = AddArchive(JD, LL.LibName.c_str(), LL);
22752357
if (!G)
22762358
return createFileError(LL.LibName, G.takeError());
@@ -2337,6 +2419,14 @@ static Error addLibraries(Session &S,
23372419
});
23382420
break;
23392421
}
2422+
case file_magic::tapi_file:
2423+
assert(LL.Modifier == LibraryLoad::Weak &&
2424+
"TextAPI file not being loaded as weak?");
2425+
if (auto G = LoadLibraryWeak(S, LibPath.data()))
2426+
JD.addGenerator(std::move(*G));
2427+
else
2428+
return G.takeError();
2429+
break;
23402430
default:
23412431
// This file isn't a recognized library kind.
23422432
LLVM_DEBUG({

0 commit comments

Comments
 (0)