Estou tentando compilar um programa prog
e vinculá-lo ao beta 1.0.2 do OpenSSL, criado a partir do código-fonte e instalado em /usr/local/ssl-1.0.2
. Em um sistema mais antigo usando 0.9.8, isso funciona sem muitos problemas. Em um sistema mais recente com 1.0.1 instalado, isso requer um pouco mais de trabalho. Estou me perguntando por quê.
1) No Ubuntu 10.04, com OpenSSL 0.9.8:
Aqui estão as etapas que sigo para compilar e vincular ao 1.0.2.
$ ./config shared --openssldir=/usr/local/ssl-1.0.2 && make && make install
$ ldconfig
$ ldconfig -p | grep libcrypto
=> Apenas os arquivos 0.9.8 aparecem, então adiciono o caminho para os arquivos 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
E assim posso 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)
... e está corretamente vinculado ao 1.0.2.
2) No Debian Wheezy, com OpenSSL 1.0.1:
Mesmas etapas, resultados diferentes.
$ ./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
Da mesma forma, adiciono o caminho para 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
Aí eu tento 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)
Mas aqui não está vinculado ao 1.0.2. O caminho da biblioteca em tempo de compilação está correto (especificado com -L
, gcc
caso contrário falharia, pois algumas funções usadas prog
são específicas para 1.0.2), mas não o caminho em tempo de execução.
3) Como fazer funcionar no Wheezy
Com ou sem corrida 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)
Como alternativa, execute export LD_LIBRARY_PATH=/usr/local/ssl-1.0.2/lib
antes de executar o gcc
.
O que eu gostaria de saber
Usando LD_DEBUG=libs ./prog
conforme sugerido por mr.spuratic, descobri que os caminhos foram pesquisados em /etc/ld.so.cache
. Abri esse arquivo e descobri que a ordem em que .so é pesquisado corresponde à saída de ldconfig -p
.
Então a questão real é:
- Por que o arquivo 1.0.2 fica no topo da lista do ldconfig em 1) mas não em 2)? Pura aleatoriedade? Confusão devido aos arquivos 1.0.1 e 1.0.2 terem o mesmo sufixo? ("1.0.0")
Ou, dito de forma diferente,
- Por que os sinalizadores adicionados em 3) não são necessários em 1)?
Responder1
Há três coisas que você precisa cuidar ao compilar/vincular um pacote não padrão:
- cabeçalhos (geralmente
CFLAGS
) - caminho da biblioteca em tempo de compilação (geralmente
LDFLAGS
) - caminho da biblioteca em tempo de execução (caminhoatravés de
LDFLAGS
,LD_RUN_PATH
,LD_LIBRARY_PATH
ould.so.conf
)
Você não disse o que prog
é, então não sei quão bem comportada sua configuração pode ser (ou se usa autoconf?). Já vi muitos que executam apenas as duas primeiras etapas de maneira confiável.
Durante o estágio de link, a ordem do caminho da biblioteca é relevante, supondo que você esteja usando o conjunto de ferramentas GNU (gcc e binutils), você provavelmente poderá ver o que está acontecendo configurando CFLAGS
antes configure
(ou possível diretamente Makefile
):
export CFLAGS="-Wl,-t"
Isso passa a -t
opção de rastreamento para o vinculador. Você pode precisar adicionar V=1
ou VERBOSE=1
ao comando make se você obtiver apenas linhas concisas "CC" e "LD" durante o make.)
Em tempo de execução você pode ver o que ld.so
tenta definindo cuidadosamente LD_DEBUG
, por exemplo
LD_DEBUG=libs ./myprog
(ou tente valores de files
ou symbols
para mais detalhes)
Para especificar todos os três parâmetros corretamente no momento da construção, você deve ser capaz de fazer:
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"
em seguida, reconfigure/recompile.
Você está usando --openssldir
em vez do mais convencional --prefix
(eu recomendo o último e também usando apenas make install_sw
se você não precisar das cerca de 1000 páginas de manual e links simbólicos que uma instalação padrão oferece). Isso pode ser parte do problema. Por alguma razão, sabe-se que as bibliotecas .so que você mostra ld.so
não possuem um sufixo de versão (por exemplo, .so.1.0.2
), um " " adequado make install
deveria ter configurado isso para você (por meio do link-shared
destino no main Makefile
).
A -R
opção instrui o vinculador a incorporar um RPATH na saída executável da biblioteca OpenSSL específica para que ele não precise depender do padrão que o vinculador de tempo de execução ( ld.so
) normalmente forneceria. Você pode modificar binários existentes comchrpath
em vez de.
Isto é mais ou menos equivalente a exportar LD_LIBRARY_PATH=/usr/local/ssl-1.0.2/lib
. Você pode ler mais sobre RPATH e o RUNPATH relacionado aqui:http://blog.tremily.us/posts/rpath/
Como último recurso, você poderia construir OpenSSL sem "shared" ou com "noshared", isso fornecerá bibliotecas estáticas que não terão esse problema (mas podem ter outros problemas, por exemplo, para uso dentro do ELF .so, causando PIC /problemas de TORTA)
Com base nos detalhes atualizados, acredito que o problema é que 1.0.1 e 1.0.2beta definem o sufixo de versão .so (SONAME) como 1.0.0. No primeiro sistema com apenas 0.9.8 isso não causa problemas; no segundo, com 1.0.1 e 1.0.2, ambos versionados como 1.0.0, são "vitórias na primeira partida" com base na ld.so.{conf,d}
ordem. Lembre-se de que ld
o vinculador em tempo de compilação é um programa diferente do ld.so
vinculador em tempo de execução e pode ter um comportamento diferente (geralmente resultando em erros de símbolo ou pior, como você viu).
$ 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
Atualizar
OpenSSL-1.1 fez algumas alterações no nível da API, o código acima não será compilado com cabeçalhos v1.1 e bibliotecas mais antigas ( undefined reference to `OpenSSL_version'
).
SSLeay_version()
agora está obsoleto e (dependendo de OPENSSL_API_COMPAT
) pode ser #define
-d para a função de API adequada OpenSSL_version()
.
Responder2
O caminho da nova lib está em um dos arquivos dentro /etc/ld.so.conf.d
? Próxima execução: -
#ldconfig -v
para reconstruir o cache. Se você for rápido, deverá ver a nova lib na longa lista impressa (ou canalizá-la para grep
ou less
)
Talvez o caminho já estivesse no primeiro servidor, mas não no segundo?