Explicação
Agora vamos à explicação das partes do programa.
Tudo o que possui uma '#' no início, é comentário. Acho que não há muito o que se dizer sobre isso. ;-)
Logo depois dos comentários, temos algumas seções específicas. E sempre que há algo que comece com '.section', não é uma instrução para que o computador execute, mas sim uma instrução diretamente inserida para o Assembler, como é o caso das seções abaixo, que quebra o programa em pedaços (seções) diferentes:
- .section .data = esse comando cria a seção "data", onde listamos quaisquer containers de memória que precisaremos para os dados.
- .section .text = é nessa seção onde inserimos as instruções a serem executadas.
- .globl _start = '.globl' é uma instrução que diz que o símbolo '_start' não deve ser descartado após a compilação e linkedição do código. E '_start' é um símbolo que marca um determinado local da memória que servirá como referência para a execução de determinadas instruções, que vêm logo abaixo:
- _start: = é onde definimos o valor do label '_start', que terá vinculado à si, o conjunto de instruções que seguem logo abaixo. Podemos traçar um paralelo com as funções que utilizamos em C.
- movl $1, %eax = aqui temos a instrução 'movl', seguido de dois operadores. Os operadores podem ser números, referência a locais da memória ou registradores. Nesse caso, inserimos o valor 1 no registrador EAX. Esse número é o valor de uma syscall específica (exit - para conhecer os valores das demais 'syscall', execute o comando "cat /usr/include/asm-i386/unistd.h" no terminal
Linux).
Bem, com o comando acima, dizemos ao programa qual 'syscall' será executada pelo kernel ao ser chamado. No entanto, essa 'syscall' precisa de um parâmetro para dizer que está tudo ok e o programa poderá ser finalizado. Esse parâmetro será armazenado em outro registrador, com a próxima instrução:
- movl $0, %ebx = aqui, inserimos o parâmetro através do valor "0" no registrador EBX. Isso é o que dirá para o kernel que está tudo ok para o 'exit' ser executado. Lembra um pouco o "return (0)" do C.
A próxima instrução é a que faz a sinalização para chamar o kernel e executar a 'syscall exit':
- int $0x80 = 'int' é o mesmo que 'interrupt'. Uma interrupção corta o fluxo de funcionamento de um programa e passa o comando para o Linux, o que em nosso caso, fará com que o kernel execute a 'syscall 1 '(exit). E o valor '$0x80' é o número de interrupção utilizado para que essa passagem de controle para o Linux, aconteça. Não se preocupe ainda do porque ser esse valor, e não outro, porque isso não importa, apenas precisa lembrar-se que é a instrução de interrupção padrão utilizada pelo Assembly AT&T.
Segundo programa
Se você conseguiu entender a explicação do que foi feito até aqui, poderá esforçar-se mais um pouco e entenderá o próximo código. Esse novo programa tem como função ler algo digitado pelo usuário, armazená-lo e depois exibi-lo:
#OBJETIVO: Ler uma string digitada pelo usuário
#
#ENTRADA: qualquer string que pode ser digitada
#
#OUTPUT: retorna o que foi digitado pelo usuário
#
#VARIÁVEIS:
# string = armazena a string digitada
# tam = armazena o tamanho da variável string
#
.section .data
string: .string "Digite algo:\n"
tam: .long . - string
.section .text
.globl _start
_start:
movl $4, %eax # insere o valor 4, para a chamada da syscall write no EAX
movl $1, %ebx # passa o parâmetro da syscall 4 para que algo seja exibido
leal string, %ecx # carrega o endereço de memória do ECX e exibe o conteúdo de string
movl tam, %edx # armazena o valor de tam no EDX
int $0x80
movl %esp, %ecx # Salva o Stack Pointer em %ecx
subl $10, %esp # Reserva 10 bytes para o usuario digitar no stack
movl $3, %eax # insere o valor da syscall read (3) no EAX, o que for escrito tbm será armazenado em EAX
movl $9, %edx # Tamanho do que vai ser lido para EDX
int $0x80
movl %eax, %edx # Move o que foi digitado para EDX.
movl $4, %eax # syscall write
movl $1, %ebx
int $0x80
movl $0x1, %eax
movl $0x0, %ebx
int $0x80
Salve como 'leia.s'. Compile, linkedite e execute:
# as leia.s -o leia.o
# lf leia.o -o leia
# ./leia