Skip to content

Commit 492c265

Browse files
lhamesgithub-actions[bot]
authored andcommitted
Automerge: [ORC] Support scanning "fallback" slices for interfaces. (#168472)
When scanning an interface source (dylib or TBD file), consider "fallback" architectures (CPUType / CPUSubType pairs) in addition to the process's CPUType / CPUSubType. Background: When dyld loads a dylib into a process it may load dylib or slice whose CPU type / subtype isn't an exact match for the process's CPU type / subtype. E.g. arm64 processes can load arm64e dylibs / slices. When building an interface we need to follow the same logic, otherwise we risk generating a spurious "does not contain a compatible slice" error. E.g. If we're running an arm64 JIT'd program and loading an interface from a TBD file, and if no arm64 slice is present in that file, then we should fall back to looking for an arm64e slice. rdar://164510783
2 parents 360183d + 4b0d422 commit 492c265

File tree

5 files changed

+157
-49
lines changed

5 files changed

+157
-49
lines changed

llvm/include/llvm/ExecutionEngine/Orc/MachO.h

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,21 +95,36 @@ class ForceLoadMachOArchiveMembers {
9595
bool ObjCOnly;
9696
};
9797

98+
using GetFallbackArchsFn =
99+
unique_function<SmallVector<std::pair<uint32_t, uint32_t>>(
100+
uint32_t CPUType, uint32_t CPUSubType)>;
101+
102+
/// Match the exact CPU type/subtype only.
103+
LLVM_ABI SmallVector<std::pair<uint32_t, uint32_t>>
104+
noFallbackArchs(uint32_t CPUType, uint32_t CPUSubType);
105+
106+
/// Match standard dynamic loader fallback rules.
107+
LLVM_ABI SmallVector<std::pair<uint32_t, uint32_t>>
108+
standardMachOFallbackArchs(uint32_t CPUType, uint32_t CPUSubType);
109+
98110
/// Returns a SymbolNameSet containing the exported symbols defined in the
99111
/// given dylib.
100-
LLVM_ABI Expected<SymbolNameSet>
101-
getDylibInterfaceFromDylib(ExecutionSession &ES, Twine Path);
112+
LLVM_ABI Expected<SymbolNameSet> getDylibInterfaceFromDylib(
113+
ExecutionSession &ES, Twine Path,
114+
GetFallbackArchsFn GetFallbackArchs = standardMachOFallbackArchs);
102115

103116
/// Returns a SymbolNameSet containing the exported symbols defined in the
104117
/// relevant slice of the TapiUniversal file.
105-
LLVM_ABI Expected<SymbolNameSet>
106-
getDylibInterfaceFromTapiFile(ExecutionSession &ES, Twine Path);
118+
LLVM_ABI Expected<SymbolNameSet> getDylibInterfaceFromTapiFile(
119+
ExecutionSession &ES, Twine Path,
120+
GetFallbackArchsFn GetFallbackArchs = standardMachOFallbackArchs);
107121

108122
/// Returns a SymbolNameSet containing the exported symbols defined in the
109123
/// relevant slice of the given file, which may be either a dylib or a tapi
110124
/// file.
111-
LLVM_ABI Expected<SymbolNameSet> getDylibInterface(ExecutionSession &ES,
112-
Twine Path);
125+
LLVM_ABI Expected<SymbolNameSet> getDylibInterface(
126+
ExecutionSession &ES, Twine Path,
127+
GetFallbackArchsFn GetFallbackArchs = standardMachOFallbackArchs);
113128

114129
} // namespace orc
115130
} // namespace llvm

llvm/lib/ExecutionEngine/Orc/MachO.cpp

Lines changed: 105 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -282,15 +282,48 @@ Expected<bool> ForceLoadMachOArchiveMembers::operator()(
282282
return true;
283283
}
284284

