Чтобы ответить на эти пронумерованные вопросы:
1) subl $24,%esp
означает esp = esp - 24
GNU AS использует синтаксис AT & T, противоположный синтаксису Intel. У AT & T пункт назначения справа, у Intel пункт назначения слева. Также AT & T явно указывает размер аргументов. Intel пытается вывести это или заставляет вас быть явным.
Стек увеличивается в памяти, память в и после esp - содержимое стека, адреса ниже, чем esp - неиспользуемое пространство стека. esp указывает на последнюю вещь, помещенную в стек.
2) Кодировка команд x86 в основном позволяет следующее:
movl rm,r ' move value from register or memory to a register
movl r,rm ' move a value from a register to a register or memory
movl imm,rm ' Move immediate value.
Нет формата инструкции «память-в-память». (Строго говоря, вы можете выполнять операции с памятью в память с помощью movs
или push mem
, pop mem
, но ни один из них не использует два операнда памяти для одной и той же инструкции)
«Немедленно» означает, что значение закодировано прямо в инструкции. Например, хранить 15 по адресу в ebx:
movl $15,(%ebx)
15 является «немедленным» значением.
Скобки позволяют использовать регистр в качестве указателя на память.
3) movl 8(%ebp),%eax
означает,
- принять значение ebp
- добавить 8 к нему (но не изменяет ebp),
- использовать его как адрес (скобки),
- читать 32-битное значение с этого адреса,
- и сохранить значение в eax
esp - указатель стека.
В 32-битном режиме каждое нажатие и выталкивание в стеке имеет ширину 4 байта. Как правило, большинство переменных в любом случае занимают 4 байта. Таким образом, вы могли бы сказать, что 8 (% ebp) означает, начиная с вершины стека, дать мне значение 2 (4 x 2 = 8) целых чисел в стеке.
Как правило, 32-битный код использует ebp для указания на начало локальных переменных в функции. В 16-битном коде x86 не было способа использовать указатель стека в качестве указателя (трудно поверить, правда?). Поэтому люди скопировали sp
в bp
и использовали bp в качестве локального указателя кадра. Это стало совершенно ненужным, когда вышел 32-битный режим (80386), у него был способ просто использовать указатель стека напрямую. К сожалению, ebp облегчает отладку, поэтому мы продолжили использовать ebp в 32-битном коде (сделать дамп стека тривиально легко, если используется ebp).
К счастью, amd64 дал нам новый ABI, который не использует ebp в качестве указателя кадра, 64-битный код обычно использует esp для доступа к локальным переменным, ebp доступен для хранения переменной.
4) Объяснено выше
5) leave
- старая инструкция, которая просто выполняет movl %ebp,%esp
и popl %ebp
и сохраняет несколько байтов кода. На самом деле он отменяет изменения в стеке и восстанавливает ebp вызывающего. Вызываемая функция должна сохранять ebp
в ABI x86.
При входе в функцию компилятор выполнил subl $ 24,% esp, чтобы освободить место для локальных переменных и иногда временного хранилища, для которого не хватало регистров для хранения.
Лучший способ «представить» кадр стека в вашем уме - это увидеть его как структуру, лежащую в стеке. Первые члены воображаемой структуры - это самые последние «выдвинутые» ценности. Поэтому, когда вы перемещаетесь в стек, представьте, что вы вставляете новый элемент в начале структуры, пока ни один из других элементов не переместился. Когда вы «выталкиваете» из стека, вы получаете значение первого члена воображаемой структуры, и эта (первая) строка структуры исчезает из существования.
Управление кадрами стека - это в основном просто перемещение указателя стека, чтобы освободить место в той воображаемой структуре, которую мы называем кадром стека. Вычитание из указателя стека просто помещает несколько воображаемых членов в начало структуры за один шаг. Добавление указателя в стек приводит к исчезновению первых многих элементов.
Конец отправленного вами кода не является типичным. Это jmp
, как правило, ret
. Компилятор был сообразителен и выполнил «оптимизацию хвостового вызова», то есть он просто очищает то, что он сделал со стеком, и переходит к f
. Когда f(2)
вернется, он на самом деле вернется прямо к вызывающей стороне (не обратно в код, который вы отправили)