Pilha

Entendendo como a pilha (hardware stack) funciona na arquitetura x86

Uma pilha, em inglĂȘs stack, Ă© uma estrutura de dados LIFO -- Last In First Out -- onde o Ășltimo dado a entrar Ă© o primeiro a sair. Imagine uma pilha de livros onde vocĂȘ vai colocando um livro sobre o outro e, apĂłs empilhar tudo, vocĂȘ resolve retirar um de cada vez. Ao retirar os livros vocĂȘ vai retirando desde o topo atĂ© a base, ou seja, os livros saem na ordem inversa em que foram colocados. O que significa que o Ășltimo livro que vocĂȘ colocou na pilha vai ser o primeiro a ser retirado, isso Ă© LIFO.

Hardware Stack

Processadores da arquitetura x86 tem uma implementação nativa de uma pilha, que é representada na memória RAM, onde essa pode ser manipulada por instruçÔes específicas da arquitetura ou diretamente como qualquer outra região da memória. Essa pilha normalmente é chamada de hardware stack.

O registrador SP/ESP/RSP, Stack Pointer, serve como ponteiro para o topo da pilha podendo ser usado como referĂȘncia inicial para manipulação de valores na mesma. Onde o "topo" nada mais Ă© que o Ășltimo valor empilhado. Ou seja, o Stack Pointer estĂĄ sempre apontando para o Ășltimo valor na pilha.

A manipulação båsica da pilha é empilhar (push) e desempilhar (pop) valores na mesma. Veja o exemplo na nossa PoC:

bits 64

global assembly
assembly:
  mov  rax, 12345
  push rax

  mov rax, 112233
  pop rax
  ret

Na linha 6 empilhamos o valor de RAX na pilha, alteramos o valor na linha 8 mas logo em seguida desempilhamos o valor e jogamos de volta em RAX. O resultado disso é o valor 12345 sendo retornado pela função.

A instrução pop recebe como operando um registrador ou endereçamento de memória onde ele deve armazenar o valor desempilhado.

A instrução push recebe como operando o valor a ser empilhado. O tamanho de cada valor na pilha também acompanha o barramento interno (64 bits em 64-bit, 32 bits em protected mode e 16 bits em real mode). Pode-se passar como operando um valor na memória, registrador ou valor imediato.

A pilha "cresce" para baixo. O que significa que toda vez que um valor é inserido nela o valor de ESP é subtraído pelo tamanho em bytes do valor. E na mesma lógica um pop incrementa o valor de ESP. Logo as instruçÔes seriam equivalentes aos dois pseudocódigos abaixo (considerando um código de 32-bit):

ESP = ESP - 4
[ESP] = operando

Last updated