diff --git a/lld/MachO/InputFiles.h b/lld/MachO/InputFiles.h --- a/lld/MachO/InputFiles.h +++ b/lld/MachO/InputFiles.h @@ -175,8 +175,20 @@ llvm::Optional readFile(StringRef path); -const llvm::MachO::load_command * -findCommand(const llvm::MachO::mach_header_64 *, uint32_t type); +template +const CommandType *findCommand(const llvm::MachO::mach_header_64 *hdr, + uint32_t type) { + const uint8_t *p = reinterpret_cast(hdr) + + sizeof(llvm::MachO::mach_header_64); + + for (uint32_t i = 0, n = hdr->ncmds; i < n; ++i) { + auto *cmd = reinterpret_cast(p); + if (cmd->cmd == type) + return cmd; + p += cmd->cmdsize; + } + return nullptr; +} } // namespace macho diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -149,20 +149,6 @@ return None; } -const load_command *macho::findCommand(const mach_header_64 *hdr, - uint32_t type) { - const uint8_t *p = - reinterpret_cast(hdr) + sizeof(mach_header_64); - - for (uint32_t i = 0, n = hdr->ncmds; i < n; ++i) { - auto *cmd = reinterpret_cast(p); - if (cmd->cmd == type) - return cmd; - p += cmd->cmdsize; - } - return nullptr; -} - void ObjFile::parseSections(ArrayRef sections) { subsections.reserve(sections.size()); auto *buf = reinterpret_cast(mb.getBufferStart()); @@ -352,6 +338,33 @@ /*isExternal=*/false, /*isPrivateExtern=*/false); } +// Checks if the version specified in `cmd` is compatible with target +// version in `config`. IOW, check if cmd's version >= config's version. +static bool hasCompatVersion(const InputFile *input, + const build_version_command *cmd, + const Configuration *config) { + + if (config->target.Platform != static_cast(cmd->platform)) { + error(toString(input) + " has platform " + + getPlatformName(static_cast(cmd->platform)) + + Twine(", which is different from target platform ") + + getPlatformName(config->target.Platform)); + return false; + } + + unsigned major = cmd->minos >> 16; + unsigned minor = (cmd->minos >> 8) & 0xffu; + unsigned subMinor = cmd->minos & 0xffu; + VersionTuple version(major, minor, subMinor); + if (version >= config->platformInfo.minimum) + return true; + + error(toString(input) + " has version " + version.getAsString() + + ", which is incompatible with target version of " + + config->platformInfo.minimum.getAsString()); + return false; +} + // Absolute symbols are defined symbols that do not have an associated // InputSection. They cannot be weak. static macho::Symbol *createAbsolute(const structs::nlist_64 &sym, @@ -496,7 +509,12 @@ getArchitectureName(config->target.Arch)); return; } - // TODO: check platform too + + if (const auto *cmd = + findCommand(hdr, LC_BUILD_VERSION)) { + if (!hasCompatVersion(this, cmd, config)) + return; + } if (const load_command *cmd = findCommand(hdr, LC_LINKER_OPTION)) { auto *c = reinterpret_cast(cmd); @@ -653,6 +671,12 @@ return; } + if (const build_version_command *cmd = + findCommand(hdr, LC_BUILD_VERSION)) { + if (!hasCompatVersion(this, cmd, config)) + return; + } + // Initialize symbols. DylibFile *exportingFile = isImplicitlyLinked(dylibName) ? this : umbrella; if (const load_command *cmd = findCommand(hdr, LC_DYLD_INFO_ONLY)) { diff --git a/lld/test/MachO/invalid/incompatible-arch.s b/lld/test/MachO/invalid/incompatible-arch.s --- a/lld/test/MachO/invalid/incompatible-arch.s +++ b/lld/test/MachO/invalid/incompatible-arch.s @@ -1,8 +1,31 @@ -# REQUIRES: aarch64 -# RUN: llvm-mc -filetype=obj -triple=arm64-apple-darwin %s -o %t.o -# RUN: not %lld -arch x86_64 -lSystem %t.o -o /dev/null 2>&1 | FileCheck %s -DFILE=%t.o +# REQUIRES: aarch64, x86 + +# RUN: rm -rf %t && mkdir -p %t + +# RUN: llvm-mc -filetype=obj -triple=arm64-apple-darwin %s -o %t/test.o +# RUN: not %lld -arch x86_64 -lSystem %t/test.o -o /dev/null 2>&1 | FileCheck %s -DFILE=%t/test.o # CHECK: error: {{.*}}[[FILE]] has architecture arm64 which is incompatible with target architecture x86_64 +# RUN: %lld -dylib -arch arm64 -platform_version macOS 9.0 11.0 -o %t/out.dylib %t/test.o + +# RUN: not %lld -dylib -arch arm64 -platform_version iOS 9.0 11.0 %t/out.dylib \ +# RUN: -o /dev/null 2>&1 | FileCheck %s --check-prefix=DYLIB-PLAT +# DYLIB-PLAT: {{.*}}out.dylib has platform macOS, which is different from target platform iOS + +# RUN: not %lld -dylib -arch arm64 -platform_version macOS 14.0 15.0 %t/out.dylib \ +# RUN: -o /dev/null 2>&1 | FileCheck %s --check-prefix=DYLIB-VERSION +# DYLIB-VERSION: {{.*}}out.dylib has version 9.0.0, which is incompatible with target version of 14.0 + +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos10.15.0 %s -o %t/test_x86.o + +# RUN: not %lld %t/test_x86.o -lSystem -arch x86_64 -platform_version iOS 10.0 15.0 \ +# RUN: -o /dev/null 2>&1 | FileCheck %s --check-prefix=OBJ-PLAT +# OBJ-PLAT: {{.*}}test_x86.o has platform macOS, which is different from target platform iOS + +# RUN: not %lld %t/test_x86.o -lSystem -arch x86_64 -platform_version macOS 14.0 15.0 \ +# RUN: -o /dev/null 2>&1 | FileCheck %s --check-prefix=OBJ-VERSION +# OBJ-VERSION: {{.*}}test_x86.o has version 10.15.0, which is incompatible with target version of 14.0 + .globl _main _main: ret