assembly - 使用堆栈框架外的堆栈空间在 cygwin 上的 gcc 输出

标签 assembly gcc cygwin x86-64 calling-convention

我正在查看 '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 之外的内存。

Microsoftx64 function stack frames

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/

相关文章:

c - 为什么我不能将FS寄存器的PID block 的值赋值给一个unsigned int?

c - 优化禁止插入地址大小覆盖前缀

c++ - std::string 类继承和繁琐的 C++ 重载解析 #2

c# - 从 C# 调用 Cygwin 的 Seg 错误

shell - 如何在powershell中运行cygwin脚本

pointers - dword ptr使用困惑

c - 系统调用包装器 asm C

macos - 在 64 位环境中编译 32 位 OS X 二进制文件

c - 与 Ubuntu 相比,我的 C 代码在 Mac 中运行缓慢

amazon-web-services - Bamboo SCP 插件 : how to find directory