第七周、可执行程序的装载
一、可执行程序是如何产生的?
(1).c文件gcc汇编形成.s和.asm汇编代码;
(2)汇编代码经过gas变成.o目标文件;
(3)目标文件变成可执行文件;
(4)可执行文件loader之后存储。
预处理:gcc –E –o hello.cpp hello.c –m32
编译:gcc –x cpp-output –S –o hello.s hello.cpp –m32 //编译为汇编代码
gcc –x assembler –c hello.s –o hello.o –m32 //编译为目标代码
链接:gcc –o hello hello.o –m32
静态链:gcc –o hello.static hello.o –m32 –static
二、目标文件格式ELF
- 目标文件三种形式
(1)可重定位文件(用来和其他object文件一起创建下面两种文件)——.o文件
(2)可执行文件(指出了应该从哪里开始执行)
(3)共享文件(主要是.so文件,用来被链接编辑器和动态链接器链)
2.ELF格式:
(左半边是ELF格式,右半边是执行时的格式)
三、新的可执行文件是从哪开始执行的?
当execve()系统调用终止且进程重新恢复它在用户态执行时,执行上下文被大幅度改变,要执行的新程序已被
映射到进程空间,从elf头中的程序入口点开始执行新程序。
如果这个新程序是静态链接的,那么这个程序就可以独立运行,elf头中的这个入口地址就是本程序的入口地址。
如果这个新程序是动态链接的,那么此时还需要装载共享库,elf头中的这个入口地址是动态链接器ld的入口地址。
四、 为什么execve系统调用返回后新的可执行程序能顺利执行?
execve和fork都是特殊一点的系统调用:一般的都是陷入到内核态再返回到用户态。 fork父进程和一般进程调度一样,子进程返回到一个特定的点ret_from_fork,子进程是从ret_from_fork开始执行然后返回到用户态; execve特殊:执行到可执行程序--陷入内核--构造新的可执行文件--覆盖掉原可执行程序--返回到新的可执行程序,作为起点(也就是main函数) ,需要构造他的执行环境。
五、对于静态链接的可执行程序和动态链接的可执行程序execev系统调用返回时会有什么不同?
静态链接:elf_entry指向可执行文件的头部,一般是main函数;
动态链接:elf_entry指向ld的起点。
六、实验过程:
1.更新menu:
2.把init 和 hello 放到了rootfs.img目录下,所以在执行exec命令的时候就相当于自动了加载了hello程序:
3.gdb进行跟踪分析:
4.设置断点后,在MenoOS中输入exec进行调试:
七、总结:Linux内核如何装载和启动一个可执行程序:
1. 创建新进程
2. 新进程调用execve()系统调用执行指定的ELF文件
3. 调用内核的入口函数sys_execve(),sys_execve()服务例程修改当前进程的执行上下文;
当ELF被load_elf_binary()装载完成后,函数返回至do_execve()在返回至sys_execve()。ELF可执行文件的入口点取决于程序的链接方式:
1. 静态链接:elf_entry就是指向可执行文件里边规定的那个头部,即main函数处。
2. 动态链接:可执行文件是需要依赖其它动态链接库,elf_entry就是指向动态链接器的起点。
多进程、多用户、虚拟存储的操作系统出现以后,可执行文件的装载过程变得非常复杂。引入了进程的虚拟地址空间;然后根据操作系统如何为程序的代码、数据、堆、栈在进程地址空间中分配,它们是如何分布的;最后以页映射的方式将程序映射进程虚拟地址空间。