我们在写程序的时候,windows下通过vs等编译软件,linux通过gcc命令得到程序的可执行文件。在这个过程中,其实大致经历了4个过程。1 预编译 2 编译 3 汇编 4 链接
1 预编译:主要处理那些源代码文件中的以”#”开头的预编译指令,比如”#include”,“#define”等。
2 编译:就是把预处理完的文件进行一系列词法分析,语法分析,语义分析以及优化后生成相应的汇编代码文件
3 汇编:就是将汇编代码转变成机器可以执行的指令。
4 链接:一个工程里面包含很多.c的文件。我们最终工程得到的是.out文件。但是这个是将所有涉及到的文件都链接起来才得到的。这个就是链接的功能。
我们用一个最常见的代码来看下这几个过程
#include <stdio.h>
int main(void){
printf("hello world");
return 0;
}
gcc main.c -o main.i生成预编译文件。通过file main.i可以查看到文件的格式是ELF。这个后面会详细介绍。
root@zhf-maple:/home/zhf/c_prj# file main.i
main.i: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=a19f9545eefb261b7ec7d27cf12702c3881c5ea7, not stripped
objdump -i main.i可以看到具体的内容。
通过gcc main.c -o main.s生成编译文件.objdump -S 可以查看具体的汇编代码
root@zhf-maple:/home/zhf/c_prj# objdump -S main.s
main.s: 文件格式 elf64-x86-64
Disassembly of section .init:
00000000000004f0 <_init>:
4f0: 48 83 ec 08 sub $0x8,%rsp
4f4: 48 8b 05 ed 0a 20 00 mov 0x200aed(%rip),%rax # 200fe8 <__gmon_start__>
4fb: 48 85 c0 test %rax,%rax
4fe: 74 02 je 502 <_init+0x12>
500: ff d0 callq *%rax
502: 48 83 c4 08 add $0x8,%rsp
506: c3 retq
Disassembly of section .plt:
0000000000000510 <.plt>:
510: ff 35 aa 0a 20 00 pushq 0x200aaa(%rip) # 200fc0 <_GLOBAL_OFFSET_TABLE_+0x8>
516: ff 25 ac 0a 20 00 jmpq *0x200aac(%rip) # 200fc8 <_GLOBAL_OFFSET_TABLE_+0x10>
51c: 0f 1f 40 00 nopl 0x0(%rax)
0000000000000520 <printf@plt>:
520: ff 25 aa 0a 20 00 jmpq *0x200aaa(%rip) # 200fd0 <printf@GLIBC_2.2.5>
526: 68 00 00 00 00 pushq $0x0
52b: e9 e0 ff ff ff jmpq 510 <.plt>
Disassembly of section .plt.got:
0000000000000530 <__cxa_finalize@plt>:
530: ff 25 c2 0a 20 00 jmpq *0x200ac2(%rip) # 200ff8 <__cxa_finalize@GLIBC_2.2.5>
536: 66 90 xchg %ax,%ax
Disassembly of section .text:
0000000000000540 <_start>:
540: 31 ed xor %ebp,%ebp
542: 49 89 d1 mov %rdx,%r9
545: 5e pop %rsi
546: 48 89 e2 mov %rsp,%rdx
549: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
54d: 50 push %rax
54e: 54 push %rsp
54f: 4c 8d 05 8a 01 00 00 lea 0x18a(%rip),%r8 # 6e0 <__libc_csu_fini>
556: 48 8d 0d 13 01 00 00 lea 0x113(%rip),%rcx # 670 <__libc_csu_init>
55d: 48 8d 3d e6 00 00 00 lea 0xe6(%rip),%rdi # 64a <main>
564: ff 15 76 0a 20 00 callq *0x200a76(%rip) # 200fe0 <__libc_start_main@GLIBC_2.2.5>
56a: f4 hlt
56b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
0000000000000570 <deregister_tm_clones>:
570: 48 8d 3d 99 0a 20 00 lea 0x200a99(%rip),%rdi # 201010 <__TMC_END__>
577: 55 push %rbp
578: 48 8d 05 91 0a 20 00 lea 0x200a91(%rip),%rax # 201010 <__TMC_END__>
57f: 48 39 f8 cmp %rdi,%rax
582: 48 89 e5 mov %rsp,%rbp
585: 74 19 je 5a0 <deregister_tm_clones+0x30>
587: 48 8b 05 4a 0a 20 00 mov 0x200a4a(%rip),%rax # 200fd8 <_ITM_deregisterTMCloneTable>
58e: 48 85 c0 test %rax,%rax
591: 74 0d je 5a0 <deregister_tm_clones+0x30>
593: 5d pop %rbp
594: ff e0 jmpq *%rax
596: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
59d: 00 00 00
5a0: 5d pop %rbp
5a1: c3 retq
5a2: 0f 1f 40 00 nopl 0x0(%rax)
5a6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
5ad: 00 00 00
00000000000005b0 <register_tm_clones>:
5b0: 48 8d 3d 59 0a 20 00 lea 0x200a59(%rip),%rdi # 201010 <__TMC_END__>
5b7: 48 8d 35 52 0a 20 00 lea 0x200a52(%rip),%rsi # 201010 <__TMC_END__>
5be: 55 push %rbp
5bf: 48 29 fe sub %rdi,%rsi
5c2: 48 89 e5 mov %rsp,%rbp
5c5: 48 c1 fe 03 sar $0x3,%rsi
5c9: 48 89 f0 mov %rsi,%rax
5cc: 48 c1 e8 3f shr $0x3f,%rax
5d0: 48 01 c6 add %rax,%rsi
5d3: 48 d1 fe sar %rsi
5d6: 74 18 je 5f0 <register_tm_clones+0x40>
5d8: 48 8b 05 11 0a 20 00 mov 0x200a11(%rip),%rax # 200ff0 <_ITM_registerTMCloneTable>
5df: 48 85 c0 test %rax,%rax
5e2: 74 0c je 5f0 <register_tm_clones+0x40>
5e4: 5d pop %rbp
5e5: ff e0 jmpq *%rax
5e7: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
5ee: 00 00
5f0: 5d pop %rbp
5f1: c3 retq
5f2: 0f 1f 40 00 nopl 0x0(%rax)
5f6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
5fd: 00 00 00
0000000000000600 <__do_global_dtors_aux>:
600: 80 3d 09 0a 20 00 00 cmpb $0x0,0x200a09(%rip) # 201010 <__TMC_END__>
607: 75 2f jne 638 <__do_global_dtors_aux+0x38>
609: 48 83 3d e7 09 20 00 cmpq $0x0,0x2009e7(%rip) # 200ff8 <__cxa_finalize@GLIBC_2.2.5>
610: 00
611: 55 push %rbp
612: 48 89 e5 mov %rsp,%rbp
615: 74 0c je 623 <__do_global_dtors_aux+0x23>
617: 48 8b 3d ea 09 20 00 mov 0x2009ea(%rip),%rdi # 201008 <__dso_handle>
61e: e8 0d ff ff ff callq 530 <__cxa_finalize@plt>
623: e8 48 ff ff ff callq 570 <deregister_tm_clones>
628: c6 05 e1 09 20 00 01 movb $0x1,0x2009e1(%rip) # 201010 <__TMC_END__>
62f: 5d pop %rbp
630: c3 retq
631: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
638: f3 c3 repz retq
63a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
0000000000000640 <frame_dummy>:
640: 55 push %rbp
641: 48 89 e5 mov %rsp,%rbp
644: 5d pop %rbp
645: e9 66 ff ff ff jmpq 5b0 <register_tm_clones>
000000000000064a <main>:
64a: 55 push %rbp
64b: 48 89 e5 mov %rsp,%rbp
64e: 48 8d 3d 9f 00 00 00 lea 0x9f(%rip),%rdi # 6f4 <_IO_stdin_used+0x4>
655: b8 00 00 00 00 mov $0x0,%eax
65a: e8 c1 fe ff ff callq 520 <printf@plt>
65f: b8 00 00 00 00 mov $0x0,%eax
664: 5d pop %rbp
665: c3 retq
666: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
66d: 00 00 00
0000000000000670 <__libc_csu_init>:
670: 41 57 push %r15
672: 41 56 push %r14
674: 41 89 ff mov %edi,%r15d
677: 41 55 push %r13
679: 41 54 push %r12
67b: 4c 8d 25 36 07 20 00 lea 0x200736(%rip),%r12 # 200db8 <__frame_dummy_init_array_entry>
682: 55 push %rbp
683: 48 8d 2d 36 07 20 00 lea 0x200736(%rip),%rbp # 200dc0 <__init_array_end>
68a: 53 push %rbx
68b: 49 89 f6 mov %rsi,%r14
68e: 49 89 d5 mov %rdx,%r13
691: 4c 29 e5 sub %r12,%rbp
694: 48 83 ec 08 sub $0x8,%rsp
698: 48 c1 fd 03 sar $0x3,%rbp
69c: e8 4f fe ff ff callq 4f0 <_init>
6a1: 48 85 ed test %rbp,%rbp
6a4: 74 20 je 6c6 <__libc_csu_init+0x56>
6a6: 31 db xor %ebx,%ebx
6a8: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
6af: 00
6b0: 4c 89 ea mov %r13,%rdx
6b3: 4c 89 f6 mov %r14,%rsi
6b6: 44 89 ff mov %r15d,%edi
6b9: 41 ff 14 dc callq *(%r12,%rbx,8)
6bd: 48 83 c3 01 add $0x1,%rbx
6c1: 48 39 dd cmp %rbx,%rbp
6c4: 75 ea jne 6b0 <__libc_csu_init+0x40>
6c6: 48 83 c4 08 add $0x8,%rsp
6ca: 5b pop %rbx
6cb: 5d pop %rbp
6cc: 41 5c pop %r12
6ce: 41 5d pop %r13
6d0: 41 5e pop %r14
6d2: 41 5f pop %r15
6d4: c3 retq
6d5: 90 nop
6d6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
6dd: 00 00 00
00000000000006e0 <__libc_csu_fini>:
6e0: f3 c3 repz retq
Disassembly of section .fini:
00000000000006e4 <_fini>:
6e4: 48 83 ec 08 sub $0x8,%rsp
6e8: 48 83 c4 08 add $0x8,%rsp
6ec: c3 retq
.o文件的格式如下。分为.text/.data/.bss/.comment/.note.GNU-stack/.eh_frame 5个段。
root@zhf-maple:/home/zhf/c_prj# objdump -h main.o
main.o: 文件格式 elf64-x86-64
节:
Idx Name Size VMA LMA File off Algn
0 .text 00000015 0000000000000000 0000000000000000 00000040 2**0
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .data 00000000 0000000000000000 0000000000000000 00000055 2**0
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 0000000000000000 0000000000000000 00000055 2**0
ALLOC
3 .comment 00000024 0000000000000000 0000000000000000 00000055 2**0
CONTENTS, READONLY
4 .note.GNU-stack 00000000 0000000000000000 0000000000000000 00000079 2**0
CONTENTS, READONLY
5 .eh_frame 00000038 0000000000000000 0000000000000000 00000080 2**3
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
.o文件又称为目标文件。在linux下目标文件的格式为ELF。ELF格式的文件可以归纳为下面4类
ELF文件类型 |
说明 |
实例 |
可重定位文件(Relocatable File) |
包含代码和数据,可以被用来链接成可执行文件或共享目标文件 |
.o文件 |
可执行文件 |
包含了可以直接执行的程序,代表就是ELF可执行文件 |
比如/bin/bash文件windows的exe文件 |
共享目标文件 |
包含代码和数据,2种情况下使用 1 链接器可以使用这种文件跟其他的可重定位文件和共享文件链接产生新的目标文件 2 动态链接将几个这种共享目标文件与可执行文件结合,做和进程映射的一部分来运行 |
linux的.so windows的dll |
核心转储文件 |
当进程意外终止时,系统可以将该进程的地址空间的内容及终止时的一些其他信息转储到核心文件 |
linux下的core dump |
通过file命令查看相应的文件格式
root@zhf-maple:/home/zhf/c_prj# file main.o
main.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
前面在用objdum -h main.o的时候看到了有
text/.data/.bss/.comment/.note.GNU-stack/.eh_frame 5个段。那么这些段的意义是什么呢。代码段最常见的就是.text和.data。 作用如下
1 一般c语言编译后的执行语句都编译成机器代码,保存在.text段
2 已初始化的全局变量和局部静态变量都保存在.data段
3 未初始化的全局变量和局部静态变量放在.bss段里面
ELF文件分别由4部分组成:1 ELF头,2 程序头表,3 节section,4 节头表。如下图。
通过readelf -a main.o命令来看到具体的信息。比如在ELF头中描述了文件属性,入口地址,程序头地址,操作系统等。
ELF 头:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
类别: ELF64
数据: 2 补码,小端序 (little endian)
版本: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
类型: DYN (共享目标文件)
系统架构: Advanced Micro Devices X86-64
版本: 0x1
入口点地址: 0x540
程序头起点: 64 (bytes into file)
Start of section headers: 6592 (bytes into file)
标志: 0x0
本头的大小: 64 (字节)
程序头大小: 56 (字节)
Number of program headers: 9
节头大小: 64 (字节)
节头数量: 29
字符串表索引节头: 28
节头中能看到对应的sectioni信息
节头:
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000000238 00000238
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000000254 00000254
0000000000000020 0000000000000000 A 0 0 4
[ 3] .note.gnu.build-i NOTE 0000000000000274 00000274
0000000000000024 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000000298 00000298
000000000000001c 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 00000000000002b8 000002b8
00000000000000a8 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 0000000000000360 00000360
0000000000000084 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 00000000000003e4 000003e4
000000000000000e 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 00000000000003f8 000003f8
0000000000000020 0000000000000000 A 6 1 8
[ 9] .rela.dyn RELA 0000000000000418 00000418
00000000000000c0 0000000000000018 A 5 0 8
[10] .rela.plt RELA 00000000000004d8 000004d8
0000000000000018 0000000000000018 AI 5 22 8
[11] .init PROGBITS 00000000000004f0 000004f0
0000000000000017 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 0000000000000510 00000510
0000000000000020 0000000000000010 AX 0 0 16
[13] .plt.got PROGBITS 0000000000000530 00000530
0000000000000008 0000000000000008 AX 0 0 8
[14] .text PROGBITS 0000000000000540 00000540
00000000000001a2 0000000000000000 AX 0 0 16
[15] .fini PROGBITS 00000000000006e4 000006e4
0000000000000009 0000000000000000 AX 0 0 4
[16] .rodata PROGBITS 00000000000006f0 000006f0
0000000000000010 0000000000000000 A 0 0 4
[17] .eh_frame_hdr PROGBITS 0000000000000700 00000700
000000000000003c 0000000000000000 A 0 0 4
[18] .eh_frame PROGBITS 0000000000000740 00000740
0000000000000108 0000000000000000 A 0 0 8
[19] .init_array INIT_ARRAY 0000000000200db8 00000db8
0000000000000008 0000000000000008 WA 0 0 8
[20] .fini_array FINI_ARRAY 0000000000200dc0 00000dc0
0000000000000008 0000000000000008 WA 0 0 8
[21] .dynamic DYNAMIC 0000000000200dc8 00000dc8
00000000000001f0 0000000000000010 WA 6 0 8
[22] .got PROGBITS 0000000000200fb8 00000fb8
0000000000000048 0000000000000008 WA 0 0 8
[23] .data PROGBITS 0000000000201000 00001000
0000000000000014 0000000000000000 WA 0 0 8
[24] .bss NOBITS 0000000000201014 00001014
0000000000000014 0000000000000000 WA 0 0 4
[25] .comment PROGBITS 0000000000000000 00001014
0000000000000023 0000000000000001 MS 0 0 1
[26] .symtab SYMTAB 0000000000000000 00001038
0000000000000648 0000000000000018 27 45 8
[27] .strtab STRTAB 0000000000000000 00001680
0000000000000240 0000000000000000 0 0 1
[28] .shstrtab STRTAB 0000000000000000 000018c0
00000000000000fe 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
下一章将继续挖掘目标文件的具体内容。