Do C ao binário ARM: gcc + objdump
A função
soma.c — soma de 1 a n:
int soma_ate(int n) {
int acc = 0;
for (int i = 1; i <= n; i++) {
acc += i;
}
return acc;
}Compilando para um objeto ARM (sem ligar, -c), alvo ARMv4T em estado ARM:
arm-none-eabi-gcc -c -O0 -march=armv4t -marm soma.c -o soma-O0.o
arm-none-eabi-gcc -c -O2 -march=armv4t -marm soma.c -o soma-O2.o
arm-none-eabi-objdump -d soma-O0.o-O0: a tradução literal
Cada variável (n, acc, i) mora num slot da pilha, referenciado por [fp, #offset]. Toda operação faz ldr → conta → str. É lento, mas é uma tradução direta do C — ideal para depurar. Repare no prólogo (push {fp} / sub sp) e no epílogo (pop {fp} / bx lr): a convenção de chamada AAPCS, assunto do artigo de sub-rotinas.
-O2: o mesmo resultado, outro caminho
Sem pilha, sem fp, tudo em registrador. Três transformações que valem notar:
- Nada de frame. A função não chama ninguém e cabe em registradores scratch (r0–r3), então não há prólogo/epílogo.
accnasce emr0. O compilador sabe que o retorno vai em r0 (AAPCS) e acumula direto lá.cmp i, nviroucmp i, n+1+bne. Em vez de "i <= n" (que precisa deble, um teste com sinal), o-O2pré-calculan+1uma vez e testai != n+1.bneé mais barato e o limite é loop-invariant.
Mesma semântica, metade das instruções. É a mesma máquina que o decoder do arm-jitter vai processar — o emulador não sabe nem se importa de onde o binário veio.
As seções do objeto
arm-none-eabi-objdump -h soma-O2.o:
Idx Name Size VMA LMA File off Algn
0 .text 00000030 ... CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000000 ... CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 ... ALLOC
3 .comment 00000019 ... CONTENTS, READONLY| Seção | O que guarda | No arquivo? |
|---|---|---|
.text | código executável | sim |
.rodata | constantes só-leitura (strings, tabelas const) | sim |
.data | variáveis globais inicializadas com valor ≠ 0 | sim |
.bss | variáveis globais zeradas (ou = 0) | não — só o tamanho; o loader zera |
.comment | metadados do compilador; descartável | sim, mas não carrega |
soma_ate não tem dados globais, então .data e .bss estão vazios. VMA (Virtual Memory Address) ainda é 0 porque este é um objeto não ligado — o arm-none-eabi-ld atribui os endereços finais, assunto do próximo artigo.
Símbolos e endereços
arm-none-eabi-nm soma-O2.o:
00000000 T soma_ateT = símbolo em .text, global (maiúscula). nm minúsculo (t, d, b) seria local. Um U seria um símbolo indefinido — uma função que este objeto usa mas não define, a ser resolvida na ligação.
Glossário
| Termo | O que é |
|---|---|
Objeto (.o) | código compilado mas ainda não ligado; endereços relativos |
-O0 / -O2 | nível de otimização: literal / agressivo |
| Prólogo / epílogo | as instruções que abrem/fecham o stack frame de uma função |
Frame pointer (fp, r11) | âncora fixa para acessar variáveis locais e argumentos na pilha |
.bss | dados zerados — ocupam RAM mas não espaço no arquivo |
| VMA | endereço onde a seção vai rodar (definido na ligação) |
Comandos
| Comando | O que faz |
|---|---|
arm-none-eabi-gcc -c -Ox -march=armv4t soma.c | compila para objeto sem ligar |
arm-none-eabi-gcc -S soma.c | para no assembly (soma.s), sem montar |
arm-none-eabi-objdump -d x.o | desmonta .text |
arm-none-eabi-objdump -h x.o | lista as seções e seus tamanhos |
arm-none-eabi-nm x.o | lista os símbolos (T/t/D/U/...) |
arm-none-eabi-readelf -a x.elf | tudo sobre o ELF (headers, seções, símbolos) |
Próximo passo
Você tem o binário. O resto do módulo 2 decodifica ele à mão — Thumb agora, e depois abre o decoder real do arm-jitter.
Trilha: Curso de Arquitetura ARM · Módulo 2, lição 1. Ver também: Decodificando instruções ARM à mão.