赵瀚青原创作品转载请注明出处《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
概述
本周学习的内容主要是讨论可执行文件,最开始讲解了可执行文件是怎么得来的,也就是上个学期娄老师教我们的四步,预处理,编译成汇编指令,变成二进制代码,最后执行可执行文件。然后讲解了可执行文件的演变,从最开始的a.out文件,演变成ELF文件,然后讲解了如何装载可执行文件。也就是这周的实验内容,先用test_execv.c覆盖tect.c,然后观察代码的变化,然后再运行menu观察到新添了HELLO WORLD的条目,然后用gdb调试,观察加入了execv这个可执行文件,有什么差别。这周的学习内容和实验内容都不是特别难,因为上个学期有所涉及,理解起来也不是特别复杂,希望能继续坚持,学好linux内核这门课。
课本内容
链接的定义
链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(或被拷贝)到存储并执行。链接可以执行于编译时,也就是在源代码被翻译成机器代码时;也可以执行于加载时,也就是在程序被加载器加载到存储器并执行时;甚至执行于运行时,由应用程序来执行。
静态链接
Unix的静态链接器ld,以一组可重位目标文件和命令行参数作为输入,生成一个完全链接的可以加载和运行的可执行目标文件作为输出。输入的可重定位目标文件由各种不同的代码和数据节组成。指令在一个节中,初始化的全局变量在另一个节中,而未初始化的变量又在另外一个节中。
```javascript
##为了构造可执行文件,链接器必须完成两个主要任务:
* 1、符号解析(symbol resolution)。目标文件定义和引用符号。符号解析的目的是将每个符号引用刚好和一个符号定义联系起来。
* 2、重定位(relocation)。编译器和汇编器生成从地址0开始的代码和数据节。链接器通过把每个符号定义与一个存储器位置联系起来,然后修改所有对这些符号的引用,使得它们指向这个存储器位置,从而重定位这些节。
#目标文件
##目标文件有三种形式:
* 1、可重定位目标文件。包含二进制代码和数据,其形式可以在编译时与其他可重定位目标文件合并起来,创建一个可执行目标文件。
* 2、可执行目标文件。包含二进制代码和数据,其形式可以被直接拷贝到存储器并执行。
* 3、共享目标文件。一种特殊类型的可重定位目标文件,可以在加载或者运行地被动态地加载到存储器并链接。
编译器和汇编器生成可重定位目标文件(包括共享目标文件)。链接器生成可执行目标文件。从技术上来说,一个目标模块就是一个字节序列,而一个目标文件就是一个存放在磁盘文件中的目标模块。
可重定位目标文件
#sys_execve的内部处理过程
装载和启动一个可执行程序依次调用以下函数:
sys_execve() -> do_execve() -> do_execve_common() -> exec_binprm() -> search_binary_handler() -> load_elf_binary() -> start_thread()
# 浅析动态链接的可执行程序的装载
* 可以关注ELF格式中的interp和dynamic。
* 动态链接库的装载过程是一个图的遍历。
* 装载和连接之后ld将CPU的控制权交给可执行程序。
#实验内容
##实验概述
```javascript
编程使用exec*库函数加载一个可执行文件,动态链接分为可执行程序装载时动态链接和运行时动态链接,编程练习动态链接库的这两种使用方式,详细内容参考本周第二节;
使用gdb跟踪分析一个execve系统调用内核处理函数sys_execve ,验证您对Linux系统加载可执行程序所需处理过程的理解,详细内容参考本周第三节;
实验步骤
1、更新menu,用test.c覆盖test_exec.c;
2、把init 和 hello 放到了rootfs.img目录下,所以在执行exec命令的时候就相当于自动了加载了hello程序
3、执行exec功能
4、运行stopped的menu
5、gdb进行跟踪分析
总结
如何使用Linux内核装载和启动一个可执行程序
步骤
- 创建一个新进程
- 新进程调用execve()系统调用执行指定的ELF文件
- 调用内核的入口函数sys_execve(),sys_execve()服务例程修改当前进程的执行上下文;
自己的理解
- 当前进程的正文、数据、堆和栈段被exec覆盖了
- 当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行。
- 因为调用exec并不创建新进程,所以前后的进程ID并未改变。