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
是这样还是我搞砸了?
最佳答案
安example on Godbolt MSVC19.14 -O2 似乎确认 MSVC 确实将帧指针放在那里,而不是相对于返回地址的固定位置。因此它必须依赖堆栈展开元数据,而不可能回退到传统的帧指针链表跟踪。
通常(没有命令行选项)MSVC 不使用 RBP 作为帧指针,甚至在调试版本中也不使用,至少对于简单的函数而言。仅在使用 alloca
的函数中,或者可能是其他原因。
x86-64 System V ABI(用于除 Windows 之外的所有系统)确实将 RBP 放在您期望的位置,即返回地址正下方的传统位置(如果您将其用作传统帧指针)。 (gcc/clang 仅在没有优化的情况下执行此操作,或者针对代码大小进行优化,或者在分配或过度对齐堆栈时执行此操作。
将 RBP 放置在更靠近您可能想要访问的空间中间的位置,可以增加使用 [rbp + disp8]
可以到达的空间量,它在寻址模式。 x86-64 System V 有一个 128 字节的红色区域(低于 RSP),因此选择了 128 字节;访问 RSP 及以上 128 字节的本地和 arg 空间,以及以下 128 字节。访问距离寄存器较远的位置需要 [reg+disp32]
,这是一个 4 字节偏移量,因此每条访问堆栈空间的指令需要 3 个额外字节。
(很少有很多字节的堆栈参数,特别是在 Windows x64 中,它通过引用传递大型结构,而不是通过堆栈上的值。因此每个参数恰好占用一个 8 字节堆栈槽,从而使可变参数函数变得简单。)
关于assembly - 设置 RBP 有不同的方法吗?为什么 Windows x64 不将 RBP 指向返回地址正下方的已保存 RBP?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75722486/