ELF文件加载分析

来源:互联网 发布:安徽网络教育平台登录 编辑:程序博客网 时间:2024/05/21 04:25

Linux内核在fs/binfmt_bin.c文件中定义了一个描述ELF文件加载函数的结构体变量:

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函数:
static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
{
struct file *interpreter = NULL; // 用以关闭GCC
unsigned long load_addr = 0, load_bias = 0;
int load_addr_set = 0;
char * elf_interpreter = NULL;
unsigned int interpreter_type = INTERPRETER_NONE;
unsigned char ibcs2_interpreter = 0;
unsigned long error;
struct elf_phdr *elf_ppnt, *elf_phdata;
unsigned long elf_bss, elf_brk;
int elf_exec_fileno;
int retval, i;
unsigned int size;
unsigned long elf_entry, interp_load_addr = 0;
unsigned long start_code, end_code, start_data, end_data;
unsigned long reloc_func_desc = 0;
char passed_fileno[6];
struct files_struct *files;
int have_pt_gnu_stack, executable_stack = EXSTACK_DEFAULT;
unsigned long def_flags = 0;
struct {
struct elfhdr elf_ex;
struct elfhdr interp_elf_ex;
struct exec interp_ex;
} *loc;
// 获取ELF头
loc = kmalloc(sizeof(*loc), GFP_KERNEL);
if (!loc) {
retval = -ENOMEM;
goto out_ret;
}
loc->elf_ex = *((struct elfhdr *)bprm->buf);
// 如果不是ELF文件则返回ENOEXEC(仅检查文件头标识和可执行文件类型)
retval = -ENOEXEC;
if (memcmp(loc->elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
goto out;
if (loc->elf_ex.e_type != ET_EXEC && loc->elf_ex.e_type != ET_DYN)
goto out;
if (!elf_check_arch(&loc->elf_ex))
goto out;
// 如果所在文件系统没有内存映射功能则返回ENOEXEC
if (!bprm->file->f_op||!bprm->file->f_op->mmap)
goto out;
// 分配空间,并读入程序头表
if (loc->elf_ex.e_phentsize != sizeof(struct elf_phdr))
goto out;
if (loc->elf_ex.e_phnum 1 || loc->elf_ex.e_phnum > 65536U/sizeof(struct elf_phdr))
goto out;
size = loc->elf_ex.e_phnum * sizeof(struct elf_phdr);
retval = -ENOMEM;
elf_phdata = kmalloc(size, GFP_KERNEL);
if (!elf_phdata)
goto out;
retval = kernel_read(bprm->file, loc->elf_ex.e_phoff, (char *)elf_phdata, size);
if (retval != size) {
if (retval >= 0)
retval = -EIO;
goto out_free_ph;
}
// 获取可用的文件标识,并将可执行文件放入到进程打开的文件列表中
files = current->files;
retval = unshare_files();
if (retval 0)
goto out_free_ph;
if (files == current->files) {
put_files_struct(files);
files = NULL;
}
retval = get_unused_fd();
if (retval 0)
goto out_free_fh;
get_file(bprm->file);
fd_install(elf_exec_fileno = retval, bprm->file);
elf_ppnt = elf_phdata; // 程序头表
elf_bss = 0; // BSS段起始地址
elf_brk = 0; // BSS段结束地址
start_code = ~0UL; // 代码段起始地址
end_code = 0; // 代码段结束地址
start_data = 0; // 数据段起始地址
end_data = 0; // 数据段结束地址
// 加载共享库
for (i = 0; i loc->elf_ex.e_phnum; i ) {
if (elf_ppnt->p_type == PT_INTERP) {
retval = -ENOEXEC;
if (elf_ppnt->p_filesz > PATH_MAX ||
elf_ppnt->p_filesz 2)
goto out_free_file;
// 给共享库解释器的名称分配空间
retval = -ENOMEM;
elf_interpreter = kmalloc(elf_ppnt->p_filesz, GFP_KERNEL);
if (!elf_interpreter)
goto out_free_file;
// 读取共享库解释器的路径
retval = kernel_read(bprm->file, elf_ppnt->p_offset,
elf_interpreter, elf_ppnt->p_filesz);
if (retval != elf_ppnt->p_filesz) {
if (retval >= 0)
retval = -EIO;
goto out_free_interp;
}
// 确保路径以NULL结束
retval = -ENOEXEC;
if (elf_interpreter[elf_ppnt->p_filesz - 1] != '/0')
goto out_free_interp;
// 如果解释器是以下两者之一则为iBCS2映像,否则为本地Linux映像
if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 ||
strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0)
ibcs2_interpreter = 1;
SET_PERSONALITY(loc->elf_ex, ibcs2_interpreter);
// 读取共享库解释器
interpreter = open_exec(elf_interpreter);
retval = PTR_ERR(interpreter);
if (IS_ERR(interpreter))
goto out_free_interp;
retval = kernel_read(interpreter, 0, bprm->buf,
BINPRM_BUF_SIZE);
if (retval != BINPRM_BUF_SIZE) {
if (retval >= 0)
retval = -EIO;
goto out_free_dentry;
}
// 填充共享库解释器头部
loc->interp_ex = *((struct exec *)bprm->buf);
loc->interp_elf_ex = *((struct elfhdr *)bprm->buf);
break;
}
elf_ppnt ;
}
// 检查动态库解释器的执行权限
elf_ppnt = elf_phdata;
for (i = 0; i loc->elf_ex.e_phnum; i , elf_ppnt )
if (elf_ppnt->p_type == PT_GNU_STACK) {
if (elf_ppnt->p_flags & PF_X)
executable_stack = EXSTACK_ENABLE_X;
else
executable_stack = EXSTACK_DISABLE_X;
break;
}
have_pt_gnu_stack = (i loc->elf_ex.e_phnum);
// 检查动态库解释器的有效性
if (elf_interpreter) {
// 设置解释器的类型
interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
if ((N_MAGIC(loc->interp_ex) != OMAGIC) &&
(N_MAGIC(loc->interp_ex) != ZMAGIC) &&
(N_MAGIC(loc->interp_ex) != QMAGIC))
interpreter_type = INTERPRETER_ELF;
// 检查解释器类型的有效性
if (memcmp(loc->interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
interpreter_type &= ~INTERPRETER_ELF;
retval = -ELIBBAD;
if (!interpreter_type)
goto out_free_dentry;
// 确保解释器类型的唯一性
if ((interpreter_type & INTERPRETER_ELF) &&
interpreter_type != INTERPRETER_ELF) {
interpreter_type = INTERPRETER_ELF;
}
// 确保解释器类型有效
if ((interpreter_type == INTERPRETER_ELF) &&
!elf_check_arch(&loc->interp_elf_ex))
goto out_free_dentry;
} else {
SET_PERSONALITY(loc->elf_ex, ibcs2_interpreter);
}
// 将程序的文件描述符传递给a.out格式的解释器
if ((!bprm->sh_bang) && (interpreter_type == INTERPRETER_AOUT)) {
char *passed_p = passed_fileno;
sprintf(passed_fileno, "%d", elf_exec_fileno);
if (elf_interpreter) {
retval = copy_strings_kernel(1, &passed_p, bprm);
if (retval)
goto out_free_dentry;
bprm->argc ;
}
}
// 释放资源
retval = flush_old_exec(bprm);
if (retval)
goto out_free_dentry;
// 丢弃无用的文件结构
if (files) {
put_files_struct(files);
files = NULL;
}
// 设置当前进程属性
current->mm->start_data = 0;
current->mm->end_data = 0;
current->mm->end_code = 0;
current->mm->mmap = NULL;
current->flags &= ~PF_FORKNOEXEC;
current->mm->def_flags = def_flags;
SET_PERSONALITY(loc->elf_ex, ibcs2_interpreter);
if (elf_read_implies_exec(loc->elf_ex, executable_stack))
current->personality |= READ_IMPLIES_EXEC;
if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
current->flags |= PF_RANDOMIZE;
// 选择内存布局
arch_pick_mmap_layout(current->mm);
current->mm->free_area_cache = current->mm->mmap_base;
current->mm->cached_hole_size = 0;
// 给用户态栈在进程地址空间中分配新内存页
retval = setup_arg_pages(bprm, randomize_stack_top(STACK_TOP), executable_stack);
if (retval 0) {
send_sig(SIGKILL, current, 0);
goto out_free_dentry;
}
current->mm->start_stack = bprm->p;
// 将代码段映射到内存空间(起始地址为0x08048000)
for(i = 0, elf_ppnt = elf_phdata; i loc->elf_ex.e_phnum; i , elf_ppnt ) {
int elf_prot = 0, elf_flags;
unsigned long k, vaddr;
// 检查段是否可加载
if (elf_ppnt->p_type != PT_LOAD)
continue;
// 分配空间
if (unlikely (elf_brk > elf_bss)) {
unsigned long nbyte;
retval = set_brk (elf_bss load_bias, elf_brk load_bias);
if (retval) {
send_sig(SIGKILL, current, 0);
goto out_free_dentry;
}
nbyte = ELF_PAGEOFFSET(elf_bss);
if (nbyte) {
nbyte = ELF_MIN_ALIGN - nbyte;
if (nbyte > elf_brk - elf_bss)
nbyte = elf_brk - elf_bss;
if (clear_user((void __user *)elf_bss
load_bias, nbyte)) {
}
}
}
// 设置映射参数,并将程序段[elf_ppnt->p_offset, elf_ppnt->p_filesz]
// 映射到虚存load_bias vaddr开始的区域
if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;
if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE;
vaddr = elf_ppnt->p_vaddr;
if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
elf_flags |= MAP_FIXED;
} else if (loc->elf_ex.e_type == ET_DYN) {
load_bias = ELF_PAGESTART(ELF_ET_DYN_BASE - vaddr);
}
error = elf_map(bprm->file, load_bias vaddr, elf_ppnt,
elf_prot, elf_flags);
if (BAD_ADDR(error)) {
send_sig(SIGKILL, current, 0);
goto out_free_dentry;
}
// 获取ELF文件在用户虚存中的起始地址
if (!load_addr_set) {
load_addr_set = 1;
load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);
if (loc->elf_ex.e_type == ET_DYN) {
load_bias = error -
ELF_PAGESTART(load_bias vaddr);
load_addr = load_bias;
reloc_func_desc = load_bias;
}
}
k = elf_ppnt->p_vaddr;
if (k start_code) start_code = k; // 取最小段地址为代码段起始
if (start_data k) start_data = k; // 取最大段地址为数据段起始
// 避免段大小溢出
if (BAD_ADDR(k) || elf_ppnt->p_filesz > elf_ppnt->p_memsz ||
elf_ppnt->p_memsz > TASK_SIZE || TASK_SIZE - elf_ppnt->p_memsz k) {
send_sig(SIGKILL, current, 0);
goto out_free_dentry;
}
// 让k指向文件段尾
k = elf_ppnt->p_vaddr elf_ppnt->p_filesz;
// 取最大文件段尾作为BSS段的起始
if (k > elf_bss)
elf_bss = k;
// 取最大可执行的文件段尾作为可执行段的终止
if ((elf_ppnt->p_flags & PF_X) && end_code k)
end_code = k;
// 取最大的文件段尾作为数据段的终止
if (end_data k)
end_data = k;
// 将k指向内存段尾
k = elf_ppnt->p_vaddr elf_ppnt->p_memsz;
// 取最大内存段尾作为BSS段的终止
if (k > elf_brk)
elf_brk = k;
}
// 重新设置各段的内存区域
loc->elf_ex.e_entry = load_bias;
elf_bss = load_bias;
elf_brk = load_bias;
start_code = load_bias;
end_code = load_bias;
start_data = load_bias;
end_data = load_bias;
// 确保BSS段空间的正确性
retval = set_brk(elf_bss, elf_brk);
if (retval) {
send_sig(SIGKILL, current, 0);
goto out_free_dentry;
}
if (likely(elf_bss != elf_brk) && unlikely(padzero(elf_bss))) {
send_sig(SIGSEGV, current, 0);
retval = -EFAULT;
goto out_free_dentry;
}
if (elf_interpreter) {
// 加载共享库链接器
if (interpreter_type == INTERPRETER_AOUT)
elf_entry = load_aout_interp(&loc->interp_ex, interpreter);
else
elf_entry = load_elf_interp(&loc->interp_elf_ex,
interpreter, &interp_load_addr);
if (BAD_ADDR(elf_entry)) {
force_sig(SIGSEGV, current);
retval = IS_ERR((void *)elf_entry) ?
(int)elf_entry : -EINVAL;
goto out_free_dentry;
}
reloc_func_desc = interp_load_addr;
// 将可执行程序的入口变为共享库连接器的入口
allow_write_access(interpreter);
fput(interpreter);
kfree(elf_interpreter);
} else {
elf_entry = loc->elf_ex.e_entry;
if (BAD_ADDR(elf_entry)) {
force_sig(SIGSEGV, current);
retval = -EINVAL;
goto out_free_dentry;
}
}
// 释放程序段表
kfree(elf_phdata);
if (interpreter_type != INTERPRETER_AOUT)
sys_close(elf_exec_fileno);
// 增加ELF内核模块的引用计数
set_binfmt(&elf_format);
#ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES
retval = arch_setup_additional_pages(bprm, executable_stack);
if (retval 0) {
send_sig(SIGKILL, current, 0);
goto out;
}
#endif
// 设置进程的相关参数
compute_creds(bprm);
current->flags &= ~PF_FORKNOEXEC;
create_elf_tables(bprm, &loc->elf_ex, (interpreter_type == INTERPRETER_AOUT),
load_addr, interp_load_addr);
if (interpreter_type == INTERPRETER_AOUT)
current->mm->arg_start = strlen(passed_fileno) 1;
current->mm->end_code = end_code;
current->mm->start_code = start_code;
current->mm->start_data = start_data;
current->mm->end_data = end_data;
current->mm->start_stack = bprm->p;
if (current->personality & MMAP_PAGE_ZERO) {
down_write(&current->mm->mmap_sem);
error = do_mmap(NULL, 0, PAGE_SIZE, PROT_READ | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE, 0);
up_write(&current->mm->mmap_sem);
}
#ifdef ELF_PLAT_INIT
ELF_PLAT_INIT(regs, reloc_func_desc);
#endif
start_thread(regs, elf_entry, bprm->p);
if (unlikely(current->ptrace & PT_PTRACED)) {
if (current->ptrace & PT_TRACE_EXEC)
ptrace_notify ((PTRACE_EVENT_EXEC 8) | SIGTRAP);
else
send_sig(SIGTRAP, current, 0);
}
retval = 0;
out:
kfree(loc);
out_ret:
return retval;
// 发生错误而返回
out_free_dentry:
allow_write_access(interpreter);
if (interpreter)
fput(interpreter);
out_free_interp:
kfree(elf_interpreter);
out_free_file:
sys_close(elf_exec_fileno);
out_free_fh:
if (files)
reset_files_struct(current, files);
out_free_ph:
kfree(elf_phdata);
goto out;
}