Linux 加载程序 (ld-linux-x86-64) 在 Glibc 2.12 和 Glibc 2.17 之间的行为差​​异

Discrepancy in behavior of Linux loaders (ld-linux-x86-64) between Glibc 2.12 and Glibc 2.17

我正在尝试在两台 x86 独立机器上编译同一个库。 两者使用相同的工具链(完全相同的一组文件)但具有不同的 Glibc 版本。

当我 运行 命令 LD_DEBUG=libs /lib64/ld-linux-x86-64.so.2 --list ./libl2ps.so 时,我注意到 2 个 Linux 加载程序之间存在以下差异:

机器 1(使用 Glibc 2.12):

 19943: find library=libm.so.6 [0]; searching
 19943:  search path=/ebs/frperies/repo/gnb/uplane/build/prefix-root/asik-x86_64-ps_lfs-dynamic-linker-on/toolchain/sysroots/core2-64-pc-linux-gnu/usr/lib64:...:/ebs/frperies/repo/gnb/uplane/build_bbp/l2_ps/build/.      (RPATH from file ./libl2ps.so)
 19943:   trying file=/ebs/frperies/repo/gnb/uplane/build/prefix-root/asik-x86_64-ps_lfs-dynamic-linker-on/toolchain/sysroots/core2-64-pc-linux-gnu/usr/lib64/libm.so.6
 19943: 
 19943: find library=libgcc_s.so.1 [0]; searching
 ...

在这种情况下,Linux 加载程序 selects lib libm.so.6 来自基于 lib libl2ps.so 的 RPATH 的工具链路径。

机器 2(使用 Glibc 2.17):

 10699: find library=libm.so.6 [0]; searching
 10699:  search path=/home/frperies/repo/gnb/uplane/build/prefix-root/asik-x86_64-ps_lfs-dynamic-linker-on/toolchain/sysroots/core2-64-pc-linux-gnu/usr/lib64:/home/frperies/repo/gnb/uplane/build/prefix-root/asik-x86_64-ps_lfs-dynamic-linker-on/toolchain/sysroots/core2-64-pc-linux-gnu/lib64:/home/frperies/repo/gnb/uplane/build/prefix-root/asik-x86_64-ps_lfs-dynamic-linker-on/toolchain/sysroots/core2-64-pc-linux-gnu/usr/lib:/home/frperies/repo/gnb/uplane/build_bbp/l2_ps/build/.        (RPATH from file ./libl2ps.so)
 10699:   trying file=/home/frperies/repo/gnb/uplane/build/prefix-root/asik-x86_64-ps_lfs-dynamic-linker-on/toolchain/sysroots/core2-64-pc-linux-gnu/usr/lib64/libm.so.6
 10699:   trying file=/home/frperies/repo/gnb/uplane/build/prefix-root/asik-x86_64-ps_lfs-dynamic-linker-on/toolchain/sysroots/core2-64-pc-linux-gnu/lib64/libm.so.6
 10699:   trying file=/home/frperies/repo/gnb/uplane/build/prefix-root/asik-x86_64-ps_lfs-dynamic-linker-on/toolchain/sysroots/core2-64-pc-linux-gnu/usr/lib/libm.so.6
 10699:   trying file=/home/frperies/repo/gnb/uplane/build_bbp/l2_ps/build/./libm.so.6
 10699:  search cache=/etc/ld.so.cache
 10699:   trying file=/lib64/libm.so.6

至于机器 1,加载器尝试从工具链路径从 libl2ps.so 的 RPATH 到 select lib libm.so.6,但由于某种原因跳过它并尝试其他路径。最后它 selects libm.so.6 来自系统路径 /lib64/.

2 个库的 RPATH lib2ps.so 完全相同。这两个文件 libm.so.6 在两台机器上也完全相同(用 md5sum 检查)。

我不明白这两个 Linux 加载程序之间的行为差​​异。 您认为有什么理由可以解释这种差异吗?

非常感谢您的回答。

更新:

谢谢 yugr 的回答。

readelf -h 的输出仅给出字段“入口点地址”和“节开始headers[=106”的差异=]”并且没有其他差异,所以我认为这无济于事。

关于使用dlopen()/dlerror(),我用以下语句做了一个小的可执行文件:

dlopen("/home/frperies/repo/gnb/uplane/build/prefix-root/asik-x86_64-ps_lfs-dynamic-linker-on/toolchain/sysroots/core2-64-pc-linux-gnu/usr/lib64/libm-2.28.so", RTLD_LAZY);

在机器 1 上它按预期工作:

C++ dlopen demo

Opening libm-2.28.so...
Closing library...

