Стек / указатели базы в блоке

Я знаю, что эта тема была затронута бесконечно здесь, и другие места в Интернете - но надо надеяться вопросом является простой, поскольку я пытаюсь получить голову вокруг блока...

Таким образом, если я понимаю правильно, что ebp (указатель базы) укажет на вершину стека, и ESP (указатель вершины стека) укажет на нижнюю часть - так как стек становится нисходящим. особенно поэтому точки к 'текущему местоположению'. Таким образом на вызове функции, после того как Вы сохранили ebp на стеке, Вы вставляете новый стековый фрейм - для функции. Таким образом в случае изображения ниже, если бы Вы запустили с N-3, Вы перешли бы к N-2 с вызовом функции. Но то, когда Вы в N-2 - является Вашим ebp == 25 и ESP == 24 (по крайней мере первоначально, прежде чем какие-либо данные будут помещены в стек)?

Это корректно или является мной прочь на касательной здесь?

Спасибо!

http://upload.wikimedia.org/wikipedia/en/a/a7/ProgramCallStack2.png
(источник: wikimedia.org)

6
задан Peter Cordes 24 July 2019 в 21:35
поделиться

3 ответа

На самом деле это зависит не только от аппаратной архитектуры и компилятора, но и от соглашения о вызовах , которое является просто согласованным способ, которым функции работают со стеком для вызова друг друга. Другими словами, существуют разные порядки, в которых функция может помещать вещи в стек, в зависимости от настроек вашего компилятора (и особых параметров #pragma и т. Д. И т. Д.).

Похоже, вы говорите о соглашении о вызовах cdecl в архитектуре x86. В этом случае ebp вызывающего абонента обычно помещается в стек сразу после адреса возврата. Таким образом, в вашем примере N-2 ячейка 25 будет содержать указатель обратно на вызывающую функцию N-3 (то есть она будет содержать адрес инструкции сразу после вызова , который привел вас к N- 2) и ячейка 24 будет содержать старый ebp , а ваш esp будет = 23 сразу после вызова, до того, как какие-либо локальные переменные будут помещены в стек. (За исключением того, что некоторые компиляторы освобождают место в стеке сразу после вызова, и поэтому ESP будет 20 вместо перемещения вверх и вниз внутри функции N-2.)

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

4
ответ дан 16 December 2019 в 21:36
поделиться
  1. После вызова N-3 , ebp будет 28 , а esp будет 25 .
  2. Старый ebp отправляется, а затем для ebp устанавливается текущее значение esp . Теперь и esp , и ebp равны 24 .
  3. Наконец, esp корректируется, чтобы освободить место для локальных переменных. esp сейчас скорее всего 20 , в зависимости от того, как функция ведет себя при вызове N-2 .

Лучший способ разобраться в этом - прочитать прологи функций и ознакомиться с реализацией x86. Это также помогает согласиться с тем, что esp и ebp используются для локализации использования стека в каждой функции с некоторыми вариациями между компиляторами, архитектурами и платформами (и почти не имеют отношения к пользователю любой язык более высокого уровня или равный C).

3
ответ дан 16 December 2019 в 21:36
поделиться

Это зависит от платформы, но, как правило, все работает именно так.

На архитектурах, с которыми я наиболее знаком, «вызывающий» (он же «возврат») адрес находится в регистре $ ra, а стек находится там, где он был оставлен вызывающей стороной. Итак, что происходит: адрес возврата помещается в стек, как и ваш базовый указатель (вызывающего), а затем базовый указатель обновляется до точки, где находится стек, и стек продолжает ползать вверх. Точный порядок того, где что-то помещается, и что устанавливается, когда я не помню, но обычно вызываемый объект должен сохранить регистры, которые будут затираться. Таким образом, вызывающей функции не нужно сохранять все, если вызываемая функция использует только один или два регистра. (На самом деле, регистр адреса возврата такой же - он не будет помещен в стек, если функция не вызывает ничего другого.)

На самом деле это довольно легко проследить, если вы дизассемблируете программу и посмотрите в прологе и эпилоге функции. Все они следуют довольно общим схемам: «хранить все» вверху и «восстанавливать все» внизу. (Обратите внимание, что иногда существуют «специальные» регистры, которые никогда не сохраняются и не восстанавливаются, и компилятор знает, что следует ожидать, что он может рассчитывать только на согласованность значений, если не было вызовов функций. В MIPS я думаю, что это регистры S, и PPC называет их t?)

1
ответ дан 16 December 2019 в 21:36
поделиться
Другие вопросы по тегам:

Похожие вопросы: