Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1033919
  • 博文数量: 26
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 437
  • 用 户 组: 普通用户
  • 注册时间: 2019-09-08 12:19
个人简介

关于个人介绍,既然你诚心诚意的看了 我就大发慈悲的告诉你 为了防止世界被破坏 为了维护世界的和平 贯彻爱与真实的邪恶 可爱又迷人的反派角色 我们是穿梭在银河的火箭队 白洞,白色的明天在等着我们

文章分类

分类: C/C++

2019-09-08 21:10:35

工作了这么久, 现在也终于有时间来写写这几年在程序世界中的感受。一时之间并不知道从哪里开始。想来想去,还是从大学入学开始吧。记得那是一个风和日丽的下午,一堆大学生抱着书跑进教室,那个时候并没有那么多逃课的,只知道相传C语言是一门学了就能找到工作的科目。从此我和我们内敛含蓄的hello world妹妹来了一次深入的体会。老师说main函数就是hello world的一切,我们的程序都是从main开始,虽然老师是好意,但是这确实导致未来很大一部分初级程序员都认为C语言的入口就是main函数。

下面这个Hello World 不知道坑害了多少善良无辜的程序员。现在我们就来解剖她,看她这么较小单纯,真舍不得让她一丝不挂的展现出来。

点击(此处)折叠或打开

  1. #include <stdio.h>

  2. int main (int argc, char *argv[])
  3. {
  4.     printf ("Hello World\n");

  5.     return 0;
  6. }

保存为hello.c

我们可以通过gcc hello.c -o hello得到可执行程序hello. 很多人会认为运行hello, CPU会首先跳转到main函数,执行printf. 至少绝大部分刚毕业的软件工程师是这样认为(因为老师就是这样说的)。下面我们就来详细讲讲CPU是怎么运行到main函数的。

先用strace跟踪一下./hello程序在运行的时候都做了一些什么, strace ./hello:

点击(此处)折叠或打开

  1. $ strace ./hello
  2. execve("./hello", ["./hello"], [/* 33 vars */]) = 0
  3. brk(NULL) = 0x1fde000
  4. access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
  5. access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
  6. open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
  7. fstat(3, {st_mode=S_IFREG|0644, st_size=118062, ...}) = 0
  8. mmap(NULL, 118062, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f4dfc15b000
  9. close(3) = 0
  10. access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
  11. open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
  12. read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\t\2\0\0\0\0\0"..., 832) = 832
  13. fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0
  14. mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4dfc15a000
  15. mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f4dfbb89000
  16. mprotect(0x7f4dfbd49000, 2097152, PROT_NONE) = 0
  17. mmap(0x7f4dfbf49000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x7f4dfbf49000
  18. mmap(0x7f4dfbf4f000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f4dfbf4f000
  19. close(3) = 0
  20. mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4dfc159000
  21. mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f4dfc158000
  22. arch_prctl(ARCH_SET_FS, 0x7f4dfc159700) = 0
  23. mprotect(0x7f4dfbf49000, 16384, PROT_READ) = 0
  24. mprotect(0x600000, 4096, PROT_READ) = 0
  25. mprotect(0x7f4dfc178000, 4096, PROT_READ) = 0
  26. munmap(0x7f4dfc15b000, 118062) = 0
  27. fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 21), ...}) = 0
  28. brk(NULL) = 0x1fde000
  29. brk(0x1fff000) = 0x1fff000
  30. write(1, "Hello World\n", 12Hello World
  31. ) = 12
  32. exit_group(0) = ?
  33. +++ exited with 0 +++

可以发现在shell终端在执行./hello的时候,实际上shell会调用execve函数来进行一次进程替换,即当前shell--->hello。execve是一个系统调用,Linux内核会在这个系统调用里面为hello程序映射必要的内存,最重要的是.text代码段, 然后为其设置对应的环境变量(具体过程在内核篇会详细讲解),最后通过修改lr寄存器的方式,在execve返回的时候将控制权交给ld-linux-x86-64.so.2(可能在某些嵌入式环境里面名字不叫这个, 这个名字可以通过readelf -l hello | grep interpreter 获取到)

