Chap-6 6.5 ELF可执行文件的装载

6.5 Linux装载ELF可执行文件的过程
当我们在Linux系统的bash下输入一个命令执行某个ELF文件时,Linux是怎样装载这个ELF文件并且执行它呢?
首先在用户层面,bash进程会调用fork()系统调用常见一个新进程,然后新的进程调用execve()系统调用执行
指定的ELF文件,原先的bash进程继续返回等待刚才启动的新进程结束。
在进入execve()系统调用后,Linux内核就开始进行真正的装载工作。在内核中,execve()系统调用相应的入口是
sys_execve(),它被定义在arch/i386/kernel/process.c文件中。sys_execve()进行一些参数的检查复制后,调用
do_execve()。do_execve()会首先查找被执行的文件,如果找到文件,则读取前128个字节。

为什么要读取前128个字节?因为Linux支持的可执行文件不止一种,还有a.out、Java程序和以"#!"开始的脚本
程序。这里do_execve()读取文件的前128个字节的目的是判断文件的格式,每种可执行文件开头的几个字节是
非常特殊的,特别是开头的4个字节,常常称为魔数(Magic Number),通过对魔数的判断可以确定文件的格式
和类型。比如ELF可执行文件格式开头的4个字节为0x7f、“e”、“l”、“f”;Java的可执行文件的前4个字节为“c”、
“a”、“f”、“e”;如果可执行文件是shell脚本或者perl、python等解释性脚本程序,那么它的第一行往往是
“#!/bin/sh”或者“#!/usr/bin/perl”或者“#!/usr/bin/python”,这时候前两个字节就够成了魔数,系统一旦判断
到这两个字节,就对后面的字符串进行解析,以确定具体的解释程序的路径。

当do_execve()读取了128字节的文件头部后,会调用search_binary_handle(),该函数会通过判断文件头部的
魔数确定文件的格式,并且调用相应的装载处理过程。比如ELF可执行文件的装载处理过程叫做load_elf_binary(),
它的主要步骤是:
1.检查ELF可执行文件格式的有效性,比如魔数、程序头表中段(segment)的数量
2.寻找动态链接的.interp段,设置动态链接器路径
3.根据ELF可执行文件的程序头表的描述,对ELF文件进行映射、比如代码、数据、只读数据
4.初始化ELF进程环境,比如进程启动时edx寄存器的地址应该是DT_FINI的地址
5.将系统调用的返回地址修改成ELF可执行文件的入口点,这个入口点取决于程序的链接方式,对于静态链接
的ELF文件,这个程序入口就是ELF文件头中e_entry所指的地址。对于动态链接的ELF可执行文件,程序入口点
是动态链接器。
当load_elf_binary()执行完毕,返回至do_execve()再返回至sys_execve()时,上面的第5步中已经把系统调用的
返回地址修改为被装载ELF程序的入口地址,所以当sys_execve()系统调用从内核态返回到用户态时,EIP寄存器
直接跳转到ELF程序的入口地址,于是新的程序开始执行,ELF可执行文件装载完成。

posted on 2014-01-10 23:18  Persistence  阅读(245)  评论(0编辑  收藏  举报

导航