Mach-O平台上的库版本控制

时间:2018-08-21 02:57:32

标签: macos cmake mach-o

SOVERSION目标属性的文档提到在Mach-O系统(例如OS X和iOS)上,它对应于“兼容版本”时,我正在查看一个小型个人项目的共享库的版本。 ,而VERSION对应于“当前版本”。

在Linux上,它类似于VERSION 5.4.2SOVERSION 5之类的内容,表明该库与5.0版及更高版本兼容,并且VERSION用作DLL映像版本,形式为在Windows上<major>.<minor>(我不确定在Windows上SOVERSION有什么区别)。

但是,the docs for the referenced FRAMEWORK target property中的示例说明了如何在Mach-O平台上拥有VERSION 16.4.0SOVERSION 1.0.0(我对构建框架不感兴趣,只是想知道版本控制方案。)

在Mach-O世界中版本控制如何工作?如果删除某些功能(如果会破坏兼容性),我习惯于碰到主要版本,那么16.4.0版的库如何与该库的1.0.0版保持兼容? “兼容”是什么意思?

1 个答案:

答案 0 :(得分:3)

首先,只是为了解决这个问题,框架只是名为Something.framework/Something而不是libsomething.dylib的dylib。但是,文件格式完全相同,因此在本篇文章中,我仅将它们称为dylib。

现在,让我们从mach-o/loader.h标头(Mach-O文件格式的实际权威来源)摘录开始:

/*
 * Dynamicly linked shared libraries are identified by two things.  The
 * pathname (the name of the library as found for execution), and the
 * compatibility version number.  The pathname must match and the compatibility
 * number in the user of the library must be greater than or equal to the
 * library being used.  The time stamp is used to record the time a library was
 * built and copied into user so it can be use to determined if the library used
 * at runtime is exactly the same as used to built the program.
 */
struct dylib {
    union lc_str  name;         /* library's path name */
    uint32_t timestamp;         /* library's build time stamp */
    uint32_t current_version;       /* library's current version number */
    uint32_t compatibility_version; /* library's compatibility vers number*/
};

/*
 * A dynamically linked shared library (filetype == MH_DYLIB in the mach header)
 * contains a dylib_command (cmd == LC_ID_DYLIB) to identify the library.
 * An object that uses a dynamically linked shared library also contains a
 * dylib_command (cmd == LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, or
 * LC_REEXPORT_DYLIB) for each library it uses.
 */
struct dylib_command {
    uint32_t    cmd;        /* LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB,
                       LC_REEXPORT_DYLIB */
    uint32_t    cmdsize;    /* includes pathname string */
    struct dylib    dylib;      /* the library identification */
};

如评论中所述,struct dylib既嵌入在库中,又嵌入到针对它的二进制链接中,都包含current_versioncompatibility_version的副本。后者的工作方式已在此处进行了说明,但前者未解决。
可以在dyld手册页上找到相关文档(来源为here,但在man之外很难看):

DYLD_VERSIONED_FRAMEWORK_PATH
    This  is a colon separated list of directories that contain potential override frame-
    works.  The dynamic linker searches  these  directories  for  frameworks.   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 is the supplied framework is newer.
    Note: dyld does not check the framework's Info.plist to find its version.  Dyld  only
    checks the -current_version number supplied when the framework was created.

[...]

DYLD_VERSIONED_LIBRARY_PATH
    This is a colon  separated  list  of  directories  that  contain  potential  override
    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.

简而言之:

  • compatibility_version用于确定库是否对于要加载的二进制文件来说“足够新”。
  • current_version用于在多个库可用时选择一个库。

关于您对使用当前版本的16.4.0和兼容版本的1.0.0感到困惑:从查看some sources来看,Apple似乎会在出现任何主要版本时撞到主要版本引入了新功能,并使用次要版本仅修复了AFAIK错误。
因此,他们称之为16.4.0,我可能会称之为1.16.4。 ;)