No meu emprego anterior eu tive a oportunidade de ‘brincar’ um pouco com assembly para power 8 e desde então minha curiosidade e vontade de aprender mais e mais asm tem crescido. Mas quais são as vantagens de se aprender asm? Eu diria que uma delas, a maior delas, é que quando você aprende asm você também revisa e aprende um bocado sobre arquitetura de computadores, senão aprende deveria.
Ao programar em asm você lida diretamente com aspectos de como a arquitetura foi desenhada. Em um resumo, em como a cpu é estruturada. E o que isso tem de bom? Bem, o bom de aprender tudo isso é que você se torna um programador mais consciente, eu diria. Além de te dar um background para projetos envolvendo desenvolvimento low level, seja através de embedded systems ou drivers, por exemplo.
Mas bem, deixando de bla bla bla, vou aqui mostrar um exemplo, bem simples, de programação para asm x86_64 onde um código em C faz uso de um função definida em um .s (asm file). Como compilador estou usando o gcc.
#include <inttypes.h>
int64_t soma(int64_t, int64_t, int64_t, int64_t, int64_t, int64_t, int64_t, int64_t, int64_t);
int main(void) {
int c;
c = soma(1,2,4,5,6,7,4,10,6);
return c;
}
No código acima definimos a função soma que recebe 9 parametros e a função main que faz uso dela. Mas, por que nove argumentos? Acontece que até seis argumentos são naturalmente passados via registradores, em ordem de argumento: rdi, rsi, rdx, rcx, r8, r9. A partir do sétimo os argumentos são passados na stack (na pilha).
A título de exemplo de como codar o .s recebendo os argumentos passados na pilha eu vou exemplificar uma função que somente soma os argumentos passados na pilha.
.global soma
.text
soma:
push     %rbp
mov %rsp, %rbp
mov 0x10(%rbp), %eax
mov 0x18(%rbp), %edx
add %edx, %eax
mov 0x20(%rbp), %edx
add %edx, %eax
pop %rbp
ret
Muito bem, o código acima é o nosso .s para a função soma. Agora vamos entendê-lo.
A linha 1 é basicamente a declaração da função.
A linha 3 determina a seção de código do programa, ou seja, toda vez que você encontrar um .S ou .asm essa seção é onde o código a ser executado começa.
A linha 5 é a nossa função soma. As linhas 6 e 7 são o que nós chamamos de prologo. Em detalhes o que elas fazem é basicamente salvar o stack frame/base pointer (rbp) na pilha e setar a pilha para o registrador do rbp. Assim a gente pode manusear a pilha como quisar, uma vez que o contexto do main vai estar salvo ao ser empilhado. As 8 e 9 pegam os argumentos passados na pilha. Diferente de variáveis locais que são colocados com decremento no rpb, por exemplo -0x8(%rbp), para argumentos nós somamos. O resto é repetição exceto pela linha 13 que retira o contexto salvo anteriormente da pilha.
Mas por que começamos na posíção 0x10 da pilha a pegar os argumentos? A explicação para isso é que na pilha já se encontra o endereço de retorno, na posição 0x0(%rsp)ou 0x0(%rbp), e após o push %rbp, teremos o %rbp na posição 0x8(%rbp). Logo, os demais valores serão acessados após eles, em outras palavras 0x8+8 = 0x10 (16 em decimal), 0x18 (24 decimal) e 0x20 (32 dec).
Para entender um pouco mais, vejamos como os dados são passados via main.
Perceba que os primeiros seis argumentos são passados nos registradores de edi até r9d, após isso são passados na pilha.
Lembra que em nosso main temos uma declaração de uma variável local c? Como disse as variáveis locais são armazenadas subindo na pilha, ou seja, subtraindo no %rbp. A linha mov %rax, -0x8(%rbp) salva o valor do registrador rax na posição menos 8 do stack frame, que é a posição do endereço de memória da variável c. Após isso faz uma operação de mover o valor desse endereço para o registrador de retorno rax.
Abaixo, para melhor exemplificar veja a anatomia da stack. Onde EIP é o registrador de retorno, EBP o stack frame salvo na pilha, var são as variáveis logais e param os parametros passados na função.
Source: [4]
Como compilar tudo isso? Simples.:
$ gcc soma.c soma.s && ./a.out
$ echo $?
$ 20
Pra finalizar, queria deixar claro que não sou um programador asm expert, que se algum dos conceitos passados aqui estiverem errados, por favor comenta ai para que eu possa corrigir :).
Referencias:
[1] http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/
[2] http://cs.lmu.edu/~ray/notes/gasexamples/
[3] http://0xax.blogspot.com.br/2014/08/say-hello-to-x64-assembly-part-1.html
[4] http://www.cs.princeton.edu/courses/archive/spr11/cos217/lectures/15AssemblyFunctions.pdf
Read Full Post »