2010-10-27 9 views
57

Los archivos binarios de Linux suelen estar vinculados dinámicamente a la biblioteca del sistema principal (libc). Esto mantiene la huella de memoria del binario bastante pequeña, pero los binarios que dependen de las últimas bibliotecas no se ejecutarán en sistemas más antiguos. Por el contrario, los binarios vinculados a bibliotecas antiguas se ejecutarán felizmente en los últimos sistemas.Vinculación con una versión anterior de libc para proporcionar una mayor cobertura de aplicaciones

Por lo tanto, para asegurarnos de que nuestra aplicación tenga una buena cobertura durante la distribución, necesitamos encontrar la libc más antigua que podamos admitir y unir nuestro binario a esa.

¿Cómo debemos determinar la versión más antigua de libc a la que podemos vincular?

Respuesta

65

Resuelva qué símbolos en su ejecutable están creando la dependencia de la versión no deseada de 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 

mirada dentro de la dependido-upon biblioteca para ver si hay símbolos en las versiones más antiguas que se pueden enlazar con:

$ 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 

Estamos de suerte!

Solicitud de la versión de GLIBC_2.2.5 en el código:

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

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

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

Obsérvese que GLIBC_2.3 ya no es necesaria:

$ 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 

Para más información, ver http://www.trevorpounds.com/blog/?p=103.

+6

También agregaría que a menudo solo hay uno o dos símbolos que causan una dependencia en una nueva versión de glibc, por lo que si me preocupa que tenga que enumerar cientos de símbolos para eliminar una dependencia, no lo hará. – Malvineous

+2

dietlibc también vale la pena mirar. –

+0

O si puede vincular estáticamente (es decir, su código no es un complemento y no usará complementos), también puede consultar musl-libc. He encontrado a menudo que los programas enlazados estáticamente con musl-libc son más pequeños que sus contrapartes glibc dinámicamente vinculados. – 0xC0000022L

3

glibc 2.2 es una versión mínima bastante común. Sin embargo, encontrar una plataforma de compilación para esa versión puede no ser trivial.

Probablemente una mejor dirección es pensar en el sistema operativo más antiguo que desea admitir y basarse en eso.

+2

tiene sentido, yo sólo con la esperanza de un poco de fruta que cuelga bajo jugosa que podría arrancar de las ramas de oportunidad :-) –

+0

2.4 es Linux Standard Base 4.0 (desde 2006), 2.2 es de 2000 ... –

+0

Eh? http://www.gnu.org/software/libc/ dice que glibc * 2.17 *, lanzado el 12/12/2012, es la última versión. ¿Cómo puede 2.2 ser un mínimo común? – musiphil

8

Desafortunadamente, la solución de @ Sam no funciona bien en mi situación. Pero de acuerdo con su manera, encontré mi propia manera de resolver eso.

Esta es mi situación:

Estoy escribiendo un programa en C++ utilizando el marco de Ahorro (es un middleware RPC). Prefiero el enlace estático al enlace dinámico, por lo que mi programa está vinculado a libthrift.a estáticamente en lugar de libthrift.so. Sin embargo, libthrift.a está vinculada de forma dinámica a glibc, y ya que mi libthrift.a se construyen en mi sistema con glibc 2.15, mi libthrift.a utiliza memcpy de la versión 2.14 ([email protected]_2.14) proporcionado por glibc 2.15.

Pero el problema es que nuestras máquinas servidor solo tienen la versión glibc 2.5 que solo tiene [email protected]_2.2.5. Es mucho más bajo que [email protected]_2.14. Entonces, por supuesto, mi programa de servidor no puede ejecutarse en esas máquinas.

Y me encontré con este solusion:

  1. Usando .symver para obtener el árbitro [email protected]_2.2.5.

  2. escribir mi propia __wrap_memcpy función que sólo llama [email protected]_2.2.5 directamente.

  3. Al vincular mi programa, agregue -Wl, - wrap = memcpy opción para gcc/g ++.

El código involucrado en los pasos 1 y 2 ya está aquí: https://gist.github.com/nicky-zs/7541169

+1

El problema parece ser con '_FORTIFY_SOURCE', que por defecto es 1 en las versiones más nuevas de GCC en ciertos niveles de optimización. Con eso habilitado, algunas de las funciones están enmascaradas. Para mí, no definir y posteriormente redefinirlo en 0 hace que la solución anterior funcione (GCC 4.8.4). – 0xC0000022L

7

Para hacer esto de una manera más automatizada, se puede utilizar la siguiente secuencia de comandos para crear una lista de todos los símbolos que son más recientes en su GLIBC que en una versión dada (configurada en la línea 2). Crea un archivo glibc.h (nombre de archivo establecido por el argumento script) que contiene todas las declaraciones .symver necesarias. A continuación, puede agregar -include glibc.h a su CFLAGS para asegurarse de que se recoja en todas partes en su compilación.

Esto es suficiente si no usa ninguna biblioteca estática que se haya compilado sin incluir lo anterior. Si lo hace, y no desea volver a compilar, puede usar objcopy para crear una copia de la biblioteca con los símbolos renombrados a las versiones anteriores. La segunda línea de la secuencia de comandos crea una versión de su sistema libstdc++.a que se vinculará con los símbolos glibc antiguos. Agregar -L. (o -Lpath/to/libstdc++.a/) hará que su programa enlace estáticamente libstdC++ sin vincular un montón de nuevos símbolos. Si no necesita esto, elimine las dos últimas líneas y la línea 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

¿Por qué el 'tipo'? – Otheus

+0

No es realmente necesario, solo hace que el archivo sea más reproducible si desea controlarlo o evitar que su sistema de compilación reconstruya todo – patstew

+0

Esta solución 'objcopy --redefine-syms' en realidad no funciona, y tomó Me horas para encontrar por qué. objcopy intenta hacer una coincidencia de cadenas para los símbolos dados, pero las versiones de los símbolos no se expresan como cadenas.objcopy simplemente no funciona en ellos: las versiones se expresan en una tabla de búsqueda, que objcopy 2.20.50 no intenta modificar. Nunca. Hay _es_ un símbolo "predeterminado" que se puede cambiar de esta manera: 'symbol @@ 1.2.3' se puede cambiar. – Otheus

Cuestiones relacionadas