Linux Kernel - 可执行程序的加载过程【转】
转自:http://www.dosrc.com/mark/linux-3.18.6/2016/05/15/linux-kernel-loading-of-executable-program.html
重点知识总结
- 可执行文件的装载也是一个系统调用(
execve
),只不过和fork
系统调用一样有一些特殊。 - Shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数。
- 库函数exec*都是execve的封装例程。
可执行文件的文件格式,Shell环境,动态链接库。
- (ELF文件格式),ELF格式的文件默认加载到进程空间的0X804800处。
- Shell环境:加载一个新程序会覆盖当前进程的用户进程空间,所以Shell命令行要加载一个新程序时,先要fork一个新进程,在新进程中加载新程序,否则Shell进程就被覆盖了,那么命令行参数和环境变量是如何进入新程序堆栈的。通过
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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
2020-02-14 Vim|多行行尾插入【转】
2017-02-14 彻底搞定C指针-函数名与函数指针【转】