2010-10-27 9 views
57

I file binari di Linux sono in genere collegati dinamicamente alla libreria di base del sistema (libc). Ciò mantiene l'ingombro di memoria del binario piuttosto piccolo, ma i binari che dipendono dalle ultime librerie non funzioneranno su sistemi meno recenti. Viceversa, i binari collegati a librerie più datate funzioneranno felicemente sui sistemi più recenti.Collegamento a una vecchia versione di libc per fornire una maggiore copertura dell'applicazione

Pertanto, al fine di garantire che la nostra applicazione abbia una buona copertura durante la distribuzione, è necessario calcolare la più vecchia libc che possiamo supportare e collegare il nostro binario a quello.

Come dovremmo determinare la versione più vecchia di libc a cui possiamo collegarci?

risposta

65

Calcolare quali simboli nel file eseguibile creano la dipendenza dalla versione indesiderata di glibc.

$ objdump -p myprog 
... 
Version References: 
    required from libc.so.6: 
    0x09691972 0x00 05 GLIBC_2.3 
    0x09691a75 0x00 03 GLIBC_2.2.5 

$ objdump -T myprog | fgrep GLIBC_2.3 
0000000000000000  DF *UND* 0000000000000000 GLIBC_2.3 realpath 

sguardo all'interno del dipendeva-upon biblioteca per vedere se ci sono dei simboli in versioni precedenti che è possibile collegare contro:

$ objdump -T /lib/libc.so.6 | grep -w realpath 
0000000000105d90 g DF .text 0000000000000021 (GLIBC_2.2.5) realpath 
000000000003e7b0 g DF .text 00000000000004bf GLIBC_2.3 realpath 

Siamo fortunati!

Richiesta la versione da GLIBC_2.2.5 nel codice:

#include <limits.h> 
#include <stdlib.h> 

__asm__(".symver realpath,[email protected]_2.2.5"); 

int main() { 
    realpath ("foo", "bar"); 
} 

Si osservi che GLIBC_2.3 non è più necessaria:

$ objdump -p myprog 
... 
Version References: 
    required from libc.so.6: 
    0x09691a75 0x00 02 GLIBC_2.2.5 

$ objdump -T myprog | grep realpath 
0000000000000000  DF *UND* 0000000000000000 GLIBC_2.2.5 realpath 

Per ulteriori informazioni, vedere http://www.trevorpounds.com/blog/?p=103.

+6

Vorrei anche aggiungere che spesso ci sono solo uno o due simboli che causano una dipendenza da una nuova versione di glibc, quindi se come me sei preoccupato di dover elencare centinaia di simboli per rimuovere una dipendenza, non lo farai. – Malvineous

+2

Anche dietlibc vale la pena guardare. –

+0

Oppure, se è possibile collegare in modo statico (ovvero il tuo codice non è un plug-in e non utilizzerà plug-in), puoi anche consultare musl-libc. Ho trovato spesso che i programmi linkati staticamente con musl-libc sono più piccoli delle loro controparti glibc collegate dinamicamente. – 0xC0000022L

3

glibc 2.2 è una versione minima piuttosto comune. Tuttavia, trovare una piattaforma di build per quella versione può essere non banale.

Probabilmente una direzione migliore è pensare al sistema operativo più vecchio che si desidera supportare e costruire su quello.

+2

senso, ho solo sperando in qualche succosa frutta a basso impiccagione che io possa cogliere dai rami di opportunità :-) –

+0

2.4 è Linux standard Base 4.0 (dal 2006), 2,2 da 2000 ... –

+0

eh? http://www.gnu.org/software/libc/ dice che glibc * 2.17 *, rilasciato 2012-12-25, è l'ultima versione. In che modo 2.2 può essere un minimo comune? – musiphil

8

Sfortunatamente, la soluzione di @ Sam non funziona bene nella mia situazione. Ma secondo il suo modo, ho trovato il mio modo di risolverlo.

Questa è la mia situazione:

Sto scrivendo un programma in C++ usando il framework Thrift (è un middleware RPC). Preferisco il collegamento statico al collegamento dinamico, quindi il mio programma è collegato a libthrift.a staticamente invece di libthrift.so. Tuttavia, libthrift.a è collegato dinamicamente ad glibc, e dato che il mio libthrift.a è costruito sul mio sistema con le glibc 2.15, il mio libthrift.a utilizza memcpy della versione 2.14 ([email protected]_2.14) fornito da glibc 2.15.

