首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >当我们有一个红色区域时,为什么我们需要堆栈分配?

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

Stack Overflow用户
提问于 2016-06-21 10:20:35
回答 2查看 1.9K关注 0票数 7

我有以下疑问:

正如我们所知道的,系统Vx86-64abi给出了堆栈帧中一个固定大小的区域(128个字节),也就是所谓的redzone。因此,我们不需要使用例如sub rsp, 12。做mov [rsp-12], X就行了,仅此而已。

但我无法理解这一点。为什么这很重要?没有红区的sub rsp, 12有必要吗?毕竟,堆栈大小在开始时是有限的,所以为什么sub rsp, 12是重要的?我知道它使我们有可能跟随堆栈的顶部,但让我们在那一刻忽略它。

我知道有些指令使用的是rsp值(比如ret),但在那一刻并不关心它。

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

代码语言:javascript
复制
function:
    mov [rsp-16], rcx
    mov [rsp-32], rcx
    mov [rsp-128], rcx
    mov [rsp-1024], rcx
    ret

有区别吗?

代码语言:javascript
复制
function:
    sub rsp, 1024
    mov [rsp-16], rcx
    mov [rsp-32], rcx
    mov [rsp-128], rcx
    mov [rsp-1024], rcx
    add rsp, 1024
    ret
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-06-21 11:00:13

“红区”并不是绝对必要的。用你的话来说,这可以被认为是“毫无意义的”。所有你能做的使用红色区域,你也可以做传统的方式,你做它的目标是IA-32 ABI。

以下是AMD64 ABI对“红色区域”的看法:

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

红色区域的真正目的是作为一个优化。它的存在允许代码假设rsp下面的128个字节不会被信号或中断处理程序异步破坏,这样就可以将其用作划痕空间。这使得没有必要通过在rsp中移动堆栈指针来显式地在堆栈上创建划痕空间。这是一个优化,因为现在可以省略减少和恢复rsp的指令,从而节省时间和空间。

所以是的,虽然您可以使用AMD64来完成这个任务(并且需要使用IA-32):

代码语言:javascript
复制
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以外的区域被认为是不稳定的,随时可能被覆盖。相反,它要求调用方在堆栈上分配一个家庭地址空间,然后在调用方需要泄漏任何经过寄存器传递的参数时,调用方可以使用它。

票数 13
EN

Stack Overflow用户

发布于 2016-06-21 11:21:38

在您的示例中有错误的偏移,这就是为什么它没有意义的原因。代码不应该访问堆栈指针下面的区域--它是未定义的。红色区域是用来保护堆栈指针下面的前128个字节的.你的第二个例子应该是:

代码语言:javascript
复制
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个字节,那么它可以使用堆栈指针下面的地址,而不需要调整堆栈:这是优化。比较:

代码语言:javascript
复制
function:        // Not using red-zone.
    sub rsp, 128
    mov [rsp+120], rcx
    add rsp, 128
    ret

使用相同的代码使用红色区域:

代码语言:javascript
复制
function:        // Using the red-zone, no adjustment of stack
    mov [rsp-8], rcx
    ret

有关堆栈指针偏移量的混淆通常是因为编译器从帧(RBP)生成负偏移,而不是从堆栈(RSP)生成正偏移。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/37941779

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档