Existem 8 registos principais de 32 bits no processador (existem mais como %EIP):
%EAX%EBX%ECX%EDX
%ESI%EDI%EBP%ESP
Os registos são armazenados em hardware, diretamente no processador
Ou seja, são independentes da RAM
Os registos servem para armazenar valores.
-Se usarmos %registo estamos a aceder diretamente ao valor desse registo.
-Se usarmos (%registo) estamos a aceder ao endereço da memória que tem o numero que esta no registo
Em termos de Layman…
Se usar %ebp
Obtenho 4
Se usar (%ebp)
Obtenho o que quer que está na RAM, no endereço quatro
movl $4, %edx(%EDX = 4)
Logo podemos usar registos como apontadores ou como variáveis
Existem 8 registos principais de 32 bits no processador (existem mais como %EIP):
%EAX%EBX%ECX%EDX
%ESI%EDI%EBP%ESP
Por convenção, apenas %EAX,%ECX e %EDX podem ser alterados à vontade
Todos os outros contém valores que necessitam ser conservados, podemos usá-los (exceto o %ESP), mas temos que garantir que no fim da nossa função, o seu valor é restaurado.
Existem 8 registos principais de 32 bits no processador (existem mais como %EIP):
%EAX%EBX%ECX%EDX
%ESI%EDI%EBP%ESP
Estas são as convenções usadas mais ou menos por quase todas as funções, podem faltar aqui algumas (mais coisa menos coisa…)
Aponta para o último elemento empilhado
Serve de referência aos argumentos de uma função (ou parametros…)
Retorno dos 4 bytes menos significativos
Retorno dos 4 bytes mais significativos
Existem 8 registos principais de 32 bits no processador (existem mais como %EIP):
%EAX%EBX%ECX%EDX
%ESI%EDI%EBP%ESP
Estas são as convenções usadas mais ou menos por quase todas as funções, podem faltar aqui algumas (mais coisa menos coisa…)
Normalmente usado como o índice destino para manipulação de
vetores de chars (Strings)
Normalmente usado como o índice fonte para manipulação de vetores
de chars (Strings)
Normalmente usado como pointer para o início de uma array
Normalmente usado como counter em iterações, loops, etc
Em termos de Layman…
%EAX%EBX%ECX%EDX
%ESI%EDI%EBP%ESP
AcummulatorBaseCounterSupporting register for 64 bit instructions (aka coisas maiores que 4 bytes)
Source IndexDestination IndexBase PointerStack Pointer
Implementação da Função Soma Em Assembly
y
x
RETURN
LIXO
C:int soma( int x, int y);
…Endereço Mais Alto
Endereço Mais Baixo
%esp
%ebp
O %ESP aponta sempre para o último elemento empilhado
Contudo, como já vimos, por convenção, o %EBP tem um valor qualquer.É necessário conservar esse valor antigo antes de o mudarmos…
y
x
RETURN
LIXO
Valor de EBP
C:int soma( int x, int y);
Assembly:pushl %ebp …
Endereço Mais Alto
Endereço Mais Baixo
%esp
%ebp
Portanto primeiro empilhamos ebp para guardar na pilha o seu valor.
y
x
RETURN
LIXO
C:int soma( int x, int y);
Assembly:pushl %ebp …
Endereço Mais Alto
Endereço Mais Baixo
%esp
%ebp
O ebp continua a apontar sabe-se lá para onde, mas nós queremos que ele aponte para os argumentos
Valor de EBP
y
x
RETURN
LIXO
C:int soma( int x, int y);
Assembly:pushl %ebpmovl %esp, %ebp
…Endereço Mais Alto
Endereço Mais Baixo
%esp%ebpSe passarmos o endereço de esp para ebp, este fica a apontar para uma posição que nós conhecemos. Mesmo que façamos push e alteremos o esp o ebp ficará sempre no mesmo sítio. Além disso o ebp aponta para onde ficou o seu antigo endereço que depois iremos restaurar.
ebp +4
ebp +8
ebp +12
Valor de EBP
y
x
RETURN
LIXO
C:int soma( int x, int y);
Assembly:pushl %ebpmovl %esp, %ebpmovl 12(%ebp), %eax
…Endereço Mais Alto
Endereço Mais Baixo
%esp%ebp
Queremos portanto somar x com y, contudo, não há nenhuma instrução que permita somar dois valores na pilha. É necessário mover um deles para um registo. Até agora temos à nossa disposição 3 registos que podemos usar livremente: %EAX, %ECX e %EDXComo já vimos a variável x tá 8 bytes acima do endereço para o qual %EBP aponta
ebp +4
ebp +8
ebp +12
EAX = y
Valor de EBP
y
x
RETURN
LIXO
C:int soma( int x, int y);
Assembly:pushl %ebpmovl %esp, %ebpmovl 12(%ebp), %eax
…Endereço Mais Alto
Endereço Mais Baixo
%esp%ebp
Queremos mover o conteúdo na pilha com o endereço de %ebp +12 para um registo qualquer, por exemplo %EDX
ebp +4
ebp +8
ebp +12
EAX = y
Valor de EBP
y
x
RETURN
LIXO
C:int soma( int x, int y);
Assembly:pushl %ebpmovl %esp, %ebpmovl 12(%ebp), %eaxaddl 8(%ebp), %eax
…Endereço Mais Alto
Endereço Mais Baixo
%esp%ebp
E agora somamos…
ebp +4
ebp +8
ebp +12
EAX = x+y
Valor de EBP
y
x
RETURN
LIXO
C:int soma( int x, int y);
Assembly:pushl %ebpmovl %esp, %ebpmovl 12(%ebp), %eaxaddl 8(%ebp), %eax
…Endereço Mais Alto
Endereço Mais Baixo
%esp%ebpJá temos o valor mas não o podemos devolver já… Porquê? Porque escrevemos no %EBP e agora temos de voltar a repo-lo. Além disso necessitamos de repor todo o espaço que gastámos na pilha.
ebp +4
ebp +8
ebp +12
EAX = x+y
Valor de EBP
y
x
RETURN
LIXO
C:int soma( int x, int y);
Assembly:pushl %ebpmovl %esp, %ebpmovl 12(%ebp), %eaxaddl 8(%ebp), %eaxmov %ebp, %esp
…Endereço Mais Alto
Endereço Mais Baixo
%esp%ebpPrimeiro, vamos colocar o %ESP a apontar para o %EBP.
Para quê? Bem neste caso, nada irá acontecer porque são iguais. Mas se tivéssemos feito mais push’s para alem do %EBP, o %ESP não apontaria para o mesmo que aponta %EBP, apontaria mais para baixo. Contudo convém utilizar sempre esta instrução, por convenção.
ebp +4
ebp +8
ebp +12
EAX = x+y
Valor de EBP
y
x
RETURN
LIXO
C:int soma( int x, int y);
Assembly:pushl %ebpmovl %esp, %ebpmovl 12(%ebp), %eaxaddl 8(%ebp), %eaxmovl %ebp, %esppop %ebp
…Endereço Mais Alto
Endereço Mais Baixo
%esp
E agora restauramos o valor do %EBP
Relembrar que pop %registo é o mesmo quemovl (%esp), %registoaddl $4, %esp
ebp +4
ebp +8
ebp +12
EAX = x+y
%ebp
Valor de EBP
y
x
RETURN
LIXO
C:int soma( int x, int y);
Assembly:pushl %ebpmovl %esp, %ebpmovl 12(%ebp), %eaxaddl 8(%ebp), %eaxmovl %ebp, %esppop %ebpret
…Endereço Mais Alto
Endereço Mais Baixo
%esp
E fazemos return da função.É importante referir que ret simplesmente indica que a função terminou (Na verdade é um pouco mais complexo, dado que volta para a main mas isso agora não interessa nada).Cabe à main buscar a variável que ela quer…
ebp +4
ebp +8
ebp +12
EAX = x+y
%ebp
Valor de EBP
E que variável é essa?Por convenção, o registo que a main vai buscar é:-Se a função for uma void a main não lê nada
-Se a função fizer return de 4 bytes ou menos a main vai buscar o que está no %EAX (exemplo: long ints (32bits), chars, pointers (a maioria, contudo alguns podem ser maiores)
-Se a função fizer return de 8 bytes, os dados são divididos em dois registos: no %EDX ficará os números mais significativos e no %EAX os menos significativos.
Neste caso, a função é do tipo int32, logo o valor é depositado no %EAX