Куди його штовхають?
esp - 4
. Точніше:
esp
віднімається від 4
- значення підштовхується до
esp
pop
змінює це.
Система V ABI вказує Linux rsp
вказувати на розумне розташування стека, коли програма запускається: Що таке стан реєстру за замовчуванням при запуску програми (asm, linux)? яким зазвичай слід користуватися.
Як можна натиснути реєстр?
Приклад мінімального 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 вирішила надати їх нам.
Причина, по якій ці комбінації є настільки частими, полягає в тому, що вони полегшують тимчасове збереження та відновлення значень регістрів, щоб вони не перезаписувались.
Щоб зрозуміти проблему, спробуйте скласти трохи коду С вручну.
Основною складністю є рішення, де буде зберігатися кожна змінна.
В ідеалі всі змінні вписувалися б у регістри, що є найшвидшим доступом до пам'яті (в даний час приблизно в 100 разів швидше, ніж оперативної пам'яті).
Але, звичайно, ми можемо мати більше змінних, ніж регістри, спеціально для аргументів вкладених функцій, тому єдиним рішенням є запис в пам’ять.
Ми могли б писати на будь-яку адресу пам’яті, але оскільки локальні змінні та аргументи викликів та повернень функції вписуються в гарний шаблон стека, який запобігає фрагментації пам’яті , це найкращий спосіб вирішити це. Порівняйте це з божевільністю написання розподілювача купи.
Потім ми дозволяємо компіляторам оптимізувати розподіл регістрів для нас, оскільки це NP завершено і є однією з найскладніших частин написання компілятора. Ця проблема називається розподілом регістрів , і вона ізоморфна розфарбуванню графіків .
Коли розподільник компілятора змушений зберігати речі в пам'яті, а не просто регістри, це відоме як розлив .
Це зводиться до однієї інструкції процесора чи вона більш складна?
Все, що ми точно знаємо, це те, що Intel документує a push
та pop
інструкцію, тому вони є однією інструкцією в цьому сенсі.
Внутрішньо він може бути розширений до декількох мікрокодів, один для модифікації, esp
а другий для введення-виведення в пам'ять, і триває кілька циклів.
Але також можливо, що сингл push
швидший, ніж еквівалентна комбінація інших інструкцій, оскільки він є більш конкретним.
Це переважно не (дер) задокументовано:
b
,w
,l
, або ,q
щоб позначити розмір пам'яті маніпулюють. Приклад:pushl %eax
іpopl %eax