我使用 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 的来源。