Prólogo e epílogo de função - Function prologue and epilogue
Na programação em linguagem assembly , o prólogo da função são algumas linhas de código no início de uma função, que preparam a pilha e os registros para uso dentro da função. Da mesma forma, o epílogo da função aparece no final da função e restaura a pilha e os registros ao estado em que estavam antes de a função ser chamada.
O prólogo e o epílogo não fazem parte da linguagem assembly em si; eles representam uma convenção usada por programadores de linguagem assembly e compiladores de muitas linguagens de nível superior . Eles são bastante rígidos, tendo a mesma forma em cada função.
O prólogo e o epílogo da função também às vezes contêm código para proteção contra estouro de buffer .
Prólogo
Um prólogo de função normalmente realiza as seguintes ações se a arquitetura tiver um ponteiro de base (também conhecido como ponteiro de quadro) e um ponteiro de pilha:
- Coloca o ponteiro da base atual na pilha, para que possa ser restaurado posteriormente.
- Atribui o valor do ponteiro de base ao endereço do ponteiro da pilha (que é apontado para o topo da pilha) de forma que o ponteiro da base aponte para o topo da pilha.
- Move o ponteiro da pilha mais adiante, diminuindo ou aumentando seu valor, dependendo se a pilha cresce para baixo ou para cima. No x86, o ponteiro da pilha é reduzido para abrir espaço para as variáveis locais da função.
Vários prólogos possíveis podem ser escritos, resultando em uma configuração de pilha ligeiramente diferente. Essas diferenças são aceitáveis, desde que o programador ou compilador use a pilha da maneira correta dentro da função.
Como exemplo, aqui está um prólogo de função de linguagem assembly x86 típico produzido pelo GCC
push ebp
mov ebp, esp
sub esp, N
O valor imediato N é o número de bytes reservados na pilha para uso local.
O mesmo resultado pode ser alcançado usando a enterinstrução:
enter N, 0
Prólogos mais complexos podem ser obtidos usando valores diferentes (diferentes de 0) para o segundo operando da enterinstrução. Esses prólogos enviam vários ponteiros de base / quadro para permitir funções aninhadas , conforme exigido por linguagens como Pascal . No entanto, as versões modernas dessas linguagens não usam essas instruções porque elas limitam a profundidade de aninhamento em alguns casos.
Epílogo
O epílogo da função reverte as ações do prólogo da função e retorna o controle para a função de chamada. Normalmente, ele executa as seguintes ações (este procedimento pode variar de uma arquitetura para outra):
- Largue o ponteiro da pilha para o ponteiro da base atual, de forma que o espaço reservado no prólogo para as variáveis locais seja liberado.
- Retira o ponteiro base da pilha, para que seja restaurado ao seu valor antes do prólogo.
- Retorna à função de chamada, retirando o contador de programa do quadro anterior da pilha e pulando para ele.
O epílogo dado irá reverter os efeitos de qualquer um dos prólogos acima (seja o completo ou aquele que usa enter). Sob certas convenções de chamada , é responsabilidade do receptor limpar os argumentos da pilha, portanto, o epílogo também pode incluir a etapa de mover o ponteiro da pilha para baixo ou para cima.
Por exemplo, essas três etapas podem ser realizadas em linguagem assembly x86 de 32 bits pelas seguintes instruções:
mov esp, ebp
pop ebp
ret
Como o prólogo, o processador x86 contém uma instrução embutida que realiza parte do epílogo. O código a seguir é equivalente ao código acima:
leave
ret
A leaveinstrução executa as instruções move pop, conforme descrito acima.
Uma função pode conter vários epílogos. Cada ponto de saída de função deve saltar para um epílogo comum no final ou conter seu próprio epílogo. Portanto, os programadores ou compiladores costumam usar a combinação de leavee retpara sair da função a qualquer momento. (Por exemplo, um compilador C substituiria uma returninstrução por uma seqüência leave/ ret).
Leitura adicional
- de Boyne Pollard, Jonathan (2010). "Os perigos da função de geração" . Respostas frequentemente fornecidas .