Какое влияние эти две инструкции вызывают в коде сборки, сгенерированном gcc для машин x86:
push %ebp
movl %esp, %ebp
Какое влияние эти две инструкции вызывают в коде сборки, сгенерированном gcc для машин x86:
push %ebp
movl %esp, %ebp
Разъяснение объяснения - это буквальная правда (несмотря на одну незначительную направленную ошибку), но не объясняет, почему.
%ebp
является "базовым указателем" для вашего фрейма стека. Это указатель, используемый средой выполнения C для доступа к локальным переменным и параметрам в стеке. Здесь некоторый типичный пролог-код функции, генерируемый GCC (g++, если быть точным). Сначала источник С++.
// junk.c++
int addtwo(int a)
{
int x = 2;
return a + x;
}
Это генерирует следующий ассемблер.
.file "junk.c++"
.text
.globl _Z6addtwoi
.type _Z6addtwoi, @function
_Z6addtwoi:
.LFB2:
pushl %ebp
.LCFI0:
movl %esp, %ebp
.LCFI1:
subl $16, %esp
.LCFI2:
movl $2, -4(%ebp)
movl -4(%ebp), %edx
movl 8(%ebp), %eax
addl %edx, %eax
leave
ret
.LFE2:
.size _Z6addtwoi, .-_Z6addtwoi
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",@progbits
Теперь, чтобы объяснить этот пролог-код (все вещи до .LCFI2:
), сначала:
pushl %ebp
хранит фрейм стека вызывающей функции в стеке.movl %esp, %ebp
принимает текущий указатель стека и использует его как фрейм для вызываемой функции.subl $16, %esp
оставляет место для локальных переменных.Теперь ваша функция готова к работе. Любые ссылки с отрицательным смещением от регистра %ebp%
являются вашими локальными переменными (x
в этом примере). Любые ссылки с положительным смещением от регистра %ebp%
передаются ваши параметры.
Последняя представляющая интерес задача - это инструкция leave
, которая представляет собой инструкцию ассемблера x86, которая выполняет работу по восстановлению фрейма стека вызывающей функции. Обычно это оптимизируется в более быстрой последовательности move %ebp %esp
и pop %ebp%
в коде C. Однако для иллюстративных целей я вообще не собирался с любыми оптимизациями.
Это типичный код, который вы видите в начале функции.
Сохраняет содержимое регистра EBP в стеке, а затем сохраняет содержимое текущего указателя стека в EBP.
Стек используется во время вызова функции для хранения локальных аргументов. Но в функции указатель стека может измениться, поскольку значения хранятся в стеке.
Если вы сохраните исходное значение стека, вы можете обратиться к сохраненным аргументам через регистр EBP, в то время как вы можете использовать (добавлять значения) в стек.
В конце функции вы, вероятно, увидите команду
pop %ebp ; restore original value
ret ; return
push %ebp
Это приведет к 32-разрядному (расширенному) регистру базового указателя в стеке, то есть указатель стека (% esp) вычитается на четыре, тогда значение% ebp будет скопировано в местоположение, на которое указывает указатель стека.
movl %esp, %ebp
Это копирует регистр указателя стека в регистр базового указателя.
Цель копирования указателя стека на базовый указатель заключается в создании фрейма стека, то есть области в стеке, где подпрограмма может хранить локальные данные. Код в подпрограмме будет использовать базовый указатель для ссылки на данные.
Это часть того, что называется пролог функции.
Сохраняет текущий базовый указатель, который будет извлекаться при завершении функции и устанавливает новый ebp в начало нового фрейма.
Я также считаю, что важно отметить, что часто после
push %ebp
и
movl %esp, %ebp
сборка будет иметь push %ebx
или push %edx
. Это номера вызывающих абонентов регистров %ebx
и %edx
. В конце рутинного вызова регистры будут восстановлены с их исходными значениями.
Также - %ebx, %esi, %edi
- все регистры сохранения вызываемого абонента. Поэтому, если вы хотите перезаписать их, вам нужно сначала их сохранить, а затем восстановить.
Кусок кода устанавливает стек для вашей программы.
В x86 информация стека хранится двумя регистрами.
Base pointer (bp): Holds starting address of the stack
Stack pointer (sp): Holds the address in which next value will be stored
Эти регистры имеют разные имена в разных режимах:
Base pointer Stack pointer
16 bit real mode: bp sp
32 bit protected mode: ebp(%ebp) esp(%esp)
64 bit mode: rbp rsp
Когда вы устанавливаете стек, указатель стека и базовый указатель получают тот же адрес в начале.
Теперь, чтобы объяснить ваш код,
push %ebp
Этот код подталкивает текущий адрес стека в стек, чтобы функция могла "выйти" или "вернуться" должным образом.
movl %esp, %ebp
Этот код устанавливает стек для вашей функции.
Для получения дополнительной информации см. Этот question.
Надеюсь эта помощь!