码迷,mamicode.com
首页 > 系统相关 > 详细

《Linux内核与分析》第七周

时间:2016-04-09 16:55:45      阅读:217      评论:0      收藏:0      [点我收藏+]

标签:

by 21035130王川东

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

一、 EIF文件格式:

  1、ELF头部在文件的开始,描述文件的总体格式,保存了路线图,描述该文件的组织情况,即生成该文件系统的字的大小和字节顺序 段头部表用来描述ELF可执行文件与连续的存储段之间的映射关系。节头表包含了描述文件节区的信息,每个节区在表中都有一个项,给出节区的名称、节区大小这类心里。用于链接的目标文件(可重定向文件)必须包含节区头部表,而可执行文件可以没有。

 

  2、一个可重定位文件保存着代码和适当的数据,用来和其他的object文件一起来创建一个可执行文件或者是一个共享文件。一个可执行文件保存着一个用来执行的程序;该文件指出了exec(BA_OS)如何来创建程序进程映象。一个共享object文件保存着代码和合适的数据,用来被下面的两个链接器链接。第一个是连接编辑器[请参看ld(SD_CMD)],可以和其他的可重定位和共享object文件来创建其他的object。第二个是动态链接器,联合一个可执行文件和其他的共享object文件来创建一个进程映象。

 

  3、ELF头描述了该文件的组织情况,程序投标告诉系统如何创建一个进程的内存映像,section头表包含了描述文件sections的信息。当系统要执行一个文件的时候,理论上讲,他会把程序段拷贝到虚拟内存中某个段。

     4、程序从0x804800开始,可执行文件加载到内存中开始执行的第一行代码,一般静态链接将会把所有代码放在同一个代码段,动态连接的进程会有多个代码段

EIF文件头包含的内容:

技术分享

 

可执行文件的生成过程:

 

 

技术分享

 

可执行程序的执行环境:

    (1)命令行参数和shell环境,一般我们执行一个程序的Shell环境,实验中直接使用execve系统调用:

    (2)Shell本身不限制命令行参数的个数,命令行参数的个数受限于命令自身

    (3)Shell会调用execve将命令行参数和环境参数传递给可执行程序的main函数

    (4)库函数exec*都是execve的封装例程

可执行程序的装载:

(1)do_execve()

       在用户态下调用execve(),引发系统中断后,在内核态执行的相应函数是do_sys_execve(),而do_sys_execve()会调用do_execve()函数。do_execve()首先会读入可执行文件,如果可执行文件不存在,会报错。然后对可执行文件的权限进行检查。如果文件不是当前用户是可执行的,则execve()会返回-1,报permissiondenied的错误。否则继续读入运行可执行文件时所需的信息。

(2)search_binary_handler()

      接着系统调用search_binary_handler(),根据可执行文件的类型,查找到相应的处理函数。系统为每种文件类型创建了一个structlinux_binfmt,并把其串在一个链表上,执行时遍历这个链表,找到相应类型的结构。然后执行相应的load_binary()函数开始加载可执行文件。

(3) load_elf_binary()

       加载elf类型文件的handler是load_elf_binary(),它先读入ELF文件的头部,根据ELF文件的头部信息读入各种数据(headerinformation)。再次扫描程序段描述表,找到类型为PT_LOAD的段,将其映射(elf_map())到内存的固定地址上。如果没有动态链接器的描述段,把返回的入口地址设置成应用程序入口。完成这个功能的是start_thread(),start_thread()并不启动一个线程,而只是用来修改了pt_regs中保存的PC等寄存器的值,使其指向加载的应用程序的入口。这样当内核操作结束,返回用户态的时候,接下来执行的就是应用程序了。

(4) load_elf_interp()

       如果有动态链接库的存在,那么我们还需要映射动态链接库到共享区域,还要把控制权交给动态连接器(programinterpreter,ld.soinlinux)以处理动态链接的程序。内核搜寻段表,找到标记为PT_INTERP的段中所对应的动态连接器的名称,并使用load_elf_interp()加载其映像,并把返回的入口地址设置成load_elf_interp()的返回值,即动态链接器入口。当execve退出的时候动态链接器接着运行。动态连接器检查应用程序对共享连接库的依赖性,并在需要时对其进行加载,对程序的外部引用进行重定位。然后动态连接器把控制权交给应用程序,从ELF文件头部中定义的程序进入点开始执行。

注:(1)ELF可执行文件默认映射到0x8048000这个地址,需要动态链接的可执行文件先加载连接器ld,否则直接把elf文件entry地址赋值给entry即可。start_thread(regs, elf_entry, bprm->p)会将CPU控制权交给ld来加载依赖        库并完成动态链接;对于静态链接的文件elf_entry是新程序执行的起点。

     (2)execve与fork是比较特殊的系统调用,execve用它加载的可执行文件把当前的进程覆盖掉,返回之后就不是原来的程序而是新的可执行程序起点

     (3)sys_execve内部会解析可执行文件格式:do_execve -> do_execve_common -> exec_binprm,earch_binary_handler符合寻找文件格式对应的解析模块

实验过程:

1、更新menu内核

技术分享

2、查看test.c文件:增加了exec系统调用

技术分享

3、查看Makefile

技术分享

4、启动内核,

技术分享

可以看到exec函数的存在。

5、启动GDB调试进行调试,先停在sys_execve处
技术分享

技术分享

6、使用new_ip返回到用户态的第一条指令
退出调试状态,输入redelf -h hello可以查看hello的EIF头部

技术分享

总结:

      多进程、多用户、虚拟存储的操作系统出现以后,可执行文件的装载过程变得非常复杂。引入了进程的虚拟地址空间;然后根据操作系统如何为程序的代码、数据、堆、栈在进程地址空间中分配,它们是如何分布的;最后以页映射的方式将程序映射进程虚拟地址空间。 
      动态链接是一种与静态链接程序不同的概念,即一个单一的可执行文件模块被拆分成若干个模块,在程序运行时进行链接的一种方式。然后根据实际例子do_exece()分析了ELF装载的大致过程,中间实现了动态链接。

    Linux系统的运行方式与其他系统有所不同,,虽然有点难懂,但还是尽力学习,习了以后感觉很有趣,越来越有兴趣学习它了

 

《Linux内核与分析》第七周

标签:

原文地址:http://www.cnblogs.com/dvew/p/5371710.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!