2013-12-13 27 views
7

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.

Odpowiedz

5

Rozumiem, problem dotyczy wersji OS ABI. To numer wskazany przez file, takich jak:

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

Kiedy glibc jest skonfigurowany z niczym innym niż --prefix, buduje domyślnie z ABI wersji mniejszy (!!) (w moim przypadku, 2.6.16) niż domyślny w systemie (2.6.24). Tak więc libc-2.18 ma wersję ABI mniejszą niż libc-2.15.

Kiedy ldconfig znajdzie 2 wersje libc.so.6 z różnymi numerami ABI, umieszcza je w ld.so.cachew kolejności malejącej liczby ABI, a nie w kolejności pojawiania. Można to sprawdzić, zamieniając ich lokalizacje, przebudowując pamięć podręczną (z ldconfig) i wyświetlając zawartość pamięci podręcznej (z ldconfig -p). Tylko wtedy, gdy pliki 2 libc.so.6 mają tę samą wersję ABI, zostaną umieszczone w pamięci podręcznej w kolejności pojawiania się.

Konfiguracja glibc z --enable-kernel=2.6.24 powoduje, że użycie tej samej wersji ABI jako system, który z kolei rozwiązuje problemy rozdzielczości w rachunku zapytania, bez konieczności wyraźnego --rpath lub LD_LIBRARY_PATH.