在使用PE格式的可执行文件时,我们常常会遇到映像文件的装载问题。那么,映像文件到底是什么?它怎样被装载到内存中呢?本文将从映像文件的概念入手,详细解析PE里映像文件的装载过程。
一、映像文件的概念
PE格式的可执行文件包含了程序的代码、数据、导入/导出表等各种信息。这些信息都被组织成为一个叫做映像文件(Image File)的文件,通常以“.exe”或“.dll”为扩展名。映像文件是程序在磁盘上的静态形态,只有把它装载到内存中才能运行它。
二、映像文件的装载过程
1. 加载器的作用
当我们运行一个PE格式的可执行文件时,操作系统会启动一个叫做“加载器(Loader)”的程序来完成映像文件的装载工作。这个加载器在Windows中就是ntdll.dll里的一个叫做LdrpLoadDll的函数。
2. 装载过程的分析
① 初步准备:在装载映像文件之前,操作系统需要先创建进程环境、用户模式堆栈等运行必需的结构。创建好这些结构后,加载器才可以继续进行映像文件的装载工作。
② 加载PE头部信息:PE格式的可执行文件的头部信息被存储在映像文件的第一扇区中,加载器首先需要读取这些信息,以便后续装载工作的进行。
③ 创建进程空间:操作系统会在进程的虚拟地址空间中为PE文件申请对应大小的可读写内存空间,并建立虚拟内存到物理内存的映射关系。这个空间中包含了代码段、数据段、堆、栈等各种用于运行程序的空间。
④ 处理节表:节表是PE格式的可执行文件中用于描述每个区段(Section)的结构信息,包括所在位置、大小、属性等。加载器会根据节表信息,将PE文件中打包好的各个区段依次映射到申请好的进程空间中。
⑤ 重定位:PE格式的可执行文件中,某些位置的指针是基于绝对地址计算的,而这个地址是在程序运行时才能确定的。因此,在装载映像文件时,操作系统可能需要修改一些指针的数值,使它们指向正确的位置。这个过程就叫做“重定位”(Relocation)。
⑥ 处理导入表:很多程序都需要调用其他DLL中的函数,这时需要用到导入表。导入表里记录了需要调用的函数名和它们在DLL中的位置等信息。加载器会根据这些信息,将需要使用的DLL装载到进程空间中,并修正导入表中的指针,使它们指向正确的函数地址。
⑦ 处理异常处理表:PE格式的可执行文件中还包含了一些用于处理异常的代码,当程序运行时发生异常时,操作系统需要定位并执行这些代码。这些信息被记录在异常处理表中,加载器会在装载映像文件的过程中解析这些表,并建立相应的数据结构以便后续处理。
⑧ 处理TLS表:TLS(Thread Local Storage)是线程局部存储,在多线程编程中有重要作用。PE格式的可执行文件中可能包含有TLS表,加载器会按照这个表中的信息申请一块TLS内存,并将其映射到进程空间中。
⑨ 处理入口点:PE格式的可执行文件中会定义一个特殊的函数作为程序的入口点。装载完所有的段并完成符号重定位之后,操作系统会跳转到这个入口点开始执行程序。
三、总结
本文分析了PE里映像文件的装载过程,介绍了加载器的作用以及装载过程中的几个重要步骤,包括读取PE头部信息、创建进程空间、处理节表、重定位等。了解这些知识可以帮助我们更好地理解Windows操作系统中的可执行文件。