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

Gil*_*esz 6 x86 assembly x86-64 abi red-zone

我有以下疑惑:

正如我们所知,System V x86-64 ABI为我们提供了堆栈帧中固定大小的区域(128字节),即所谓的redzone.因此,结果我们不需要使用,例如,sub rsp, 12.只是做mov [rsp-12], X,这就是全部.

但我无法理解这一点.为什么这有关系?sub rsp, 12没有redzone 是否有必要?毕竟,堆栈大小在开始时是有限的,为什么sub rsp, 12重要?我知道这使得我们可以跟随堆栈的顶部但是让我们忽略它.

我知道一些指令使用了什么rsp价值(比如ret)但在那一刻并不关心它.

问题的症结在于:我们没有红区,我们已经完成了:

function:
    mov [rsp-16], rcx
    mov [rsp-32], rcx
    mov [rsp-128], rcx
    mov [rsp-1024], rcx
    ret
Run Code Online (Sandbox Code Playgroud)

它有区别吗?

function:
    sub rsp, 1024
    mov [rsp-16], rcx
    mov [rsp-32], rcx
    mov [rsp-128], rcx
    mov [rsp-1024], rcx
    add rsp, 1024
    ret
Run Code Online (Sandbox Code Playgroud)

Cod*_*ray 11

"红区"并非绝对必要.用你的话来说,它可以被认为是"毫无意义的".使用红色区域可以做的所有事情,你也可以采用传统的方式,针对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
Run Code Online (Sandbox Code Playgroud)

我们并不需要做在我们只需要128字节的临时区域(或更小)的情况下,因为这时我们可以利用红色区域作为我们的刮开区.

另外,由于我们不再需要递减堆栈指针,我们可以使用它rsp作为基指针(而不是rbp),使得不需要保存和恢复rbp(在序言和结语中),并且还可以释放rbp用作另一个通用 -目的登记!

(从技术上讲,启用框架指针省略(-fomit-frame-pointer默认情况下启用,-O1因为ABI允许它)也可以使编译器可以省略序言和结尾部分,具有相同的好处.但是,如果没有红色区域,需要调整堆栈指针以保留空间不会改变.)

但请注意,ABI仅保证信号和中断处理程序等异步操作不会修改红色区域.调用其他函数可能会破坏红色区域中的值,因此除了叶函数(那些不调用任何其他函数的函数,就好像它们位于函数调用树的"叶子")之外,它不是特别有用. .


最后一点:Windows x64 ABI 与其他操作系统上使用的AMD64 ABI略有不同.特别是,它没有"红区"的概念.超出区域rsp被认为是易变的,并且可能随时被覆盖.相反,它要求调用者在堆栈上分配一个归属地址空间,然后在需要溢出任何寄存器传递的参数时,可以将其用于被调用者.