Atualizado: 18/10/2025

Este conteúdo é original e não foi gerado por inteligência artificial.

Definição de Variáveis e Tipos de Dados em Assembly NASM: Seção .data

Tipos de Dados

Todos os dados utilizados em um programa Assembly possuem um tipo, determinado pela sua largura em bits. A arquitetura Intel x86-64 oferece os seguintes tipos básicos:

  • byte – número inteiro de 8 bits
  • word – número inteiro de 16 bits (palavra)
  • dword – número inteiro de 32 bits (palavra dupla)
  • qword – número inteiro de 64 bits (palavra quádrupla)

Por exemplo, o registrador de 8 bits AL representa um byte, AX representa uma palavra de 16 bits, EAX uma palavra dupla de 32 bits e RAX uma palavra quádrupla de 64 bits.


Definição de Dados no Programa

O NASM permite definir objetos que armazenam dados diretamente no código fonte. Esses objetos podem ser utilizados e modificados durante a execução do programa — em outras palavras, são variáveis.

A forma geral de definição é:

label directive value

O identificador label é o nome da variável. Em seguida vem a diretiva que indica o tipo de dado, e por fim o valor inicial.

As diretivas mais comuns são:

  • db – define um número inteiro de 1 byte
  • dw – define um número inteiro de 2 bytes
  • dd – define um número inteiro de 4 bytes
  • dq – define um número inteiro de 8 bytes

Exemplo

number dq 22

A variável number é um valor de 8 bytes (qword) inicializado com 22. Também é possível adicionar dois-pontos após o nome, mas isso é opcional:

number: dq 22

O assembler reserva um espaço de 8 bytes na memória e o associa à variável number.


Localização e Uso dos Dados

Os dados podem ser definidos em diferentes seções do programa Assembly.

Definindo Dados na Seção .text

Também é possível declarar dados dentro da seção de código .text:

global _start

section .text
number: dq 123   ; definição de dado dentro da seção .text
_start:
    ; restante do código

É importante não misturar instruções com declarações de dados. As variáveis devem aparecer antes da primeira instrução ou após a última.

O nome da variável representa seu endereço na memória, e não o valor armazenado. Por exemplo:

global _start

section .text
number: dq 124
_start:
    mov rdi, number   ; move o endereço da variável para RDI
    mov rax, 60
    syscall

Para acessar o valor contido na variável, usa-se colchetes:

mov rdi, [number]

Assim, o registrador RDI receberá o valor 124:

global _start

section .text
number: dq 124
_start:
    mov rdi, [number]   ; RDI = 124
    mov rax, 60
    syscall

Particularidades no Windows

Em Windows, o uso de variáveis na seção .text pode gerar erros de relocação durante a vinculação.

global _start

section .text
number: db 124
_start:
    mov rax, [number]
    ret

Ao compilar com o ld (GCC), ocorre:

relocation truncated to fit: IMAGE_REL_AMD64_ADDR32 against `.text`

E com o link.exe (Microsoft):

error LNK2017: 'ADDR32' relocation to '.text' invalid without /LARGEADDRESSAWARE:NO

O problema surge porque o programa de 64 bits tenta usar endereços de 32 bits. Embora seja possível contornar com /LARGEADDRESSAWARE:NO, a solução correta é usar o operador rel, que acessa o endereço relativo ao registrador RIP (Instruction Pointer):

mov rax, [rel number]

Versão corrigida:

global _start

section .text
number: dq 124
_start:
    mov rax, [rel number]
    ret

Essa abordagem funciona com o link.exe, mas pode produzir resultados incorretos ao usar o ld do GCC. Em resumo, a seção .text não é adequada para armazenar dados.


Seção .data

A seção .data é o local apropriado para armazenar dados modificáveis. Ela é declarada com:

section .data

Exemplo no Linux

global _start

section .data
number dq 125

section .text
_start:
    mov rdi, [number]
    mov rax, 60
    syscall

Exemplo no Windows

global _start

section .data
number dq 125

section .text
_start:
    mov rax, [rel number]
    ret

A seção .data é ideal para variáveis, e ambos os vinculadores (ld e link.exe) tratam-na corretamente.


Organização dos Dados em Memória

Quando várias variáveis são declaradas na seção .data, o NASM aloca blocos de memória consecutivos para cada uma:

section .data
i64 dq 8
i32 dd 4
i16 dw 2
i8  db 1

A variável i64 ocupa 8 bytes, i32 ocupa 4 bytes, i16 ocupa 2 e i8 ocupa 1. Essas variáveis são armazenadas em sequência na memória.

Seção .data na memória

Se i64 começa no endereço 0x0000, então:

  • i32 estará em 0x0008
  • i16 em 0x000C
  • i8 em 0x000E

Alteração de Dados

Variáveis definidas na seção .text se comportam como constantes, pois essa seção é marcada como somente leitura.

global _start

