当前版本和兼容版本在 macOS 上的使用

Usage of current version and compatibility version on macOS

在 macOS 上,dylib 有一个 兼容版本 和一个 当前版本 作为元数据,每个都有 [=10= 的形式]. otool -L可以显示这些。

系统或各种tools/program如何以及何时使用这些版本?

对于兼容版本,我发现:

问题:

1。 当前版本是否在任何情况下被系统(例如动态linker)使用?

是的,当使用 DYLD_VERSIONED_LIBRARY_PATHDYLD_VERSIONED_FRAMEWORK_PATH 环境变量时。来自 man dyld:

DYLD_VERSIONED_LIBRARY_PATH
       This is a colon separated list of directories that contain potential over-
       ride libraries.  The dynamic linker searches these directories for dynamic
       libraries.  For each library found dyld looks at its LC_ID_DYLIB and  gets
       the  current_version and install name.  Dyld then looks for the library at
       the install name path.  Whichever has  the  larger  current_version  value
       will  be  used  in  the process whenever a dylib with that install name is
       required.  This is similar to DYLD_LIBRARY_PATH except instead  of  always
       overriding, it only overrides is the supplied library is newer.

DYLD_VERSIONED_FRAMEWORK_PATH
       This is a colon separated list of directories that contain potential over-
       ride frameworks.  The dynamic linker searches these directories for frame-
       works.  For each framework found dyld looks at its  LC_ID_DYLIB  and  gets
       the  current_version  and install name.  Dyld then looks for the framework
       at the install name path.  Whichever has the larger current_version  value
       will be used in the process whenever a framework with that install name is
       required.  This is similar to DYLD_FRAMEWORK_PATH except instead of always
       overriding,  it  only overrides if the supplied framework is newer.  Note:
       dyld does not check the framework's Info.plist to find its version.   Dyld
       only  checks  the -currrent_version number supplied when the framework was
       created.

这些变量仅支持 macOS 和 DriverKit 目标。

此外,库的 Mach-O 头文件中的 current_version 可以通过 NSVersionOfRunTimeLibrary() 查询,Mach-O 头文件中的 current_version link NSVersionOfLinkTimeLibrary().

的图书馆

2。在比较compatibility版本时,是否使用了x.y.z方案的所有部分?比较是字典序的吗?还是x,y,z分别有特殊含义?

所有部分都用到了,比较字典序
从技术上讲,x.y.z 部分形成一个 xxxxyyzz 形式的 32 位数字,即 16 位 x、8 位 y 和 z。

3。是否有关于这些版本号在何处使用的文档?请注意,我要求 where/when 它们 实际上 在实践中使用,而不仅仅是关于如何建议设置它们的指南。

man ld 中有一些文档:

-compatibility_version number
            Specifies the compatibility version number of the library.  When a
            library is loaded by dyld, the compatibility version is checked and if
            the program's version is greater that the library's version, it is an
            error.  The format of number is X[.Y[.Z]] where X must be a positive
            non-zero number less than or equal to 65535, and .Y and .Z are
            optional and if present must be non-negative numbers less than or
            equal to 255.  If the compatibility version number is not specified,
            it has a value of 0 and no checking is done when the library is used.
            This option is also called -dylib_compatibility_version for compati-
            bility.

但这只是事实的一半。对于真正发生的事情,我们必须看看dyld sources:

// check found library version is compatible
// <rdar://problem/89200806> 0xFFFFFFFF is wildcard that matches any version
if ( (requiredLibInfo.info.minVersion != 0xFFFFFFFF) && (actualInfo.minVersion < requiredLibInfo.info.minVersion)
        && ((dyld3::MachOFile*)(dependentLib->machHeader()))->enforceCompatVersion() ) {
    // record values for possible use by CrashReporter or Finder
    dyld::throwf("Incompatible library version: %s requires version %d.%d.%d or later, but %s provides version %d.%d.%d",
            this->getShortName(), requiredLibInfo.info.minVersion >> 16, (requiredLibInfo.info.minVersion >> 8) & 0xff, requiredLibInfo.info.minVersion & 0xff,
            dependentLib->getShortName(), actualInfo.minVersion >> 16, (actualInfo.minVersion >> 8) & 0xff, actualInfo.minVersion & 0xff);
}

