Что происходит в этом простом фрагменте кода вызова функции сборки x86 из Wikibooks? - PullRequest
0 голосов
/ 07 марта 2019

код

Следующий простой код сборки x86 указан в Wikibooks для соглашения о вызовах CDECL:

Определение функции:

_MyFunction1:
  push ebp
  mov ebp, esp
  mov eax, [ebp + 8]
  mov edx, [ebp + 12]
  add eax, edx
  pop ebp
  ret

Функция вызова:

push 3
push 2
call _MyFunction1
add esp, 8

Это то, что будет сгенерировано из следующего кода C:

_cdecl int MyFunction1(int a, int b)
{
  return a + b;
}

И этот вызов функции:

 x = MyFunction1(2, 3);

выпуск

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

  • вставить целое число 3 в стек
  • положить целое число 2 в стек
  • вызов _MyFunction1, помещающий IP-адрес указателя инструкций в стек
  • поместите базовый указатель ebp в стек, чтобы иметь возможность восстановить его позже. Я полагаю, это необходимо только потому, что в следующей инструкции мы переносим значение в ebp?
  • переместить значение текущего указателя стека esp в ebp для последующего использования при адресации памяти. Зачем это делать? Почему бы просто не использовать значение esp в следующих двух инструкциях напрямую (т.е. [esp + 8], [esp + 12])
  • получить два целочисленных значения, которые мы поместили в стек, и сохранить их в eax и edx. Очевидно, что мы делаем это, указывая каждый адрес памяти. Как процессор знает, как далеко он должен смотреть, пока полностью не прочитает, например, целое число 3? Откуда он знает, что это 32 или 16-битное целое число здесь? Как именно получаются 8 и 12 как смещение указателя стека? Что они имеют в виду, например 8 означает 8-битное смещение?
  • сложить два значения вместе в eax
  • восстановить ebp из стека
  • return, то есть выталкивание eip, содержащего следующую инструкцию из стека, и переход к ней
  • добавить 8 (опять, какой блок?) К указателю стека esp
    • теперь, с CDECL, вызывающая сторона должна очистить стек. Я считаю, что это должно было произойти с этой инструкцией. Но как просто продвигать указатель как способ очистки стека? Целочисленные значения 3 и 2 никогда не выталкивались. Во всяком случае, я бы подумал, что уменьшение значение указателя сделает свое дело, а не увеличение его, как здесь

1 Ответ

2 голосов
/ 07 марта 2019
  • Инструкции с ebp и esp являются стандартным способом установки стекового фрейма. Вы правы, в этом нет особой необходимости, и многие компиляторы этого не делают. Одна из причин этого - помочь отладчику отслеживать кадры стека.
  • Он знает, что нужно прочитать 4 байта из [ebp + 8] и [ebp + 12], потому что eax и edx являются 4-байтовыми регистрами. Этот размер закодирован в инструкциях.
  • x86-адреса всегда в байтах. Таким образом, смещения 8 и 12, добавленные в ebp, указаны в байтах.
  • когда 8 добавлено в esp, это просто число, без единиц измерения. Но когда esp используется для доступа к памяти, он обрабатывается как адрес байта. Таким образом, по сути, 8 означает 8 байтов.
  • Стек растет. Push уменьшает указатель стека. Таким образом, добавление в esp удаляет вещи из стека. Значения 2 и 3 все еще находятся в памяти, но, поскольку они находятся ниже указателя стека, они фактически исчезли. (И может на самом деле исчезнуть при некоторых редких обстоятельствах, таких как отладчик, оценивающий вызов функции с использованием вашего стека, или обработчик исключений Windows SEH , или в не-Windows, обработчик сигнала .)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...