Estoy intentando compilar un programa prog
y vincularlo con la versión beta 1.0.2 de OpenSSL, creado desde el código fuente e instalado en /usr/local/ssl-1.0.2
. En un sistema antiguo que usa 0.9.8, esto funciona sin demasiados problemas. En un sistema más reciente con 1.0.1 instalado, esto requiere un poco más de trabajo. Me pregunto por qué.
1) En Ubuntu 10.04, con OpenSSL 0.9.8:
Estos son los pasos que sigo para compilar y vincular con 1.0.2.
$ ./config shared --openssldir=/usr/local/ssl-1.0.2 && make && make install
$ ldconfig
$ ldconfig -p | grep libcrypto
=> Sólo aparecen los archivos 0.9.8, así que agrego la ruta a los archivos 1.0.2...
$ ldconfig /usr/local/ssl-1.0.2/lib
$ ldconfig -p | grep libcrypto
=>
libcrypto.so.1.0.0 (libc6) => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0
libcrypto.so.0.9.8 (libc6, hwcap: 0x0008000000008000) => /lib/i686/cmov/libcrypto.so.0.9.8
libcrypto.so.0.9.8 (libc6, hwcap: 0x0004000000000000) => /lib/i586/libcrypto.so.0.9.8
libcrypto.so.0.9.8 (libc6, hwcap: 0x0002000000000000) => /lib/i486/libcrypto.so.0.9.8
libcrypto.so.0.9.8 (libc6) => /lib/libcrypto.so.0.9.8
libcrypto.so.0.9.8 (libc6) => /usr/lib/libcrypto.so.0.9.8
libcrypto.so (libc6) => /usr/local/ssl-1.0.2/lib/libcrypto.so
Y así puedo compilar prog
...
$ gcc -o prog ... -L/usr/local/ssl-1.0.2/lib -lcrypto
$ ldd prog
=>
libcrypto.so.1.0.0 => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0 (0x0083b000)
... y está correctamente vinculado con 1.0.2.
2) En Debian Wheezy, con OpenSSL 1.0.1:
Mismos pasos, diferente resultado.
$ ./config shared --openssldir=/usr/local/ssl-1.0.2 && make && make install
$ ldconfig
$ ldconfig -p | grep libcrypto
=>
libcrypto.so.1.0.0 (libc6, hwcap: 0x0008000000008000) => /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0
libcrypto.so.1.0.0 (libc6, hwcap: 0x0004000000000000) => /usr/lib/i386-linux-gnu/i586/libcrypto.so.1.0.0
libcrypto.so.1.0.0 (libc6) => /usr/lib/i386-linux-gnu/libcrypto.so.1.0.0
Asimismo, agrego la ruta a 1.0.2...
$ ldconfig /usr/local/ssl-1.0.2/lib
$ ldconfig -p | grep libcrypto
=>
libcrypto.so.1.0.0 (libc6, hwcap: 0x0008000000008000) => /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0
libcrypto.so.1.0.0 (libc6, hwcap: 0x0004000000000000) => /usr/lib/i386-linux-gnu/i586/libcrypto.so.1.0.0
libcrypto.so.1.0.0 (libc6) => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0
libcrypto.so.1.0.0 (libc6) => /usr/lib/i386-linux-gnu/libcrypto.so.1.0.0
libcrypto.so (libc6) => /usr/local/ssl-1.0.2/lib/libcrypto.so
Luego intento compilar...
$ gcc -o prog ... -L/usr/local/ssl-1.0.2/lib -lcrypto
$ ldd prog
=>
libcrypto.so.1.0.0 => /usr/lib/i386-linux-gnu/i686/cmov/libcrypto.so.1.0.0 (0xb7591000)
Pero aquí no está vinculado a 1.0.2. La ruta de la biblioteca en tiempo de compilación es correcta (especificada con -L
, gcc
de lo contrario fallaría ya que algunas funciones utilizadas prog
son específicas de 1.0.2), pero no la ruta en tiempo de ejecución.
3) Cómo hacer que funcione en Wheezy
Con o sin carrera ldconfig /usr/local/ssl-1.0.2/lib
:
$ gcc -o prog ... -Wl,--rpath=/usr/local/ssl-1.0.2/lib -L/usr/local/ssl-1.0.2/lib -lcrypto
$ ldd prog
=>
libcrypto.so.1.0.0 => /usr/local/ssl-1.0.2/lib/libcrypto.so.1.0.0 (0xb7592000)
Alternativamente, ejecute export LD_LIBRARY_PATH=/usr/local/ssl-1.0.2/lib
antes de ejecutar gcc
.
lo que me gustaria saber
Usando LD_DEBUG=libs ./prog
lo sugerido por mr.spuratic, descubrí que las rutas se buscaron en /etc/ld.so.cache
. Abrí ese archivo y descubrí que el orden en que se buscan .so corresponde al resultado de ldconfig -p
.
Entonces la pregunta real es:
- ¿Por qué el archivo 1.0.2 aparece en la parte superior de la lista de ldconfig en 1) pero no en 2)? ¿Pura aleatoriedad? ¿Confusión debido a que los archivos 1.0.1 y 1.0.2 tienen el mismo sufijo? ("1.0.0")
O, dicho de otra manera,
- ¿Por qué las banderas agregadas en 3) no son necesarias en 1)?
Respuesta1
Hay tres cosas que debe tener en cuenta al compilar/vincular con un paquete no predeterminado:
- encabezados (generalmente
CFLAGS
) - ruta de la biblioteca en tiempo de compilación (generalmente
LDFLAGS
) - ruta de la biblioteca en tiempo de ejecución (caminovía
LDFLAGS
,LD_RUN_PATH
,LD_LIBRARY_PATH
old.so.conf
)
No has dicho qué prog
es, así que no sé qué tan bien se comportará su configuración (¿o si usa autoconf?). He visto muchos que solo realizan los primeros dos pasos de manera confiable.
Durante la etapa de enlace, el orden de la ruta de la biblioteca es relevante; suponiendo que esté utilizando la cadena de herramientas GNU (gcc y binutils), probablemente pueda ver lo que sucede configurando CFLAGS
antes configure
(o posible directamente Makefile
):
export CFLAGS="-Wl,-t"
Esto pasa la -t
opción de seguimiento al vinculador. Es posible que necesite agregar V=1
o VERBOSE=1
al comando make si solo obtiene líneas concisas "CC" y "LD" durante el make.)
En tiempo de ejecución, puede ver qué ld.so
intenta configurar cuidadosamente LD_DEBUG
, por ejemplo
LD_DEBUG=libs ./myprog
(o pruebe los valores de files
o symbols
para más detalles)
Para especificar los tres parámetros correctamente en el momento de la compilación, debería poder hacer:
export CFLAGS="-I/usr/local/ssl-1.0.2/include"
export LDFLAGS="-L/usr/local/ssl-1.0.2/lib -R/usr/local/ssl-1.0.2/lib"
luego reconfigure/recompile.
Está utilizando --openssldir
en lugar del más convencional --prefix
(recomiendo este último y también usarlo solo make install_sw
si no necesita las aproximadamente 1000 páginas de manual y enlaces simbólicos que le brinda una instalación predeterminada). Esto puede ser parte del problema. Por alguna razón, se sabe que las bibliotecas .so que muestra ld.so
no tienen un sufijo de versión (por ejemplo .so.1.0.2
), un " " adecuado make install
debería haberlo configurado para usted (a través del link-shared
destino en el archivo principal Makefile
).
La -R
opción indica al vinculador que incruste un RPATH en la salida ejecutable de la biblioteca OpenSSL específica para que no necesite depender del valor predeterminado que ld.so
normalmente proporcionaría el vinculador de tiempo de ejecución ( ). Puede modificar los binarios existentes conchrpath
en cambio.
Esto es más o menos equivalente a exportar LD_LIBRARY_PATH=/usr/local/ssl-1.0.2/lib
. Puede leer más sobre RPATH y el RUNPATH relacionado aquí:http://blog.tremily.us/posts/rpath/
Como último recurso, podría compilar OpenSSL sin "compartido" o con "no compartido", esto le brindará bibliotecas estáticas que no tendrán este problema (pero que bien pueden tener otros problemas, por ejemplo, para usar dentro de ELF .so, causando PIC /problemas de pastel)
Según los detalles actualizados, creo que el problema es que 1.0.1 y 1.0.2beta establecen el sufijo de versión .so (SONAME) en 1.0.0. En el primer sistema con sólo 0.9.8 esto no causa problemas; en el segundo con 1.0.1 y 1.0.2, ambos con la versión 1.0.0, "el primer partido gana" según el ld.so.{conf,d}
orden. Recuerde, ld
el vinculador en tiempo de compilación es un programa diferente al ld.so
vinculador en tiempo de ejecución y puede tener un comportamiento diferente (generalmente dando como resultado errores de símbolos o algo peor, como ha visto).
$ cd /usr/local/src/openssl/openssl/1.0.2beta1
$ readelf -a libssl.so | grep SONAME
0x0000000e (SONAME) Library soname: [libssl.so.1.0.0]
$ cat verchk.c
int main(int argc, char *argv[]) {
printf("build: %s\n",OPENSSL_VERSION_TEXT);
printf("run : %s\n",SSLeay_version(SSLEAY_VERSION));
return 0;
}
$ gcc -Wall -I/usr/local/src/openssl/openssl-1.0.2-beta1/include \
-Wl,-rpath /usr/local/src/openssl/openssl-1.0.2-beta1/ \
-o verchk /usr/local/src/openssl/openssl-1.0.2-beta1/libcrypto.so verchk.c
$ ./verchk
build: OpenSSL 1.0.2-beta1 24 Feb 2014
run : OpenSSL 1.0.2-beta1 24 Feb 2014
$ grep SHLIB_M...R= Makefile
SHLIB_MAJOR=1
SHLIB_MINOR=0.0
Actualizar
OpenSSL-1.1 ha realizado algunos cambios a nivel de API; el código anterior no podrá compilarse con encabezados v1.1 y bibliotecas más antiguas ( undefined reference to `OpenSSL_version'
).
SSLeay_version()
Ahora está en desuso y (según OPENSSL_API_COMPAT
) se puede #define
asignar a la función API adecuada OpenSSL_version()
.
Respuesta2
¿La ruta de la nueva biblioteca está en uno de los archivos que contiene /etc/ld.so.conf.d
? Próxima ejecución: -
#ldconfig -v
para reconstruir el caché. Si es rápido, debería ver la nueva biblioteca en la larga lista que imprime (o canalizarla a grep
o less
)
¿Quizás la ruta ya estaba en el primer servidor, pero no en el segundo?