diff --git a/lld/MachO/Driver.h b/lld/MachO/Driver.h --- a/lld/MachO/Driver.h +++ b/lld/MachO/Driver.h @@ -49,7 +49,8 @@ llvm::Optional resolveDylibPath(llvm::StringRef path); DylibFile *loadDylib(llvm::MemoryBufferRef mbref, DylibFile *umbrella = nullptr, - bool isBundleLoader = false); + bool isBundleLoader = false, + bool explicitlyLinked = false); void resetLoadedDylibs(); // Search for all possible combinations of `{root}/{name}.{extension}`. diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -327,11 +327,9 @@ case file_magic::macho_dynamically_linked_shared_lib: case file_magic::macho_dynamically_linked_shared_lib_stub: case file_magic::tapi_file: - if (DylibFile *dylibFile = loadDylib(mbref)) { - if (isExplicit) - dylibFile->explicitlyLinked = true; + if (DylibFile *dylibFile = + loadDylib(mbref, nullptr, /*isBundleLoader=*/false, isExplicit)) newFile = dylibFile; - } break; case file_magic::bitcode: newFile = make(mbref, "", 0, isLazy); diff --git a/lld/MachO/DriverUtils.cpp b/lld/MachO/DriverUtils.cpp --- a/lld/MachO/DriverUtils.cpp +++ b/lld/MachO/DriverUtils.cpp @@ -204,11 +204,14 @@ static DenseMap loadedDylibs; DylibFile *macho::loadDylib(MemoryBufferRef mbref, DylibFile *umbrella, - bool isBundleLoader) { + bool isBundleLoader, bool explicitlyLinked) { CachedHashStringRef path(mbref.getBufferIdentifier()); DylibFile *&file = loadedDylibs[path]; - if (file) + if (file) { + if (explicitlyLinked) + file->explicitlyLinked = explicitlyLinked; return file; + } DylibFile *newFile; file_magic magic = identify_magic(mbref.getBuffer()); @@ -219,7 +222,8 @@ ": " + toString(result.takeError())); return nullptr; } - file = make(**result, umbrella, isBundleLoader); + file = + make(**result, umbrella, isBundleLoader, explicitlyLinked); // parseReexports() can recursively call loadDylib(). That's fine since // we wrote the DylibFile we just loaded to the loadDylib cache via the @@ -234,7 +238,7 @@ magic == file_magic::macho_dynamically_linked_shared_lib_stub || magic == file_magic::macho_executable || magic == file_magic::macho_bundle); - file = make(mbref, umbrella, isBundleLoader); + file = make(mbref, umbrella, isBundleLoader, explicitlyLinked); // parseLoadCommands() can also recursively call loadDylib(). See comment // in previous block for why this means we must copy `file` here. diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -184,10 +184,10 @@ // to the root. On the other hand, if a dylib is being directly loaded // (through an -lfoo flag), then `umbrella` should be a nullptr. explicit DylibFile(MemoryBufferRef mb, DylibFile *umbrella, - bool isBundleLoader = false); + bool isBundleLoader, bool explicitlyLinked); explicit DylibFile(const llvm::MachO::InterfaceFile &interface, - DylibFile *umbrella = nullptr, - bool isBundleLoader = false); + DylibFile *umbrella, bool isBundleLoader, + bool explicitlyLinked); void parseLoadCommands(MemoryBufferRef mb); void parseReexports(const llvm::MachO::InterfaceFile &interface); diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -1134,7 +1134,8 @@ make_pointee_range(currentTopLevelTapi->documents())) { assert(child.documents().empty()); if (path == child.getInstallName()) { - auto file = make(child, umbrella); + auto file = make(child, umbrella, /*isBundleLoader=*/false, + /*explicitlyLinked=*/false); file->parseReexports(child); return file; } @@ -1175,9 +1176,9 @@ } DylibFile::DylibFile(MemoryBufferRef mb, DylibFile *umbrella, - bool isBundleLoader) + bool isBundleLoader, bool explicitlyLinked) : InputFile(DylibKind, mb), refState(RefState::Unreferenced), - isBundleLoader(isBundleLoader) { + explicitlyLinked(explicitlyLinked), isBundleLoader(isBundleLoader) { assert(!isBundleLoader || !umbrella); if (umbrella == nullptr) umbrella = this; @@ -1286,7 +1287,7 @@ } } -// Some versions of XCode ship with .tbd files that don't have the right +// Some versions of Xcode ship with .tbd files that don't have the right // platform settings. constexpr std::array skipPlatformChecks{ "/usr/lib/system/libsystem_kernel.dylib", @@ -1294,10 +1295,19 @@ "/usr/lib/system/libsystem_pthread.dylib", "/usr/lib/system/libcompiler_rt.dylib"}; +static bool skipPlatformCheckForCatalyst(const InterfaceFile &interface, + bool explicitlyLinked) { + // Catalyst outputs can link against implicitly linked macOS-only libraries. + if (config->platform() != PLATFORM_MACCATALYST || explicitlyLinked) + return false; + return is_contained(interface.targets(), + MachO::Target(config->arch(), PLATFORM_MACOS)); +} + DylibFile::DylibFile(const InterfaceFile &interface, DylibFile *umbrella, - bool isBundleLoader) + bool isBundleLoader, bool explicitlyLinked) : InputFile(DylibKind, interface), refState(RefState::Unreferenced), - isBundleLoader(isBundleLoader) { + explicitlyLinked(explicitlyLinked), isBundleLoader(isBundleLoader) { // FIXME: Add test for the missing TBD code path. if (umbrella == nullptr) @@ -1313,7 +1323,8 @@ inputFiles.insert(this); if (!is_contained(skipPlatformChecks, installName) && - !is_contained(interface.targets(), config->platformInfo.target)) { + !is_contained(interface.targets(), config->platformInfo.target) && + !skipPlatformCheckForCatalyst(interface, explicitlyLinked)) { error(toString(this) + " is incompatible with " + std::string(config->platformInfo.target)); return; diff --git a/lld/test/MachO/Inputs/MacOSX.sdk/System/Library/Frameworks/MacOnly-Indirect.framework/MacOnly-Indirect.tbd b/lld/test/MachO/Inputs/MacOSX.sdk/System/Library/Frameworks/MacOnly-Indirect.framework/MacOnly-Indirect.tbd new file mode 100644 --- /dev/null +++ b/lld/test/MachO/Inputs/MacOSX.sdk/System/Library/Frameworks/MacOnly-Indirect.framework/MacOnly-Indirect.tbd @@ -0,0 +1,22 @@ +--- !tapi-tbd +tbd-version: 4 +targets: [ x86_64-macos, x86_64-maccatalyst ] +uuids: + - target: x86_64-maccatalyst + value: 00000000-0000-0000-0000-000000000000 + - target: x86_64-macos + value: 00000000-0000-0000-0000-000000000000 +install-name: 'MacOnly-Indirect.dylib' +current-version: 0001.001.1 +reexported-libraries: + - targets: [ x86_64-macos, x86_64-maccatalyst ] + libraries: [ 'MacOnly-reexport.dylib' ] +--- !tapi-tbd +tbd-version: 4 +targets: [ x86_64-macos ] +uuids: + - target: x86_64-macos + value: 00000000-0000-0000-0000-000000000000 +install-name: 'MacOnly-reexport.dylib' +current-version: 0001.001.1 +... diff --git a/lld/test/MachO/Inputs/MacOSX.sdk/System/Library/Frameworks/MacOnly.framework/MacOnly.tbd b/lld/test/MachO/Inputs/MacOSX.sdk/System/Library/Frameworks/MacOnly.framework/MacOnly.tbd new file mode 100644 --- /dev/null +++ b/lld/test/MachO/Inputs/MacOSX.sdk/System/Library/Frameworks/MacOnly.framework/MacOnly.tbd @@ -0,0 +1,9 @@ +--- !tapi-tbd +tbd-version: 4 +targets: [ x86_64-macos ] +uuids: + - target: x86_64-macos + value: 00000000-0000-0000-0000-000000000000 +install-name: 'MacOnly.dylib' +current-version: 0001.001.1 +... diff --git a/lld/test/MachO/zippered.yaml b/lld/test/MachO/zippered.yaml --- a/lld/test/MachO/zippered.yaml +++ b/lld/test/MachO/zippered.yaml @@ -8,6 +8,12 @@ # RUN: %lld -lSystem -dylib %t/test.dylib %t/test_macos.o -o /dev/null # RUN: %no-arg-lld -syslibroot %S/Inputs/MacOSX.sdk -lSystem -dylib -arch x86_64 -platform_version mac-catalyst 13.15.0 14.0 %t/test.dylib %t/test_maccatalyst.o -o /dev/null + +# RUN: %no-arg-lld -syslibroot %S/Inputs/MacOSX.sdk -lSystem -dylib -arch x86_64 -platform_version mac-catalyst 13.15.0 14.0 %t/test_maccatalyst.o -o /dev/null -framework MacOnly-Indirect + +# RUN: not %no-arg-lld -syslibroot %S/Inputs/MacOSX.sdk -lSystem -dylib -arch x86_64 -platform_version mac-catalyst 13.15.0 14.0 %t/test_maccatalyst.o -o /dev/null -framework MacOnly 2>&1 | FileCheck --check-prefix=INCOMPATIBLE %s +# INCOMPATIBLE: System/Library/Frameworks{{[\\/]}}MacOnly.framework{{[\\/]}}MacOnly.tbd(MacOnly.dylib) is incompatible with x86_64 (macCatalyst) + # RUN: not %no-arg-lld -syslibroot %S/Inputs/MacOSX.sdk -lSystem -dylib -arch x86_64 -platform_version ios 13.15.0 14.0 %t/test.dylib %t/test_ios.o -o /dev/null 2>&1 | FileCheck %s # CHECK: test.dylib has platform macOS/macCatalyst, which is different from target platform iOS