(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
externint a;int main(){return a;}
$ cat b.cpp
externint b;int a = b;
$ cat d.cpp
int b;
Vinculando a Bibliotecas Estáticas
$ 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.
Vinculando a Bibliotecas Dinâmicas
$ 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-neededsinalizador 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.socontra d.soao criar o primeiro. Você será solicitado a especificar a biblioteca ao vincular a, mas na averdade não precisa do número inteiro b, portanto não deve se preocupar com bas 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 libbcomo deveria. O binário precisará ser vinculado novamente se libbposteriormente depender de outra biblioteca, se você fizer dessa maneira. E se outra pessoa carregar libbusando dlopenem tempo de execução (pense em carregar plugins dinamicamente), a chamada também falhará. Então o "right"realmente deveria ser um wrongtambém.
Repita até que todos os símbolos sejam resolvidos, eh - você acha que eles podem gerenciar uma classificação topológica. O LLVM possui 78 bibliotecas estáticas por conta própria, com dependências de quem sabe o quê. É verdade que ele também possui um script para descobrir as opções de compilação / link - mas você não pode usá-lo em todas as circunstâncias.
Steve314
6
@ Steve é o que os programas lorder+ tsortfazem. 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.
Johannes Schaub - litb 18/08/19
10
@Johannes - Determine os componentes máximos fortemente conectados (por exemplo, o algoritmo Tarjans) e classifique topologicamente o dígrafo (inerentemente não cíclico) dos componentes. Cada componente pode ser tratado como uma biblioteca - se qualquer biblioteca do componente for necessária, o (s) ciclo (s) de dependência fará com que todas as bibliotecas desse componente sejam necessárias. Portanto, não, realmente não há necessidade de percorrer todas as bibliotecas para resolver tudo, e não há opções difíceis de linha de comando - um método que usa dois algoritmos conhecidos pode lidar com todos os casos corretamente.
Steve314
4
Gostaria de acrescentar um detalhe importante a esta excelente resposta: Usar "- (archives -)" ou "--start-group archives --end-group" é a única maneira segura de resolver dependências circulares , pois cada vez o vinculador visita um arquivo, extrai (e registra os símbolos não resolvidos) apenas os arquivos de objeto que resolvem os símbolos não resolvidos no momento . Por esse motivo, o algoritmo do CMake de repetir componentes conectados no gráfico de dependência pode ocasionalmente falhar. (Veja também excelente post de Ian Lance Taylor em ligadores para mais detalhes.)
Jørgen
3
Sua resposta me ajudou a resolver meus erros de vinculação e você explicou muito claramente como evitar problemas, mas você tem alguma idéia de por que foi projetado para funcionar dessa maneira?
precisa
102
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!
Isso é algo apenas com gnu ld / gcc? Ou isso é algo comum com vinculadores?
Mike
2
Aparentemente, mais compiladores Unix têm problemas semelhantes. O MSVC não está totalmente livre desses problemas, mas não parece tão ruim assim.
MSalters
4
As ferramentas de desenvolvimento MS não tendem a mostrar esses problemas tanto porque, se você usa uma cadeia de ferramentas totalmente MS, acaba configurando o pedido do vinculador corretamente e nunca percebe o problema.
Michael Kohne
16
O vinculador MSVC é menos sensível a esse problema, pois procurará todas as bibliotecas por um símbolo não referenciado. A ordem da biblioteca ainda pode afetar qual símbolo será resolvido se mais de uma biblioteca tiver o símbolo. No MSDN: "As bibliotecas também são pesquisadas na ordem da linha de comando, com a seguinte ressalva: Os símbolos não resolvidos ao trazer um arquivo de objeto de uma biblioteca são pesquisados nessa biblioteca primeiro e, em seguida, nas seguintes bibliotecas na linha de comando e / DEFAULTLIB (especificar biblioteca padrão) e, em seguida, a todas as bibliotecas no início da linha de comando "
Michael Burr
4
"... vinculador inteligente ..." - acredito que seja classificado como vinculador de "passagem única", não como "vinculador inteligente".
JWW
54
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:
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
A ordem dentro de um grupo é importante! Aqui está um exemplo: uma biblioteca de depuração tem uma rotina de depuração, mas a biblioteca não de depuração tem uma versão fraca da mesma. Você deve colocar a biblioteca de depuração PRIMEIRO no grupo ou resolverá a versão sem depuração.
Você precisa preceder cada biblioteca na lista de grupos com -Xlinker
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.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
Vinculando a Bibliotecas Estáticas
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
.Vinculando a Bibliotecas Dinâmicas
É 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
contrad.so
ao criar o primeiro. Você será solicitado a especificar a biblioteca ao vinculara
, mas naa
verdade não precisa do número inteirob
, portanto não deve se preocupar comb
as próprias dependências.Aqui está um exemplo das implicações se você deixar de especificar as dependências para
libb.so
Se você agora examinar quais dependências o binário possui, observe que o próprio binário depende também
libd
, não apenaslibb
como deveria. O binário precisará ser vinculado novamente selibb
posteriormente depender de outra biblioteca, se você fizer dessa maneira. E se outra pessoa carregarlibb
usandodlopen
em tempo de execução (pense em carregar plugins dinamicamente), a chamada também falhará. Então o"right"
realmente deveria ser umwrong
também.fonte
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!
fonte
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
- contendomain()
função, dependente delibmysqlclient
libmysqlclient
- estático, pelo bem do exemplo (você prefere a biblioteca compartilhada, é claro, porquelibmysqlclient
é 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)
fonte
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:
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).
fonte
Outra alternativa seria especificar a lista de bibliotecas duas vezes:
Fazendo isso, você não precisa se preocupar com a sequência correta, pois a referência será resolvida no segundo bloco.
fonte
Você pode usar a opção -Xlinker.
é quase igual a
Cuidado !
fonte
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.
fonte
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).
fonte
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.
fonte