объяснение раскручивания - буквальная истина (несмотря на одну незначительную ошибку направления), но не объясняет почему.
%ebp
- это «базовый указатель» для вашего стекового фрейма. Это указатель, используемый средой выполнения C для доступа к локальным переменным и параметрам в стеке. Вот типичный код пролога функции, сгенерированный GCC (точнее, g ++). Сначала источник C ++.
// 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-коде. Однако для наглядности я вообще не компилировал никаких оптимизаций.