Куди його штовхають?
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