Jump to content

Assembly (iniciantes)


Lincoln Arantes

Recommended Posts

Vamos a pequena e importante dica!

Em algumas distribuições Linux já vem instalado o compilador Assembly, como no caso do Fedora 29, bastando apenas o usuário desenvolver seu código e compilar..

Nesse exemplo mando para você um código simples que fiz. Espero que goste!


===================================================

.section .data
    mensagem:
        .ascii "Lincoln Arantes\n"
.section .text
.globl _start
_start:
    movq  $1, %rax
    movq  $1, %rdi
    movq  $mensagem, %rsi
    movq  $15, %rdx
    syscall

    movq  $60, %rax
    movq  $0, %rdi
    syscall

==================================================

Desconsidere os sinais de = (igual)
Copie o código e salve com o nome hello.s

Depois abra o terminal do Linux na pasta onde você salvou o arquivo e compile usando cada linha e teclando [enter] no teclado.

#    as hello.s -o hello.o
#    ld -s hello.o -o hello.x

Pronto!

Para executar digite: ./hello.x

Autor: Lincoln César dos Reis Arantes (ADS - graduado)

E-mail: lincolnsetelagoas@yahoo.com.br

Link to comment
Share on other sites

Algumas correções e dicas:
 

# test.S
#
#   $ as -o test.o test.S
#   $ ld -s -o test test.o

# Read Only data section
# Usada para conter contantes que nunca serão alteradas.
.section .rodata

# Label 'mensagem' com a mensagem.
# Note que criei o símbolo 'msg_len' que é calculado com o tamanho da mensagem.
# O símbolo especial '.' é o equivalente ao $, em outros assemblers.
mensagem: .ascii "Lincoln Arantes\n"
          .equ msg_len, . - mensagem

# Code section
.section .text

# O símbolo _start é usado pelo linker, automaticamente.
# pode-se usar outro, mas a opção -e deve ser usada na chamada do linker.
.globl _start
_start:
    ; Para criar instruções menores, inicializar registradores de 32 bits
    ; automaticamente zera os 32 bits superiores dos registradores extendidos.
    ; Por isso, coloquei 1 em %eax.
    ; Ainda, copiar %eax para %edi cria uma instrução ainda menor (e zerará os
    ; 32 bits superiores de %edi).
    movl  $1, %eax
    movl  %eax, %edi

    ; Usando o endereçamento relativo com RIP gera instruções menores... 
    leaq  mensagem(%rip),%rsi

    ; O símbolo msg_len é usado como constante, daí o $.
    movl  $msg_len, %edx

    ; Chama a systecall write().
    syscall

    ; Note que zerei %edi via XOR. Usando %edi para aproveitar o zero
    ; automático dos 32 bits superiores.
    movl  $60, %eax
    xorl  %edi,%edi

    ; Chama a syscall exit().
    syscall 

Outra coisa... Códigos-fonte em assembly, tipicamente têm extensão .S (maiúsculo)... a extensão .s (minúsculo) geralmente é usado para listagens geradas por outras ferramentas (gcc, por exemplo).

[]s
Fred

Link to comment
Share on other sites

Observe... seu código (test), depois de compilado com o as, e o meu código (test2), abaixo. Repare que o fato de não usar uma constante calculada para o tamanho da string fez com que você pedisse ao write() para imprimir 1 caracter a menos (o '\n' não irá para stdout!).

Repare também que meu código tem 16 bytes a menos...

$ objdump -dM intel test | sed -nE '/_start/,$p'
00000000004000b0 <_start>:
  4000b0: 48 c7 c0 01 00 00 00  mov    rax,0x1
  4000b7: 48 c7 c7 01 00 00 00  mov    rdi,0x1
  4000be: 48 c7 c6 de 00 60 00  mov    rsi,0x6000de
  4000c5: 48 c7 c2 0f 00 00 00  mov    rdx,0xf
  4000cc: 0f 05                 syscall 
  4000ce: 48 c7 c0 3c 00 00 00  mov    rax,0x3c
  4000d5: 48 c7 c7 00 00 00 00  mov    rdi,0x0
  4000dc: 0f 05                 syscall 

$ objdump -dM intel test2 | sed -nE '/_start/,$p'
00000000004000b0 <_start>:
  4000b0: b8 01 00 00 00        mov    eax,0x1
  4000b5: 89 c7                 mov    edi,eax
  4000b7: 48 8d 35 10 00 20 00  lea    rsi,[rip+0x200010]        # 6000ce <mensagem>
  4000be: ba 10 00 00 00        mov    edx,0x10
  4000c3: 0f 05                 syscall 
  4000c5: b8 3c 00 00 00        mov    eax,0x3c
  4000ca: 31 ff                 xor    edi,edi
  4000cc: 0f 05                 syscall 

 

Link to comment
Share on other sites

Uma desvantagem de chamar código para processadores Intel de "Assembly" é que existem "Assemblies" diferentes para modos e processadores diferentes. Eis um código que faz a mesma coisa para o ARM (AArch32):

@ test.S

@ Compilar com:
@   as -o test.o test.S
@   ld -o test test.o

@ Note que tanto faz usar uma instrução ou outra para zerar um registrador:
@    mov r1, #0
@    eor r1, r1, r1
@ As instruções do ARM tem tamanho fixo de 32 bits!

@ Usando o cortex-a53 como plataforma base!
.cpu cortex-a53

.section .rodata

msg:  .ascii  "Hello!\n"
      .equ    msg_len, . - msg

.text

.globl _start
_start:
  mov   r7, #4        @ syscall_write (syscalls ids vão em R7).
  mov   r0, #1        @   fd = STDOUT
  ldr   r1, msgptr    @   ptr
  mov   r2, #msg_len  @   size
  swi   0             @ chama a syscall (o vetor 0 é a interface para syscalls!)

  mov   r7, #1        @ syscall_exit
  mov   r1, #0
  swi   0             @ chama a syscall.

@ No ARM a referência a um símbolo numa outra section
@ tem que ser feita de forma indireta?!

msgptr: .word msg

Dá pra notar que:

  • As instruções e os registradores são completamente diferentes;
  • As systemcalls são diferentes;
  • As regras para acesso a recursos em sections diferentes são... ora bolas... diferentes...

"Assembly" é a linguagem mnemônica de programação cujo alvo é um processador de tipo específico, operando em um modo específico. Por exemplo, se você fizer um código para Intel usando instruções como AAA, AAM, DAA, no modo IA-32 (i386), entre outras, elas não poderão ser compiladas para o mesmo processador trabalhando no modo IA-32e (x86-64). Essas instruções não existem no modo x86-64...

Escrever programas inteiros diretamente em Assembly (sempre para o processador específico), geralmente, não é uma boa ideia...

 

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...