Index: lld/MachO/Config.h =================================================================== --- lld/MachO/Config.h +++ 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; Index: lld/MachO/Driver.cpp =================================================================== --- lld/MachO/Driver.cpp +++ 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; @@ -1131,6 +1150,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)); @@ -1237,7 +1257,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) Index: lld/MachO/Writer.cpp =================================================================== --- lld/MachO/Writer.cpp +++ 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()); Index: lld/test/MachO/platform-version.s =================================================================== --- lld/test/MachO/platform-version.s +++ 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 \ Index: lld/test/MachO/zippered.yaml =================================================================== --- lld/test/MachO/zippered.yaml +++ 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 Index: llvm/tools/llvm-objdump/MachODump.cpp =================================================================== --- llvm/tools/llvm-objdump/MachODump.cpp +++ llvm/tools/llvm-objdump/MachODump.cpp @@ -9163,14 +9163,20 @@ outs() << " size " << Nt.size << "\n"; } -static void PrintBuildToolVersion(MachO::build_tool_version bv) { - outs() << " tool " << MachOObjectFile::getBuildTool(bv.tool) << "\n"; +static void PrintBuildToolVersion(MachO::build_tool_version bv, bool verbose) { + outs() << " tool "; + if (verbose) + outs() << MachOObjectFile::getBuildTool(bv.tool); + else + outs() << bv.tool; + outs() << "\n"; outs() << " version " << MachOObjectFile::getVersionString(bv.version) << "\n"; } static void PrintBuildVersionLoadCommand(const MachOObjectFile *obj, - MachO::build_version_command bd) { + MachO::build_version_command bd, + bool verbose) { outs() << " cmd LC_BUILD_VERSION\n"; outs() << " cmdsize " << bd.cmdsize; if (bd.cmdsize != @@ -9179,8 +9185,12 @@ outs() << " Incorrect size\n"; else outs() << "\n"; - outs() << " platform " << MachOObjectFile::getBuildPlatform(bd.platform) - << "\n"; + outs() << " platform "; + if (verbose) + outs() << MachOObjectFile::getBuildPlatform(bd.platform); + else + outs() << bd.platform; + outs() << "\n"; if (bd.sdk) outs() << " sdk " << MachOObjectFile::getVersionString(bd.sdk) << "\n"; @@ -9191,7 +9201,7 @@ outs() << " ntools " << bd.ntools << "\n"; for (unsigned i = 0; i < bd.ntools; ++i) { MachO::build_tool_version bv = obj->getBuildToolVersion(i); - PrintBuildToolVersion(bv); + PrintBuildToolVersion(bv, verbose); } } @@ -10168,7 +10178,7 @@ } else if (Command.C.cmd == MachO::LC_BUILD_VERSION) { MachO::build_version_command Bv = Obj->getBuildVersionLoadCommand(Command); - PrintBuildVersionLoadCommand(Obj, Bv); + PrintBuildVersionLoadCommand(Obj, Bv, verbose); } else if (Command.C.cmd == MachO::LC_SOURCE_VERSION) { MachO::source_version_command Sd = Obj->getSourceVersionCommand(Command); PrintSourceVersionCommand(Sd);