【问题标题】:Library path order for alternate glibc dynamic linker (ld.so)备用 glibc 动态链接器 (ld.so) 的库路径顺序
【发布时间】:2014-01-01 21:06:02
【问题描述】:

我需要使用另一个 glibc 版本,比我系统上安装的版本更新(2.182.15)。涉及几个相关问题 herehere。我在这里问的具体问题如下:

我设置了新动态链接器 (ld-2.18.so) 的库路径,以便在旧 libc (libc-2.15.so) 之前找到新的 libc (libc-2.18.so)。但是,当我尝试使用新的ld 运行程序时,会选择旧版本的libc,生成SEGV为什么会这样?

注意:我知道这可以通过在编译时使用 --rpath 或在运行时使用 LD_LIBRARY_PATH 来解决。但是,我仍然想了解为什么仍然需要其中之一。

详情如下:

我下载了glibc-2.18 并在/opt/glibc-2.18 构建了它。默认情况下,文件/opt/glibc-2.18/etc/ld.so.conf 丢失。我创建了它,并更新了新的glibc 的库缓存,如下所示。我强调一点:新的libc 在旧的libc 之前找到

$ cat /opt/glibc-2.18/etc/ld.so.conf
/opt/glibc-2.18/lib
/usr/local/lib
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu/mesa
/lib
/usr/lib
$ /opt/glibc-2.18/sbin/ldconfig -v |& grep -E '^[^'$'\t'']|libc\.'
/opt/glibc-2.18/sbin/ldconfig: Path `/opt/glibc-2.18/lib' given more than once
/opt/glibc-2.18/sbin/ldconfig: Can't stat /opt/glibc-2.18/lib64: No such file or directory
/opt/glibc-2.18/sbin/ldconfig: Can't stat /opt/glibc-2.18/libx32: No such file or directory
/opt/glibc-2.18/lib:
        libc.so.6 -> libc-2.18.so
/usr/local/lib:
/lib/x86_64-linux-gnu:
        libc.so.6 -> libc-2.15.so
/usr/lib/x86_64-linux-gnu:
/usr/lib/x86_64-linux-gnu/mesa:
/lib:
/usr/lib:

然后,我创建了一个简单的 C 程序:

$ cat <<EOF >a.c
> #include <stdio.h>
> int main()
> {
>     fprintf(stdout, "ok\n");
>     return 0;
> }
> EOF
$ g++ a.c
$ file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x43b8484e3910072375d68418cb6327478266c0e9, not stripped
$ ldd a.out
    linux-vdso.so.1 =>  (0x00007fffd7ffe000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa7c47bd000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fa7c4b9b000)
$ readelf -a a.out | grep lib
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
000000601000  000100000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main + 0
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
    46: 00000000004005f0     2 FUNC    GLOBAL DEFAULT   13 __libc_csu_fini
    52: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
    57: 0000000000400560   137 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
  000000: Version: 1  File: libc.so.6  Cnt: 1
$ objdump -x a.out | grep -A3 Version
Version References:
  required from libc.so.6:
    0x09691a75 0x00 02 GLIBC_2.2.5

如上所示,这个程序内部有旧的ld 硬编码。我可以用新的ld 强制运行它,并且我希望使用新的ld 的路径(你可以看到新的ld.so.cache 正在打开)。但是,出于某种原因,我试图理解,旧的 libc 在新的 libc 之前找到,生成一个 SEGV:

$ /opt/glibc-2.18/lib/ld-2.18.so ./a.out
Segmentation fault (core dumped)
$ strace /opt/glibc-2.18/lib/ld-2.18.so ./a.out |& grep open
open("./a.out", O_RDONLY|O_CLOEXEC)     = 3
open("/opt/glibc-2.18/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3

我还可以使用新库编译并烘焙新的ld,如下所示:

$ g++ -L/opt/glibc-2.18/lib -Wl,--dynamic-linker=/opt/glibc-2.18/lib/ld-2.18.so a.c -o a.2.18.out
$ file a.2.18.out
a.2.18.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0x25ab43f3d29b49fa21385a15e43325e9fb904e81, not stripped
$ ldd a.2.18.out
    linux-vdso.so.1 =>  (0x00007fffa68da000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9df5cbe000)
    /opt/glibc-2.18/lib/ld-2.18.so => /lib64/ld-linux-x86-64.so.2 (0x00007f9df609c000)
$ readelf -a a.2.18.out | grep lib
      [Requesting program interpreter: /opt/glibc-2.18/lib/ld-2.18.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
000000601000  000100000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main + 0
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)
    54: 0000000000400600     2 FUNC    GLOBAL DEFAULT   13 __libc_csu_fini
    60: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
    65: 0000000000400570   137 FUNC    GLOBAL DEFAULT   13 __libc_csu_init
  000000: Version: 1  File: libc.so.6  Cnt: 1
$ objdump -x a.2.18.out | grep -A3 Version
Version References:
  required from libc.so.6:
    0x09691a75 0x00 02 GLIBC_2.2.5

不过,如果我尝试运行新程序,同样的事情会发生,旧的libc 被使用而不是新的libc

$ ./a.2.18.out
Segmentation fault (core dumped)
$ strace ./a.2.18.out |& grep open
open("/opt/glibc-2.18/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3

对于任一可执行文件,指定LD_LIBRARY_PATH=/opt/glibc-2.18/lib 使其工作。但是,我的问题是为什么仍然需要这样做,因为新ld 的路径在开始时配置为在旧libc 之前获取新libc

【问题讨论】:

    标签: linux shared-libraries ld glibc dynamic-linking


    【解决方案1】:

    我明白了,问题出在 OS ABI 版本上。也就是file表示的数字,如:

    $ file /lib/x86_64-linux-gnu/libc-2.15.so | grep -o "for GNU/Linux [0-9.]*"
    for GNU/Linux 2.6.24
    

    glibc 只配置了--prefix 时,默认情况下它使用ABI 版本更小(!!)(在我的情况下为2.6.16)构建,而不是默认的系统 (2.6.24)。所以libc-2.18 的 ABI 版本小于libc-2.15

    ldconfig 找到具有不同 ABI 编号的 libc.so.6 的 2 个版本时,它会将它们放在 ld.so.cache按 ABI 编号的降序排列,而不是按出现的顺序。这可以通过交换它们的位置、重建缓存(使用ldconfig)和列出缓存内容(使用ldconfig -p)来检查。只有当 2 个libc.so.6 文件的 ABI 版本相同时,才会按出现顺序放入缓存中。

    glibc 配置为--enable-kernel=2.6.24 会使其使用与系统相同的ABI 版本,从而修复问题语句中的解析问题,而无需显式的--rpathLD_LIBRARY_PATH

    【讨论】:

      猜你喜欢
      • 2010-10-01
      • 1970-01-01
      • 2013-12-05
      • 2012-01-14
      • 1970-01-01
      • 2012-10-22
      • 1970-01-01
      • 1970-01-01
      • 2011-05-28
      相关资源
      最近更新 更多