285-
Expected<SymbolNameSet> getDylibInterfaceFromDylib(ExecutionSession &ES,
286-
Twine Path) {
287-
auto CPUType = MachO::getCPUType(ES.getTargetTriple());
288-
if (!CPUType)
289-
return CPUType.takeError();
285+
LLVM_ABI SmallVector<std::pair<uint32_t, uint32_t>>
286+
noFallbackArchs(uint32_t CPUType, uint32_t CPUSubType) {
287+
SmallVector<std::pair<uint32_t, uint32_t>> Result;
288+
Result.push_back({CPUType, CPUSubType});
289+
return Result;
290+
}
291+
292+
SmallVector<std::pair<uint32_t, uint32_t>>
293+
standardMachOFallbackArchs(uint32_t CPUType, uint32_t CPUSubType) {
294+
SmallVector<std::pair<uint32_t, uint32_t>> Archs;
295+
296+
// Match given CPU type/subtype first.
297+
Archs.push_back({CPUType, CPUSubType});
298+
299+
switch (CPUType) {
300+
case MachO::CPU_TYPE_ARM64:
301+
// Handle arm64 variants.
302+
switch (CPUSubType) {
303+
case MachO::CPU_SUBTYPE_ARM64_ALL:
304+
Archs.push_back({CPUType, MachO::CPU_SUBTYPE_ARM64E});
305+
break;
306+
default:
307+
break;
308+
}
309+
break;
310+
default:
311+
break;
312+
}
313+
314+
return Archs;
315+
}
316+
317+
Expected<SymbolNameSet>
318+
getDylibInterfaceFromDylib(ExecutionSession &ES, Twine Path,
319+
GetFallbackArchsFn GetFallbackArchs) {
320+
auto InitCPUType = MachO::getCPUType(ES.getTargetTriple());
321+
if (!InitCPUType)
322+
return InitCPUType.takeError();
290323

291-
auto CPUSubType = MachO::getCPUSubType(ES.getTargetTriple());
292-
if (!CPUSubType)
293-
return CPUSubType.takeError();
324+
auto InitCPUSubType = MachO::getCPUSubType(ES.getTargetTriple());
325+
if (!InitCPUSubType)
326+
return InitCPUSubType.takeError();
294327

295328
auto Buf = MemoryBuffer::getFile(Path);
296329
if (!Buf)
@@ -301,25 +334,38 @@ Expected<SymbolNameSet> getDylibInterfaceFromDylib(ExecutionSession &ES,
301334
return BinFile.takeError();
302335

303336
std::unique_ptr<object::MachOObjectFile> MachOFile;
304-
if (isa<object::MachOObjectFile>(**BinFile))
337+
if (isa<object::MachOObjectFile>(**BinFile)) {
305338
MachOFile.reset(dyn_cast<object::MachOObjectFile>(BinFile->release()));
306-
else if (auto *MachOUni =
307-
dyn_cast<object::MachOUniversalBinary>(BinFile->get())) {
308-
for (auto &O : MachOUni->objects()) {
309-
if (O.getCPUType() == *CPUType &&
310-
(O.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) == *CPUSubType) {
311-
if (auto Obj = O.getAsObjectFile())
312-
MachOFile = std::move(*Obj);
313-
else
314-
return Obj.takeError();
315-
break;
339+
340+
// TODO: Check that dylib arch is compatible.
341+
} else if (auto *MachOUni =
342+
dyn_cast<object::MachOUniversalBinary>(BinFile->get())) {
343+
SmallVector<std::pair<uint32_t, uint32_t>> ArchsToTry;
344+
if (GetFallbackArchs)
345+
ArchsToTry = GetFallbackArchs(*InitCPUType, *InitCPUSubType);
346+
else
347+
ArchsToTry.push_back({*InitCPUType, *InitCPUSubType});
348+
349+
for (auto &[CPUType, CPUSubType] : ArchsToTry) {
350+
for (auto &O : MachOUni->objects()) {
351+
if (O.getCPUType() == CPUType &&
352+
(O.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) == CPUSubType) {
353+
if (auto Obj = O.getAsObjectFile())
354+
MachOFile = std::move(*Obj);
355+
else
356+
return Obj.takeError();
357+
break;
358+
}
316359
}
360+
if (MachOFile) // If found, break out.
361+
break;
317362
}
318363
if (!MachOFile)
319-
return make_error<StringError>("MachO universal binary at " + Path +
320-
" does not contain a slice for " +
321-
ES.getTargetTriple().str(),
322-
inconvertibleErrorCode());
364+
return make_error<StringError>(
365+
"MachO universal binary at " + Path +
366+
" does not contain a compatible slice for " +
367+
ES.getTargetTriple().str(),
368+
inconvertibleErrorCode());
323369
} else
324370
return make_error<StringError>("File at " + Path + " is not a MachO",
325371
inconvertibleErrorCode());
@@ -339,8 +385,9 @@ Expected<SymbolNameSet> getDylibInterfaceFromDylib(ExecutionSession &ES,
339385
return std::move(Symbols);
340386
}
341387

342-
Expected<SymbolNameSet> getDylibInterfaceFromTapiFile(ExecutionSession &ES,
343-
Twine Path) {
388+
Expected<SymbolNameSet>
389+
getDylibInterfaceFromTapiFile(ExecutionSession &ES, Twine Path,
390+
GetFallbackArchsFn GetFallbackArchs) {
344391
SymbolNameSet Symbols;
345392

346393
auto TapiFileBuffer = MemoryBuffer::getFile(Path);
@@ -352,37 +399,54 @@ Expected<SymbolNameSet> getDylibInterfaceFromTapiFile(ExecutionSession &ES,
352399
if (!Tapi)
353400
return Tapi.takeError();
354401

355-
auto CPUType = MachO::getCPUType(ES.getTargetTriple());
356-
if (!CPUType)
357-
return CPUType.takeError();
402+
auto InitCPUType = MachO::getCPUType(ES.getTargetTriple());
403+
if (!InitCPUType)
404+
return InitCPUType.takeError();
358405

359-
auto CPUSubType = MachO::getCPUSubType(ES.getTargetTriple());
360-
if (!CPUSubType)
361-
return CPUSubType.takeError();
406+
auto InitCPUSubType = MachO::getCPUSubType(ES.getTargetTriple());
407+
if (!InitCPUSubType)
408+
return InitCPUSubType.takeError();
409+
410+
SmallVector<std::pair<uint32_t, uint32_t>> ArchsToTry;
411+
if (GetFallbackArchs)
412+
ArchsToTry = GetFallbackArchs(*InitCPUType, *InitCPUSubType);
413+
else
414+
ArchsToTry.push_back({*InitCPUType, *InitCPUSubType});
362415

363416
auto &IF = (*Tapi)->getInterfaceFile();
364-
auto Interface =
365-
IF.extract(MachO::getArchitectureFromCpuType(*CPUType, *CPUSubType));
366-
if (!Interface)
367-
return Interface.takeError();
368417

369-
for (auto *Sym : (*Interface)->exports())
370-
Symbols.insert(ES.intern(Sym->getName()));
418+
auto ArchSet = IF.getArchitectures();
419+
for (auto [CPUType, CPUSubType] : ArchsToTry) {
420+
auto A = MachO::getArchitectureFromCpuType(CPUType, CPUSubType);
421+
if (ArchSet.has(A)) {
422+
if (auto Interface = IF.extract(A)) {
423+
for (auto *Sym : (*Interface)->exports())
424+
Symbols.insert(ES.intern(Sym->getName()));
425+
return Symbols;
426+
} else
427+
return Interface.takeError();
428+
}
429+
}
371430

372-
return Symbols;
431+
return make_error<StringError>(
432+
"MachO interface file at " + Path +
433+
" does not contain a compatible slice for " +
434+
ES.getTargetTriple().str(),
435+
inconvertibleErrorCode());
373436
}
374437

375-
Expected<SymbolNameSet> getDylibInterface(ExecutionSession &ES, Twine Path) {
438+
Expected<SymbolNameSet> getDylibInterface(ExecutionSession &ES, Twine Path,
439+
GetFallbackArchsFn GetFallbackArchs) {
376440
file_magic Magic;
377441
if (auto EC = identify_magic(Path, Magic))
378442
return createFileError(Path, EC);
379443

380444
switch (Magic) {
381445
case file_magic::macho_universal_binary:
382446
case file_magic::macho_dynamically_linked_shared_lib:
383-
return getDylibInterfaceFromDylib(ES, Path);
447+
return getDylibInterfaceFromDylib(ES, Path, std::move(GetFallbackArchs));
384448
case file_magic::tapi_file:
385-
return getDylibInterfaceFromTapiFile(ES, Path);
449+
return getDylibInterfaceFromTapiFile(ES, Path, std::move(GetFallbackArchs));
386450
default:
387451
return make_error<StringError>("Cannot get interface for " + Path +
388452
" unrecognized file type",
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: [ arm64e-macos ]
4+
uuids:
5+
- target: arm64e-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: [ arm64e-macos ]
14+
umbrella: System
15+
exports:
16+
- targets: [ arm64e-macos ]
17+
symbols: [ _foo ]
18+
objc-classes: []
19+
objc-eh-types: []
20+
objc-ivars: []
21+
weak-symbols: []
22+
thread-local-symbols: []
23+
...
Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
# RUN: rm -rf %t && mkdir -p %t
22
# RUN: llvm-mc -triple=arm64-apple-darwin19 -filetype=obj -o %t/main.o \
33
# RUN: %S/Inputs/MachO_main_ret_foo.s
4-
# RUN: llvm-jitlink -noexec %t/main.o -weak_library %S/Inputs/MachO_Foo.tbd
5-
4+
# RUN: llvm-jitlink -noexec %t/main.o -weak_library \
5+
# RUN: %S/Inputs/MachO_Foo_arm64.tbd
6+
# RUN: llvm-jitlink -noexec %t/main.o -weak_library \
7+
# RUN: %S/Inputs/MachO_Foo_arm64e.tbd
8+
#
69
# Check that we can load main.o, which unconditionally uses symbol foo, by
710
# using -weak_library on a TBD file to emulate forced weak linking against
811
# a library that supplies foo, but is missing at runtime.
12+
#
13+
# Check that weak linking works for arm64 JIT'd programs even if the TBD
14+
# file contains only an arm64e interface.

0 commit comments

Comments
 (0)