用于构建共享库的'soname'选项是什么?

时间:2012-09-28 10:02:07

标签: linux gcc shared-libraries

我学会了“ 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命令检查时告诉您共享库的版本。

它还能做什么?

5 个答案:

答案 0 :(得分:46)

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

链接器在编译时使用

SONAME来从库文件中确定实际的目标库版本。 gcc -l NAME将寻找lib NAME。所以链接或文件然后捕获它肯定会更具体的SONAME(ex libnuke.so链接到包含SONAME libnuke的libnuke.so.0.1.4) .so.0)。

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

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

可以使用'objdump -p file | grep SONAME'查看库的'SONAME'。 使用'objdump -p file | grep NEEDED'可以看到'需要'的二进制文件。

[编辑]警告以下是一般性评论,而非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]。所以。[主要版本] major-version只是一个整数值,在每个主要库更改时会增加。 默认情况下,API-VERSION为空

ex libnuke.so.0

然后真正的文件名包括次要版本和颠覆率:libnuke.so.0.1.5

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

答案 1 :(得分:2)

我们假设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会让你免于地狱......

答案 2 :(得分:1)

以下是支持Johann Klasek's answer的示例。

总而言之,运行时需要SONAME。 在编译时,仅需要linker namereal name(例如g ++ main.cpp -L。-ladd或g ++ main.cpp -L。-l:libadd.so.1.1)。 linker namereal name的定义遵循Program Library HOWTO: 3. Shared Libraries

源代码树:

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

Makefile:

SOURCE_FILE=add.cpp
# main.cpp include `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 with `ldconfig -n .` create a soft 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}

答案 3 :(得分:1)

您使用传统的libname创建了一个名为libx.1.0.0的动态库。{a}。{b}。{c}

{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的作用。

答案 4 :(得分:0)

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