可执行文件在被调度运行时需要读取ELF文件的头,操作系统内核只关心头中的三种类型条目:
(1)第一种条目是PT_LOAD,用于描述可执行文件被装载程序装载到内存后的运行区域,包括可执行文件的.text和.data以及.bss的大小,.bss将用0填充(因此只需将其长度存储在可执行文件中)。
(2)第二种条目是PT_INTERP,用于标识链接完整程序所需的运行时链接器名字,即动态链接器的名字。
(3)第三种条目是PT_GNU_STACK,如果该条目存在,则操作系统内核可以从该条目获取一个信息位,该信息位用于指示程序的栈是否可执行。
在Linux下可执行文件被调度运行的过程如图2.16所示:
第一步将可执行文件的PT_LOAD段装载进内存,创建程序的内存镜像,将.bss全部用0填充。
第二步检索头的PT_INTERP以及PT_GNU_STACK,使用PT_INTERP标识的动态链接器,如/lib64/ld-linux-x86-64.so.2,读取可执行文件依赖的所有库信息,在磁盘上搜索这些库,并将它们装载到内存中。
第三步执行重定位。需要执行重定位操作的有两个:
①共享库刚开始被装载到不确定的地址,需要重定位以确定其绝对地址。
图2.16 在Linux下可执行文件被调度运行的过程
②在多模块的工程中,一个目标文件对其他目标文件的引用也需要进行重定位,以确定其地址。
第四步调用注册在.preinit_array、.init、.init_array中的共享库初始化函数。
第五步将控制权传递给原始二进制文件的入口点,使用户感觉二进制文件是直接从exec传递过来的。