GCC访问栈顶之上的内存



我有一个C函数,它可以进行一些SSE计算。当我用GCC编译它时,我得到了下一个代码

/* Start of function */
mov    (%rdi),%rax
movslq %ecx,%rcx
...
mov    0x8(%rdi),%rax
pxor   %xmm12,%xmm3
movaps %xmm0,-0x28(%rsp)
movaps %xmm6,%xmm1
...
movaps 0x50(%rax,%rcx,1),%xmm2
movaps 0x60(%rax,%rcx,1),%xmm15
pxor   %xmm2,%xmm0
pxor   %xmm2,%xmm6
movaps -0x28(%rsp),%xmm2
pxor   %xmm15,%xmm5
pxor   %xmm15,%xmm2
movaps 0x70(%rax,%rcx,1),%xmm15
movaps (%rax,%rcx,1),%xmm11
mov    0x10(%rdi),%rax
movaps %xmm15,-0x18(%rsp)
pxor   %xmm11,%xmm4
pxor   %xmm12,%xmm11
pxor   %xmm15,%xmm12

看看movaps指令——它在栈顶上访问内存:

movaps %xmm15,-0x18(%rsp)

这不是访问未定义的内存吗?为什么GCC生成了如此错误的代码?

在汇编级没有"未定义内存"这回事。gcc可以自由地发出代码,以任何它认为合适的方式访问堆栈,只要行为符合预期。

我对为什么会发生这种情况的猜测是,这是一个叶函数,调整堆栈指针是徒劳的。您可以尝试通过检查程序集是否有任何call指令来验证这一点。(您也可以检查C源代码,但内联可能会降低其可靠性。)

某些平台的ABI明确允许这种欺骗行为,包括x86-64。来自AMD64 ABI文档:

%rsp指向的位置之外的128字节区域被视为保留,不得通过信号或中断进行修改处理程序。因此,函数可能会将此区域用作临时数据这在函数调用中是不需要的。特别是叶子函数可以将该区域用于其整个堆栈帧,而不是在序言和尾声中调整堆栈指针。该区域被称为红色区域。

这篇博客文章可能会引起对这个主题的有趣阅读。

最新更新