ELF:加载过程

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(*
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

坤昱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值