Entre os dois formatos binários incompatíveis, a distinção de bibliotecas estáticas versus dinâmicas, e a sobrecarga do verbo `link' significar tanto `o que acontece após a compilação' como `o que acontece quando um programa compilado é chamado' (e, realmente, a sobrecarga da palavra `carregar' em um sentido comparável mas oposto), esta seção é complicada. Pouco dela, é muito mais complicada do aquela sentença, entretanto, de modo que não se preocupe muito com isso.
Para aliviar um pouco a confusão, nós referenciamos o que acontece em tempo de execução como 'carregamento dinâmico' e discorremos sobre ele na próxima seção. Você também o verá descrito como `linkagem dinâmica', mas não aqui. Esta seção, então, é dedicada exclusivamente ao tipo de linkagem que acontece no fim de uma compilação.
O último estágio da construção de um programa é `linká-lo'; reunir todas as suas partes e ver o que está faltando. Obviamente existem algumas coisas que muitos programas vão querer fazer --- abrir arquivos, por exemplo, e as peças que efetuam essas coisas são fornecidas para você na forma de bibliotecas. Em um sistema Linux comum, estas podem ser encontradas em /lib e /usr/lib/, entre outros lugares.
Ao usar uma biblioteca estática, o linker encontra as partes que os módulos do programa precisam, e as copia fisicamente no arquivo de saída executável que ele gera. Para bibliotecas compartilhadas, não --- ao invés disso, ele deixa uma nota na saída dizendo `quando este programa for executado, ele terá de carregar primeiro esta biblioteca'. Obviamente bibliotecas compartilhadas tendem a gerar executáveis menores; eles também usam menos memória e significa que menos espaço em disco é usado. O comportamento padrão do Linux é linkar de forma compartilhada se ele pode encontrar as bibliotecas compartilhadas, ou estaticamente em caso contrário. Se você está obtendo binários estáticos quando queria compartilhados, verifique se os arquivos das bibliotecas compartilhadas (*.sa para a.out, *.so para ELF) estão onde eles deviam estar, e podem ser lidos.
No Linux, bibliotecas estáticas tem nomes como libname.a, enquanto que bibliotecas compartilhadas são chamadas libname.so.x.y.z onde x.y.z é alguma forma de número de versão. Bibliotecas compartilhadas frequentemente também tem links apontando para elas, que são importantes, e (em configurações a.out) arquivos .sa associados. As bibliotecas padrão vêm em ambos os formatos, compartilhado e estático.
Você pode descobrir quais bibliotecas compartilhadas um programa requer usando ldd (List Dynamic Dependencies - Listar Dependências Dinâmicas)
$ ldd /usr/bin/lynx
libncurses.so.1 => /usr/lib/libncurses.so.1.9.6
libc.so.5 => /lib/libc.so.5.2.18
|
Isto mostra que no meu sistema o browser da WWW 'lynx' depende da presença de libc.so.5 (a biblioteca C) e libncurses.so.1 (usada para controle de terminal). Se um programa não tem dependências, ldd irá dizer `statically linked' ou `statically linked (ELF)'.
nm nomebiblioteca deve listar todos os símbolos para os quais nomebiblioteca tem referências. Ele funciona tanto em bibliotecas estáticas como compartilhadas. Suponha que você quer saber onde tcgetattr() está definida: você poderia fazer
$ nm libncurses.so.1 |grep tcget
U tcgetattr
|
O U quer dizer `undefined' --- ele mostra que a biblioteca ncurses usa mas não define-o. Você poderia também fazer
$ nm libc.so.5 | grep tcget
00010fe8 T __tcgetattr
00010fe8 W tcgetattr
00068718 T tcgetpgrp
|
O `W' quer dizer `weak', o que significa que o símbolo está definido, mas de tal forma que ele pode ser sobreescrito por outra definição em uma biblioteca diferente. Uma definição `normal' direta (tal como aquela para tcgetpgrp) é marcada por um `T'
A resposta curta para a questão no título, por falar nisso, é libm.(so|a). Todas as funções definidas em <math.h> são mantidas na biblioteca maths; assim você precisa linkar com -lm quando usar qualquer uma delas.
ld: Output file requires shared library `libfoo.so.1`
A estratégia de procura de arquivos do ld e amigos varia de acordo com a versão, mas o único padrão que você pode razoavelmente assumir é /usr/lib. Se você quer que bibliotecas em outros lugares sejam procuradas, especifique os seus diretórios com a opção -L para gcc ou ld.
Se aquilo não ajudar, verifique se você tem o arquivo correto naquele lugar. Para a.out, linkar com -lfoo faz ld procurar por libfoo.sa (stubs compartilhados), e se não for bem-sucedido, por libfoo.a (estático). Para ELF, ele procura por libfoo.so e depois por libfoo.a. libfoo.so é usualmente um link simbólico para libfoo.so.x.
Como qualquer outro programa, bibliotecas tendem a possuir bugs que são corrigidos com o tempo. Elas também podem introduzir novas características, alterar o efeito de características existentes, ou remover outras antigas. Isto poderia ser um problema para programas que as utilizam; e se ele estava dependendo daquela característica antiga?
Assim, nós apresentamos o controle de versão de bibliotecas. Nós categorizamos as mudanças que podem ser feitas às bibliotecas como `menor' ou `maior', e nós estabelecemos que uma mudança `menor' não pode danificar programas antigos que estão usando a biblioteca. Você pode dizer qual é a versão de uma biblioteca olhando o nome do arquivo (na verdade, isto é, a rigor, uma mentira para ELF; continue lendo para descobrir porque) : libfoo.so.1.2 tem versão maior 1, versão menor 2. O número da versão menor pode ser mais ou menos qualquer coisa --- libc adiciona um `nível de correção' a ele, gerando nomes de bibliotecas como libc.so.5.2.18, e também é razoável incluir letras, sublinhados, ou mais ou menos qualquer caracter ASCII visível.
Uma das maiores diferenças entre os formatos ELF e a.out está na construção das bibliotecas compartilhadas. Nos olhamos ELF primeiro, porque ele é mais simples.
ELF (Executable and Linking Format - Formato Executável e de Linkagem) é um formato binário originalmente desenvolvido por USL (Unix System Laboratories) e atualmente usado no Solaris e System V Release 4. Devido à sua maior flexibilidade em relação ao formato mais antigo a.out, que o Linux estava usando, os desenvolvedores do GCC e da biblioteca C decidiram, no ano passado, iniciar o uso de ELF como o formato binário padrão do Linux também.
Esta seção vem do documento '/news-archives/comp.sys.sun.misc'.
"ELF ("Executable Linking Format) é o formato "novo, melhorado" de arquivos objeto introduzido com o SVR4. ELF é muito mais poderoso do que COFF simples, em função de que ele *é* extensível pelo usuário. ELF vê um arquivo-objeto como uma lista arbitrariamente longa de seções (no lugar de uma matriz de entidades de tamanho fixo), estas seções, ao contrário de COFF, não TEM de estar em um certo lugar e não TEM de vir em qualquer ordem específica, etc. Usuários podem adicionar novas seções a arquivos-objeto se eles desejam armazenar novos dados. ELF também tem um formato de depuração muito mais poderoso chamado DWARF (Debugging With Attribute Record Format - Depuração Com Formato de Registro de Atributos) - atualmente não suportado plenamente no linux (mas o trabalho está progredindo). Uma lista ligada de DWARF DIEs (Debugging Information Entries - Entradas de Informação de Depuração) forma a seção .debug no ELF. Ao invés de ser uma coleção de pequenos registros de informação de tamanho fixo, cada DWARF DIE contém uma lista arbitrariamente longa de atributos complexos e são escritos como uma árvore de dados do programa, com base no seu escopo. Os DIEs podem armazenar uma grande quantidade de informação que a seção .debug do COFF simplesmente não suportava (como grafos de herança de C++ etc.)." "Arquivos ELF são acessados através da biblioteca de acesso a ELF do SVR4 (Solaris 2.0 ?), a qual fornece uma interface simples e fácil para as partes mais desagradáveis do ELF. Uma das maiores utilidades do uso da biblioteca de acesso a ELF é que você nunca terá de olhar para um arquivo ELF como um arquivo UNIX, ele é acessado como um Elf *, após uma chamada a elf_open() e daí em diante, você executa chamadas a elf_foobar() sobre os seus componentes ao invés de lidar diretamente com a sua imagem em disco (algo que muitos usuários de COFF fizeram com impunidade). "
A discussão a favor/contra ELF, e as contorções necessárias para atualizar um sistema a.out para suportá-lo, são cobertas no COMO FAZER ELF e eu não proponho copiá-las aqui. O COMO FAZER deve estar disponível no mesmo lugar onde você encontrou este.
Para construir libfoo.so como uma biblioteca compartilhada, os pasos básicos parecem com isto:
$ gcc -fPIC -c *.c
$ gcc -shared -Wl,-soname,libfoo.so.1 -o libfoo.so.1.0 *.o
$ ln -s libfoo.so.1.0 libfoo.so.1
$ ln -s libfoo.so.1 libfoo.so
$ LD_LIBRARY_PATH=`pwd`:$LD_LIBRARY_PATH ; export LD_LIBRARY_PATH
|
Isto irá gerar uma biblioteca compartilhada chamada libfoo.so.1.0, e os links apropriados para que ld (libfoo.so) e o carregador dinâmico (libfoo.so.1) a encontrem. Para testar, nós adicionamos o diretório corrente a LD_LIBRARY_PATH.
Quando você estiver satisfeito com o funcionamento da biblioteca, você terá que movê-la para, digamos, /usr/local/lib, e recriar os links apropriados. O link de libfoo.so.1 para libfoo.so.1.0 é mantido atualizado por ldconfig, o qual na maioria dos sistemas é executado como parte do processo de boot. O link libfoo.so tem de ser atualizado manualmente. Se você é cuidadoso com atualizações de todas as partes de uma biblioteca, (por exemplo os arquivos de cabeçalho) ao mesmo tempo, a coisa mais simples a fazer é criar o link libfoo.so -> libfoo.so.1, de forma que ldconfig irá manter ambos os links atuais para você. Se você não é, você está se colocando em uma situação em que todos os tipos de coisas estranhas podem acontecer em uma data posterior. Não diga que não foi avisado.
$ su
# cp libfoo.so.1.0 /usr/local/lib
# /sbin/ldconfig
# ( cd /usr/local/lib ; ln -s libfoo.so.1 libfoo.so )
|
Cada biblioteca tem um soname. Quando o linker encontra um desses em uma biblioteca que ele está pesquisando, ele incorpora o soname no binário ao invés do nome do arquivo que ele está examinando. Em tempo de execução, o carregador dinâmico irá então procurar por um arquivo com o nome do soname, não o nome de arquivo da biblioteca. Assim uma biblioteca chamada libfoo.so poderia ter um soname libbar.so, e todos os programas a ela linkados procurariam por libbar.so quando inicializados.
Isto soa como uma característica sem sentido, mas ela é a chave para entender como múltiplas versões da mesma biblioteca podem coexistir em um sistema. O padrão de fato para nomeação de bibliotecas no Linux é chamar a biblioteca de, digamos, libfoo.so.1.2, e dar-lhe um soname de libfoo.so.1. Se ela é adicionada a um diretório de bibliotecas `padrão' (por exemplo /usr/lib), ldconfig irá criar um link simbólico libfoo.so.1 -> libfoo.so.1.2 de forma que a imagem apropriada seja encontrada em tempo de execução. Você também precisa de um link libfoo.so -> libfoo.so.1 para que ld encontre o soname correto para usar durante a linkagem.
Então, quando você corrige bugs na biblioteca, ou adiciona novas funções (quaisquer mudanças que não vão afetar adversamente programas existentes), você a reconstrói, mantendo o soname anterior, e mudando o nome do arquivo. Quando você faz mudanças na biblioteca que podem danificar binários existentes, você simplesmente incrementa o número do soname --- neste caso, chame a nova versão de libfoo.so.2.0, e de-lhe um soname de libfoo.so.2. Agora altere o link libfoo.so para que aponte para a nova versão e tudo estará bem novamente.
Note que você não tem que nomear bibliotecas desta maneira, mas é uma boa convenção. ELF lhe dá a flexibilidade para nomear bibliotecas de maneiras que irão confundir completamente as pessoas, mas isso não significa que você tem que usá-la.
Sumário executivo: supondo que você observe a tradição de que grandes atualizações podem danificar a compatibilidade e pequenas atualizações não, então link com
gcc -shared -Wl,-soname,libfoo.so.major -o libfoo.so.major.minor
|
e tudo estará bem.
A facilidade para construir bibliotecas compartilhadas é uma grande razão para atualizar para ELF. Dito isto, também é possível fazê-lo em a.out. Obtenha � e leia o dcoumento de 20 páginas que você vai achar após descompactá-lo. Eu odeio ser parcial de modo tão transparente, mas deve ser claro pelo contexto que eu nunca me preocupei com isso :-)
QMAGIC é um formato executável tal como os velhos binários a.out (também conhecidos como ZMAGIC), mas que deixa a primeira página não mapeada. Isto permite uma captura mais fácil de dereferenciamentos de NULL, pois não existe qualquer mapeamento no intervalo 0-4096. Como um efeito colateral, os seus binários são nominalmente menores também (cerca de 1K).
Linkers obsoletos suportam apenas ZMAGIC, semi-obsoletos suportam os dois formatos, e versões correntes suportam apenas QMAGIC. Isto realmente não importa, entretanto, pois o kernel ainda pode executar ambos.
Seu comando `file' deve ser capaz de identificar se um programa é QMAGIC.
Uma biblioteca compartilhada a.out (DLL) consiste de dois arquivos reais e um link simbólico. Para a biblioteca `foo' usada em todo este documento como exemplo, estes arquivos seriam libfoo.sa e libfoo.so.1.2; o link simbólico seria libfoo.so.1 e apontaria para o último dos arquivos. Para que eles servem?
Em tempo de compilação, ld procura libfoo.sa. Este é o arquivo `stub' para a biblioteca, e contém todos os dados exportados e ponteiros para as funções requeridas para linkagem em tempo de execução.
Em tempo de execução, o carregador dinâmico procura libfoo.so.1. Este é um link simbólico em vez de um arquivo real para que as bibliotecas possam ser atualizadas com versões mais novas, corrigidas sem destruir qualquer aplicação que estiver usando a biblioteca no momento. Depois que a nova versão --- digamos, libfoo.so.1.3 --- está completamente lá, a execução de ldconfig irá alterar o link para apontar para ela em uma operação atômica, mantendo qualquer programa que tinha a versão antiga perfeitamente feliz.
Bibliotecas DLL (eu sei que é uma tautologia --- processe-me) frquentemente parecem maiores que seus equivalentes estáticos. Elas reservam espaço para expansão futura na forma de `buracos' que podem ser tratados de forma a não ocupar espaço em disco. Uma simples chamada a cp ou o uso do programa makehole irá alcançar isto. Você também pode removê-los após a construção, pois os endereços estão em locais fixos. Não tente a remoção em bibliotecas ELF.
Uma libc-lite é uma versão peso-leve da biblioteca libc construída de forma que ela irá caber em um disquete e será suficiente para todas das mais servis tarefas UNIX. Ela não inclue código de curses, dbm, termcap etc. Se sua /lib/libc.so.4 está linkada a uma biblioteca lite, você está aconselhado a substituí-la com uma versão completa.
Envie para mim os seus problemas de linkagem! Eu provavelmente não farei qualquer coisa a respeito deles, mas irei escrevê-los se tiver um número suficiente ...
Verifique se você tem os links corretos para que ld encontre cada biblioteca compartilhada. Para ELF isto significa um link simbólico libfoo.so para a imagem, para a.out um arquivo libfoo.sa. Muitas pessoas tiveram este problema depois de passar de binutils 2.5 ELF para 2.6 --- a versão anterior procurava de forma mais `inteligente' as bibliotecas compartilhadas, de forma que eles não tinham criado todos os links. O comportamento inteligente foi removido para compatibilidade com outras arquiteturas, e porque frequentemente as suas suposições eram erradas e causavam mais problemas do que resolviam.
Desde libc.so.4.5.x e acima, libgcc não é mais compartilhada. Portanto você deve substituir ocorrências de `-lgcc' nas linha incorreta com `gcc -print-libgcc-file-name` (incluindo as crases).
Também, exclua todos os arquivos /usr/lib/libgcc*. Isto é importante.
são outra consequência do mesmo problema.
Esta mensagem obscura mais provavelmente significa que ocorreu um overflow em uma das entradas da sua tabela de deslocamento, porque um espaço muito pequeno foi reservado no arquivo jump.vars original. Você pode localizar o(s) culpado(s) executando o comando `getsize' fornecido no pacote tools-2.17.tar.gz. Provavelmente a única solução, entretanto, é aumentar o número de versão maior da biblioteca, forçando-a a ser incompatível para trás.
Isto usualmente acontece quando você está linkando com bibliotecas diferentes da libc (por exemplo bibliotecas X), e usa a opção -g na linha de comando de linkagem sem usar também -static.
Os stubs .sa para as bibliotecas compartilhadas usualmente tem um símbolo não definido _NEEDS_SHRLIB_libc_4 o qual é resolvido a partir do stub libc.sa. Entretanto com -g você acaba linkando com libg.a ou libc.a e assim este símbolo nunca é resolvido levando à mensagem de erro acima.
Concluindo, adicione -static quando compilar com a flag -g , ou não linke com -g. Frequentemente você pode obter informações suficientes para depuração compilando os arquivos individuais com -g, e linkando-os sem ela.