parcial da aula 3
This commit is contained in:
parent
13353156c1
commit
3695f289ef
1 changed files with 230 additions and 0 deletions
230
curso/aula-03.org
Normal file
230
curso/aula-03.org
Normal file
|
@ -0,0 +1,230 @@
|
||||||
|
#+title: 3 -- O formato binário ELF
|
||||||
|
#+author: Blau Araujo
|
||||||
|
#+email: cursos@blauaraujo.com
|
||||||
|
|
||||||
|
|
||||||
|
#+options: toc:3
|
||||||
|
|
||||||
|
* Objetivos
|
||||||
|
|
||||||
|
- Compreender a estrutura do formato ELF.
|
||||||
|
- Compreender a definição das seções do programa com a linguagem Assembly.
|
||||||
|
- Explorar as seções do binário com ferramentas =readelf=, =objdump= e =hexdump=.
|
||||||
|
- Relacionar o binário ELF com seu conteúdo em Assembly.
|
||||||
|
|
||||||
|
* O que é o formato ELF
|
||||||
|
|
||||||
|
O formato binário ELF, de /Executable and Linking Format/ (/Formato Executável
|
||||||
|
e de Ligação/) foi originalmente desenvolvido e publicado como parte da /Interface
|
||||||
|
Binária de Aplicações/ (ABI) do Unix System V Release 4 (SVR4). Desde então,
|
||||||
|
tornou-se o padrão adotado pela maioria dos sistemas /Unix-like/ para representar
|
||||||
|
arquivos objeto binários, como executáveis e bibliotecas.
|
||||||
|
|
||||||
|
** Principais tipos de arquivos objeto
|
||||||
|
|
||||||
|
Os arquivos objeto, criados por montadores e editores de ligações
|
||||||
|
(/link-editores/), são representações binárias de programas destinados
|
||||||
|
a serem executados diretamente por um processador. As especificações
|
||||||
|
do ELF para o Linux definem três tipos principais de arquivos objeto:
|
||||||
|
|
||||||
|
- Arquivo relocável ::
|
||||||
|
|
||||||
|
Contém código e dados preparados para serem ligados a outros objetos
|
||||||
|
para criar um executável ou um objeto compartilhado.
|
||||||
|
|
||||||
|
- Arquivo executável ::
|
||||||
|
|
||||||
|
O arquivo de um programa que pode ser executado.
|
||||||
|
|
||||||
|
- Arquivo objeto compartilhado ::
|
||||||
|
|
||||||
|
Contém código e dados que podem ser ligados de duas formas: com outros
|
||||||
|
arquivos relocáveis para criar um outro objeto, ou com um executável e
|
||||||
|
outros objetos compartilhados para formar a imagem de um processo.
|
||||||
|
|
||||||
|
* Formato do arquivo
|
||||||
|
|
||||||
|
Os arquivos objeto participam tanto da link-edição quanto da execução de
|
||||||
|
programas. Portanto, seu formato deve acomodar as estruturas necessárias
|
||||||
|
para ambas as atividades. Na link-edição, o foco está nas seções e na
|
||||||
|
tabela de cabeçalhos de seções, enquanto que, na execução, o carregador
|
||||||
|
(/loader/) utiliza os cabeçalhos do programa para mapear segmentos na
|
||||||
|
memória.
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
LINK-EDIÇÃO (ARQUIVO) EXECUÇÃO (MEMÓRIA)
|
||||||
|
┌────────────────────────┐ ┌────────────────────────┐
|
||||||
|
│ CABEÇALHO ELF │ │ CABEÇALHO ELF │
|
||||||
|
├────────────────────────┤ ├────────────────────────┤
|
||||||
|
│ TABELA DE CABEÇALHOS │ │ TABELA DE CABEÇALHOS │
|
||||||
|
│ DO PROGRAMA (OPCIONAL) │ │ DO PROGRAMA │
|
||||||
|
├────────────────────────┤ ├────────────────────────┤
|
||||||
|
│ SEÇÃO 1 │ │ │
|
||||||
|
├────────────────────────┤ │ SEGMENTO 1 │
|
||||||
|
│ SEÇÃO 2 │ │ │
|
||||||
|
├────────────────────────┤ ├────────────────────────┤
|
||||||
|
│ ... │ │ │
|
||||||
|
├────────────────────────┤ │ SEGMENTO 2 │
|
||||||
|
│ SEÇÃO N │ │ │
|
||||||
|
├────────────────────────┤ ├────────────────────────┤
|
||||||
|
│ ... │ │ ... │
|
||||||
|
├────────────────────────┤ ├────────────────────────┤
|
||||||
|
│ TABELA DE CABEÇALHOS │ │ TABELA DE CABEÇALHOS │
|
||||||
|
│ DE SEÇÕES │ │ DE SEÇÕES (OPCIONAL) │
|
||||||
|
└────────────────────────┘ └────────────────────────┘
|
||||||
|
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
*Importante!*
|
||||||
|
|
||||||
|
- O posicionamento real das tabelas de seções e do programa pode ser
|
||||||
|
diferente de como está representado nos diagramas.
|
||||||
|
- Do mesmo modo, as seções e os seguimentos não têm uma ordem específica.
|
||||||
|
- Só o cabeçalho ELF tem uma posição fixa no arquivo.
|
||||||
|
- Um segmento na memória pode conter várias seções do arquivo.
|
||||||
|
|
||||||
|
No diagrama...
|
||||||
|
|
||||||
|
- Cabeçalho ELF ::
|
||||||
|
|
||||||
|
Escrito nos primeiros 52 ou 64 bytes do arquivo, o cabeçalho ELF contém
|
||||||
|
um resumo da sua organização e diversas informações, como o formato do
|
||||||
|
arquivo (32 ou 64 bits), a ordem de escrita dos bytes de dados (/little/
|
||||||
|
ou /big endian/), o tipo do arquivo objeto (se é relocável, executável
|
||||||
|
ou compartilhado), a arquitetura do conjunto de instruções, entre outras
|
||||||
|
definições. Os primeiros 4 bytes do cabeçalho ELF contêm a assinatura do
|
||||||
|
formato, o seu /número mágico/: o byte =0x7F= seguido dos bytes dos caracteres
|
||||||
|
=E=, =L= e =F= na tabela ASCII (=45 4C 46=).
|
||||||
|
|
||||||
|
- Seções ::
|
||||||
|
|
||||||
|
Contêm a organização dos dados do programa para efeito da edição das
|
||||||
|
ligações no arquivo, como as instruções do código, dados globais
|
||||||
|
contantes e variáveis, símbolos, informações de relocação, etc.
|
||||||
|
|
||||||
|
- Tabela de cabeçalhos do programa ::
|
||||||
|
|
||||||
|
Se existir, diz ao programa como montar uma imagem do programa quando
|
||||||
|
ele for carregado na memória para execução. Logo, se o arquivo objeto
|
||||||
|
for executável, ele terá que conter uma tabela de cabeçalhos do programa,
|
||||||
|
o que é desnecessário em arquivos objeto relocáveis.
|
||||||
|
|
||||||
|
- Tabela de cabeçalhos de seções ::
|
||||||
|
|
||||||
|
Contém a descrição das seções do arquivo. Cada seção tem uma entrada
|
||||||
|
na tabela com informações como seu nome, seu tamanho, a partir de onde
|
||||||
|
pode ser encontrada no arquivo (/offset/), etc. Arquivos utilizados
|
||||||
|
durante a edição de ligações precisam ter a tabela de cabeçalhos de
|
||||||
|
seções, mas ela é desnecessária no caso de arquivos objeto que só
|
||||||
|
serão ligados dinamicamente em tempo de execução.
|
||||||
|
|
||||||
|
* Seções especiais
|
||||||
|
|
||||||
|
Várias seções de um arquivo ELF são predefinidas para conter informações
|
||||||
|
de controle utilizadas pelo sistema operacional. Na pŕática, programas
|
||||||
|
executáveis são formados vários arquivos objeto e bibliotecas vinculados
|
||||||
|
através do processo de ligação, que pode ser:
|
||||||
|
|
||||||
|
- *Estática:* Vários arquivo objeto são combinados em um só arquivo executável.
|
||||||
|
- *Dinâmica:* O objeto executável é ligado na memoria, em tempo de execução,
|
||||||
|
com objetos compartilhados e bibliotecas disponíveis no sistema.
|
||||||
|
|
||||||
|
No fim das contas, o resultado será sempre um programa completo na memória,
|
||||||
|
a diferença está em quando o objeto executável é construído e em quem
|
||||||
|
resolve e processa as ligações.
|
||||||
|
|
||||||
|
No GNU/Linux:
|
||||||
|
|
||||||
|
- *Ligações estáticas:* Processadas pelo editor de ligações (=ld=, da suíte
|
||||||
|
GNU Binutils).
|
||||||
|
- *Ligações dinâmicas:* Processadas pelo carregador e ligador dinâmico do
|
||||||
|
sistema (=ld-linux=, da =glibc=).
|
||||||
|
|
||||||
|
Seja a ligação realizada estaticamente, durante a construção do executável,
|
||||||
|
ou dinamicamente, no momento da execução, o correto processamento das
|
||||||
|
ligações depende da organização interna do arquivo ELF. Essa organização é
|
||||||
|
descrita por suas seções especiais, entre as quais podemos destacar:
|
||||||
|
|
||||||
|
| Seção | Descrição |
|
||||||
|
|---------------+----------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
|
| =.bss= | Contém dados globais não inicializados ou inicializados com zero; ocupa espaço na memória, mas não ocupa espaço no arquivo executável. |
|
||||||
|
| =.data= | Contém dados globais inicializados; ocupa espaço tanto no arquivo quanto na memória. |
|
||||||
|
| =.rodata= | Contém dados que não podem ser alterados (/read only/), geralmente usada para constantes e strings literais. |
|
||||||
|
| =.text= | Seção que contém o código executável do programa (instruções da CPU). |
|
||||||
|
| =.symtab= | Tabela de símbolos completa, usada pelo ligador e depuradores para localizar símbolos. |
|
||||||
|
| =.strtab= | Tabela de strings que armazena os nomes dos símbolos referenciados em =.symtab=. |
|
||||||
|
|
||||||
|
* Tipos de segmentos
|
||||||
|
|
||||||
|
Na execução de um programa ELF, o sistema operacional utiliza a Tabela de
|
||||||
|
Cabeçalhos do Programa para saber quais partes do arquivo devem ser carregadas
|
||||||
|
na memória e como tratá-las. Cada entrada nessa tabela descreve um segmento
|
||||||
|
que representa uma região de interesse para o carregador, como código do
|
||||||
|
programa, seus dados, informações de ligação dinâmica e o caminho do carregador
|
||||||
|
e ligador dinâmico. Esses segmentos são identificados por tipos padronizados,
|
||||||
|
cada um com uma finalidade específica no processo de carregamento e execução
|
||||||
|
do programa.
|
||||||
|
|
||||||
|
Aqui estão alguns tipos de segmentos:
|
||||||
|
|
||||||
|
| Tipo | Descrição |
|
||||||
|
|--------------+---------------------------------------------------------------------------|
|
||||||
|
| =LOAD= | Segmento que deve ser carregado na memória, contendo código ou dados. |
|
||||||
|
| =INTERP= | Indica o caminho do carregador dinâmico (=ld-linux=) para execução. |
|
||||||
|
| =DYNAMIC= | Contém informações para ligação dinâmica, como símbolos e dependências. |
|
||||||
|
| =GNU_STACK= | Define permissões da pilha (stack), como se pode ou não ser executável. |
|
||||||
|
| =NOTE= | Armazena metadados usados pelo sistema operacional. |
|
||||||
|
| =PHDR= | Contém a própria Tabela de Cabeçalhos do Programa, usada por depuradores. |
|
||||||
|
| =GNU_EH_FRAME= | Informações para suporte à pilha de exceções (C, C++, etc.). |
|
||||||
|
| =GNU_RELRO= | Segmento de dados que será tornado somente leitura após inicialização. |
|
||||||
|
|
||||||
|
|
||||||
|
** Exemplo de programa mínimo em Assembly
|
||||||
|
|
||||||
|
#+begin_src nasm :tangle elf_min.asm
|
||||||
|
section .text
|
||||||
|
global _start
|
||||||
|
|
||||||
|
_start:
|
||||||
|
mov rax, 60
|
||||||
|
xor rdi, rdi
|
||||||
|
syscall
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
** Compilação
|
||||||
|
|
||||||
|
#+begin_src sh
|
||||||
|
nasm -f elf64 -o elf_min.o elf_min.asm
|
||||||
|
ld -o elf_min elf_min.o
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
** Inspeção do ELF
|
||||||
|
|
||||||
|
#+begin_src sh
|
||||||
|
file elf_min
|
||||||
|
readelf -h elf_min
|
||||||
|
readelf -S elf_min # Seções
|
||||||
|
readelf -l elf_min # Segmentos (program headers)
|
||||||
|
objdump -d elf_min
|
||||||
|
hexdump -C elf_min | less
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
** Versão em C para comparação
|
||||||
|
|
||||||
|
#+begin_src C :tangle elf_min.c
|
||||||
|
int main() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#+end_src
|
||||||
|
|
||||||
|
** Exercícios
|
||||||
|
|
||||||
|
1. Compare os headers do binário gerado em C com o de Assembly.
|
||||||
|
2. Use `readelf -x .text` para inspecionar o código de máquina.
|
||||||
|
3. Modifique o binário com `hexedit` para alterar manualmente um byte do código.
|
||||||
|
4. Gere um programa com `.data` e `.bss` e localize essas seções no ELF.
|
||||||
|
|
||||||
|
** Referências
|
||||||
|
- man 5 elf
|
||||||
|
- https://refspecs.linuxfoundation.org/elf/elf.pdf
|
||||||
|
- https://wiki.osdev.org/ELF
|
Loading…
Add table
Reference in a new issue