在机器 2 上失败,dlerror() 给出以下输出:

Cannot open library: /home/frperies/repo/gnb/uplane/build/prefix-root/asik-x86_64-ps_lfs-dynamic-linker-on/toolchain/sysroots/core2-64-pc-linux-gnu/usr/lib64/libm-2.28.so: cannot open shared object file: No such file or directory

但是文件 libm-2-28.so 确实存在于我的文件系统中:

$ ls -l /home/frperies/repo/gnb/uplane/build/prefix-root/asik-x86_64-ps_lfs-dynamic-linker-on/toolchain/sysroots/core2-64-pc-linux-gnu/usr/lib64/libm-2.28.so
-rwxr-xr-x 1 frperies linseeusers_lte_espoo 1682944 Oct  5 13:50 /home/frperies/repo/gnb/uplane/build/prefix-root/asik-x86_64-ps_lfs-dynamic- linker-on/toolchain/sysroots/core2-64-pc-linux-gnu/usr/lib64/libm-2.28.so

这很奇怪,是什么导致了这种情况???

谢谢

更新二:

没错,我没有指出机器 1 是 RHEL6.8 发行版,而机器 2 是 RHEL7.4 发行版。我(天真地?)认为这并不重要...

在机器 1 上:

$ cat /proc/sys/kernel/osrelease
4.4.115-1.NSN.el6.x86_64

$ uname -a
Linux sq24-3 4.4.115-1.NSN.el6.x86_64 #1 SMP Mon Feb 12 12:35:46 CET 2018 x86_64 x86_64 x86_64 GNU/Linux

$ readelf -n libl2ps.so 

Notes at offset 0x00000270 with length 0x00000024:
  Owner                 Data size   Description
  GNU                  0x00000014   NT_GNU_BUILD_ID (unique build ID bitstring)
  Build ID: b598468830fdf2f61eda25553b9a367c4d28cdc9

在机器 2 上:

$ cat /proc/sys/kernel/osrelease
3.10.0-693.el7.x86_64

$ uname -a
Linux localhost.localdomain 3.10.0-693.el7.x86_64 #1 SMP Thu Jul 6 19:56:57 EDT 2017 x86_64 x86_64 x86_64 GNU/Linux

$ readelf -n libl2ps.so 

Displaying notes found at file offset 0x00000270 with length 0x00000024:
  Owner                 Data size   Description 
  GNU                  0x00000014   NT_GNU_BUILD_ID (unique build ID bitstring)
  Build ID: 5829181bc0502233748149369108915ea7b10e8f

有帮助吗?

谢谢

更新 3:

$ readelf -n libm.so.6 

Notes at offset 0x00000238 with length 0x00000024:
  Owner                 Data size   Description
  GNU                  0x00000014   NT_GNU_BUILD_ID (unique build ID bitstring)
  Build ID: 0d84c7247dd76008c096719043e5592735a1c4bd

Notes at offset 0x0000025c with length 0x00000020:
  Owner                 Data size   Description
  GNU                  0x00000010   NT_GNU_ABI_TAG (ABI version tag)
  OS: Linux, ABI: 4.4.0

那么,如何解读这个设置为4.4.0的ABI版本号??

谢谢

感谢 yugr 和 Employed Russian 的回答!! 我将通过在机器 2 上升级我的内核版本来尝试一下。

谢谢 此致

您看到的错误消息是臭名昭著的令人困惑的 ENOENT errno。我在 dl-load.c:

中看到它的两个实例

我怀疑第一个在你的情况下失败了,这意味着 OS 内核在两台机器之间不兼容。 ld.so manpage 确实这么说

Each shared object can inform the dynamic linker of the minimum kernel ABI version that it requires. (This requirement is encoded in an ELF note section that is viewable via readelf -n as a section labeled NT_GNU_ABI_TAG.) At run time, the dynamic linker determines the ABI version of the running kernel and will reject loading shared objects that specify minimum ABI versions that exceed that ABI version.

NT_GNU_ABI_TAG 是 4.4.0,这意味着您 运行 一个程序期望在 3.10 内核上至少有 4.4 内核。理论上较新的 Glibc 也应该 运行 在较旧的内核上,但在你的情况下,Glibc 可能是用明确的 --enable-kernel 标志构建的,这防止它在 4.4 之前的内核上使用(参见 this explanation of --enable-kernel).

作为变通方法,您可以尝试通过

覆盖机器 2 上的内核版本来欺骗 Glibc
export LD_ASSUME_KERNEL=4.4.0

但如果 libm 进行 4.4 特定的系统调用,而这些系统调用在 3.10 上并不存在。