张潇月+《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
可执行程序怎么来的?
以C语言为例:C代码-编译预处理-编译成汇编代码-汇编器将其变成目标代码-链接成可执行代码-执行
预处理负责把include的文件包含进来及宏替换等工作。
目标文件的格式ELF
目标文件已经是适应到某一种CPU体系架构上的二进制指令了=二进制兼容的格式了
ABI=应用程序二进制接口
ELF格式里有三种主要目标文件:
ELF头在文件的开始,保存了线路图,秒速了该文件的组织情况程序头表告诉系统如何来创建一个进程的内存映像。section头表包含了描述文件section的信息。
当创建或增加一个进程映像的时候,系统在理论上将拷贝只一个文件的段到一个虚拟的内存段。
启动一个刚加载可执行文件的时候:
可执行文件加载到内存中开始执行的第一行代码,一般静态链接会将所有代码放在一个代码段。
命令行参数和环境变量是如何传递的?
压栈!在建立新的用户态堆栈时,这些参数内容是通过指针方式传到系统调用内核处理函数。
shell程序-execve-sys_execve,然后再初始化新程序堆栈时拷贝进去
先函数调用参数传递,再系统调用参数传递。
动态链接:可执行程序装载时的动态链接和运行时的动态链接
execve和fork都是特殊的系统调用,fork子进程是从ret_from_fork开始执行返回到用户态。
execve陷入内核态,在内核里用execve加载了可执行文件,把当前进程的可执行程序给覆盖了,当系统调用返回时,是返回给了新的可执行程序。
sys_execve内核处理过程
do_execve-do_execve_common-execve_binprm
search_binary_handle符合寻找文件格式对应的解析模块
如何支持多种不同的可执行文件?
观察者模式,多态,发布订阅架构
execve系统调用返回到用户态从哪里开始执行?
通过修改内核堆栈中EIP的值作为新程序的起点。
需要依赖动态链接的可执行文件先加载链接器ld作为elf_entry
静态链接的文件直接将变量赋给elf_entry
需要依赖其他链接库的话,将CPU控制权变给ld来加载依赖库并完成动态链接。
实验:使用gdb跟踪sys_execve内核函数的处理过程