源代码到可执行文件经过的步骤:
(1)预处理(preprocess) -E 可单独执行预处理
(2)编译(compile) -S
(3)汇编(assemble) -c
(4)链接(link)
- 预处理:主要是处理源代码中以‘#‘开头的预处理指令,eg:‘#include‘
- 编译:读入以某种语言(源语言)编写的程序,输出目标语言编写的程序。该阶段将预处理文件进行一系列的词法分析,语法分析,语义分析以及优化,最终生成汇编代码。(实际在GCC的实现中,编译与预处理已经合并处理)且由于GCC的优化策略,生成汇编代码中函数printf()被替换成了puts(),因为在printf()只有单一参数时,与puts十分类似且性能较高。
- 汇编: 汇编器根据汇编指令和机器指令的对照表进行编译,将.s汇编成目标文件.o,此时的.o文件是可重定位文件,可以使用objdump命令来查看其内容。
- 链接:将目标文件及其依赖库进行链接生成可执行文件。‘static‘即可指定只用静态链接
形象地理解寄存器,内存,辅存
1.寄存器:寄存器就是衣服上的口袋,只有那么几个,只装最常用或者马上用的东西。
2.内存:内存是背包,有时候拿点东西放到口袋里,有时候从口袋里拿出点东西放到背包里
3.辅存:辅存就是你家的抽屉,可以放很多东西,但是存取不方便
ELF文件类型
1.ELF文件分为3类:
(1).exec 可执行文件
经过链接的,可执行的目标文件,通常也被称为程序
(2).rel 可重定位文件:由源文件编译而成尚未链接的目标文件,通常以(.o)作为扩展名。用于与其他目标文件进行链接以构成可执行文件或动态链接库
由源文件编译而成且尚未链接的目标文件,通常以‘.o‘作为扩展名,用于与其他目标文件进行链接构成可执行文件或动态链接库,通常是一段位置独立的代码
(3).dyn 共享目标文件
动态链接库文件。用于在链接过程中与其他动态链接库或可重定位文件一起构建新的目标文件,或者在可执行文件加载时,链接到进程中作为运行代码的一部分
- 总结:源文件---->预处理---->编译---->汇编---->链接
???????????????????.rel
ELF文件结构
审视一个目标文件的两个视角:
- 链接视角:以‘节’来进行划分
- 运行视角:以‘段’来进行划分
链接视角: - 代码节(.text):用于保存可执行的机器指令
- 数据节(.data):用于保存已初始化的全局变量和局部静态变量
- BSS节(.bss):用于保存未初始化的全局变量和局部静态变量
将程序指令和程序数据分开存放有许多好处
当程序被加载,数据和指令分别被映射到不同的虚拟区域:
由于数据区域对于进程来说是可读写的,而指令区域对于进程来说是只读的,所以这两个虚存区域可以被设置成可读写和只读,防止程序的指令被改写和利用
ELF文件头:
位于目标文件最开始的位置,包含描述整个文件的一些基本信息(ELF文件类型,版本/ABI版本,目标机器,程序入口,段表和节表的位置和长度等)
魔术字符的作用:当文件被映射到内存时,可以通过搜索该字符确定映射地址,在dump时非常有用
- 何为dump:
From zhihu:
作为动词:一般指将数据导出,转存成文件或静态形式。比如可以理解成:把内存某一时刻的内容转换成文件
作为名词:一般特指上述过程中所用得到的文件或静态形式
节头表
一个目标文件包含许多节(代码节,数据节,BSS节),这个节的信息保存在节头表中,表的每一项都是一个Elf64_Shdr结构体(也被称为节描述符),记录了节的名字,长度,偏移,读写权限等信息。
节头表对于程序运行不是必须的,因为它与程序布局无关,是程序头表的任务