Artigo :: Começando com GDB
Introdução
Este guia completo cobre tudo o que você precisa para começar a depurar programas C com GDB, desde compilar com símbolos de depuração até inspecionar memória e controlar a execução do programa.
Visão Geral
GDB (GNU Debugger) é o depurador padrão para programas compilados com GCC. Ao contrário de ferramentas como strace que mostram chamadas de sistema, o GDB permite percorrer seu código-fonte linha por linha, inspecionar variáveis, definir breakpoints condicionais e até modificar o comportamento do programa durante a execução.
Seja rastreando um segfault, entendendo como o código executa, ou fazendo engenharia reversa de um binário, o GDB é uma ferramenta indispensável para qualquer desenvolvedor C/C++ ou pesquisador de segurança.
Pré-requisitos
Antes de começar, você deve:
- Ter o GCC instalado e entender compilação básica
- Conhecer fundamentos de programação C (variáveis, funções, ponteiros)
- Estar confortável com a linha de comando do Linux
- Opcionalmente, ler nosso Cheat Sheet de Compilação GCC para o básico de compilação
Compilando com Símbolos de Depuração
Para depurar efetivamente com GDB, você precisa de símbolos de depuração no seu binário. Esses símbolos mapeiam código de máquina de volta para seu código-fonte (funções, variáveis, números de linha).
| |
A flag -ggdb produz informações de depuração específicas para GDB com mais detalhes que a flag padrão -g.
Sempre compile builds de debug e release separados:
- Build de debug:
gcc -ggdb -O0(sem otimização, símbolos completos) - Build de release:
gcc -O2(otimizado, remover símbolos antes da distribuição)
Otimizações podem tornar a depuração confusa pois o compilador reordena e elimina código.
Gerenciando Símbolos de Depuração
Removendo Símbolos de Binários
Para deploys em produção, remova símbolos de depuração para reduzir o tamanho do binário:
Usando strip (remove todos os símbolos):
| |
Usando objcopy (cria arquivo de debug separado):
| |
Esta abordagem mantém os símbolos em um arquivo separado, permitindo depurar problemas de produção sem distribuir binários grandes.
Adicionando Símbolos de Volta
Opção 1: Vincular arquivo de debug ao binário
| |
O GDB automaticamente encontrará e carregará debug_file ao depurar binary_file.
Opção 2: Carregar arquivo de símbolos manualmente no GDB
| |
Analisando Símbolos com nm
O comando nm lista símbolos em arquivos objeto e binários, útil para entender quais funções e variáveis existem em um programa.
Convenções de capitalização de símbolos:
- Minúsculas = símbolos locais (escopo de arquivo)
- Maiúsculas = símbolos externos (escopo global)
Tipos Comuns de Símbolos
| Símbolo | Significado |
|---|---|
| T | Na Seção de Texto (código) |
| D | Na Seção de Dados Inicializados |
| B | Na Seção de Dados Não-Inicializados (BSS) |
| U | Indefinido (referência externa) |
| A | Símbolo absoluto |
| N | Símbolo de depuração |
man nm para ver todos os tipos de símbolos. Estes são os mais comuns que você encontrará.Comandos Úteis do nm
Procurar por um símbolo específico:
| |
Exibir símbolos ordenados por endereço:
| |
Mostrar apenas símbolos externos (globais):
| |
Mostrar todos os símbolos incluindo símbolos de depuração:
| |
Listar apenas símbolos na seção TEXT (código executável):
| |
Desmontando com objdump
O comando objdump desmonta binários, mostrando o código assembly real:
| |
Isso mostra 20 linhas de assembly começando da função main usando sintaxe Intel.
-M intel: Usa sintaxe Intel (mais legível que AT&T padrão)-D: Desmonta todas as seções (não apenas código)-d: Desmonta apenas seções de código-S: Intercala código-fonte com assembly (requer símbolos de debug)
Referência de Comandos do GDB
Iniciando o GDB
| |
Alternando Sintaxe (AT&T vs Intel)
Por padrão, o GDB usa sintaxe assembly AT&T. A sintaxe Intel é frequentemente mais legível:
| |
Adicione isso ao ~/.gdbinit para tornar permanente.
Desmontando Código
| |
Desmontar um intervalo:
| |
Dois argumentos (separados por vírgula) especificam um intervalo: início,fim ou início,+tamanho.
Executando o Programa
| |
Listando Código-Fonte
Requer arquivo-fonte disponível no mesmo diretório:
| |
O GDB precisa do arquivo-fonte no mesmo caminho onde foi compilado. Se você compilou em /home/user/projeto mas executa o GDB de /tmp, use:
| |
Inspecionando Estado do Programa
Informações de registradores:
| |
Listagem de funções:
| |
Arquivos-fonte:
| |
Informações de variáveis:
| |
Dump da tabela de símbolos:
| |
Trabalhando com Breakpoints
Definir breakpoints:
| |
Listar breakpoints:
| |
Habilitar/desabilitar breakpoints:
| |
Deletar breakpoints:
| |
Break apenas quando uma condição for verdadeira:
| |
Isso é incrivelmente poderoso para depurar loops ou condições de erro específicas.
Modificando Memória e Registradores
O GDB permite modificar o estado do programa durante a execução:
| |
Opcodes também podem ser modificados:
| |
objcopy ou editores hexadecimais.Definindo Macros
Crie comandos personalizados que executam automaticamente:
| |
Isso executa após cada passo, mostrando registradores, pilha e próximas instruções. Salve em ~/.gdbinit para usar em todas as sessões.
Trabalhando com Dados
Imprimir valores de registradores em diferentes formatos:
| |
Exemplo de saída:
(gdb) print /d $eax
$17 = 13
(gdb) print /t $eax
$18 = 1101
(gdb) print /x $eax
$19 = 0xd
Examinar memória (x/nyz):
Formato: x/[quantidade][formato][tamanho] endereço
- quantidade (n): Número de itens a exibir
- formato (y):
c(char),d(decimal),x(hex),s(string) - tamanho (z):
b(byte),h(halfword/2 bytes),w(word/4 bytes),g(giant/8 bytes)
Exemplos:
| |
Variáveis de Conveniência
Crie variáveis temporárias para depuração:
| |
Essas variáveis existem apenas na sessão do GDB.
Chamando Funções
Você pode chamar funções no programa sendo depurado:
| |
malloc() para alocar memória, modifique-a e veja como seu programa lida com isso.Pontos-Chave
- Compile com
-ggdbpara incluir símbolos de depuração para debugging em nível de código - Use arquivos de debug separados para binários de produção para economizar espaço
- nm e objdump ajudam a analisar binários antes da depuração
- GDB permite controlar a execução com breakpoints, stepping e breaks condicionais
- Inspecione e modifique memória, registradores e variáveis durante runtime
- Breakpoints condicionais são poderosos para depurar cenários específicos
- Plugin GEF melhora dramaticamente a experiência do GDB com contexto visual
Exercícios Práticos
- Escreva um programa C simples com um bug, compile com
-ggdb, e use GDB para encontrar o problema - Defina um breakpoint em
main(), execute o programa e percorra linha por linha usandonextestep - Use
x/20xw $esppara examinar a pilha e entender como variáveis locais são armazenadas - Crie um breakpoint condicional que só dispara quando um contador de loop atinge 100
- Use
callpara invocar uma função com diferentes argumentos sem recompilar - Pratique desmontar funções com
disassemblee correlacionar assembly com código-fonte
Leitura Adicional
- Cheat Sheet de Compilação GCC - Aprenda opções de compilação e gerenciamento de símbolos de debug
- Instalando GEF - Turbine seu GDB com uma interface moderna
- Depurando com strace - Rastreamento de chamadas de sistema quando o código-fonte não está disponível
- Syscalls do Linux em Assembly - Entendendo execução de programas em baixo nível
- Manual do GDB - Documentação oficial completa