目录
导读
一.预编译
二.编译
三.汇编
四.链接
五.扩展
六.例子
生成.o目标文件(编译间段)
查看符号表信息(编译间段)
.o目标文件的文件格式
查看.o文件的段头信息(编译间段)
查看汇编代码(编译间段)
查看链接后文件的符号表(链接间段)
查看汇编代码(链接间段)
查看段头信息(链接间段)
查看段表信息(链接间段)
可执行文件的加载过程
相信大家对下列这段代码都不陌生
a.c
#include
int main()
{
printf("Hello World!\n");
return 0;
}
在Linux中,当我们使用gcc来编译这个程序时,只需使用最简单的命令
gcc -o a a.c
实际上,上述过程可以分解为4个步骤,分别是预处理(又可以被称为预编译),编译,汇编,链接。
用命令来体现就是如下四个步骤:
预处理 gcc -E a.c -o a.i
编译 gcc -s a.i -o a.s
汇编 gcc -c a.s -o a.o
链接 gcc a.o -o a//gcc -o a a.o
预编译过程主要处理那些源代码文件中的以“#”开始的预编译指令,比如“#include”,“#define”等,主要处理规则如下:
编译过程就是把预处理完的文件进行一系列词法分析,语法分析,语义分析,代码优化及优化后生成相应的汇编代码文件。
代码优化的优缺点:
汇编过程就是由汇编器将汇编代码转变成机器可以执行的二进制指令。
链接的主要内存就是把各个模块之间相互引用长度部分都处理好,使得各个模块之间能够正确的衔接。简单的理解为将各个目标文件链接起来生成最终的可执行文件。
链接过程可以具体的分为以下四步:
链接又分为
目标文件就是源代码经过编译后但未进行链接的那些中间文件。Linux下的 .o文件就是目标文件,目标文件和可执行文件内容和
格式几乎都一样,所以我们可以广义地将目标文件和可执行文化看成一类型文件。
目标文件除了含有汇编后的机器指令代码,数据外,还包括了链接时所需要的一些信息,比如符号表,调试信息,字符串等。一般目标文件将这些信息按不同的属性,以“节”的形式存储,有时候也叫做“段”。
程序源代码编译后的机器指令经常被放在.text段,已初始化的全局变量和局部静态变量都保存在.data段,未初始化的全局变量和局部静态变量一般放在.bss段。注意,.bss段只是为未初始化的全局变量和局部静态变量预留位置而已,它并没有内容,所以它在文件中也不占据空间。.rodata段存放的是只读数据。
现在有上述三个文件,在这里需要注意,我们定义全局变量gdata时,不能将它定义在 sum.h文件中,否则会引起gdata的多重定义,而是应该将它定义在sum.c中,并且当我们在main.cpp中引用gdata时,应该加上 extern int gdata这一句。extern是计算机语言中的一个关键字,可置于变量或者函数前,以表示变量或者函数的定义在别的文件中。提示编译器遇到此变量或函数时,在其它模块中寻找其定义,另外,extern也可用来进行链接指定。
gcc -c sum.c
gcc -c main.c
执行后,就会生成sum.o 和 main.o
objdump -t main.o
objdump -t sum-o
来看一下这两个.o文件中的段信息。如下
main.o: 文件格式 elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 main.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .rodata 0000000000000000 .rodata
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 g O .data 0000000000000004 data
0000000000000000 g F .text 0000000000000049 main
0000000000000000 *UND* 0000000000000000 gdata
0000000000000000 *UND* 0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000000000 *UND* 0000000000000000 sum
0000000000000000 *UND* 0000000000000000 printf
sum.o: 文件格式 elf64-x86-64
SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 sum.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 g O .data 0000000000000004 gdata
0000000000000000 g F .text 0000000000000014 sum
关于objdump命令:
objdump -h xxxx.o
打印主要段的信息
objdump -x xxxx.o
打印更多的详细信息
objdump -s xxx.o
将所有段的内容以16进制方式打印出来
objdump -d xxx.o 或者-S
将所有包含指令的段反汇编
objdump -t xxx.o
查看所有的符号以及他们所在段
readelf -h xxx.o
查看.o文件的文件头详细信息
readelf -S xxx.o
显示.o文件中的所有段,即查看段表
size xxx.o
查看.o文件中各个段所占大小
nm xxx.o
查看.o文件中所有的符号
我们看到虽然gdata和sum函数并不是在main.c中定义的,但编译后的main.o也产生了他们的符号,main.o符号表中*gdata
与_Z3sumii
都是*UND*
未定义,表明未在本文件中定义,引用于其他文件。第二列的 l (local)表示只能在当前文件中可见,g(global)表示其他文件中可见。编译过程中符号是不分配虚拟地址的,只有在进行链接后(进行完符号解析后)才会给符号表分配虚拟地址。
readelf -h main.o
readelf -h sum.o
结果如下:
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
类型: REL (可重定位文件)
系统架构: Advanced Micro Devices X86-64
版本: 0x1
入口点地址: 0x0
程序头起点: 0 (bytes into file)
Start of section headers: 920 (bytes into file)
标志: 0x0
本头的大小: 64 (字节)
程序头大小: 0 (字节)
Number of program headers: 0
节头大小: 64 (字节)
节头数量: 13
字符串表索引节头: 12
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
类型: REL (可重定位文件)
系统架构: Advanced Micro Devices X86-64
版本: 0x1
入口点地址: 0x0
程序头起点: 0 (bytes into file)
Start of section headers: 568 (bytes into file)
标志: 0x0
本头的大小: 64 (字节)
程序头大小: 0 (字节)
Number of program headers: 0
节头大小: 64 (字节)
节头数量: 11
字符串表索引节头: 10
objdump -S main.o
结果如下
main.o: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000000000 :
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 10 sub $0x10,%rsp
8: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # e
e: 89 45 fc mov %eax,-0x4(%rbp)
11: 8b 05 00 00 00 00 mov 0x0(%rip),%eax # 17
17: 89 45 f8 mov %eax,-0x8(%rbp)
1a: 8b 55 f8 mov -0x8(%rbp),%edx
1d: 8b 45 fc mov -0x4(%rbp),%eax
20: 89 d6 mov %edx,%esi
22: 89 c7 mov %eax,%edi
24: e8 00 00 00 00 callq 29
29: 89 45 f4 mov %eax,-0xc(%rbp)
2c: 8b 45 f4 mov -0xc(%rbp),%eax
2f: 89 c6 mov %eax,%esi
31: 48 8d 3d 00 00 00 00 lea 0x0(%rip),%rdi # 38
38: b8 00 00 00 00 mov $0x0,%eax
3d: e8 00 00 00 00 callq 42
42: b8 00 00 00 00 mov $0x0,%eax
47: c9 leaveq
48: c3 retq
我们可以看到其中的多条指令显示,他们在执行时对 0x0 地址进行了相关操作,很明显这两句指令不可能执行成功,原因就在于该处指令中用到的符号地址不确定,暂时用 0x0 代替。而在链接后通过符号重定位这一步可以把这些暂时不确定的符号地址重新改写为确定的地址。
gcc -o main main.o sum.o
objdump -t main
main: 文件格式 elf64-x86-64
SYMBOL TABLE:
0000000000000238 l d .interp 0000000000000000 .interp
0000000000000254 l d .note.ABI-tag 0000000000000000 .note.ABI-tag
0000000000000274 l d .note.gnu.build-id 0000000000000000 .note.gnu.build-id
0000000000000298 l d .gnu.hash 0000000000000000 .gnu.hash
00000000000002b8 l d .dynsym 0000000000000000 .dynsym
0000000000000378 l d .dynstr 0000000000000000 .dynstr
0000000000000410 l d .gnu.version 0000000000000000 .gnu.version
0000000000000420 l d .gnu.version_r 0000000000000000 .gnu.version_r
0000000000000440 l d .rela.dyn 0000000000000000 .rela.dyn
0000000000000518 l d .rela.plt 0000000000000000 .rela.plt
0000000000000530 l d .init 0000000000000000 .init
0000000000000550 l d .plt 0000000000000000 .plt
0000000000000570 l d .plt.got 0000000000000000 .plt.got
0000000000000580 l d .text 0000000000000000 .text
0000000000000784 l d .fini 0000000000000000 .fini
0000000000000790 l d .rodata 0000000000000000 .rodata
0000000000000798 l d .eh_frame_hdr 0000000000000000 .eh_frame_hdr
00000000000007e0 l d .eh_frame 0000000000000000 .eh_frame
0000000000200dd8 l d .init_array 0000000000000000 .init_array
0000000000200de0 l d .fini_array 0000000000000000 .fini_array
0000000000200de8 l d .jcr 0000000000000000 .jcr
0000000000200df0 l d .dynamic 0000000000000000 .dynamic
0000000000200fd0 l d .got 0000000000000000 .got
0000000000201000 l d .got.plt 0000000000000000 .got.plt
0000000000201020 l d .data 0000000000000000 .data
0000000000201038 l d .bss 0000000000000000 .bss
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 l df *ABS* 0000000000000000 crtstuff.c
0000000000200de8 l O .jcr 0000000000000000 __JCR_LIST__
00000000000005b0 l F .text 0000000000000000 deregister_tm_clones
00000000000005f0 l F .text 0000000000000000 register_tm_clones
0000000000000640 l F .text 0000000000000000 __do_global_dtors_aux
0000000000201038 l O .bss 0000000000000001 completed.6972
0000000000200de0 l O .fini_array 0000000000000000 __do_global_dtors_aux_fini_array_entry
0000000000000680 l F .text 0000000000000000 frame_dummy
0000000000200dd8 l O .init_array 0000000000000000 __frame_dummy_init_array_entry
0000000000000000 l df *ABS* 0000000000000000 main.c
0000000000000000 l df *ABS* 0000000000000000 sum.c
0000000000000000 l df *ABS* 0000000000000000 crtstuff.c
0000000000000908 l O .eh_frame 0000000000000000 __FRAME_END__
0000000000200de8 l O .jcr 0000000000000000 __JCR_END__
0000000000000000 l df *ABS* 0000000000000000
0000000000200de0 l .init_array 0000000000000000 __init_array_end
0000000000200df0 l O .dynamic 0000000000000000 _DYNAMIC
0000000000200dd8 l .init_array 0000000000000000 __init_array_start
0000000000000798 l .eh_frame_hdr 0000000000000000 __GNU_EH_FRAME_HDR
0000000000201000 l O .got.plt 0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000000780 g F .text 0000000000000002 __libc_csu_fini
0000000000000000 w *UND* 0000000000000000 _ITM_deregisterTMCloneTable
0000000000201020 w .data 0000000000000000 data_start
0000000000201034 g O .data 0000000000000004 gdata
0000000000201038 g .data 0000000000000000 _edata
0000000000000784 g F .fini 0000000000000000 _fini
0000000000000000 F *UND* 0000000000000000 printf@@GLIBC_2.2.5
0000000000000000 F *UND* 0000000000000000 __libc_start_main@@GLIBC_2.2.5
0000000000201020 g .data 0000000000000000 __data_start
0000000000000000 w *UND* 0000000000000000 __gmon_start__
0000000000201028 g O .data 0000000000000000 .hidden __dso_handle
00000000000006f9 g F .text 0000000000000014 sum
0000000000000790 g O .rodata 0000000000000004 _IO_stdin_used
0000000000201030 g O .data 0000000000000004 data
0000000000000710 g F .text 0000000000000065 __libc_csu_init
0000000000201040 g .bss 0000000000000000 _end
0000000000000580 g F .text 000000000000002b _start
0000000000201038 g .bss 0000000000000000 __bss_start
00000000000006b0 g F .text 0000000000000049 main
0000000000000000 w *UND* 0000000000000000 _Jv_RegisterClasses
0000000000201038 g O .data 0000000000000000 .hidden __TMC_END__
0000000000000000 w *UND* 0000000000000000 _ITM_registerTMCloneTable
0000000000000000 w F *UND* 0000000000000000 __cxa_finalize@@GLIBC_2.2.5
0000000000000530 g F .init 0000000000000000 _init
可以看到此时所有的符号都已经分配了虚拟地址。并且已经进行了符号解析。
objdump -S main
main: 文件格式 elf64-x86-64
Disassembly of section .init:
0000000000000530 <_init>:
530: 48 83 ec 08 sub $0x8,%rsp
534: 48 8b 05 a5 0a 20 00 mov 0x200aa5(%rip),%rax # 200fe0 <__gmon_start__>
53b: 48 85 c0 test %rax,%rax
53e: 74 02 je 542 <_init+0x12>
540: ff d0 callq *%rax
542: 48 83 c4 08 add $0x8,%rsp
546: c3 retq
Disassembly of section .plt:
0000000000000550 <.plt>:
550: ff 35 b2 0a 20 00 pushq 0x200ab2(%rip) # 201008 <_GLOBAL_OFFSET_TABLE_+0x8>
556: ff 25 b4 0a 20 00 jmpq *0x200ab4(%rip) # 201010 <_GLOBAL_OFFSET_TABLE_+0x10>
55c: 0f 1f 40 00 nopl 0x0(%rax)
0000000000000560 :
560: ff 25 b2 0a 20 00 jmpq *0x200ab2(%rip) # 201018
566: 68 00 00 00 00 pushq $0x0
56b: e9 e0 ff ff ff jmpq 550 <.plt>
Disassembly of section .plt.got:
0000000000000570 <.plt.got>:
570: ff 25 82 0a 20 00 jmpq *0x200a82(%rip) # 200ff8 <__cxa_finalize@GLIBC_2.2.5>
576: 66 90 xchg %ax,%ax
Disassembly of section .text:
0000000000000580 <_start>:
580: 31 ed xor %ebp,%ebp
582: 49 89 d1 mov %rdx,%r9
585: 5e pop %rsi
586: 48 89 e2 mov %rsp,%rdx
589: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
58d: 50 push %rax
58e: 54 push %rsp
58f: 4c 8d 05 ea 01 00 00 lea 0x1ea(%rip),%r8 # 780 <__libc_csu_fini>
596: 48 8d 0d 73 01 00 00 lea 0x173(%rip),%rcx # 710 <__libc_csu_init>
59d: 48 8d 3d 0c 01 00 00 lea 0x10c(%rip),%rdi # 6b0
5a4: ff 15 2e 0a 20 00 callq *0x200a2e(%rip) # 200fd8 <__libc_start_main@GLIBC_2.2.5>
5aa: f4 hlt
5ab: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
00000000000005b0 :
5b0: 48 8d 3d 81 0a 20 00 lea 0x200a81(%rip),%rdi # 201038 <__TMC_END__>
5b7: 48 8d 05 81 0a 20 00 lea 0x200a81(%rip),%rax # 20103f <__TMC_END__+0x7>
5be: 55 push %rbp
5bf: 48 29 f8 sub %rdi,%rax
5c2: 48 89 e5 mov %rsp,%rbp
5c5: 48 83 f8 0e cmp $0xe,%rax
5c9: 76 15 jbe 5e0
5cb: 48 8b 05 fe 09 20 00 mov 0x2009fe(%rip),%rax # 200fd0 <_ITM_deregisterTMCloneTable>
5d2: 48 85 c0 test %rax,%rax
5d5: 74 09 je 5e0
5d7: 5d pop %rbp
5d8: ff e0 jmpq *%rax
5da: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
5e0: 5d pop %rbp
5e1: c3 retq
5e2: 0f 1f 40 00 nopl 0x0(%rax)
5e6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
5ed: 00 00 00
00000000000005f0 :
5f0: 48 8d 3d 41 0a 20 00 lea 0x200a41(%rip),%rdi # 201038 <__TMC_END__>
5f7: 48 8d 35 3a 0a 20 00 lea 0x200a3a(%rip),%rsi # 201038 <__TMC_END__>
5fe: 55 push %rbp
5ff: 48 29 fe sub %rdi,%rsi
602: 48 89 e5 mov %rsp,%rbp
605: 48 c1 fe 03 sar $0x3,%rsi
609: 48 89 f0 mov %rsi,%rax
60c: 48 c1 e8 3f shr $0x3f,%rax
610: 48 01 c6 add %rax,%rsi
613: 48 d1 fe sar %rsi
616: 74 18 je 630
618: 48 8b 05 d1 09 20 00 mov 0x2009d1(%rip),%rax # 200ff0 <_ITM_registerTMCloneTable>
61f: 48 85 c0 test %rax,%rax
622: 74 0c je 630
624: 5d pop %rbp
625: ff e0 jmpq *%rax
627: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
62e: 00 00
630: 5d pop %rbp
631: c3 retq
632: 0f 1f 40 00 nopl 0x0(%rax)
636: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
63d: 00 00 00
0000000000000640 <__do_global_dtors_aux>:
640: 80 3d f1 09 20 00 00 cmpb $0x0,0x2009f1(%rip) # 201038 <__TMC_END__>
647: 75 27 jne 670 <__do_global_dtors_aux+0x30>
649: 48 83 3d a7 09 20 00 cmpq $0x0,0x2009a7(%rip) # 200ff8 <__cxa_finalize@GLIBC_2.2.5>
650: 00
651: 55 push %rbp
652: 48 89 e5 mov %rsp,%rbp
655: 74 0c je 663 <__do_global_dtors_aux+0x23>
657: 48 8b 3d ca 09 20 00 mov 0x2009ca(%rip),%rdi # 201028 <__dso_handle>
65e: e8 0d ff ff ff callq 570 <.plt.got>
663: e8 48 ff ff ff callq 5b0
668: 5d pop %rbp
669: c6 05 c8 09 20 00 01 movb $0x1,0x2009c8(%rip) # 201038 <__TMC_END__>
670: f3 c3 repz retq
672: 0f 1f 40 00 nopl 0x0(%rax)
676: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
67d: 00 00 00
0000000000000680 :
680: 48 8d 3d 61 07 20 00 lea 0x200761(%rip),%rdi # 200de8 <__JCR_END__>
687: 48 83 3f 00 cmpq $0x0,(%rdi)
68b: 75 0b jne 698
68d: e9 5e ff ff ff jmpq 5f0
692: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
698: 48 8b 05 49 09 20 00 mov 0x200949(%rip),%rax # 200fe8 <_Jv_RegisterClasses>
69f: 48 85 c0 test %rax,%rax
6a2: 74 e9 je 68d
6a4: 55 push %rbp
6a5: 48 89 e5 mov %rsp,%rbp
6a8: ff d0 callq *%rax
6aa: 5d pop %rbp
6ab: e9 40 ff ff ff jmpq 5f0
00000000000006b0 :
6b0: 55 push %rbp
6b1: 48 89 e5 mov %rsp,%rbp
6b4: 48 83 ec 10 sub $0x10,%rsp
6b8: 8b 05 76 09 20 00 mov 0x200976(%rip),%eax # 201034
6be: 89 45 fc mov %eax,-0x4(%rbp)
6c1: 8b 05 69 09 20 00 mov 0x200969(%rip),%eax # 201030
6c7: 89 45 f8 mov %eax,-0x8(%rbp)
6ca: 8b 55 f8 mov -0x8(%rbp),%edx
6cd: 8b 45 fc mov -0x4(%rbp),%eax
6d0: 89 d6 mov %edx,%esi
6d2: 89 c7 mov %eax,%edi
6d4: e8 20 00 00 00 callq 6f9
6d9: 89 45 f4 mov %eax,-0xc(%rbp)
6dc: 8b 45 f4 mov -0xc(%rbp),%eax
6df: 89 c6 mov %eax,%esi
6e1: 48 8d 3d ac 00 00 00 lea 0xac(%rip),%rdi # 794 <_IO_stdin_used+0x4>
6e8: b8 00 00 00 00 mov $0x0,%eax
6ed: e8 6e fe ff ff callq 560
6f2: b8 00 00 00 00 mov $0x0,%eax
6f7: c9 leaveq
6f8: c3 retq
00000000000006f9 :
6f9: 55 push %rbp
6fa: 48 89 e5 mov %rsp,%rbp
6fd: 89 7d fc mov %edi,-0x4(%rbp)
700: 89 75 f8 mov %esi,-0x8(%rbp)
703: 8b 55 fc mov -0x4(%rbp),%edx
706: 8b 45 f8 mov -0x8(%rbp),%eax
709: 01 d0 add %edx,%eax
70b: 5d pop %rbp
70c: c3 retq
70d: 0f 1f 00 nopl (%rax)
0000000000000710 <__libc_csu_init>:
710: 41 57 push %r15
712: 41 56 push %r14
714: 41 89 ff mov %edi,%r15d
717: 41 55 push %r13
719: 41 54 push %r12
71b: 4c 8d 25 b6 06 20 00 lea 0x2006b6(%rip),%r12 # 200dd8 <__frame_dummy_init_array_entry>
722: 55 push %rbp
723: 48 8d 2d b6 06 20 00 lea 0x2006b6(%rip),%rbp # 200de0 <__init_array_end>
72a: 53 push %rbx
72b: 49 89 f6 mov %rsi,%r14
72e: 49 89 d5 mov %rdx,%r13
731: 4c 29 e5 sub %r12,%rbp
734: 48 83 ec 08 sub $0x8,%rsp
738: 48 c1 fd 03 sar $0x3,%rbp
73c: e8 ef fd ff ff callq 530 <_init>
741: 48 85 ed test %rbp,%rbp
744: 74 20 je 766 <__libc_csu_init+0x56>
746: 31 db xor %ebx,%ebx
748: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
74f: 00
750: 4c 89 ea mov %r13,%rdx
753: 4c 89 f6 mov %r14,%rsi
756: 44 89 ff mov %r15d,%edi
759: 41 ff 14 dc callq *(%r12,%rbx,8)
75d: 48 83 c3 01 add $0x1,%rbx
761: 48 39 dd cmp %rbx,%rbp
764: 75 ea jne 750 <__libc_csu_init+0x40>
766: 48 83 c4 08 add $0x8,%rsp
76a: 5b pop %rbx
76b: 5d pop %rbp
76c: 41 5c pop %r12
76e: 41 5d pop %r13
770: 41 5e pop %r14
772: 41 5f pop %r15
774: c3 retq
775: 90 nop
776: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
77d: 00 00 00
0000000000000780 <__libc_csu_fini>:
780: f3 c3 repz retq
Disassembly of section .fini:
0000000000000784 <_fini>:
784: 48 83 ec 08 sub $0x8,%rsp
788: 48 83 c4 08 add $0x8,%rsp
78c: c3 retq
我们可以看到,在 .o 文件中没有确定的地址已经被重定向至正确的地址。
readelf -h main
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
入口点地址: 0x580
程序头起点: 64 (bytes into file)
Start of section headers: 6776 (bytes into file)
标志: 0x0
本头的大小: 64 (字节)
程序头大小: 56 (字节)
Number of program headers: 9
节头大小: 64 (字节)
节头数量: 31
字符串表索引节头: 30
readelf -l main
Elf 文件类型为 DYN (共享目标文件)
入口点 0x580
共有 9 个程序头,开始于偏移量64
程序头:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
0x00000000000001f8 0x00000000000001f8 R E 0x8
INTERP 0x0000000000000238 0x0000000000000238 0x0000000000000238
0x000000000000001c 0x000000000000001c R 0x1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x000000000000090c 0x000000000000090c R E 0x200000
LOAD 0x0000000000000dd8 0x0000000000200dd8 0x0000000000200dd8
0x0000000000000260 0x0000000000000268 RW 0x200000
DYNAMIC 0x0000000000000df0 0x0000000000200df0 0x0000000000200df0
0x00000000000001e0 0x00000000000001e0 RW 0x8
NOTE 0x0000000000000254 0x0000000000000254 0x0000000000000254
0x0000000000000044 0x0000000000000044 R 0x4
GNU_EH_FRAME 0x0000000000000798 0x0000000000000798 0x0000000000000798
0x0000000000000044 0x0000000000000044 R 0x4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 0x10
GNU_RELRO 0x0000000000000dd8 0x0000000000200dd8 0x0000000000200dd8
0x0000000000000228 0x0000000000000228 R 0x1
Section to Segment mapping:
段节...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got
可以看到在程序的头部信息中,有两个 LOAD(加载器),在执行程序时分别对指令和数据进行加载,这是可执行文件和目标文件的一个区别。
在程序运行时,首先创建虚拟地址空间和物理内存的映射(创建内核映射结构体PCB),创建页目录,页表。通过两个加载器把程序的指令和数据加载至虚拟内存空间中。虚拟空间的用户程序并不直接被加载到真实的物理内存上,而是把该程序需要被访问的部分映射到真实的物理地址上,而暂时没有被访问到的部分任然存在于虚拟内存中。通过这样的方式我们发现每个程序的虚拟地址空间可以远大于实际的物理地址空间,使之可以运行远大于自身内存的程序,同时虚拟地址空间也可以小于实际的物理地址空间,使之可以同时运行多个程序。加载完指令和数据后,接着将程序的入口地址写入到下一行指令寄存器。这样,程序就可以被执行了。