使用gold链接器和ld链接器时,与glibc/pthreads使用不同的符号

6

我有一个简单的测试程序,调用 pthread_cond_broadcast

当与 ld 链接时,会显示以下内容:

情况1:

$ nm ld-test  | grep cond_broadcast
U pthread_cond_broadcast@@GLIBC_2.3.2

当与gold链接器链接时,它会显示:
情况2:
 $ nm gold-test  | grep cond_broadcast
 U pthread_cond_broadcast

pthread/libc 包含多个 pthread_cond_broadcast 符号,这些符号具有不同的版本符号,可能是因为 ABI 已经更改。

$ nm  /lib64/libc.so.6  |grep cond_broadca
00000036b84f7d30 t __pthread_cond_broadcast
00000036b85278f0 t __pthread_cond_broadcast_2_0
00000036b84f7d30 T pthread_cond_broadcast@@GLIBC_2.3.2
00000036b85278f0 T pthread_cond_broadcast@GLIBC_2.2.5
$ nm  /lib64/libpthread.so.0  |grep cond_broadcast
00000036b880bee0 t __pthread_cond_broadcast
00000036b880c250 t __pthread_cond_broadcast_2_0
00000036b880bee0 T pthread_cond_broadcast@@GLIBC_2.3.2
00000036b880c250 T pthread_cond_broadcast@GLIBC_2.2.5

所以问题是:
  1. gold与旧版/普通版的ld之间为什么有不同的行为。
  2. 在Case 2中,当二进制文件链接到未版本化的pthread_cond_broadcast符号时,使用了哪个pthread_cond_broadcast符号?最新实现的pthread_cond_broadcast?还是最老的?
这是使用gcc 4.9.2和来自binutils 2.24的gold / ld链接器(作为Red Hat devtoolset-3的一部分)。
1个回答

1
首先澄清一下:ELF文件通常有两个符号表。其中一个是“fat”或“full”,包含所有内部符号(通常在名为.symtab的类型为SHT_SYMTAB的部分中),另一个是仅包含运行时详细信息的“slim”符号表(通常在名为.dynsym的类型为SHT_DYNSYM的部分中)。nm只解析第一个,因此通常不能很好地指示运行时行为。相反,您应该使用readelf -s并查看.dynsym表以查看实际在运行时发挥作用的内容。
(1) 我不知道为什么使用gold时调试符号表不包括完整的符号版本。对我来说似乎是个错误。
(2) 一旦您查看了运行时符号表,您的答案就会自动出现:pthread_cond_broadcast@GLIBC_2.3.2将被使用。

(2a) 关于这个符号将从哪个加载,完全取决于 ELF 的链接方式和运行时环境。让我们忽略所有可能性,关注常见情况:如果你使用了 -lpthread 进行链接,则会使用 libpthread.so 中的版本。如果没有使用,那么将使用 libc.so 中的版本。

(2b) 为什么一个库会有多个版本(例如 libc.sopthread_cond_broadcast@@GLIBC_2.2.5pthread_cond_broadcast@@GLIBC_2.3.2)?你猜对了,这是为了向后兼容。在过去的某个时刻,ABI(应用程序二进制接口)被更改,因此不是破坏所有现有应用程序,他们更新了版本。所有链接到旧glibc版本的程序将使用旧符号版本,而新程序将使用新符号。作为政策,glibc将最新的符号版本公开为默认版本,因此当您使用 pthread_cond_broadcast 时,您将链接到 pthread_cond_broadcast@@GLIBC_2.3.2 版本。这纯粹是大多数人都使用的约定...尽管从技术上讲,可以将任何符号版本公开为默认版本。

(2c) 为什么符号存在于libc.so和libpthread.so中?这允许库在不受单线程环境惩罚的情况下支持多线程环境,或者无需编写两个不同的库(例如一个名为libfoo.so,另一个名为libfoo_thread.so)。libc.so中的符号是虚拟符号 - 它们始终返回“成功”。因此,如果您的主程序不是多线程的,则libfoo.so中的所有调用都将被存根化。但是,如果您的主程序是多线程的(即链接到-lpthread),则符号将透明地自动路由到libpthread.so,并且libfoo.so中的所有调用都将DTRT(即获取互斥锁/等等)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接