diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h --- a/lld/MachO/Config.h +++ b/lld/MachO/Config.h @@ -151,6 +151,7 @@ bool deadStrip = false; bool errorForArchMismatch = false; PlatformInfo platformInfo; + llvm::Optional secondaryPlatformInfo; NamespaceKind namespaceKind = NamespaceKind::twolevel; UndefinedSymbolTreatment undefinedSymbolTreatment = UndefinedSymbolTreatment::error; diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -642,12 +642,27 @@ return PLATFORM_UNKNOWN; } if (platformVersions.size() == 2) { - // FIXME: If you implement support for this, add a diagnostic if - // outputType is not dylib or bundle -- linkers shouldn't be able to - // write zippered executables. - warn("writing zippered outputs not yet implemented, " - "ignoring all but last -platform_version flag"); + bool isZipperedCatalyst = platformVersions.count(PLATFORM_MACOS) && + platformVersions.count(PLATFORM_MACCATALYST); + + if (!isZipperedCatalyst) { + error("lld supports writing zippered outputs only for " + "macos and mac-catalyst"); + } else if (config->outputType != MH_DYLIB && + config->outputType != MH_BUNDLE) { + error("writing zippered outputs only valid for -dylib and -bundle"); + } else { + config->platformInfo.minimum = platformVersions[PLATFORM_MACOS].minimum; + config->platformInfo.sdk = platformVersions[PLATFORM_MACOS].sdk; + config->secondaryPlatformInfo = PlatformInfo{}; + config->secondaryPlatformInfo->minimum = + platformVersions[PLATFORM_MACCATALYST].minimum; + config->secondaryPlatformInfo->sdk = + platformVersions[PLATFORM_MACCATALYST].sdk; + } + return PLATFORM_MACOS; } + config->platformInfo.minimum = lastVersionInfo->minimum; config->platformInfo.sdk = lastVersionInfo->sdk; return lastVersionInfo->platform; @@ -664,6 +679,10 @@ PlatformType platform = parsePlatformVersions(args); config->platformInfo.target = MachO::Target(getArchitectureFromName(archName), platform); + if (config->secondaryPlatformInfo) { + config->secondaryPlatformInfo->target = + MachO::Target(getArchitectureFromName(archName), PLATFORM_MACCATALYST); + } uint32_t cpuType; uint32_t cpuSubtype; @@ -1128,6 +1147,7 @@ config = std::make_unique(); symtab = std::make_unique(); + config->outputType = getOutputType(args); target = createTargetInfo(args); depTracker = std::make_unique( args.getLastArgValue(OPT_dependency_info)); @@ -1234,7 +1254,6 @@ config->printEachFile = args.hasArg(OPT_t); config->printWhyLoad = args.hasArg(OPT_why_load); config->omitDebugInfo = args.hasArg(OPT_S); - config->outputType = getOutputType(args); config->errorForArchMismatch = args.hasArg(OPT_arch_errors_fatal); if (const Arg *arg = args.getLastArg(OPT_bundle_loader)) { if (config->outputType != MH_BUNDLE) diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -458,9 +458,11 @@ auto *c = reinterpret_cast(buf); c->cmd = LC_BUILD_VERSION; c->cmdsize = getSize(); + c->platform = static_cast(platformInfo.target.Platform); c->minos = encodeVersion(platformInfo.minimum); c->sdk = encodeVersion(platformInfo.sdk); + c->ntools = ntools; auto *t = reinterpret_cast(&c[1]); t->tool = TOOL_LD; @@ -768,6 +770,11 @@ else in.header->addLoadCommand(make(config->platformInfo)); + if (config->secondaryPlatformInfo) { + in.header->addLoadCommand( + make(*config->secondaryPlatformInfo)); + } + // This is down here to match ld64's load command order. if (config->outputType == MH_EXECUTE) in.header->addLoadCommand(make()); diff --git a/lld/test/MachO/platform-version.s b/lld/test/MachO/platform-version.s --- a/lld/test/MachO/platform-version.s +++ b/lld/test/MachO/platform-version.s @@ -61,10 +61,10 @@ # FAIL-PLATFORM-NOT: malformed {{minimum|sdk}} version: {{.*}} # RUN: not %no-arg-lld -arch x86_64 -o %t %t.o -fatal_warnings 2>&1 \ -# RUN: -platform_version 'mac catalyst' 14.0 15.0 \ +# RUN: -platform_version iOS 14.0 15.0 \ # RUN: -platform_version macos 12.0 12.0 \ # RUN: | FileCheck --check-prefix=FAIL-TODO %s -# FAIL-TODO: writing zippered outputs not yet implemented, ignoring all but last -platform_version flag +# FAIL-TODO: lld supports writing zippered outputs only for macos and mac-catalyst # RUN: not %no-arg-lld -arch x86_64 -o %t %t.o 2>&1 \ # RUN: -platform_version bridgeOS 1 5 \ 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 @@ -5,10 +5,10 @@ # RUN: echo "" | llvm-mc -filetype=obj -triple=x86_64-apple-ios13.15.0-macabi -o %t/test_maccatalyst.o # RUN: echo "" | llvm-mc -filetype=obj -triple=x86_64-apple-ios13.15.0 -o %t/test_ios.o +## Test linking against a zippered dylib. # 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 @@ -17,6 +17,50 @@ # 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 +## Test creating a zippered dylib. +# RUN: %no-arg-lld -syslibroot %S/Inputs/MacOSX.sdk -lSystem -dylib -arch x86_64 -platform_version macos 12.0 13.0 -platform_version mac-catalyst 13.15.0 14.0 %t/test.dylib %t/test_macos.o -o %t/test_zippered.dylib +# RUN: llvm-otool -l %t/test_zippered.dylib | FileCheck --check-prefix=ZIPPERED %s +# ZIPPERED: cmd LC_BUILD_VERSION +# ZIPPERED-NEXT: cmdsize +# ZIPPERED-NEXT: platform 1 +# ZIPPERED-NEXT: sdk 13.0 +# ZIPPERED-NEXT: minos 12.0 +# ZIPPERED-NEXT: ntools 1 +# ZIPPERED-NEXT: tool +# ZIPPERED-NEXT: version +# ZIPPERED-NEXT: Load command +# ZIPPERED-NEXT: cmd LC_BUILD_VERSION +# ZIPPERED-NEXT: cmdsize +# ZIPPERED-NEXT: platform 6 +# ZIPPERED-NEXT: sdk 14.0 +# ZIPPERED-NEXT: minos 13.15 +# ZIPPERED-NEXT: ntools 1 +# ZIPPERED-NEXT: tool +# ZIPPERED-NEXT: version + +# RUN: %no-arg-lld -syslibroot %S/Inputs/MacOSX.sdk -lSystem -dylib -arch x86_64 -platform_version macos 10.8 13.0 -platform_version mac-catalyst 13.15.0 14.0 %t/test.dylib %t/test_macos.o -o %t/test_zippered.dylib +# RUN: llvm-otool -l %t/test_zippered.dylib | FileCheck --check-prefix=ZIPPERED-OLD %s +# ZIPPERED-OLD: cmd LC_VERSION_MIN_MACOSX +# ZIPPERED-OLD-NEXT: cmdsize +# ZIPPERED-OLD-NEXT: version 10.8 +# ZIPPERED-OLD-NEXT: sdk 13.0 +# ZIPPERED-OLD-NEXT: Load command +# ZIPPERED-OLD-NEXT: cmd LC_BUILD_VERSION +# ZIPPERED-OLD-NEXT: cmdsize +# ZIPPERED-OLD-NEXT: platform 6 +# ZIPPERED-OLD-NEXT: sdk 14.0 +# ZIPPERED-OLD-NEXT: minos 13.15 +# ZIPPERED-OLD-NEXT: ntools 1 +# ZIPPERED-OLD-NEXT: tool +# ZIPPERED-OLD-NEXT: version + +# RUN: not %no-arg-lld -syslibroot %S/Inputs/MacOSX.sdk -lSystem -arch x86_64 -platform_version macos 10.8 13.0 -platform_version mac-catalyst 13.15.0 14.0 %t/test.dylib %t/test_macos.o -o %t/test_zippered.dylib 2>&1 | FileCheck --check-prefix=ZIPPERED-EXE %s +# ZIPPERED-EXE: writing zippered outputs only valid for -dylib and -bundle + +## Reject .o files which only have the secondary platform. +# RUN: not %no-arg-lld -syslibroot %S/Inputs/MacOSX.sdk -lSystem -dylib -arch x86_64 -platform_version macos 12.0 13.0 -platform_version mac-catalyst 13.15.0 14.0 %t/test.dylib %t/test_maccatalyst.o -o %t/test_zippered.dylib 2>&1 | FileCheck --check-prefix=CAT %s +# CAT: test_maccatalyst.o has platform macCatalyst, which is different from target platform macOS + --- !mach-o FileHeader: magic: 0xFEEDFACF