Зачем нам нужно выделение стека, когда у нас красная зона?
У меня есть следующие сомнения:
Как мы знаем, System V x86-64 ABI дает нам область фиксированного размера (128 байт) в кадре стека, так называемую красную зону. Таким образом, в результате нам не нужно использовать, например, sub rsp, 12
, Просто сделай mov [rsp-12], X
и это все.
Но я не могу понять идею этого. Почему это имеет значение? Нужно ли sub rsp, 12
без красной зоны? В конце концов, размер стека ограничен в начале, так почему sub rsp, 12
это важно? Я знаю, что это позволяет нам следовать за вершиной стека, но давайте проигнорируем это в тот момент.
Я знаю, что используют некоторые инструкции rsp
значение (как ret
) но не волнует это в тот момент.
Суть проблемы в том, что у нас нет красной зоны, и мы сделали:
function:
mov [rsp-16], rcx
mov [rsp-32], rcx
mov [rsp-128], rcx
mov [rsp-1024], rcx
ret
Это разница с?
function:
sub rsp, 1024
mov [rsp-16], rcx
mov [rsp-32], rcx
mov [rsp-128], rcx
mov [rsp-1024], rcx
add rsp, 1024
ret
2 ответа
"Красная зона" не является строго необходимой. С вашей точки зрения, это можно считать "бессмысленным". Все, что вы могли бы сделать, используя красную зону, вы также можете сделать это традиционным способом, ориентируясь на IA-32 ABI.
Вот что говорит AMD64 ABI о "красной зоне":
128-байтовая область за пределами места, на которое указывает
%rsp
считается зарезервированным и не должен изменяться обработчиками сигналов или прерываний. Поэтому функции могут использовать эту область для временных данных, которые не нужны при вызовах функций. В частности, листовые функции могут использовать эту область для всего кадра стека, вместо того, чтобы корректировать указатель стека в прологе и эпилоге. Эта область известна как красная зона.
Настоящая цель красной зоны - оптимизация. Его существование позволяет коду предполагать, что 128 байтов ниже rsp
не будет асинхронно засоряться сигналами или обработчиками прерываний, что позволяет использовать его в качестве рабочего пространства. Это делает ненужным явное создание пустого пространства в стеке путем перемещения указателя стека в rsp
, Это оптимизация, потому что инструкции по уменьшению и восстановлению rsp
Теперь можно отказаться, экономя время и пространство.
Так что да, хотя вы могли бы сделать это с AMD64 (и нужно было бы сделать это с IA-32):
function:
push rbp ; standard "prologue" to save the
mov rbp, rsp ; original value of rsp
sub rsp, 32 ; reserve scratch area on stack
mov QWORD PTR [rsp], rcx ; copy rcx into our scratch area
mov QWORD PTR [rsp+8], rdx ; copy rdx into our scratch area
; ...do something that clobbers rcx and rdx...
mov rcx, [rsp] ; retrieve original value of rcx from our scratch area
mov rdx, [rsp+8] ; retrieve original value of rdx from our scratch area
add rsp, 32 ; give back the stack space we used as scratch area
pop rbp ; standard "epilogue" to restore rsp
ret
нам не нужно делать это в тех случаях, когда нам нужна только 128-байтовая область царапин (или меньше), потому что тогда мы можем использовать красную зону в качестве нашей области царапин.
Кроме того, поскольку нам больше не нужно уменьшать указатель стека, мы можем использовать rsp
в качестве базового указателя (вместо rbp
), что делает ненужным сохранение и восстановление rbp
(в прологе и эпилоге), а также освобождая rbp
для использования в качестве другого общего регистра!
(Технически, включение пропуска указателя кадра (-fomit-frame-pointer
, включен по умолчанию с -O1
поскольку ABI позволяет это), это также позволило бы компилятору исключить разделы пролога и эпилога с теми же преимуществами. Однако при отсутствии красной зоны необходимость настройки указателя стека на резервное пространство не изменится.)
Обратите внимание, однако, что ABI только гарантирует, что асинхронные вещи, такие как сигналы и обработчики прерываний, не изменяют красную зону. Вызовы к другим функциям могут перекрывать значения в красной зоне, поэтому это не особенно полезно, за исключением конечных функций (те функции, которые не вызывают никаких других функций, как если бы они находились на "листе" дерева вызовов функций),
И последнее замечание: Windows x64 ABI немного отличается от AMD64 ABI, используемого в других операционных системах. В частности, он не имеет понятия "красная зона". Площадь за пределами rsp
считается изменчивым и подлежит перезаписи в любое время. Вместо этого он требует, чтобы вызывающий объект выделил домашнее адресное пространство в стеке, которое затем будет доступно для использования вызывающим абонентом в случае, если ему потребуется пролить любой из переданных регистром параметров.
У вас неправильные смещения в вашем примере, поэтому это не имеет смысла. Код не должен обращаться к области ниже указателя стека - он не определен. Красная зона предназначена для защиты первых 128 байтов ниже указателя стека. Ваш второй пример должен читать:
function:
sub rsp, 1024
mov [rsp+16], rcx
mov [rsp+32], rcx
mov [rsp+128], rcx
mov [rsp+1016], rcx
add rsp, 1024
ret
Если объем пустого пространства, в котором нуждается функция, составляет до 128 байт, тогда она может использовать адреса под указателем стека без необходимости настройки стека: это оптимизация. Для сравнения:
function: // Not using red-zone.
sub rsp, 128
mov [rsp+120], rcx
add rsp, 128
ret
С тем же кодом, используя красную зону:
function: // Using the red-zone, no adjustment of stack
mov [rsp-8], rcx
ret
Путаница в отношении смещений от указателя стека обычно возникает из-за того, что компиляторы генерируют отрицательные смещения из кадра (RBP), а не положительные смещения из стека (RSP).