#include <stdio.h>
int main ( void )
{
printf("hello\n");
return(0);
}
有趣的相关部分。
0000000000400430 <main>:
400430: 48 83 ec 08 sub $0x8,%rsp
400434: bf d4 05 40 00 mov $0x4005d4,%edi
400439: e8 c2 ff ff ff callq 400400 <puts@plt>
40043e: 31 c0 xor %eax,%eax
400440: 48 83 c4 08 add $0x8,%rsp
400444: c3 retq
400445: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
40044c: 00 00 00
40044f: 90 nop
0000000000400450 <_start>:
400450: 31 ed xor %ebp,%ebp
400452: 49 89 d1 mov %rdx,%r9
400455: 5e pop %rsi
400456: 48 89 e2 mov %rsp,%rdx
400459: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
40045d: 50 push %rax
40045e: 54 push %rsp
40045f: 49 c7 c0 c0 05 40 00 mov $0x4005c0,%r8
400466: 48 c7 c1 50 05 40 00 mov $0x400550,%rcx
40046d: 48 c7 c7 30 04 40 00 mov $0x400430,%rdi
400474: e8 97 ff ff ff callq 400410 <__libc_start_main@plt>
400479: f4 hlt
40047a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
在大多数情况下,main 和 printf 没有什么特别之处,等等这些只是符合调用约定的函数。正如重新提出的 SO 问题所显示的那样,有时编译器会在看到 main() 时添加额外的堆栈或其他调用,否则它不会。但它仍然是一个需要符合调用约定的函数。如本例所示,堆栈指针被放回找到它的位置。
在操作系统(Linux、Windows、MacOS 等)甚至可以考虑运行程序之前,它需要为该程序分配一些空间,并根据处理器的功能以某种方式为该程序标记该内存,并且操作系统等然后你从任何媒体加载程序,并在指定的二进制文件和/或众所周知的入口点启动它。程序的干净退出将导致操作系统释放该内存,.text、.data、.bss 和堆栈是微不足道的/显而易见的,随着它们的内存消失,它们就会消失。可能已分配并与该程序相关联的其他项目、打开的文件、运行时分配的(非堆栈)内存等也可以/应该被释放,这取决于操作系统和/或 C 库的设计如何发生.
在上述情况下,我们看到 bootstrap 调用 main 并且 main 返回然后 hlt 被命中,这是一个应用程序而不是内核代码,因此应该会导致导致操作系统清理的陷阱。显式 exit() 应该与 printf() 或 puts() 或 fopen() 或任何其他最终对操作系统进行一个或多个系统调用的函数没有什么不同。对于这些类型的操作系统(Linux、Windows、MacOS),您所能找到的只有系统调用。内存的释放发生在程序外部,因为程序无法控制它,这将是鸡和蛋的问题,程序释放用于释放 mmu 表的 mmu 表...
为main而不是整个程序编译和反汇编对象
0000000000000000 <main>:
0: 48 83 ec 08 sub $0x8,%rsp
4: bf 00 00 00 00 mov $0x0,%edi
9: e8 00 00 00 00 callq e <main+0xe>
e: 31 c0 xor %eax,%eax
10: 48 83 c4 08 add $0x8,%rsp
14: c3 retq
毫不奇怪,这里和以前一样,我们需要看到的就是了解堆栈在返回之前被清理了。并且那个 main 并不特别:
#include <stdio.h>
int notmain ( void )
{
printf("hello\n");
return(0);
}
0000000000000000 <notmain>:
0: 48 83 ec 08 sub $0x8,%rsp
4: bf 00 00 00 00 mov $0x0,%edi
9: e8 00 00 00 00 callq e <notmain+0xe>
e: 31 c0 xor %eax,%eax
10: 48 83 c4 08 add $0x8,%rsp
14: c3 retq
现在,如果您询问 main 中是否有 exit(),那么请确保它不会到达 main 中的返回点,因此堆栈指针会偏移任何数量。但是如果 main 调用了某个函数并且该函数调用了某个函数,则该函数调用了 exit() 然后堆栈指针将留在函数号 2 的堆栈帧点加上任何调用(这是 x86)加上 exit() 堆栈框架添加到它。你不能简单地假设当 exit() 被调用时,如果它被调用,堆栈指针指向什么。您必须检查对 exit() 的调用以及 exit() 代码及其调用的任何内容的反汇编,才能弄清楚这一点。