section .text
number dq 124
_start:
    mov rdx, 111
    mov [number], rdx   ; tentativa de sobrescrita
    mov rdi, [number]
    mov rax, 60
    syscall

Ao executar, o sistema gera segmentation fault, pois .text não permite escrita.

A solução é mover os dados para .data:

global _start

section .data
number dq 125

section .text
_start:
    mov rdx, 111
    mov [number], rdx
    mov rdi, [number]
    mov rax, 60
    syscall

Agora o valor pode ser alterado normalmente.

Exemplo equivalente no Windows

global _start

section .data
number dq 125

section .text
_start:
    mov rdx, 111
    mov [rel number], rdx
    mov rax, [rel number]
    ret

Definição de Conjuntos e Arrays

O NASM permite definir conjuntos de dados (arrays). Se os valores iniciais forem conhecidos, basta listá-los:

nums dq 11, 12, 13, 14, 15, 16, 17

A variável nums representa um conjunto de 7 números de 8 bytes cada. O nome da variável corresponde ao primeiro elemento do conjunto.

global _start

section .data
nums dq 11, 12, 13, 14, 15, 16, 17

section .text
_start:
    mov rdi, [nums]   ; RDI = 11
    mov rax, 60
    syscall

Também é possível repetir valores usando a diretiva times:

label: times N type value

Exemplo:

numbers: times 10 db 2

Cria um array de 10 bytes, todos com o valor 2.

Para reservar espaço sem inicializar valores, o NASM oferece as diretivas resX:

  • resb – reserva bytes
  • resw – reserva palavras (2 bytes)
  • resd – reserva palavras duplas (4 bytes)
  • resq – reserva palavras quádruplas (8 bytes)

Cada posição é automaticamente preenchida com zeros.

buffer resb 10
numbers resq 5

Outras Seções de Dados

Além da .data, o NASM oferece outras seções específicas:

Seção .rodata

Armazena dados somente leitura, ou seja, constantes.

global _start

section .rodata
number dq 125

section .text
_start:
    mov rdi, [number]
    ; mov [number], rdi  - causaria Segmentation fault
    mov rax, 60
    syscall

Qualquer tentativa de escrita nessa área provoca erro de proteção.

Seção .bss

A seção .bss (Block Started by Symbol) contém dados não inicializados. Somente o tamanho é definido; os valores são preenchidos com zeros na execução. Isso reduz o tamanho do arquivo binário.

global _start

section .bss
buffer resq 1024   ; 1024 números de 8 bytes

section .text
_start:
    mov rdi, [buffer]   ; RDI = 0
    mov rax, 60
    syscall

O array ocupa 1024 × 8 = 8192 bytes na memória, mas não aumenta o tamanho do executável.


Conversão e Tamanho dos Dados

Ao manipular dados, é essencial respeitar a largura dos registradores e variáveis. Por exemplo:

global _start

section .data
number db 12

section .text
_start:
    mov rdi, [number]
    mov rax, 60
    syscall

Aqui, um valor de 8 bits é movido para um registrador de 64 bits. O sistema preenche automaticamente os bytes superiores com zeros, e a execução ocorre sem erros.

Versão para Windows:

global _start

section .data
number db 12

section .text
_start:
    mov rax, [rel number]
    ret

Ambas exibem corretamente o valor 12.


Conversão Explícita e Carregamento de Arrays

global _start

section .data
nums db 1, 2, 0, 0, 0, 0, 0, 0

section .text
_start:
    mov rax, [rel nums]
    ret

O array nums contém 8 bytes. Como o registrador RAX também tem 8 bytes, todos os elementos são carregados de uma vez, gerando o valor 513.

Isso ocorre porque os dois primeiros bytes (1 e 2) formam o número binário:

00000010_00000001b - 513

Para carregar apenas o primeiro byte, é necessário indicar o tamanho e expandir corretamente:

movzx rax, byte [rel nums]

O operador byte limita a leitura, e movzx preenche os demais bytes do registrador com zeros.

Da mesma forma, ao gravar valores, deve-se indicar o tamanho:

mov byte [nums], 101

Exemplo completo no Linux

global _start

section .data
nums db 12, 13, 14, 15

section .text
_start:
    mov byte [nums], 101    ; altera o primeiro elemento
    movzx rdi, byte [nums]  ; RDI = 101
    mov rax, 60
    syscall

Resumo

  • .text: código executável (constante, não modificável)
  • .data: variáveis inicializadas
  • .bss: variáveis não inicializadas
  • .rodata: dados somente leitura
  • db, dw, dd, dq: tamanhos de 1, 2, 4 e 8 bytes
  • resX: reserva espaço sem inicializar
  • rel: acesso relativo ao registrador RIP (Windows 64-bit)
  • movzx + byte/word/dword/qword: conversão e carregamento com tamanho definido
Política de Privacidade

Copyright © www.programicio.com Todos os direitos reservados

É proibida a reprodução do conteúdo desta página sem autorização prévia do autor.

Contato: programicio@gmail.com