102.3 Lição 1
Certificação: |
LPIC-1 |
---|---|
Versão: |
5.0 |
Tópico: |
102 Instalação do Linux e gerenciamento de pacotes |
Objetivo: |
102.3 Gerenciar bibliotecas compartilhadas |
Lição: |
1 de 1 |
Introdução
Nesta lição, trataremos das bibliotecas compartilhadas, também conhecidas como objetos compartilhados: trechos de código compilado e reutilizável, como funções ou classes, usados de forma recorrente por diversos programas.
Para começar, explicaremos o que são bibliotecas compartilhadas, como identificá-las e onde são encontradas. A seguir, veremos como configurar seus locais de armazenamento. Por fim, mostraremos como procurar pelas bibliotecas compartilhadas das quais depende um determinado programa.
O que são bibliotecas compartilhadas
As bibliotecas de software são coleções de código que podem ser usadas por vários programas diferentes, assim como as bibliotecas físicas permitem que livros e outros recursos sejam usados por várias pessoas diferentes.
Há duas etapas importantes na criação de um arquivo executável a partir do código fonte de um programa. Primeiro, o compilador transforma o código fonte em código de máquina, que é armazenado nos chamados arquivos-objeto. Em segundo lugar, o vinculador (linker) combina os arquivos-objeto e os vincula às bibliotecas para gerar o arquivo executável final. Essa ligação pode ser feita de maneira estática ou dinâmica. Dependendo do método escolhido, falaremos em bibliotecas estáticas ou, no caso de um vínculo dinâmico, em bibliotecas compartilhadas. Vamos explicar as diferenças entre elas.
- Bibliotecas estáticas
-
Uma biblioteca estática é mesclada com o programa no momento do vínculo. Uma cópia do código da biblioteca é incorporada ao programa e se torna parte dele. Portanto, o programa não tem dependências na biblioteca em tempo de execução, pois já contém o código da biblioteca. Não ter dependências pode ser visto como uma vantagem, pois não precisamos nos preocupar em garantir que as bibliotecas usadas estejam sempre disponíveis. Pelo lado negativo, os programas vinculados estaticamente são mais pesados.
- Bibliotecas compartilhadas (ou dinâmicas)
-
No caso de bibliotecas compartilhadas, o vinculador simplesmente cuida para que o programa faça referência às bibliotecas corretamente. No entanto, o vinculador não copia nenhum código da biblioteca para o arquivo do programa. Assim, a biblioteca compartilhada deve estar disponível em tempo de execução para satisfazer as dependências do programa. Essa é uma abordagem econômica para gerenciar recursos do sistema, pois ajuda a reduzir o tamanho dos arquivos de programas; apenas uma cópia da biblioteca é carregada na memória, mesmo quando ela é usada por vários programas.
Convenções de nomenclatura para arquivos-objeto compartilhados
O nome de uma biblioteca compartilhada, também chamado de soname, segue um padrão composto por três elementos:
-
Nome da biblioteca (normalmente com o prefixo
lib
) -
so
(que significa “shared object”, ou objeto compartilhado) -
Número de versão da biblioteca
Eis um exemplo: libpthread.so.0
Em comparação, os nomes das bibliotecas estáticas terminam em .a
, por exemplo libpthread.a
.
Note
|
Como os arquivos que contêm bibliotecas compartilhadas precisam estar disponíveis quando o programa é executado, a maioria dos sistemas Linux já contém bibliotecas compartilhadas. Uma vez que as bibliotecas estáticas são necessárias apenas em um arquivo dedicado quando um programa é vinculado, elas podem não estar presentes no sistema do usuário final. |
A glibc
(biblioteca GNU C) é um bom exemplo de biblioteca compartilhada. Em um sistema Debian GNU/Linux 9.9, seu arquivo se chama libc.so.6
. Esses nomes de arquivo mais gerais normalmente são links simbólicos apontando para o arquivo real que contém uma biblioteca, cujo nome contém o número exato da versão. No caso da glibc
, esse link simbólico é assim:
$ ls -l /lib/x86_64-linux-gnu/libc.so.6 lrwxrwxrwx 1 root root 12 feb 6 22:17 /lib/x86_64-linux-gnu/libc.so.6 -> libc-2.24.so
Essa maneira de usar nomes de arquivos mais gerais para fazer referência a arquivos de bibliotecas compartilhadas nomeados por uma versão específica é uma prática comum.
Outros exemplos de bibliotecas compartilhadas incluem libreadline
(que permite aos usuários editar linhas de comando à medida que são digitadas e inclui suporte para os modos de edição Emacs e vi), libcrypt
(que contém funções relacionadas à criptografia, hash e codificação), ou libcurl
(que é uma biblioteca multiprotocolo de transferência de arquivos).
Estes são os locais comuns para bibliotecas compartilhadas em um sistema Linux:
-
/lib
-
/lib32
-
/lib64
-
/usr/lib
-
/usr/local/lib
Note
|
O conceito de bibliotecas compartilhadas não é exclusivo do Linux. No Windows, por exemplo, elas são chamadas de DLL, que significa bibliotecas de vínculo dinâmico. |
Configuração dos caminhos da biblioteca compartilhada
As referências contidas nos programas vinculados dinamicamente são resolvidas pelo vinculador dinâmico (ld.so
ou ld-linux.so
) quando o programa é executado. O vinculador dinâmico busca por bibliotecas em uma série de diretórios. Esses diretórios são especificados pelo caminho da biblioteca. O caminho da biblioteca é configurado no diretório /etc
, especificamente no arquivo /etc/ld.so.conf
e, o que atualmente é mais comum, nos arquivos do diretório /etc/ld.so.conf.d
. Normalmente, o primeiro inclui uma única linha include
para os arquivos *.conf
do segundo:
$ cat /etc/ld.so.conf include /etc/ld.so.conf.d/*.conf
O diretório /etc/ld.so.conf.d
contém arquivos *.conf
:
$ ls /etc/ld.so.conf.d/ libc.conf x86_64-linux-gnu.conf
Esses arquivos *.conf
devem incluir os caminhos absolutos para os diretórios da biblioteca compartilhada:
$ cat /etc/ld.so.conf.d/x86_64-linux-gnu.conf # Multiarch support /lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu
O comando ldconfig
trata de ler esses arquivos de configuração, criando o conjunto de links simbólicos anteriormente mencionados que ajudam a localizar as bibliotecas individuais, e, por fim, de atualizar o arquivo de cache /etc/ld.so.cache
. Assim, o ldconfig
deve ser executado sempre que atualizarmos ou adicionarmos arquivos de configuração.
Eis algumas opções úteis para o ldconfig
:
-v
,--verbose
-
Exibe os números da versão da biblioteca, o nome de cada diretório e os vínculos criados:
$ sudo ldconfig -v /usr/local/lib: /lib/x86_64-linux-gnu: libnss_myhostname.so.2 -> libnss_myhostname.so.2 libfuse.so.2 -> libfuse.so.2.9.7 libidn.so.11 -> libidn.so.11.6.16 libnss_mdns4.so.2 -> libnss_mdns4.so.2 libparted.so.2 -> libparted.so.2.0.1 (...)
Assim, podemos ver, por exemplo, como
libfuse.so.2
está vinculado ao arquivo-objeto compartilhado reallibfuse.so.2.9.7
. -p
,--print-cache
-
Exibe as listas de diretórios e bibliotecas candidatas armazenadas na cache atual:
$ sudo ldconfig -p 1094 libs found in the cache `/etc/ld.so.cache' libzvbi.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzvbi.so.0 libzvbi-chains.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzvbi-chains.so.0 libzmq.so.5 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzmq.so.5 libzeitgeist-2.0.so.0 (libc6,x86-64) => /usr/lib/x86_64-linux-gnu/libzeitgeist-2.0.so.0 (...)
Note como a cache emprega o soname completo dos vínculos:
$ sudo ldconfig -p |grep libfuse libfuse.so.2 (libc6,x86-64) => /lib/x86_64-linux-gnu/libfuse.so.2
Se fizermos a lista longa de /lib/x86_64-linux-gnu/libfuse.so.2
, encontraremos a referência ao arquivo-objeto compartilhado real libfuse.so.2.9.7
, que fica armazenado no mesmo diretório:
$ ls -l /lib/x86_64-linux-gnu/libfuse.so.2 lrwxrwxrwx 1 root root 16 Aug 21 2018 /lib/x86_64-linux-gnu/libfuse.so.2 -> libfuse.so.2.9.7
Note
|
Como ele exige acesso de escrita a |
Além dos arquivos de configuração descritos acima, a variável de ambiente LD_LIBRARY_PATH
pode ser usada para adicionar temporariamente novos caminhos para bibliotecas compartilhadas. Ele é composto por um conjunto de diretórios separados por dois pontos (:
) no qual as bibliotecas são buscadas. Assim, para adicionar /usr/local/mylib
ao caminho da biblioteca na sessão atual do shell, poderíamos digitar:
$ LD_LIBRARY_PATH=/usr/local/mylib
Em seguida podemos verificar o valor:
$ echo $LD_LIBRARY_PATH /usr/local/mylib
Para adicionar /usr/local/mylib
ao caminho da biblioteca na sessão atual do shell e exportá-lo para todos os processos secundários originados desse shell, escreveríamos:
$ export LD_LIBRARY_PATH=/usr/local/mylib
Para remover a variável de ambiente LD_LIBRARY_PATH
, basta digitar:
$ unset LD_LIBRARY_PATH
Se quiser tornar as alterações permanentes, escrevemos a linha
export LD_LIBRARY_PATH=/usr/local/mylib
Em um dos scripts de inicialização do Bash, como /etc/bash.bashrc
ou ~/.bashrc
.
Note
|
|
Buscando pelas dependências de um executável específico
Para buscar as bibliotecas compartilhadas requeridas por um programa específico, use o comando ldd
seguido do caminho absoluto para o programa. A saída mostra o caminho do arquivo da biblioteca compartilhada, bem como o endereço de memória hexadecimal no qual ele é carregado:
$ ldd /usr/bin/git linux-vdso.so.1 => (0x00007ffcbb310000) libpcre.so.3 => /lib/x86_64-linux-gnu/libpcre.so.3 (0x00007f18241eb000) libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f1823fd1000) libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x00007f1823db6000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f1823b99000) librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f1823991000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f18235c7000) /lib64/ld-linux-x86-64.so.2 (0x00007f182445b000)
Da mesma forma, usamos ldd
para procurar as dependências de um objeto compartilhado:
$ ldd /lib/x86_64-linux-gnu/libc.so.6 /lib64/ld-linux-x86-64.so.2 (0x00007fbfed578000) linux-vdso.so.1 (0x00007fffb7bf5000)
Com a opção -u
(ou --unused
), o ldd
imprime as dependências diretas não utilizadas (se existirem):
$ ldd -u /usr/bin/git Unused direct dependencies: /lib/x86_64-linux-gnu/libz.so.1 /lib/x86_64-linux-gnu/libpthread.so.0 /lib/x86_64-linux-gnu/librt.so.1
A razão para haver dependências não utilizadas está relacionada às opções usadas pelo vinculador ao criar o binário. Embora o programa não precise de uma biblioteca não utilizada, ela ainda estava vinculada e rotulada como NEEDED
(necessária) nas informações sobre o arquivo-objeto. Para investigar isso, podemos usar comandos como readelf
ou objdump
, que serão necessários mais adiante, nos exercícios exploratórios.
Exercícios Guiados
-
Divida os seguintes nomes de bibliotecas compartilhadas nas partes que os constituem:
Nome completo do arquivo nome da biblioteca sufixo so Número da versão linux-vdso.so.1
libprocps.so.6
libdl.so.2
libc.so.6
libsystemd.so.0
ld-linux-x86-64.so.2
-
Você desenvolveu um programa e deseja adicionar um novo diretório de biblioteca compartilhada em seu sistema (
/opt/lib/mylib
). Você escreve o caminho absoluto em um arquivo chamadomylib.conf
.-
Em que diretório deve ser armazenado esse arquivo?
-
Qual comando deve ser executado para que as alterações sejam totalmente efetivas?
-
-
Qual comando você usaria para listar as bibliotecas compartilhadas exigidas por
kill
?
Exercícios Exploratórios
-
objdump
é um utilitário de linha de comando que exibe informações sobre arquivos objeto. Confira se ele está instalado em seu sistema comwhich objdump
. Se não estiver, instale-o agora.-
Use
objdump
com-p
(ou--private-headers
) egrep
para exibir as dependências deglibc
: -
Use
objdump
com-p
(ou--private-headers
) egrep
para exibir o soname deglibc
: -
Use
objdump
com-p
(ou--private-headers
) egrep
para exibir as dependências do Bash:
-
Resumo
Nesta lição, você aprendeu:
-
O que é uma biblioteca compartilhada (ou dinâmica).
-
As diferenças entre bibliotecas compartilhadas e estáticas.
-
Os nomes das bibliotecas compartilhadas (sonames).
-
Os locais de preferência para as bibliotecas compartilhadas em um sistema Linux, como
/lib
ou/usr/lib
. -
A finalidade do vinculador dinâmico
ld.so
(ould-linux.so
). -
Como configurar caminhos para as bibliotecas compartilhadas usando arquivos em
/etc/
comold.so.conf
ou os arquivos do diretóriold.so.conf.d
. -
Como configurar caminhos para as bibliotecas compartilhadas usando a variável de ambiente
LD_LIBRARY_PATH
. -
Como buscar por executáveis e dependências de bibliotecas compartilhadas.
Comandos usados nesta lição:
ls
-
Lista o conteúdo do diretório.
cat
-
Concatena arquivos e exibe na saída padrão.
sudo
-
Permite que o superusuário execute o comando com privilégios administrativos.
ldconfig
-
Configura as ligações de tempo de execução do vinculador dinâmico.
echo
-
Exibe o valor da variável de ambiente.
export
-
Exporta o valor da variável de ambiente para shells secundários.
unset
-
Remove a variável de ambiente.
ldd
-
Exibe as dependências de objetos compartilhados de um programa.
readelf
-
Exibe informações sobre arquivos ELF (ELF significa formato executável e vinculável).
objdump
-
Exibe informações de arquivos de objetos.
Respostas aos Exercícios Guiados
-
Divida os seguintes nomes de bibliotecas compartilhadas nas partes que os constituem:
Nome completo do arquivo nome da biblioteca sufixo so Número da versão linux-vdso.so.1
linux-vdso
so
1
libprocps.so.6
libprocps
so
6
libdl.so.2
libdl
so
2
libc.so.6
libc
so
6
libsystemd.so.0
libsystemd
so
0
ld-linux-x86-64.so.2
ld-linux-x86-64
so
2
-
Você desenvolveu um programa e deseja adicionar um novo diretório de biblioteca compartilhada em seu sistema (
/opt/lib/mylib
). Você escreve o caminho absoluto em um arquivo chamadomylib.conf
.-
Em que diretório deve ser armazenado esse arquivo?
/etc/ld.so.conf.d
-
Qual comando deve ser executado para que as alterações sejam totalmente efetivas?
ldconfig
-
-
Qual comando você usaria para listar as bibliotecas compartilhadas exigidas por
kill
?ldd /bin/kill
Respostas aos Exercícios Exploratórios
-
objdump
é um utilitário de linha de comando que exibe informações sobre arquivos objeto. Confira se ele está instalado em seu sistema comwhich objdump
. Se não estiver, instale-o agora.-
Use
objdump
com-p
(ou--private-headers
) egrep
para exibir as dependências deglibc
:objdump -p /lib/x86_64-linux-gnu/libc.so.6 | grep NEEDED
-
Use
objdump
com-p
(ou--private-headers
) egrep
para exibir o soname deglibc
:objdump -p /lib/x86_64-linux-gnu/libc.so.6 | grep SONAME
-
Use
objdump
com-p
(ou--private-headers
) egrep
para exibir as dependências do Bash:objdump -p /bin/bash | grep NEEDED
-