paulo1205
(usa Ubuntu)
Enviado em 24/11/2014 - 19:11h
A carga de bibliotecas dinâmicas para uso por um programa é feita por um utilitário chamado ld.so. O ld.so pode buscar bibliotecas utilizando informações contidas no próprio programa que está começando a executar ou a partir de uma lista de locais especificados em arquivos de configuração comuns a qualquer programa, ou em algumas variáveis de ambiente.
Para embutir informações dentro do executável, você passa um parâmetro adicional ao linker, chamado
-rpath. Se você utilizar o GCC como front-end para o linker, ele assume a forma
-Wl,-rpath. Por exemplo:
gcc my_prog.c -L/some/library/directory -lmy_lib -Wl,-rpath=/some/library/directory -o my_prog
Explicando:
-L indica “/some/library/directory” como caminho que o linker deve usar para encontrar a biblioteca “libmy_lib.so” (indicado por “
-lmy_lib”) durante o momento de execução do próprio linker. Já a opção
-rpath embute o caminho “/some/library/directory” dentro do executável, para que o ld.so o empregue para encontrar a biblioteca, independentemente da configuração do sistema.
O ld.so admite alguma mobilidade sobe a opção
-rpath. Por exemplo, se eu usar o texto “$ORIGIN” como parte do caminho, o ld.so vai entender como sendo o mesmo caminho onde o executável estava quando foi executado. Se eu gerar o executável usando o comando
gcc my_prog.c -L/some/library/directory -lmy_lib -Wl,-rpath='$ORIGIN' -o my_prog
e copiar o programa gerado para “/home/paulo/bin” e tentar executá-lo a partir desse caminho, o ld.so vai esperar que a biblioteca “libmy_lib.so” também resida em “/home/paulo/bin”.
Eu poderia também fazer uma composição, usando nomes relativos.
gcc my_prog.c -L/some/library/directory -lmy_lib -Wl,-rpath='$ORIGIN'/../lib -o my_prog
Desse modo, se eu instalar o programa “my_prog” em “/my/programs/bin” e mandar executá-lo a partir dali, o ld.so vai esperar encontrar a biblioteca em “/my/programs/lib”.
(Nos comandos de compilação acima, eu coloquei “$ORIGIN” entre apóstrofos para que o sinal “$” seja passado literalmente para o linker, em lugar de deixar o shell tentar expandir o valor de uma possível variável do próprio shell chamada “ORIGIN”.)
Se você não usar a opção
-rapth, no momento em que o programa for executado o ld.so vai buscar as bibliotecas em diretórios designados num arquivo de configuração do sistema (a saber: /etc/ld.so.cache, que é, por sua vez, “compilado” pelo utilitário ldconfig a partir do arquivo “/etc/ld.so.conf” e/ou arquivos em “/etc/ld.so.cond.d/”). Nesse caso, a biblioteca libmy_lib.so teria de ter sido copiada para um dos diretórios especificados nos arquivos de configuração.
É possível ainda usar variáveis de ambiente como
LD_LIBRARY_PATH ou
LD_PRELOAD para acrescentar caminhos à lista de caminhos a serem buscados ou alterar o comportamento do ld.so.
Recomendo consultar as manpages de ld.so e ldconfig para mais detalhes.
Quando usar cada uma dessas formas?
Sem dúvida, a forma que dá mais controle para quem está gerando a aplicação é a de embutir o caminho para a biblioteca no executável, por meio de
-rpath passado ao linker. O uso de formas envolvendo “$ORIGIN” facilita o isolamento da aplicação de outras, desde que ela seja instalada num diretório (ou ramo de diretórios) próprio, que não seja compartilhado por outras aplicações (ou versões diferentes da mesma aplicação). A mobilidade da aplicação também fica muito fácil, simplificando o processo de instalação (e.g. aplicações do tipo “portable”).
Deixar a cargo do “/etc/ld.so.cache” joga a responsabilidade no colo do administrador da máquina, que tem de garantir a instalação da biblioteca em pelo menos (e, de preferência, apenas) um diretório daquela lista e, ao mesmo tempo, administrar potenciais conflitos de versões diferentes da mesma biblioteca ou até mesmo casos raros de bibliotecas com funções distintas, mas nomes idênticos. O uso de gestores de pacotes (RPM, DPKG etc.) feitos pelo mesmo fabricante (ou um número pequeno deles, e de preferência homologados pelo fornecedor do SO) pode ajudar nesse trabalho. Mas se aparecer alguma (ou mais de uma) aplicação que tenha de ser manualmente compilada e instalada, podem começar a surgir complicações. Mais ainda se ela (ou elas) tiver de ser frequentemente atualizada.
Já as variáveis de ambiente abrem espaço para escolhas de bibliotecas pelo próprio usuário. Isso pode ser utilizado para o bem (por exemplo depuração de problemas ou atualizações parciais, quando desejáveis) ou para o mal (por exemplo: atualizações parciais indesejáveis ou tentativas de burlar a segurança ou subverter o funcionamento normal de uma aplicação). Um caso particular de uso é o do próprio desenvolvedor, que é um usuário de sua aplicação durante o desenvolvimento, e pode querer alternar entre versões diferentes da biblioteca durante o desenvolvimento, sem necessariamente ter de ficar a todo momento re-linkando a aplicação com as diferentes versões da biblioteca.