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

来源:互联网 发布:在线视频下载软件 编辑:程序博客网 时间:2024/05/22 05:32

“郭孟琦(与最后申请证书的姓名务必一致) + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ”


以下都是梦话,感觉是快坚持不住了!


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

从刚学C语言开始就知道程序 通过 编译 汇编  链接 产生最终生成一个exe可执行文件,但是到底exe是怎么运行的?实际上并没有给出答案。在这里根据计组所学的东西我认为肯定是什么机制修改了ip从而使cpu开始执行程序中的指令。


这周课里我知道了可执行文件实际上也是一种ELF文件,也是具有一定的文件格式,通过readelf和objdump工具可以分析其内部结构。


readelf -h 反映了头信息,这里显示了该程序的入口地址(0x8048d0a)


objdump则反映了section的size和offset,对helloword而言其执行语句都在.text段,当然还有很多很多的段,这里不具体分析了。

有意思的是可以看出入口地址(0x8048d0a)就指向.text段中的内容。

在大致了解了一个可执行程序的本质后,开始正式探究Linux内核如何装载和启动一个可执行程序。

首先linux是通过execve系统调用来执行一个程序的。因此用gdb追踪了sys_execve。具体使用了老师提供的menu。

在代码中首先fork了一个子进程,并且在子进程中调用了execlp来执行helloworld

在sys_execve出设置断点发现执行过程为

do_execve -> do_execve_common ->  exec_binprm

在这里开始对elf文件进行解析
search_binary_handler符合寻找文件格式对应的解析模块。


对于ELF格式的可执行文件fmt->load_binary(bprm);执行的应该是load_elf_binary

因此在load_elf_binary处设置断点

追踪发现了

start_thread(regs, elf_entry, bprm->p);

而elf_entry就在前边的

elf_entry = load_elf_interp(&loc->interp_elf_ex,    interpreter,            &interp_map_addr,            load_bias);

或者

elf_entry = loc->elf_ex.e_entry;

赋值,这2句取决于是否有动态链接器。当然在helloworld中是使用了后者,也就是elf文件中的入口地址(0x8048d0a)(应该就是main函数)


这样堆栈sp和ip的变化就都是在

start_thread(regs, elf_entry, bprm->p);

198start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)199{200set_user_gs(regs, 0);201regs->fs= 0;202regs->ds= __USER_DS;203regs->es= __USER_DS;204regs->ss= __USER_DS;205regs->cs= __USER_CS;206regs->ip= new_ip;207regs->sp= new_sp;208regs->flags= X86_EFLAGS_IF;209/*210 * force it to the iret return path by making it look as if there was211 * some work pending.212 */213set_thread_flag(TIF_NOTIFY_RESUME);214}

中发生了变化,用老师的话说就是 蝴蝶已经变成周公了。

返回用户态后新的eip开始执行helloword了。


补充一下新的sp又是谁呢?

根据参数他是bprm->p

实际上bprm是在 do_execve_common()中

分配新的进程地址空间

bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);


而p
unsigned long p; /* current top of mem */
应该是栈底


我对“Linux内核装载和启动一个可执行程序”的理解可以用一个流程图来表示


0 0
原创粉丝点击