我正在查看 'objdump -S' 的程序集输出并发现一些奇怪的东西。这是在 Windows 10 上使用 gcc 9.3.0 的 cygwin/x86_64 v. 3.1.5 上。
这里是特定函数的汇编输出(该函数没有用,只是说明问题):
u_int64_t returnit(u_int64_t x) {
1004010b9: 55 push rbp
1004010ba: 48 89 e5 mov rbp,rsp
1004010bd: 48 83 ec 10 sub rsp,0x10
1004010c1: 48 89 4d 10 mov QWORD PTR [rbp+0x10],rcx
u_int64_t a = 1;
1004010c5: 48 c7 45 f8 01 00 00 mov QWORD PTR [rbp-0x8],0x1
1004010cc: 00
return a + x;
1004010cd: 48 8b 55 f8 mov rdx,QWORD PTR [rbp-0x8]
1004010d1: 48 8b 45 10 mov rax,QWORD PTR [rbp+0x10]
1004010d5: 48 01 d0 add rax,rdx
}
1004010d8: 48 83 c4 10 add rsp,0x10
1004010dc: 5d pop rbp
1004010dd: c3 ret
几乎一切看起来都很正常:设置堆栈帧,为局部变量留出额外空间,并将传递的参数(“x”,在寄存器 rcx 中)复制到堆栈上的某个位置。
这是看起来很奇怪的部分:
mov QWORD PTR [rbp+0x10],rcx
它将 rcx 的内容复制到当前堆栈帧之外。局部变量应该存储在当前堆栈帧中。
我在较旧的 cygwin 安装(32 位,v. 2.9.0 和 gcc 6.4.0)上尝试了这个,它的行为方式相同。
我还在其他平台上尝试过这个 - 一个带有内核 4.4.0 和 gcc 5.3.1 的较旧的 ubuntu linux liveboot,以及一个带有 clang 8.0.1 的 FreeBSD 12.1 机器,都是 64 位的 - 他们做到了人们所期望的,复制在本地堆栈帧内的寄存器中传递的参数的值。例如,这是 FreeBSD 上的相关行(它使用 rdi 而不是 rcx):
2012e8: 89 7d fc mov DWORD PTR [rbp-0x4],edi
在cygwin上这样做有什么特别的原因吗?
最佳答案
此行为符合 Windows x64 ABI。
看x64 stack usage从 Microsoft 的页面中,我们可以看到 ABI 指定在堆栈上保留四个寄存器参数的空间,即使使用的参数较少。这些是家庭地址,充当实际参数寄存器的影子。
该区域可用于保存否则会被覆盖的参数,以帮助调试等。鉴于为极其简单的操作完成的工作量,我假设这是未优化/调试的代码。代码的优化编译可能会跳过这些冗余存储和加载,并且可能不会触及除了 ret
之外的内存。
Windows 使用的 Microsoft x64 调用约定与 Linux、OS X 等在 x86-64 上使用的 System V AMD64 ABI 中看到的不同。
This example显示了 MSVC 中优化的效果(不同的编译器,但仍以 Windows 为目标)。无需将值实际存储在堆栈中,只需一条指令即可完成计算。
关于assembly - 使用堆栈框架外的堆栈空间在 cygwin 上的 gcc 输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62665668/