首先当一个进程调用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中的值是不确定的。