atualização das partes 1 2 e 3
This commit is contained in:
parent
7085ead45c
commit
325a77ae6c
3 changed files with 378 additions and 35 deletions
|
@ -8,7 +8,8 @@
|
|||
|
||||
- Entender o que é o GDB;
|
||||
- Conhecer o conceito de /depuração/;
|
||||
- Demonstrar o GDB em ação.
|
||||
- Demonstrar o GDB em ação;
|
||||
- Apresentar uma lista de comandos essenciais.
|
||||
|
||||
** O que é o GDB
|
||||
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
|
||||
* 2. Instalação e configurações de início
|
||||
|
||||
** Objetivos
|
||||
|
||||
- Como instalar o GDB nas principais distribuições GNU/Linux;
|
||||
- Conhecer algumas possibilidades de customização.
|
||||
|
||||
** Instalação
|
||||
|
||||
*** Debian e derivados
|
||||
|
|
|
@ -2,47 +2,174 @@
|
|||
#+author: Blau Araujo
|
||||
#+email: blau@debxp.org
|
||||
|
||||
* 3. Formato ELF
|
||||
* 3. Binários executáveis
|
||||
|
||||
Todo arquivo executável pelo sistema é construído em um binário segundo um
|
||||
formato padrão. No GNU/Linux, esse formato é o *ELF* (/Executable and Linkable Format/),
|
||||
que inclui no binário:
|
||||
** Objetivos
|
||||
|
||||
- O código de máquina do programa;
|
||||
- Dados de variáveis globais e estáticas;
|
||||
- Dados constantes;
|
||||
- Tabelas de símbolos;
|
||||
- Informações de depuração (se incluídas).
|
||||
- Entender o que é um programa para o sistema operacional;
|
||||
- Conhecer os elementos do formato de binários executáveis ELF;
|
||||
- Investigar os símbolos de depuração;
|
||||
- Descobrir como programas são executados.
|
||||
|
||||
Os primeiros bytes de um arquivo ELF contém um cabeçalho com diversas
|
||||
informações sobre o binário, o que nós podemos listar com o utilitário
|
||||
=readelf=:
|
||||
** O que são programas
|
||||
|
||||
Do ponto de vista do sistema operacional GNU/Linux (e de sistemas UNIX-like em
|
||||
geral), um programa é sempre um arquivo binário contendo um conjunto de instruções
|
||||
que a CPU do computador é capaz de executar. Isso não significa que todo programa
|
||||
é escrito para ser transformado em um arquivo binário e, só então, ser executado!
|
||||
É preciso entender que, o que nós escrevemos e chamamos de "programa", pode
|
||||
não ser exatamente o que o sistema operacional vai executar.
|
||||
|
||||
Para nós, programas podem ser...
|
||||
|
||||
- Escritos diretamente na linguagem que a CPU é capaz de interpretar e executar
|
||||
(código de máquina, com baixo nível de abstração), e apenas armazenados como
|
||||
arquivos binários;
|
||||
- Escritos usando mnemônicos e pseudoinstruções relativamente simples (linguagem
|
||||
de montagem, ou assembly), e então montados em código de máquina para gerar
|
||||
arquivos binários executáveis;
|
||||
- Escritos em linguagens com expressões e regras de sintaxe mais próximas da
|
||||
escrita humana (alto nível de abstração), e depois traduzidos para código de
|
||||
máquina por meio de um processo de compilação;
|
||||
- Escritos em linguagens de alto nível, projetadas para serem lidas e
|
||||
interpretadas em tempo de execução por outro programa (o interpretador), que é
|
||||
um binário executável.
|
||||
|
||||
Mas o sistema operacional só executa arquivos binários que seguem uma convenção
|
||||
específica de formato.
|
||||
|
||||
*** O que significa executar
|
||||
|
||||
Em sistemas GNU/Linux e UNIX-like, executar um programa significa:
|
||||
|
||||
- Criar uma estrutura de dados para gerenciar a execução do programa (processo);
|
||||
- Carregar o conteúdo do binário executável na memória;
|
||||
- Carregar na memória o conteúdo de outros arquivos necessários para o
|
||||
funcionamento do programa, como bibliotecas compartilhadas;
|
||||
- Disponibilizar um espaço de endereços de memória para uso exclusivo do
|
||||
programa;
|
||||
- Dar acesso a dados do ambiente de execução, como variáveis exportadas e
|
||||
argumentos passados na invocação do programa;
|
||||
- Conceder acesso a recursos diversos do sistema, como dispositivos de terminal
|
||||
e descritores de arquivos;
|
||||
- Administrar a passagem do controle da CPU para o programa;
|
||||
- Monitorar o término do programa, mantendo sua estrutura de processo até que o
|
||||
término seja completamente tratado.
|
||||
|
||||
Para que o sistema possa realizar todas essas etapas corretamente, é necessário
|
||||
que o conteúdo do binário executável siga uma convenção específica de organização
|
||||
de dados. Essa convenção define, por exemplo, onde se encontram as instruções do
|
||||
programa, os dados que ele utiliza e as informações sobre bibliotecas externas.
|
||||
No sistema GNU/Linux, o formato padrão utilizado para isso é o ELF (/Executable
|
||||
and Linkable Format/).
|
||||
|
||||
** O formato ELF
|
||||
|
||||
Um arquivo no formato ELF possui uma estrutura interna bem definida que informa
|
||||
ao sistema onde estão -- e onde devem ser carregados na memória -- os dados e os
|
||||
elementos essenciais para a execução do programa, entre eles:
|
||||
|
||||
- As instruções do programa em código de máquina (seção =.text=);
|
||||
- Dados constantes (=.rodata=);
|
||||
- Dados de variáveis globais e estáticas (seções =.data= e =.bss=);
|
||||
- Tabelas de símbolos do programa e de seções;
|
||||
- Tabelas de símbolos de depuração (se incluídas).
|
||||
|
||||
*** Passagem do arquivo para a memória
|
||||
|
||||
O conteúdo do binário no formato ELF é organizado em seções descritas em várias
|
||||
tabelas. Quando um processo é iniciado para executar o programa, essas seções são
|
||||
mapeadas para segmentos de memória correspondentes em seu espaço de endereços.
|
||||
Isso é feito por uma biblioteca do sistema chamada de /loader/ (carregador).
|
||||
|
||||
No GNU/Linux, o /loader/ é a biblioteca compartilhada =ld-linux= e, entre suas várias
|
||||
atribuições, nós podemos destacar:
|
||||
|
||||
- Carregar o programa na memória, mapeando as seções do arquivo ELF para os
|
||||
segmentos apropriados no espaço de endereços do processo.
|
||||
- Configurar o ambiente de execução, incluindo a configuração de uma estrutura
|
||||
de pilha (/stack/) no espaço de endereços do processo.
|
||||
- Resolver dependências de bibliotecas compartilhadas, carregando-as no espaço
|
||||
de endereços do processo, se for necessário.
|
||||
|
||||
*** Layout de memória
|
||||
|
||||
Após o carregamento do binário e a configuração do ambiente de execução, o espaço
|
||||
de endereços do processo estará organizado desta forma:
|
||||
|
||||
#+begin_example
|
||||
:~$ readelf -h demo
|
||||
Cabeçalho ELF:
|
||||
Magia: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
|
||||
Classe: ELF64
|
||||
Dados: complemento 2, little endian
|
||||
Versão: 1 (actual)
|
||||
OS/ABI: UNIX - System V
|
||||
Versão ABI: 0
|
||||
Tipo: DYN (Position-Independent Executable file)
|
||||
Máquina: Advanced Micro Devices X86-64
|
||||
Versão: 0x1
|
||||
Endereço do ponto de entrada: 0x1050
|
||||
Início dos cabeçalhos do programa: 64 (bytes no ficheiro)
|
||||
Start of section headers: 14944 (bytes no ficheiro)
|
||||
Bandeiras: 0x0
|
||||
Tamanho deste cabeçalho: 64 (bytes)
|
||||
Tamanho dos cabeçalhos do programa:56 (bytes)
|
||||
Nº de cabeçalhos do programa: 14
|
||||
Tamanho dos cabeçalhos de secção: 64 (bytes)
|
||||
Nº dos cabeçalhos de secção: 37
|
||||
Índice de tabela de cadeias da secção: 36
|
||||
ENDEREÇO MAIS ALTO +-----------------------+ - Dados de ambiente
|
||||
| | - Argumentos de linha de comando
|
||||
| PILHA | - Endereços de retorno
|
||||
| | - Dados locais de chamadas de funções
|
||||
+-----------------------+
|
||||
| ↓ |
|
||||
| |
|
||||
| |
|
||||
+-----------------------+
|
||||
| MMAP | - Mapeamento de bibliotecas compartilhadas
|
||||
+-----------------------+
|
||||
| |
|
||||
| HEAP | - Dados dinâmicos do programa
|
||||
| |
|
||||
+-----------------------+
|
||||
| SEGMENTO .BSS | - Dados globais e estáticos não inicializados
|
||||
+-----------------------+
|
||||
| SEGMENTO .DATA | - Dados globais e estáticos inicializados
|
||||
+-----------------------+
|
||||
| SEGMENTO .RODATA | - Dados constantes
|
||||
+-----------------------+
|
||||
| SEGMENTO .TEXT | - Instruções e funções do programa
|
||||
ENDEREÇO MAIS BAIXO +-----------------------+
|
||||
#+end_example
|
||||
|
||||
** Símbolos no binário
|
||||
*** Utilitários para examinar binários ELF
|
||||
|
||||
**** Utilitário =readelf=
|
||||
|
||||
Imprime cabeçalhos e seções de binários ELF.
|
||||
|
||||
Exemplos de uso:
|
||||
|
||||
#+begin_example
|
||||
readelf -h PROGRAMA Informações do cabeçalho ELF
|
||||
readelf -l PROGRAMA Lista de cabeçalhos do programa
|
||||
readelf -S PROGRAMA Lista dos cabeçalhos de seções do arquivo
|
||||
readelf -x SEÇÂO PROGRAMA Exibe o conteúdo de uma seção em hexadecimal
|
||||
#+end_example
|
||||
|
||||
**** Utilitário =ldd=
|
||||
|
||||
Lista dependências de bibliotecas compartilhadas.
|
||||
|
||||
Exemplo de uso:
|
||||
|
||||
#+begin_example
|
||||
ldd PROGRAMA Lista as dependências dinâmicas do programa
|
||||
#+end_example
|
||||
|
||||
**** Utilitário =nm=
|
||||
|
||||
Lista símbolos declarados no binário, como funções e variáveis globais.
|
||||
|
||||
Exemplo de uso:
|
||||
|
||||
#+begin_example
|
||||
nm PROGRAMA Lista todos os símbolos no programa.
|
||||
#+end_example
|
||||
|
||||
**** Utilitário =objdump=
|
||||
|
||||
Analisa, desmonta e imprime informações detalhadas sobre as seções de um
|
||||
programa.
|
||||
|
||||
Exemplo de uso:
|
||||
|
||||
#+begin_example
|
||||
objdump -d PROGRAMA Desmonta e exibe o conteúdo das seções executáveis do programa.
|
||||
#+end_example
|
||||
|
||||
** Símbolos de depuração
|
||||
|
||||
Símbolos são nomes associados a elementos do programa, como funções e
|
||||
variáveis. Quando os símbolos de depuração são incluídos no binário
|
||||
|
@ -107,3 +234,213 @@ Com a opção =-g=:
|
|||
:~$ gdb demo
|
||||
Reading symbols from demo...
|
||||
#+end_example
|
||||
|
||||
** Inspecionando a execução de programas
|
||||
|
||||
Com o GDB, nós podemos obter várias informações relacionadas à execução de
|
||||
programas.
|
||||
|
||||
Utilizando o programa =demo.c= como exemplo:
|
||||
|
||||
#+begin_example
|
||||
:~$ gcc -g -o demo demo.c
|
||||
:~$ gdb ./demo
|
||||
Reading symbols from ./demo...
|
||||
(gdb)
|
||||
#+end_example
|
||||
|
||||
*** Listagem de símbolos
|
||||
|
||||
#+begin_example
|
||||
(gdb) info files
|
||||
Symbols from "/home/blau/git/gdb-pratico/mods/01/demo".
|
||||
Local exec file:
|
||||
`/home/blau/git/gdb-pratico/mods/01/demo', file type elf64-x86-64.
|
||||
Entry point: 0x1050
|
||||
0x0000000000000350 - 0x0000000000000370 is .note.gnu.property
|
||||
0x0000000000000370 - 0x0000000000000394 is .note.gnu.build-id
|
||||
0x0000000000000394 - 0x00000000000003b0 is .interp
|
||||
0x00000000000003b0 - 0x00000000000003d4 is .gnu.hash
|
||||
0x00000000000003d8 - 0x0000000000000480 is .dynsym
|
||||
0x0000000000000480 - 0x000000000000050f is .dynstr
|
||||
0x0000000000000510 - 0x000000000000051e is .gnu.version
|
||||
0x0000000000000520 - 0x0000000000000550 is .gnu.version_r
|
||||
0x0000000000000550 - 0x0000000000000610 is .rela.dyn
|
||||
0x0000000000000610 - 0x0000000000000628 is .rela.plt
|
||||
0x0000000000001000 - 0x0000000000001017 is .init
|
||||
0x0000000000001020 - 0x0000000000001040 is .plt
|
||||
0x0000000000001040 - 0x0000000000001048 is .plt.got
|
||||
0x0000000000001050 - 0x000000000000119b is .text
|
||||
0x000000000000119c - 0x00000000000011a5 is .fini
|
||||
0x0000000000002000 - 0x0000000000002013 is .rodata
|
||||
0x0000000000002014 - 0x0000000000002048 is .eh_frame_hdr
|
||||
0x0000000000002048 - 0x0000000000002114 is .eh_frame
|
||||
0x0000000000002114 - 0x0000000000002134 is .note.ABI-tag
|
||||
0x0000000000003dd0 - 0x0000000000003dd8 is .init_array
|
||||
0x0000000000003dd8 - 0x0000000000003de0 is .fini_array
|
||||
0x0000000000003de0 - 0x0000000000003fc0 is .dynamic
|
||||
0x0000000000003fc0 - 0x0000000000003fe8 is .got
|
||||
0x0000000000003fe8 - 0x0000000000004008 is .got.plt
|
||||
0x0000000000004008 - 0x0000000000004018 is .data
|
||||
0x0000000000004018 - 0x0000000000004020 is .bss
|
||||
#+end_example
|
||||
|
||||
*** Listagem de símbolos conhecidos
|
||||
|
||||
Funções:
|
||||
|
||||
#+begin_example
|
||||
(gdb) info functions
|
||||
All defined functions:
|
||||
|
||||
File demo.c:
|
||||
8: int main();
|
||||
3: int soma(int, int);
|
||||
|
||||
Non-debugging symbols:
|
||||
0x0000000000001000 _init
|
||||
0x0000000000001030 printf@plt
|
||||
0x0000000000001040 __cxa_finalize@plt
|
||||
0x0000000000001050 _start
|
||||
0x0000000000001080 deregister_tm_clones
|
||||
0x00000000000010b0 register_tm_clones
|
||||
0x00000000000010f0 __do_global_dtors_aux
|
||||
0x0000000000001130 frame_dummy
|
||||
0x000000000000119c _fini
|
||||
#+end_example
|
||||
|
||||
Variáveis:
|
||||
|
||||
#+begin_example
|
||||
(gdb) info variables
|
||||
All defined variables:
|
||||
|
||||
Non-debugging symbols:
|
||||
0x0000000000002000 _IO_stdin_used
|
||||
0x0000000000002014 __GNU_EH_FRAME_HDR
|
||||
0x0000000000002110 __FRAME_END__
|
||||
0x0000000000002114 __abi_tag
|
||||
0x0000000000003dd0 __frame_dummy_init_array_entry
|
||||
0x0000000000003dd8 __do_global_dtors_aux_fini_array_entry
|
||||
0x0000000000003de0 _DYNAMIC
|
||||
0x0000000000003fe8 _GLOBAL_OFFSET_TABLE_
|
||||
0x0000000000004008 __data_start
|
||||
0x0000000000004008 data_start
|
||||
0x0000000000004010 __dso_handle
|
||||
0x0000000000004018 __TMC_END__
|
||||
0x0000000000004018 __bss_start
|
||||
0x0000000000004018 _edata
|
||||
0x0000000000004018 completed
|
||||
0x0000000000004020 _end
|
||||
#+end_example
|
||||
|
||||
*** Examinando o estado da memória
|
||||
|
||||
A inspeção da memória só pode ser feita com o início de um processo associado
|
||||
à execução do programa, portanto...
|
||||
|
||||
#+begin_example
|
||||
(gdb) b main
|
||||
Breakpoint 1 at 0x115b: file demo.c, line 9.
|
||||
(gdb) r
|
||||
Starting program: /home/blau/git/gdb-pratico/mods/01/demo
|
||||
[Thread debugging using libthread_db enabled]
|
||||
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
|
||||
|
||||
Breakpoint 1, main () at demo.c:9
|
||||
9 int x = 10;
|
||||
#+end_example
|
||||
|
||||
Para exibir o layout do mapeamento de memória do processo:
|
||||
|
||||
#+begin_example
|
||||
(gdb) info proc mappings
|
||||
process 1029871
|
||||
Mapped address spaces:
|
||||
|
||||
Start Addr End Addr Size Offset Perms File
|
||||
0x0000555555554000 0x0000555555555000 0x1000 0x0 r--p /home/blau/git/gdb-pratico/mods/01/demo
|
||||
0x0000555555555000 0x0000555555556000 0x1000 0x1000 r-xp /home/blau/git/gdb-pratico/mods/01/demo
|
||||
0x0000555555556000 0x0000555555557000 0x1000 0x2000 r--p /home/blau/git/gdb-pratico/mods/01/demo
|
||||
0x0000555555557000 0x0000555555558000 0x1000 0x2000 r--p /home/blau/git/gdb-pratico/mods/01/demo
|
||||
0x0000555555558000 0x0000555555559000 0x1000 0x3000 rw-p /home/blau/git/gdb-pratico/mods/01/demo
|
||||
0x00007ffff7da3000 0x00007ffff7da6000 0x3000 0x0 rw-p
|
||||
0x00007ffff7da6000 0x00007ffff7dce000 0x28000 0x0 r--p /usr/lib/x86_64-linux-gnu/libc.so.6
|
||||
0x00007ffff7dce000 0x00007ffff7f33000 0x165000 0x28000 r-xp /usr/lib/x86_64-linux-gnu/libc.so.6
|
||||
0x00007ffff7f33000 0x00007ffff7f89000 0x56000 0x18d000 r--p /usr/lib/x86_64-linux-gnu/libc.so.6
|
||||
0x00007ffff7f89000 0x00007ffff7f8d000 0x4000 0x1e2000 r--p /usr/lib/x86_64-linux-gnu/libc.so.6
|
||||
0x00007ffff7f8d000 0x00007ffff7f8f000 0x2000 0x1e6000 rw-p /usr/lib/x86_64-linux-gnu/libc.so.6
|
||||
0x00007ffff7f8f000 0x00007ffff7f9c000 0xd000 0x0 rw-p
|
||||
0x00007ffff7fbf000 0x00007ffff7fc1000 0x2000 0x0 rw-p
|
||||
0x00007ffff7fc1000 0x00007ffff7fc5000 0x4000 0x0 r--p [vvar]
|
||||
0x00007ffff7fc5000 0x00007ffff7fc7000 0x2000 0x0 r-xp [vdso]
|
||||
0x00007ffff7fc7000 0x00007ffff7fc8000 0x1000 0x0 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||||
0x00007ffff7fc8000 0x00007ffff7ff0000 0x28000 0x1000 r-xp /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||||
0x00007ffff7ff0000 0x00007ffff7ffb000 0xb000 0x29000 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||||
0x00007ffff7ffb000 0x00007ffff7ffd000 0x2000 0x34000 r--p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||||
0x00007ffff7ffd000 0x00007ffff7ffe000 0x1000 0x36000 rw-p /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
|
||||
0x00007ffff7ffe000 0x00007ffff7fff000 0x1000 0x0 rw-p
|
||||
0x00007ffffffde000 0x00007ffffffff000 0x21000 0x0 rw-p [stack]
|
||||
#+end_example
|
||||
|
||||
*** Observando o conteúdo da pilha
|
||||
|
||||
Terminando a execução anterior (=kill=):
|
||||
|
||||
#+begin_example
|
||||
(gdb) k
|
||||
[Inferior 1 (process 1030168) killed]
|
||||
#+end_example
|
||||
|
||||
Executando novamente:
|
||||
|
||||
#+begin_example
|
||||
(gdb) r
|
||||
Starting program: /home/blau/git/gdb-pratico/mods/01/demo a b c
|
||||
[Thread debugging using libthread_db enabled]
|
||||
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
|
||||
|
||||
Breakpoint 1, main () at demo.c:9
|
||||
9 int x = 10;
|
||||
#+end_example
|
||||
|
||||
Avançar a execução até a chamada da função =soma=:
|
||||
|
||||
#+begin_example
|
||||
(gdb) n
|
||||
10 int y = 20;
|
||||
(gdb) n
|
||||
11 int z = soma(x, y);
|
||||
#+end_example
|
||||
|
||||
Inspecionar o conteúdo da pilha neste momento:
|
||||
|
||||
#+begin_example
|
||||
(gdb) bt
|
||||
#0 main () at demo.c:11
|
||||
#+end_example
|
||||
|
||||
Avançar a execução entrando na função =soma=:
|
||||
|
||||
#+begin_example
|
||||
(gdb) s
|
||||
soma (a=10, b=20) at demo.c:4
|
||||
4 int resultado = a + b;
|
||||
#+end_example
|
||||
|
||||
Inspecionar o conteúdo da pilha:
|
||||
|
||||
#+begin_example
|
||||
(gdb) bt
|
||||
#0 soma (a=10, b=20) at demo.c:4
|
||||
#1 0x0000555555555178 in main () at demo.c:11
|
||||
#+end_example
|
||||
|
||||
*** Listagem de bibliotecas compartilhadas carregadas
|
||||
|
||||
#+begin_example
|
||||
(gdb) info sharedlibrary
|
||||
From To Syms Read Shared Object Library
|
||||
0x00007ffff7fc8000 0x00007ffff7fef2d1 Yes /lib64/ld-linux-x86-64.so.2
|
||||
0x00007ffff7dce400 0x00007ffff7f3217d Yes /lib/x86_64-linux-gnu/libc.so.6
|
||||
#+end_example
|
||||
|
|
Loading…
Add table
Reference in a new issue