Olá mundo no Linux

Finalmente o Hello World.

Geralmente o "Hello World" é a primeira coisa que vemos quando estamos aprendendo uma linguagem de programação. Nesse caso eu deixei por último pois acredito que seria de extrema importância entender todos os conceitos antes de vê-lo, isso evitaria a intuição de ver um código em Assembly como um "código em C mais difícil de ler". Acredito que essa comparação mental involuntária é muito ruim e prejudicaria o aprendizado. Por isso optei por explicar tudo antes mesmo de apresentar o famoso "Hello World".

Hello World

Desta vez vamos escrever um código em Assembly sem misturar com C, será um executável do Linux (formato ELF64) fazendo chamadas de sistema diretamente. Vamos vê-lo logo:

hello.asm
bits 64

section .rodata
  msg:     db  `Hello World!\n`
  MSG_SIZE equ $-msg

section .text

global _start
_start:
  mov rax, 1
  mov rdi, 1
  mov rsi, msg
  mov rdx, MSG_SIZE
  syscall             ; write

  mov rax, 60
  xor rdi, rdi
  syscall             ; exit

Para compilar esse código basta usar o NASM especificando o format elf64 e desta vez iremos usar o linker do pacote GCC diretamente. O nome do executável é ld e o uso básico é bem simples, basta especificar o nome do arquivo de saída com -o. Ficando assim:

$ nasm hello.asm -felf64
$ ld hello.o -o hello
$ ./hello

Na linha 5 definimos uma constante usando o símbolo $ para pegar o endereço da instrução atual e subtraímos pelo endereço do rótulo msg. Isso resulta no tamanho do texto porque msg aponta para o início da string e, como está logo em seguida, $ seria o endereço do final da string. final - início = tamanho

write

Nome

RAX

RDI

RSI

RDX

write

1

file_descriptor

endereço

tamanho (em bytes)

Como deve ter reparado usamos mais uma syscall, que foi a syscall write. Essa syscall basicamente escreve dados em um arquivo. O primeiro argumento é um número que serve para identificar o arquivo para o qual queremos escrever os dados.

No Linux a saída e entrada de um programa nada mais é que dados sendo escritos e lidos em arquivos. E isso é feito por três arquivos que estão por padrão abertos em um programa e tem sempre o mesmo file descriptor, são eles:

Nome

File descriptor

Descrição

stdin

0

Entrada de dados (o que é digitado pelo usuário)

stdout

1

Saída padrão (o que é impresso no terminal)

stderr

2

Saída de erro (também impresso no terminal, porém destinado a mensagens de erro)

Se quiser ver o código de implementação desta syscall no Linux, pode ver aqui.

Entry point

Reparou que nosso programa tem um símbolo _start e que magicamente esse é o código que o sistema operacional está executando primeiro? Isso acontece porque o linker definiu o endereço daquele símbolo como o entry point (ponto de entrada) do nosso programa.

O entry point nada mais é o que o próprio nome sugere, o endereço inicial de execução do programa. Eu sei o que você está pensando:

Então a função main de um programa em C é o entry point?

A resposta é não! Um programa em C usando a libc tem uma série de códigos que são executados antes da main. E o primeiro deles, pasme, é uma função chamada _start definida pela própria libc.

Na verdade qualquer símbolo pode ser definido como o entry point para o executável, não faz diferença qual nome você dá para ele. Só que _start é o símbolo padrão que o ld define como entry point.

Se você quiser usar um símbolo diferente é só especificar com a opção -e. Por exemplo, podemos reescrever nosso Hello World assim:

bits 64

section .rodata
  msg:     db  `Hello World!\n`
  MSG_SIZE equ $-msg

section .text

global _eu_que_mando_no_meu_exec
_eu_que_mando_no_meu_exec:
  mov rax, 1
  mov rdi, 1
  mov rsi, msg
  mov rdx, MSG_SIZE
  syscall

  mov rax, 60
  xor rdi, rdi
  syscall

E compilar assim:

$ nasm hello.asm -felf64
$ ld hello.o -o hello -e _eu_que_mando_no_meu_exec
$ ./hello

Fácil fazer um "Hello World", né? Ei, o que acha de fazer uns macros para melhorar o uso dessas syscalls aí? Seria interessante também salvar os macros em um arquivo separado e incluir o arquivo com a diretiva %include.

Last updated