x64 MASM中获取C++函数第5、6个参数及栈帧相关疑问
x64 MASM 栈参数布局与寄存器解析(基于你的示例)
先修正你写的汇编代码——你的代码错误地读取了影子空间和多余的栈数据,正确实现应该是这样:
.code AddAsm proc mov rax, rcx ; 取第1个参数a=1 add rax, rdx ; 加第2个参数b=2 → 3 add rax, r8 ; 加第3个参数c=3 → 6 add rax, r9 ; 加第4个参数d=4 → 10 add rax, qword ptr [rsp+40] ; 加第5个参数e=5 → 15 add rax, qword ptr [rsp+48] ; 加第6个参数f=6 → 21 ret AddAsm endp end
下面结合你的示例拆解核心问题:
1. Windows x64调用约定的栈布局(函数刚进入时)
当main调用AddAsm(1,2,3,4,5,6)时,调用者会先完成以下栈操作:
- 预留32字节的影子空间(用于备份前4个寄存器参数,调用者必须分配,不管函数是否使用)
- 从右往左压入剩余参数:先压
f=6,再压e=5 - 执行
call AddAsm,此时返回地址(8字节)被压入栈
进入AddAsm后,rsp指向返回地址,栈的具体布局如下(偏移以rsp为基准):
| 偏移 | 内容 | 值 |
|---|---|---|
| 0 | 返回地址(8字节) | —— |
| +8 | 影子空间第1位(rcx备份) | 1 |
| +16 | 影子空间第2位(rdx备份) | 2 |
| +24 | 影子空间第3位(r8备份) | 3 |
| +32 | 影子空间第4位(r9备份) | 4 |
| +40 | 第5个参数e(8字节) | 5 |
| +48 | 第6个参数f(8字节) | 6 |
你之前的代码错误地读取了影子空间的rsp+32(对应d的备份,已经在r9里),还多取了rsp+56的垃圾数据,这会导致结果错误。
2. rsp与rbp的含义
- rsp:栈指针:始终指向栈顶(x64栈向下增长,push会让rsp减小,pop会让rsp增大)。因为rsp会随栈操作动态变化,直接用它访问参数时要注意偏移的准确性。
- rbp:基指针:作为栈帧的固定基准,方便访问局部变量和参数。如果要用rbp,需要手动保存旧rbp并设置新的栈帧:
.code AddAsm proc push rbp ; 把旧rbp压栈保存 mov rbp, rsp ; 让rbp指向当前栈顶(旧rbp的位置) ; 此时用rbp访问参数: mov rax, rcx add rax, rdx add rax, r8 add rax, r9 add rax, qword ptr [rbp+48] ; e=5(rbp+8是返回地址,+16到+40是影子空间) add rax, qword ptr [rbp+56] ; f=6 pop rbp ; 恢复旧rbp ret AddAsm endp end
3. 参数偏移的规律
- 前4个参数固定用
rcx/rdx/r8/r9传递 - 第5个及以后的参数,在函数刚进入时,从
rsp+40开始,每个参数占8字节(x64栈强制8字节对齐,即使是int类型也会被零扩展为8字节存储) - 如果用rbp作为基准,第5个参数的偏移是
rbp+48,后续每个参数偏移+8
内容的提问来源于stack exchange,提问作者Jacob Krieg
相关产品推荐
相关产品推荐

