Hands On: Shared Libraries no Linux

Bibliotecas compartilhadas são usadas em qualquer sistema operacional, seja Windows, Linux, Mac OSX ou qualquer *BSD ou *NIX. No Windows este recurso tem o nome de DLL (Dynamic-link Library) com sua extensão .dll, no Mac OSX tem a extensão .dylib  e nos outros sistemas UNIX-like(Linux, *BSD) este se chama Shared Object e tem extensão .so. Um entendimento básico sobre o funcionamento deste recurso ajuda muito usuários destes sistemas a resolverem possíveis problemas de falta de bibliotecas.

A nomenclatura de bibliotecas compartilhadas segue um padrão em cada sistema. Nos sistemas Linux o padrão é lib<nome_da_lib><.versão>.so. Podemos exemplificar esta convenção com a biblioteca pthreads no Linux. No Fedora, esta biblioteca está definida como libpthread-2.18.so, e um link libpthread.so para esta biblioteca.

Sobre a localização de bibliotecas compartilhadas, no Fedora as libs estão dentro de /usr/lib64. Em sistemas 32 bits, fica dentro de /usr/lib.

Para poder fazer uso de uma shared library utilizando o GCC no Linux basta adicionar um parâmetro -l<nome_da_biblioteca>. O prefixo lib neste caso deve ser removido. Para exemplificar o uso de uma biblioteca na compilação de um programa C/C++ temos o seguinte comando abaixo:

gcc fonte.c -o bin -lpthread

No comando acima estamos compilando o programa fonte chamado fonte.c, gerando um binário com o nome bin e estamos linkando com a biblioteca pthreads do Linux.

Para criar um biblioteca compartilhada é uma tarefa simples. Para iniciar uma biblioteca precisamos criar uma função que será utilizada em outros programas. Como exemplo, irei criar o arquivo soma.c e soma.h para criar uma biblioteca chamada libmatematica.so.

Os arquivos soma.h e soma.c são listados abaixo:

soma.c


int soma(int a, int b)

{
    return a + b;
}

soma.h


int soma(int a, int b);

A linha de comando abaixo compila o código fonte soma.c e cria a biblioteca libmatematica.so:


gcc -shared -Wall soma.c -o libmatematica.so

A opção -shared diz ao GCC que o binário compilado será uma biblioteca compartilhada. O arquivo soma.h será utilizada pelos programas que vão usar esta biblioteca. O programa a seguir, chamado main.c, vai utilizar a função soma da biblioteca que criamos:

#include <soma.h>
#include <stdio.h>

int main()
{
    printf("%dn", soma(1, 5));
    return 0;
}

Para compilar este programa usamos o comando abaixo:


gcc main.c -o main -lmatematica

Ao executar o binário main, obtemos como saída o número 6, número este que é retornado pela função soma da biblioteca libmatematica.so.

Podemos ainda verificar quais bibliotecas um binário específico utiliza. O comando ldd é o responsável por dar tal informação . O comando abaixo mostra quais as bibliotecas utilizadas no binário main recém gerado utiliza.


ldd main

Como saída eu obtive:

[[email protected] teste]$ ldd main
linux-vdso.so.1 => (0x00007fff339fe000)
libmatematica.so => /home/marcos/teste/libmatematica.so (0x00007fb8129fb000)
libc.so.6 => /lib64/libc.so.6 (0x00007fb812612000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb812bfe000)

Algumas observações sobre a biblioteca e o arquivo header. No include foi utilizado os símbolos “<” e “>” ao redor do nome do header. Quando usamos esta notação o compilador espera que o header este dentro da estrutura /usr/include. Para este teste eu movi o arquivo soma.h para lá. Sobre o parâmetro -l<nome_lib>, como já explicado antes, espera-se que a biblioteca esteja dentro de /usr/lib. Para este teste eu criei um link simbólico libmatematica.so dentro de /usr/lib e apontei para a pasta a pasta onde está a minha biblioteca.

Alguns link interessantes sobre o assunto:

