Respostas:
Existem (na maioria dos casos, descontando o código interpretado) dois estágios na obtenção do código-fonte (o que você escreve) para o código executável (o que você executa).
A primeira é a compilação que transforma o código fonte em módulos de objetos.
O segundo, vinculação, é o que combina módulos de objetos para formar um executável.
A distinção é feita para, entre outras coisas, permitir que bibliotecas de terceiros sejam incluídas no seu executável sem que você veja o código-fonte (como bibliotecas para acesso ao banco de dados, comunicações de rede e interfaces gráficas de usuário) ou para compilar código em diferentes idiomas ( C e código de montagem, por exemplo) e, em seguida, vinculá-los todos juntos.
Quando você vincula estaticamente um arquivo a um executável, o conteúdo desse arquivo é incluído no momento do link. Em outras palavras, o conteúdo do arquivo é fisicamente inserido no executável que você executará.
Quando você vincula dinamicamente , um ponteiro para o arquivo que está sendo vinculado (o nome do arquivo, por exemplo) é incluído no executável e o conteúdo do referido arquivo não é incluído no momento do link. Somente depois que você executa o executável é que esses arquivos vinculados dinamicamente são comprados e comprados apenas na cópia do executável na memória, não no disco.
É basicamente um método de vinculação diferida. Existe um método ainda mais diferido (chamado ligação tardia em alguns sistemas) que não trará o arquivo vinculado dinamicamente até que você tente chamar uma função nele.
Arquivos vinculados estaticamente são 'bloqueados' no executável no momento do link, para que nunca sejam alterados. Um arquivo vinculado dinamicamente referenciado por um executável pode mudar apenas substituindo o arquivo no disco.
Isso permite atualizações na funcionalidade sem precisar vincular novamente o código; o carregador é vinculado novamente toda vez que você o executa.
Isso é bom e ruim - por um lado, permite atualizações mais fáceis e correções de bugs, por outro, pode levar a programas que deixam de funcionar se as atualizações forem incompatíveis - às vezes isso é responsável pelo temido "inferno da DLL" que algumas pessoas mencione que os aplicativos podem ser quebrados se você substituir uma biblioteca vinculada dinamicamente por uma que não seja compatível (os desenvolvedores que fazem isso devem esperar ser caçados e punidos severamente, a propósito).
Como exemplo , vejamos o caso de um usuário compilando seu main.c
arquivo para vinculação estática e dinâmica.
Phase Static Dynamic
-------- ---------------------- ------------------------
+---------+ +---------+
| main.c | | main.c |
+---------+ +---------+
Compile........|.........................|...................
+---------+ +---------+ +---------+ +--------+
| main.o | | crtlib | | main.o | | crtimp |
+---------+ +---------+ +---------+ +--------+
Link...........|..........|..............|...........|.......
| | +-----------+
| | |
+---------+ | +---------+ +--------+
| main |-----+ | main | | crtdll |
+---------+ +---------+ +--------+
Load/Run.......|.........................|..........|........
+---------+ +---------+ |
| main in | | main in |-----+
| memory | | memory |
+---------+ +---------+
Você pode ver no caso estático que o programa principal e a biblioteca de tempo de execução C estão vinculados juntos no momento do link (pelos desenvolvedores). Como o usuário normalmente não pode vincular novamente o executável, ele fica preso ao comportamento da biblioteca.
No caso dinâmico, o programa principal está vinculado à biblioteca de importação de tempo de execução C (algo que declara o que há na biblioteca dinâmica, mas na verdade não a define ). Isso permite que o vinculador vincule, mesmo que o código real esteja ausente.
Em tempo de execução, o carregador do sistema operacional faz uma ligação tardia do programa principal com a DLL de tempo de execução C (biblioteca de vínculo dinâmico ou biblioteca compartilhada ou outra nomenclatura).
O proprietário do tempo de execução C pode soltar uma nova DLL a qualquer momento para fornecer atualizações ou correções de bugs. Como afirmado anteriormente, isso tem vantagens e desvantagens.
.dll
ou .so
extensão) - pense na resposta como explicando os conceitos, em vez de ser uma descrição exata. E, de acordo com o texto, este é um exemplo que mostra links estáticos e dinâmicos apenas para os arquivos de tempo de execução C, portanto, sim, é isso que `crt indica em todos eles.
Eu acho que uma boa resposta para essa pergunta deve explicar o que é vinculação .
Quando você compila algum código C (por exemplo), ele é traduzido para o idioma da máquina. Apenas uma sequência de bytes que, quando executada, faz com que o processador adicione, subtraia, compare, "vá", leia memória, escreva memória, esse tipo de coisa. Esse material é armazenado em arquivos de objeto (.o).
Agora, há muito tempo, cientistas da computação inventaram essa coisa de "sub-rotina". Execute-este-pedaço-de-código-e-retorne-aqui. Não demorou muito para que eles percebessem que as sub-rotinas mais úteis poderiam ser armazenadas em um local especial e usadas por qualquer programa que precisasse delas.
Agora, nos primeiros dias, os programadores precisariam digitar o endereço de memória em que essas sub-rotinas estavam localizadas. Algo como CALL 0x5A62
. Isso foi entediante e problemático, caso esses endereços de memória precisem ser alterados.
Então, o processo foi automatizado. Você escreve um programa que chama printf()
e o compilador não sabe o endereço de memória de printf
. Portanto, o compilador apenas grava CALL 0x0000
e adiciona uma nota ao arquivo de objeto dizendo "deve substituir este 0x0000 pelo local da memória de printf ".
Ligação estática significa que o programa vinculador (o GNU é chamado ld ) adiciona printf
o código de máquina diretamente ao seu arquivo executável e altera o 0x0000 para o endereço de printf
. Isso acontece quando seu executável é criado.
A ligação dinâmica significa que a etapa acima não acontece. O arquivo executável ainda possui uma observação que diz "deve substituir 0x000 pelo local da memória de printf". O carregador do sistema operacional precisa encontrar o código printf, carregá-lo na memória e corrigir o endereço CALL, sempre que o programa for executado .
É comum os programas chamarem algumas funções que serão vinculadas estaticamente (funções de biblioteca padrão como printf
geralmente são vinculadas estaticamente) e outras funções que são vinculadas dinamicamente. Os estáticos "se tornam parte" do executável e os dinâmicos "se juntam" quando o executável é executado.
Existem vantagens e desvantagens nos dois métodos e diferenças entre os sistemas operacionais. Mas como você não pediu, eu terminarei isso aqui.
ld
documentação do GNU .
Bibliotecas vinculadas estaticamente são vinculadas no momento da compilação. Bibliotecas vinculadas dinamicamente são carregadas em tempo de execução. A vinculação estática transforma o bit da biblioteca em seu executável. A vinculação dinâmica é apenas uma referência à biblioteca; os bits da biblioteca dinâmica existem em outros lugares e podem ser trocados mais tarde.
Como nenhuma das postagens acima mostra realmente como vincular estaticamente algo e como você fez isso corretamente, então vou abordar esse problema:
Um programa C simples
#include <stdio.h>
int main(void)
{
printf("This is a string\n");
return 0;
}
Vincular dinamicamente o programa C
gcc simpleprog.c -o simpleprog
E execute file
no binário:
file simpleprog
E isso mostrará que está vinculado dinamicamente algo ao longo das linhas de:
"simpleprog: executável LSF ELF de 64 bits, x86-64, versão 1 (SYSV), vinculado dinamicamente (usa libs compartilhadas), para GNU / Linux 2.6.26, BuildID [sha1] = 0xf715572611a8b04f686809d90d1c0d75c6028f0f, sem remoção"
Em vez disso, vamos vincular estaticamente o programa desta vez:
gcc simpleprog.c -static -o simpleprog
A execução do arquivo neste binário vinculado estaticamente mostrará:
file simpleprog
"simpleprog: executável LSF ELF de 64 bits, x86-64, versão 1 (GNU / Linux), vinculado estaticamente, para GNU / Linux 2.6.26, BuildID [sha1] = 0x8c0b12250801c5a7c7434647b7dc65a644d6132b"
E você pode ver que ele está vinculado estaticamente. Infelizmente, porém, nem todas as bibliotecas são simples de vincular estaticamente dessa maneira e podem exigir um esforço prolongado usando libtool
ou vinculando o código de objeto e as bibliotecas C manualmente.
Felizmente, muitas bibliotecas C incorporadas como musl
oferecem opções de vinculação estática para quase todas, se não todas as bibliotecas.
Agora, strace
o binário que você criou e você pode ver que não há bibliotecas acessadas antes do início do programa:
strace ./simpleprog
Agora compare com a saída do strace
programa vinculado dinamicamente e você verá que o rastreio da versão vinculada estaticamente é muito menor!
(Não sei c #, mas é interessante ter um conceito de vinculação estática para uma linguagem de VM)
A vinculação dinâmica envolve saber como encontrar uma funcionalidade necessária, que você só tem uma referência do seu programa. O tempo de execução de idioma ou o SO procura um trecho de código no sistema de arquivos, na rede ou no cache de código compilado, correspondendo à referência e, em seguida, toma várias medidas para integrá-lo à imagem do programa na memória, como realocação. Todos eles são feitos em tempo de execução. Isso pode ser feito manualmente ou pelo compilador. Existe a capacidade de atualizar com o risco de estragar (a saber, DLL inferno).
A vinculação estática é feita no momento da compilação; você informa ao compilador onde estão todas as partes funcionais e instrui-o a integrá-las. Não há pesquisa, ambiguidade, capacidade de atualização sem recompilação. Todas as suas dependências são fisicamente uma com a imagem do programa.