除了 0xffffffff 可以用作通配符之外,这里有趣的一点是对 enforceCompatVersion():

的调用
bool MachOFile::enforceCompatVersion() const
{
    __block bool result = true;
    forEachSupportedPlatform(^(Platform platform, uint32_t minOS, uint32_t sdk) {
        switch ( platform ) {
            case Platform::macOS:
                if ( minOS >= 0x000A0E00 )  // macOS 10.14
                    result = false;
                break;
            case Platform::iOS:
            case Platform::tvOS:
            case Platform::iOS_simulator:
            case Platform::tvOS_simulator:
                if ( minOS >= 0x000C0000 )  // iOS 12.0
                    result = false;
                break;
            case Platform::watchOS:
            case Platform::watchOS_simulator:
                if ( minOS >= 0x00050000 )  // watchOS 5.0
                    result = false;
                break;
            case Platform::bridgeOS:
                if ( minOS >= 0x00030000 )  // bridgeOS 3.0
                    result = false;
                break;
            case Platform::driverKit:
            case Platform::iOSMac:
                result = false;
                break;
            case Platform::unknown:
                break;
        }
    });
    return result;
}

如您所见,如果库声明其支持的最低 OS 版本有点新,那么兼容版本将被 dyld 完全忽略。

因此,如果您完全依赖强制执行的兼容版本,则需要使用 --target=arm64-macos10.13 之类的选项来构建您的库。

4。系统的任何部分是否对元数据中的两种类型的版本与文件名或为库创建的 symlinks 名称有何关系有任何期望?

Dynamic linking 仅严格要求,如果您的二进制文件要求 /usr/lib/libz.dylib,则库也必须具有完全相同的设置作为其名称。如果该库具有 /usr/lib/libz.0.dylib 的嵌入式安装路径,那么它将被视为不同的库。

但是,在绝大多数情况下,您将依赖于在文件系统的安装路径中找到的库,这需要在 /usr/lib/libz.dylib 处有一个文件,其中一个是您的库重新寻找,或指向它的 symlink。但通常没有理由在这个阶段涉及 symlinks。

现在,您看到版本化文件编号的原因是 API 损坏。 compatibility_version 字段处理向前兼容性:如果你 link 反对版本 1.2.3,那么任何大于或等于 1.2.3 的版本都可以工作。但是,如果您曾经以破坏向后兼容性的方式删除或更改导出的 API,则必须使用新的安装名称创建一个新库,并继续向后运送旧库的最新版本的副本兼容性。
然后 Symlinks 仅用于 link 时间,指向库的最新版本。

示例:
假设您有一个 /usr/lib/libz.0.dylib,它有许多修复错误的更新,扩展了 API 并增加了 compatibility_version。所有这些都将作为 /usr/lib/libz.0.dylib 发布,并且该库的最新版本仍然可以使用 link 针对它的第一个版本的二进制文件。
现在您要删除一个导出的函数,因为这是一个重大更改,所以从这一点开始的任何版本都不能作为 /usr/lib/libz.0.dylib 发布。因此,您创建 /usr/lib/libz.1.dylib 并运送两个库,dyld 将加载主二进制文件构建所针对的任何库。
但是现在任何人 link 反对库都必须在命令行上传递 -lz.0lz.1,这不是特别好并且需要手动更新,如果你想要的话通常是不好的人们采用新版本。因此,您创建了一个从 /usr/lib/libz.dylib/usr/lib/libz.1.dylib 的符号 link,以便 -lz link 与最新版本的库相对应。