内容说明
本次的内容,是一次 MOOC 课程的作业。具体的,是使用汇编对 Linux 系统调用部分进行模拟实现,从而更加直观的验证 Linux 系统的基本机制。作业声明
qianyizhou17 + 原创作品转载请注明出处 + 《Linux 内核分析》MOOC 课程 http://mooc.study.163.com/course/USTC-1000029000实验准备
1 本次实验并没有使用 MOOC 课程上提供的实验楼的环境,而是自行搭建了 64Bit Ubuntu 虚拟机。
2 本次试验使用了 Ubuntu 16.04,环境搭建和软件安装部分,可以参照上一篇博客的环境搭建部分。
3 之前完成过 qemu 的实验,rootfs.img 的制作,gdb 的跟踪等。实验操作
1 下载示例中的 menu 代码:Git clone https://github.com/mengning/menu.git ,并使用 test_fork.c 替换 test.c 文件:$ mv test_exec.c test.c
2 注意:在简易的系统初始化完成之后,再加入断点(否则系统在初始化阶段即进入 sys_execve 等断点)
3 分别在 sys_execve、load_elf_binary、start_thread等处打入断点
4 menuos中输入 exec,进行断点观察。在调用 start_thread时,gdb 中键入po new_ip
来观察程序入口信息,显示输出为 0x8048736。
事先在外部调用$ readelf -h hello
,显示其Entry point address:
信息与 new_ip 的地址信息一致。
分析总结
1 可执行程序使用一定的文件格式来描述该程序的入口信息、数据段、代码段、依赖信息等,目的是加载时使用。
2 执行目标程序的本质
就是将目标程序(使用 ELF 等格式描述)加载到“宿主”进程的地址空间中去、覆盖“宿主”进程的栈空间,并将“宿主”进程的 cs:ip 指向目标程序的入口地址。从而,“宿主”进程的执行便转化为目标程序的执行。
3 Linux 下如何装载一个可执行程序?
我们使用系统调用 exe*(对 execve 的一系列封装)来装载一个可执行程序,并传递相关参数和环境变量:int execve(const char * filename,char * const argv[],char * const envp[]);
3.1 系统调用 execve 在陷入内核态后调用 sys_execve,逐层调用了 do_execve, do_execve_common, exec_binprm
3.2 在 exec_binprm 中,使用 search_binary_handler 实现对不同格式的可执行文件进行不同的解析和执行
3.3 对于 elf 文件,将使用 load_elf_binary 来进行执行。其中,对于静态链接的程序,将直接传递可执行文件入口地址给 elf_entry;对于动态链接程序,将采用广度优先的搜索方式借助 ld 来加载依赖库和依赖文件,最后才由 ld 返回可执行文件的入口地址。
3.4 完成加载后,使用 start_thread 来更换当前进程的堆栈信息为目标可执行程序在 ELF 中描述的信息
3.5 在返回时,当前进程消失,取而代之的是运行“目标可执行程序”的进程
3.6 参数传递
参数通过系统调用 execv 传递,并传递到内核中,最终传递给目标进程
Linux 装载可执行程序过程的分析
最新推荐文章于 2024-06-30 01:30:36 发布