Como se compila um binário, de código a processo

Publicado em 2026-09-12 por frfrand

Las cuatro etapas que convierten texto en un programa, qué hace el enlazador y por qué falla tanto, la diferencia entre enlazado estático y dinámico, y qué pasa al ejecutarlo.

Como se compila um binário, de código a processo

Você escreve código, executa um comando, aparece um executável. No meio, há quatro etapas, e saber qual é qual muda completamente a interpretação dos erros.

1. Pré-processamento

São resolvidas as diretivas: os arquivos incluídos são colados, as macros são expandidas, os blocos condicionais que não se aplicam são removidos.

O resultado continua sendo texto, normalmente muito mais longo que o original.

2. Compilação

O compilador traduz esse texto para instruções de máquina. Verifica a sintaxe, aplica otimizações e gera um arquivo objeto por cada arquivo fonte.

Um arquivo objeto tem código de máquina, mas não é executável ainda: contém referências a coisas que estão em outro lugar. Se o seu código chama uma função definida em outro arquivo, o objeto guarda o nome e um espaço a ser preenchido.

Os erros desta etapa são os familiares: um ponto e vírgula, um tipo que não confere, uma variável não declarada. Eles indicam arquivo e linha.

3. Edição de links

Aqui é onde aparecem os erros que mais confundem.

O vinculador junta todos os objetos e as bibliotecas, e resolve cada referência pendente: para cada nome usado, busca onde está definido e anota o endereço.

Se um nome não aparecer em nenhum lugar, falha com o clássico:

undefined reference to 'mi_funcion'

Este erro confunde porque parece um erro de compilação e não é. O compilador esteve perfeitamente feliz: sabia que essa função existia em algum lugar. Quem não a encontra é o vinculador.

As causas habituais: falta adicionar um arquivo à compilação, falta vincular uma biblioteca (o clássico -lm para matemática), ou a ordem das bibliotecas está errada (o vinculador as processa em ordem e alguns casos são sensíveis a isso).

Estático ou dinâmico

Uma decisão com consequências muito práticas.

Vinculação estática: o código das bibliotecas é copiado dentro do seu executável. O binário é maior e não depende de nada externo: você o copia para outra máquina e funciona.

Vinculação dinâmica: o executável guarda apenas o nome da biblioteca, e ela é carregada ao executar. O binário é pequeno, e vários programas compartilham a mesma biblioteca na memória. Além disso, uma atualização de segurança da biblioteca beneficia todos os programas sem recompilar.

O custo do dinâmico é o erro do outro extremo:

error while loading shared libraries: libfoo.so.3: cannot open shared object file

Isso é tempo de execução: o programa compilou e vinculou bem, mas ao iniciar não encontra a biblioteca. Para ver de que depende um binário:

ldd /usr/bin/miprograma

Este é o motivo pelo qual um binário compilado em uma distribuição pode não rodar em outra, e também é uma das razões pelas quais existem os contêineres: encapsular o conjunto exato de bibliotecas.

4. Carregar e executar

Quando você executa o programa, o kernel lê o cabeçalho do arquivo (no Linux, formato ELF), reserva memória, mapeia as seções de código e dados e passa o controle para o carregador dinâmico, que resolve as bibliotecas que faltam e finalmente salta para o ponto de entrada.

Curiosidade: o segmento de código é mapeado em modo somente leitura e compartilhado. Se você executar o mesmo programa vinte vezes, o código está apenas uma vez na memória física.

E as linguagens que não compilam assim

Python, JavaScript ou Ruby não produzem um binário nativo. São interpretados, ou compilados para um formato intermediário que é executado por uma máquina virtual.

Java e C# estão no meio: compilam para bytecode e o traduzem para código nativo enquanto estão sendo executados.

Go e Rust fazem o contrário e por isso se tornaram populares em infraestrutura: produzem um binário estático que não depende de nada. Você copia o arquivo para o servidor e ele funciona. Sem interpretador, sem bibliotecas do sistema, sem ambiente virtual.

  • compilación
  • binarios
  • linker
  • Linux
  • fundamentos

Continuar lendo

Todos os artigos · Planos VPS