STDCALL против CDECL: `ret` против` sub esp` имеют какое-либо отношение к соглашению о вызовах? - PullRequest
0 голосов
/ 09 октября 2018

In Язык ассемблера, седьмое издание для процессоров x86 от Kip Irvine , на стр. 325, говорится в 8.2.4 32-битных соглашениях о вызовах ,

Соглашение о вызовах C ... Соглашение о вызовах C решает проблему очистки стека времени выполнения простым способом: когда программа вызывает подпрограмму, она следуетCALL инструкция с оператором, который добавляет значение к указателю стека (ESP), равное объединенным размерам параметров подпрограммы.Вот пример, в котором два аргумента (5 и 6) помещаются в стек перед выполнением инструкции CALL,

Example1 PROC
  push 6
  push 5
  call AddTwo
  add esp, 8
  ret
Example1 ENDP

Поэтому программы, написанные на C / C ++, всегда удаляют аргументы из стека ввызывающая программа после возврата подпрограммы.

Далее говорится:

Соглашение о вызовах STDCALL Другой распространенный способ удаления параметров из стека:использовать соглашение под названием STDCALL.В следующей процедуре AddTwo мы добавляем параметр interger к инструкции RET, которая, в свою очередь, добавляет 8 к ESP после возврата к вызывающей процедуре. Целое число должно равняться количеству байтов в стекепространство, занимаемое параметрами процедуры:

AddTwo PROC
  push ebp
  mov ebp,esp
  mov eax,[ebp+12]
  add eax,[ebp+8]
  pop ebp
  ret 8
AddTwo ENDP

Следует отметить, что STDCALL, как и C, помещает аргументы в стек в обратном порядке.Имея параметр в инструкции RET, STDCALL уменьшает количество кода, сгенерированного для вызовов подпрограммы (на одну инструкцию), и гарантирует, что вызывающие программы никогда не забудут очистить стек.Соглашение о вызове C, с другой стороны, позволяет подпрограммам объявлять переменное число параметров.Вызывающий может решить, сколько аргументов он передаст.

1 Ответ

0 голосов
/ 09 октября 2018

Этот код немного сбивает с толку, потому что один показывает вызов, а другой - функцию.И для простоты они оба должны показать оба.Существует два этапа модификации стека с целью вызова соглашений:

  • При подготовке к вызову, когда аргументы и помещаются в стек.
  • В вызываемой функциигде локальные ресурсы расположены в стеке.

Разница между этими двумя соглашениями не "одна инструкция" и не имеет ничего общего с RET, скажем,, но где происходит уборка.Аргументы помещаются в стек до вызова, поэтому они

  1. должны быть очищены, когда функция очищает себя (локальные).
  2. Должны быть очищены, когда послефункция возвращает.

Как примечание к импорту, у первого варианта есть свои преимущества, а именно то, что вы объявляете функцию с переменным количеством аргументов.

Весь фрагмент RET кажетсяотвлекать внимание, поскольку в соглашении о вызовах нет ничего конкретного для x86.Фактически, Windows 10 работает на ARM, который даже не поддерживает RET Более того, в первом примере с cdecl компилятор мог написать:

ret 8

Вместо

 add esp,8
 ret

И это имело бы тот же эффект.Фактически, это спасло бы инструкцию

...