Linux内核分析——可执行程序的装载
链接的过程
- 首先运行C预处理器cpp,将C的源程序(a.c)翻译成ASCII码的中间文件(a.i)
- 接着C编译器ccl,将a.i翻译成ASCII汇编语言文件a.s
- 接着运行汇编器as,将a.s翻译成可重定位的目标文件a.o
- 最终完全链接成可执行文件a.out
目标文件
- 目标文件有三种形式:
- 可重定位的目标文件
- 可执行目标文件
- 共享目标文件
ELF格式的可重定位目标文件的结构如下:
- .text:已编译程序的机器代码
- .rodata:只读数据
- .data:已初始化的全局C变量
- .bss:未初始化的全局C变量.在目标文件中这个节不占实际空间,仅是一个占位符.
- .sysmtab:一个符号表,存放在程序中被定义和引用的函数和全局变量的信息.
- .rel.text:当链接器把这个目标文件和其他文件结合时,.text节中的许多位置都需要修改.一般而言,任何调用外部函数或者引用全局变量的指令都要修改.另一个方面,调用本地函数的指令则不需要修改.
- .rel//.data:被模块定义或引用的任何全局变量的信息.
- .debug:一个调试符号表
- .line:原始C源程序中的行号和.text节中机器指令之间的映射.
- .strtab:一个字符串表,其中内容包括.symtab和.debug节中的符号表,以及节头部中的节名字.
可以通过readelf -h process查看的ELF文件的头部信息
如何将新程序的数据保存下来
通过shell程序调用execve将命令行参数和环境参数传递给可执行程序的main函数中。而后execve在创建新的用户态堆栈时,则将main函数中参数压入堆栈中。最终执行sys_execve来真正实现在系统下参数的传递。
当新的可执行文件被调用的时候,则旧的可执行文件所占有的空间会被新的可执行文件所占用,从而execve返回时,返回的并非为旧的可执行文件所产生的数据,而是新加载进来的可执行文件的返回数据,从而使新的可执行文件可以被执行。
可执行文件的相关点
start_thread通过修改内核堆栈中EIP的值作为新程序的起点
根据静态链接的可执行文件elf_entry就是可执行文件头中的起点entry,多为main函数对应的位置
若需要依赖动态链接库的话,则elf_entry则指向动态链接器的起点,即将CPU控制权交给ld来加载依赖库并完成动态链接
新的可执行程序被调用前,需要通过修改int 0x80压入内核堆栈的EIP
elf可执行文件会被默认映射到0x8048000这个地址上
execve在内核中的执行过程
- execve运行可执行程序的主要步骤:
删除已存在的用户区域:删除当前可执行文件所占有的用户部分中的堆栈空间
隐藏私有区域:为新程序的文本、数据和堆栈创建新的他空间,而这些空间是新的可执行文件所私有的,并且是写时拷贝的。
映射共享区域:如果ELF文件与共享目标连接,就需要动态链接,并映射至用户虚拟地址空间中的共享区域。
- 设置程序计数器:设置EIP,使其指向新的可执行文件的入口地址
如下图:
execve函数在内核中执行流程
在用户态中调用execve(),引发系统中断,在内核态中执行对应的函数sys_execve
sys_execve函数调用do_execve函数,该函数会读入可执行文件。
接下来系统会调用search_binary_handler,根据可执行文件的类型查找到相应的处理函数。根据每种文件创建一个struct linux_binfmt的结构体,并将其连接到一个链表智商,执行时候系统就会遍历这个链表,从而找到相应的结构。
从而调用对应的load_binary函数开始加载可执行文件。系统是通过load_elf_binary来加载elf类型的可执行文件。该函数会先读入ELF文件的头部,根据ELF文件的头部信息读入各种数据。
如果存在动态链接库,则需要将动态链接映射到共享区域之中。此时就需要使用load_elf_interp来加载映像,并把返回的入口地址设置为load_elf_interp的动态链接器的入口
如下图所示:
实验部分
实验目的
使用gdb跟踪sys_execve内核函数的处理过程,分析exec*函数对应的系统调用处理过程,理解Linux内核如何装载和启动一个可执行程序。
实验过程
执行MenuOS,其中装载了execve
设置断点
装载和运行一个可执行文件的顺序为:
sys_execve() -> do_execve() -> do_execve_common() -> exec_binprm() -> search_binary_handler() -> load_elf_binary() -> start_thread()
总结
当linux内核或程序(例如shell)用fork函数创建子进程后,子进程往往要调用一种exec函数以执行另一个程序。
当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始
池彬宁 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000
Linux内核分析——可执行程序的装载的更多相关文章
- 20135239 益西拉姆 linux内核分析 可执行程序的装载
益西拉姆 + 原创作品请勿转载 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ” week 7 可 ...
- 《Linux内核分析》期终总结
作者:杨舒雯,原创作品转载请注明出处,<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 目录: 1.通过简 ...
- Linux内核分析作业7:Linux内核如何装载和启动一个可执行程序
1.可执行文件的格式 在 Linux 平台下主要有以下三种可执行文件格式: 1.a.out(assembler and link editor output 汇编器和链接编辑器的输出) ...
- Linux内核分析之可执行程序的装载和启动
一.内容分析 1.可执行文件的创建 (1)预处理阶段 预处理过程读入源代码,检查包含预处理指令的语句和宏定义,并对源代码进行相应的转换,预处理过程还会删除程序中的注释和多余的空白字符.其中预处理指令主 ...
- 《Linux内核分析》第七周 可执行程序的装载
[刘蔚然 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000] WEEK SEVEN ...
- LINUX内核分析第七周学习总结:可执行程序的装载
LINUX内核分析第七周学习总结:可执行程序的装载 韩玉琪 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/cours ...
- 《Linux内核分析》第七周笔记 可执行程序的装载
20135132陈雨鑫 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ...
- Linux内核分析 笔记七 可执行程序的装载 ——by王玥
一.预处理.编译.链接和目标文件的格式 (一)可执行程序是怎么得来的? 1. 2.可执行文件的创建——预处理.编译和链接 shiyanlou:~/ $ cd Code ...
- LINUX内核分析第七周学习总结——可执行程序的装载
LINUX内核分析第六周学习总结——进程的描述和进程的创建 张忻(原创作品转载请注明出处) <Linux内核分析>MOOC课程http://mooc.study.163.com/cours ...
随机推荐
- 【SDOI2009】Bill的挑战
Description Sheng bill不仅有惊人的心算能力,还可以轻松地完成各种统计.在昨天的比赛中,你凭借优秀的程序与他打成了平局,这导致Sheng bill极度的不满.于是他再次挑战你.这次 ...
- read_csv报错Initializing from file failed
Python版本:Python 3.6 pandas.read_csv() 报错 OSError: Initializing from file failed,一般由两种情况引起:一种是函数参数为路径 ...
- 使用idea搭建SSM框架
搭建个SSM框架居然花费了我好长时间!特此记录! 需要准备的环境: idea 2017.1 jdk1.8 Maven 3.3.9 请提前将idea与Maven.jdk配置好,本次项目用的都是比较新的 ...
- 转://对于11gR2的集群relink
对于11gR2的集群relink参考MOS:Do I need to relink the Oracle Clusterware / Grid Infrastructure home after an ...
- 路由器不重启,是否ip就永远不变
今天发现公司的公网ip突然变了,没有人去动过路由器怎么会这样呢?经查原因如下:1.不一定,IP变化是每一次拨号重新获取的.2.路由器重启了,会自动拨号,获得IP3.但如果说因各种原因,掉线,路由器也会 ...
- php结合redis高并发下,悲观锁解决数据二次写入
悲观锁 在悲观锁的情况下,为了保证事务的隔离性,就须要一致性锁定读.读取数据时给加锁,其他事务无法改动这些数据.改动删除数据时也要加锁,其他事务无法读取这些数据. 在做数据缓存的时候,通常都是把数据从 ...
- MySQL比like语句更高效写法locate position instr find_in_set
你是否一直在寻找比MySQL的LIKE语句更高效的方法的,下面我就为你介绍几种. LIKE语句 SELECT `column` FROM `table` where `condition` like ...
- (2)free详解 (每周一个linux命令系列)
(2)free详解 (每周一个linux命令系列) linux命令 free详解 引言:今天的命令是用来看内存的free free 换一个套路,我们先看man free中对free的描述: Displ ...
- 多线程操作的方法(sleep,)setPriority(Thread.MIN_PRIORITY);yield();
在多线程中所有的操作方法都是从Thread类开始的,所有的操作基本都在Thread类中. 第一取得线程名字 a,在Thread类中,可以通过getName()方法,获得线程的名字,可以通过setNam ...
- Jmeter之mysql性能测试
Jmeter官网地址:https://jmeter.apache.org/ 作为开发人员,必要的性能测试还是需要掌握的,虽然配置druid可以比较直观获得sql的执行时间,那些表被访问的比较多等等,但 ...