zoukankan      html  css  js  c++  java
  • linux编程之main()函数启动过程【转】

    转自:http://blog.csdn.net/gary_ygl/article/details/8506007

    1 最简单的程序 

    1)编辑helloworld程序,$vim helloworld.c

    1. 1 #include <stdio.h>  
    2. 2   
    3. 3 int main (int argc, char *argv[])  
    4. 4 {  
    5. 5         printf("Hello world! ");  
    6. 6   
    7. 7         return 0;  
    8. 8 }  

    2) 编译,$ gcc helloworld.c -o helloworld

    3) 运行,$./helloworld 

    Hello world!

    2 最简单的程序其实不简单

    上面这个helloword程序已经再简单不过了,先来看一看它的反汇编代码:

    1. $ objdump -d helloworld  
    2.   
    3. helloworld:     file format elf32-i386  
    4.   
    5.   
    6. Disassembly of section .init:  
    7.   
    8. 080482b0 <_init>:  
    9.  80482b0:   53                      push   %ebx  
    10.  80482b1:   83 ec 08                sub    $0x8,%esp  
    11.  80482b4:   e8 00 00 00 00          call   80482b9 <_init+0x9>  
    12.  80482b9:   5b                      pop    %ebx  
    13.  80482ba:   81 c3 3b 1d 00 00       add    $0x1d3b,%ebx  
    14.  80482c0:   8b 83 fc ff ff ff       mov    -0x4(%ebx),%eax  
    15.  80482c6:   85 c0                   test   %eax,%eax  
    16.  80482c8:   74 05                   je     80482cf <_init+0x1f>  
    17.  80482ca:   e8 31 00 00 00          call   8048300 <__gmon_start__@plt>  
    18.  80482cf:   e8 dc 00 00 00          call   80483b0 <frame_dummy>  
    19.  80482d4:   e8 97 01 00 00          call   8048470 <__do_global_ctors_aux>  
    20.  80482d9:   83 c4 08                add    $0x8,%esp  
    21.  80482dc:   5b                      pop    %ebx  
    22.  80482dd:   c3                      ret      
    23.   
    24. Disassembly of section .plt:  
    25.   
    26. 080482e0 <puts@plt-0x10>:  
    27.  80482e0:   ff 35 f8 9f 04 08       pushl  0x8049ff8  
    28.  80482e6:   ff 25 fc 9f 04 08       jmp    *0x8049ffc  
    29.  80482ec:   00 00                   add    %al,(%eax)  
    30.     ...  
    31.   
    32. 080482f0 <puts@plt>:  
    33.  80482f0:   ff 25 00 a0 04 08       jmp    *0x804a000  
    34.  80482f6:   68 00 00 00 00          push   $0x0  
    35.  80482fb:   e9 e0 ff ff ff          jmp    80482e0 <_init+0x30>  
    36.   
    37. 08048300 <__gmon_start__@plt>:  
    38.  8048300:   ff 25 04 a0 04 08       jmp    *0x804a004  
    39.  8048306:   68 08 00 00 00          push   $0x8  
    40.  804830b:   e9 d0 ff ff ff          jmp    80482e0 <_init+0x30>  
    41.   
    42. 08048310 <__libc_start_main@plt>:  
    43.  8048310:   ff 25 08 a0 04 08       jmp    *0x804a008  
    44.  8048316:   68 10 00 00 00          push   $0x10  
    45.  804831b:   e9 c0 ff ff ff          jmp    80482e0 <_init+0x30>  
    46.   
    47. Disassembly of section .text:  
    48.   
    49. 08048320 <_start>:  
    50.  8048320:   31 ed                   xor    %ebp,%ebp  
    51.  8048322:   5e                      pop    %esi  
    52.  8048323:   89 e1                   mov    %esp,%ecx  
    53.  8048325:   83 e4 f0                and    $0xfffffff0,%esp  
    54.  8048328:   50                      push   %eax  
    55.  8048329:   54                      push   %esp  
    56.  804832a:   52                      push   %edx  
    57.  804832b:   68 60 84 04 08          push   $0x8048460  
    58.  8048330:   68 f0 83 04 08          push   $0x80483f0  
    59.  8048335:   51                      push   %ecx  
    60.  8048336:   56                      push   %esi  
    61.  8048337:   68 d4 83 04 08          push   $0x80483d4  
    62.  804833c:   e8 cf ff ff ff          call   8048310 <__libc_start_main@plt>  
    63.  8048341:   f4                      hlt      
    64.  8048342:   90                      nop  
    65.  8048343:   90                      nop  
    66.  8048344:   90                      nop  
    67.  8048345:   90                      nop  
    68.  8048346:   90                      nop  
    69.  8048347:   90                      nop  
    70.  8048348:   90                      nop  
    71.  8048349:   90                      nop  
    72.  804834a:   90                      nop  
    73.  804834b:   90                      nop  
    74.  804834c:   90                      nop  
    75.  804834d:   90                      nop  
    76.  804834e:   90                      nop  
    77.  804834f:   90                      nop  
    78.   
    79. 08048350 <__do_global_dtors_aux>:  
    80.  8048350:   55                      push   %ebp  
    81.  8048351:   89 e5                   mov    %esp,%ebp  
    82.  8048353:   53                      push   %ebx  
    83.  8048354:   83 ec 04                sub    $0x4,%esp  
    84.  8048357:   80 3d 14 a0 04 08 00    cmpb   $0x0,0x804a014  
    85.  804835e:   75 3f                   jne    804839f <__do_global_dtors_aux+0x4f>  
    86.  8048360:   a1 18 a0 04 08          mov    0x804a018,%eax  
    87.  8048365:   bb 20 9f 04 08          mov    $0x8049f20,%ebx  
    88.  804836a:   81 eb 1c 9f 04 08       sub    $0x8049f1c,%ebx  
    89.  8048370:   c1 fb 02                sar    $0x2,%ebx  
    90.  8048373:   83 eb 01                sub    $0x1,%ebx  
    91.  8048376:   39 d8                   cmp    %ebx,%eax  
    92.  8048378:   73 1e                   jae    8048398 <__do_global_dtors_aux+0x48>  
    93.  804837a:   8d b6 00 00 00 00       lea    0x0(%esi),%esi  
    94.  8048380:   83 c0 01                add    $0x1,%eax  
    95.  8048383:   a3 18 a0 04 08          mov    %eax,0x804a018  
    96.  8048388:   ff 14 85 1c 9f 04 08    call   *0x8049f1c(,%eax,4)  
    97.  804838f:   a1 18 a0 04 08          mov    0x804a018,%eax  
    98.  8048394:   39 d8                   cmp    %ebx,%eax  
    99.  8048396:   72 e8                   jb     8048380 <__do_global_dtors_aux+0x30>  
    100.  8048398:   c6 05 14 a0 04 08 01    movb   $0x1,0x804a014  
    101.  804839f:   83 c4 04                add    $0x4,%esp  
    102.  80483a2:   5b                      pop    %ebx  
    103.  80483a3:   5d                      pop    %ebp  
    104.  80483a4:   c3                      ret      
    105.  80483a5:   8d 74 26 00             lea    0x0(%esi,%eiz,1),%esi  
    106.  80483a9:   8d bc 27 00 00 00 00    lea    0x0(%edi,%eiz,1),%edi  
    107.   
    108. 080483b0 <frame_dummy>:  
    109.  80483b0:   55                      push   %ebp  
    110.  80483b1:   89 e5                   mov    %esp,%ebp  
    111.  80483b3:   83 ec 18                sub    $0x18,%esp  
    112.  80483b6:   a1 24 9f 04 08          mov    0x8049f24,%eax  
    113.  80483bb:   85 c0                   test   %eax,%eax  
    114.  80483bd:   74 12                   je     80483d1 <frame_dummy+0x21>  
    115.  80483bf:   b8 00 00 00 00          mov    $0x0,%eax  
    116.  80483c4:   85 c0                   test   %eax,%eax  
    117.  80483c6:   74 09                   je     80483d1 <frame_dummy+0x21>  
    118.  80483c8:   c7 04 24 24 9f 04 08    movl   $0x8049f24,(%esp)  
    119.  80483cf:   ff d0                   call   *%eax  
    120.  80483d1:   c9                      leave    
    121.  80483d2:   c3                      ret      
    122.  80483d3:   90                      nop  
    123.   
    124. 080483d4 <main>:  
    125.  80483d4:   55                      push   %ebp  
    126.  80483d5:   89 e5                   mov    %esp,%ebp  
    127.  80483d7:   83 e4 f0                and    $0xfffffff0,%esp  
    128.  80483da:   83 ec 10                sub    $0x10,%esp  
    129.  80483dd:   c7 04 24 c0 84 04 08    movl   $0x80484c0,(%esp)  
    130.  80483e4:   e8 07 ff ff ff          call   80482f0 <puts@plt>  
    131.  80483e9:   b8 00 00 00 00          mov    $0x0,%eax  
    132.  80483ee:   c9                      leave    
    133.  80483ef:   c3                      ret      
    134.   
    135. 080483f0 <__libc_csu_init>:  
    136.  80483f0:   55                      push   %ebp  
    137.  80483f1:   57                      push   %edi  
    138.  80483f2:   56                      push   %esi  
    139.  80483f3:   53                      push   %ebx  
    140.  80483f4:   e8 69 00 00 00          call   8048462 <__i686.get_pc_thunk.bx>  
    141.  80483f9:   81 c3 fb 1b 00 00       add    $0x1bfb,%ebx  
    142.  80483ff:   83 ec 1c                sub    $0x1c,%esp  
    143.  8048402:   8b 6c 24 30             mov    0x30(%esp),%ebp  
    144.  8048406:   8d bb 20 ff ff ff       lea    -0xe0(%ebx),%edi  
    145.  804840c:   e8 9f fe ff ff          call   80482b0 <_init>  
    146.  8048411:   8d 83 20 ff ff ff       lea    -0xe0(%ebx),%eax  
    147.  8048417:   29 c7                   sub    %eax,%edi  
    148.  8048419:   c1 ff 02                sar    $0x2,%edi  
    149.  804841c:   85 ff                   test   %edi,%edi  
    150.  804841e:   74 29                   je     8048449 <__libc_csu_init+0x59>  
    151.  8048420:   31 f6                   xor    %esi,%esi  
    152.  8048422:   8d b6 00 00 00 00       lea    0x0(%esi),%esi  
    153.  8048428:   8b 44 24 38             mov    0x38(%esp),%eax  
    154.  804842c:   89 2c 24                mov    %ebp,(%esp)  
    155.  804842f:   89 44 24 08             mov    %eax,0x8(%esp)  
    156.  8048433:   8b 44 24 34             mov    0x34(%esp),%eax  
    157.  8048437:   89 44 24 04             mov    %eax,0x4(%esp)  
    158.  804843b:   ff 94 b3 20 ff ff ff    call   *-0xe0(%ebx,%esi,4)  
    159.  8048442:   83 c6 01                add    $0x1,%esi  
    160.  8048445:   39 fe                   cmp    %edi,%esi  
    161.  8048447:   75 df                   jne    8048428 <__libc_csu_init+0x38>  
    162.  8048449:   83 c4 1c                add    $0x1c,%esp  
    163.  804844c:   5b                      pop    %ebx  
    164.  804844d:   5e                      pop    %esi  
    165.  804844e:   5f                      pop    %edi  
    166.  804844f:   5d                      pop    %ebp  
    167.  8048450:   c3                      ret      
    168.  8048451:   eb 0d                   jmp    8048460 <__libc_csu_fini>  
    169.  8048453:   90                      nop  
    170.  8048454:   90                      nop  
    171.  8048455:   90                      nop  
    172.  8048456:   90                      nop  
    173.  8048457:   90                      nop  
    174.  8048458:   90                      nop  
    175.  8048459:   90                      nop  
    176.  804845a:   90                      nop  
    177.  804845b:   90                      nop  
    178.  804845c:   90                      nop  
    179.  804845d:   90                      nop  
    180.  804845e:   90                      nop  
    181.  804845f:   90                      nop  
    182.   
    183. 08048460 <__libc_csu_fini>:  
    184.  8048460:   f3 c3                   repz ret   
    185.   
    186. 08048462 <__i686.get_pc_thunk.bx>:  
    187.  8048462:   8b 1c 24                mov    (%esp),%ebx  
    188.  8048465:   c3                      ret      
    189.  8048466:   90                      nop  
    190.  8048467:   90                      nop  
    191.  8048468:   90                      nop  
    192.  8048469:   90                      nop  
    193.  804846a:   90                      nop  
    194.  804846b:   90                      nop  
    195.  804846c:   90                      nop  
    196.  804846d:   90                      nop  
    197.  804846e:   90                      nop  
    198.  804846f:   90                      nop  
    199.   
    200. 08048470 <__do_global_ctors_aux>:  
    201.  8048470:   55                      push   %ebp  
    202.  8048471:   89 e5                   mov    %esp,%ebp  
    203.  8048473:   53                      push   %ebx  
    204.  8048474:   83 ec 04                sub    $0x4,%esp  
    205.  8048477:   a1 14 9f 04 08          mov    0x8049f14,%eax  
    206.  804847c:   83 f8 ff                cmp    $0xffffffff,%eax  
    207.  804847f:   74 13                   je     8048494 <__do_global_ctors_aux+0x24>  
    208.  8048481:   bb 14 9f 04 08          mov    $0x8049f14,%ebx  
    209.  8048486:   66 90                   xchg   %ax,%ax  
    210.  8048488:   83 eb 04                sub    $0x4,%ebx  
    211.  804848b:   ff d0                   call   *%eax  
    212.  804848d:   8b 03                   mov    (%ebx),%eax  
    213.  804848f:   83 f8 ff                cmp    $0xffffffff,%eax  
    214.  8048492:   75 f4                   jne    8048488 <__do_global_ctors_aux+0x18>  
    215.  8048494:   83 c4 04                add    $0x4,%esp  
    216.  8048497:   5b                      pop    %ebx  
    217.  8048498:   5d                      pop    %ebp  
    218.  8048499:   c3                      ret      
    219.  804849a:   90                      nop  
    220.  804849b:   90                      nop  
    221.   
    222. Disassembly of section .fini:  
    223.   
    224. 0804849c <_fini>:  
    225.  804849c:   53                      push   %ebx  
    226.  804849d:   83 ec 08                sub    $0x8,%esp  
    227.  80484a0:   e8 00 00 00 00          call   80484a5 <_fini+0x9>  
    228.  80484a5:   5b                      pop    %ebx  
    229.  80484a6:   81 c3 4f 1b 00 00       add    $0x1b4f,%ebx  
    230.  80484ac:   e8 9f fe ff ff          call   8048350 <__do_global_dtors_aux>  
    231.  80484b1:   83 c4 08                add    $0x8,%esp  
    232.  80484b4:   5b                      pop    %ebx  
    233.  80484b5:   c3                      ret   
    从反汇编结果来看,helloworld可执行文件并不只是main函数,除main外,还有几个重要的函数,_init,_fini,_start,__libc_start_main,__libc_csu_init,__libc_csu_fini等。
    除main函数外,其它函数从哪来的?分析一下编译好的可执行文件helloworld的库依赖关系如下:
    1. $ ldd helloworld  
    2.     linux-gate.so.1 =>  (0x00876000)  
    3.     libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x00110000)  
    4.     /lib/ld-linux.so.2 (0x006a3000)  
    其中linux-gate.so.1为系统调用相关的动态库,ld-linux.so.2为动态链接库,libc.so.6为GNU C库。
    从反汇编出来的相关函数名称(__libc_start_main)来看,我们把目标锁定在GNU C库,即glibc,也是GNU开源项目,可以从GNU 源码ftp站点(http://ftp.gnu.org/gnu/libc/)上找到。
    __libc_start_main函数可以在glibc源码的csu/libc-start.c中找到:
    1. STATIC int LIBC_START_MAIN (int (*main) (int, char **, char **  
    2.                      MAIN_AUXVEC_DECL),  
    3.                 int argc,  
    4.                 char *__unbounded *__unbounded ubp_av,  
    5. #ifdef LIBC_START_MAIN_AUXVEC_ARG  
    6.                 ElfW(auxv_t) *__unbounded auxvec,  
    7. #endif  
    8.                 __typeof (main) init,  
    9.                 void (*fini) (void),  
    10.                 void (*rtld_fini) (void),  
    11.                 void *__unbounded stack_end)  
    12.      __attribute__ ((noreturn));  
    __libc_csu_init,__libc_csu_fini函数可以在glibc源码的csu/elf-init.c中找到:
    1. void  
    2. __libc_csu_init (int argc, char **argv, char **envp)  
    3. {  
    4.   /* For dynamically linked executables the preinit array is executed by  
    5.      the dynamic linker (before initializing any shared object.  */  
    6.   
    7. #ifndef LIBC_NONSHARED  
    8.   /* For static executables, preinit happens right before init.  */  
    9.   {  
    10.     const size_t size = __preinit_array_end - __preinit_array_start;  
    11.     size_t i;  
    12.     for (i = 0; i size; i++)  
    13.       (*__preinit_array_start [i]) (argc, argv, envp);  
    14.   }  
    15. #endif  
    16.   
    17.   _init ();  
    18.   
    19.   const size_t size = __init_array_end - __init_array_start;  
    20.   for (size_t i = 0; i size; i++)  
    21.       (*__init_array_start [i]) (argc, argv, envp);  
    22. }  
    23.   
    24. /* This function should not be used anymore.  We run the executable's  
    25.    destructor now just like any other.  We cannot remove the function,  
    26.    though.  */  
    27. void  
    28. __libc_csu_fini (void)  
    29. {  
    30. #ifndef LIBC_NONSHARED  
    31.   size_t i = __fini_array_end - __fini_array_start;  
    32.   while (i-- > 0)  
    33.     (*__fini_array_start [i]) ();  
    34.   
    35.   _fini ();  
    36. #endif  
    37. }  
    _start函数可以在glibc源码的sysdeps/i386/elf/start.S中找到:
    1. #include "bp-sym.h"  
    2.   
    3.     .text  
    4.     .globl _start  
    5.     .type _start,@function  
    6. _start:  
    7.     /* Clear the frame pointer.  The ABI suggests this be done, to mark  
    8.        the outermost frame obviously.  */  
    9.     xorl %ebp, %ebp  
    10.   
    11.     /* Extract the arguments as encoded on the stack and set up  
    12.        the arguments for `main': argc, argv.  envp will be determined  
    13.        later in __libc_start_main.  */  
    14.     popl %esi       /* Pop the argument count.  */  
    15.     movl %esp, %ecx     /* argv starts just at the current stack top.*/  
    16.   
    17.     /* Before pushing the arguments align the stack to a 16-byte  
    18.     (SSE needs 16-byte alignment) boundary to avoid penalties from  
    19.     misaligned accesses.  Thanks to Edward Seidl <seidl@janed.com>  
    20.     for pointing this out.  */  
    21.     andl $0xfffffff0, %esp  
    22.     pushl %eax      /* Push garbage because we allocate  
    23.                    28 more bytes.  */  
    24.   
    25.     /* Provide the highest stack address to the user code (for stacks  
    26.        which grow downwards).  */  
    27.     pushl %esp  
    28.   
    29.     pushl %edx      /* Push address of the shared library  
    30.                    termination function.  */  
    31.   
    32. #ifdef SHARED  
    33.     /* Load PIC register.  */  
    34.     call 1f  
    35.     addl $_GLOBAL_OFFSET_TABLE_, %ebx  
    36.   
    37.     /* Push address of our own entry points to .fini and .init.  */  
    38.     leal __libc_csu_fini@GOTOFF(%ebx), %eax  
    39.     pushl %eax  
    40.     leal __libc_csu_init@GOTOFF(%ebx), %eax  
    41.     pushl %eax  
    42.   
    43.     pushl %ecx      /* Push second argument: argv.  */  
    44.     pushl %esi      /* Push first argument: argc.  */  
    45.   
    46.     pushl BP_SYM (main)@GOT(%ebx)  
    47.   
    48.     /* Call the user's main function, and exit with its value.  
    49.        But let the libc call main.    */  
    50.     call BP_SYM (__libc_start_main)@PLT  
    51. #else  
    52.     /* Push address of our own entry points to .fini and .init.  */  
    53.     pushl $__libc_csu_fini  
    54.     pushl $__libc_csu_init  
    55.   
    56.     pushl %ecx      /* Push second argument: argv.  */  
    57.     pushl %esi      /* Push first argument: argc.  */  
    58.   
    59.     pushl $BP_SYM (main)  
    60.   
    61.     /* Call the user's main function, and exit with its value.  
    62.        But let the libc call main.    */  
    63.     call BP_SYM (__libc_start_main)  
    64. #endif  
    65.   
    66.     hlt         /* Crash if somehow `exit' does return.  */  
    _init,_fini函数可以在glibc源码的sysdeps/generic/initfini.c中找到:
    1. /* The beginning of _init:  */  
    2. asm (" /*@_init_PROLOG_BEGINS*/");  
    3.   
    4. static void  
    5. call_gmon_start(void)  
    6. {  
    7.   extern void __gmon_start__ (void) __attribute__ ((weak)); /*weak_extern (__gmon_start__);*/  
    8.   void (*gmon_start) (void) = __gmon_start__;  
    9.   
    10.   if (gmon_start)  
    11.     gmon_start ();  
    12. }  
    13.   
    14. SECTION (".init");  
    15. extern void __attribute__ ((section (".init"))) _init (void);  
    16. void  
    17. _init (void)  
    18. {  
    19.   /* We cannot use the normal constructor mechanism in gcrt1.o because it  
    20.      appears before crtbegin.o in the link, so the header elt of .ctors  
    21.      would come after the elt for __gmon_start__.  One approach is for  
    22.      gcrt1.o to reference a symbol which would be defined by some library  
    23.      module which has a constructor; but then user code's constructors  
    24.      would come first, and not be profiled.  */  
    25.   call_gmon_start ();  
    26.   
    27.   asm ("ALIGN");  
    28.   asm("END_INIT");  
    29.   /* Now the epilog. */  
    30.   asm (" /*@_init_PROLOG_ENDS*/");  
    31.   asm (" /*@_init_EPILOG_BEGINS*/");  
    32.   SECTION(".init");  
    33. }  
    34. asm ("END_INIT");  
    35.   
    36. /* End of the _init epilog, beginning of the _fini prolog. */  
    37. asm (" /*@_init_EPILOG_ENDS*/");  
    38. asm (" /*@_fini_PROLOG_BEGINS*/");  
    39.   
    40. SECTION (".fini");  
    41. extern void __attribute__ ((section (".fini"))) _fini (void);  
    42. void  
    43. _fini (void)  
    44. {  
    45.   
    46.   /* End of the _fini prolog. */  
    47.   asm ("ALIGN");  
    48.   asm ("END_FINI");  
    49.   asm (" /*@_fini_PROLOG_ENDS*/");  
    50.   
    51.   {  
    52.     /* Let GCC know that _fini is not a leaf function by having a dummy  
    53.        function call here.  We arrange for this call to be omitted from  
    54.        either crt file.  */  
    55.     extern void i_am_not_a_leaf (void);  
    56.     i_am_not_a_leaf ();  
    57.   }  
    58.   
    59.   /* Beginning of the _fini epilog. */  
    60.   asm (" /*@_fini_EPILOG_BEGINS*/");  
    61.   SECTION (".fini");  
    62. }  
    63. asm ("END_FINI");  
    64.   
    65. /* End of the _fini epilog.  Any further generated assembly (e.g. .ident)  
    66.    is shared between both crt files. */  
    67. asm (" /*@_fini_EPILOG_ENDS*/");  
    68. asm (" /*@TRAILER_BEGINS*/");  
    但是为什么不能在libc.so.6中找到_start,_init,_fini这三个函数呢?
    是因为GNU把这三个作为了程序启动和结束的最基本运行库函数,分别放在crt1.o,crti.o,crtn.o这三个object文件中供程序链接时使用。
    从glibc的源码sysdeps/generic/initfini.c相关注释也可以看出:
    1. /* This file is compiled into assembly code which is then munged by a sed  
    2.    script into two files: crti.s and crtn.s.  
    3.   
    4.    * crti.s puts a function prologue at the beginning of the  
    5.    .init and .fini sections and defines global symbols for  
    6.    those addresses, so they can be called as functions.  
    7.   
    8.    * crtn.s puts the corresponding function epilogues  
    9.    in the .init and .fini sections. */  
    从上面注释来看crti.o,crtn.o分别包含.init和.fini段的开头和结束部分,分析它们的反汇编代码也可以看出:
    1. $ objdump -d /usr/lib/i386-linux-gnu/crti.o   
    2. /usr/lib/i386-linux-gnu/crti.o:     file format elf32-i386  
    3.   
    4.   
    5. Disassembly of section .init:  
    6.   
    7. 00000000 <_init>:  
    8.    0:   53                      push   %ebx  
    9.    1:   83 ec 08                sub    $0x8,%esp  
    10.    4:   e8 00 00 00 00          call   9 <_init+0x9>  
    11.    9:   5b                      pop    %ebx  
    12.    a:   81 c3 03 00 00 00       add    $0x3,%ebx  
    13.   10:   8b 83 00 00 00 00       mov    0x0(%ebx),%eax  
    14.   16:   85 c0                   test   %eax,%eax  
    15.   18:   74 05                   je     1f <_init+0x1f>  
    16.   1a:   e8 fc ff ff ff          call   1b <_init+0x1b>  
    17.   
    18. Disassembly of section .fini:  
    19.   
    20. 00000000 <_fini>:  
    21.    0:   53                      push   %ebx  
    22.    1:   83 ec 08                sub    $0x8,%esp  
    23.    4:   e8 00 00 00 00          call   9 <_fini+0x9>  
    24.    9:   5b                      pop    %ebx  
    25.    a:   81 c3 03 00 00 00       add    $0x3,%ebx  
    1. $ objdump -d /usr/lib/i386-linux-gnu/crtn.o   
    2.   
    3. /usr/lib/i386-linux-gnu/crtn.o:     file format elf32-i386  
    4.   
    5.   
    6. Disassembly of section .init:  
    7.   
    8. 00000000 <.init>:  
    9.    0:   83 c4 08                add    $0x8,%esp  
    10.    3:   5b                      pop    %ebx  
    11.    4:   c3                      ret      
    12.   
    13. Disassembly of section .fini:  
    14.   
    15. 00000000 <.fini>:  
    16.    0:   83 c4 08                add    $0x8,%esp  
    17.    3:   5b                      pop    %ebx  
    18.    4:   c3                      ret  

    回过头来看看helloworld程序反汇编代码中的_init,_fini函数的组成:

    1. 080482b0 <_init>:  
    2.  80482b0:   53                      push   %ebx  
    3.  80482b1:   83 ec 08                sub    $0x8,%esp  
    4.  80482b4:   e8 00 00 00 00          call   80482b9 <_init+0x9>  
    5.  80482b9:   5b                      pop    %ebx  
    6.  80482ba:   81 c3 3b 1d 00 00       add    $0x1d3b,%ebx  
    7.  80482c0:   8b 83 fc ff ff ff       mov    -0x4(%ebx),%eax  
    8.  80482c6:   85 c0                   test   %eax,%eax  
    9.  80482c8:   74 05                   je     80482cf <_init+0x1f>  
    1. 80482ca:    e8 31 00 00 00          call   8048300 <__gmon_start__@plt>  
    2. 80482cf:    e8 dc 00 00 00          call   80483b0 <frame_dummy>  
    3. 80482d4:    e8 97 01 00 00          call   8048470 <__do_global_ctors_aux>  
    1. 80482d9:    83 c4 08                add    $0x8,%esp  
    2. 80482dc:    5b                      pop    %ebx  
    3. 80482dd:    c3                      ret   
    1. 0804849c <_fini>:  
    2.  804849c:   53                      push   %ebx  
    3.  804849d:   83 ec 08                sub    $0x8,%esp  
    4.  80484a0:   e8 00 00 00 00          call   80484a5 <_fini+0x9>  
    5.  80484a5:   5b                      pop    %ebx  
    6.  80484a6:   81 c3 4f 1b 00 00       add    $0x1b4f,%ebx  
    1. 80484ac:    e8 9f fe ff ff          call   8048350 <__do_global_dtors_aux>  
    1. 80484b1:    83 c4 08                add    $0x8,%esp  
    2. 80484b4:    5b                      pop    %ebx  
    3. 80484b5:    c3                      ret   
    helloworld程序反汇编代码中的_init,_fini函数中间多出来的部分是其它库或用户自定义的.init和.fini段代码。
    从上面helloworld程序反汇编来看,程序启动的过程应该:
    _start -> __libc_start_main -> main. 具体一点就是:
    _start -> __libc_start_main -> __libc_csu_init -> main. 再具体一点就是:
    _start -> __libc_start_main -> __libc_csu_init -> _init -> main -> _fini.
    可以通过分析__libc_start_main函数进一步了解,下面的__libc_start_main函数是我精简后的伪代码:
    1. /* Note: the fini parameter is ignored here for shared library.  It  
    2.    is registered with __cxa_atexit.  This had the disadvantage that  
    3.    finalizers were called in more than one place.  */  
    4. STATIC int  
    5. LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),  
    6.          int argc, char *__unbounded *__unbounded ubp_av,  
    7. #ifdef LIBC_START_MAIN_AUXVEC_ARG  
    8.          ElfW(auxv_t) *__unbounded auxvec,  
    9. #endif  
    10.          __typeof (main) init,  
    11.          void (*fini) (void),  
    12.          void (*rtld_fini) (void), void *__unbounded stack_end)  
    13. {  
    14.   ...  
    15.   /* Register the destructor of the dynamic linker if there is any.  */  
    16.   if (__builtin_expect (rtld_fini != NULL, 1))  
    17.     __cxa_atexit ((void (*) (void *)) rtld_fini, NULL, NULL);  
    18.   
    19.   /* Call the initializer of the libc.  This is only needed here if we  
    20.      are compiling for the static library in which case we haven't  
    21.      run the constructors in `_dl_start_user'.  */  
    22.   __libc_init_first (argc, argv, __environ);  
    23.   
    24.   /* Register the destructor of the program, if any.  */  
    25.   if (fini)  
    26.     __cxa_atexit ((void (*) (void *)) fini, NULL, NULL);  
    27.   
    28.   /* Call the initializer of the program, if any.  */  
    29.   if (init)  
    30.     (*init) (argc, argv, __environ MAIN_AUXVEC_PARAM);  
    31.   
    32.   /* Nothing fancy, just call the function.  */  
    33.   result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);  
    34.   
    35.   exit (result);  
    36. }  

    __libc_start_main函数的参数main, argc, ubp_av, init, fini, rtld_fini都是通过_start入栈得到。

    其中argc, ubp_av为传递给main函数的参数argc, argv。init为__libc_csu_init函数指针,fini为__libc_csu_fini函数指针,rtld_fini为运行库加载收尾函数指针。

    从__libc_start_main函数可以看出在call init之前还通过__cxa_atexit向exit函数注册了rtlf_fini和fini函数,目的是为了在main结束后call exit自动完成一些收尾工作。

    这里的_init, _fini函数功能还主要负责完成C++程序全局/静态对象的构造与析构,有兴趣的可以深入一下。

    __cxa_atexit函数用于注册main结束后程序退出时调用的函数,例如:

    1.  1 #include <stdio.h>  
    2.  2 #include <stdlib.h>  
    3.  3   
    4.  4 void pre_exit (void)  
    5.  5 {  
    6.  6         printf("Will be exit! ");  
    7.  7 }  
    8.  8   
    9.  9 int main (int argc, char *argv[])  
    10. 10 {  
    11. 11         __cxa_atexit(pre_exit, NULL, NULL);  
    12. 12   
    13. 13         printf("Hello world! ");  
    14. 14   
    15. 15         return 0;  
    16. 16 }  
    1. $ gcc helloworld2.c -o helloworld2  
    2. $ ./helloworld2  
    3. Hello world!  
    4. Will be exit!  

    可以看到pre_exit函数并不是靠main来执行的,而是靠__libc_start_main函数里面的exit函数来调用完成。

    为了更进一步理解_init函数的作用,我们再改写一下代码:

    因为链接器会自动把.init段粘贴到一起组成_init函数,所以为了不使_init函数半途返回退出,我们只能用下面汇编的方法定义哪些程序块是需要被链接到_init函数,且该程序块不能有函数的返回指令,这样我们必须在该程序块结束的地方加入汇编指令告诉编译器后面的程序不属于.init段,因此就有了下面这个测试函数pre_init,它会被link到两个section,即.init和.text段。

    1.  1 #include <stdio.h>  
    2.  2 #include <stdlib.h>  
    3.  3   
    4.  4 void __attribute__((used)) pre_init (void)  
    5.  5 {  
    6.  6         asm (".section .init");  
    7.  7         printf("pre-init section .init part ");  
    8.  8         printf("Call pre_init before main ");  
    9.  9         asm (".section .text");  
    10. 10         printf("pre-init section .text part ");  
    11. 11         printf("call pre_init in main ");  
    12. 12 }  
    13. 13   
    14. 14 void pre_exit (void)  
    15. 15 {  
    16. 16         printf("Call pre_exit after main! ");  
    17. 17 }  
    18. 18   
    19. 19 int main (int argc, char *argv[])  
    20. 20 {  
    21. 21         printf("Enter main ");  
    22. 22   
    23. 23         __cxa_atexit(pre_exit, NULL, NULL);  
    24. 24   
    25. 25         printf("Hello world! ");  
    26. 26   
    27. 27         pre_init();  
    28. 28   
    29. 29         printf("Exit main ");  
    30. 30   
    31. 31         return 0;  
    32. 32 }  
    1. $ gcc helloworld3.c -o helloworld3  
    2. $ ./helloworld3  
    3. pre-init section .init part  
    4. Call pre_init before main  
    5. Enter main  
    6. Hello world!  
    7. pre-init section .text part  
    8. call pre_init in main  
    9. Exit main  
    10. Call pre_exit after main!  
    从helloworld3程序的运行结果也可以看出该程序函数的调用过程:

    pre_init (.init段部分 代码) -> main -> pre_init (.text段部分 代码) -> pre_exit.

    同样我们也可以添加.fini段代码,链接器会把.fini段代码部分链接进_fini函数,这样_fini函数就会帮我们在main函数退出后自动执行这段代码:

    1. 14 void __attribute__((used)) pre_fini (void)  
    2. 15 {  
    3. 16         asm (".section .fini");  
    4. 17         printf("pre-fini section .fini part ");  
    5. 18         printf("Call pre_fini after main ");  
    6. 19         asm (".section .text");  
    7. 20 }  
    1. $ gcc helloworld4.c -o helloworld4  
    2. $ ./helloworld4  
    3. pre-init section .init part  
    4. Call pre_init before main  
    5. Enter main  
    6. Hello world!  
    7. pre-init section .text part  
    8. call pre_init in main  
    9. Exit main  
    10. Call pre_exit after main!  
    11. pre-fini section .fini part  
    12. Call pre_fini after main  

    从以上运行结果可知pre_fini函数是在main函数退出后才被调用的,但是为什么pre_fini打印的内容比pre_exit打印的内容要晚出来呢?这是因为pre_exit函数注册比pre_fini要晚,而通过__cxa_atexit注册的过程其实就是建立一个exit函数指针链表栈,按栈的规则是后进先出,因此pre_exit比pre_fini要先被调用。

    从helloworld4程序的运行结果也可以看出该程序函数的调用过程:

    pre_init (.init段部分 代码) -> main -> pre_init (.text段部分 代码) -> pre_exit -> pre_fini (.fini段部分 代码).

    为了让程序正常运行和结束,链接器ld帮我们做了好多事情,可以用下面来表示:

    ld crt1.o crti.o [usr object] [lib] crtn.o 

    最后总结一下,程序启动和结束的过程可以描述为: 

    _start -> __libc_start_main ->  init -> main -> exit.

    3 Self-check

    如果想对main的启动及结束有进一步的理解,最好的方法就亲自读一读glibc的相关源码。
    另外,介绍一本好书《程序员的自我修养—链接、装载与库》。
    (待完善)

    版权声明:本文为博主原创文章,未经博主允许不得转载。
  • 相关阅读:
    anaconda的一些命令
    ffmpeg播放RTSP的一点优化
    CUDA JPEG编码
    获取CPU和内存的使用率
    《OpenCL编程指南》之 与Direct3D互操作
    OpenGL全景视频
    win32调用系统颜色对话框
    [转]RGB数据保存为BMP图片
    NVML查询显卡信息
    ffmpeg nvenc编码
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/8422023.html
Copyright © 2011-2022 走看看