
O GNU Assembler, comumente conhecido como gas ou as, é o assembler desenvolvido pelo Projeto GNU.
É o back-end padrão do GCC.
Ele é usado para montar o sistema operacional GNU e o kernel Linux, e vários outros softwares.
Faz parte do pacote GNU Binutils.
O GAS é multiplataforma e pode ser executado e montado em diversas arquiteturas de computador diferentes.
É um software livre lançado sob a Licença Pública Geral GNU v3.
A extensão padrão é .s(o ideal é para identificação), mas você pode usar qualquer extensão desde que haja somente código GAS dentro do arquivo, exemplos: .gas, .as e .S.
Os registradores em assembly x86_64 são utilizados para armazenar dados temporários e realizar operações aritméticas e lógicas. São eles:
Esses são os principais registradores em GAS/NASM para x86_64. Eles são essenciais para manipulação de dados, controle de fluxo, e execução de operações em programas assembly.
Os registradores possuem o mesmo conceito e funcionalidade básica tanto no NASM quanto no GNU Assembler (GAS) para a arquitetura x86_64. A principal diferença entre NASM e GAS é a sintaxe usada para escrever os programas, mas os registradores e suas utilizações permanecem consistentes.
rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp, r8-r15%rax, %rbx, %rcx, %rdx, %rsi, %rdi, %rbp, %rsp, %r8-%r15eax, ebx, ecx, edx, esi, edi, ebp, esp, r8d-r15d%eax, %ebx, %ecx, %edx, %esi, %edi, %ebp, %esp, %r8d-%r15dax, bx, cx, dx, si, di, bp, sp, r8w-r15w%ax, %bx, %cx, %dx, %si, %di, %bp, %sp, %r8w-%r15wah, al, bh, bl, ch, cl, dh, dl, spl, bpl, sil, dil, r8b-r15b%ah, %al, %bh, %bl, %ch, %cl, %dh, %dl, %spl, %bpl, %sil, %dil, %r8b-%r15bcs, ds, ss, es, fs, gs%cs, %ds, %ss, %es, %fs, %gsrip, rflags%rip, %rflagsxmm0-xmm15, ymm0-ymm15, zmm0-zmm31%xmm0-%xmm15, %ymm0-%ymm15, %zmm0-%zmm31
Para instalar o GNU Assembler (GAS) você deve instalar o pacote binutils, que inclui o assembler as (parte do conjunto de ferramentas GNU Binutils).
Você pode fazer o download direto da página: https://www.gnu.org/software/binutils/ ou usar o seu gerenciador de pacotes, exemplo no Ubuntu:
sudo apt install binutilsNo Windows você deve usar o MinGW e fazer download aqui do binutils.
Agora veremos alguns exemplos de códigos básicos para nos adaptarmos como a sintaxe é utilizada.
.section .data
hello:
.ascii "Hello, World!\0"
.section .text
.globl _start
_start:
mov $1, %rax # syscall: sys_write
mov $1, %rdi # file descriptor: stdout
mov $hello, %rsi # endereço da string
mov $13, %rdx # comprimento da string
syscall # chama o kernel
mov $60, %rax # syscall: sys_exit
xor %rdi, %rdi # status de saída: 0
syscall # chama o kernel.section .data: Declara a seção de dados, onde as variáveis e strings são armazenadas.hello: .ascii "Hello, World!\0": Define uma string terminada em nulo (0)..section .text: Declara a seção de código, onde o código executável é armazenado..globl _start: Torna a label _start visível para o linker._start: Ponto de entrada do programa.mov $1, %rax: Coloca o número do syscall sys_write no registrador rax.mov $1, %rdi: Coloca o número do file descriptor (stdout) no registrador rdi.mov $hello, %rsi: Coloca o endereço da string hello no registrador rsi.mov $13, %rdx: Coloca o comprimento da string no registrador rdx.syscall: Chama o kernel para executar o syscall.mov $60, %rax: Coloca o número do syscall sys_exit no registrador rax.xor %rdi, %rdi: Zera o registrador rdi para definir o status de saída como 0.syscall: Chama o kernel para terminar o programa.Compilar e executar:
as --64 -o hello.o hello.s
ld -o hello hello.o
./helloNo Windows é o
hello.exeem vez de./hello.
Após rodar, note que o Hello, World! vai colar com o prompt:
Hello, World!$prompt> Para resolver isso, troque o \0 por \n:
# .ascii "Hello, World!\0"
.ascii "Hello, World!\n"E aumente o comprimento da string para 14 bytes (13 caracteres + 1 para a quebra de linha \n):
# mov $13, %rdx
mov $14, %rdxRecompile e rode!
Mesmo código, mas com NASM:
section .data
msg db 'Hello, World!', 0
section .text
global _start
_start:
mov rax, 1 ; syscall: write
mov rdi, 1 ; file descriptor: stdout
mov rsi, msg ; pointer to message
mov rdx, 13 ; message length
syscall ; make the syscall
mov rax, 60 ; syscall: exit
xor rdi, rdi ; status: 0
syscall ; make the syscallPara compilar e rodar o NASM:
# Antes instale o NASM, ex.: sudo apt install nasm
nasm -f elf64 hello.asm
ld -s -o hello hello.o
./helloNote: Além do símbolo %(do GAS) na frente do registradores e os comentários serem #(GAS) e ;(NASM), o GAS também pode comentar estilo C: /* Comentário para multiplas linhas */, além de outras formas dependendo da arquitetura, exemplos:
; — AMD 29k family, ARC, H8/300 family, HPPA, PDP-11, picoJava, Motorola e M32C@ — ARM 32-bit// — AArch64| — M680x0! — Renesas SH# — i386, x86-64, i960, 68HC11, 68HC12, VAX, V850, M32R, PowerPC, MIPS, M680x0, e RISC-VEmbora a sintaxe seja diferente (NASM usa uma abordagem mais “intel” enquanto GAS usa uma abordagem “AT&T”), os registradores desempenham as mesmas funções em ambas as assemblers.
Além do GNU Assembler (GAS) e do NASM (Netwide Assembler), existem vários outros assemblers conhecidos e amplamente utilizados. Aqui estão alguns dos mais notáveis:
.section .bss
.lcomm buffer, 13 # reserva 13 bytes para o buffer
.section .data
hello:
.ascii "Hello, World!\0"
.section .text
.globl _start
_start:
mov $buffer, %rdi # endereço do buffer
mov $hello, %rsi # endereço da string
call copy_string # chama a função para copiar a string
mov $1, %rax # syscall: sys_write
mov $1, %rdi # file descriptor: stdout
mov $buffer, %rsi # endereço do buffer
mov $13, %rdx # comprimento da string
syscall # chama o kernel
mov $60, %rax # syscall: sys_exit
xor %rdi, %rdi # status de saída: 0
syscall # chama o kernel
copy_string:
mov $13, %rcx # comprimento da string
.loop:
mov (%rsi), %al # lê um byte da string
mov %al, (%rdi) # escreve no buffer
inc %rsi # avança para o próximo byte da string
inc %rdi # avança para o próximo byte do buffer
loop .loop # repete até copiar todos os bytes
ret # retorna para _start.lcomm buffer, 13: Reserva 13 bytes para o buffer na seção BSS.call copy_string: Chama a função copy_string para copiar a string hello para o buffer.copy_string: Função que copia a string para o buffer.mov $13, %rcx: Define o contador de repetição para o comprimento da string.loop .loop: Laço de repetição para copiar cada byte da string..section .data
hello:
.ascii "Hello, World!\0"
.section .text
.globl _start
_start:
call print_hello # chama a função para imprimir a string
mov $60, %rax # syscall: sys_exit
xor %rdi, %rdi # status de saída: 0
syscall # chama o kernel
print_hello:
mov $1, %rax # syscall: sys_write
mov $1, %rdi # file descriptor: stdout
mov $hello, %rsi # endereço da string
mov $13, %rdx # comprimento da string
syscall # chama o kernel
ret # retorna para _startcall print_hello: Chama a função print_hello para imprimir a string hello;print_hello: Função que executa o syscall sys_write para imprimir a string.E imprime na tela com quebra de linha/nova linha (sem colar no prompt)!
.section .data
quebra:
.ascii "\n" # para pular/quebrar uma linha
num1:
.quad 5 # define o primeiro número
num2:
.quad 10 # define o segundo número
result:
.quad 0 # reserva espaço para o resultado
buffer:
.space 2 # espaço para a string do número (2 dígitos)
.section .text
.globl _start
_start:
mov num1(%rip), %rax # carrega num1 em rax
add num2(%rip), %rax # soma num2 a rax
mov %rax, result(%rip) # armazena o resultado
# código para imprimir o resultado
mov result(%rip), %rax # carrega o resultado em rax
mov $buffer + 2, %rsi # aponta para o final do buffer(2 dígitos)
call int_to_string # converte o número para string
mov $1, %rax # syscall: sys_write
mov $1, %rdi # file descriptor: stdout
lea buffer(%rip), %rsi # endereço da string
mov $2, %rdx # comprimento máximo da string (2 dígitos)
syscall # chama o kernel
mov $1, %rax # syscall: sys_write
mov $1, %rdi # file descriptor: stdout
mov $quebra, %rsi # endereço da quebra
mov $1, %rdx # comprimento da quebra(1 dígito)
syscall # chama o kernel
mov $60, %rax # syscall: sys_exit
xor %rdi, %rdi # status de saída: 0
syscall # chama o kernel
int_to_string:
# converte %rax para string decimal e armazena em %rsi
mov %rax, %rcx # copia o número para rcx
mov $10, %rbx # base decimal
convert_loop:
xor %rdx, %rdx # limpa rdx (dividendo)
div %rbx # divide rax por 10
add $'0', %dl # converte o resto para caractere ASCII
dec %rsi # move o ponteiro do buffer para trás
mov %dl, (%rsi) # armazena o caractere no buffer
test %rax, %rax # verifica se rax é 0
jnz convert_loop # se não for 0, continua o loop
ret # retorna para _startnum1: .quad 5 e num2: .quad 10: Define os números a serem somados.result: .quad 0: Reserva espaço para armazenar o resultado.add num2(%rip), %rax: Soma num2 ao valor de num1 armazenado em rax.mov %rax, result(%rip): Armazena o resultado da soma.call print_result: Chama a função print_result para imprimir o resultado.Para mais informações e uma documentação completa do GNU Assembler visite o endereço: https://sourceware.org/binutils/docs-2.42/as.html.