生成可执行程序的四个步骤——预处理,编译,汇编,链接

目录

导读

一.预编译

二.编译

三.汇编

四.链接

五.扩展

六.例子

生成.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”等,主要处理规则如下:

  • 将所有的“#define”删除,并且展开所有的宏定义。
  • 处理所有条件预编译指令,比如“#if”,“#ifdef”,“#elif”,“#else”,“#endif”。
  • 处理“#include”预编译指令,将被包含的文件插入到该预编译指令的位置。注意,这个过程是递归进行的,也就是说被包含的文件可能还包含其他文件。
  • 删除所有的注释“//”和“/**/”。
  • 添加行号和文件名标识,比如#2“a.c”2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号。
  • 保留所有的#pragma编译器指令,因为编译器需要使用它们。

二.编译

编译过程就是把预处理完的文件进行一系列词法分析,语法分析,语义分析,代码优化及优化后生成相应的汇编代码文件。

代码优化的优缺点:

  • 优点:提高效率
  • 缺点:多线程下不可控

三.汇编

汇编过程就是由汇编器将汇编代码转变成机器可以执行的二进制指令。

四.链接

链接的主要内存就是把各个模块之间相互引用长度部分都处理好,使得各个模块之间能够正确的衔接。简单的理解为将各个目标文件链接起来生成最终的可执行文件。

链接过程可以具体的分为以下四步:

  1. 合并段和符号表,合并多个文件的符号表及各段内容,放入一个新的文件中。
  2. 符号解析,在每个文件符号引用(引用外部符号)的地方找到符号的定义。这就是符号解析。
  3. 地址和空间分配,符号解析成功后,为程序分配虚拟地址空间。
  4. 符号重定位 // 指令段,符号重定向就是对.o文件中.text段指令中的无效地址给出具体的虚拟地址或者相对位移偏移量。

链接又分为

  • 静态链接
  • 动态链接

五.扩展

目标文件就是源代码经过编译后但未进行链接的那些中间文件。Linux下的 .o文件就是目标文件,目标文件和可执行文件内容和
格式几乎都一样,所以我们可以广义地将目标文件和可执行文化看成一类型文件。

目标文件除了含有汇编后的机器指令代码,数据外,还包括了链接时所需要的一些信息,比如符号表,调试信息,字符串等。一般目标文件将这些信息按不同的属性,以“节”的形式存储,有时候也叫做“段”。

程序源代码编译后的机器指令经常被放在.text段,已初始化的全局变量和局部静态变量都保存在.data段,未初始化的全局变量和局部静态变量一般放在.bss段注意,.bss段只是为未初始化的全局变量和局部静态变量预留位置而已,它并没有内容,所以它在文件中也不占据空间.rodata段存放的是只读数据。

六.例子

生成可执行程序的四个步骤——预处理,编译,汇编,链接_第1张图片

生成可执行程序的四个步骤——预处理,编译,汇编,链接_第2张图片

生成可执行程序的四个步骤——预处理,编译,汇编,链接_第3张图片

现在有上述三个文件,在这里需要注意,我们定义全局变量gdata时,不能将它定义在 sum.h文件中,否则会引起gdata的多重定义,而是应该将它定义在sum.c中,并且当我们在main.cpp中引用gdata时,应该加上 extern int gdata这一句。extern是计算机语言中的一个关键字,可置于变量或者函数前,以表示变量或者函数的定义在别的文件中。提示编译器遇到此变量或函数时,在其它模块中寻找其定义,另外,extern也可用来进行链接指定。

生成.o目标文件(编译间段)

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)表示其他文件中可见。编译过程中符号是不分配虚拟地址的,只有在进行链接后(进行完符号解析后)才会给符号表分配虚拟地址。

.o目标文件的文件格式

生成可执行程序的四个步骤——预处理,编译,汇编,链接_第4张图片

查看.o文件的段头信息(编译间段)

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),创建页目录,页表。通过两个加载器把程序的指令和数据加载至虚拟内存空间中。虚拟空间的用户程序并不直接被加载到真实的物理内存上,而是把该程序需要被访问的部分映射到真实的物理地址上,而暂时没有被访问到的部分任然存在于虚拟内存中。通过这样的方式我们发现每个程序的虚拟地址空间可以远大于实际的物理地址空间,使之可以运行远大于自身内存的程序,同时虚拟地址空间也可以小于实际的物理地址空间,使之可以同时运行多个程序。加载完指令和数据后,接着将程序的入口地址写入到下一行指令寄存器。这样,程序就可以被执行了。

你可能感兴趣的:(Linux)