当我们有一个红区时,为什么我们需要堆栈分配?

时间:2016-06-21 10:20:36

标签: assembly x86 x86-64 abi red-zone

我有以下疑问:

正如我们所知,System V x86-64 ABI为我们提供了堆栈帧中固定大小的区域(128字节),即所谓的redzone。 因此,我们不需要使用sub rsp, 12。只需制作mov [rsp-12], X即可。

但我无法理解这一点。为什么这有关系?没有redzone是否有必要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

2 个答案:

答案 0 :(得分:11)

“红区”并非绝对必要。用你的话来说,它可以被认为是“毫无意义的”。使用红色区域可以做的所有事情,你也可以采用传统的方式来定位IA-32 ABI。

以下是AMD64 ABI关于“红区”的说法:

  

%rsp指向的位置之外的128字节区域被认为是保留的,不应被信号或中断处理程序修改。因此,函数可以将此区域用于函数调用不需要的临时数据。特别是,叶子函数可以将这个区域用于它们的整个堆栈帧,而不是调整序言和尾声中的堆栈指针。这个区域被称为红区。

     

红色区域的真正目的是作为优化。它的存在允许代码假设rsp以下的128个字节不会被信号或中断处理程序异步破坏,这使得它可以用作临时空间。这使得不必通过移动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 deviates slightly from the AMD64 ABI used on other operating systems。特别是,它没有“红区”的概念。 rsp以外的区域被视为易变区,并且可能随时被覆盖。相反,它要求调用者在堆栈上分配home address space,然后在需要溢出任何寄存器传递的参数的情况下,可以将其用于被调用者。

答案 1 :(得分: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)产生正偏移。