O documento discute como o código fonte é transformado em um programa executável através da compilação e ligação. Explica que o compilador converte o código fonte em código de máquina e que o ligador junta os arquivos objeto gerados em um arquivo executável que o sistema operacional possa usar.
2. Grupo C & C++ Brasil
• Nasceu da vontade de conhecer outras áreas de
uso para C & C++
• Grupo de entusiastas/aficcionados
• Aberto à todos
• Organiza eventos para reunir profissionais,
estudantes e entusiastas
• Grupo mais multilinguagem que eu conheço
• groups.google.com/group/ccppbrasil/
• Temos um grupo no Telegram: t.me/ccppbrasil
• #ccppbrasil
3. Eu, eu e eu
• Escovador de bits
• Sócio programador da BitForge e da
Intelitrader
• Fundador do grupo C & C++ Brasil junto com o
Wanderley Caloni
• Fui MVP de Visual C++ por dois anos
• @rodrigostrauss
• www.1bit.com.br
4. Por que entender a linkedição
• Quanto mais você entende as mágicas melhor
programador você é
• Conhecimento dos ”internals” economiza tempo
e stress quando você mais precisa
• Abra as caixas pretas. Sempre
• A cada dia mais abstrações são criadas. Se você
sabe a raiz das coisas você entende tudo
rapidamente
• É um tópico extenso, eu foquei nos tópicos mais
úteis para diagnóstico dos problemas mais
comuns
5. Como seu código fonte vira um
programa?
• O compilador é só uma das partes
• O compilador converte código fonte para código
de máquina
• Para esse código de máquina ser útil ele precisa
ser
– Organizado
– ”Formatado” de uma forma que o sistema operacional
entenda
– Disponibilizado para ser usado por outros módulos
6. Compilação
• Resumidamente entra código fonte e sai
código de máquina
• Complicadamente isso requer diversos passos
– Parser
– Lexer
– Tratamento de AST
– Otimizador
7. Responsabilidades de cada um
Gerando um
executável
Compilador
Transforma fonte
em código de
máquina
Faz otimizações
Linker
Resolve chamadas
de função
Formata código
de uma forma que
o SO entenda
Relocação
8. O que um linker faz?
• Junta arquivos com código de máquina (principalemente)
em um arquivo executável que o SO consiga usar
• O compilador gera um arquivo .obj para cada arquivo .cpp
• O linker depois ”junta” esses arquivos .obj em outros tipos
de arquivo
– EXE
– DLL
– SYS
– DRV
– LIB
• Exceção à regra, esse arquivo não pode ser usado pelo sistema
operacional, ele é um simples agrupamentos de arquivos .obj
9. Considerações importantes
• Nem só de código de máquina vive um linker
– Código de máquina
• Erroneamente chamado de ”assembler”
– Dados
• Variáveis globais
• Variáveis estáticas
• TLS
• Resources (Windows)
• Informacão de debug
• Tabela de symbols
10. Por que existe o linker
• Motivos para precisarmos de um linker
– Dividir o código fonte em vários arquivos
– Permitir usar funções e dados disponibilizados de
forma binária
– O sistema operacional precisa de mais do um arquivo
com código de máquina sequencial
• Qualquer outra opção seria inviável
– Juntar todos os fontes
– Recompilar a partir dos fontes sempre
• Até o Gentoo usa um linker, certo Gianni?
11. Como um linker faz?
• Abre todos os arquivos .obj
• Verifica todas as funções não resolvidas
internamente
– Exemplo: uma função que está em main.obj chama
uma função que está em matematica.obj
• Procura essas funções em outros arquivos .obj ou
.lib
• Coloca essas funções dentro do executável e
aponta corretamente uma para outra
• Esse processo chama-se ”symbol resolution”
12. External resolving
• Consiste em verificar as promessas feitas ao
compilador
• Uma declaração de função em C e C++ nada
mais é que ”eu te prometo que essa função
existe em algum lugar”
• Onde procurar a função?
• Em que ordem?
13. Link estático
• Junta-se todos os arquivos .obj e .lib e gera-se um
executável
• Todo código de todas as funções resolvidas são
copiadas para dentro desse executável
• Isso faz com que o executável não tenha
dependência de DLLs (mais sobre isso daqui a
pouco)
• É o que acontece nos casos mais simples, quando
você cria um projeto novo no Visual C++
14. Link dinâmico
• A fase de symbol resolution é executada
quando o executável é carregado pelo sistema
operacional
– Cada DLL tem uma tabela de símbolos exportados
– Cada EXE tem uma tabela que contém os nomes
das DLLs e das funções importadas
• No Windows, isso vai dentro do arquivo PE,
em sessões específicas
15. DEMO DE COMPILAÇÃO E LINK
• Um arquivo cpp gerando um EXE
• Dois arquivos cpp gerando um EXE
• Movendo as funções para uma LIB
• Movendo as funções para uma DLL
17. Formato COFF
• Common Object File Format
• É um velho formato do UNIX, criado pela AT&T em 1983
• O Windows usa uma extensão desse formato, que é
chamado PE (Portable Executable)
• Todo arquivo com código de máquina no Windows é um PE
– OBJ
– LIB
– DLL
– EXE
– DRV
– SYS
• O Linux usa o formato ELF
18. O que tem dentro de um COFF/PE?
• Um arquivo COFF/PE é composto por
– Header
– Section definition
– Section Content
20. Sections PE
• .text é a seção com código
• Podemos ver várias seções com nomes extras
depois de um $
– .text$M
– .text$XPTO
• Aí é que está a parte executável do programa
21. Sections PE
• As informações de debug estão,
surpreendentemente, nas seções .debug
– .debug$T
– .debug$S
– .debug$P
• Algumas informações são inline, outras são
referências para arquivos PDB
26. Algumas considerações
• Arquivos .obj e .lib declaram suas
dependências por nome
• Calling convention define o formato dos
nomes
– stdcall, cdecl, fastcall, etc
27. Problemas comuns e como
diagnosticá-los
• LNK2019: Unresolved external
• LNK1104: cannot open file ’xpto.lib’
• LNK2005: ”xpto” already defined in xyz.obj
• Dependências de diferentes runtimes
• Unresolved external: MessageBoxA ou
MessageBoxW
• Erro de DLL não encontrada em runtime
28. Maiores causadores de problemas no
link
• PREPROCESSADOR, o campeão dos campeões
• Declarações diferentes para cada .obj ou .lib
• Versão UNICODE ou ANSI das apis Win32
• _WINVER
• _HAS_ITERATOR_DEBUGGING
• Diferentes libs usam diferentes versões da
runtime do Visual C++
• #pragma comment(lib)
• Calling convention errado
29. Recursos do Visual C++
• Whole Program Optimization
• #pragma comment (lib)
• FAILIFMISMATCH
• /NODEFAULTLIB
• __declspec(dllexport)
• __declspec(selectany)
30. Whole Program Optimization
• O linker faz inline de funções
• Acaba sendo um módulo do otimizador do
compilador rodando na hora do link
• Melhora a performance do código fazendo uma
otimização que antes só era possível ”grudando”
todos os arquivos fontes em um só
– Sqlite tem o amalgamation
– SQL Server foi o primeiro usuário do WPO dentro da
Microsoft