【发布时间】:2016-11-14 02:00:33
【问题描述】:
参考以下代码
#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 个字节,并将其他内容存储在堆栈帧的基础上。..
【问题讨论】:
-
是的,你没看错。它用于对齐,
lea是&运算符,但它与另一个方向的返回地址无关。它是-4,因为sizeof(int)是4,而locals 低于rbp(也因为编译器决定在它分配的16 个字节的最后4 个字节中分配它)。请注意,在 64 位模式下,返回地址实际上是 8 个字节。 -
@Jester 我很难弄清楚 lea 指令到底在做什么,我不想猜测,因为我觉得以后会适得其反。你能解释一下参数是如何传递的以及这个函数调用中堆栈有什么吗?
-
参数传入
rdi,这就是为什么使用leaq -0x4(%rbp), %rdi。从rbp向上推送rbp,然后返回地址到main。从rbp向下,您有局部变量a,后跟12 个未使用的字节。func的返回地址会放在那个下面。 -
@Jester 如果这与对齐有关,那么为什么要保留 16 个字节? 8个字节不行吗?
-
ABI 需要 16 字节对齐(以使 SSE 对齐指令更容易)。
标签: c++ assembly x86-64 disassembly