在 64 位机器上创建栈帧
Stack frame creation in 64 bit machine
我只是在学习一些程序的低级分析。在用 gcc 进行 32 位编译时,我发现堆栈帧是按以下顺序创建的:
- 以相反的顺序推送函数参数。
- 保存return地址
- 保存帧指针
- 创建局部变量
所以参数的地址应该是最高的,因为堆栈以相反的顺序增长。但是当我在 64 位编译中尝试同样的操作时,我无法理解它是如何创建的,这与我在 32 位编译中发现的正好相反。这是代码和内存详细信息:
void test(int a, int b, int c, int d)
{
int flag;
char buf[10];
num = 100;
}
int main()
{
test(1, 2, 3, 4);
}
现在为了简单起见,我们只使用参数和 return 地址。
32 位编译:
0xffffd130: 0x00000001 0xffffd1f4 0xffffd1fc 0xf7e3ad1d
0xffffd140: 0xffffd158 0x0804842d 0x00000001 0x00000002
0xffffd150: 0x00000003 0x00000004 0x00000000 0xf7e22933
0xffffd160: 0x00000001 0xffffd1f4 0xffffd1fc 0xf7fdb6b0
0x08048421 <+30>: mov DWORD PTR [esp],0x1
0x08048428 <+37>: call 0x80483f0 <test>
0x0804842d <+42>: leave
这里一切正常。我可以看到较高地址的参数,紧接着是较低地址的 return 地址 0x0804842d
。现在,
64 位编译:
0x7fffffffdf80: 0x00000004 0x00000003 0x00000002 0x00000001
0x7fffffffdf90: 0x00400530 0x00000000 0x00400400 0x00000000
0x7fffffffdfa0: 0xffffdfb0 0x00007fff 0x0040052a 0x00000000
0x7fffffffdfb0: 0x00000000 0x00000000 0xf7a3baf5 0x00007fff
0x0000000000400525 <+24>: call 0x4004f0 <test>
0x000000000040052a <+29>: pop rbp
0x000000000040052b <+30>: ret
这里我可以看到参数在低位地址,return地址在高位地址0x0040052a
。这里有什么问题?堆栈是否以相反的方向增长(低地址到高地址)或堆栈帧的创建与上述顺序不同?请帮助我理解。谢谢。
在 x86-64 上,传递参数的标准方式是使用寄存器,而不是堆栈(除非你有超过 6 个)。参见 http://www.x86-64.org/documentation/abi.pdf
我强烈建议在未先阅读适当的文档(比如我刚刚链接的文档)的情况下不要进行任何类型的实验。
无论如何,如果反汇编 main,您可以很容易地看到参数没有在堆栈上传递:
0x0000000000400509 <+0>: push %rbp
0x000000000040050a <+1>: mov %rsp,%rbp
0x000000000040050d <+4>: mov [=10=]x4,%ecx
0x0000000000400512 <+9>: mov [=10=]x3,%edx
0x0000000000400517 <+14>: mov [=10=]x2,%esi
0x000000000040051c <+19>: mov [=10=]x1,%edi
0x0000000000400521 <+24>: callq 0x4004f0 <test>
0x0000000000400526 <+29>: pop %rbp
0x0000000000400527 <+30>: retq
您还可以看到它们如何在测试中最终进入堆栈:
0x00000000004004f0 <+0>: push %rbp
0x00000000004004f1 <+1>: mov %rsp,%rbp
0x00000000004004f4 <+4>: mov %edi,-0x14(%rbp)
0x00000000004004f7 <+7>: mov %esi,-0x18(%rbp)
0x00000000004004fa <+10>: mov %edx,-0x1c(%rbp)
0x00000000004004fd <+13>: mov %ecx,-0x20(%rbp)
0x0000000000400500 <+16>: movl [=11=]x64,-0x4(%rbp)
0x0000000000400507 <+23>: pop %rbp
0x0000000000400508 <+24>: retq
我只是在学习一些程序的低级分析。在用 gcc 进行 32 位编译时,我发现堆栈帧是按以下顺序创建的:
- 以相反的顺序推送函数参数。
- 保存return地址
- 保存帧指针
- 创建局部变量
所以参数的地址应该是最高的,因为堆栈以相反的顺序增长。但是当我在 64 位编译中尝试同样的操作时,我无法理解它是如何创建的,这与我在 32 位编译中发现的正好相反。这是代码和内存详细信息:
void test(int a, int b, int c, int d)
{
int flag;
char buf[10];
num = 100;
}
int main()
{
test(1, 2, 3, 4);
}
现在为了简单起见,我们只使用参数和 return 地址。
32 位编译:
0xffffd130: 0x00000001 0xffffd1f4 0xffffd1fc 0xf7e3ad1d
0xffffd140: 0xffffd158 0x0804842d 0x00000001 0x00000002
0xffffd150: 0x00000003 0x00000004 0x00000000 0xf7e22933
0xffffd160: 0x00000001 0xffffd1f4 0xffffd1fc 0xf7fdb6b0
0x08048421 <+30>: mov DWORD PTR [esp],0x1
0x08048428 <+37>: call 0x80483f0 <test>
0x0804842d <+42>: leave
这里一切正常。我可以看到较高地址的参数,紧接着是较低地址的 return 地址 0x0804842d
。现在,
64 位编译:
0x7fffffffdf80: 0x00000004 0x00000003 0x00000002 0x00000001
0x7fffffffdf90: 0x00400530 0x00000000 0x00400400 0x00000000
0x7fffffffdfa0: 0xffffdfb0 0x00007fff 0x0040052a 0x00000000
0x7fffffffdfb0: 0x00000000 0x00000000 0xf7a3baf5 0x00007fff
0x0000000000400525 <+24>: call 0x4004f0 <test>
0x000000000040052a <+29>: pop rbp
0x000000000040052b <+30>: ret
这里我可以看到参数在低位地址,return地址在高位地址0x0040052a
。这里有什么问题?堆栈是否以相反的方向增长(低地址到高地址)或堆栈帧的创建与上述顺序不同?请帮助我理解。谢谢。
在 x86-64 上,传递参数的标准方式是使用寄存器,而不是堆栈(除非你有超过 6 个)。参见 http://www.x86-64.org/documentation/abi.pdf
我强烈建议在未先阅读适当的文档(比如我刚刚链接的文档)的情况下不要进行任何类型的实验。
无论如何,如果反汇编 main,您可以很容易地看到参数没有在堆栈上传递:
0x0000000000400509 <+0>: push %rbp
0x000000000040050a <+1>: mov %rsp,%rbp
0x000000000040050d <+4>: mov [=10=]x4,%ecx
0x0000000000400512 <+9>: mov [=10=]x3,%edx
0x0000000000400517 <+14>: mov [=10=]x2,%esi
0x000000000040051c <+19>: mov [=10=]x1,%edi
0x0000000000400521 <+24>: callq 0x4004f0 <test>
0x0000000000400526 <+29>: pop %rbp
0x0000000000400527 <+30>: retq
您还可以看到它们如何在测试中最终进入堆栈:
0x00000000004004f0 <+0>: push %rbp
0x00000000004004f1 <+1>: mov %rsp,%rbp
0x00000000004004f4 <+4>: mov %edi,-0x14(%rbp)
0x00000000004004f7 <+7>: mov %esi,-0x18(%rbp)
0x00000000004004fa <+10>: mov %edx,-0x1c(%rbp)
0x00000000004004fd <+13>: mov %ecx,-0x20(%rbp)
0x0000000000400500 <+16>: movl [=11=]x64,-0x4(%rbp)
0x0000000000400507 <+23>: pop %rbp
0x0000000000400508 <+24>: retq