Linux内核如何装载和启动一个可执行程序

Linux内核如何装载和启动一个可执行程序

“20135224陈实 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ”

第一部分

可执行文件的创建——预处理、编译和链接 
简单hello.c的预处理、编译和链接方式(对应图进行操作步骤):                                       

shiyanlou:Code/ $ vi hello.c                                         

shiyanlou:Code/ $ gcc -E -o hello.cpp hello.c -m32

(预处理)                   

shiyanlou:Code/ $ vi hello.cpp                                      

shiyanlou:Code/ $ gcc -x cpp-output -S -o hello.s hello.cpp -m32

(编译)

     

shiyanlou:Code/ $ vi hello.s                                        

shiyanlou:Code/ $ gcc -x assembler -c hello.s -o hello.o -m32         

shiyanlou:Code/ $ vi hello.o                                        

shiyanlou:Code/ $ gcc -o hello hello.o -m32 

(链接)     

              

关于链接:

生成ELF文件:

对于目标文件,至少有编译后的机器指令代码、数据,也还包括了链接时所须要的一些信息,比如符号表、调试信息、字符串等。Linux是可以支持多种可执行文件格式的,所有的格式处里信息用一个结构体存储在一个链表中,其中的load_binary是一个函数指针,对应于该中格式的可执行文件的加载方式;要想支持一种新的可执行文件,只需要向链表中注册一个新的format结构体就可以了,此种设计类似观察者模式,具有很好的扩展性。

第二部分

GDB跟踪查看:

实验笤俑虚拟机启动内核:(qemu)

设置断点在sys_execve处:

程序跑动截图:

 命令行参数和shell环境,一般我们执行一个程序的Shell环境,我们的实验直接使用execve系统调用。Shell本身不限制命令行参数的个数, 命令行参数的个数受限于命令自身例如,int main(int argc, char *argv[]又如, int main(int argc, char *argv[], char *envp[]),Shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数int execve(const char * filename,char * const argv[ ],char * const envp[ ]);库函数exec*都是execve的封装例程sys_execve内部会解析可执行文件格式do_execve -> do_execve_common -> exec_binprm

总结:

对于linux如何装载和启动的多半都是这样的步骤:先处理文件必要提前操作,通过GCC工具弄成cpu可以看懂与执行的文件格式,然后可以通过execve API启动一个新进程,该API又呼叫sys_execve系统调用,负责将新的程序代码和数据替换到新的进程中,打开可执行文件,载入依赖的库文件,申请新的内存空间,最后执行 start_thread(regs, elf_entry, bprm->p) ,设置 new_ip, new_sp ,完成新进程的代码和数据替换,然后返回,接下来就是执行新的进程代码了。

 

原文地址:https://www.cnblogs.com/chuishi/p/5371649.html