diff --git a/llvm/include/llvm/ExecutionEngine/Orc/MachO.h b/llvm/include/llvm/ExecutionEngine/Orc/MachO.h index 049595c330f5c..0e789b5e05a75 100644 --- a/llvm/include/llvm/ExecutionEngine/Orc/MachO.h +++ b/llvm/include/llvm/ExecutionEngine/Orc/MachO.h @@ -95,21 +95,36 @@ class ForceLoadMachOArchiveMembers { bool ObjCOnly; }; +using GetFallbackArchsFn = + unique_function>( + uint32_t CPUType, uint32_t CPUSubType)>; + +/// Match the exact CPU type/subtype only. +LLVM_ABI SmallVector> +noFallbackArchs(uint32_t CPUType, uint32_t CPUSubType); + +/// Match standard dynamic loader fallback rules. +LLVM_ABI SmallVector> +standardMachOFallbackArchs(uint32_t CPUType, uint32_t CPUSubType); + /// Returns a SymbolNameSet containing the exported symbols defined in the /// given dylib. -LLVM_ABI Expected -getDylibInterfaceFromDylib(ExecutionSession &ES, Twine Path); +LLVM_ABI Expected getDylibInterfaceFromDylib( + ExecutionSession &ES, Twine Path, + GetFallbackArchsFn GetFallbackArchs = standardMachOFallbackArchs); /// Returns a SymbolNameSet containing the exported symbols defined in the /// relevant slice of the TapiUniversal file. -LLVM_ABI Expected -getDylibInterfaceFromTapiFile(ExecutionSession &ES, Twine Path); +LLVM_ABI Expected getDylibInterfaceFromTapiFile( + ExecutionSession &ES, Twine Path, + GetFallbackArchsFn GetFallbackArchs = standardMachOFallbackArchs); /// Returns a SymbolNameSet containing the exported symbols defined in the /// relevant slice of the given file, which may be either a dylib or a tapi /// file. -LLVM_ABI Expected getDylibInterface(ExecutionSession &ES, - Twine Path); +LLVM_ABI Expected getDylibInterface( + ExecutionSession &ES, Twine Path, + GetFallbackArchsFn GetFallbackArchs = standardMachOFallbackArchs); } // namespace orc } // namespace llvm diff --git a/llvm/lib/ExecutionEngine/Orc/MachO.cpp b/llvm/lib/ExecutionEngine/Orc/MachO.cpp index 14d1c843bf6e4..731d24d1272d4 100644 --- a/llvm/lib/ExecutionEngine/Orc/MachO.cpp +++ b/llvm/lib/ExecutionEngine/Orc/MachO.cpp @@ -282,15 +282,48 @@ Expected ForceLoadMachOArchiveMembers::operator()( return true; } -Expected getDylibInterfaceFromDylib(ExecutionSession &ES, - Twine Path) { - auto CPUType = MachO::getCPUType(ES.getTargetTriple()); - if (!CPUType) - return CPUType.takeError(); +LLVM_ABI SmallVector> +noFallbackArchs(uint32_t CPUType, uint32_t CPUSubType) { + SmallVector> Result; + Result.push_back({CPUType, CPUSubType}); + return Result; +} + +SmallVector> +standardMachOFallbackArchs(uint32_t CPUType, uint32_t CPUSubType) { + SmallVector> Archs; + + // Match given CPU type/subtype first. + Archs.push_back({CPUType, CPUSubType}); + + switch (CPUType) { + case MachO::CPU_TYPE_ARM64: + // Handle arm64 variants. + switch (CPUSubType) { + case MachO::CPU_SUBTYPE_ARM64_ALL: + Archs.push_back({CPUType, MachO::CPU_SUBTYPE_ARM64E}); + break; + default: + break; + } + break; + default: + break; + } + + return Archs; +} + +Expected +getDylibInterfaceFromDylib(ExecutionSession &ES, Twine Path, + GetFallbackArchsFn GetFallbackArchs) { + auto InitCPUType = MachO::getCPUType(ES.getTargetTriple()); + if (!InitCPUType) + return InitCPUType.takeError(); - auto CPUSubType = MachO::getCPUSubType(ES.getTargetTriple()); - if (!CPUSubType) - return CPUSubType.takeError(); + auto InitCPUSubType = MachO::getCPUSubType(ES.getTargetTriple()); + if (!InitCPUSubType) + return InitCPUSubType.takeError(); auto Buf = MemoryBuffer::getFile(Path); if (!Buf) @@ -301,25 +334,38 @@ Expected getDylibInterfaceFromDylib(ExecutionSession &ES, return BinFile.takeError(); std::unique_ptr MachOFile; - if (isa(**BinFile)) + if (isa(**BinFile)) { MachOFile.reset(dyn_cast(BinFile->release())); - else if (auto *MachOUni = - dyn_cast(BinFile->get())) { - for (auto &O : MachOUni->objects()) { - if (O.getCPUType() == *CPUType && - (O.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) == *CPUSubType) { - if (auto Obj = O.getAsObjectFile()) - MachOFile = std::move(*Obj); - else - return Obj.takeError(); - break; + + // TODO: Check that dylib arch is compatible. + } else if (auto *MachOUni = + dyn_cast(BinFile->get())) { + SmallVector> ArchsToTry; + if (GetFallbackArchs) + ArchsToTry = GetFallbackArchs(*InitCPUType, *InitCPUSubType); + else + ArchsToTry.push_back({*InitCPUType, *InitCPUSubType}); + + for (auto &[CPUType, CPUSubType] : ArchsToTry) { + for (auto &O : MachOUni->objects()) { + if (O.getCPUType() == CPUType && + (O.getCPUSubType() & ~MachO::CPU_SUBTYPE_MASK) == CPUSubType) { + if (auto Obj = O.getAsObjectFile()) + MachOFile = std::move(*Obj); + else + return Obj.takeError(); + break; + } } + if (MachOFile) // If found, break out. + break; } if (!MachOFile) - return make_error("MachO universal binary at " + Path + - " does not contain a slice for " + - ES.getTargetTriple().str(), - inconvertibleErrorCode()); + return make_error( + "MachO universal binary at " + Path + + " does not contain a compatible slice for " + + ES.getTargetTriple().str(), + inconvertibleErrorCode()); } else return make_error("File at " + Path + " is not a MachO", inconvertibleErrorCode()); @@ -339,8 +385,9 @@ Expected getDylibInterfaceFromDylib(ExecutionSession &ES, return std::move(Symbols); } -Expected getDylibInterfaceFromTapiFile(ExecutionSession &ES, - Twine Path) { +Expected +getDylibInterfaceFromTapiFile(ExecutionSession &ES, Twine Path, + GetFallbackArchsFn GetFallbackArchs) { SymbolNameSet Symbols; auto TapiFileBuffer = MemoryBuffer::getFile(Path); @@ -352,27 +399,44 @@ Expected getDylibInterfaceFromTapiFile(ExecutionSession &ES, if (!Tapi) return Tapi.takeError(); - auto CPUType = MachO::getCPUType(ES.getTargetTriple()); - if (!CPUType) - return CPUType.takeError(); + auto InitCPUType = MachO::getCPUType(ES.getTargetTriple()); + if (!InitCPUType) + return InitCPUType.takeError(); - auto CPUSubType = MachO::getCPUSubType(ES.getTargetTriple()); - if (!CPUSubType) - return CPUSubType.takeError(); + auto InitCPUSubType = MachO::getCPUSubType(ES.getTargetTriple()); + if (!InitCPUSubType) + return InitCPUSubType.takeError(); + + SmallVector> ArchsToTry; + if (GetFallbackArchs) + ArchsToTry = GetFallbackArchs(*InitCPUType, *InitCPUSubType); + else + ArchsToTry.push_back({*InitCPUType, *InitCPUSubType}); auto &IF = (*Tapi)->getInterfaceFile(); - auto Interface = - IF.extract(MachO::getArchitectureFromCpuType(*CPUType, *CPUSubType)); - if (!Interface) - return Interface.takeError(); - for (auto *Sym : (*Interface)->exports()) - Symbols.insert(ES.intern(Sym->getName())); + auto ArchSet = IF.getArchitectures(); + for (auto [CPUType, CPUSubType] : ArchsToTry) { + auto A = MachO::getArchitectureFromCpuType(CPUType, CPUSubType); + if (ArchSet.has(A)) { + if (auto Interface = IF.extract(A)) { + for (auto *Sym : (*Interface)->exports()) + Symbols.insert(ES.intern(Sym->getName())); + return Symbols; + } else + return Interface.takeError(); + } + } - return Symbols; + return make_error( + "MachO interface file at " + Path + + " does not contain a compatible slice for " + + ES.getTargetTriple().str(), + inconvertibleErrorCode()); } -Expected getDylibInterface(ExecutionSession &ES, Twine Path) { +Expected getDylibInterface(ExecutionSession &ES, Twine Path, + GetFallbackArchsFn GetFallbackArchs) { file_magic Magic; if (auto EC = identify_magic(Path, Magic)) return createFileError(Path, EC); @@ -380,9 +444,9 @@ Expected getDylibInterface(ExecutionSession &ES, Twine Path) { switch (Magic) { case file_magic::macho_universal_binary: case file_magic::macho_dynamically_linked_shared_lib: - return getDylibInterfaceFromDylib(ES, Path); + return getDylibInterfaceFromDylib(ES, Path, std::move(GetFallbackArchs)); case file_magic::tapi_file: - return getDylibInterfaceFromTapiFile(ES, Path); + return getDylibInterfaceFromTapiFile(ES, Path, std::move(GetFallbackArchs)); default: return make_error("Cannot get interface for " + Path + " unrecognized file type", diff --git a/llvm/test/ExecutionEngine/JITLink/AArch64/Inputs/MachO_Foo.tbd b/llvm/test/ExecutionEngine/JITLink/AArch64/Inputs/MachO_Foo_arm64.tbd similarity index 100% rename from llvm/test/ExecutionEngine/JITLink/AArch64/Inputs/MachO_Foo.tbd rename to llvm/test/ExecutionEngine/JITLink/AArch64/Inputs/MachO_Foo_arm64.tbd diff --git a/llvm/test/ExecutionEngine/JITLink/AArch64/Inputs/MachO_Foo_arm64e.tbd b/llvm/test/ExecutionEngine/JITLink/AArch64/Inputs/MachO_Foo_arm64e.tbd new file mode 100644 index 0000000000000..7b21ab0cff165 --- /dev/null +++ b/llvm/test/ExecutionEngine/JITLink/AArch64/Inputs/MachO_Foo_arm64e.tbd @@ -0,0 +1,23 @@ +--- !tapi-tbd +tbd-version: 4 +targets: [ arm64e-macos ] +uuids: + - target: arm64e-macos + value: 00000000-0000-0000-0000-000000000000 +flags: [ installapi ] +install-name: Foo.framework/Foo +current-version: 1.2.3 +compatibility-version: 1.2 +swift-abi-version: 5 +parent-umbrella: + - targets: [ arm64e-macos ] + umbrella: System +exports: + - targets: [ arm64e-macos ] + symbols: [ _foo ] + objc-classes: [] + objc-eh-types: [] + objc-ivars: [] + weak-symbols: [] + thread-local-symbols: [] +... diff --git a/llvm/test/ExecutionEngine/JITLink/AArch64/MachO_weak_link.test b/llvm/test/ExecutionEngine/JITLink/AArch64/MachO_weak_link.test index 4326a604297b6..37847918bfe88 100644 --- a/llvm/test/ExecutionEngine/JITLink/AArch64/MachO_weak_link.test +++ b/llvm/test/ExecutionEngine/JITLink/AArch64/MachO_weak_link.test @@ -1,8 +1,14 @@ # RUN: rm -rf %t && mkdir -p %t # RUN: llvm-mc -triple=arm64-apple-darwin19 -filetype=obj -o %t/main.o \ # RUN: %S/Inputs/MachO_main_ret_foo.s -# RUN: llvm-jitlink -noexec %t/main.o -weak_library %S/Inputs/MachO_Foo.tbd - +# RUN: llvm-jitlink -noexec %t/main.o -weak_library \ +# RUN: %S/Inputs/MachO_Foo_arm64.tbd +# RUN: llvm-jitlink -noexec %t/main.o -weak_library \ +# RUN: %S/Inputs/MachO_Foo_arm64e.tbd +# # Check that we can load main.o, which unconditionally uses symbol foo, by # using -weak_library on a TBD file to emulate forced weak linking against # a library that supplies foo, but is missing at runtime. +# +# Check that weak linking works for arm64 JIT'd programs even if the TBD +# file contains only an arm64e interface.