程序的编译与执行过程

本文以C程序为例。

构建C程序需要4个步骤,分别使用4个工具完成: preprocessor, compiler, assembler, and linker.四步完成后生成一个可执行文件。

  1. 第一步,预处理. 这一步处理 头文件、条件编译指令和宏定义。

  2. 第二步,编译. 将第一步产生的文件连同其他源文件一起编译成汇编代码。

  3. 第三步,汇编。将第二步产生的汇编源码转换为 object file.

  4. 第四步,链接. 将第三步产生的一些object file 链接成一个可执行的文件。

文件后缀 对于编译来说
file_name.c 需要预处理的C原文件
file_name.i 预处理C文件后产生的文件
file_name.ii 预处理C++文件后产生的文件
file_name.cc file_name.cpp 需要预处理的C++原文件
file_name.s 产生的汇编代码
file_name.o 产生的 object file

程序的编译与执行过程_第1张图片

可执行文件

源码经过汇编产生对象文件(Object files),然后经过链接生成可执行文件(executable files)。对象文件和可执行文件的格式可以是ELF(Executable and Linking Format) 或者 COFF(Common Object-File Format)。ELF格式用于Linux系统,COFF用于windows系统。
ELF格式来源于System V Release 4 unix。ELF文件由几个区组成,每个区前面都包含一个头部。区总数有限制。区内可以包含可执行文件,数据,动态链接信息,调试数据,符号表,重定位信息,注释,字符串表和备注等信息。其中,一些区等内容直接加载到进程镜像中,一些区为构建进程镜像过程中提供辅助信息,还有一些区只在链接对象文件过程中使用到。
下面几个区是所有可执行文件通用部分。

section 描述
.text 这部分内容为可执行指令码,被执行该文件所产生到进程之间所共享
.bss BSS 代表 ‘Block Started by Symbol’. 这里记录未初始化的全局和静态变量。由于 BSS 只会记录还未赋值到变量,所以它不会实际保存变量镜像。
.data 包含始化的全局和静态变量。这里是可执行文件的最大一部分。
.rdata 只读数据区,这里包含常量和字符串。
Symbol table symbol就是一些名字地址Symbol table存储着程序的symbolic definitionssymbolic references定位信息。
Relocation records 比如,当程序调用一个函数,对应的调用指令必须出让控制到目的地址去执行。简单来说,relocation records就是一些用来给linker用来调整区块内容的参考信息。

Because the various object files will include references to each others code and/or data, so various locations, these shall need to be combined during the link time.
For example in Figure w.2, the object file that has main() includes calls to functions funct() and printf().

重定位信息(RELOCATION RECORDS)

由于不同的对象文件之间保存了彼此之间的引用,所以在链接期间需要整合这些定位。下图是RELOCATION RECORDS信息的使用过程。
程序的编译与执行过程_第2张图片

符号表(SYMBOL TABLE)

由于从汇编到机器码过程中需要移除labels,所以,对象文件必须另找地方保存对labels的跟踪信息。符号表里保存着名字以及和名字对应的datatext区的偏移量。符号表就是用来记录这些信息的。

链接

程序的编译与执行过程_第3张图片

共享对象文件(shared object)

printf(), malloc(), strcpy()这样的标准库函数,一般在系统中以共享对象文件存在,一遍程序链接时候取用。共享对象文件根据链接方式不同,以两种形态出现,一种是.a后缀文件,以便静态链接使用,如libc.a;一种是.so后缀文件,以便动态链接使用。比如libc.so

静态链接

静态链接将共享对象文件.a一同链接至可执行文件内,比如:

gcc –static filename.c –o filename
这么干的缺点是可执行文件大,程序运行耗费内存。

动态链接

动态链接不会将共享对象文件链接至可执行文件内,而是将.so文件的依赖信息写入可执行文件中,当程序加载时,让运行时链接器去找.so文件。动态链接的好处是,1)可执行文件小,2)程序运行耗内存少,因为不同进程会共用同一份虚拟内存中的.so载入,而不是每个进程单独载入一份.so至内存。3)库升级不用重新链接程序。

程序如何利用共享对象

ELF区块
.init           - Startup

.text          - String

.fini           - Shutdown

.rodata     - Read Only

.data         - Initialized Data

.tdata        - Initialized Thread Data

.tbss          - Uninitialized Thread Data

.ctors         - Constructors

.dtors         - Destructors

.got            - Global Offset Table

.bss           - Uninitialized Data

在Linux系统上,可以使用readelf或者objdump工具产看ELF文件。
程序的编译与执行过程_第4张图片

进程加载

Linux进程加载自文件系统里的ELF文件(使用execve()或者spawn()系统调用),如果文件系统是块设备比如硬盘,则将其读取到内存中,如果设备已经内存映射比如flash盘,那么无需加载至RAM,直接本地运行。
将可执行文件加载至内存需要用到加载器(loader),加载器是操作系统通用必备的。加载器的作用:
1. Memory and access validation内存与访问校验。OS内核读取可执行文件头部信息确定可读性以及内存评估,确定对其指令的执行能力。
2. 建立进程:1)为程序分配主存,2)copy可执行文件的必要数据至主存,3)初始化寄存器比如esp等,4)跳进主程main()

程序加载后,在内存中的布局:
程序的编译与执行过程_第5张图片

进程(镜像)

程序的编译与执行过程_第6张图片

程序的编译与执行过程_第7张图片
reference:http://www.tenouk.com/ModuleW.html
reference:http://www.tenouk.com/Bufferoverflowc/Bufferoverflow1c.html

你可能感兴趣的:(linux)