Trabalho com Arquivos em Assembly NASM
Uma das tarefas mais comuns na programação é a manipulação de arquivos. No Linux, o conceito de arquivo é bastante amplo, mas aqui o foco será em arquivos de texto armazenados em disco.
Em C, o acesso a arquivos é realizado por meio da estrutura FILE e de um conjunto de funções da biblioteca padrão.
Neste artigo, destacam-se três delas:
fopen— abre um arquivo e retorna um descritor para acesso.fclose— fecha o arquivo, recebendo seu descritor.fputs— grava uma string (linha de texto) no arquivo.
Não entraremos em detalhes sobre o funcionamento interno dessas funções em C; o foco será o uso delas em código Assembly NASM.
Exemplo no Linux
global main ; função main - ponto de entrada
extern fopen
extern fputs
extern fclose
section .data
filename db "myfile2.txt", 0
openmode db "w", 0
message db "Hello www.programicio.com!", 10, 0
section .text
main:
sub rsp, 8 ; alinhamento a 16 bytes
; Abre o arquivo para escrita
mov rdi, filename
mov rsi, openmode
call fopen
; Salva o descritor de arquivo na pilha
mov qword [rsp], rax
; Grava a string no arquivo
mov rdi, message ; 1º parâmetro - string a gravar
mov rsi, [rsp] ; 2º parâmetro - descritor de arquivo
call fputs
; Fecha o arquivo
mov rdi, [rsp]
call fclose
add rsp, 8 ; restaura a pilha
retNeste programa, são definidas três variáveis globais:
filename→ contém o nome do arquivo ("myfile2.txt") que será criado ou sobrescrito.openmode→ define o modo de abertura do arquivo ("w", ou seja, escrita).message→ contém o texto que será gravado no arquivo.
Ao executar sub rsp, 8, o código reserva 8 bytes na pilha, garantindo espaço para armazenar o descritor retornado por fopen e preservando o alinhamento de 16 bytes exigido pelo System V ABI.
A sequência a seguir abre o arquivo:
mov rdi, filename
mov rsi, openmode
call fopenO caminho do arquivo é passado em RDI e o modo de abertura em RSI.
O descritor retornado por fopen é armazenado em RAX e logo salvo na pilha:
mov qword [rsp], raxA gravação é feita com fputs, que recebe a string e o descritor:
mov rdi, message
mov rsi, [rsp]
call fputsPor fim, o arquivo é fechado com fclose:
mov rdi, [rsp]
call fcloseCompilação e Execução
Salve o código em hello.asm e execute:
nasm -f elf64 hello.asm -o hello.o gcc hello.o -static -o hello ./hello
Após a execução, será criado o arquivo myfile2.txt na pasta atual, contendo o texto:
Hello www.programicio.com!
Exemplo no Windows
O mesmo exemplo adaptado para o Microsoft Windows ABI:
global main ; função main - ponto de entrada
extern fopen
extern fputs
extern fclose
section .data
filename db "myfile2.txt", 0
openmode db "w", 0
message db "Hello www.programicio.com!", 10, 0
section .text
main:
sub rsp, 40 ; 32 bytes de shadow space + 8 bytes de alinhamento
; Abre o arquivo para escrita
mov rcx, filename
mov rdx, openmode
call fopen
; Salva o descritor de arquivo
mov qword [rsp], rax
; Grava a string
mov rcx, message ; 1º parâmetro - string
mov rdx, [rsp] ; 2º parâmetro - descritor
call fputs
; Fecha o arquivo
mov rcx, [rsp]
call fclose
add rsp, 40
retPrincipais diferenças em relação à versão Linux:
- O primeiro parâmetro é passado em RCX, e o segundo em RDX, conforme o Microsoft ABI.
- É necessário reservar 40 bytes na pilha (32 para o shadow space e 8 para alinhamento a 16 bytes).
Explicação do Funcionamento
- Abertura do arquivo:
A função
fopenrecebe dois parâmetros — o caminho (filename) e o modo ("w"). O valor retornado é um ponteiro para FILE, armazenado em RAX. - Gravação no arquivo:
A função
fputsrecebe a mensagem (message) e o ponteiro para o arquivo. Ela escreve o texto e o caractere de nova linha (10) no arquivo. - Fechamento:
A função
fcloseé chamada com o mesmo ponteiro (FILE*) para liberar o recurso. - Alinhamento da pilha: Tanto no Linux quanto no Windows, é essencial garantir que o RSP esteja alinhado a 16 bytes antes da chamada de qualquer função C.
Resumo
- A manipulação de arquivos em Assembly usa as funções padrão do C:
fopen,fputsefclose. - O descritor de arquivo retornado por
fopendeve ser armazenado antes de ser usado em chamadas subsequentes. - No Linux, os parâmetros são passados por RDI e RSI; no Windows, por RCX e RDX.
- O alinhamento da pilha (16 bytes) e a reserva de espaço (8 bytes no Linux, 40 bytes no Windows) são obrigatórios.
- Após a execução, o arquivo especificado é criado (ou sobrescrito) e o texto é gravado corretamente.