Стек вызовов сборки — вопросы терминологии

Я совершенно новичок в ассемблере и хочу подтвердить, где в следующих утверждениях у меня есть недоразумение и что его нужно исправить.

Указатель стека (ESP) указывает на вершину (самый низкий адрес памяти) стека.

Базовый указатель (EBP) используется для временного хранения различных адресов памяти при построении кадра стека. Обычно он содержит самый высокий адрес памяти текущего кадра стека.

Указатель инструкции (EIP) относится к адресу памяти строки кода в текстовом (кодовом) сегменте памяти.

После того, как что-то было помещено в стек, его нельзя изменить на месте. т.е. Если мы PUSH EBP в стек, мы помещаем текущее значение EBP, а не какую-то ссылку или указатель на него. Затем мы не можем изменить это значение на месте.

Аргументы, передаваемые в функцию, обычно перемещаются в адресное пространство, которое является смещением указателя стека. т.е. [ESP-12].

При вызове функции (используя CALL) происходит следующее:

  1. Адрес возврата добавляется в стек (память адреса, следующего сразу за текущим EIP, чтобы мы знали, куда вернуться после завершения вызванной функции).
  2. Сохраненный указатель фрейма добавляется в стек, который обычно является указателем стека фрейма стека вызывающей функции.
  3. Затем мы перейдем к прологу вызываемой функции.

Спасибо. Я пытаюсь разобраться в этом.


person Jordan    schedule 15.11.2017    source источник


Ответы (2)


Аргументы, передаваемые в функцию, обычно перемещаются в адресное пространство, которое является смещением указателя стека. т.е. [ЭСП-12].

Часто аргументы помещаются в стек перед вызовом.

push paramA  ; ( some 32bit value, register, whatever )
push paramB
call myFunct

Это приводит к следующему содержимому стека:

---------------
|    paramA   |
---------------
|    paramB   |
---------------
| return addr |   <-- ESP
--------------- 

Поскольку адрес возврата (нажатый call ) составляет 4 байта, параметры функции находятся в [ESP+4] и [ESP+8].

Если ваша функция добавляет кадр стека, обычно вы делаете

myFunct:  push EBP
          mov EBP, ESP

Теперь стек выглядит так:

---------------
|    paramA   |
---------------
|    paramB   |
---------------
| return addr |   
---------------     
|   saved EBP |   <-- EBP, ESP
--------------- 

и параметры находятся в [EBP+8] и [EBP+12], даже если вы введете больше значений (или добавите какое-то место для локальных переменных), поскольку EBP больше не меняется:

myFunct:  push EBP
          mov EBP, ESP
          sub ESP, 12      ; make room for 3 32bit local variables

          mov eax, [EBP+8] ; access one of the parameters
          mov [EBP-4], eax ; save it in local variable #1

rel |  rel |
to  |  to  |
ESP |  EBP |
----|------|--------------
+24 | +12  |    paramA   |
    |      |--------------
+20 | +8   |    paramB   |
    |      |--------------
+16 | +4   | return addr |  
    |      |--------------
+12 |      |   saved EBP |  <-- EBP   (is fixed here for now)
    |      |--------------- 
+8  | -4   |    local#1  |
    |      |--------------- 
+4  | -8   |    local#2  |
    |      | --------------- 
0   | -12  |    local#3  |  <--- ESP  (keeps growing, by pushing, calling etc)
           --------------- 

Локальные переменные находятся в [EBP-4], [EBP-8], [EBP-12] и т. д.
Адрес возврата находится в [EBP+4].

Примечание: как видите, можно

  • для доступа по ESP (тогда вам не нужен указатель фрейма, но тогда вам нужно отслеживать, сколько данных вы отправили, чтобы «найти» параметры и переменные)
  • или EBP (что добавляет некоторые накладные расходы). Во многих функциях указатель фрейма вообще не нужен и оптимизируется компиляторами.
person Tommylee2k    schedule 15.11.2017
comment
Стек растет вниз, параметры имеют положительные смещения на esp и ebp. - person Jester; 15.11.2017
comment
@Jester да, глупый я, уже заметил (и исправил). сегодня был тяжелый день :D - person Tommylee2k; 15.11.2017
comment
Вы также ошиблись в смещениях: параметры функции находятся в [ESP+8] и [ESP+12] должны быть +4 и +8 соответственно. - person Jester; 15.11.2017
comment
в версии без фрейма-указателя, вполне. спасибо еще раз - person Tommylee2k; 15.11.2017
comment
Стоит ли упоминать (я знаю, что это на самом деле не задано в вопросе) о том, как вы можете избежать использования EBP и просто использовать ESP, если текущее использование стека известно во всей вашей функции. (Обычно доступен как оптимизация с компиляторами C или C++). - person Damien_The_Unbeliever; 15.11.2017

После того, как что-то было помещено в стек, его нельзя изменить на месте. т.е. Если мы PUSH EBP в стек, мы помещаем в стек текущее значение EBP, а не какую-то ссылку или указатель на него. Мы не можем изменить это значение на месте.

Что вы можете. Стек — это обычная компьютерная память, в нем нет ничего особенного, за исключением того, что 99% кода ожидает действительный (доступ для чтения и записи) адрес памяти в esp и некоторое зарезервированное пространство, поэтому он может подталкивать в него какие-то локальные вещи по мере необходимости.

push  ebp    ; store current value in ebp to stack

Почти эквивалентно:

sub   esp,4
mov   [esp],ebp

(но второй вариант также изменит флаги и несколько менее атомарен)

Теперь вы можете перезаписать его чем угодно, например:

mov [esp],eax ; overwrite the old_ebp value with current_eax value

ссылка или указатель на него

Ну нет никакой ссылки или указателя на регистр ebp, это регистр в процессоре, всего 32 бита (32x 0 или 1 значение) и у него нет адреса, с ним можно работать только по его имени ebp в инструкциях, позволяющих использовать его в своей кодировке.

После push ebp эти 32 бита (и никакой другой информации) копируются в память (которая затем содержит эти значения 0/1, скопированные в свои собственные 32 бита = 4 байта). Нет информации, откуда было записано значение в память, когда и по какой инструкции, хранятся только биты значения.

person Ped7g    schedule 15.11.2017