Linux、GNU GCC、ld、版本脚本和 ELF 二进制格式 -- 它是如何工作的?

Linux、GNU GCC、ld、版本脚本和 ELF 二进制格式 -- 它是如何工作的?

我正在尝试了解有关 Linux 中的库版本控制以及如何将其全部发挥作用的更多信息。这是上下文:

-- 我有两个版本的动态库,它们公开相同的接口集,例如libsome1.solibsome2.so

- 应用程序链接到libsome1.so

- 该应用程序用于libdl.so动态加载另一个模块,例如libmagic.so

——现在libmagic.so是挂钩的libsome2.so。显然,在不使用链接描述文件隐藏 中的符号的情况下libmagic.so,在运行时对 中的接口的所有调用都libsome2.so将解析为libsome1.so。这可以通过libVersion()对照宏的值检查返回的值来确认LIB_VERSION

-- 因此,接下来我尝试使用libmagic.so链接器脚本进行编译和链接,该链接器脚本隐藏了除 3 之外的所有符号,这些符号是在其中定义libmagic.so并由其导出的。这有效......或者至少libVersion()LIB_VERSION值匹配(并且它报告版本 2 而不是 1)。

-- 但是,当某些数据结构序列化到磁盘时,我注意到一些损坏。在应用程序的目录中,如果我删除libsome1.so并在其指向的位置创建一个软链接libsome2.so,一切都会按预期工作,并且不会发生相同的损坏。

我不禁认为这可能是由于运行时链接器对符号的解析存在一些冲突造成的。我已经尝试了很多事情,比如尝试链接libsome2.so以便所有符号都对齐symbol@@VER_2(我仍然对此感到困惑,因为该命令nm -CD libsome2.so仍然将符号列为symbol而不是symbol@@VER_2)...似乎没有任何效果!帮助!!!!!!

答案1

这并不能完全回答你的问题,但是......

首先,ELF是Linux对可执行文件(程序)、共享库以及目标文件(编译软件时找到的中间文件)使用的规范。目标文件以 .o 结尾,共享库以 .so 结尾,后跟零个或多个以句点分隔的数字,可执行文件通常没有任何扩展名。

通常有三种形式来命名共享库,第一种形式只是以 .so 结尾。例如,名为 readline 的库存储在名为 libreadline.so 的文件中,并且通常位于 /lib、/usr/lib 或 /usr/local/lib 之一下。使用 -lreadline 等选项编译软件时会找到该文件。 -l 告诉编译器链接到以下库。由于库会不时发生变化,因此它可能会过时,因此库嵌入了称为 SONAME 的东西。 readline 的 SONAME 可能类似于 libreadline 第二个主要版本的 libreadline.so.2。还可能有许多兼容的 readline 次要版本,并且不需要重新编译软件。 readline 的次要版本可能被命名为 libreadline.so.2.14。通常,libreadline.so 只是 readline 最新主要版本(本例中为 libreadline.so.2)的符号链接。 libreadline.so.2 也是 libreadline.so.2.14 的符号链接,而 libreadline.so.2.14 实际上是正在使用的文件。

库的 SONAME 嵌入在库文件本身内。文件 libreadline.so.2.14 内的某个位置是字符串 libreadline.so.2。当程序被编译并使用 readline 链接时,它将查找文件 libreadline.so 并读取嵌入其中的 SONAME。稍后,当程序实际执行时,它将加载 libreadline.so.2,而不仅仅是 libreadline.so,因为那是第一次链接时读取的 SONAME。这允许系统安装多个不兼容版本的 readline,并且每个程序将加载与其链接的适当的主要版本。另外,当将 readline 升级到 2.17 时,我可以将 libreadline.so.2.17 与现有库一起安装,一旦我将符号链接 libreadline.so.2 从 libreadline.so.2.13 移动到 libreadline.so.2.17,所有使用相同主要版本的软件现在都将看到新的次要更新。

相关内容