转自:http://www.dosrc.com/mark/linux-3.18.6/2016/05/15/linux-kernel-loading-of-executable-program.html
execve
),只不过和fork
系统调用一样有一些特殊。Shell程序
-> execve
-> sys_execve
然后在初始化新程序堆栈时拷贝进去。即先函数调用参数传递,再系统调用参数传递将命令行参数和环境变量传递进新程序堆栈。动态链接库:分为装载时动态链接和运行时动态链接,如何在64位环境下编译32位动态链接库参看下面链接。
动态链接
寻找文件格式对应的解析模块,如下:
sys_execve
-> do_execve
-> do_execve_common
-> exec_binprm
-> search_binary_handler
search_binary_handler
根据文件头部信息寻找对应的文件格式解析模块。
list_for_each_entry(fmt, &formats, lh) { if (!try_module_get(fmt->module)) continue; read_unlock(&binfmt_lock); bprm->recursion_depth++; retval = fmt->load_binary(bprm); read_lock(&binfmt_lock); ... }
对于ELF格式的可执行文件,语句fmt->load_binary(bprm);
执行的应该是load_elf_binary(bprm);
。 load_binary
是个函数指针。寻找对应的文件格式解析模块采用了设计模式中的观察者模式。如下为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, };
static int __init init_elf_binfmt(void) { register_binfmt(&elf_format); return 0; }
这里不同的文件格式解析模块就是观察者,可执行文件就是被观察者,list_for_each_entry
遍历文件格式解析模块向其发送通知,通知文件格式解析模块来解析当前的可执行文件,只不过这里只通知对应的文件解析模块(对应的观察者),而不是所有的文件格式解析模块(所有的观察者)。
上面已经说了,对于ELF格式的文件。执行到fmt->load_binary(bprm);
,实际上执行的是load_elf_binary(bprm);
,在load_elf_binary(bprm);
函数中的start_thread(regs, elf_entry, bprm->p);
语句指明了可执行文件的起点。函数原型start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
中的第二个参数就是新程序的起点,它通过修改内核堆栈中EIP的值作为新程序的起点,execve
系统调用返回到用户态就从这里开始执行。
对于静态链接的可执行文件elf_entry
就是ELF头中定义的起点,对于动态链接的可执行文件,先加载连接器ld,将CPU控制权交给ld来加载依赖库并完成动态链接,这部分不由内核完成,源代码如下:
if (elf_interpreter) {//需要动态链接 unsigned long interp_map_addr = 0; elf_entry = load_elf_interp(&loc->interp_elf_ex, interpreter, &interp_map_addr, load_bias); ... } else {//不需要动态链接 elf_entry = loc->elf_ex.e_entry; }
wdk 原创作品转载请注明出处
相关链接 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000