Aprenda Assembly

  • Página Inicial
  • Contato!
  • Tudo sobre Assembly Parte 1!
  • Tudo sobre Assembly Parte 2!
  • Tudo sobre Assembly Parte 3!
  • Tudo sobre Assembly Parte 4!
  • Tudo sobre Assembly Parte 3

    Introdução ao Assembly em 64 bits

    Basicamente, um Olá, Mundo em Assembly com 64 bits fazemos assim:

    
    section .data
        msg db "Olá, Mundo!"
    
    section .text
        global _start
     
    _start:
        mov rax, 1 ; Chamada sys_write
        mov rdi, 1 ; Saída padrão
        mov rsi, msg ; Ponteiro pra mensagem
        mov rdx, 11 ; Número de caracteres (bytes) da mensagem
        syscall ; Ordem pra executar os comandos.
    
        mov rax, 60 ; identificador do sys_exit
        mov rdi, 0 ; Valor de saída
        syscall
    
    ; Registradores como ax são de 16 bits, eax de 32 bits, e rax de 64 bits.
    ; Registradores estão na CPU
    ; Dentro do rax tem eax, do eax tem ax, etc.
    ; rax - id do system call
    ; rdi - primeiro parãmetro
    ; rsi - segundo parâmetro
    ; rdx - terceiro parâmetro
    ; r10 - quarto parâmetro
    ; r9 - quinto parâmetro
    ; r8 - sexto parâmetro
    
    ; Chamadas syscall:
    ; 0 - sys_read
    ; 1 - sys_write
    ; 2 - sys_open
    ; 3 - sys_close
    ; 60 - sys_exit
    
    ; rdi:
    ; 0 - entrada padrão
    ; 1 - saída padrão
    ; 2 - erro
    
    ; sys_exit:
    ; 0 - deu certo
    ; 1 - deu erro
    
    

    Para compilar e montar, digite nasm -f elf64 olamundo64.asm -o olamundo64.o e ld olamundo64.o -s -o olamundo64.

    Tabela ASCII e Pulo de Linha

    Para pular de linha, basta isso:

    
    section .data
        msg db "Olá, Mundo!", 10 ; O 10 é o número do pulo de linha na tabela ASCII, não conta no tamanho da string
    
    

    Para ver os códigos ASCII, veja essa tabela: https://www.ascii-code.com/pt

    Veja um exemplo de exibição de mensagens só com ASCII:

    
    section .data
        msg db 79, 105, 33, 10 ; Altere o rdx para 3. Exibirá "Oi"
    
    

    Informando Tamanho do Programa Automaticamente

    Podemos criar uma macro informando o tamanho da mensagem automaticamente:

    
    section .data
        msg db 79, 105, 33, 10
        tam equ 3 ; Macro que define uma constante chamada tam
    
    section .text
        global _start
    
    _start:
        mov rax, 1
        mov rdi, 1
        mov rsi, msg
        mov rdx, tam ; Usando a constante
        syscall
    
        mov rax, 60
        mov rdi, 0
        syscall
    
    

    Para ele pegar o tamanho da string automaticamente, colocamos esse parâmetro em tam:

    
    section .data
        msg db "Olá, Mundo!", 10
        tam equ $- msg ; Macro que define uma constante chamada tam
    
    

    Leitura de Dados

    Para ler dados do teclado, faça o programa assim:

    
    section .data
        perg db "Como você se chama? "
        tamPerg equ $- perg
    
        ola db "Olá, "
        tamOla equ $- ola
        tamNome equ 10
    
    section .bss
        nome resb tamNome
    
    section .text
        global _start
    
    _start:
        ; Imprimindo a mensagem "Como você se chama"
        mov rax, 1
        mov rdi, 1
        mov rsi, perg
        mov rdx, tamPerg
    
        syscall
    
        ; Ler o nome do usuário
        mov rax, 0
        mov rdi, 0
        mov rsi, nome
        mov rdx, tamNome
    
        syscall
    
        ; Imprimir "Olá"
        mov rax, 1
        mov rdi, 1
        mov rsi, ola
        mov rdx, tamOla
    
        syscall
    
        ; Imprimir nome do usuário
    
        mov rax, 1
        mov rdi, 1
        mov rsi, nome
        mov rdx, tamNome
    
        syscall
    
        ; Encerrar o programa
        mov rax, 60
        mov rdi, 0
    
        syscall
    
    

    Compile e monte ele.

    A Seção .data

    Vamos considerar esse programa:

    
    section .data
        ola db "Olá "
        ana db "Ana."
    
    section .text
        global _start
    
    _start:
        ; Imprimindo a mensagem "Olá"
        mov rax, 1
        mov rdi, 1
        mov rsi, ola
        mov rdx, 5
    
        syscall
    
        ; Encerrando o programa
        mov rax, 60
        mov rdi, 0
    
        syscall
    
    

    Para imprimir o nome também, faça assim:

    
    section .data
        ola db "Olá "
        ana db "Ana."
    
    section .text
        global _start
    
    _start:
        ; Imprimindo a mensagem "Olá"
        mov rax, 1
        mov rdi, 1
        mov rsi, ola
        mov rdx, 4
    
        syscall
    
        ; Imprimindo a mensagem "Ana"
        mov rax, 1
        mov rdi, 1
        mov rsi, ana
        mov rdx, 4
    
        syscall
    
        ; Encerrando o programa
        mov rax, 60
        mov rdi, 0
    
        syscall
    
    

    Agora volte ao programa anterior, mas com 9 no tamanho da mensagem Olá:

    
    section .data
        ola db "Olá "
        ana db "Ana."
    
    section .text
        global _start
    
    _start:
        ; Imprimindo a mensagem "Olá"
        mov rax, 1
        mov rdi, 1
        mov rsi, ola
        mov rdx, 9
    
        syscall
    
        ; Encerrando o programa
        mov rax, 60
        mov rdi, 0
    
        syscall
    
    

    Note que ele imprimiu "Ana" também, mas desta vez não deveria ser impresso por não temos dado comandos para imprimir isso. A section .data é como se fosse a memória RAM, onde ola e ana são ponteiros para determinadas posições da memória. O ponteiro de Ana será logo após o de Olá. Por isso o certo é especificar o tamanho com o "$-".

    Registradores e Compatibilidade entre 32 e 64 bits.

    Podemos fazer os mesmos programas para serem compatíveis com 32 bits, apenas mudando os comandos de r para e (como por exemplo, rax vira eax, rdi vira edi, etc. Da mesma forma fazemos para 16 bits, usando coisas como ax e di).

    Se fizermos as alterações para 32 bits e compilar para 64 bits, ele continuará compatível, no entanto, poderá ter problemas para 16 bits.

    Depuração (Debug) de Código-Fonte

    Com a depuração, podemos ver passo a passo da execução de um programa para encontrar erros.

    Vamos supor esse programa:

    
    section .data
        msg db "Boa noite.", 10
        tam equ $- msg
    
    section .text
        global _start
    
    _start:
        mov rax, 1
        mov rdi, 1
        mov rsi, msg
        mov rdi, tam ; Erro proposital, o certo seria rdx
    
        syscall
    
        mov rax, 60
        mov rdi, 0
    
        syscall
    
    

    Compile e monte. Para depurar o programa digite gdb -q ./programa. No caso, só poderemos usar o breakpoint na parte _start, digitando dentro do gdb break _start, depois digite run e layout asm, e para uma sintaxe mais parecida com o programa, digite set disassembly-flavor intel e layout regs. No layout regs mostrará cada instrução sendo executada passo a passo, para isso digite stepi. Para examinar a string (como por exemplo em hexa, 0x402000) digite x/s 0x402000. Para sair, dê Ctrl C e digite quit.

    Subrotinas

    Voltando ao nosso programa de perguntar o nome do usuário, vamos refazer ele com funções:

    
    section .data
        perg db "Como você se chama? "
        tamPerg equ $- perg
    
        ola db "Olá, "
        tamOla equ $- ola
        tamNome equ 10
    
    section .bss
        nome resb tamNome
    
    section .text
        global _start
    
    _start:
        call imprimePergunta ; call chama as funções
        call lerNome
        call imprimeOla
        call imprimeNome
        call encerraPrograma
    
    imprimePergunta:
        ; Imprimindo a mensagem "Como você se chama"
        mov rax, 1
        mov rdi, 1
        mov rsi, perg
        mov rdx, tamPerg
    
        syscall
    
        ret ; Retorna para quem a chamou
    
    lerNome:
        ; Ler o nome do usuário
        mov rax, 0
        mov rdi, 0
        mov rsi, nome
        mov rdx, tamNome
    
        syscall
    
        ret
    
    imprimeOla:
        ; Imprimir "Olá"
        mov rax, 1
        mov rdi, 1
        mov rsi, ola
        mov rdx, tamOla
    
        syscall
    
        ret
    
    imprimeNome:
        ; Imprimir nome do usuário
        mov rax, 1
        mov rdi, 1
        mov rsi, nome
        mov rdx, tamNome
    
        syscall
    
        ret
    
    encerraPrograma:
        ; Encerrar o programa
        mov rax, 60
        mov rdi, 0
    
        syscall
    
        ret
    
    

    Com isso, quando precisarmos repetir alguma coisa no programa, basta chamar a função especificada ao invés de reescrever ele. A ordem das funções não importa, apenas a ordem na qual elas são invocadas.