Указатели стека / базы в сборке - PullRequest
5 голосов
/ 28 апреля 2010

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

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

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

Спасибо!

http://upload.wikimedia.org/wikipedia/en/a/a7/ProgramCallStack2.png http://upload.wikimedia.org/wikipedia/en/a/a7/ProgramCallStack2.png

Ответы [ 3 ]

3 голосов
/ 28 апреля 2010

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

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

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

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

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

0 голосов
/ 28 апреля 2010

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...