主要函数调用:
sys_execve
| - do_execve
|
| - search_binary_handler
|- linux_binfmt= elf_format
|- elf_format-> load_elf_binary
| - elf_entry = load_elf_interp()
|-
| if (BAD_ADDR(elf_entry))
| force_sig(SIGSEGV, current);
| retval =-EINVAL;
binfmt_elf.c: line 1024
elf_entry = loc->elf_ex.e_entry;
if (BAD_ADDR(elf_entry)) {
force_sig(SIGSEGV, current);
retval = -EINVAL;
goto out_free_dentry;
}
ELF可行档的载入:
内核中实际执行execv()或execve()系统调用的程序是do_execve(),这个函数先打开目标映像文件,并从目标文件的头部(从第一个字节开始)读入若干(128)字节,然后调用另一个函数search_binary_handler(),在那里面让各种可执行程序的处理程序前来认领和处理。内核所支持的每种可执行程序都有个struct linux_binfmt数据结构,通过向内核登记挂入一个队列。而search_binary_handler(),则扫描这个队列,让各个数据结构所提供的处理程序、即各种映像格式、逐一前来认领。如果某个格式的处理程序发现特征相符而,便执行该格式映像的装入和启动。
我们从ELF格式映像的linux_binfmt数据结构开始:
#define load_elf_binary load_elf32_binary
static struct linux_binfmt elf_format = {
.module = THIS_MODULE,
.load_binary = load_elf_binary,
.load_shlib = load_elf_library,
.core_dump = elf_core_dump,
.min_coredump = ELF_EXEC_PAGESIZE
};
ELF格式的二进制映像的认领、装入和启动是由load_elf_binary()完成的。而“共享库”、即动态连接库映像的装入则由load_elf_library()完成。实际上共享库的映像也是二进制的,但是一般说“二进制”映像是指带有main()函数的、可以独立运行并构成一个进程主体的可执行程序的二进制映像。
[sys_execve() > do_execve() > search_binary_handler() > load_elf_binary()]
//ELF文件头 from Linux2.6
typedef struct elf32_hdr{
unsigned char e_ident[EI_NIDENT]; //Magic: Class/Data/Version/Os_ABI/ABI Version
Elf32_Half e_type; //elf文件类型: 可重定位/可执行/共享目标/coredump
Elf32_Half e_machine; //elf的CPU平台属性
Elf32_Word e_version; //elf的版本号
Elf32_Addr e_entry; /* Entry point */ //入口地址
Elf32_Off e_phoff; //program header的起始位置
Elf32_Off e_shoff; //section header的起始位置
Elf32_Word e_flags; //标记位,标记平台相关属性
Elf32_Half e_ehsize; //elf文件头的大小
Elf32_Half e_phentsize; //program header的大小
Elf32_Half e_phnum; //program header的数目
Elf32_Half e_shentsize; //section header的大小
Elf32_Half e_shnum; //section header的数量
Elf32_Half e_shstrndx; //段表字符串表所在的段 在段表的 索引
} Elf32_Ehdr;
整个ELF映像就是由文件头、区段头表、程序头表、一定数量的区段、以及一定数量的部构成,而ELF映像的装入/启动过程,则就是在各种头部信息的指引下将某些部或区段装入一个进程的用户空间,并为其运行做好准备(例如装入所需的共享库),最后(在目标进程首次受调度运行时)让CPU进入其程序入口的过程。接着是对elf_bss 、elf_brk、start_code、end_code等等变量的初始化。这些变量分别纪录着当前(到此刻为止)目标映像的bss段、代码段、数据段、以及动态分配“堆” 在用户空间的位置。除start_code的初始值为0xffffffff外,其余均为0。随着映像内容的装入,这些变量也会逐步得到调整。
//ELF的 program header 结构体 from linux 2.6
typedef struct elf32_phdr{
Elf3