Estruturas de Dados em Assembly NASM
O NASM, assim como o GNU Assembler, não possui suporte nativo a estruturas como tipo de dado, diferentemente de linguagens de mais alto nível. No entanto, é possível simular estruturas organizando manualmente os dados na memória de forma contígua.
Estrutura Simples em Memória
section .data
; estrutura simbólica
person:
db "Alice", 10 ; nome
dq 34 ; idadeAqui, a variável person representa dois elementos consecutivos na memória:
uma string e um número de 64 bits.
Essa combinação pode ser entendida como uma estrutura que descreve uma pessoa — o nome e a idade.
Exemplo: Exibindo Nome e Idade
global _start
section .data
; estrutura simbólica
person:
db "Alice", 10 ; nome
dq 34 ; idade
; deslocamentos dos campos na estrutura
NAME_OFFSET equ 0
AGE_OFFSET equ 6
section .text
_start:
mov rsi, person ; RSI - endereço do nome
mov rdi, 1 ; RDI - descritor de saída padrão (console)
mov rdx, AGE_OFFSET ; RDX - tamanho do nome em bytes
mov rax, 1 ; RAX - syscall write
syscall
mov rdi, [rsi + AGE_OFFSET] ; RDI - idade
mov rax, 60
syscallNesta estrutura, o campo NAME_OFFSET representa o deslocamento inicial (0 bytes),
e AGE_OFFSET indica o deslocamento do campo de idade, que começa no 6º byte, após o nome “Alice”.
Durante a execução:
RSIaponta para o início da estrutura (a string).RDXcontém o tamanho da string (6 bytes, incluindo o caractere de nova linha).- A chamada
syscallcomRAX = 1imprime o nome. - A idade é carregada a partir do endereço
person + AGE_OFFSET.
Saída:
programicio@machine:~/asm$ nasm -f elf64 hello.asm -o hello.o programicio@machine:~/asm$ ld hello.o -o hello programicio@machine:~/asm$ ./hello Alice programicio@machine:~/asm$ echo $? 34
Estrutura com Endereços
Esse método simples tem limitações: se o nome tiver tamanho variável, o deslocamento de cada campo muda. Uma solução é armazenar endereços e tamanhos dentro da estrutura, em vez da string diretamente:
global _start
section .data
; estrutura simbólica
person:
dq alice ; endereço da string
dq alice_name_size ; tamanho da string
dq 34 ; idade
; deslocamentos dos campos
NAME_OFFSET equ 0
NAME_SIZE_OFFSET equ 8
AGE_OFFSET equ 16
alice db "Alice", 10
alice_name_size equ $ - alice
section .text
_start:
mov rbx, person
mov rsi, [rbx + NAME_OFFSET] ; RSI - endereço da string
mov rdi, 1 ; RDI - descritor de saída padrão
mov rdx, [rbx + NAME_SIZE_OFFSET] ; RDX - tamanho do nome
mov rax, 1 ; syscall write
syscall
mov rdi, [rbx + AGE_OFFSET] ; RDI - idade
mov rax, 60
syscallAgora, a estrutura person contém três campos de 8 bytes cada (.quad):
- Endereço da string.
- Tamanho da string.
- Idade.
Isso facilita o gerenciamento e o reuso de estruturas.
Campos e Deslocamentos
; estrutura simbólica
person:
dq alice
dq alice_name_size
dq 34
; deslocamentos
NAME_OFFSET equ 0
NAME_SIZE_OFFSET equ 8
AGE_OFFSET equ 16
alice db "Alice", 10
alice_name_size equ $ - aliceCada campo ocupa 8 bytes, portanto o tamanho total da estrutura é 24 bytes. Esse formato padronizado permite manipular coleções de estruturas sem recalcular tamanhos manualmente.
Exemplo: Array de Estruturas
O exemplo a seguir mostra como percorrer um array de estruturas contendo nome e idade de várias pessoas:
global _start
section .data
; array de estruturas
people:
dq tom, tom_name_size, 39
dq sam, sam_name_size, 29
dq bob, bob_name_size, 43
dq kate, kate_name_size, 25
dq alice, alice_name_size, 34
count equ ($ - people) / PERSON_SIZE ; número de elementos
tom db "Tom", 10
tom_name_size equ $ - tom
sam db "Sam", 10
sam_name_size equ $ - sam
bob db "Bob", 10
bob_name_size equ $ - bob
kate db "Kate", 10
kate_name_size equ $ - kate
alice db "Alice", 10
alice_name_size equ $ - alice
; deslocamentos e tamanho da estrutura
NAME_OFFSET equ 0
NAME_SIZE_OFFSET equ 8
AGE_OFFSET equ 16
PERSON_SIZE equ 24
section .text
_start:
mov r9, 0 ; contador
mov rbx, people
jmp condition
mainloop:
mov rbx, people
mov rax, PERSON_SIZE
mul r9
add rbx, rax
mov rsi, [rbx + NAME_OFFSET] ; RSI - endereço do nome
mov rdi, 1 ; RDI - descritor de saída padrão
mov rdx, [rbx + NAME_SIZE_OFFSET] ; RDX - tamanho do nome
mov rax, 1
syscall
inc r9
condition:
cmp r9, count
jne mainloop
mov rdi, r9
mov rax, 60
syscallAqui, o registrador R9 atua como índice de iteração, movendo-se de uma estrutura para a seguinte conforme o tamanho fixo PERSON_SIZE.
Saída esperada:
Tom Sam Bob Kate Alice
Conclusão
- O NASM não oferece estruturas nativas, mas é possível emular esse comportamento com dados sequenciais.
- Os deslocamentos (
OFFSET) permitem acessar campos individuais dentro da estrutura. - Usar endereços e tamanhos em vez de dados diretos torna o formato mais flexível e consistente.
- Com tamanho fixo (
PERSON_SIZE), é possível criar e percorrer arrays de estruturas com facilidade.