Compilação, Amarração e
Relocação
Desenvolvimento modular
• Programas grandes devem ser implementados de forma modular
– Limitam a complexidade de manutenção do código
– Mudança em um módulo não requer a re-compilação de todo o programa
– Módulos comuns podem ser disponibilizados em bibliotecas
• Cada módulo:
– define uma coleção símbolos que representam tipos, constantes, variáveis e procedimentos, e , p ,
– referencia outros símbolos de outros módulos
• O compilador gera código objeto para cada módulo, e o amarrador (linker) junta os objetos em um executável.
O processo completo
O driver de compilador (gcc) invoca cada uma das etapas: pré-processador (cpp), compilador(cc1),
assembler (as) e linker(ld), para gerar o executável (para ver as etapas use gcc –v ) executável (para ver as etapas, use gcc v ... )
Li k (ld) ccp, cc1, as m.c m.o ccp, cc1, as a.c
a.o Arquivos objeto relocáveis (endereços relativos ao
início do arquivo)
Linker (ld)
p Arquivo objeto executável (com todos dados e procedimentos definidos em
m.c and a.c)
Obs: arquivos objeto (.o) não podem ser executados, pois há referências não resolvidas e os endereços não são os definitivos.
Tarefas do processo de amarração
(linker)
1. Combina vários arquivos objeto relocáveis (.o) em um único arquivo executável, que pode ser carregado na memória e executado;
memória e executado;
2. Resolve todas as referências externas, isto é
referências a um símbolo definido em outro arquivo objeto
3. Faz a relocação de todos os símbolos
– Transforma endereços relativos em endereços absolutos no executável
– Atualiza todas as referências para estes símbolos para refletir i õ
suas novas posições
Obs:
• As referências podem ser para código ou dados
• Amarração poder ser feita em tempo de compilação (ld do Unix), tempo de carga ou de execução
Estrutura geral de um arquivo objeto
• Tabela de símbolos exportados • Tabela de referências externas • Código ( text)
• Código (.text)
• Dados incializados (.data)
– Exemplo: int x = 3;
• Dados não incializados
– Exemplo: int x;
• Dicionário de relocação (= tabela com as posições no código onde existe uma referência à memória)
código onde existe uma referência à memória) • Informação para o depurador (debugger)
Os formatos mais usados:
• ELF (Executable and Linkable Format) para sistemas Unix • PE (Portable Executable) para Windows
Formato ELF de arquivo objeto (1/2)
• Header ELF– contém magic number, tipo (.o, exec, .so), arquitetura, big/little endian, etc.
ELF header Program header table (required for executables)
0
• Tabela de header de programas (para executáveis)
– Contém tamanho de página (para memória virtual), segmentos e seus tamanhos
• .text – código • .data
– Dados inicializados státicos (*)
( q ) .text section .data section .bss section .symtab .rel.txt .rel.data Dados inicializados státicos ( )
• .bss section
– Dados não incializados státicos – Não ocupa espaço (“Better Save
Space”)
.debug Section header table (required for relocatables)
(*) Lembre-se que variáveis dinâmicas, no corpo de procedimentos, são alocados na pilha de execução
Formato ELF de arquivo objeto (2/2)
• .symtab =Tabela de símbolos com:– Nomes de procedimentos e variáveis estáticas
– Nomes de seções e posições
ELF header Program header table (required for executables)
0
Nomes de seções e posições • .rel.text
– Informação de relocação para seção .text
– Endereços das instruções que precisarão ser modificados no executável
• .rel.data
– Informação de relocação para seção data ( q ) .text section .data section .bss section .symtab .rel.text .rel.data seção.data
– Endereços dos enderços (ponteiros para dados) que precisarão ser modificados no executável
• .debug
– Seção que relaciona posições no arquivo objeto com linhas do código fonte; para depuração (gcc -g)
.debug Section header table (required for relocatables)
Amarração (Linker)
Principais tarefas (recapitulando)
1. combinar todos os objetos em um executável
2. Resolver as referências externas
3. Relocar as referências absolutas à memória
Vejamos usando um exempo:
int e=7; m.c a.c extern int e; int main() { int r = a(); exit(0); } int *ep=&e; int x=15; int y; int a() { return *ep+x+y; }
Combinação de todos os objetos
em um executável
headers 0
t d
Arquivos objeto relocáveis Arquivo executável
text main() m.o i 7 headers main() a() system code int e = 7 system data
more system code system data .text .text .data .text .data system code
int *ep = &e a() a.o
int e = 7 int *ep = &e int x = 15 int y int x = 15 .text .data .bss .symtab .debug .data uninitialized data .bss
A união requer a relocação dos endereços
em cada módulo de forma a acrescentar sua posição dentro da concatenação
Resolução de referências
– Cada símbolo (entidade léxica) possui um valor(=seu endereço de memória).
– Referências podem serReferências podem ser locaislocaisououexternas.externas.
int e=7; int main() { int r = a(); exit(0); } m.c a.c extern int e; int *ep=&e; int x=15; int y; i () { Def of local symbol e Ref to external symbol e Def of D f f int a() { return *ep+x+y; } Ref to external symbol exit (defined in libc.so) local symbol ep Defs of local symbols x and y Refs of local symbols ep,x,y Def of local symbol a Ref to external symbol a
Resolução de referências
• Objetivo: associar cada referência externa a
exatamente uma (1) definição do símbolo
• Usa a tabela de símbolo de cada módulo m que
Usa a tabela de símbolo de cada módulo m que
contém:
– Símbolos globais: definidos em m e que podem ser referenciados por outros módulos (funções e
variáveis não definidos com static)
– Símbolos referenciados por m, mas definidos em outro módulo
– Símbolos locais: definfidos e referenciadosSímbolos locais: definfidos e referenciados
exclusivamente por m (funções e variáveis definidos com static)
Obs: as tabelas de símbolos são criadas pelo
montador (as), usando os símbolos exportados
na etapa de compilação.
Resolução de referências
• Pode ser impossível:
– Se um símbolo referenciado não foi declarado
– Linker produz uma mensagem de erro (“undefined reference to ‘foo’)
• Pode ser ambígua:
– se existem várias definições para o mesmo símbolo em diferentes módulos
– Linker usa o conceito de símbolo forte e fraco (variáveis incializadas VS não inicializadas) e as seguintes regras:
• R1: não permite definição de >1 símbolos fortes • R2: se houver definição de 1 símbolo forte e vários
fracos, escolha a definição forte
• R3: se só houver definições de símbolos fracos, escolha qualquer uma (de a cordo com a ordem dos módulos)
Exemplos
Linker escolhe o x de foo3.c por ser forte, e resultado é “x= 15212”!
Linker escolhe qualquer um dos x (ambos fracos) e resultado pode ser igualmente inesperado!
Mais um erro de difícil detecção
Linker escolhe o x de foo5.c por ser forte, mas agora x de bar5.c ocupa 8 bytes, e f() sobre escreve y!
Resultado é “x= 0x80000000”
• Para identificar possíveis conflitos use flag
warn-common no gcc
Relocação
• É o processo (no ligador) de atribuir endereços
de carga às várias partes do programa:
Ajustando o código e dados ( text e data) para – Ajustando o código e dados (.text e .data) para
refletir os endereços designados e
– Levando em conta os endereços dos símbolos definidos em outros módulos
• Na resolução de referências, as posições são
calculadas com relação ao endereço 0 (zero),
que seria o início do código.
• Quando a relocação é feita as posições de
memória calculadas na etapa anterior
funcionarão como distâncias (offsets)
Informação de relocação de m.o
Disassembly of section .text:00000000 <main>: 00000000 <main>: m.c 0: 55 pushl %ebp 1: 89 e5 movl %esp,%ebp 3: e8 fc ff ff ff call 4 <main+0x4> 4: R_386_PC32 a 8: 6a 00 pushl $0x0 a: e8 fc ff ff ff call b <main+0xb> b: R_386_PC32 exit f: 90 nop int e=7; int main() { int r = a(); exit(0); }
Disassembly of section .data: 00000000 <e>:
0: 07 00 00 00
Informação de Relocação de a.o
(.text)
a.c Disassembly of section .text:
extern int e; int *ep=&e; int x=15; int y; int a() { return *ep+x+y; } 00000000 <a>: 0: 55 pushl %ebp 1: 8b 15 00 00 00 movl 0x0,%edx 6: 00 3: R_386_32 ep 7: a1 00 00 00 00 movl 0x0,%eax 8: R_386_32 x c: 89 e5 movl %esp,%ebp e: 03 02 addl (%edx),%eax } 10: 89 ec movl %ebp,%esp 12: 03 05 00 00 00 addl 0x0,%eax 17: 00 14: R_386_32 y 18: 5d popl %ebp 19: c3 ret
Informação de Relocação de a.o
(.data)
a.c extern int e; int *ep=&e; int x=15; int y; int a() { return *ep+x+y;Disassembly of section .data: 00000000 <ep>: 0: 00 00 00 00 0: R_386_32 e 00000004 <x>: 4: 0f 00 00 00 p y }
Executável depois de resolução de
referência e relocação (.text)
08048530 <main>: 8048530: 55 pushl %ebp 8048531 89 5 l % % b 8048531: 89 e5 movl %esp,%ebp 8048533: e8 08 00 00 00 call 8048540 <a> 8048538: 6a 00 pushl $0x0804853a: e8 35 ff ff ff call 8048474 <_init+0x94> 804853f: 90 nop 08048540 <a>: 8048540: 55 pushl %ebp 8048541: 8b 15 1c a0 04 movl 0x804a01c,%edx 8048546: 08 Seção de Dados em 0x804a... 8048546: 08 8048547: a1 20 a0 04 08 movl 0x804a020,%eax 804854c: 89 e5 movl %esp,%ebp 804854e: 03 02 addl (%edx),%eax 8048550: 89 ec movl %ebp,%esp 8048552: 03 05 d0 a3 04 addl 0x804a3d0,%eax 8048557: 08 8048558: 5d popl %ebp 8048559: c3 ret
Bibliotecas
• Permitem agrupar módulos objeto relacionados em um arquivo
• No momento da ligação apenas os módulosNo momento da ligação, apenas os módulos “necessários” são copiados e combinados com o executável
• Para resolução de referências, processa-se as bibliotecas da esquerda para a direita!
gcc main.c mylib.a /usr/lib/libm.a • Driver gcc já amarra automaticamente com libc.ag j
• Para criar uma biblioteca, basta listar os arquivos objeto relocáveis, por exemplo:
Bibliotecas estáticas (archives)
p1.c p2.c
compilação
p1.o p2.o libc.a
Arquivo executável somente conterá código e dados de libc que são
Linker (ld) p compilação g q referenciadas em p1.c e p2.c
Carga (loading)
• O ato de copiar algumas seções para o espaço de endereçamento do processo, naturalmente,fazendo-se a relocação.
ELF header Program header table (required for executables)
.text section .data section .bss section symtab .text segment (r/o) Process image 0x08048494
init and shared lib segments 0x080483e0 Virtual addr .symtab .rel.text .rel.data .debug Section header table (required for relocatables)
.data segment (initialized r/w) .bss segment (uninitialized r/w) 0x0804a010 0x0804a3b0
Bibliotecas dinâmicas e
compartilhadas
• Principais desvantagens de bibliotecas estáticas:
– Replicação de código comum nos executáveis
• Desperdício de espaço em disco e na memória • Desperdício de espaço em disco e na memória
– Qualquer modificação (correção) nas bibliotecas requer uma re-amarração (re-linking) de todas as aplicações que as usam
• Bibliotecas dinâmicas
(ou dynamic link libraries,
DLLs) os módulos são dinâmicamente
DLLs) os módulos são dinâmicamente
carregados e ligados à aplicação em tempo de
carga ou em tempo de execução
• Vantagem adicional: estas bibliotecas podem ser compartilhadas por vários processos.
Bibliotecas Dinâmicas e compartilhadas
(cc1, as) m.c (cc1,as) a.c Biblioteca dinâmica m.o a.o libc.so Linker (ld) p Executável parcialmente ligado (em disco)Funções de libc.so chamdas por m.c & a.c são carregadas, ligadas, e compartilhadas entre vários processos.
Loader/Dynamic Linker (ld-linux.so)
Executável totalmente ligado (em ´RAM)