2016-09-17 5 views
2

Enter в функции, стандартный прологкак ссылаться на локальные переменные в стеке правильно

push rbp 
mov rbp, rsp 
sub rsp, 128 ; large space for storing doubles, for example 

Как ссылаться на локальные переменные в настоящее время, с помощью RSP + положительное смещение, или через РСП + отрицательное смещение?

Чтение https://en.wikibooks.org/wiki/X86_Disassembly/Functions_and_Stack_Frames, действительно вполне понятно. Он пишет

... значение esp не может быть надежно использовано для определения (с использованием соответствующего смещения) местоположения памяти определенной локальной переменной. Чтобы решить эту проблему, многие компиляторы получают доступ к локальным переменным, используя отрицательные смещения из регистров ebp.

Почему не надежный? До этого вопроса я не был доступ к локальным переменным через RSP, как это:

mov rax, [rsp+.length] ; get length of array 
mov [rsp+8], rax ; store sum at the stack 

все идет довольно хорошо, используя RSP для стека ссылок.

+1

Указатель стека * может * часто использоваться для определения адреса переменных в стеке.Однако, если функция использует массивы переменной длины или эквивалент 'alloca()', использование смещений из указателя стека может оказаться невозможным. – EOF

+0

@EOF, поэтому следует использовать адресацию по отношению к rsp во всех случаях, кроме двух? –

ответ

3

Посмотрите на выход gcc. По умолчанию он равен при оптимизации, только создавая фрейм стека, когда функции используют массивы переменной длины или ему нужно выровнять стек более чем на 16B.

Эта страница wiki в основном неправильная. Там не страшно странно, что делает его «ненадежным». Единственный раз, когда вы не можете этого сделать, - это когда вам нужно изменить RSP на величину, которая не является постоянной времени сборки.


Однако, если вы сделать сделать кадр стека с push rbp/mov rbp, rsp, вы должны использовать РБП-относительный режимы адресации. Это более эффективно, потому что [rsp + 8] берет дополнительный байт для кодирования (против [rbp - 8]). Режимы адресации с RSP в качестве базового регистра всегда нуждаются в байте SIB, даже если нет индексного регистра.

Точка использования RSP-относительных режимов адресации, что вы можете избежать потерь инструкции сделать кадр стека, так ОПБ это просто еще один вызов сохраняемых регистров (например, RBX), которые вы можете сохранить/восстановить и использовать для любого вы хотите.


Другое большое преимущество ОПБ-относительной адресации является то, что смещение от RBP к данной переменной остается постоянной в течение всей функции. В отличие от компиляторов, мы, мелкие люди, легко путаем толками и попсами, которые меняют RSP внутри функции. Конечно, 64-битный код вряд ли когда-либо изменяет RSP внутри функции между прологом и эпилогом, потому что оба ABI передают аргументы в регистры. Сохранение/восстановление некоторых регистров, сохраняющих код (например, RBX или R12-R15) в прологе/эпилоге, часто лучше, чем нажатие/поп внутри функции (и, безусловно, лучше, чем внутри цикла). Когда вам нужно проливать/перезагружать, mov для случайного доступа обычно лучше всего.

В 32-битном коде создание фрейма стека в рукописном коде часто имеет смысл, особенно. для ремонтопригодности. В 64-битном коде это обычно не проблема. Хотя сохранение/восстановление дополнительного регистра с дополнительной парой push/pop изменяет структуру стека, что имеет значение, если в стеке были переданы какие-либо аргументы (например, большая структура по значению, но напишите свою функцию, чтобы взять const-указатель arg вместо!).

+0

Очень полезно, особенно подсказка об эффективности. Таким образом, в случае компиляции с оптимизацией (упущение указателя кадра) можно получить более крупный код, потому что для кодирования rsp-адресации требуется больше байтов? –

+1

@BulatM: иногда сохранение из-за отсутствия 3 или 4 инструкций для каждой функции будет сбалансировано. Это 1 + 3 байта пролога и 1 + 0 или 3 байта эпилога. (опционально 'mov rsp, rbp', затем' pop rbp'. Или некоторые компиляторы используют LEAVE, который составляет 1 байт, который является всего 3 uops IIRC, поэтому это хороший выбор, если RSP не указывает на сохраненное значение RBP в конец функции.) –

+2

Когда вы делаете сборку вручную, указатель стека может быть сложным, чтобы отследить, поскольку локальные переменные могут не иметь фиксированного смещения от указателя стека. – fuz

 Смежные вопросы

  • Нет связанных вопросов^_^