Стек компьютера и язык ассемблера

Прямо сейчас я пытаюсь изучить ассемблер в системах x86. Поэтому готовлю книгу «Программирование с нуля». (Доступно бесплатно по адресу http://download.savannah.gnu.org/releases/pgubook/ < / а>)

На странице 53 объясняется, как работает стек компьютера:

Стек компьютера находится в самых верхних адресах памяти. Вы можете помещать значения в верхнюю часть стека с помощью инструкции pushl. [...] Ну, мы говорим, что это верх, но "верх" стека на самом деле является низом памяти стека. [...] В памяти стек начинается с вершины памяти и увеличивается вниз по архитектурным соображениям. Поэтому, когда мы говорим о «вершине стека», помните, что она находится в нижней части памяти стека.

Я понимаю эту часть. Допустим, память стека начинается с адреса 0 и заканчивается по адресу 11 (включительно). Это означает, что в настоящее время в стеке есть три слова (по 4 байта). Насколько я понимаю, слово, которое находится «наверху» стека, в настоящее время занимает адреса 8, 9, 10 и 11. (поскольку одно слово имеет 4 байта и, следовательно, занимает четыре места хранения в основной памяти). Однако теперь в книге говорится следующее:

Регистр стека% esp всегда содержит указатель на текущую вершину стека.

Хорошо, в моем примере регистр% esp будет содержать адрес 8. Он указывает на слово, которое в настоящее время находится на вершине стека. Но...

Каждый раз, когда мы помещаем что-то в стек с помощью pushl,% esp вычитается на 4, так что он указывает на новую вершину стека (помните, что каждое слово имеет длину четыре байта, и стек увеличивается вниз).

Какие? Разве не наоборот? Если я помещу в стек еще одно машинное слово размером 4 байта, это слово займет адреса основной памяти с 12 по 15. Как они и сказали: стек растет вниз. Теперь регистр% esp указывает на слово, которое в данный момент находится на вершине стека. Он начинается с адреса 12. До того, как мы поместили еще одно слово в стек, адрес, который был сохранен в% esp, был 8. Итак,% esp явно был добавлен 4, а не вычтен. Откуда у них вычитание? Что я пропустил? Я очень смущен...

Помощь очень ценится;)


person lkbaerenfaenger    schedule 26.10.2013    source источник
comment
Расти вниз просто означает вычитание. 12 - 4 = 8, ESP получает меньшее значение, чем вы помещаете что-то в стек. Просто встаньте на голову, и все будет нормально.   -  person Hans Passant    schedule 26.10.2013


Ответы (3)


Если я помещу в стек еще одно машинное слово размером 4 байта, это слово займет адреса основной памяти с 12 по 15. Как они и сказали: стек растет вниз.

Вниз означает переход к более низким адресам, поэтому добавление другого значения в стек означает вычитание 4 и запись значения в новое место. Итак,% esp становится 4.

  +--------+
8 |12345678| <- top of stack before push
  +--------+
4 |11223344| <- top of stack
  +--------+
0 |00000000|
  +--------+
person Mark Tolonen    schedule 26.10.2013
comment
Тем не менее, если вам удалось заставить стек достичь 0x0, вы делаете что-то серьезно неправильно :) - person Matteo Italia; 26.10.2013
comment
Я все еще очень запутался: допустим, адреса от 0 до 39 зарезервированы в памяти для стека. Это означает, что имеется достаточно места для размещения 10 машинных слов размером 4 байта в верхней части стека. Это означает, что верхний элемент занимает адреса 39, 38, 37, 36. Если бы мы поместили другое слово в этот стек, оно заняло бы адреса 35, 34, 33, 32. Таким образом, регистр% esp указал бы на 36 в первом место, затем по адресу 32. Я знаю, что это абстрактный пример, но правильно ли это теоретически? - person lkbaerenfaenger; 26.10.2013
comment
Да исправить. Обычно стек начинается с верха памяти и растет вниз, а код и данные начинаются с нижней части памяти и накапливаются. Когда ваши данные растут и стеки растут вниз, вам не хватает памяти. - person Mark Tolonen; 26.10.2013

Обычно «верхние адреса памяти» относятся к старшим адресам. Например, ваш стек может начинаться с 0x00105000; если вы добавляете слово, вы перемещаете esp в 0x00104ffc (т.е. вы растете вниз в адресах памяти). См., Например, здесь для хорошей диаграммы.

person Matteo Italia    schedule 26.10.2013

Если вы вводите свою функцию и стек находится по адресу 0x100. Обычно стек растет вниз, к нулю, от высоких адресов к низким. поэтому, если вы затем нажмете один 4-байтовый элемент, «верх» стека теперь будет в 0xFC. Нажимаем еще один, 0xF8 и так далее.

Еще одна вещь, которая здесь мешает, - это ebp vs esp.

Обычно процессор имеет указатель стека, либо специальный регистр, либо регистр общего назначения, к которому привязаны определенные инструкции, push, pop и, возможно, относительная адресация указателя стека, загрузка и сохранение. Также компиляторы нередко используют регистр общего (или специального назначения), отличный от указателя стека, для кадра стека. Почему? чтобы облегчить чтение и отладку кода, сгенерированного компилятором.

При вводе функции, например, вы можете сделать копию указателя стека в этот другой регистр, и на время работы функции, в которой регистр не перемещается, это позволяет вам выполнять относительную адресацию к этому регистру для поиска локальных переменных, а также параметры функции (если этот компилятор и процессор имеют тенденцию передавать параметры в стек). Таким образом, на протяжении всей функции вы можете обращаться к любой из этих локальных переменных с одинаковым смещением. Если вы не использовали кадр стека, то, если по причинам оптимизации (сохранение памяти стека) указатель стека был динамическим внутри функции, то относительные смещения от указателя стека до локальных переменных и параметров функции на основе стека также были бы динамическими. и труднее читать и отлаживать. (сохраняя пространство стека и возвращая регистр для других целей, обе оптимизации).

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

person old_timer    schedule 26.10.2013