Você está na página 1de 4

Pilha

Procedimentos Base da pilha


• Cresce em direção a
• Ao chamarmos um procedimento precisamos endereços menores da
memória
passar dados & controle de uma parte do código Endereços
• A unidade de alocação é crescentes
para outra uma palavra (4 bytes)
• Em particular, precisamos guardar o endereço • Registrador %esp contém
d retorno,
de t passar argumentost e salvar
l o endereço do elemento topo
conteúdo dos registradores. • Push (alocação de uma
palavra na pilha): subtrair 4 Pilha cresce
• Afinal, em Assembly não temos variáveis locais de %esp Stack
p/ baixo
Pointer
a procedimentos! • Pop (desalocar uma palavra %esp
da pilha): somar 4 a %esp
• Para isso, utiliza-se uma pilha (=um espaço de
memória gerenciado de forma controlada) Topo da pilha

Push Pop
Base da pilha Base da pilha

pushl Src popl Dest


– Decrementa %esp de 4 – Copie conteúdo de
memória indicado por
– Copia Src para
endereço dado por %esp para Dest
%esp
p – Incrementa %espp de 4
Equivalente a:
Equivalente a: Pilha cresce movl (%esp),Dest
Stack
Pointer
Pilha cresce
subl $4, %esp p/ baixo %esp p/ baixo
Stack addl $4,%esp +4
movl Src, (%esp) Pointer
-4
%esp

Novo topo da pilha


Novo topo da pilha

1
Controle de Fluxo de Procedimentos Exemplo Chamada Procedimento
804854e: call 8048b90 <main>
• Usa-se pilha para a transferência de controle na 8048553: pushl %eax
chamada a procedimentos e retorno
Antes do call Depois do call
• Chamada a procedimento:
call label Pilha
Empilha endereço de retorno na pilha, e desvia para label 0x110 0x110 (na memória)
0 10c
0x10c 0 10
0x10c
Obs: end.de retorno é o endereço da instrução seguinte ao
0x108 123 0x108 123
call
0x104 0x8048553
• Retorno:
ret Registradores
%esp 0x108 %esp 0x104
Desempilha endereço da pilha; e desvia para este endereço na CPU
%eip 0x804854e %eip 0x8048b90

Atenção: tanto call como ret alteram o valor de %esp Obs: %eip é o contador de programa (Program Counter – PC)

Exemplo Retorno
Pilha
8048591: ret

• Além de armazenar o endereço de


Antes do ret Depois do ret
retorno, a pilha é usada para armazenar o
estado de cada instância de procedimento
0x110 0x110
0 10c
0x10c 0 10c
0x10c chamada
c a ada (e
(entre
t e o momento
o e to da cchamada
a ada
0x108 123 0x108 123 e do retorno)
0x104 0x8048553 0x8048553

%esp 0x104 %esp 0x108


• O espaço da pilha reservado para cada
instância é chamado de registro de
%eip 0x8048591 %eip 0x8048553
ativação
Obs: %eip é o PC

2
Registro de Ativação
Registro de ativação: Exemplo
• Contém
– Variáveis locais Sequência
g(…)
– End. de retorno {
de Chamadas
h()
– Espaço de mem. temporária • h
f();
• Uso • g()
– Espaço é alocado quando se entra no procedimento }
g
f(…) f()
– Espaço liberado quando retorna %ebp End.ret
{
• Ponteiros • %ebp-ant
• f
– %esp (stack pointer) indica topo da pilha } %esp
– %ebp (base pointer) indica início do registro atual (da Topo
• O endereço de retorno é o último dado no registro de pilha
chamada de procedimento em execução) ativação
• A cada vez que é criado um novo registro de ativação o
%ebp do registro anterior é salvo (na própria pilha) e o
novo %ebp será o atual topo da pilha.

Salvamento da base do
Registro de Ativação (R.A.) Conteúdo dos Registradores
Segundo convenção da maioria dos compiladores no início
de cada procedimento: • Precisam ser salvos, para não serem sobre-
pushl %ebp #salva ebp do R.A. anterior escritos, p.ex. %edx
movl %esp, %ebp #novo ebp passa a ser o topo pilha
g:
• • • f:
E as últimas instruções de cada procedimento: movl $15213, %edx • • •
movl %ebp, %esp #desaloca todo o R.A. call f movl 8(%ebp), %edx
addl %edx, %eax addl $91125, %edx
popl %ebp #recupera o ebp do R.A. anterior • • •
• • •
ret #pop do end.retorno e desvia para lá ret ret
Entrada em procedimento Saida de procedimento
Caller Callee
%ebp-ant %ebp
g g
End.ret End.ret • Questão: Quem (caller/ callee) deve ficar
%ebp %ebp-ant
%esp
%esp %ebp-ant
responsável por salvar o conteúdo dos registradores
na pilha?

3
Convenção de Salvamento de Convenção de uso registradores
Registradores IA32/Linux
• Convenção no Linux:
callee-save %eax
– Caller (código chamador) deve salvar: %eax,%ecx e Caller-Save
%edx
%edx antes da chamada %ebx, %esi, %edi
%ecx
– Callee (código chamado) deve salvar: %ebx, %esi, Salve antes de usar!
%ebx
%edi
caller save
caller-save Callee Save
Callee-Save
%esi
– Portanto, dentro de um procedimento (callee) pode-
se sobre-escrever %eax,%ecx e %edx, mas %ebx, %eax, %edx, %ecx %edi
%esi e %edi precisam ser salvos na pilha (se Salve antes de call! %esp
Uso
forem usados) Especial %ebp

%eax sempre contém o


• Convenção no Windows:
valor de retrono do
– Caller deve salvar: %eax, %ebx %ecx e %edx
procedimento

Exemplo de Chamada
Passagem de Parâmetros int g (int a,int b) = return(a+b);
• Em C, a convenção é que o caller empilhe
g:
os parâmetros na ordem inversa em que
... push %ebp
aparecem na declaração do procedimento
pushl %ecx movl %esp, %ebp
int teste(int tam, int nums[])
pushl %eax push %ebx
• O 1º. parâmetro está no call g movl
l 8(%
8(%ebp),
b ) % %eax
endereço (%ebp)+8, o %ebp-ant popl %eax movl 12(%ebp), %ebx
2º.parâmetro no (%ebp)+12 popl %ecx addl %ebx,%eax
...
... popl %ebx
• Cada parâmetro é colocado ...
movl %ebp, %esp
em 4 bytes, mesmo se nums(end)
tam popl %ebp
ocupar menos que isso. End.ret
ret
• Por que não usar o %esp? epb-ant
%ebp
Código do Caller Código do Callee

Você também pode gostar