从源程序到目标文件的生成过程

从源程序到目标文件的生成过程

最简单的编译命令是gcc helloworld.c,它包含了以下几个步骤:

预处理、编译、汇编、链接,下面分别简介。

 

预处理:处理#define宏定义、#if #ifdef等条件编译指令、#include预编译指令,删除注释,添加行号和文件名标识,保留所有的#pargma编译器指令,经过预编译后的文件为.i文件。预编译命令为:gcc -E hello.c -o hello.i或者cpp hello.c > hello.i

 

编译:把预处理完得文件进行一系列的词法分析、语法分析、语意分析及优化后产生的汇编代码文件。编译命令为gcc -S hello.i -o hello.s。现在版本的gcc把预编译和编译两个步骤合并成一个步骤,使用ccl程序来完成,命令为ccl hello.c。

也可以使用gcc -S hello.c -o hello.s直接从.c文件生成.s汇编文件。

 

汇编:将汇编代码转变成机器可以执行的指令,每一条汇编语句几乎都对应一条机器指令。命令为:as hello.s -o hello.o或者gcc -c hello.s -o hello.o或者我们最熟悉的gcc -c hello.c -o hello.o

 

链接:当我们的程序模块调用a另一个模块中b的函数(foo())或变量时,在编译的阶段编译器并不知道函数foo的地址,所以暂时把调用foo的指令的目标地址搁置,等待最后链接的时候由连接器去将这些指令的目标地址修正。把目标文件和库一起链接成可执行文件。最常见的库时运行时库。

 

目标文件中的格式

目标文件就是源代码编译后但未进行链接的那些中间文件,它和可执行文件的内容和结构其实很相似,所以一般和可执行文件采用同一种格式存储,那就是ELF格式。与ELF格式相对应的是Windows平台下的PE格式,它们都是COFF格式的变种。不光是可执行文件和目标文件按照ELF格式存储,动态链接库(.so)和静态链接库(.a)都按照ELF格式存储。

 

ELF格式的文件可分为以下4类:

1 可重定位文件(Relocatable File):这类文件包含了代码和数据,可以被用来链接成可执行文件或共享目标文件,静态链接库也可以归为这一类,代表是Linux的.o文件

 

2 可执行文件(Executable File):这类文件包含了可以直接执行的程序,它的代表就是ELF可执行文件,它们一般都没有扩展名,如/bin/bash文件

 

3 共享目标文件(Shared Object File):这种文件包含了代码和数据,可以在以下两种情况下使用。一种是链接器可以使用这种文件跟其他的可重定位文件和共享目标文件链接,产生新的目标文件。第二种是动态链接器可以将这几个共享目标文件与可执行文件结合,作为进程影响的一部分来运行。

 

4 核心转储文件(Core Dump File):当进程意外终止时,系统可以将该进程的地址空间的内容以终止时的一些其他信息转储带核心转储文件。

上面几种文件在file命令下会显示出相应的类型。

目标文件里有什么:

ELF文件最重要的结构是ELF文件头(ELF Header):ELF文件头里定义了ELF魔数、文件机器字节长度、数据存储方式、版本、运行平台、ABI版本、ELF重定位类型、硬件平台、硬件平台版本、入口地址、程序头入口和长度、段表的位置和长度及段得数量等。

 

三个最重要的段:代码段(.text)、数据段(.data)和只读数据段(.rodata)、BSS段(.bss)

可以用objdump -s -x -d hello.o来分析各个段得内容,-d表示反汇编,-s表示把各段内容用16进制打印,-x表示详细数据。

 

 顾名思义,.text段主要存放可执行的代码数据;.data段保存的是已经初始化了的全局变量和静态变量;.rodata段保存只读数据,一般是程序里的只读变量(如const修饰的变量)和字符串常量;bss保存未初始化的全局变量和静态变量。

 

除此之外,还有一些常见的段,如

.comment段存放编译器版本信息,比如字符串“GCC:(GNU)4.2.0”

.dubug段存放调试信息

.dynamic存放动态链接信息

.hash段存放符号哈希表

.line段存放调试时的行号表

.note段存放额外的编译器信息

.strtab段存放字符串表,用于存储ELF文件中用到的各种字符串,比如符号的名字

.shstrtab段存放段表中用到的字符串,最常见的就是段名

.symtab段是符号表,通过符号表就能知道这个符号在哪个段,以及在这个段的具体位置,还有这个符号在字符串表中的位置

Section Table(段表)也是其中一个段,它保存了各个段的信息,如段名、段的长度、在文件中的偏移、读写权限及段的其他属性

.rel.text段是一个重定位表,正如前面所说的,链接器在处理目标文件时,需要对目标文件的某些部位进行重定位,即代码段和数据段中那些对绝对地址的引用的位置,这些重定位的信息都记录在ELF文件的重定位表里

 

通过ELF文件头的信息可以找到段表的位置,从而找到各个段得位置和信息。

 

可以用readelf -S hello.o命令详细查看ELF文件中各段的信息。

经过了编译和汇编,便由源文件生成了目标文件,接下来的就是如果由多个目标文件连接成可执行文件的问题了。

原文地址:https://www.cnblogs.com/davy2013/p/3175579.html