【问题标题】:What is the 'soname' option for building shared libraries for?构建共享库的“soname”选项是什么?
【发布时间】:2012-09-20 05:03:17
【问题描述】:

我学会了“Program Library HOWTO”。它提到使用soname 来管理版本,如下所示。

gcc -shared -fPIC -Wl,-soname,libfoo.so.1  -o libfoo.so.1.0.0 foo.c
ln -s libfoo.so.1.0.0  libfoo.so.1
ln -s libfoo.so.1 libfoo.so

如果没有设置soname,我会得到信息。它将等于 libfoo.so.1.0.0 ,请参阅here 的答案。

而且我发现它也可以在没有 soname 的情况下工作,比如关注

 gcc -shared -fPIC -o libfoo.so.1.0.0 foo.c
 ln -s libfoo.so.1.0.0  libfoo.so.1
 ln -s libfoo.so.1 libfoo.so

所以我认为唯一有用的一点是soname 选项可以在您使用readelf -d libfoo.so 命令检查共享库时告诉您共享库的版本。

它还能做什么?

【问题讨论】:

    标签: linux gcc shared-libraries


    【解决方案1】:

    soname 用于指示您的库支持的二进制 api 兼容性。

    SONAME 被链接器在编译时用于从库文件中确定实际的目标库版本。 gcc -lNAME 将寻找 libNAME.so 链接或文件,然后捕获其 SONAME 肯定会更具体(前 libnuke.so 链接到包含 SONAME libnuke.so.0 的 libnuke.so.0.1.4 )。

    在运行时它将与此链接,然后将其设置为 ELF 动态部分NEEDED,然后应该存在具有此名称(或指向它的链接)的库。 在运行时SONAME 被忽略,所以只有链接或文件存在就足够了。

    备注:SONAME 仅在链接/构建时被强制执行,而不是在运行时。

    库的“SONAME”可以通过“objdump -p file |grep SONAME”查看。 可以使用 'objdump -p file |grep NEEDED' 来查看二进制文件的 'NEEDED'。

    [EDIT] WARNING 以下是一般性评论,不是在 linux 中部署的评论。见最后。

    假设您有一个名为 libnuke.so.1.2 的库,并且您开发了一个新的 libnuke 库:

    • 如果您的新库是以前的修复,没有更改 api,您应该保持相同的 soname,增加文件名的版本。即文件将是libnuke.so.1.2.1,但soname 仍将是libnuke.so.1.2。
    • 如果您有一个新库,它只添加了新功能但没有破坏功能并且仍然与以前的兼容,您希望使用与以前相同的 soname 加上一个新的后缀,如 .1。即文件和soname 将是libnuke.so.1.2.1。任何与 libnuke.1.2 链接的程序仍然可以使用该程序。与 libnuke.1.2.1 链接的新程序只能与该程序一起使用(直到新的颠覆出现,如 libnuke.1.2.1.1)。
    • 如果您的新库与任何 libnuke 不兼容:libnuke.so.2
    • 如果您的新库与裸旧版本兼容:libnuke.so.1.3 [即仍与 libnuke.so.1 兼容]

    [编辑]完成:linux案例。

    在 linux 现实生活中 SONAME 作为一种特定的形式: lib[NAME][API-VERSION].so.[major-version] major-version 只是一个整数值,在每次主要库更改时都会增加。 API-VERSION 默认为空

    前 libnuke.so.0

    然后真正的文件名包括次要版本和子版本,例如:libnuke.so.0.1.5

    我认为不提供 soname 是一种不好的做法,因为重命名文件会改变其行为。

    【讨论】:

    • Philippe,根据 David A. Wheeler (bit.ly/1CkQJmR) 的说法,soname 有一个版本号,如 libnuke.so.1 或 libnuke.so.4。你知道第二个版本号是否真的可以成为 soname 的一部分,比如 libnuke.so.1.2?
    • 嗯,存在多个数字的sonmae,例如openssl库:SONAME:libssl.so.0.9.8,但你是对的,它比debian的“Program Library HOWTO”更通用似乎确实更严格
    • 您似乎在描述文件名和 soname 应该设置不同,但您没有描述在以后的情况下应该如何设置 soname。
    • @Gabriel 我会更新(稍后)我的答案,实际上对于大多数 linux 案例来说太笼统而且实际上是错误的(太糟糕了,没有其他答案出现)。
    • 如果有时间了解这个问题的人可以更新这个答案吗?
    【解决方案2】:

    另一方面: 至少在 Linux 上,SONAME 条目为运行时链接器系统提供了有关如何在 /lib、/lib64 等中创建适当链接的提示。 运行命令 ldconfig 会尝试创建一个以 SONAME 命名的符号链接,该链接也被带入运行时链接器缓存。标记相同 SONAME 的最新库赢得了链接竞赛。 如果某些软件依赖于特定的 SONAME 并且您想要更新一个库,您必须提供此 SONAME 以使 ldconfig 坚持这个新库(如果 ldconfig 用于重建缓存和链接)。例如。 libssl.so.6 和 libcrypto.so.6 就是这样的情况。

    【讨论】:

      【解决方案3】:

      假设 libA.so 依赖于 libB.so,并且它们都在一个目录中(当然动态链接器无法找到该目录)。如果您没有设置soname,那么dlopen 不起作用:

      auto pB = dlopen("./libB.so", RTLD_LAZY | RTLD_GLOBAL);
      auto pA = dlopen("./libA.so", RTLD_LAZY | RTLD_GLOBAL);
      

      因为运行时链接器找不到libB.so,所以将pA设置为NULL

      在这种情况下,soname 将把你从地狱中拯救出来......

      【讨论】:

        【解决方案4】:

        这是一个支持Johann Klasek's answer的例子。

        简而言之,运行时需要 SONAME。 在编译时,只需要链接器名称或真实名称(例如g++ main.cpp -L. -laddg++ main.cpp -L. -l:libadd.so.1.1)。链接器名和实名的定义遵循Program Library HOWTO: 3. Shared Libraries

        源码树:

        ├── add.cpp
        ├── add.h
        ├── main.cpp
        └── Makefile
        

        生成文件:

        SOURCE_FILE=add.cpp
        # main.cpp includes `add.h`, whose implementation is `add.cpp`
        MAIN_FILE=main.cpp
        SONAME=libadd.so.1
        REAL_NAME=libadd.so.1.1
        LINKER_NAME=libadd.so
        OUTPUT_FILE=a.out
        
        all:
           g++ -shared -fPIC -Wl,-soname,${SONAME} -o ${REAL_NAME} ${SOURCE_FILE}
           ln -s ${REAL_NAME} ${LINKER_NAME}
           g++ main.cpp -I. -L. -ladd -o ${OUTPUT_FILE} 
           # Same as `ldconfig -n .`, creates a symbolic link
           ln -s ${REAL_NAME} ${SONAME}
           #./a.out: error while loading shared libraries: libadd.so.1: cannot open 
           # shared object file: No such file or directory
           LD_LIBRARY_PATH=. ./${OUTPUT_FILE}
        clean:
           rm ${SONAME} ${REAL_NAME} ${LINKER_NAME} ${OUTPUT_FILE}
        

        【讨论】:

          【解决方案5】:

          您在命名传统 libname.{a}.{b}.{c} 中创建了一个名为 libx.1.0.0 的动态库

          {a} stand for primary version, should changes when APIs changes(which making things incompatible).
          {b} stand for sub version, should changes by adding APIs.
          {c} stand for mirror version, should changes by bug fixing or optimizing
          

          现在你正在发布 libx.1.2.0,你需要声明 libx.1.2.0 与 libx.1.0.0 兼容,因为只是添加函数和人们的可执行文件不会崩溃,只需像旧的一样链接它时间:

          将 libx.1.0.0 和 libx.1.2.0 设置为具有相同的 soname,例如 libx.1

          这就是 soname 的作用。

          【讨论】:

          • 但是还是需要手动创建一个符号链接吧?说 soname 是符号链接名称是否正确?
          猜你喜欢
          • 2015-02-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-11-08
          • 1970-01-01
          • 1970-01-01
          • 2011-05-03
          相关资源
          最近更新 更多