gpt4 book ai didi

c++ - 为什么堆栈帧上保留的空间比 x86 中需要的空间多

转载 作者:行者123 更新时间:2023-11-30 03:36:39 25 4
gpt4 key购买 nike

引用以下代码

#include <iostream>
using namespace std;

void do_something(int* ptr) {
cout << "Got address " << reinterpret_cast<void*>(ptr) << endl;
}

void func() {
int a;
do_something(&a);
}

int main() {
func();
}

当我反汇编 func 函数时 x86(我不确定它是 x86 还是 x86_64)代码是

->  0x100001140 <+0>:  pushq  %rbp
0x100001141 <+1>: movq %rsp, %rbp
0x100001144 <+4>: subq $0x10, %rsp
0x100001148 <+8>: leaq -0x4(%rbp), %rdi
0x10000114c <+12>: callq 0x100000f90 ; do_something(int*)
0x100001151 <+17>: addq $0x10, %rsp
0x100001155 <+21>: popq %rbp
0x100001156 <+22>: retq
0x100001157 <+23>: nopw (%rax,%rax)

据我所知,第一个 push 语句是将基指针推送到堆栈上的前一个函数调用,然后将堆栈指针值复制到基指针。但是为什么要为堆栈保留 16 个字节呢?

这与对齐有什么关系吗?变量 a 只需要 4 个字节..

此外,lea 指令在此函数调用中究竟做了什么?它只是获取整数相对于基指针的地址吗?在这种情况下,它似乎距离基址有 4 个字节(假设返回地址有 4 个字节长,并且是堆栈中的第一个东西)

其他架构似乎保留了超过 16 个字节,并在堆栈帧的底部存储了其他内容。

最佳答案

这是x64代码,注意rsp寄存器的用法。 x86 代码使用 esp 寄存器。 x64 ABI 最重要的实现细节是堆栈必须始终对齐到 16。正确运行 64 位代码实际上不是必需的,但对齐保证可确保编译器可以安全地发出 SSE 指令。它们的操作数需要 16 字节对齐才能更快。此代码段中实际上没有使用任何内容,但它们可能在 do_something 中。

在您的函数进入时,调用者的 CALL 指令已将 8 个字节压入堆栈以存储返回地址。第一个 PUSH 指令再次将堆栈对齐到 16,不需要额外的更正。

然后它创建堆栈帧来存储 a 变量。虽然只需要 4 个字节,但仅将 rsp 调整 4 不足以提供必要的对齐。所以它选择下一个合适的值 16。额外的 12 个字节根本没有使用。

LEA 指令是一个非常方便的指令,它实现了&a。 LEA = 加载有效地址 = “获取地址”。这里不是特别复杂的计算,当你使用像 &array[ix] 这样的东西时,它会变得更加复杂。如果数组元素大小为 1、2 或 4 个字节长,则仍然可以由单个 LEA 完成,这很常见。

-4 是 a 变量从堆栈帧开始的偏移量。需要 4 个字节来存储 int,您的编译器实现了 LP64 data model .请记住,堆栈向下增长,因此它不是 0。

然后它只是进行函数调用,rdi 寄存器用于传递 x64 ABI 中的第一个参数。然后它通过重新调整 rsp 并恢复 rbp 再次破坏堆栈帧。

请记住,您正在查看未优化的代码。通常在优化器完成后这些都不会留下,像这样的小函数几乎总是内联的。因此,这并没有教给您太多关于实际运行的代码的实用知识。查看 -O2 代码。

关于c++ - 为什么堆栈帧上保留的空间比 x86 中需要的空间多,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40580914/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com