Какова функция инструкций push / pop, используемых для регистров в сборке x86?

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

  • Как вы можете нажать регистр? Куда это толкнуло? Зачем это нужно?
  • Это сводится к одной инструкции процессора или это более сложный?

5 ответов

Решение

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

Поппинг означает восстановление всего, что находится на вершине стека, в регистр. Это основные инструкции:

push 0xdeadbeef      ; push a value to the stack
pop eax              ; eax is now 0xdeadbeef

; swap contents of registers
push eax
mov eax, ebx
pop ebx

Вот как вы нажимаете регистр. Я предполагаю, что мы говорим о x86.

push ebx
push eax

Он помещается в стек. Значение ESP регистр уменьшается до размера выдвигаемого значения по мере увеличения стека в системах x86.

Это необходимо для сохранения ценностей. Общее использование

push eax           ;   preserve the value of eax
call some_method   ;   some method is called which will put return value in eax
mov  edx, eax      ;    move the return value to edx
pop  eax           ;    restore original eax

push это отдельная инструкция в x86, которая выполняет две вещи внутренне.

  1. Сохраните переданное значение по текущему адресу ESP регистр.
  2. Уменьшить ESP записать в размер заданного значения.

Куда это толкнуло?

esp - 4, Точнее:

  • esp вычитается на 4
  • значение доводится до esp

pop меняет это.

System V ABI говорит Linux сделать rsp при запуске программы укажите разумное расположение стека: /questions/9900042/kakovo-sostoyanie-registra-po-umolchaniyu-pri-zapuske-programmyi-asm-linux/9900057#9900057 которое обычно следует использовать.

Как вы можете нажать регистр?

Пример минимального GNU GAS:

.data
    /* .long takes 4 bytes each. */
    val1:
        /* Store bytes 0x 01 00 00 00 here. */
        .long 1
    val2:
        /* 0x 02 00 00 00 */
        .long 2
.text
    /* Make esp point to the address of val2.
     * Unusual, but totally possible. */
    mov $val2, %esp

    /* eax = 3 */
    mov $3, %ea 

    push %eax
    /*
    Outcome:
    - esp == val1
    - val1 == 3
    esp was changed to point to val1,
    and then val1 was modified.
    */

    pop %ebx
    /*
    Outcome:
    - esp == &val2
    - ebx == 3
    Inverses push: ebx gets the value of val1 (first)
    and then esp is increased back to point to val2.
    */

Выше на GitHub с работоспособными утверждениями.

Зачем это нужно?

Это правда, что эти инструкции могут быть легко реализованы с помощью mov, add а также sub,

Они объясняют, что существуют такие комбинации инструкций, что Intel решила предоставить их нам.

Причина, по которой эти комбинации встречаются так часто, заключается в том, что они позволяют легко сохранять и восстанавливать значения регистров в памяти, чтобы они не перезаписывались.

Чтобы понять проблему, попробуйте скомпилировать код на C вручную.

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

В идеале все переменные должны помещаться в регистры, которые являются самой быстрой памятью для доступа (в настоящее время примерно в 100 раз быстрее, чем оперативная память).

Но, конечно, мы можем легко иметь больше переменных, чем регистров, особенно для аргументов вложенных функций, поэтому единственным решением является запись в память.

Мы могли бы писать по любому адресу памяти, но поскольку локальные переменные и аргументы вызовов и возвращений функций вписываются в красивый шаблон стека, который предотвращает фрагментацию памяти, это лучший способ справиться с этим. Сравните это с безумием написания распределителя кучи.

Затем мы позволяем компиляторам оптимизировать распределение регистров для нас, так как это завершает NP и является одной из самых сложных частей написания компилятора. Эта проблема называется распределением регистров, и она изоморфна раскраске графов.

Когда распределитель компилятора вынужден хранить вещи в памяти вместо просто регистров, это называется разливом.

Это сводится к одной инструкции процессора или это более сложный?

Все, что мы знаем наверняка, это то, что Intel документирует push и pop инструкция, поэтому они являются одной инструкцией в этом смысле.

Внутренне его можно расширить до нескольких микрокодов, один для изменения esp и один, чтобы сделать IO памяти, и принять несколько циклов.

Но также возможно, что один push быстрее, чем эквивалентная комбинация других инструкций, поскольку она более конкретна.

В основном это документировано:

Зарегистрированные и выталкивающие регистры находятся за кадром, эквивалентным этому:

push reg   <= same as =>      sub  $8,%rsp        # subtract 8 from rsp
                              mov  reg,(%rsp)     # store, using rsp as the address

pop  reg    <= same as=>      mov  (%rsp),reg     # load, using rsp as the address
                              add  $8,%rsp        # add 8 to the rsp

Обратите внимание, что это x86-64 At&t синтаксис.

Используется как пара, это позволяет сохранить регистр в стеке и восстановить его позже. Есть и другие варианты использования.

Почти все процессоры используют стек. Стек программ выполнен в технике LIFO с аппаратным управлением.

Стек - это объем памяти программ (ОЗУ), который обычно выделяется в верхней части кучи памяти ЦП и увеличивается (по инструкции PUSH указатель стека уменьшается) в противоположном направлении. Стандартный термин для вставки в стек - PUSH, а для удаления из стека - POP.

Управление стеком осуществляется с помощью предназначенного для стека регистра ЦП, также называемого указателем стека, поэтому, когда ЦП выполняет POP или PUSH, указатель стека будет загружать / сохранять регистр или константу в памяти стека, а указатель стека будет автоматически уменьшаться или увеличиваться в зависимости от количества нажатых слов или вставлен в (из) стека.

С помощью инструкций ассемблера мы можем хранить в стеке:

  1. Регистры процессора, а также константы.
  2. Обратные адреса для функций или процедур
  3. Переменные функции / процедуры в / из
  4. Функции / процедуры локальных переменных.
Другие вопросы по тегам