首先当一个进程调用fork并在子进程中运行exec函数后。会加载一个新的可执行文件,然后从可执行文件
头中指定的代码段地址开始运行。下面是个简单helloworld程序的小实验
peng@Ubuntu:~/c$ cat test.c
#include<stdio.h>
int main()
{
printf("hello,world\n");
return 0;
}
peng@Ubuntu:~/c$ gcc -o test test.c
peng@Ubuntu:~/c$ readelf -h test //查看可执行文件的头部信息
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
.....
Entry point address: 0x8048330
......
peng@Ubuntu:~/c$ objdump -d test //反汇编可执行文件的代码段
test: file format elf32-i386
.....
08048308 <__libc_start_main@plt>:
8048308: ff 25 04 a0 04 08 jmp *0x804a004
804830e: 68 08 00 00 00 push $0x8
8048313: e9 d0 ff ff ff jmp 80482e8 <_init+0x30>
Disassembly of section .text:
08048330 <_start>:
8048330: 31 ed xor %ebp,%ebp
8048332: 5e pop %esi
8048333: 89 e1 mov %esp,%ecx
8048335: 83 e4 f0 and $0xfffffff0,%esp
8048338: 50 push %eax
8048339: 54 push %esp
804833a: 52 push %edx
804833b: 68 00 84 04 08 push $0x8048400
8048340: 68 10 84 04 08 push $0x8048410
8048345: 51 push %ecx
8048346: 56 push %esi
8048347: 68 e4 83 04 08 push $0x80483e4 //把main的地址放到栈上
804834c: e8 b7 ff ff ff call 8048308 <__libc_start_main@plt>
8048351: f4 hlt
8048352: 90 nop
080483e4 <main>:
80483e4: 55 push %ebp
80483e5: 89 e5 mov %esp,%ebp
80483e7: 83 e4 f0 and $0xfffffff0,%esp
80483ea: 83 ec 10 sub $0x10,%esp
80483ed: c7 04 24 c0 84 04 08 movl $0x80484c0,(%esp)
80483f4: e8 1f ff ff ff call 8048318 <puts@plt>
80483f9: b8 00 00 00 00 mov $0x0,%eax //返回0
80483fe: c9 leave
80483ff: c3 ret
....
以上数据删除了部分无关代码。这里可执行文件的头部信息Entry point address: 0x8048330 就是可执行文件开始运行的地方(这里的地址都是在可执行文件中的偏移量)
我 们可以看出这个直至是反汇编中的08048330 <_start>:位置。_start又调用了 call 8048308<__libc_start_main@plt> 在调用前又将main函数的地址作为参数(push $0x80483e4)
传给了__libc_start_main@plt函数。这个函数做了一些初始化操作。最后通过传给他的函数指针调用main,main调用返回后接着调用exit结束运行。
__libc_start_main@plt 函数比较长看汇编比较困难从网上找到了源码。地址:http://sourceware.org/git/?p=glibc.git;a=blob;f=csu/libc-
start.c;h=a14ed71616a3f63f092837e9c30780f8344b4fbe;hb=cvs/glibc-2_9-branch
其中最后两句调用main的比较有意思
249 result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);
252 exit (result);
我们可以看出此函数调用main后立即拿main返回值又调用了exit.
这里可以解释未什么不显示返回值时进程的退出状态是随机的。看看main方法的反汇编最后三条指令
函数的返回值是放到eax寄存器中传递个调用方的。如果不显示返回(也就是返回时没设置eax)。那么eax中的值是不确定的。
转载于:https://blog.51cto.com/hanjie/486693