Executable and Linkable Format,中文名称“可执行可链接格式”,简称为ELF。ELF是Linux类系统的主要可执行文件,参数格式对应架构Application Binary Interface(ABI)文档。
init_elf_binfmt
init_elf_binfmt属于elf初始化加载函数,通过core_initcall(init_elf_binfmt)启动:
tatic int __init init_elf_binfmt(void)
{
register_binfmt(&elf_format);
return 0;
}
register_binfmt函数分析,继续看elf_format定义:
static struct linux_binfmt elf_format = {
.module = THIS_MODULE,
.load_binary = load_elf_binary,
.load_shlib = load_elf_library,
#ifdef CONFIG_COREDUMP
.core_dump = elf_core_dump,
.min_coredump = ELF_EXEC_PAGESIZE,
#endif
};
load_elf_binary函数分析,load_elf_library函数分析,elf_core_dump函数分析,ELF_EXEC_PAGESIZE在x86_64架构中为4096。
register_binfmt
注册linux_binfmt结构到链表中
static inline void register_binfmt(struct linux_binfmt *fmt)
{
__register_binfmt(fmt, 0); // 1表示linux_binfmt结构加入到链表最前面
}
||
\/
insert ? list_add(&fmt->lh, &formats) :
list_add_tail(&fmt->lh, &formats);
load_elf_binary
加载elf可执行文件到内存中
static int load_elf_binary(struct linux_binprm *bprm)
{
if (memcmp(elf_ex->e_ident, ELFMAG, SELFMAG) != 0) // 如果不是ELF格式
// #define ELFMAG "\177ELF"
goto out;
if (elf_ex->e_type != ET_EXEC && elf_ex->e_type != ET_DYN) // 如果不是可执行程序,也不是动态库
// #define ET_EXEC 2
// #define ET_DYN 3
goto out;
if (!elf_check_arch(elf_ex)) // 如果不是x86_64架构
// ((x)->e_machine == EM_X86_64)
// #define EM_X86_64 62 /* AMD x86-64 */
goto out;
if (elf_check_fdpic(elf_ex)) // 这里不做检查
// #define elf_check_fdpic(ex) false
goto out;
if (!bprm->file->f_op->mmap) // 已传入mmap函数
goto out;
/* 将二进制文件elf_file中的ELF程序头加载到一个新分配的数组中,该文件的ELF头由elf_ex指向 */
/* elf_ex: 加载程序头的二进制文件的ELF头 */
/* elf_file: 打开的ELF二进制文件 */
elf_phdata = load_elf_phdrs(elf_ex, bprm->file);
if (!elf_phdata)
goto out;
elf_ppnt = elf_phdata;
// 程序头表中的条目数量,如果文件没有程序头表,e_phnum将值保留为零
for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++) {
char *elf_interpreter;
if (elf_ppnt->p_type == PT_GNU_PROPERTY) {
// PT_GNU_PROPERTY由链接器生成,用于描述.note.gnu.property 部分
// #define PT_GNU_PROPERTY (PT_LOOS + 0x474e553)
// #define PT_LOOS 0x60000000
elf_property_phdata = elf_ppnt;
continue;
}
if (elf_ppnt->p_type != PT_INTERP)
// 指向了一个以 "NULL"结尾的字符串,这种段类型只对可执行程序有意义
// #define PT_INTERP 3
continue;
/* 这是用于共享库的程序解释器-现在假设这是一个a.out格式的二进制文件 */
retval = -ENOMEM;
elf_interpreter = kmalloc(elf_ppnt->p_filesz, GFP_KERNEL);
if (!elf_interpreter)
goto out_free_ph;
retval = elf_read(bprm->file, elf_interpreter, elf_ppnt->p_filesz,
elf_ppnt->p_offset);
...
/* 确保路径以NULL结尾 */
if (elf_interpreter[elf_ppnt->p_filesz - 1] != '\0')
goto out_free_interp;
interpreter = open_exec(elf_interpreter); // 打开可执行文件
open_exec函数分析,继续往下看:
kfree(elf_interpreter); // 释放内存
retval = PTR_ERR(interpreter); // 检查返回值
if (IS_ERR(interpreter)) // 判断指针指向实际地址还是错误信息
goto out_free_ph;
would_dump(bprm, interpreter); // 确保mm->user_ns包含可执行文件
/* 获取exec标头 */
retval = elf_read(interpreter, interp_elf_ex,
sizeof(*interp_elf_ex), 0);
if (retval < 0)
goto out_free_dentry;
break;
elf_ppnt = elf_phdata;
for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++)
switch (elf_ppnt->p_type) {
case PT_GNU_STACK: //
if (elf_ppnt->p_flags & PF_X) // 对应的段可以被执行
executable_stack = EXSTACK_ENABLE_X; // 启用可执行堆栈
else
executable_stack = EXSTACK_DISABLE_X; // 禁用可执行堆栈
break;
case PT_LOPROC ... PT_HIPROC: // 保留用于特定于处理器的语义
retval = arch_elf_pt_proc(elf_ex, elf_ppnt,
bprm->file, false,
&arch_state); // 检查程序头,以验证其正确性和/或适合于系统
if (retval)
goto out_free_dentry;
break;
}
if (interpreter) { // 一些简单的解释器一致性检查
retval = -ELIBBAD;
/* Not an ELF interpreter */
if (memcmp(interp_elf_ex->e_ident, ELFMAG, SELFMAG) != 0) // 如果不是ELF格式
goto out_free_dentry;
// 验证解释器有(支持)一个有效的架构
if (!elf_check_arch(interp_elf_ex) ||
elf_check_fdpic(interp_elf_ex))
goto out_free_dentry;
/* 加载解释器程序头 */
interp_elf_phdata = load_elf_phdrs(interp_elf_ex,
interpreter);
if (!interp_elf_phdata)
goto out_free_dentry;
}
...
retval = begin_new_exec(bprm); // 启动可执行文件
if (retval)
goto out_free_dentry;
begin_new_exec函数分析,继续往下看:
/* 立即执行此操作,因为setup_arg_pages中使用的STACK_TOP可能取决于personality */
SET_PERSONALITY2(*elf_ex, &arch_state);
if (elf_read_implies_exec(*