我编写了一个基本的C程序,它定义了一个整数变量x,将其设置为零并返回该变量的值:
#include <stdio.h>
int main(int argc, char **argv) {
int x;
x = 0;
return x;
}
Run Code Online (Sandbox Code Playgroud)
当我使用objdump(使用gcc在Linux X86-64上编译)转储目标代码时:
0x0000000000400474 <main+0>: push %rbp
0x0000000000400475 <main+1>: mov %rsp,%rbp
0x0000000000400478 <main+4>: mov %edi,-0x14(%rbp)
0x000000000040047b <main+7>: mov %rsi,-0x20(%rbp)
0x000000000040047f <main+11>: movl $0x0,-0x4(%rbp)
0x0000000000400486 <main+18>: mov -0x4(%rbp),%eax
0x0000000000400489 <main+21>: leaveq
0x000000000040048a <main+22>: retq
Run Code Online (Sandbox Code Playgroud)
我可以看到函数序言,但在我们在地址处将x设置为0之前,0x000000000040047f有两条指令将%edi和%rsi移动到堆栈上.这些是为了什么?
另外,与将x设置为0的情况不同,GAS语法中显示的mov指令没有后缀.
如果未指定后缀,并且指令没有存储器操作数,则GAS根据目标寄存器操作数的大小推断操作数大小.
在这种情况下,是-0x14(%rsbp)和-0x20(%rbp)两个内存操作数和它们的大小是什么?由于%edi是32位寄存器,因此移动到32位,-0x14(%rsbp)而由于%rsi是64位寄存器,64位移动到%rsi,-0x20(%rbp)?
在这个简单的例子中,为什么不直接问你的编译器?对于GCC,clang和ICC,有-fverbose-asm选择权.
main:
pushq %rbp #
movq %rsp, %rbp #,
movl %edi, -20(%rbp) # argc, argc
movq %rsi, -32(%rbp) # argv, argv
movl $0, -4(%rbp) #, x
movl -4(%rbp), %eax # x, D.2607
popq %rbp #
ret
Run Code Online (Sandbox Code Playgroud)
所以,是的,它们通过使用"旧"帧指针方法保存argv并argv堆叠到堆栈上,因为新架构允许直接从堆栈指针中减去/添加,因此省略了帧指针(-fomit-frame-pointer).