Registradores e CPU state
A CPU tem um punhado de variáveis ultra-rápidas embutidas no chip — os registradores. Entendê-los é o primeiro passo para ler qualquer código assembly.
Variáveis dentro da CPU
Quando você escreve int x = 5;, o compilador escolhe onde guardar esse valor.
Na maior parte do tempo, ele vai para um registrador — uma célula de memória
embutida diretamente no chip, a centímetros dos transistores que fazem os cálculos.
Acessar um registrador leva zero ciclos extras; acessar a RAM leva 200–300 ciclos.
Em x86-64, existem 16 registradores de uso geral de 64 bits: RAX, RBX, RCX, RDX, RSI, RDI, RSP, RBP, R8–R15. Cada um tem "versões menores" que acessam parte do mesmo registrador físico — EAX acessa os 32 bits inferiores de RAX, AX os 16, AL os 8 inferiores.
Explorador de registradores
Selecione um registrador, defina um valor (decimal ou 0x... hexadecimal) e veja a representação binária e os sub-registradores atualizarem em tempo real. Clique nas flags para alternás-las.
Acumulador — valor de retorno de funções, resultado de MUL/DIV
Escrever em AL muda apenas os bits 7–0 de EAX — os outros bits ficam intactos.
Convenções e o registrador de flags
// x86-64: os registradores de uso geral (64-bit) // Cada registrador tem versões menores que compartilham bits // RAX (64-bit) // └─ EAX (bits 31–0) // └─ AX (bits 15–0) // ├─ AH (bits 15–8) // └─ AL (bits 7–0) // Convenção de chamada x86-64 Linux (System V ABI): // Argumentos: RDI, RSI, RDX, RCX, R8, R9 // Retorno: RAX (e RDX para 128-bit) // Callee-saved: RBX, RBP, R12–R15 (função deve preservar) // Caller-saved: RAX, RCX, RDX, RSI, RDI, R8–R11 (pode sobrescrever) // Exemplo: tradução de C → x86-64 // int resultado = add(3, 5); mov edi, 3 ; 1º argumento → EDI (parte de RDI) mov esi, 5 ; 2º argumento → ESI call add ; chama a função ; resultado em EAX após o retorno
EFLAGS — o registrador de status
// EFLAGS: registrador de 32 bits com bits de status // Bit 0 — CF (Carry Flag) add al, 0xFF ; se al=1: resultado=0x100, CF=1 (carry) // Bit 6 — ZF (Zero Flag) sub eax, eax ; EAX=0 → ZF=1 je destino ; salta SE ZF=1 // Bit 7 — SF (Sign Flag) = cópia do bit 31 do resultado mov eax, -1 ; EAX = 0xFFFFFFFF → SF=1 // Bit 11 — OF (Overflow Flag): overflow com sinal mov eax, 0x7FFFFFFF ; INT_MAX com sinal add eax, 1 ; → 0x80000000 = -2147483648: OF=1 ! // Diferença CF vs OF: // CF = carry em aritmética sem sinal (unsigned) // OF = overflow em aritmética com sinal (signed) // A mesma instrução pode setar ambos, só um, ou nenhum.
movzx eax, al
(zero-extend) é diferente de simplesmente usar al diretamente.
Inspetor de registradores com GDB
Mini projeto: compile qualquer programa C simples com gcc -O0 -g -o prog prog.c, execute com gdb ./prog, coloque um breakpoint (break main) e use info registers para ver todos os registradores. Use si (step instruction) para avançar uma instrução por vez e observar o que muda.
Projeto principal: escreva uma função em C que some os elementos de um array de inteiros. Compile com gcc -O0 -S para gerar o assembly. Identifique: qual registrador guarda o ponteiro do array? Qual guarda o contador do loop? Qual retorna o resultado? Compare com -O2.
Desafio extra: use Python com a biblioteca ctypes para chamar uma função assembly escrita diretamente (use mmap para memória executável). Escreva os bytes de uma instrução ret (0xC3) e chame a função — ela retorna o valor de RAX do Python em si.
Teste sua intuição
Onde você encontra isso
Depuração com GDB / LLDB
Todo debugger mostra o estado dos registradores na pausa. Quando você vê "Segmentation fault", o endereço inválido estava em RSP (pilha corrompida) ou em algum registrador de ponteiro. info registers no GDB imprime todos os 16 registradores + EFLAGS em hex e decimal.
Engenharia reversa e CTF
Ao descompilar malware ou resolver desafios de reverse engineering, você lê assembly diretamente. Entender que o valor de retorno está sempre em RAX (Linux) ou EAX (Windows 32-bit) é o primeiro passo para entender o que uma função desconhecida faz sem ter o código-fonte.
Otimizações SIMD
Registradores YMM (256-bit) e ZMM (512-bit) — extensões AVX2/AVX-512 — permitem operar em 8 floats ou 4 doubles simultaneamente. Bibliotecas como NumPy, OpenCV e codecs de vídeo (x264, AV1) usam intrinsics SIMD para atingir desempenho de GFLOPs em um único core.