标签:
这周的实验是关于Linux内核如何加载一个可执行程序的。下面开始实验:
打开实验楼环境,重新下载编译内核,然后小s大s,启动停止后用gdb打好断点,开始调试。
下面分析:
1.先看ELF文件的格式。ELF头描述了该文件的组织情况,ELF文件默认从0x8048000开始加载,文件头中Entry point address的内容为程序实际入口,启动一个刚加载过可执行文件的进程时就从此处执行。使用命令readelf -h hello可以查看文件的elf头。具体结构定义如下:
ELF在object文件中有三种主要的目标文件类型:
1)一个可重定位(relocatable)文件保存着代码和适当的数据,用来和其他的object文件一起来创建一个可执行文件或者是一个共享文件。主要是.o文件
2)一个可执行(executable)文件保存着一个用来执行的程序;该文件指出了exec(BA_OS)如何来创建程序进程映象。
3)一个共享object文件保存着代码和合适的数据,用来被下面的两个链接器链接。第一个是连接编辑器,可以和其他的可重定位和共享object文件来创建其他的object。第二个是动态链接器,联合一个可执行文件和其他的共享object文件来创建一个进程映象。主要是.so文件
3)在shell中输入可执行程序所需的参数,shell程序把这些参数用参数传递的方式传给execve系统调用,然后execve系统调用以系统调用参数传递的方式传给sys_execve,最后sys_execve在初始化新程序的用户态堆栈时,将这些参数放在main函数取参数的位置上。
4)如果当前系统中没有所需要的资源,那么新进程会被挂起,直到有资源唤醒新进程,转为可运行态。
3.关于静态链接的可执行程序和动态链接的可执行程序,它们在处理execve系统调用返回时的比较:
1)对于静态链接的可执行程序,elf_entry是新程序的执行起点。execve系统调用会调用sys_execve,sys_execve再调用do_execve,do_execve再调用do_execve_common,do_execve_common再调用exec_binprm。在exec_binprm中:对于ELF文件格式,fmt函数指针实际会执行load_elf_binary,load_elf_binary会调用start_thread,在start_thread中通过修改内核堆栈中EIP的值,使其指向elf_entry,让执行流程跳转到elf_entry执行。
2)对于动态链接的可执行程序,需要先加载链接器ld,elf_entry = load_elf_interp(…) 将CPU控制权交给ld来加载依赖库,ld完成加载工作后将CPU控制权还给新进程。
3) 载入时间和执行时间对比
-静态链接是在生成可执行程序的时候就把库中的内容加入到程序中,即一开始就把所有模块都加载进入内存
-装载时动态链接是在将功能模块读入内存时把动态库中调用到的相关模块的内容载入内存
-运行时动态链接是在执行程序调用到模块内容时再将动态库中的相应模块载入到内存。
总结:
新的可执行程序通过修改内核堆栈EIP作为新程序的起点,从new_ip开始执行后start_thread把返回到用户态的位置从Int 0x80的下一条指令变成新加载的可执行文件的入口位置。当执行到execve系统调用时,陷入内核态,用execve加载的可执行文件覆盖当前进程的可执行程序,当execve系统调用返回时,返回新的可执行程序的执行起点(main函数位置),所以execve系统调用返回后新的可执行程序能顺利执行。execve系统调用返回时,如果是静态链接,elf_entry指向可执行文件规定的头部(main函数对应的位置0x8048***);如果需要依赖动态链接库,elf_entry指向动态链接器的起点。动态链接主要是由动态链接器ld来完成的。
标签:
原文地址:http://www.cnblogs.com/qinzhoaqi/p/5372428.html