目录
Linux内核装载ELF过程简介
用户层:
用户层bash进程会调用fork
系统调用创建一个新的进程,然后新的进程调用execve()
系统调用执行指定的ELF文件
,原先的bash进程继续返回等待刚才启动的新进程结束,然后继续等待用户输入命令。
扩展学习:https://blog.csdn.net/qq_41453285/article/details/89006116
int execve(const char *filename,char *const argv[], char *const envp[]);
//filename = 被执行的程序文件名
//argv[] = 执行的参数
//envp[] = 环境变量
利用fork()、execlp()实现minibash:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
char buf[1024]={0};
pid_t pid;
while(1)
{
printf("minibash$");
scanf("%s",buf);
pid = fork();
if(pid == 0)//子进程
{
if(execlp(buf,0) < 0){
printf("exec error/n");
}
}else if(pid > 0)//父进程
{
int status;
waitpid(pid,&status,0);//等待子进程返回
printf("子进程执行完毕!/n");
}else{
//返回-1 创建进程失败!
printf("创建进程失败!/n");
}
}
return 0;
}
内核层
在用户层调用execve
函数后,进入到内核层,Linux内核开始进行真正的装载工作。
execve() 系统调用相应的入口是sys_execve()
,在源码arch/i386/kernel/Process.c
中被定义。
最终sys_execve会调用do_execve()
函数,do_execve函数会首先查找被执行的文件,如果找到文件,读取文件的前128
字节,用来判断文件Magic(魔数)
。
确定是ELF文件后,最后会调用load_elf_binary()
函数来进行ELF文件的装载,该文件被定义在fs/Binfmt_elf.c
中。(函数的主要实现步骤):
(1) 检查ELF可执行文件格式的有效性,比如Magic(魔数)、程序头表(段表)的数量。
(2) 寻找动态链接的 ".interp"段,设置动态链接器的路径。
(3) 根据ELF可执行文件的程序头表(段表),对ELF文件进行映射,代码段、数据段、只读数据段。
(4) 初始化ELF进程环境,比如进程启动时EDX寄存器的地址应该是"DT_FINI"地址。
(5) 将系统调用的返回地址修改成"ELF可执行文件入口点",(如果是静态链接,那么入口点是"e_entry"),(如果是动态链接,那么入口点是"动态链接器")
(6) laod_elf_binary() ret -> do_execve() ret -> sys_execve() ret[返回已被修改] -> eip =(ELF入口) 开始执行ELF程序,装载完毕,进程执行。
原创文章,作者:wdmbts,如若转载,请注明出处:https://blog.ytso.com/267676.html