Функция пролог и эпилог
В на языке ассемблера программировании представляет пролог функции собой несколько строк кода в начале функции, которые подготавливают стек и регистры для использования внутри функции. Аналогично, эпилог функции появляется в конце функции и восстанавливает стек и регистры в состояние, в котором они находились до вызова функции.
Пролог и эпилог не являются частью самого языка ассемблера; они представляют собой соглашение, используемое программистами на ассемблере и компиляторами многих языков более высокого уровня . Они достаточно жесткие и имеют одинаковую форму в каждой функции.
Пролог и эпилог функций также иногда содержат код защиты от переполнения буфера .
Пролог
[ редактировать ]Пролог функции обычно выполняет следующие действия, если архитектура имеет базовый указатель (также известный как указатель кадра) и указатель стека:
- Помещает текущий базовый указатель в стек, чтобы его можно было восстановить позже.
- Значение базового указателя устанавливается по адресу указателя стека (который указывает на вершину стека), так что базовый указатель будет указывать на вершину стека.
- Перемещает указатель стека дальше, уменьшая или увеличивая его значение, в зависимости от того, растет стек вниз или вверх. В x86 указатель стека уменьшается, чтобы освободить место для локальных переменных функции.
Можно написать несколько возможных прологов, что приведет к несколько иной конфигурации стека. Эти различия допустимы, если программист или компилятор правильно использует стек внутри функции.
В качестве примера, вот типичный пролог функции языка ассемблера x86 , созданный GCC.
push ebp
mov ebp, esp
sub esp, N
Непосредственное значение N — это количество байтов, зарезервированных в стеке для локального использования.
Того же результата можно добиться, используя enter
инструкция:
enter N, 0
Более сложные прологи можно получить, используя другие значения (кроме 0) для второго операнда enter
инструкция. Эти прологи помещают несколько указателей базы/фрейма, чтобы обеспечить вложенные функции , как того требуют такие языки, как Паскаль . Однако современные версии этих языков не используют эти инструкции, поскольку в некоторых случаях они ограничивают глубину вложенности. [ нужна ссылка ]
Эпилог
[ редактировать ]Эпилог функции отменяет действия пролога функции и возвращает управление вызывающей функции. Обычно он выполняет следующие действия (эта процедура может отличаться от одной архитектуры к другой):
- Перетащите указатель стека на текущий базовый указатель, чтобы освободить место, зарезервированное в прологе для локальных переменных.
- Извлекает базовый указатель из стека, чтобы восстановить его значение до пролога.
- Возвращается к вызывающей функции, извлекая счетчик программы предыдущего кадра из стека и переходя к нему.
Данный эпилог обратит эффекты любого из вышеупомянутых прологов (либо полного, либо того, который использует enter
). Согласно определенным соглашениям о вызовах, ответственность за очистку аргументов из стека лежит на вызываемом объекте, поэтому эпилог также может включать этап перемещения указателя стека вниз или вверх.
Например, эти три шага можно выполнить на 32-битном языке ассемблера x86 с помощью следующих инструкций:
mov esp, ebp
pop ebp
ret
Как и пролог, процессор x86 содержит встроенную инструкцию, выполняющую часть эпилога. Следующий код эквивалентен приведенному выше коду:
leave
ret
The leave
инструкция выполняет mov
и pop
инструкции, как указано выше.
Функция может содержать несколько эпилогов. Каждая точка выхода из функции должна либо перейти к общему эпилогу в конце, либо содержать собственный эпилог. Поэтому программисты или компиляторы часто используют комбинацию leave
и ret
выйти из функции в любой момент. (Например, компилятор C заменил бы return
заявление с leave
/ ret
последовательность).
Дальнейшее чтение
[ редактировать ]- де Бойн Поллард, Джонатан (2010). «Поколение функциональных перилогов» . Часто встречающиеся ответы .