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

Sza*_*lcs 4 macos version shared-libraries dylib dynamic-linking

在 macOS 上,dylib 有一个兼容版本和一个当前版本作为元数据,每个版本都有x.y.z.. otool -L可以显示这些。

系统或各种工具/程序如何以及何时使用这些版本?

对于兼容性版本,我发现:

  • 当将程序链接到库时,该库的兼容版本将被记录到程序中。动态链接器将拒绝链接到兼容版本小于记录版本的库。(参考,尽管这是针对框架的)。

问题:

  • 系统(例如动态链接器)在任何情况下都使用当前版本吗?
  • 在比较兼容版本时,是否使用了该方案的所有部分x.y.z?比较是字典顺序吗?还是x、y、z分别有特殊含义?
  • 是否有有关使用这些版本号的文档?请注意,我询问的是它们在实践中实际使用的地点/时间,而不仅仅是建议如何设置它们的指南。
  • 系统的任何部分是否对元数据中的两种类型的版本应如何与文件名或为库创建的符号链接名称相关有任何期望?例如,ls -l /usr/lib/libz*我看到同一个库有许多不同的符号链接,每个符号链接的名称都有不同的版本。

Sig*_*uza 7

1.系统(例如动态链接器)在任何情况下都使用当前版本吗?

是的,当使用DYLD_VERSIONED_LIBRARY_PATH或DYLD_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.
Run Code Online (Sandbox Code Playgroud)

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

此外,库的 Mach-O 标头中的 current_version 可以通过 查询NSVersionOfRunTimeLibrary(),并且 Mach-O 标头中的 current_version 通过 与库链接NSVersionOfLinkTimeLibrary()。

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

所有部分均已使用,并且比较是按字典顺序进行的。
从技术上讲,xyz 部分形成 xxxxyyzz 形式的 32 位数字,即各 16 位 x、8 位 y 和 z。

3. 是否有文档说明这些版本号的用途?请注意,我询问的是它们在实践中实际使用的地点/时间,而不仅仅是建议如何设置它们的指南。

有一些文档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.
Run Code Online (Sandbox Code Playgroud)

但这只是事实的一半。对于真正发生的事情,我们必须查看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.
Run Code Online (Sandbox Code Playgroud)

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

-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.
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,如果库声明其支持的最低操作系统版本有点新,则 dyld 会完全忽略兼容性版本。

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

4. 系统的任何部分是否对元数据中的两种类型的版本应如何与文件名或为库创建的符号链接名称相关有任何期望?

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

但是,在绝大多数情况下,您将依赖于在文件系统的安装路径上找到的库,并且这要求存在一个文件,该文件要么/usr/lib/libz.dylib是您正在查找的库,要么是指向它的符号链接。但在这个阶段通常没有理由涉及符号链接。

现在,您看到版本化文件编号的原因是 API 损坏。该compatibility_version字段处理向前兼容性:如果您链接到 version 1.2.3,则任何大于或等于的版本都1.2.3将起作用。但是,如果您以破坏向后兼容性的方式删除或更改导出的 API,则必须使用新的安装名称创建一个新库,并继续发布旧库的最后版本的副本以实现向后兼容性。
然后,符号链接仅用于链接时,以指向库的最新版本。

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