diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h --- a/lld/MachO/Config.h +++ b/lld/MachO/Config.h @@ -44,6 +44,8 @@ bool searchDylibsFirst = false; bool saveTemps = false; uint32_t headerPad; + uint32_t dylibCompatibilityVersion = 0; + uint32_t dylibCurrentVersion = 0; llvm::StringRef installName; llvm::StringRef outputFile; llvm::StringRef ltoObjPath; diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -663,6 +663,46 @@ error(msg + ": " + StringRef(err).trim()); } +static uint32_t parseDylibVersion(const opt::ArgList& args, unsigned id) { + const opt::Arg *arg = args.getLastArg(id); + if (!arg) + return 0; + + if (config->outputType != MH_DYLIB) { + error(arg->getAsString(args) + ": only valid with -dylib"); + return 0; + } + + llvm::VersionTuple version; + if (version.tryParse(arg->getValue()) || version.getBuild().hasValue()) { + error(arg->getAsString(args) + ": malformed version"); + return 0; + } + + unsigned major = version.getMajor(); + if (major > UINT16_MAX) { + error(arg->getAsString(args) + ": component " + Twine(major) + + " out of range"); + return 0; + } + + unsigned minor = version.getMinor().getValueOr(0); + if (minor > UINT8_MAX) { + error(arg->getAsString(args) + ": component " + Twine(minor) + + " out of range"); + return 0; + } + + unsigned subminor = version.getSubminor().getValueOr(0); + if (subminor > UINT8_MAX) { + error(arg->getAsString(args) + ": component " + Twine(subminor) + + " out of range"); + return 0; + } + + return (major << 16) | (minor << 8) | subminor; +} + bool macho::link(llvm::ArrayRef argsArr, bool canExitEarly, raw_ostream &stdoutOS, raw_ostream &stderrOS) { lld::stdoutOS = &stdoutOS; @@ -732,6 +772,10 @@ config->searchDylibsFirst = (arg && arg->getOption().getID() == OPT_search_dylibs_first); + config->dylibCompatibilityVersion = + parseDylibVersion(args, OPT_compatibility_version); + config->dylibCurrentVersion = parseDylibVersion(args, OPT_current_version); + config->saveTemps = args.hasArg(OPT_save_temps); if (args.hasArg(OPT_v)) { diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td --- a/lld/MachO/Options.td +++ b/lld/MachO/Options.td @@ -290,7 +290,6 @@ def compatibility_version : Separate<["-"], "compatibility_version">, MetaVarName<"">, HelpText<"Compatibility of this library">, - Flags<[HelpHidden]>, Group; def dylib_compatibility_version : Separate<["-"], "dylib_compatibility_version">, MetaVarName<"">, @@ -301,7 +300,6 @@ def current_version : Separate<["-"], "current_version">, MetaVarName<"">, HelpText<"Current of this library">, - Flags<[HelpHidden]>, Group; def dylib_current_version : Separate<["-"], "dylib_current_version">, MetaVarName<"">, diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -242,7 +242,10 @@ // * LC_REEXPORT_DYLIB class LCDylib : public LoadCommand { public: - LCDylib(LoadCommandType type, StringRef path) : type(type), path(path) { + LCDylib(LoadCommandType type, StringRef path, + uint32_t compatibilityVersion = 0, uint32_t currentVersion = 0) + : type(type), path(path), compatibilityVersion(compatibilityVersion), + currentVersion(currentVersion) { instanceCount++; } @@ -257,6 +260,9 @@ c->cmd = type; c->cmdsize = getSize(); c->dylib.name = sizeof(dylib_command); + c->dylib.timestamp = 0; + c->dylib.compatibility_version = compatibilityVersion; + c->dylib.current_version = currentVersion; memcpy(buf, path.data(), path.size()); buf[path.size()] = '\0'; @@ -267,6 +273,8 @@ private: LoadCommandType type; StringRef path; + uint32_t compatibilityVersion; + uint32_t currentVersion; static uint32_t instanceCount; }; @@ -431,7 +439,9 @@ in.header->addLoadCommand(make()); break; case MH_DYLIB: - in.header->addLoadCommand(make(LC_ID_DYLIB, config->installName)); + in.header->addLoadCommand(make(LC_ID_DYLIB, config->installName, + config->dylibCompatibilityVersion, + config->dylibCurrentVersion)); break; case MH_BUNDLE: break; diff --git a/lld/test/MachO/dylib-version.s b/lld/test/MachO/dylib-version.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/dylib-version.s @@ -0,0 +1,57 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %s -o %t.o + +# RUN: not %lld -o %t/executable %t.o -o /dev/null \ +# RUN: -compatibility_version 10 -current_version 11 2>&1 | \ +# RUN: FileCheck --check-prefix=NEEDDYLIB %s +# RUN: not %lld -execute -o %t/executable %t.o -o /dev/null \ +# RUN: -compatibility_version 10 -current_version 11 2>&1 | \ +# RUN: FileCheck --check-prefix=NEEDDYLIB %s +# RUN: not %lld -bundle -o %t/executable %t.o -o /dev/null \ +# RUN: -compatibility_version 10 -current_version 11 2>&1 | \ +# RUN: FileCheck --check-prefix=NEEDDYLIB %s + +# NEEDDYLIB: error: -compatibility_version 10: only valid with -dylib +# NEEDDYLIB: error: -current_version 11: only valid with -dylib + +# RUN: not %lld -dylib -o %t/executable %t.o -o /dev/null \ +# RUN: -compatibility_version 1.2.3.4 -current_version 1.2.3.4.5 2>&1 | \ +# RUN: FileCheck --check-prefix=MALFORMED %s + +# MALFORMED: error: -compatibility_version 1.2.3.4: malformed version +# MALFORMED: error: -current_version 1.2.3.4.5: malformed version + +# RUN: not %lld -dylib -o %t/executable %t.o -o /dev/null \ +# RUN: -compatibility_version 80000.1 -current_version 1.2.3 2>&1 | \ +# RUN: FileCheck --check-prefix=BADMAJOR %s + +# BADMAJOR: error: -compatibility_version 80000.1: component 80000 out of range + +# RUN: not %lld -dylib -o %t/executable %t.o -o /dev/null \ +# RUN: -compatibility_version 8.300 -current_version 1.2.3 2>&1 | \ +# RUN: FileCheck --check-prefix=BADMINOR %s + +# BADMINOR: error: -compatibility_version 8.300: component 300 out of range + +# RUN: not %lld -dylib -o %t/executable %t.o -o /dev/null \ +# RUN: -compatibility_version 8.8.300 -current_version 1.2.3 2>&1 | \ +# RUN: FileCheck --check-prefix=BADSUBMINOR %s + +# BADSUBMINOR: error: -compatibility_version 8.8.300: component 300 out of range + +# RUN: %lld -dylib -o %t/executable %t.o -o %t.dylib \ +# RUN: -compatibility_version 1.2.3 -current_version 2.5.6 +# RUN: llvm-objdump --macho --all-headers %t.dylib | FileCheck %s + +# CHECK: cmd LC_ID_DYLIB +# CHECK-NEXT: cmdsize +# CHECK-NEXT: name +# CHECK-NEXT: time stamp +# CHECK-NEXT: current version 2.5.6 +# CHECK-NEXT: compatibility version 1.2.3 + +.text +.global _foo +_foo: + mov $0, %rax + ret