conteúdo da aula 3
This commit is contained in:
parent
95ad4cc85b
commit
9780039c11
2 changed files with 434 additions and 45 deletions
|
@ -15,10 +15,10 @@
|
||||||
* O que é o formato ELF
|
* O que é o formato ELF
|
||||||
|
|
||||||
O formato binário ELF, de /Executable and Linking Format/ (/Formato Executável
|
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
|
e de Ligação/), foi originalmente desenvolvido e publicado como parte da ABI
|
||||||
Binária de Aplicações/ (ABI) do Unix System V Release 4 (SVR4). Desde então,
|
(/Interface Binária de Aplicações/) do Unix System V Release 4 (SVR4). Desde
|
||||||
tornou-se o padrão adotado pela maioria dos sistemas /Unix-like/ para representar
|
então, tornou-se o padrão adotado pela maioria dos sistemas /Unix-like/ para
|
||||||
arquivos objeto binários, como executáveis e bibliotecas.
|
representar arquivos objeto binários, como executáveis e bibliotecas.
|
||||||
|
|
||||||
** Principais tipos de arquivos objeto
|
** Principais tipos de arquivos objeto
|
||||||
|
|
||||||
|
@ -146,7 +146,7 @@ ligações depende da organização interna do arquivo ELF. Essa organização
|
||||||
descrita por suas seções especiais, entre as quais podemos destacar:
|
descrita por suas seções especiais, entre as quais podemos destacar:
|
||||||
|
|
||||||
| Seção | Descrição |
|
| 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. |
|
| =.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. |
|
| =.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. |
|
| =.rodata= | Contém dados que não podem ser alterados (/read only/), geralmente usada para constantes e strings literais. |
|
||||||
|
@ -156,8 +156,8 @@ descrita por suas seções especiais, entre as quais podemos destacar:
|
||||||
|
|
||||||
* Tipos de segmentos
|
* Tipos de segmentos
|
||||||
|
|
||||||
Na execução de um programa ELF, o sistema operacional utiliza a Tabela de
|
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
|
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
|
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
|
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
|
programa, seus dados, informações de ligação dinâmica e o caminho do carregador
|
||||||
|
@ -178,53 +178,410 @@ Aqui estão alguns tipos de segmentos:
|
||||||
| =GNU_EH_FRAME= | Informações para suporte à pilha de exceções (C, C++, etc.). |
|
| =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. |
|
| =GNU_RELRO= | Segmento de dados que será tornado somente leitura após inicialização. |
|
||||||
|
|
||||||
|
* Definindo seções ELF em NASM para Linux 64 bits
|
||||||
|
|
||||||
** Exemplo de programa mínimo em Assembly
|
Nós podemos definir qualquer seção ELF em Assembly NASM com a /diretiva/ =section=,
|
||||||
|
ou com seu sinônimo =segment=, oriundo de uma terminologia mais antiga para
|
||||||
|
arquiteturas x86 de 16 e 32 bits, onde o conceito de segmentação de memória era
|
||||||
|
explícito. Em ambientes modernos, =section= é mais comum, pois reflete a
|
||||||
|
nomenclatura utilizada no formato ELF e que está padronizada no editor de
|
||||||
|
ligações =ld=, no /loader/ =ld-linux= e em ferramentas como =readelf= e =objdump=.
|
||||||
|
|
||||||
|
#+begin_src asm :tangle exemplos/03/sections.asm
|
||||||
|
; Arquivo : sections.asm
|
||||||
|
; Descrição : Demonstra a definição das seções .rodata, .data, .bss e .text
|
||||||
|
; Montagem : nasm -f elf64 sections.asm
|
||||||
|
; Link-edição: ld sections.o -o sections
|
||||||
|
|
||||||
|
section .rodata
|
||||||
|
msg db "Eu sou imutável!", 0x0a
|
||||||
|
len equ $ - msg
|
||||||
|
|
||||||
|
section .data
|
||||||
|
contador dq 0 ; preenche 8 bytes (qword) com 0x00 em 'contador'
|
||||||
|
|
||||||
|
section .bss
|
||||||
|
buffer resb 32 ; reserva 32 bytes no endereço 'buffer'
|
||||||
|
|
||||||
#+begin_src nasm :tangle elf_min.asm
|
|
||||||
section .text
|
section .text
|
||||||
global _start
|
global _start ; ponto de entrada do programa
|
||||||
|
|
||||||
_start:
|
_start:
|
||||||
mov rax, 60
|
mov rax, 1 ; syscall: write
|
||||||
xor rdi, rdi
|
mov rdi, 1 ; fd 1 = stdout
|
||||||
|
mov rsi, msg ; endereço da mensagem
|
||||||
|
mov rdx, len ; tamanho da mensagem
|
||||||
|
syscall
|
||||||
|
|
||||||
|
mov rax, [contador] ; copia o dado no endereço 'contador' para rax
|
||||||
|
inc rax ; incrementa em 1 o valor em rax
|
||||||
|
mov [contador], rax ; copia o valor em rax para o endereço 'contador'
|
||||||
|
|
||||||
|
mov rax, 60 ; syscall: exit
|
||||||
|
xor rdi, rdi ; estado de término = 0
|
||||||
syscall
|
syscall
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
** Compilação
|
Antes de falarmos das seções do programa, é importante saber que, no caso de
|
||||||
|
programas em Assembly link-editados para serem carregados pelo sistema como
|
||||||
|
executáveis ELF, a ordem das seções no código-fonte só é mantida no arquivo
|
||||||
|
objeto montado (=.o=). Mas, quando o objeto é link-editado para gerar o arquivo
|
||||||
|
executável, o link-editor (=ld=) reorganiza as seções de modo a atender as
|
||||||
|
especificações do formato ELF.
|
||||||
|
|
||||||
#+begin_src sh
|
Montando e executando o programa:
|
||||||
nasm -f elf64 -o elf_min.o elf_min.asm
|
|
||||||
ld -o elf_min elf_min.o
|
#+begin_example
|
||||||
|
:~$ nasm -f elf64 sections.asm
|
||||||
|
:~$ ld sections.o -o sections
|
||||||
|
:~$ ./sections
|
||||||
|
Eu sou imutável!
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Com o utilitário =readelf=, nós podemos listar os cabeçalhos do programa:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
$ readelf -l sections
|
||||||
|
|
||||||
|
Tipo de ficheiro Elf é EXEC (ficheiro executável)
|
||||||
|
Entry point 0x401000
|
||||||
|
There are 4 program headers, starting at offset 64
|
||||||
|
|
||||||
|
Cabeçalhos do programa:
|
||||||
|
Tipo Desvio EndVirtl EndFís
|
||||||
|
TamFich TamMem Bndrs Alinh
|
||||||
|
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
|
||||||
|
0x0000000000000120 0x0000000000000120 R 0x1000
|
||||||
|
LOAD 0x0000000000001000 0x0000000000401000 0x0000000000401000
|
||||||
|
0x0000000000000038 0x0000000000000038 R E 0x1000
|
||||||
|
LOAD 0x0000000000002000 0x0000000000402000 0x0000000000402000
|
||||||
|
0x0000000000000012 0x0000000000000012 R 0x1000
|
||||||
|
LOAD 0x0000000000002014 0x0000000000403014 0x0000000000403014
|
||||||
|
0x0000000000000008 0x000000000000002c RW 0x1000
|
||||||
|
|
||||||
|
Secção para mapa do segmento:
|
||||||
|
Secções do segmento...
|
||||||
|
00
|
||||||
|
01 .text
|
||||||
|
02 .rodata
|
||||||
|
03 .data .bss
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Onde:
|
||||||
|
|
||||||
|
- A seção =.text= inicia no byte =0x1000= do arquivo e ocupa 56 bytes (=0x38=).
|
||||||
|
- A seção =.rodata= inicia no byte =0x2000= do arquivo e ocupa 18 bytes (=0x12=).
|
||||||
|
- A seção =.data= inicia no byte =0x2014= do arquivo e ocupa 8 bytes.
|
||||||
|
- A seção =.bss= é indicada para mapeamento na execução, mas não ocupa
|
||||||
|
espaço no arquivo binário.
|
||||||
|
|
||||||
|
#+begin_quote
|
||||||
|
*Nota:* Repare que as seções =.text= e =.rodata= só têm permissão de leitura (=R=),
|
||||||
|
enquanto =.data= e =.bss= têm permissão de leitura e escrita (=RW=).
|
||||||
|
#+end_quote
|
||||||
|
|
||||||
|
Nós podemos localizar a definição da seção =.bss= listando a tabela de cabeçalhos
|
||||||
|
de seção, utilizada para orientar o mapeamento do executável nos segmentos da
|
||||||
|
memória:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
:~$ readelf -S sections
|
||||||
|
There are 8 section headers, starting at offset 0x2188:
|
||||||
|
|
||||||
|
Cabeçalhos de secção:
|
||||||
|
[Nr] Nome Tipo Endereço Desvio
|
||||||
|
Tam. Tam.Ent Bands Lig. Info Alinh
|
||||||
|
[ 0] NULL 0000000000000000 00000000
|
||||||
|
0000000000000000 0000000000000000 0 0 0
|
||||||
|
[ 1] .text PROGBITS 0000000000401000 00001000
|
||||||
|
0000000000000038 0000000000000000 AX 0 0 16
|
||||||
|
[ 2] .rodata PROGBITS 0000000000402000 00002000
|
||||||
|
0000000000000012 0000000000000000 A 0 0 4
|
||||||
|
[ 3] .data PROGBITS 0000000000403014 00002014
|
||||||
|
0000000000000008 0000000000000000 WA 0 0 4
|
||||||
|
[ 4] .bss NOBITS 000000000040301c 0000201c
|
||||||
|
0000000000000024 0000000000000000 WA 0 0 4
|
||||||
|
[ 5] .symtab SYMTAB 0000000000000000 00002020
|
||||||
|
00000000000000f0 0000000000000018 6 6 8
|
||||||
|
[ 6] .strtab STRTAB 0000000000000000 00002110
|
||||||
|
000000000000003e 0000000000000000 0 0 1
|
||||||
|
[ 7] .shstrtab STRTAB 0000000000000000 0000214e
|
||||||
|
0000000000000034 0000000000000000 0 0 1
|
||||||
|
Key to Flags:
|
||||||
|
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
|
||||||
|
L (link order), O (extra OS processing required), G (group), T (TLS),
|
||||||
|
C (compressed), x (unknown), o (OS specific), E (exclude),
|
||||||
|
D (mbind), l (large), p (processor specific)
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Aqui, nós podemos ver que =.bss= deve ocupar um espaço de 36 bytes na memória
|
||||||
|
(=0x24=), a partir do endereço de /offset/ 0x40301c. A diferença entre os 32 bytes
|
||||||
|
reservados no programa e os 36 bytes que vemos na tabela, é o resultado do
|
||||||
|
alinhamento de 4 bytes aplicado pelo =ld=, como podemos ver na última coluna
|
||||||
|
da tabela.
|
||||||
|
|
||||||
|
** Uma nota sobre alinhamento de dados
|
||||||
|
|
||||||
|
Em sistemas Linux 64 bits, é comum que dados sejam alinhados a endereços
|
||||||
|
múltiplos de 8 bytes, o que corresponde à largura dos registradores e garante
|
||||||
|
acesso eficiente e seguro à memória. Se pegarmos o /offset/ da seção =.bss=
|
||||||
|
(=0x201c=) e somarmos os 32 bytes reservados no programa, nós veremos que o
|
||||||
|
próximo dado teria que ser escrito no /offset/ =0x203c= (8252, em base 10), que
|
||||||
|
não é múltiplo de 8 (~8252/8=1031.5~). Somando 4 ao próximo endereço, nós
|
||||||
|
chegamos ao /offset/ =0x2040= (8256, em base 10), que é um múltiplo de 8
|
||||||
|
(~8256/8=1032.0~).
|
||||||
|
|
||||||
|
No entanto, observe que as seções =.rodata= e =.data= também tiveram alinhamento,
|
||||||
|
mas seus tamanhos não são diferentes daqueles definidos no código-fonte. Isso
|
||||||
|
aconteceu porque, diferente de =.bss=, os dados em =.rodata= e em =.data= foram
|
||||||
|
inicializados e, por definição, =.bss= é uma região reservada para dados não
|
||||||
|
inicializados -- logo, o tamanho da seção inclui os bytes de alinhamento.
|
||||||
|
|
||||||
|
#+begin_quote
|
||||||
|
*Nota:* O nome =.bss= vem da linguagem Fortran e significa /Block Started by Symbol/
|
||||||
|
(/bloco iniciado por um símbolo/, em tradução livre).
|
||||||
|
#+end_quote
|
||||||
|
|
||||||
|
** Inspecionando a seção .rodata
|
||||||
|
|
||||||
|
#+begin_src asm
|
||||||
|
section .rodata
|
||||||
|
msg db "Eu sou imutável!", 0x0a
|
||||||
|
len equ $ - msg
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
** Inspeção do ELF
|
Aqui, o endereço de rótulo =msg= receberá a cadeia de bytes definida com a
|
||||||
|
diretiva de /definição de bytes/ (=db=). Na montagem, esses bytes serão escritos
|
||||||
|
na seção =.rodata=, onde não poderão ser alterados.
|
||||||
|
|
||||||
#+begin_src sh
|
Como visto na tabela de cabeçalhos do programa, a seção =.rodata= está no
|
||||||
file elf_min
|
/offset/ =0x002000= do arquivo e tem permissão apenas para leitura (=R=):
|
||||||
readelf -h elf_min
|
|
||||||
readelf -S elf_min # Seções
|
#+begin_example
|
||||||
readelf -l elf_min # Segmentos (program headers)
|
Tipo Desvio EndVirtl EndFís
|
||||||
objdump -d elf_min
|
TamFich TamMem Bndrs Alinh
|
||||||
hexdump -C elf_min | less
|
...
|
||||||
|
LOAD 0x0000000000002000 0x0000000000402000 0x0000000000402000
|
||||||
|
0x0000000000000012 0x0000000000000012 R 0x1000
|
||||||
|
...
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Seu conteúdo também pode ser visualizado com a opção =-x= do =readelf=:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
:~$ readelf -x .rodata sections
|
||||||
|
|
||||||
|
Despejo máximo da secção ".rodata":
|
||||||
|
0x00402000 45752073 6f752069 6d7574c3 a176656c Eu sou imut..vel
|
||||||
|
0x00402010 210a !.
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Ou com o =xxd=, utilizando as opções =-s= (/skip/) e =-l= (/length/):
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
:~$ xxd -s 0x2000 -l 32 sections
|
||||||
|
00002000: 4575 2073 6f75 2069 6d75 74c3 a176 656c Eu sou imut..vel
|
||||||
|
00002010: 210a 0000 0000 0000 0000 0000 0000 0000 !...............
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
** Inspeção da seção .data
|
||||||
|
|
||||||
|
#+begin_src asm
|
||||||
|
|
||||||
|
section .data
|
||||||
|
contador dq 0 ; preenche 8 bytes (qword) com 0x00 em 'contador'
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
** Versão em C para comparação
|
Neste exemplo, o valor =0= será escrito na seção =.data=, no endereço indicado
|
||||||
|
pelo rótulo =contador=, e ocupara 8 bytes (diretiva =dq=), resultando em 8 bytes
|
||||||
|
no arquivo (e na memória) preenchidos com zeros.
|
||||||
|
|
||||||
#+begin_src C :tangle elf_min.c
|
No NASM, os dados podem ser definidos com tamanhos de:
|
||||||
int main() {
|
|
||||||
return 0;
|
- 1 byte: =db=, de /define bytes/
|
||||||
}
|
- 2 bytes: =dw=, de /define word/
|
||||||
|
- 4 bytes: =dd=, de /define double word/
|
||||||
|
- 8 bytes =dq=, de /define quad word/
|
||||||
|
|
||||||
|
Como visto na tabela de cabeçalhos do programa, a seção =.data= inicia no
|
||||||
|
/offset/ =0x2014= do arquivo e tem permissões de leitura e escrita (=RW=):
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
Tipo Desvio EndVirtl EndFís
|
||||||
|
TamFich TamMem Bndrs Alinh
|
||||||
|
...
|
||||||
|
LOAD 0x0000000000002014 0x0000000000403014 0x0000000000403014
|
||||||
|
0x0000000000000008 0x000000000000002c RW 0x1000
|
||||||
|
...
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Seu conteúdo pode ser visualizado com a opção =-x= do =readelf=:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
:~$ readelf -x .data sections
|
||||||
|
|
||||||
|
Despejo máximo da secção ".data":
|
||||||
|
0x00403014 00000000 00000000 ........
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Ou com o =xxd=, sabendo seu tamanho:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
:~$ xxd -s 0x2014 -l 8 sections
|
||||||
|
00002014: 0000 0000 0000 0000 ........
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Ainda no exempĺo, nós escrevemos uma rotina para incrementar o valor no
|
||||||
|
endereço identificado pelo rótulo =contador=:
|
||||||
|
|
||||||
|
#+begin_src asm
|
||||||
|
mov rax, [contador] ; copia o dado no endereço 'contador' para rax
|
||||||
|
inc rax ; incrementa em 1 o valor em rax
|
||||||
|
mov [contador], rax ; copia o valor em rax para o endereço 'contador'
|
||||||
#+end_src
|
#+end_src
|
||||||
|
|
||||||
** Exercícios
|
#+begin_quote
|
||||||
|
Em Assembly, isso é o mais próximo que podemos chegar do conceito de
|
||||||
|
/variáveis/, próprio de linguagens de alto nível.
|
||||||
|
#+end_quote
|
||||||
|
|
||||||
1. Compare os headers do binário gerado em C com o de Assembly.
|
O efeito dessa rotina só pode ser examinado com o programa em execução, por
|
||||||
2. Use `readelf -x .text` para inspecionar o código de máquina.
|
exemplo, com o depurador GDB (GNU Debugger). Mas, antes, o programa terá que
|
||||||
3. Modifique o binário com `hexedit` para alterar manualmente um byte do código.
|
ser montado com símbolos de depuração (opção =-g= do =nasm=):
|
||||||
4. Gere um programa com `.data` e `.bss` e localize essas seções no ELF.
|
|
||||||
|
|
||||||
** Referências
|
#+begin_example
|
||||||
- man 5 elf
|
:~$ nasm -g -f elf64 sections.asm
|
||||||
|
:~$ ld sections.o -o sections
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Carregando o programa com o GDB:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
:~$ gdb sections
|
||||||
|
Reading symbols from sections...
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Listando o código-fonte (=list=) para descobrir as linhas antes e depois da
|
||||||
|
alteração do dado em =contador=:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(gdb) list
|
||||||
|
1 ; Arquivo : sections.asm
|
||||||
|
2 ; Descrição : Demonstra a definição das seções .rodata, .data, .bss e .text
|
||||||
|
3 ; Montagem : nasm -f elf64 sections.asm
|
||||||
|
4 ; Link-edição: ld sections.o -o sections
|
||||||
|
5
|
||||||
|
6 section .rodata
|
||||||
|
7 msg db "Eu sou imutável!", 0x0a
|
||||||
|
8 len equ $ - msg
|
||||||
|
9
|
||||||
|
10 section .data
|
||||||
|
(gdb)
|
||||||
|
11 contador dq 0 ; preenche 8 bytes (qword) com 0x00 em 'contador'
|
||||||
|
12
|
||||||
|
13 section .bss
|
||||||
|
14 buffer resb 32 ; reserva 32 bytes no endereço 'buffer'
|
||||||
|
15
|
||||||
|
16 section .text
|
||||||
|
17 global _start ; ponto de entrada do programa
|
||||||
|
18
|
||||||
|
19 _start:
|
||||||
|
20 mov rax, 1 ; syscall: write
|
||||||
|
(gdb)
|
||||||
|
21 mov rdi, 1 ; fd 1 = stdout
|
||||||
|
22 mov rsi, msg ; endereço da mensagem
|
||||||
|
23 mov rdx, len ; tamanho da mensagem
|
||||||
|
24 syscall
|
||||||
|
25
|
||||||
|
26 mov rax, [contador] ; copia o dado no endereço 'contador' para rax
|
||||||
|
27 inc rax ; incrementa em 1 o valor em rax
|
||||||
|
28 mov [contador], rax ; copia o valor em rax para o endereço 'contador'
|
||||||
|
29
|
||||||
|
30 mov rax, 60 ; syscall: exit
|
||||||
|
(gdb)
|
||||||
|
31 xor rdi, rdi ; estado de término = 0
|
||||||
|
32 syscall
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Definindo os pontos de parada (=break=) nas linhas 26 e 30:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(gdb) break 26
|
||||||
|
Breakpoint 1 at 0x40101b: file sections.asm, line 26.
|
||||||
|
(gdb) break 29
|
||||||
|
Breakpoint 2 at 0x40102e: file sections.asm, line 30.
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Executando o programa (=run=):
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(gdb) run
|
||||||
|
Starting program: /home/blau/git/pbn/curso/exemplos/03/sections
|
||||||
|
Eu sou imutável!
|
||||||
|
|
||||||
|
Breakpoint 1, _start () at sections.asm:26
|
||||||
|
26 mov rax, [contador] ; copia o dado no endereço 'contador' para rax
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Imprimindo o valor no endereço =contador= (8 bytes equivale ao tipo =long=):
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(gdb) print (long *)contador
|
||||||
|
$1 = (long *) 0x0
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Continuando a execução (=continue=):
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(gdb) continue
|
||||||
|
Continuing.
|
||||||
|
|
||||||
|
Breakpoint 2, _start () at sections.asm:30
|
||||||
|
30 mov rax, 60 ; syscall: exit
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Verificando a alteração do valor no endereço =contador=:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
(gdb) print (long *)contador
|
||||||
|
$2 = (long *) 0x1
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
* Exercícios propostos
|
||||||
|
|
||||||
|
1. Crie um exemplo em C onde seja possível observar as seções =.text=, =.rodata=,
|
||||||
|
=.data= e =.bss=.
|
||||||
|
2. Com o arquivo objeto do exemplo =sections.asm=, tente interpretar as
|
||||||
|
informações presentes no seu cabeçalho ELF (a descrição dos campos
|
||||||
|
pode ser encontrada na página "ELF" da wiki OS Dev, nas referências).
|
||||||
|
3. Em seguida, interprete o cabeçalho ELF do arquivo executável e anote
|
||||||
|
as diferenças, se houver.
|
||||||
|
|
||||||
|
** Desafio
|
||||||
|
|
||||||
|
Sabendo que o NASM pode montar binários puros, tente criar um programa que
|
||||||
|
resulte em um arquivo executável 64 bits utilizando apenas as especificações
|
||||||
|
ELF para definir os bytes de seu conteúdo, que deverá ser funcional sem a
|
||||||
|
edição de ligações. O programa não precisa fazer nada além de terminar com
|
||||||
|
estado de saída 42.
|
||||||
|
|
||||||
|
Para montar e dar permissões de execução:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
:~$ nasm -f bin -o elf_exit42 elf_exit42.asm
|
||||||
|
:~$ chmod +x elf_exit42
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
Para testá-lo:
|
||||||
|
|
||||||
|
#+begin_example
|
||||||
|
$ ./elf_exit42
|
||||||
|
$ echo $?
|
||||||
|
42
|
||||||
|
#+end_example
|
||||||
|
|
||||||
|
* Referências
|
||||||
|
|
||||||
|
- =man 5 elf=
|
||||||
|
- [[https://wiki.osdev.org/ELF][OS Dev: ELF]]
|
||||||
- https://refspecs.linuxfoundation.org/elf/elf.pdf
|
- https://refspecs.linuxfoundation.org/elf/elf.pdf
|
||||||
- https://wiki.osdev.org/ELF
|
|
||||||
|
|
32
curso/exemplos/03/sections.asm
Normal file
32
curso/exemplos/03/sections.asm
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
; Arquivo : sections.asm
|
||||||
|
; Descrição : Demonstra a definição das seções .rodata, .data, .bss e .text
|
||||||
|
; Montagem : nasm -f elf64 sections.asm
|
||||||
|
; Link-edição: ld sections.o -o sections
|
||||||
|
|
||||||
|
section .rodata
|
||||||
|
msg db "Eu sou imutável!", 0x0a
|
||||||
|
len equ $ - msg
|
||||||
|
|
||||||
|
section .data
|
||||||
|
contador dq 0 ; preenche 8 bytes (qword) com 0x00 em 'contador'
|
||||||
|
|
||||||
|
section .bss
|
||||||
|
buffer resb 32 ; reserva 32 bytes no endereço 'buffer'
|
||||||
|
|
||||||
|
section .text
|
||||||
|
global _start ; ponto de entrada do programa
|
||||||
|
|
||||||
|
_start:
|
||||||
|
mov rax, 1 ; syscall: write
|
||||||
|
mov rdi, 1 ; fd 1 = stdout
|
||||||
|
mov rsi, msg ; endereço da mensagem
|
||||||
|
mov rdx, len ; tamanho da mensagem
|
||||||
|
syscall
|
||||||
|
|
||||||
|
mov rax, [contador] ; copia o dado no endereço 'contador' para rax
|
||||||
|
inc rax ; incrementa em 1 o valor em rax
|
||||||
|
mov [contador], rax ; copia o valor em rax para o endereço 'contador'
|
||||||
|
|
||||||
|
mov rax, 60 ; syscall: exit
|
||||||
|
xor rdi, rdi ; estado de término = 0
|
||||||
|
syscall
|
Loading…
Add table
Reference in a new issue