2016-06-21 6 views
5

У меня есть следующие сомнения:Зачем нам нужно распределение стека, когда у нас есть красная зона?

Как известно System V x86-64 ABI дает нам информацию о фиксированной области (128 байт) в стеке, так называемой redzone. Итак, в результате нам не нужно использовать, например, sub rsp, 12. Просто сделайте mov [rsp-12], X и все.

Но я не могу понять это. Почему это имеет значение? Нужно ли sub rsp, 12 без красной зоны? В конце концов, размер стека ограничен в начале, так почему важно sub rsp, 12? Я знаю, что это позволяет нам следить за вершиной стека, но давайте проигнорируем его в тот момент.

Я знаю, что некоторые инструкции используют значение rsp (например, ret), но в этот момент все равно.

Суть проблемы заключается в том: У нас нет RedZone и мы сделали:

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 
+0

Второй фрагмент кода, который вы здесь показали, неверен. Если вы уменьшаете указатель стека, вы должны * восстановить его перед возвратом из функции. Таким образом, вам нужно добавить 'add rsp, 1024' перед' ret'. –

+0

Какой ABI это? Я предполагаю, что Linux один, но есть другие, например. один для Windows 64, Mac OS X 64 бит и т. д. –

+1

@rudy Насколько я понимаю, существует только два x86-64 ABI: System V AMD64 ABI (используется Linux, Solaris, OS X и другими POSIX совместимые операционные системы) и реализация Microsoft, используемая в Windows. Вопрос, похоже, о первом. –

ответ

9

«Красная зона» не является обязательной. В ваших терминах это можно считать «бессмысленным». Все, что вы могли бы сделать, используя красную зону, вы также можете сделать традиционный способ, которым вы это сделали, ориентируясь на 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 (в прологе и эпилоге), а также освобождение up rbp для использования в качестве другого универсального регистра!

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

Обратите внимание, однако, что ABI гарантирует, что асинхронные вещи, такие как сигналы и обработчики прерываний, не изменят красную зону. Звонки на другие функции могут иметь значение clobber в красной зоне, поэтому это не особенно полезно, кроме функций листа (которые те функции, которые не вызывают никаких других функций, как если бы они находились в «листе» дерева функций) ,


Окончательный момент: Windows x64 ABIdeviates slightly from the AMD64 ABI used on other operating systems. В частности, он не имеет понятия о «красной зоне». Площадь за пределами rsp считается изменчивой и может быть перезаписана в любое время. Вместо этого он требует, чтобы вызывающий абонент распределил home address space в стеке, который затем доступен для использования вызываемого абонента в случае, если ему необходимо пролить любой из параметров, прошедших регистрацию.

+0

Хорошо, теперь понятно. Итак, я понимаю, что обработчик сигнала/прерывания нашего процесса просто принимает (на самом деле ОС его дает) 'rsp' и использует его. И действительно, это может сделать проблему, когда у нас есть что-то ниже 'rsp'. Очевидно, что это ситуация без красной зоны. Да? – Gilgamesz

+1

Сигнал или обработчик прерывания * не может * использовать красную зону. Это гарантируется ABI. –

3

У вас есть смещения неправильного пути в вашем примере, поэтому это не имеет смысла. Код не должен иметь доступ к региону ниже указатель стека - он не определен. Красная зона защищает первые 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).

+1

'-fomit-frame-pointer' является по умолчанию для gcc-таргетинга Linux на' -O1' и выше и уже много лет. Обычно вы видите только смещения от 'rbp' для locals на выходе' -O0', что не очень интересно смотреть. Интересный факт: размер красной зоны был выбран, потому что '-128' является максимальным однобайтовым смещением из' rsp'. –

+0

Я не знал, что стал дефолтом. Я старше, чем выгляжу :) – Amoss

+0

@Peter, почему бы не 255 байтов, поскольку беззнаковый байт может принимать максимальное значение 255? –