点击(此处)折叠或打开

  1. readelf -l hello | grep interpreter
  2.       [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
ld-linux-x86-64.so.2这个文件就是著名的动态解释器,这是一个全部由与位置无关(PIC)的代码组成,能够进行动态库代码重定向等功能,这里不详细解析,以后会有文章解释。当execve将控制权交给ld-linux-x86-64.so.2的时候, 这个文件就负责执行程序和动态库的代码重定向功能,最后通过hello的elf头部信息,将控制权交给地址0x400430,为什么是0x400430,可以从elf部分信息知晓,这里可以简单的理解为内核解析hello文件的时候,读取了它的elf信息,便知道了它的入口函数位置,如下:

点击(此处)折叠或打开

  1. $ readelf -h hello
  2. ELF Header:
  3. Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
  4. Class: ELF64
  5. Data: 2's complement, little endian
  6. Version: 1 (current)
  7. OS/ABI: UNIX - System V
  8. ABI Version: 0
  9. Type: EXEC (Executable file)
  10. Machine: Advanced Micro Devices X86-64
  11. Version: 0x1
  12. Entry point address: 0x400430
  13. Start of program headers: 64 (bytes into file)
  14. Start of section headers: 6616 (bytes into file)
  15. Flags: 0x0
  16. Size of this header: 64 (bytes)
  17. Size of program headers: 56 (bytes)
  18. Number of program headers: 9
  19. Size of section headers: 64 (bytes)
  20. Number of section headers: 31
  21. Section header string table index: 28
注意带颜色的那一行。elf文件里面详细的记录了这个文件的启动函数地址:0x400430。接下来,我们看看0x400430到底有什么先用objdump -axd hello > hello.s来看看,实际使用的可执行程序hello到底包含了哪些:


点击(此处)折叠或打开

  1. hello: file format elf64-x86-64
  2. hello
  3. architecture: i386:x86-64, flags 0x00000112:
  4. EXEC_P, HAS_SYMS, D_PAGED
  5. start address 0x0000000000400430

  6. 省略其他段....

  7. Disassembly of section .init:

  8. 00000000004003c8 <_init>:
  9.   4003c8: 48 83 ec 08 sub $0x8,%rsp
  10.   4003cc: 48 8b 05 25 0c 20 00 mov 0x200c25(%rip),%rax # 600ff8 <_DYNAMIC+0x1d0>
  11.   4003d3: 48 85 c0 test %rax,%rax
  12.   4003d6: 74 05 je 4003dd <_init+0x15>
  13.   4003d8: e8 43 00 00 00 callq 400420 <__libc_start_main@plt+0x10>
  14.   4003dd: 48 83 c4 08 add $0x8,%rsp
  15.   4003e1: c3 retq

  16. Disassembly of section .plt:

  17. 00000000004003f0 <puts@plt-0x10>:
  18.   4003f0: ff 35 12 0c 20 00 pushq 0x200c12(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
  19.   4003f6: ff 25 14 0c 20 00 jmpq *0x200c14(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
  20.   4003fc: 0f 1f 40 00 nopl 0x0(%rax)

  21. 0000000000400400 <puts@plt>:
  22.   400400: ff 25 12 0c 20 00 jmpq *0x200c12(%rip) # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>
  23.   400406: 68 00 00 00 00 pushq $0x0
  24.   40040b: e9 e0 ff ff ff jmpq 4003f0 <_init+0x28>

  25. 0000000000400410 <__libc_start_main@plt>:
  26.   400410: ff 25 0a 0c 20 00 jmpq *0x200c0a(%rip) # 601020 <_GLOBAL_OFFSET_TABLE_+0x20>
  27.   400416: 68 01 00 00 00 pushq $0x1
  28.   40041b: e9 d0 ff ff ff jmpq 4003f0 <_init+0x28>

  29. Disassembly of section .plt.got:

  30. 0000000000400420 <.plt.got>:
  31.   400420: ff 25 d2 0b 20 00 jmpq *0x200bd2(%rip) # 600ff8 <_DYNAMIC+0x1d0>
  32.   400426: 66 90 xchg %ax,%ax

  33. Disassembly of section .text:
  34. 0000000000400430 <_start>:
  35.   400430: 31 ed xor %ebp,%ebp
  36.   400432: 49 89 d1 mov %rdx,%r9
  37.   400435: 5e pop %rsi
  38.   400436: 48 89 e2 mov %rsp,%rdx
  39.   400439: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
  40.   40043d: 50 push %rax
  41.   40043e: 54 push %rsp
  42.   40043f: 49 c7 c0 c0 05 40 00 mov $0x4005c0,%r8
  43.   400446: 48 c7 c1 50 05 40 00 mov $0x400550,%rcx
  44.   40044d: 48 c7 c7 26 05 40 00 mov $0x400526,%rdi
  45.   400454: e8 b7 ff ff ff callq 400410 <__libc_start_main@plt>
  46.   400459: f4 hlt
  47.   40045a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)

  48. 0000000000400460 <deregister_tm_clones>:
  49.   400460: b8 3f 10 60 00 mov $0x60103f,%eax
  50.   400465: 55 push %rbp
  51.   400466: 48 2d 38 10 60 00 sub $0x601038,%rax
  52.   40046c: 48 83 f8 0e cmp $0xe,%rax
  53.   400470: 48 89 e5 mov %rsp,%rbp
  54.   400473: 76 1b jbe 400490 <deregister_tm_clones+0x30>
  55.   400475: b8 00 00 00 00 mov $0x0,%eax
  56.   40047a: 48 85 c0 test %rax,%rax
  57.   40047d: 74 11 je 400490 <deregister_tm_clones+0x30>
  58.   40047f: 5d pop %rbp
  59.   400480: bf 38 10 60 00 mov $0x601038,%edi
  60.   400485: ff e0 jmpq *%rax
  61.   400487: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
  62.   40048e: 00 00
  63.   400490: 5d pop %rbp
  64.   400491: c3 retq
  65.   400492: 0f 1f 40 00 nopl 0x0(%rax)
  66.   400496: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
  67.   40049d: 00 00 00

  68. 00000000004004a0 <register_tm_clones>:
  69.   4004a0: be 38 10 60 00 mov $0x601038,%esi
  70.   4004a5: 55 push %rbp
  71.   4004a6: 48 81 ee 38 10 60 00 sub $0x601038,%rsi
  72.   4004ad: 48 c1 fe 03 sar $0x3,%rsi
  73.   4004b1: 48 89 e5 mov %rsp,%rbp
  74.   4004b4: 48 89 f0 mov %rsi,%rax
  75.   4004b7: 48 c1 e8 3f shr $0x3f,%rax
  76.   4004bb: 48 01 c6 add %rax,%rsi
  77.   4004be: 48 d1 fe sar %rsi
  78.   4004c1: 74 15 je 4004d8 <register_tm_clones+0x38>
  79.   4004c3: b8 00 00 00 00 mov $0x0,%eax
  80.   4004c8: 48 85 c0 test %rax,%rax
  81.   4004cb: 74 0b je 4004d8 <register_tm_clones+0x38>
  82.   4004cd: 5d pop %rbp
  83.   4004ce: bf 38 10 60 00 mov $0x601038,%edi
  84.   4004d3: ff e0 jmpq *%rax
  85.   4004d5: 0f 1f 00 nopl (%rax)
  86.   4004d8: 5d pop %rbp
  87.   4004d9: c3 retq
  88.   4004da: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)

  89. 00000000004004e0 <__do_global_dtors_aux>:
  90.   4004e0: 80 3d 51 0b 20 00 00 cmpb $0x0,0x200b51(%rip) # 601038 <__TMC_END__>
  91.   4004e7: 75 11 jne 4004fa <__do_global_dtors_aux+0x1a>
  92.   4004e9: 55 push %rbp
  93.   4004ea: 48 89 e5 mov %rsp,%rbp
  94.   4004ed: e8 6e ff ff ff callq 400460 <deregister_tm_clones>
  95.   4004f2: 5d pop %rbp
  96.   4004f3: c6 05 3e 0b 20 00 01 movb $0x1,0x200b3e(%rip) # 601038 <__TMC_END__>
  97.   4004fa: f3 c3 repz retq
  98.   4004fc: 0f 1f 40 00 nopl 0x0(%rax)

  99. 0000000000400500 <frame_dummy>:
  100.   400500: bf 20 0e 60 00 mov $0x600e20,%edi
  101.   400505: 48 83 3f 00 cmpq $0x0,(%rdi)
  102.   400509: 75 05 jne 400510 <frame_dummy+0x10>
  103.   40050b: eb 93 jmp 4004a0 <register_tm_clones>
  104.   40050d: 0f 1f 00 nopl (%rax)
  105.   400510: b8 00 00 00 00 mov $0x0,%eax
  106.   400515: 48 85 c0 test %rax,%rax
  107.   400518: 74 f1 je 40050b <frame_dummy+0xb>
  108.   40051a: 55 push %rbp
  109.   40051b: 48 89 e5 mov %rsp,%rbp
  110.   40051e: ff d0 callq *%rax
  111.   400520: 5d pop %rbp
  112.   400521: e9 7a ff ff ff jmpq 4004a0 <register_tm_clones>

  113. 0000000000400526 <main>:
  114.   400526: 55 push %rbp
  115.   400527: 48 89 e5 mov %rsp,%rbp
  116.   40052a: 48 83 ec 10 sub $0x10,%rsp
  117.   40052e: 89 7d fc mov %edi,-0x4(%rbp)
  118.   400531: 48 89 75 f0 mov %rsi,-0x10(%rbp)
  119.   400535: bf d4 05 40 00 mov $0x4005d4,%edi
  120.   40053a: e8 c1 fe ff ff callq 400400 <puts@plt>
  121.   40053f: b8 00 00 00 00 mov $0x0,%eax
  122.   400544: c9 leaveq
  123.   400545: c3 retq
  124.   400546: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
  125.   40054d: 00 00 00

  126. 0000000000400550 <__libc_csu_init>:
  127.   400550: 41 57 push %r15
  128.   400552: 41 56 push %r14
  129.   400554: 41 89 ff mov %edi,%r15d
  130.   400557: 41 55 push %r13
  131.   400559: 41 54 push %r12
  132.   40055b: 4c 8d 25 ae 08 20 00 lea 0x2008ae(%rip),%r12 # 600e10 <__frame_dummy_init_array_entry>
  133.   400562: 55 push %rbp
  134.   400563: 48 8d 2d ae 08 20 00 lea 0x2008ae(%rip),%rbp # 600e18 <__init_array_end>
  135.   40056a: 53 push %rbx
  136.   40056b: 49 89 f6 mov %rsi,%r14
  137.   40056e: 49 89 d5 mov %rdx,%r13
  138.   400571: 4c 29 e5 sub %r12,%rbp
  139.   400574: 48 83 ec 08 sub $0x8,%rsp
  140.   400578: 48 c1 fd 03 sar $0x3,%rbp
  141.   40057c: e8 47 fe ff ff callq 4003c8 <_init>
  142.   400581: 48 85 ed test %rbp,%rbp
  143.   400584: 74 20 je 4005a6 <__libc_csu_init+0x56>
  144.   400586: 31 db xor %ebx,%ebx
  145.   400588: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
  146.   40058f: 00
  147.   400590: 4c 89 ea mov %r13,%rdx
  148.   400593: 4c 89 f6 mov %r14,%rsi
  149.   400596: 44 89 ff mov %r15d,%edi
  150.   400599: 41 ff 14 dc callq *(%r12,%rbx,8)
  151.   40059d: 48 83 c3 01 add $0x1,%rbx
  152.   4005a1: 48 39 eb cmp %rbp,%rbx
  153.   4005a4: 75 ea jne 400590 <__libc_csu_init+0x40>
  154.   4005a6: 48 83 c4 08 add $0x8,%rsp
  155.   4005aa: 5b pop %rbx
  156.   4005ab: 5d pop %rbp
  157.   4005ac: 41 5c pop %r12
  158.   4005ae: 41 5d pop %r13
  159.   4005b0: 41 5e pop %r14
  160.   4005b2: 41 5f pop %r15
  161.   4005b4: c3 retq
  162.   4005b5: 90 nop
  163.   4005b6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
  164.   4005bd: 00 00 00

  165. 00000000004005c0 <__libc_csu_fini>:
  166.   4005c0: f3 c3 repz retq

  167. Disassembly of section .fini:

  168. 00000000004005c4 <_fini>:
  169.   4005c4: 48 83 ec 08 sub $0x8,%rsp
  170.   4005c8: 48 83 c4 08 add $0x8,%rsp
  171.   4005cc: c3 retq
如上44行, 0x400430地址存放的内容如下:

点击(此处)折叠或打开

  1. Disassembly of section .text:
  2. 0000000000400430 <_start>:
  3.   400430: 31 ed xor %ebp,%ebp
  4.   400432: 49 89 d1 mov %rdx,%r9
  5.   400435: 5e pop %rsi
  6.   400436: 48 89 e2 mov %rsp,%rdx
  7.   400439: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
  8.   40043d: 50 push %rax
  9.   40043e: 54 push %rsp
  10.   40043f: 49 c7 c0 c0 05 40 00 mov $0x4005c0,%r8
  11.   400446: 48 c7 c1 50 05 40 00 mov $0x400550,%rcx
  12.   40044d: 48 c7 c7 26 05 40 00 mov $0x400526,%rdi
  13.   400454: e8 b7 ff ff ff callq 400410 <__libc_start_main@plt>
  14.   400459: f4 hlt
  15.   40045a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
这里我们简单的科普一下objdump工具, 下图,红色框框为地址栏,黄色框框为对应地址栏上的16进制数据,蓝色框框是这个16进制的数据翻译成的汇编语言(注意objdump会尝试去翻译,但并不代表,数据一定是汇编语言,所以当发现一个地址存放的是看不懂的汇编的时候,可以考虑一下这可能仅仅是数据而已,常见每个函数的函数尾部)


现在继续正题,由地址0x400430,我们可以看出,hello文件的真正入口并不是main函数,而是我们的_start函数,当然这个入口函数和用的C库有关,所以这个_start函数实际上是由C库实现的,hello程序在ld链接的阶段,gcc会默认将crtbegin.o, crtend.o,crt1.o,crti.o,crtn.o等目标文件链接到hello当中,这其中就包括_start函数的内容。而链接的地址有链接脚本而定,通常我们采用默认的链接脚本,这个脚本可以通过ld --verbose查看。

_start函数具体的情况大家可以下载C库来找到具体的代码实现,这里简单看看汇编,可以了解到_start函数最后调用了__libc_start_main函数,并向__libc_start_main函数传入了3个参数,0x4005c0,0x400550,0x400526;这三个地址上所存放的内容是:
0x4005c0:__libc_csu_fini函数实现
0x400550:__libc_csu_init函数实现
0x400526:main函数实现

由于__libc_start_main的具体实现在动态库中,libc.so当中,因此,我们在hello.s中是无法看到它的具体实现的。__libc_start_main@plt表示这是一个延迟加载函数,什么是延迟加载函数呢?延迟加载函数就是指在动态解释阶段不进行代码重定位,只有在真正使用该函数的时候,才去定位该函数的地址, 这样做的目的是加快程序启动,常见就是很多大型游戏,存在大量动态库的时候, 解析会很耗费启动时间。这里__libc_start_main函数的作用就是把传入的这三个函数分别运行,运行的顺序为:__libc_csu_init->main->__libc_csu_fini。
__libc_csu_init函数,主要完成一些构造函数相关的内容。是的,C语言也有构造函数。

我们可以通过在函数上添加__attribute__ ((constructor)),来标记函数为C程序的构造函数,用__attribute__ ((destructor))来标记对应函数为析构函数,如:

点击(此处)折叠或打开

  1. #include <stdio.h>

  2. static void hello_after() __attribute__ ((destructor));
  3. static void hello_before() __attribute__ ((constructor));

  4. static void hello_before(void)
  5. {
  6.     printf("Before main\n");
  7. }

  8. static void hello_after(void)
  9. {
  10.     printf("After main\n");
  11. }

  12. int main (int argc, char *argv[])
  13. {
  14.     printf ("Hello World\n");

  15.     return 0;
  16. }

点击(此处)折叠或打开

  1. $ ./hello
  2. Before main
  3. Hello World
  4. After main


因此,我们通过分析能很清楚的分析到,C语言的构造函数在main函数运行之前运行,析构函数在main函数运行之后运行。

__libc_csu_init的实现汇编如下

点击(此处)折叠或打开

  1. 0000000000400550 <__libc_csu_init>:
  2.   400550: 41 57 push %r15
  3.   400552: 41 56 push %r14
  4.   400554: 41 89 ff mov %edi,%r15d
  5.   400557: 41 55 push %r13
  6.   400559: 41 54 push %r12
  7.   40055b: 4c 8d 25 ae 08 20 00 lea 0x2008ae(%rip),%r12 # 600e10 <__frame_dummy_init_array_entry>
  8.   400562: 55 push %rbp
  9.   400563: 48 8d 2d ae 08 20 00 lea 0x2008ae(%rip),%rbp # 600e18 <__init_array_end>
  10.   40056a: 53 push %rbx
  11.   40056b: 49 89 f6 mov %rsi,%r14
  12.   40056e: 49 89 d5 mov %rdx,%r13
  13.   400571: 4c 29 e5 sub %r12,%rbp
  14.   400574: 48 83 ec 08 sub $0x8,%rsp
  15.   400578: 48 c1 fd 03 sar $0x3,%rbp
  16.   40057c: e8 47 fe ff ff callq 4003c8 <_init>
  17.   400581: 48 85 ed test %rbp,%rbp
  18.   400584: 74 20 je 4005a6 <__libc_csu_init+0x56>
  19.   400586: 31 db xor %ebx,%ebx
  20.   400588: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
  21.   40058f: 00
  22.   400590: 4c 89 ea mov %r13,%rdx
  23.   400593: 4c 89 f6 mov %r14,%rsi
  24.   400596: 44 89 ff mov %r15d,%edi
  25.   400599: 41 ff 14 dc callq *(%r12,%rbx,8)
  26.   40059d: 48 83 c3 01 add $0x1,%rbx
  27.   4005a1: 48 39 eb cmp %rbp,%rbx
  28.   4005a4: 75 ea jne 400590 <__libc_csu_init+0x40>
  29.   4005a6: 48 83 c4 08 add $0x8,%rsp
  30.   4005aa: 5b pop %rbx
  31.   4005ab: 5d pop %rbp
  32.   4005ac: 41 5c pop %r12
  33.   4005ae: 41 5d pop %r13
  34.   4005b0: 41 5e pop %r14
  35.   4005b2: 41 5f pop %r15
  36.   4005b4: c3 retq
  37.   4005b5: 90 nop
  38.   4005b6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
  39.   4005bd: 00 00 00
main函数实现由用户实现,这里就不多说了,我们这里只是打印了一个Hello World


点击(此处)折叠或打开

  1. 0000000000400526 <main>:
  2.   400526: 55 push %rbp
  3.   400527: 48 89 e5 mov %rsp,%rbp
  4.   40052a: 48 83 ec 10 sub $0x10,%rsp
  5.   40052e: 89 7d fc mov %edi,-0x4(%rbp)
  6.   400531: 48 89 75 f0 mov %rsi,-0x10(%rbp)
  7.   400535: bf d4 05 40 00 mov $0x4005d4,%edi
  8.   40053a: e8 c1 fe ff ff callq 400400 <puts@plt>
  9.   40053f: b8 00 00 00 00 mov $0x0,%eax
  10.   400544: c9 leaveq
  11.   400545: c3 retq
  12.   400546: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
  13.   40054d: 00 00 00
__libc_csu_fini函数,在本例子中,基本上是空函数,这里不过多说明:

点击(此处)折叠或打开

  1. 00000000004005c0 <__libc_csu_fini>:
  2.   4005c0: f3 c3 repz retq


NOTE: Linux当中,任何应用程序的退出,都是由do_exit完成,即使是main函数主动return也是如此,最多只是封装不一样。c库中调用main函数的逻辑如下:
点击(此处)折叠或打开

  1. XXXX(....)
  2. {
  3.     result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);
  4.     exit (result);
  5. }
可见退出都是由exit函数完成,至于exit到底做了什么,陷入内核后,内核是怎么回收task_struct结构的,详细看do_exit内核分析实现(以后会分析)。

从上面的简单说明当中,我们初步的了解了程序从运行是怎么到main函数的。以上遗留的问题,会在下一篇文档给出说明:
1、execve函数内核实现,创建进程逻辑,生命的摇篮(后续讲解)
2、动态解释器工作细节(后续讲解)
3、延迟加载PLT实现细节(后续讲解)

4、do_exit怎么完成回收(后续讲解)

文中对汇编代码并没有详细描述,对于__libc_csu_fini,__libc_csu_init函数的实现,大家可以下载glibc库,去查看具体的对应实现。

注:第一次写,写的比较尴尬。


阅读(3601) | 评论(2) | 转发(1) |
2

上一篇:没有了

下一篇:Linux应用程序elf描述

给主人留下些什么吧!~~

kokozhao2019-10-15 19:16:02

期待后续分析

HongqiangXu2019-10-09 19:27:45

插眼!