gdb-pratico/mods/05/README.org

231 lines
5 KiB
Org Mode
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#+title: Curso prático de introdução ao GDB
#+author: Blau Araujo
#+email: blau@debxp.org
* 5. Casos de uso
** Objetivo
Demonstrar o uso do GDB para localizar e solucionar bugs comuns, como:
- Falha de segmentação;
- Variável não inicializada;
- Loop infinito;
- Índice de vetor inválido;
- Parâmetro incorreto na chamada de uma função;
- Registradores incorretos em uma chamada de sistema;
- Desalinhamento da pilha;
- Erro de empilhamento e desempilhamento de registros;
- Estouro de buffer;
- Estouro de pilha;
- Vazamento de memória.
** Falha de segmentação
Acesso inválido à memória causado por um ponteiro não inicializado.
Código (=segfault.c=):
#+begin_src c
#include <stdio.h>
int main() {
int *p = NULL;
*p = 42; // ERRO: tentativa de escrever em um ponteiro nulo
printf("%d\n", *p);
return 0;
}
#+end_src
Compilação:
#+begin_example
:~$ gcc -g -o segfault segfault.c
$ gdb ./segfault
Reading symbols from ./segfault...
#+end_example
*** Processo de depuração
Verificar o que acontece ao executar:
#+begin_example
(gdb) r
Starting program: /home/blau/git/gdb-pratico/mods/05/exemplos/segfault
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x000055555555514d in main () at segfault.c:5
5 *p = 42; // ERRO: tentativa de escrever em um ponteiro nulo
#+end_example
Matando o processo:
#+begin_example
(gdb) k
[Inferior 1 (process 1034793) killed]
#+end_example
Definindo =main= como ponto de parada e executando:
#+begin_example
(gdb) b main
Breakpoint 5 at 0x555555555141: file segfault.c, line 4.
(gdb) r
Starting program: /home/blau/git/gdb-pratico/mods/05/exemplos/segfault
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 5, main () at segfault.c:4
4 int *p = NULL;
#+end_example
Listando variáveis locais antes e após a inicialização do ponteiro =p=:
#+begin_example
(gdb) i locals
p = 0x7fffffffdf40
(gdb) n
5 *p = 42; // ERRO: tentativa de escrever em um ponteiro nulo
(gdb) i locals
p = 0x0
#+end_example
O endereço de memória no ponteiro é claramente nulo (=0x0=)!
** Variável não inicializada
Uso de uma variável antes da atribuição de um valor (seu valor é lixo de memória).
Código do exemplo (=notinit.c=):
#+begin_src c
#include <stdio.h>
int main(void) {
int a; // PROBLEMA: Variável não inicializada!
printf("Digite um número: ");
scanf("%d", &a); // Se scanf falhar, o valor de a será lixo!
printf("Depois de %d vem %d\n", a, a + 1);
return 0;
}
#+end_src
Compilação e teste:
#+begin_example
:~$ gcc -g -o notinit notinit.c
:~$ ./notinit
Digite um número: (teclar CTRL+D) Depois de 32765 vem 32766
#+end_example
*** Processo de depuração
Basta executar o programa passo a passo examinando o valor da variável =a=:
#+begin_example
:~$ gdb ./notinit
Reading symbols from ./notinit...
(gdb) b main
Breakpoint 1 at 0x1151: file notinit.c, line 7.
(gdb) r
Starting program: /home/blau/git/gdb-pratico/mods/05/exemplos/notinit
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, main () at notinit.c:7
7 printf("Digite um número: ");
(gdb) p a
$1 = 32767
(gdb) n
8 scanf("%d", &a); // Se scanf falhar, o valor de a será lixo!
(gdb) n
Digite um número: 10 printf("Depois de %d vem %d\n", a, a + 1);
(gdb) p a
$2 = 32767
(gdb) n
Depois de 32767 vem 32768
12 return 0;
(gdb) c
Continuing.
[Inferior 1 (process 1039355) exited normally]
#+end_example
Com o erro na execução de =scanf= (simulado com o atalho =CTRL+D=), o valor
da variável não inicializada continuou sendo lixo de memória (32767).
** Loop infinito
Uma condição de parada incorreta faz com que as repetições não tenham fim.
Código de exemplo (=iloop.c=):
#+begin_src c
#include <stdio.h>
int main() {
int i = 0;
// A condição de parada nunca será atendida!
while (i < 100) {
printf("i = %d\n", i);
i *= 2; // Sempre zero!
}
return 0;
}
#+end_src
Compilação e teste:
#+begin_example
:~$ gcc -g -o iloop iloop.c
:~$ ./iloop
i = 0
i = 0
i = 0
i = 0
i = 0
i = 0
i = 0
i = 0
^C
#+end_example
*** Processo de depuração
#+begin_example
$ gdb ./iloop
Reading symbols from ./iloop...
(gdb) b main
Breakpoint 1 at 0x1141: file iloop.c, line 4.
(gdb) r
Starting program: /home/blau/git/gdb-pratico/mods/05/exemplos/iloop
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, main () at iloop.c:4
4 int i = 0;
(gdb) n
7 while (i < 100) {
(gdb) p i
$1 = 0
(gdb) n
8 printf("i = %d\n", i);
(gdb) n
i = 0
9 i *= 2; // Sempre zero!
(gdb) p i
$2 = 0
(gdb) n
7 while (i < 100) {
(gdb) p i
$3 = 0
#+end_example
Fica claro que o valor de nunca foi alterado, porque ~2 × 0 = 0~!