Linux 上的共享库到底是如何进行版本控制的?

Linux 上的共享库到底是如何进行版本控制的?

我在 SE 和其他教程上收到了很多关于此的相互矛盾的信息。大多数人似乎认为so版本是语义版本。然后其他人纠正他们说它必须符合libtools 约定,并继续隐式假设或暗示-version-info C:R:A传递给 libtools 的数字创建名为libfoo.so.C.R.A.但我在 libtools 手册中没有看到任何地方明确说明文件的命名方式,这与我所看到的行为不匹配。

我正在构建一个第三方包(gdal),在构建过程中,它调用libtool --mode=link -version-info 25:4:5 <many other arguments>,但是构建后留在 .libs 中的 so 文件具有版本 libgdal.20.5.4。

我尝试了同一库的几个其他版本,它们似乎都遵循相同的模式。当您调用 libtools 时,您传入 current:revision:age,它会生成libfoo.so.current-age.age.revision.这会导致sonamelibfoo.so.current-age始终是最低限度该库兼容的版本,而不是其他帖子建议的最大版本。

我在 RHEL 7 和 Debian 10 上进行了此测试。

这是事情应该如何进行的吗?这个图书馆在做什么奇怪的事情吗?是否有任何地方可以权威地或至少正确地记录这一点?

答案1

Linux 上的典型期望是共享库具有形式libfoo.so.N,其中 N 是整数。它也可能具有带有附加整数的形式,这可能具有附加含义,具体取决于您使用的约定(libtool 或其他)。可以使用其他形式,并且某些库(例如 OpenSSL)可以使用其他形式。

Linux 上正确的共享库的一部分是 ELF 条目SONAME,它指定二进制文件的共享对象名称。例如,对于 libz:

$ readelf -a /lib/x86_64-linux-gnu/libz.so.1 | grep SONAME
 0x000000000000000e (SONAME)             Library soname: [libz.so.1]

您会看到,在本例中,SONAME 是libz.so.1。当您的二进制文件链接到此共享库时,它将NEEDED嵌入一个部分。动态链接器在解析共享库时,将在搜索路径中查找名称与 SONAME 相同的共享库。例如,Git 需要以下四个共享库:

$ readelf -a /usr/bin/git | grep NEEDED
 0x0000000000000001 (NEEDED)             Shared library: [libpcre2-8.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libz.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libpthread.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

因此,实际上重要的是 SONAME 中的内容,它相当于语义版本代码段的主要版本。在共享库中进行不兼容的更改而不更改 SONAME(通常为下一个整数)值是一个严重错误,它将导致成群的愤怒用户降临到您的项目上(这是理所当然的)。

因此,共享库中不属于 SONAME 的部分并不重要。 libtool 约定和语义版本控制约定都将.so名称部分后面的第一个整数作为不兼容更改的版本,两者都执行相同的操作。由于这是 SONAME 中通常存在的部分,因此它们在这个目的上是等效的。

我提到过一些共享库做不同的事情。例如,OpenSSL 具有libssl.so.1.1libcrypto.so.1.1作为其使用的共享库 SONAMEs。虽然这不是推荐的约定,但它确实有效。不过,他们在未来的版本中将改为语义版本控制,因为人们发现这令人困惑。

答案2

发现和你一样:

C:R:A被映射到(C-A).A.R

我找到了文档这里但他们并没有很清楚地表明这就是它要做的事情。

相关内容