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