为什么加载 SO 文件时会在末尾附加版本?

为什么加载 SO 文件时会在末尾附加版本?

我使用 CMake 和 Ninja 构建一个用 C++ 编写的测试可执行文件。我使用海湾合作委员会4.8。我使用的是 Ubuntu 14。

我运行 CMake 来配置 ninja 构建脚本,然后运行 ​​ninja 进行构建。我在 CMake 脚本中指定libcrypto.so和其他 openssl文件作为目标依赖项。*.so

运行可执行文件时,出现以下错误:

加载共享库时出错:libcrypto.so.1.0.0:无法打开共享对象文件:没有这样的文件或目录

我的libcrypto.so文件位于测试可执行文件旁边:

-rw-r--r-- 1 robert domain users 1796847 Apr 20 15:43 libboost_wave.so
-rw-r--r-- 1 robert domain users  410916 Apr 20 15:43 libboost_wserialization.so
-rw-r--r-- 1 robert domain users 2251519 Apr 21 11:28 libcrypto.so
-rw-r--r-- 1 robert domain users  701627 Apr 20 15:43 libEGL.so
-rw-r--r-- 1 robert domain users 4255871 Apr 20 15:43 libGLES_CM.so
-rw-r--r-- 1 robert domain users 7550551 Apr 20 15:43 libGLESv2.so
-rw-r--r-- 1 robert domain users  949113 Apr 17 14:34 libsqlite.so
-rw-r--r-- 1 robert domain users  496298 Apr 21 11:28 libssl.so
-rwxr-xr-x 1 robert domain users 3099250 Apr 21 11:30 Test_UI_String*

(我正在运行的可执行文件名为Test_UI_String

CMake 设置为在可执行文件旁边输出共享库,因为在 Windows(我最熟悉开发)等其他平台上,DLL 文件需要位于 EXE 旁边才能找到并加载。

我很困惑为什么它附加1.0.0到库文件的末尾。它应该在末尾没有版本号的情况下查找它,因为当我链接依赖项时,它的名称与libcrypto.so上面显示的目录中现在的名称相同。

我想更好地理解这里的行为,但是我的主要问题基本上是关于如何使其按预期工作(查找与可执行文件位于同一目录中的库依赖项)。

答案1

当链接器生成可执行文件(或另一个共享库)时,它会通过名称的-l前缀lib和后缀来搜索请求使用的库。.so例如,如果您询问,-lcrypto那么它会搜索libcrypto.so.

一旦找到库,链接器就会从库中读取一段称为其 soname 的元数据。然后,soname 被记录到正在构建的可执行文件中,这就是在运行时再次搜索相同库时使用的名称。当然,soname 可能与链接时实际找到的库的名称不同。

此功能的用例是能够同时在系统上安装多个不同的、不兼容的库版本。下面是它的一般使用方式。

首先,让我描述一下如果没有这个约定会发生什么:

开发头文件(.h文件)与库的特定版本耦合,并且该.so文件也包含该版本:

/usr/include/mylibrary.h
/usr/lib/libmylibrary.so

应用程序myapp已针对此版本的库进行编译,并/usr/lib/libmylibrary.so在运行时加载。

后来,该库升级到新版本。.h和文件都.so被新版本替换。不幸的是,新版本有不兼容的 ABI。

现在,当myapp执行时,它会崩溃或表现出未定义的行为,因为它是针对具有一个 ABI 的一个版本进行编译的,但它在运行时加载了不同的版本。

会发生什么soname 约定:

该库安装如下:

/usr/include/mylibrary.h
/usr/lib/libmylibrary.so -> libmylibrary.so.1
/usr/lib/libmylibrary.so.1

/usr/lib/libmylibrary.so.1在其中嵌入了 soname libmylibrary.so.1

myapp构建时,它会定位/usr/lib/libmylibrary.so,但链接器遵循符号链接并实际加载/usr/lib/libmylibrary.so.1.此外,sonamelibmylibrary.so.1是实际记录在里面的内容myapp。在运行时,直接myapp加载/usr/lib/libmylibrary.so.1,绕过符号链接。

后来,该库升级到具有不兼容 ABI 的新版本。.h和文件都.so被新版本替换。libmylibrary.so.2出现一个新文件。旧版本中的内容libmylibrary.so.1保持不变。

/usr/include/mylibrary.h
/usr/lib/libmylibrary.so -> libmylibrary.so.2
/usr/lib/libmylibrary.so.1
/usr/lib/libmylibrary.so.2

现在,当myapp执行时,它会继续加载libmylibrary.so.1并且仍然按预期运行。但是,如果安装了其他新应用程序或myapp重新编译了应用程序本身,则将遵循符号链接并使用新版本。


您看到的问题是,当链接您的应用程序时,链接器找到了一个包含 soname 的库libcrypto.so.1.0.0

你的副本是libcrypto.so用 soname if 生成的吗libcrypto.so.1.0.0

另一种可能性(很可能在这里)是您的副本libcrypto.so中没有soname,但链接器实际上找到并使用了系统(来自 OpenSSL)的版本libcrypto.so,这就是它的 soname 的来源。

相关内容