MSDN附上此图片,以描述如何在x64 fastcall调用约定中使用堆栈:
我的问题是:为什么帧指针设置在箭头显示的地方?毕竟,我们最初希望after call:
push rbp
mov rbp, rsp
; allocate stack memory for local vars and for register and stack parameter area
; function's work
mov rsp, rbp
pop rbp
ret
我们使用以下命令访问B函数的参数区:
mov [rbp + 10h], rcx
mov [rbp + 18h], rdx
mov [rbp + 20h], r8
mov [rbp + 28h], r9
本地变量:
mov [rbp - 8h], 1
mov [rbp - 10h],2
; etc
并通过以下方式访问参数区,以实现未来功能:
; rcx rdx r8 r8 for 1-4
mov [rsp + 20h], 1 ; fifth arg, first stack arg
etc
是这样吗还是我搞砸了?
1条答案
按热度按时间fiei3ece1#
一个关于Godbolt的MSVC19.14 -O2的例子似乎证实了MSVC确实把帧指针放在那里,而不是相对于返回地址的固定位置,因此它必须依赖于栈展开元数据,而不可能回退到传统的帧指针链表。
通常(没有命令行选项)MSVC不使用RBP作为帧指针,甚至在调试构建中也不使用,至少对于简单的函数是这样。只有在使用
alloca
的函数中,或者可能是其他原因。x86-64 System V ABI(除Windows之外的所有系统上都使用)确实会将RBP放在您期望的位置,在返回地址正下方的传统位置,如果您将其用作传统的帧指针的话。(gcc/clang只会在没有优化的情况下,或者在针对代码大小进行优化的情况下,或者在分配或过度对齐堆栈时才这样做。
将RBP放置在您可能想要访问的空间的中间,可以增加
[rbp + disp8]
可以达到的空间量,[rbp + disp8]
在寻址模式中使用符号扩展的8位位移。x86-64 System V有一个128字节的红色区域(在RSP下面),为此选择了128字节;访问RSP及其以上的128字节本地和arg空间,以及RSP以下的另外128字节。访问更远离寄存器的空间需要[reg+disp32]
,即4字节偏移量,因此访问堆栈空间的每条指令需要额外3个字节。(It很少有很多字节的栈参数,特别是在Windows x64中,它通过引用而不是栈上的值传递大型结构体。因此,每个参数正好占用一个8字节的栈槽,这使得变量函数变得简单。)