程序员的自我修养–链接、装载与库
第三部分 装载与动态链接
第六章 可执行文件的装载与进程
6.3从操作系统角度看可执行文件的装载
6.3.1 进程的建立
6.3.2 页错误
6.5Linux内核装载ELF过程简介
bash下执行某个elf程序为例:bash进程fork创建出一个新的进程。新进程调用execve()系统调用执行指定的LEF文件,bash进程继续返回等待刚才启动的新进程结束,然后继续等待用户输入命令。
进入evecve()系统调用之后,Linux内核就开始进行真正的装载工作
sys_execve():evecve()系统调用入口是sys_execve() 进行参数检查赋值后调用do_execve()
do_execve():查找被执行的文件,读取前128个字节判断文件的格式(ELF可执行文件的头4个字节为0X7F、‘e’、‘l’、‘f’)然后调用search_binary_handle()
search_binary_handle():搜索和匹配合适的可执行文件装载处理过程,linux中所有被支持的可执行文件格式都有相应的装载处理过程,elf可执行文件的装在处理过程叫做load_elf_binary();a.out可执行文件的装载处理过程叫做load_aout_handle();装载可执行脚本程序的处理过程叫做load_script()
load_elf_binary():load_elf_binary()被定义在fs/Binfmt_elf.c主要步骤如下
- 检查ELF可执行文件格式的有效性,如魔数、程序头表中段的数量
- 寻找动态链接的".interp"段,设置动态链接路径
- 根据ELF可执行文件的程序头表的描述对ELF文件进行映射,比如代码、数据、只读数据。
- 初始化ELF进程环境,比如进程启动时EDX寄存器的地址应该是DT_FINI的地址
- 将系统调用的返回地址修改成ELF可执行文件的入口点,入口点取决于程序的链接方式,静态链接的ELF可执行文件的程序入口就是ELF文件的头文件中e_entry所指的地址;对于动态链接的ELF可执行文件,程序入口点是动态连接器
当load_elf_binary()执行完毕,返回do_execve()再返回至sys_execve()时,上面的5步中已经把系统调用的返回地址改成了被装载的ELF程序的入口地址了。所以当sys_execve()系统调用从内核态返回到用户态时,EIP寄存器直接跳转到了ELF程序的入口地址,于是新的程序开始执行,ELF我可执行文件装载完成。
6.6Windows PE的装载
PE的装载跟ELF有所不同,PE文件中所有短的起始地址都是页的倍数,段的长度如果不是页的整数倍那么在映射时向上补齐到页的整数倍,即在32位的PE文件中,段的起始地址和长度都是4096字节的整数倍,由于这个原因PE文件的映射过程回避ELF简单得多,因为它无需考虑ELF里面诸多段地址对齐之类的问题,缺点是浪费磁盘和内存空间。PE可执行文件的段的数量一般很少,不想ELF中经常有十多个“Section”,最后不得不使用“Segment"的概念把他们合并到一起装载,PE文件中连接器在生产可执行文件时往往将所有的段尽可能地合并,所以一般只有代码段、数据段、只读数据段和BSS等为数不多的几个段。
PE里面有个很常见的术语叫做RVA(Relative Virtual Address),它表示一个相对虚拟地址,就是相对于文件中的偏移量,它是相对于PE文件的装载基地址的一个偏移地址。比如一个PE文件被装载到虚拟地址(VA)0x400000,那么一个RVA为0x1000的地址就是0x00401000。每个PE文件在装载时都会有一个装载目标地址(Target Address),这个地址就是所谓的基地址(Base Address)。由于PE文件被设计成可以装载到任何地址所以这个及地址并不是固定的,每次装载是都可能会变化。如果PE文件中的地址都是用绝对地址。他们都要随着基地址的变化而变化,但如果使用RVA这种基于基地址的相对地址,无论及地址怎么变化,PE文件中的各个RVA都保持一致。
装载一个PE可执行文件是个比ELF文件相对简单的过程:
- 限度去文件的第一个页,在这个野种包含了DOS头、PE文件头和段表
- 检查进城地址空间中目标地址是否可用,不可用就另选一个装载地址。这个额问题对于可执行文件来说基本不存在,因为它往往是往进程装入的模块,所以目标地址不太可能被占用。
- 使用段表中提供的信息,将PE文件中所有的段一一映射到地址空间中相应的位置。
- 若装载地址不是目标地址则进行Rebasing。
- 装载所有PE文件所需要的DLL文件
- 对PE文件中的所有导入符号进行解析。
- 根据PE够指定的参数建立初始化栈和堆
- 建立主线程并且启动进程
PE文件中预装在相关的主要信息都包含在PE扩展头(PE Optional Header)和段表