gdb-pratico/mods/03/README.org

3.6 KiB

Curso prático de introdução ao GDB

3. Formato ELF

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:

  • 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).

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:

:~$ 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

Símbolos no binário

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 (compilando com -g, por exemplo), o GDB é capaz de:

  • Exibir nomes legíveis, em vez de endereços de memória;
  • Acompanhar o fluxo do programa com o código-fonte;
  • Receber definições de breakpoints por nomes de funções;
  • Fazer a associação de códigos binários com as linhas do código-fonte;
  • Acessar nomes de variáveis, tipos, estruturas, etc.

Nós podemos verificar se o programa foi compilado com os símbolos de depuração com o utilitário file

Sem a opção -g:

:~$ gcc -o demo demo.c
:~$ file demo
demo: ELF 64-bit LSB pie executable [...] not stripped

Com a opção -g:

:~$ gcc -g -o demo demo.c
:~$ file demo
demo: ELF 64-bit LSB pie executable [...] with debug_info, not stripped

Repare que, desta vez, nós temos a informação with debug_info no final da linha.

Nós também podemos verificar se há símbolos de depuração a partir das informações no formato ELF:

:~$ readelf -S demo | grep debug
  [28] .debug_aranges    PROGBITS         0000000000000000  00003037
  [29] .debug_info       PROGBITS         0000000000000000  00003067
  [30] .debug_abbrev     PROGBITS         0000000000000000  00003180
  [31] .debug_line       PROGBITS         0000000000000000  0000324a
  [32] .debug_str        PROGBITS         0000000000000000  000032ba
  [33] .debug_line_str   PROGBITS         0000000000000000  00003367

Mas o próprio GDB informa se há símbolos de depuração ao iniciar…

Sem a opção -g:

:~$ gcc -o demo demo.c
:~$ gdb demo
Reading symbols from demo...
(No debugging symbols found in demo)

Com a opção -g:

:~$ gcc -g -o demo demo.c
:~$ gdb demo
Reading symbols from demo...