Potrzebuję użyć alternatywnej wersji glibc
, nowszej niż ta zainstalowana w moim systemie (2.18
vs 2.15
). Kilka pokrewnych zagadnień zostało omówionych: here i here. Specyficzny pytanie Pytam tutaj jest następujący:Kolejność ścieżek biblioteki dla alternatywnego dynamicznego linkera glibc (ld.so)
skonfigurować ścieżkę biblioteki nowego dynamicznego linkera (ld-2.18.so
), tak by nowy libc
(libc-2.18.so
) znajduje się przed starym libc
(libc-2.15.so
). Jednak, gdy próbuję uruchomić program z nowym ld
, stara wersja libc
jest pobierana, generując SEGV
. Dlaczego tak się dzieje?
Uwaga: Wiem, że można to naprawić, używając --rpath
w czasie kompilacji lub LD_LIBRARY_PATH
w czasie wykonywania. Jednak nadal chciałbym zrozumieć, dlaczego jeden z nich jest nadal potrzebny.
Dane śledzić:
Pobrałem glibc-2.18
i zbudował go na /opt/glibc-2.18
. Domyślnie brakuje pliku /opt/glibc-2.18/etc/ld.so.conf
. Stworzyłem go i zaktualizowałem pamięć podręczną biblioteki nowego glibc
w następujący sposób. Chcę podkreślić, że: nowy libc
znajduje się przed starym 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:
Potem stworzyliśmy prosty program w 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 [email protected]_2.2.5 (2)
46: 00000000004005f0 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
52: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [email protected]@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
Jak widać powyżej, program ten ma stary ld
ciężko zakodowane wewnątrz. Mogę na siłę uruchomić go z nowym ld
i oczekuję, że ścieżka nowego ld
zostanie użyta (możesz zobaczyć nową otwartą ld.so.cache
). Jednak z jakiegoś powodu staram się zrozumieć, stary libc
znajduje się przed nowym libc
, generując 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
Mogę również przygotować z nową biblioteką i piec w nowym ld
w następujący sposób:
$ 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 [email protected]_2.2.5 (2)
54: 0000000000400600 2 FUNC GLOBAL DEFAULT 13 __libc_csu_fini
60: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [email protected]@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
Mimo to, jeśli próbuję uruchomić nowy program to samo dzieje się, stary libc
jest używany zamiast nowego 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
W przypadku każdego pliku wykonywalnego podanie LD_LIBRARY_PATH=/opt/glibc-2.18/lib
powoduje jego działanie. Jednakże, moim pytaniem jest to, dlaczego wciąż jest to konieczne, ponieważ ścieżka nowego ld
jest skonfigurowana na początku do przechwytywania nowego libc
przed starym libc
.