_cdecl соглашение о вызовах - PullRequest
3 голосов
/ 28 февраля 2012

В статье о соглашении о вызовах _cdecl писатель упомянул:

Освободить локальное хранилище Когда функция выделяет локальное, временное пространство, она делает это, уменьшая с точки стека величину пространстванеобходимо, и этот процесс должен быть обратным, чтобы освободить это пространство.Обычно это делается путем добавления к указателю стека той же суммы, которая была вычтена ранее, хотя серия инструкций POP могла бы достичь того же результата.

Мой вопрос: могу ли я просто установить ESP на текущийЗначение EBP вместо «добавления к указателю стека того же количества, которое было вычтено» или «серии инструкций POP»?

Как:

mov esp, ebp

Мне кажется, это лучший способпотому что, если я позже изменю число локальных переменных этой функции, мне не придется беспокоиться о дальнейшем увеличении значения.

Ответы [ 2 ]

4 голосов
/ 28 февраля 2012

Технически это было бы злоупотреблением кадрами стека, которые предназначены для выявления ошибок, возникающих из-за дисбаланса стека, но это было бы совершенно законно.

Есть несколько вещей, на которые следует обратить внимание, если у вас очень ограниченное пространство стека из-за большого выделения памяти или встроенного устройства, стоит очистить стек после каждого вызова,Это также делает отладку немного проще, потому что тогда вы знаете, что что-то использует неправильное количество аргументов.

Также, если кто-то собирается поддерживать ваш код, он сочтет это очень запутанным.

2 голосов
/ 28 февраля 2012

На самом деле, это именно то, что делает инструкция leave , и она была введена для поддержки языков высокого уровня.Тем не менее, он используется не очень часто;большинство компиляторов просто делают явную последовательность mov esp, ebp; pop ebp.См. Также этот вопрос .

Однако иногда вы можете выполнить оптимизацию «опустить указатель кадра».Это освобождает EBP для использования в качестве регистра общего назначения, но вы (или компилятор) должны отслеживать изменения ESP в течение всей функции и использовать возможные изменения смещения для адресации локальных переменных или входящих аргументов.Если вы сделаете это, вам придется использовать pops или явное добавление, чтобы восстановить ESP до его первоначального значения перед возвратом.

Обратите внимание, что вышесказанное касается всей функции (то есть prolog / epilog);когда вам нужно вызвать определенную функцию __cdecl в середине функции, вы не можете просто восстановить ESP до значения EBP, потому что это значение действительно только в самом начале функции, до любогоместо выделено для локальных переменных.Здесь есть два подхода:

1) push-аргументы, восстановить ESP после вызова:

push offset msg
call _printf
pop ecx ; clobbers ECX but shorter than add esp, 4

2) переместить аргументы в зарезервированные слоты стека;вам не нужно восстанавливать ESP в этом случае:

mov dword ptr [esp+0], offset msg
call _printf
; no need to change ESP

Если вы выберете второй вариант, вам нужно убедиться, что вы не храните локальные переменные в этих слотах.Кроме того, такие mov инструкции обычно намного длиннее, чем нажатия, поэтому вам может потребоваться учитывать это, если размер кода является проблемой.

...