Ma il problema è che le nostre macchine server hanno solo la versione 2.5 di glibc che ha solo [email protected]_2.2.5. È molto inferiore a [email protected]_2.14. Quindi, ovviamente, il mio programma server non può essere eseguito su quelle macchine.

e ho trovato questo solusion:

  1. Utilizzando .symver per ottenere l'arbitro per [email protected]_2.2.5.

  2. scrivere il mio __wrap_memcpy funzione che chiama semplicemente [email protected]_2.2.5 direttamente.

  3. Quando si collega il mio programma, aggiungere -Wl, - wrap = memcpy a gcc/g ++.

Il codice coinvolti nei passaggi 1 e 2 è qui: https://gist.github.com/nicky-zs/7541169

+1

Il problema sembra essere con '_FORTIFY_SOURCE', che per impostazione predefinita è 1 nelle versioni GCC più recenti a determinati livelli di ottimizzazione. Con quello abilitato, alcune delle funzioni sono mascherate. Per me l'indefinimento e la successiva ridefinizione a 0 rendono la soluzione sopra descritta (GCC 4.8.4). – 0xC0000022L

7

Per fare questo in modo più automatizzato, è possibile utilizzare il seguente script per creare un elenco di tutti i simboli che sono più recenti in il tuo GLIBC rispetto a una determinata versione (impostata sulla riga 2). Crea un file glibc.h (nome file impostato dall'argomento script) che contiene tutte le necessarie dichiarazioni .symver. Puoi quindi aggiungere -include glibc.h ai tuoi CFLAGS per assicurarti che vengano raccolti ovunque nella tua compilation.

Ciò è sufficiente se non si utilizzano librerie statiche compilate senza l'inclusione precedente. Se lo fai, e non vuoi ricompilare, puoi usare objcopy per creare una copia della libreria con i simboli rinominati nelle vecchie versioni. La seconda riga inferiore dello script crea una versione del sistema libstdc++.a che si collegherà ai vecchi simboli glibc. L'aggiunta di -L. (o -Lpath/to/libstdc++.a/) renderà il programma in grado di collegare staticamente libstdC++ senza collegarsi a un gruppo di nuovi simboli. Se non ti serve, elimina le ultime due righe e la riga printf ... redeff.

#!/bin/bash 
maxver=2.9 
headerf=${1:-glibc.h} 
set -e 
for lib in libc.so.6 libm.so.6 libpthread.so.0 libdl.so.2 libresolv.so.2 librt.so.1; do 
objdump -T /usr/lib/$lib 
done | awk -v maxver=${maxver} -vheaderf=${headerf} -vredeff=${headerf}.redef -f <(cat <<'EOF' 
BEGIN { 
split(maxver, ver, /\./) 
limit_ver = ver[1] * 10000 + ver[2]*100 + ver[3] 
} 
/GLIBC_/ { 
gsub(/\(|\)/, "",$(NF-1)) 
split($(NF-1), ver, /GLIBC_|\./) 
vers = ver[2] * 10000 + ver[3]*100 + ver[4] 
if (vers > 0) { 
    if (symvertext[$(NF)] != $(NF-1)) 
     count[$(NF)]++ 
    if (vers <= limit_ver && vers > symvers[$(NF)]) { 
     symvers[$(NF)] = vers 
     symvertext[$(NF)] = $(NF-1) 
    } 
} 
} 
END { 
for (s in symvers) { 
    if (count[s] > 1) { 
     printf("__asm__(\".symver %s,%[email protected]%s\");\n", s, s, symvertext[s]) > headerf 
     printf("%s %[email protected]%s\n", s, s, symvertext[s]) > redeff 
    } 
} 
} 
EOF 
) 
sort ${headerf} -o ${headerf} 
objcopy --redefine-syms=${headerf}.redef /usr/lib/libstdc++.a libstdc++.a 
rm ${headerf}.redef 
+0

Perché il 'sort'? – Otheus

+0

Non è davvero necessario, rende solo il file più riproducibile se si desidera controllare il controllo del codice sorgente o impedire al sistema di generazione di ricostruire tutto – patstew

+0

Questa soluzione 'objcopy --redefine-syms' in realtà non funziona, e ci sono voluti mi ore per trovare il perché. objcopy prova a fare una stringa match-replace per i simboli dati, ma le versioni dei simboli sono _non_ espresse come stringhe.objcopy semplicemente non funziona su di esse - le versioni sono espresse in una tabella di ricerca, che objcopy 2.20.50 non tenta di modificare. Mai. C'è un simbolo "predefinito" che può essere cambiato in questo modo - 'simbolo @@ 1.2.3' può essere cambiato. – Otheus

Problemi correlati