Respostas:
(Veja o histórico desta resposta para obter um texto mais elaborado, mas agora acho que é mais fácil para o leitor ver linhas de comando reais).
Arquivos comuns compartilhados por todos os comandos abaixo
$ cat a.cpp
extern int a;
int main() {
return a;
}
$ cat b.cpp
extern int b;
int a = b;
$ cat d.cpp
int b;
$ g++ -c b.cpp -o b.o
$ ar cr libb.a b.o
$ g++ -c d.cpp -o d.o
$ ar cr libd.a d.o
$ g++ -L. -ld -lb a.cpp # wrong order
$ g++ -L. -lb -ld a.cpp # wrong order
$ g++ a.cpp -L. -ld -lb # wrong order
$ g++ a.cpp -L. -lb -ld # right order
O vinculador pesquisa da esquerda para a direita e observa os símbolos não resolvidos. Se uma biblioteca resolver o símbolo, serão necessários os arquivos de objeto dessa biblioteca para resolvê-lo (neste caso, sai da libb.a).
As dependências das bibliotecas estáticas funcionam uma da outra - a biblioteca que precisa de símbolos deve ser a primeira e, em seguida, a biblioteca que resolve o símbolo.
Se uma biblioteca estática depende de outra biblioteca, mas a outra biblioteca depende novamente da biblioteca anterior, há um ciclo. Você pode resolver isso encerrando as bibliotecas dependentes de ciclismo por -(
e -)
, como -( -la -lb -)
(você pode precisar escapar das parênteses, como -\(
e -\)
). O vinculador, em seguida, procura na biblioteca incluída várias vezes para garantir que as dependências de ciclismo sejam resolvidas. Alternativamente, você pode especificar as bibliotecas várias vezes, por isso, cada um é antes um outro: -la -lb -la
.
$ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc
$ g++ -fpic -shared d.cpp -o libd.so
$ g++ -fpic -shared b.cpp -L. -ld -o libb.so # specifies its dependency!
$ g++ -L. -lb a.cpp # wrong order (works on some distributions)
$ g++ -Wl,--as-needed -L. -lb a.cpp # wrong order
$ g++ -Wl,--as-needed a.cpp -L. -lb # right order
É o mesmo aqui - as bibliotecas devem seguir os arquivos de objeto do programa. A diferença aqui em comparação com as bibliotecas estáticas é que você não precisa se preocupar com as dependências das bibliotecas umas contra as outras, porque as bibliotecas dinâmicas ordenam suas próprias dependências .
Aparentemente, algumas distribuições recentes usam o --as-needed
sinalizador vinculador, o que força os arquivos de objeto do programa antes das bibliotecas dinâmicas. Se esse sinalizador for passado, o vinculador não fará o link para bibliotecas que não são realmente necessárias para o executável (e o detecta da esquerda para a direita). Minha distribuição recente do archlinux não usa esse sinalizador por padrão, portanto não deu um erro por não seguir a ordem correta.
Não é correto omitir a dependência de b.so
contra d.so
ao criar o primeiro. Você será solicitado a especificar a biblioteca ao vincular a
, mas na a
verdade não precisa do número inteiro b
, portanto não deve se preocupar com b
as próprias dependências.
Aqui está um exemplo das implicações se você deixar de especificar as dependências para libb.so
$ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc
$ g++ -fpic -shared d.cpp -o libd.so
$ g++ -fpic -shared b.cpp -o libb.so # wrong (but links)
$ g++ -L. -lb a.cpp # wrong, as above
$ g++ -Wl,--as-needed -L. -lb a.cpp # wrong, as above
$ g++ a.cpp -L. -lb # wrong, missing libd.so
$ g++ a.cpp -L. -ld -lb # wrong order (works on some distributions)
$ g++ -Wl,--as-needed a.cpp -L. -ld -lb # wrong order (like static libs)
$ g++ -Wl,--as-needed a.cpp -L. -lb -ld # "right"
Se você agora examinar quais dependências o binário possui, observe que o próprio binário depende também libd
, não apenas libb
como deveria. O binário precisará ser vinculado novamente se libb
posteriormente depender de outra biblioteca, se você fizer dessa maneira. E se outra pessoa carregar libb
usando dlopen
em tempo de execução (pense em carregar plugins dinamicamente), a chamada também falhará. Então o "right"
realmente deveria ser um wrong
também.
lorder
+ tsort
fazem. Mas, às vezes, não há ordem, se você tiver referências cíclicas. Depois, basta percorrer a lista de bibliotecas até que tudo esteja resolvido.
O vinculador GNU ld é o chamado vinculador inteligente. Ele acompanhará as funções usadas pelas bibliotecas estáticas anteriores, descartando permanentemente as funções que não são usadas em suas tabelas de pesquisa. O resultado é que, se você vincular uma biblioteca estática muito cedo, as funções nessa biblioteca não estarão mais disponíveis para bibliotecas estáticas posteriormente na linha de link.
O vinculador típico do UNIX funciona da esquerda para a direita; portanto, coloque todas as suas bibliotecas dependentes à esquerda e aquelas que satisfazem essas dependências à direita da linha de link. Você pode achar que algumas bibliotecas dependem de outras, enquanto outras bibliotecas dependem delas. É aqui que fica complicado. Quando se trata de referências circulares, corrija seu código!
Aqui está um exemplo para deixar claro como as coisas funcionam com o GCC quando bibliotecas estáticas estão envolvidas. Então, vamos supor que temos o seguinte cenário:
myprog.o
- contendo main()
função, dependente delibmysqlclient
libmysqlclient
- estático, pelo bem do exemplo (você prefere a biblioteca compartilhada, é claro, porque libmysqlclient
é enorme); in /usr/local/lib
; e dependente de coisas delibz
libz
(dinâmico)Como vinculamos isso? (Nota: exemplos de compilação no Cygwin usando o gcc 4.3.4)
gcc -L/usr/local/lib -lmysqlclient myprog.o
# undefined reference to `_mysql_init'
# myprog depends on libmysqlclient
# so myprog has to come earlier on the command line
gcc myprog.o -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'
# we have to link with libz, too
gcc myprog.o -lz -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'
# libz is needed by libmysqlclient
# so it has to appear *after* it on the command line
gcc myprog.o -L/usr/local/lib -lmysqlclient -lz
# this works
Se você adicionar -Wl,--start-group
aos sinalizadores do vinculador, não importa em que ordem eles estão ou se existem dependências circulares.
No Qt, isso significa adicionar:
QMAKE_LFLAGS += -Wl,--start-group
Economiza muito tempo brincando e não parece diminuir muito a velocidade de vinculação (o que leva muito menos tempo do que a compilação).
Você pode usar a opção -Xlinker.
g++ -o foobar -Xlinker -start-group -Xlinker libA.a -Xlinker libB.a -Xlinker libC.a -Xlinker -end-group
é quase igual a
g++ -o foobar -Xlinker -start-group -Xlinker libC.a -Xlinker libB.a -Xlinker libA.a -Xlinker -end-group
Cuidado !
Uma dica rápida que me surpreendeu: se você estiver chamando o vinculador como "gcc" ou "g ++", usar "--start-group" e "--end-group" não passará essas opções para o vinculador - nem sinalizará um erro. Ele apenas falhará no link com símbolos indefinidos, se a ordem da biblioteca estiver errada.
Você precisa escrevê-los como "-Wl, - start-group" etc. para informar ao GCC para passar o argumento para o vinculador.
A ordem dos links certamente importa, pelo menos em algumas plataformas. Vi falhas em aplicativos vinculados a bibliotecas na ordem errada (onde errado significa A vinculado antes de B, mas B depende de A).
Eu já vi isso muito, alguns de nossos módulos vinculam mais de 100 bibliotecas do nosso código, mais sistema e bibliotecas de terceiros.
Dependendo dos diferentes vinculadores HP / Intel / GCC / SUN / SGI / IBM / etc, você pode obter funções / variáveis não resolvidas etc., em algumas plataformas é necessário listar as bibliotecas duas vezes.
Na maioria das vezes, usamos hierarquia estruturada de bibliotecas, núcleo, plataforma, diferentes camadas de abstração, mas para alguns sistemas você ainda precisa jogar com a ordem no comando link.
Depois de encontrar um documento de solução, o próximo desenvolvedor não precisará trabalhar novamente.
Meu antigo professor costumava dizer: " alta coesão e baixo acoplamento ", ainda é verdade hoje.
gcc
alterado para um comportamento mais estrito (relativamente) recentemente.