Linux Kernel - 可执行程序的加载过程【转】
2022/2/14 7:11:36
本文主要是介绍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
这篇关于Linux Kernel - 可执行程序的加载过程【转】的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-12如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)
- 2024-11-08linux的 vi编辑器中搜索关键字有哪些常用的命令和技巧?-icode9专业技术文章分享
- 2024-11-08在 Linux 的 vi 或 vim 编辑器中什么命令可以直接跳到文件的结尾?-icode9专业技术文章分享
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南
- 2024-08-21【Linux】分区向左扩容的方法