Linux

Linux 上的共享庫是如何真正進行版本控制的?

  • February 26, 2021

在 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 上都做了這個測試。

這是事情應該如何運作的嗎?這個圖書館有什麼奇怪的嗎?是否有任何地方可以權威地或至少正確地記錄這一點?

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作為它使用的共享庫 SONAME。雖然這不是推薦的約定,但它恰好可以工作。不過,在未來的版本中,它們將更改為語義版本控制,因為人們覺得這很混亂。

引用自:https://unix.stackexchange.com/questions/581863