可执行程序工作原理
ELF目标文件格式
ELF概述
目标文件
“目标文件”:也叫ABI,编译器生成的文件,“目标”决定编译器使用的机器指令集,它和“目标平台”二进制兼容的。
a.out是最古老的目标文件格式,后发展为COFF格式,现在linux常用的格式为ELF和PE。
ELF
ELF:可执行并可链接的格式,是一个目标文件格式的标准。一种对象文件的格式,用于定义不同类型的对象文件中都内容,格式。在首部会描绘整个文件的组织结构,还包括了很多系统定义的以及用户自定义的节。
ELF文件的3种类型
-
可重定位文件:中间文件,需继续处理。由编译器和汇编器创建,一个源代码文件会生成一个可重定位文件。
内核编译中.o文件便是可重定位目标文件,最后所有的.o文件链接为一个文件,即linux内核 -
可执行文件:由多个可重定位文件结合生成,是完成了所有重定位工作和符号解析的文件。
-
共享目标文件:共享库,指可以被可执行文件或其他库文件使用的目标文件。可以理解为没有主函数main的“可执行”文件,只有一堆函数可供其他可执行文件调用。linux下共享库后缀为.so文件,so代表shared object。
ELF文件的作用
- 节的集合;
- 程序头表的段集合;
- 1和2的集合;
ELF格式
ELF Header结构
ELF表头首先会给出很多关于本ELF文件的属性信息,3种ELF类型就是通过e_type来实现的。e_type值1、2、3、4分别代表可重定位文件、可执行文件、共享目标文件、核心文件。节头表基本定义了整个ELF文件的组成,可以说是整个ELF就是由若干个节组成的。
Section Header结构
节头表是由Section Header用于链接的目标文件必须包含节区头部表,其他目标文件有没有这个表皆可。
program Header结构
段头表是和创建进程相关的,描述了连续的几个节在文件中的位置、大小以及它被放进内存后的位置和大小,告诉系统如何创建进程映像。
相关操作指令
- man elf:查看elf详细的格式定义
- readelf:用于显示一个或多个elf格式的目标文件的信息,可以通过它的选项来控制显示哪些信息。
- objdump:显示二进制文件信息,用于查看目标文件或者可执行的目标文件的构成的gcc工具。
程序编译
程序从源代码到可执行文件经过四个步骤,例hello.c
- 预处理:gcc -E hello.c -o hello.i
- 编译:gcc -S hello.i -o hello.s -m32
- 汇编:gcc -C hello.s -o hello.o -m32
- 链接:gcc hello.o -o hello -m32 -static
预处理
- 删除所有的注释“//”和“/**/”
- 删除所有的“#define”,展开所有的宏定义
- 处理所有的条件预编译指令
- 处理“#include”预编译指令
- 添加行号和文件名标识
编译
gcc首先检查代码的规范性、是否有语法错误,确定代码实际要做的工作。
在检查代码无误后,gcc讲代码翻译成汇编语言
汇编
汇编后形成的.o格式的文件已经是ELF格式文件。
程序编译后生成的目标文件至少含有3个节区(.text .data .bss)
链接
链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可以被加载到内存种并执行。
链接与库
- 链接过程上分为符号解析和重定位两部分;
- 链接时机上分为静态链接和动态连接两种;
符号与符号解析
符号
符号包含全局变量和全局函数。例如printf就是一个符号,hello程序需要在函数库中找到这个符号。
符号表
供编译器用于保存有关源程序构造的各种信息的数据结构。
符号表的功能是找未知函数在其他库文件中的代码段的具体位置。
查看方式是objdump -t xxx.o或readelf -s xxx.o
符号表中的函数
链接前后,内存地址和符号对应的节区编号会发生改变;
链接前后,符号表个数的差异会很大
重定位
重定位是把程序的逻辑地址空间变换成内存中的实际物理地址空间的过程,即装入时对目标程序中指令和数据的修改过程,是实现多道程序在内存中同时运行的基础。
静态链接与动态链接
静态链接
在编译链接时直接将需要的执行代码复制到最终可执行文件中。
优点:代码的装载速度快,执行速度也比较快,对外部的环境依赖度低。
缺点:应用程序相对比较大,装载多次,浪费内存。
动态链接
在编译时不直接复制可执行代码,而是通过记录一系列符号和参数,在程序运行或加载时将这些信息传递给操作系统。
装载时动态链接与运行时动态链接
实验