http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html

http://linux.die.net/man/1/gcc

http://unixhelp.ed.ac.uk/CGI/man-cgi?ldd+1

Qualquer dúvida, crítica ou sugestão, basta colocar nos comentários! Não se esqueçam de se inscrever no nosso feed!  Até mais pessoal!

  • Bruno

    Ótimo artigo! Esse comando ldd eu não conhecia, muito útil!

    obs.: Quando mostra o arquivo soma.c esta repetindo o conteúdo do main.c

    • Marcos Paulo de Souza

      Ola Bruno! Verdade, ja ajustei! Obrigado por notar 🙂

  • Bruno

    Ótimo artigo! Esse comando ldd eu não conhecia, muito útil!

    obs.: Quando mostra o arquivo soma.c esta repetindo o conteúdo do main.c

    • Marcos Paulo de Souza

      Ola Bruno! Verdade, ja ajustei! Obrigado por notar 🙂

  • Bruno

    Ótimo artigo! Esse comando ldd eu não conhecia, muito útil!

    obs.: Quando mostra o arquivo soma.c esta repetindo o conteúdo do main.c

    • Marcos Paulo de Souza

      Ola Bruno! Verdade, ja ajustei! Obrigado por notar 🙂

  • Bem legal, mas dá para complementar… Poderia explicar também as opções -I (i maiúsculo) e -L do compilador… com -I, você pode colocar os arquivos .h em qualquer pasta, assim o compilador vai acrescentar aquele caminho na hora de procurar os cabeçalhos… por exemplo -I/opt/include… é possível acrescentar vários caminhos… com -L você especifica o diretório onde estão as bibliotecas compiladas… por ex, -L/opt/lib

    Importante:
    No Linux e no Mac, a princípio, os links com as bibliotecas são rígidos, ou seja, você não poderá mudá-las de lugar pois o executável não será capaz de localizá-las novamente. Veja as saídas do comando ldd (Linux) ou otool -L (Mac).

    No linux a única maneira que eu sei de se mudar o caminho onde um programa procura uma biblioteca é exportando a variável LD_LIBRARY_PATH=/caminho/desejado no momento em que se executa o programa (pode ser até dentro de um shell script).
    No Mac não há algo do tipo, mas é possível mudar o caminho de busca das bibliotecas dentro do executável e bibliotecas, utilizando o comando install_name_tool (opção -change).

    Outra possibilidade de se trabalhar é empacotar binário e bibliotecas num pacote único e instalar tudo em /opt ou outro diretório (mais ou menos como o OSX faz com os aplicativos .app). Neste caso, o caminho que o binário vai ter que ter para mapear as bibliotecas deve ser relativo.
    No Mac, ao se compilar uma dylib, acrescente a opção -install_name @loader_path/caminho/relativo/até/a/pasta/das/bibliotecas, onde @loader_path retorna o local do próprio binário que chama a biblioteca.
    No Linux é parecido, use -rpath,’$ORIGIN/../lib/dir/sub’ -Llib/dir/sub

    Por exemplo, minha estrutura se eu faço um aplicativo deste tipo (bundle):
    Linux
    App
    |-> bin (executáveis)
    |-> lib (bibliotecas)
    |->plugins (plugins)
    |->doc (documentação e ajuda)
    |->shared (outras coisas, traduções)

    Mac
    *.app
    |->Contents
    |-> Info.plist (arquivo XML)
    |-> PkgInfo (arquivo de texto)
    |-> MacOS (binários)
    |-> Library (bibliotecas)
    |-> Resources (plugins, documentação, ajuda, traduções, etc)

    Windows
    App
    |->*exe e *dll soltos na pasta raiz do aplicativo
    |-> plugins
    |-> doc (documentação)
    |-> help (ajuda)
    |-> i18n (traduções)

    No caso do Windows, se os dll estão na mesma pasta do executável, basta para que o aplicativo funcione.

    • Marcos Paulo de Souza

      Ola Vinicius,

      Achei bem interessante o seu comentario. De fato, não falei sobre o LD_LIBRARY_PATH por esquecimento mesmo. Minha idéia era inicialmente ensinar como se criar uma shared library para uso em outros programas de forma simples, mais como um “Shared Libraries for Dummies” mesmo 🙂

      Mas o seu comentário enriqueceu muito o post. Muito obrigado mesmo pelas informações. A parte do Mac OSX eu nem sabia 🙂

      Sobre a parte dos outros o flag -I eu nem escrevi pois achei que não era o foco do artigo, e sobre o -L, eu realmente esqueci de falar. Novamente, lhe agradeço muito pelo seu tempo gasto neste comentário para enriquecer o post.

  • Bem legal, mas dá para complementar… Poderia explicar também as opções -I (i maiúsculo) e -L do compilador… com -I, você pode colocar os arquivos .h em qualquer pasta, assim o compilador vai acrescentar aquele caminho na hora de procurar os cabeçalhos… por exemplo -I/opt/include… é possível acrescentar vários caminhos… com -L você especifica o diretório onde estão as bibliotecas compiladas… por ex, -L/opt/lib

    Importante:
    No Linux e no Mac, a princípio, os links com as bibliotecas são rígidos, ou seja, você não poderá mudá-las de lugar pois o executável não será capaz de localizá-las novamente. Veja as saídas do comando ldd (Linux) ou otool -L (Mac).

    No linux a única maneira que eu sei de se mudar o caminho onde um programa procura uma biblioteca é exportando a variável LD_LIBRARY_PATH=/caminho/desejado no momento em que se executa o programa (pode ser até dentro de um shell script).
    No Mac não há algo do tipo, mas é possível mudar o caminho de busca das bibliotecas dentro do executável e bibliotecas, utilizando o comando install_name_tool (opção -change).

    Outra possibilidade de se trabalhar é empacotar binário e bibliotecas num pacote único e instalar tudo em /opt ou outro diretório (mais ou menos como o OSX faz com os aplicativos .app). Neste caso, o caminho que o binário vai ter que ter para mapear as bibliotecas deve ser relativo.
    No Mac, ao se compilar uma dylib, acrescente a opção -install_name @loader_path/caminho/relativo/até/a/pasta/das/bibliotecas, onde @loader_path retorna o local do próprio binário que chama a biblioteca.
    No Linux é parecido, use -rpath,’$ORIGIN/../lib/dir/sub’ -Llib/dir/sub

    Por exemplo, minha estrutura se eu faço um aplicativo deste tipo (bundle):
    Linux
    App
    |-> bin (executáveis)
    |-> lib (bibliotecas)
    |->plugins (plugins)
    |->doc (documentação e ajuda)
    |->shared (outras coisas, traduções)

    Mac
    *.app
    |->Contents
    |-> Info.plist (arquivo XML)
    |-> PkgInfo (arquivo de texto)
    |-> MacOS (binários)
    |-> Library (bibliotecas)
    |-> Resources (plugins, documentação, ajuda, traduções, etc)

    Windows
    App
    |->*exe e *dll soltos na pasta raiz do aplicativo
    |-> plugins
    |-> doc (documentação)
    |-> help (ajuda)
    |-> i18n (traduções)

    No caso do Windows, se os dll estão na mesma pasta do executável, basta para que o aplicativo funcione.

    • Marcos Paulo de Souza

      Ola Vinicius,

      Achei bem interessante o seu comentario. De fato, não falei sobre o LD_LIBRARY_PATH por esquecimento mesmo. Minha idéia era inicialmente ensinar como se criar uma shared library para uso em outros programas de forma simples, mais como um “Shared Libraries for Dummies” mesmo 🙂

      Mas o seu comentário enriqueceu muito o post. Muito obrigado mesmo pelas informações. A parte do Mac OSX eu nem sabia 🙂

      Sobre a parte dos outros o flag -I eu nem escrevi pois achei que não era o foco do artigo, e sobre o -L, eu realmente esqueci de falar. Novamente, lhe agradeço muito pelo seu tempo gasto neste comentário para enriquecer o post.

  • Bem legal, mas dá para complementar… Poderia explicar também as opções -I (i maiúsculo) e -L do compilador… com -I, você pode colocar os arquivos .h em qualquer pasta, assim o compilador vai acrescentar aquele caminho na hora de procurar os cabeçalhos… por exemplo -I/opt/include… é possível acrescentar vários caminhos… com -L você especifica o diretório onde estão as bibliotecas compiladas… por ex, -L/opt/lib

    Importante:
    No Linux e no Mac, a princípio, os links com as bibliotecas são rígidos, ou seja, você não poderá mudá-las de lugar pois o executável não será capaz de localizá-las novamente. Veja as saídas do comando ldd (Linux) ou otool -L (Mac).

    No linux a única maneira que eu sei de se mudar o caminho onde um programa procura uma biblioteca é exportando a variável LD_LIBRARY_PATH=/caminho/desejado no momento em que se executa o programa (pode ser até dentro de um shell script).
    No Mac não há algo do tipo, mas é possível mudar o caminho de busca das bibliotecas dentro do executável e bibliotecas, utilizando o comando install_name_tool (opção -change).

    Outra possibilidade de se trabalhar é empacotar binário e bibliotecas num pacote único e instalar tudo em /opt ou outro diretório (mais ou menos como o OSX faz com os aplicativos .app). Neste caso, o caminho que o binário vai ter que ter para mapear as bibliotecas deve ser relativo.
    No Mac, ao se compilar uma dylib, acrescente a opção -install_name @loader_path/caminho/relativo/até/a/pasta/das/bibliotecas, onde @loader_path retorna o local do próprio binário que chama a biblioteca.
    No Linux é parecido, use -rpath,’$ORIGIN/../lib/dir/sub’ -Llib/dir/sub

    Por exemplo, minha estrutura se eu faço um aplicativo deste tipo (bundle):
    Linux
    App
    |-> bin (executáveis)
    |-> lib (bibliotecas)
    |->plugins (plugins)
    |->doc (documentação e ajuda)
    |->shared (outras coisas, traduções)

    Mac
    *.app
    |->Contents
    |-> Info.plist (arquivo XML)
    |-> PkgInfo (arquivo de texto)
    |-> MacOS (binários)
    |-> Library (bibliotecas)
    |-> Resources (plugins, documentação, ajuda, traduções, etc)

    Windows
    App
    |->*exe e *dll soltos na pasta raiz do aplicativo
    |-> plugins
    |-> doc (documentação)
    |-> help (ajuda)
    |-> i18n (traduções)

    No caso do Windows, se os dll estão na mesma pasta do executável, basta para que o aplicativo funcione.

    • Marcos Paulo de Souza

      Ola Vinicius,

      Achei bem interessante o seu comentario. De fato, não falei sobre o LD_LIBRARY_PATH por esquecimento mesmo. Minha idéia era inicialmente ensinar como se criar uma shared library para uso em outros programas de forma simples, mais como um “Shared Libraries for Dummies” mesmo 🙂

      Mas o seu comentário enriqueceu muito o post. Muito obrigado mesmo pelas informações. A parte do Mac OSX eu nem sabia 🙂

      Sobre a parte dos outros o flag -I eu nem escrevi pois achei que não era o foco do artigo, e sobre o -L, eu realmente esqueci de falar. Novamente, lhe agradeço muito pelo seu tempo gasto neste comentário para enriquecer o post.

  • demoncyber

    Uma sugestão para um novo post seria falar dos binários stripped, que apesar de maiores provêm uma vantagem de não ter problemas de dependências das outras bibliotecas. O tema vem sendo bastante conversado e discutido, principalmente da reclamação do Linus Towarld sobre o caso. É interessante ver um conceito similar sendo adotado pela distribuição do gobolinux, o pc-bsd com seus pbi, e Mac OS X. Claro que diferente de um binário stripped eles muitas vezes separam as bibliotecas em uma estrutura de pasta própria de acesso da aplicação.
    Este conceito é uma abordagem interessante, para resolver os problemas de dependências em um contexto onde espaço em disco não é mais um problema, claro que uma abordagem destas gera outros complicadores, pois o fluxo de dependências e atualizações ficam a cargo da aplicação.. o melhor dos mundos seria uma abordagem mista.
    Os complicadores também de garantias do binário ser integro no sentido de não conter malwares é algo complicado, não temos como ter um mundo totalmente seguro, mas esta estrutura poderia vir junto com enjaulamento de aplicações que são tão em moda ….

    XP é um tema muito interessante a estudar e conversar …

    E olha que ainda nem abordamos os apk .. .xD

    Abraços .. fica a sugestão

    • Marcos Paulo de Souza

      Claro, podemos fazer posts sobre as libs stripped sim! Podemos usar a Steam como um “case de sucesso” das libs stripped e citar o Linus nesse post 🙂

      Obrigado pela sugestão!

  • demoncyber

    Uma sugestão para um novo post seria falar dos binários stripped, que apesar de maiores provêm uma vantagem de não ter problemas de dependências das outras bibliotecas. O tema vem sendo bastante conversado e discutido, principalmente da reclamação do Linus Towarld sobre o caso. É interessante ver um conceito similar sendo adotado pela distribuição do gobolinux, o pc-bsd com seus pbi, e Mac OS X. Claro que diferente de um binário stripped eles muitas vezes separam as bibliotecas em uma estrutura de pasta própria de acesso da aplicação.
    Este conceito é uma abordagem interessante, para resolver os problemas de dependências em um contexto onde espaço em disco não é mais um problema, claro que uma abordagem destas gera outros complicadores, pois o fluxo de dependências e atualizações ficam a cargo da aplicação.. o melhor dos mundos seria uma abordagem mista.
    Os complicadores também de garantias do binário ser integro no sentido de não conter malwares é algo complicado, não temos como ter um mundo totalmente seguro, mas esta estrutura poderia vir junto com enjaulamento de aplicações que são tão em moda ….

    XP é um tema muito interessante a estudar e conversar …

    E olha que ainda nem abordamos os apk .. .xD

    Abraços .. fica a sugestão

    • Marcos Paulo de Souza

      Claro, podemos fazer posts sobre as libs stripped sim! Podemos usar a Steam como um “case de sucesso” das libs stripped e citar o Linus nesse post 🙂

      Obrigado pela sugestão!

  • demoncyber

    Uma sugestão para um novo post seria falar dos binários stripped, que apesar de maiores provêm uma vantagem de não ter problemas de dependências das outras bibliotecas. O tema vem sendo bastante conversado e discutido, principalmente da reclamação do Linus Towarld sobre o caso. É interessante ver um conceito similar sendo adotado pela distribuição do gobolinux, o pc-bsd com seus pbi, e Mac OS X. Claro que diferente de um binário stripped eles muitas vezes separam as bibliotecas em uma estrutura de pasta própria de acesso da aplicação.
    Este conceito é uma abordagem interessante, para resolver os problemas de dependências em um contexto onde espaço em disco não é mais um problema, claro que uma abordagem destas gera outros complicadores, pois o fluxo de dependências e atualizações ficam a cargo da aplicação.. o melhor dos mundos seria uma abordagem mista.
    Os complicadores também de garantias do binário ser integro no sentido de não conter malwares é algo complicado, não temos como ter um mundo totalmente seguro, mas esta estrutura poderia vir junto com enjaulamento de aplicações que são tão em moda ….

    XP é um tema muito interessante a estudar e conversar …

    E olha que ainda nem abordamos os apk .. .xD

    Abraços .. fica a sugestão

    • Marcos Paulo de Souza

      Claro, podemos fazer posts sobre as libs stripped sim! Podemos usar a Steam como um “case de sucesso” das libs stripped e citar o Linus nesse post 🙂

      Obrigado pela sugestão!