2017-2018-1 20179215《Linux内核原理与分析》第八周作业
实验:ELF文件格式与程序的编译链接
一、可执行文件的创建
从源代码到可执行程序所要经历的过程概述:
源代码(.c .cpp .h)经过c预处理器(cpp)后生成.i文件,编译器(cc1、cc1plus)编译.i文件后生成.s文件,汇编器(as)汇编.s文件后生成.o文件,链接器(ld)链接.o文件生成可执行文件。gcc是对cpp、cc1(cc1plus)、as、ld这些后台程序的包装,它会根据不同的参数要求去调用后台程序。

以helloworld程序为例:
gcc -E -o hello.cpp hello.c -m32 //生成预处理文件
hello.cpp //预处理负责把include的文件包含进来及宏替换等工作
gcc -x cpp-output -S -o hello.s hello.cpp -m32 编译成汇编代码hello.s
gcc -x assembler -c hello.s -o hello.o -m32 编译成目标代码,得到二进制文件hello.o
gcc -o hello hello.o -m32 链接成可执行文件hello
./hello 运行hello文件
二、可执行文件的组成
(1)以ELF为格式的主要有三种文件:
①可重定位文件:保持着代码和适当的数据,用来和其他的object文件一起来创建一个可执行文件或者一个共享文件。例如.o文件。
②可执行文件:可以运行的文件。该文件指出了exec(BA_OS)如何来创建进程映象。再来联想下程序和进程的区别。到底这种可执行文件是进程还是程序?我们发现它的段中只含.text和.data一类的段,而不含有堆栈段。所以可以确定它只是程序。当它被操作系统调入内存开始执行时才会真正的成为进程。例如.out文件。
③共享object文件:保存着代码和数据,被两个链接器链接。一个是连接编辑器,可以和其他可重定位和共享object文件来创建其他的object。第二个是动态链接器,联合一个可执行文件和其他共享object文件来创建一个进程映像。
(2)ELF文件的头部:使用命令 readelf -h hello 查看hello文件的头:

ELF的头保存的是元数据,也就是路线图,描述了文件的组织情况。比如程序头表(program header table)告诉系统如何来创建一个进程的内存映像。section头表(section header table)包含描述文件sections的信息。每个section在这个表中有一个入口;每个入口给出了该section的名字,大小等等信息。ELF的剩余部分是sections,包括代码段,数据段。这些在程序变成进程映像时加载到内存的虚拟地址空间中,从ELF头开始加载,所以从图中可以看出真正的代码从0x400430开始,这才是真正的程序入口。静态链接时程序所需要的代码全部在代码段中,而动态链接就不一样了,它会在运行时候去找内存中间部分加载的库函数。
三、可执行程序的加载
(1)装载:可执行程序的执行环境shell。shell就是用户键入命令,加载并执行可执行程序的控制台.shell的本质就是提供图形化的界面,将用户写入的字符串解析成真正执行的命令或者说可执行程序。
有两个问题:
①真正执行程序的是什么程序或指令?答案是execve系统调用(库函数exec*都是execve的封装例程)。
②如何给该系统调用传参?也就是传参的默认格式是什么?答:shell会传入execve的参数有两种,一种是程序本身参数,也就是main的参数argc,argv;第二种是shell环境变量的参数,envp字符串数组中。
来看下execve的参数类型:
int execve(const char * filename,char * const argv[ ],char * const envp[ ]);
当然,有些程序的main函数是不处理环境变量参数的,例如常见的:
int main(int argc, char *argv[])。
但是有时候也支持,例如,所以这个时候传入的环境变量参数才会被解析使用。
int main(int argc, char *argv[], char *envp[])
(2)execve是如何从内核态将参数传给进程(假设该进程是用户态)的用户态堆栈的?

发现,仍是从用户态的数据段中复制到用户态堆栈中的。那么跟内核什么关系呢?执行execve的进程就是当前的shell,所以参数首先会被压在当前shell进程的内核堆栈中。关键在传入内核的参数是指针,所以内核(sys_ execve)要做的就是把指针的值复制回新进程的代码段,再复制到进程的用户堆栈段。初始化后的进程内存地址空间就是第二张图那样。也就解释了sys_ execve加载进程并初始化的作用、结果。shell每次都fork一个shell去执行命令,所以,当新进程起来后,启动它的shell结束,曾经保存的参数就不要了。
(3)两种动态链接
①装载时动态链接(Load-time Dynamic Linking):这种方法的前提是在编译之前已经明确知道要调用的动态库的哪些函数,编译时在目标文件中只保留必要的链接信息,而不含动态库函数代码;当程序执行时,调用函数的时候利用链接信息加载动态库函数代码并在内存中将其链接入调用程序的执行空间中(全部函数加载进内存),其主要目的是便于代码共享。(动态加载程序,处在加载阶段,主要为了共享代码,共享代码内存)
②运行时动态链接(Run-time Dynamic Linking):这种方式是指在编译之前并不知道将会调用哪些动态库函数,完全是在运行过程中根据需要决定应调用哪个函数,将其加载到内存中(只加载调用的函数进内存);并标识内存地址,其他程序也可以使用该程序,并获得动态库函数的入口地址。(动态库在内存中只存在一份,处在运行阶段)
四、使用gdb跟踪分析一个execve系统调用内核处理函数sys_execve
(1)增加execve系统调用指令:

(2)在Makefile找到启动内核命令:

(3)启动内核后,找到增加的exec命令,执行exec——新加载的执行程序来输出的“hello world”:

(4)冻结后gdb跟踪,设置断点:

(5)进入do_execve内部

(6)继续执行,到了load_elf_binary

(7)list后,可以看出静态链接时elf_interp为空

(8)再执行,跟踪到start_thread

五、总结:
新的可执行程序是从new_ ip开始执行,start_ thread实际上是把返回到用户态的位置从Int 0x80的下一条指令,变成了规定的新加载的可执行文件的入口位置,即修改内核堆栈的EIP的值作为新程序的起点。当执行到execve系统调用时,陷入内核态,用execve加载的可执行文件覆盖当前进程的可执行程序,当execve系统调用返回时,返回新的可执行程序的执行起点(main函数位置),所以execve系统调用返回后新的可执行程序能顺利执行。对于静态链接的可执行程序和动态链接的可执行程序execve系统调用返回时,如果是静态链接,elf_ entry指向可执行文件规定的头部(main函数对应的位置0x8048***);如果需要依赖动态链接库,elf_entry指向动态链接器的起点。
2017-2018-1 20179215《Linux内核原理与分析》第八周作业的更多相关文章
- 2017-2018-1 20179215《Linux内核原理与分析》第二周作业
20179215<Linux内核原理与分析>第二周作业 这一周主要了解了计算机是如何工作的,包括现在存储程序计算机的工作模型.X86汇编指令包括几种内存地址的寻址方式和push.pop.c ...
- 20169212《Linux内核原理与分析》第二周作业
<Linux内核原理与分析>第二周作业 这一周学习了MOOCLinux内核分析的第一讲,计算机是如何工作的?由于本科对相关知识的不熟悉,所以感觉有的知识理解起来了有一定的难度,不过多查查资 ...
- 20169210《Linux内核原理与分析》第二周作业
<Linux内核原理与分析>第二周作业 本周作业分为两部分:第一部分为观看学习视频并完成实验楼实验一:第二部分为看<Linux内核设计与实现>1.2.18章并安装配置内核. 第 ...
- 2018-2019-1 20189221 《Linux内核原理与分析》第九周作业
2018-2019-1 20189221 <Linux内核原理与分析>第九周作业 实验八 理理解进程调度时机跟踪分析进程调度与进程切换的过程 进程调度 进度调度时机: 1.中断处理过程(包 ...
- 2019-2020-1 20199329《Linux内核原理与分析》第九周作业
<Linux内核原理与分析>第九周作业 一.本周内容概述: 阐释linux操作系统的整体构架 理解linux系统的一般执行过程和进程调度的时机 理解linux系统的中断和进程上下文切换 二 ...
- 2019-2020-1 20199329《Linux内核原理与分析》第二周作业
<Linux内核原理与分析>第二周作业 一.上周问题总结: 未能及时整理笔记 Linux还需要多用 markdown格式不熟练 发布博客时间超过规定期限 二.本周学习内容: <庖丁解 ...
- 2019-2020-1 20209313《Linux内核原理与分析》第二周作业
2019-2020-1 20209313<Linux内核原理与分析>第二周作业 零.总结 阐明自己对"计算机是如何工作的"理解. 一.myod 步骤 复习c文件处理内容 ...
- 2018-2019-1 20189221《Linux内核原理与分析》第一周作业
Linux内核原理与分析 - 第一周作业 实验1 Linux系统简介 Linux历史 1991 年 10 月,Linus Torvalds想在自己的电脑上运行UNIX,可是 UNIX 的商业版本非常昂 ...
- 《Linux内核原理与分析》第一周作业 20189210
实验一 Linux系统简介 这一节主要学习了Linux的历史,Linux有关的重要人物以及学习Linux的方法,Linux和Windows的区别.其中学到了LInux中的应用程序大都为开源自由的软件, ...
- 2018-2019-1 20189221《Linux内核原理与分析》第二周作业
读书报告 <庖丁解牛Linux内核分析> 第 1 章 计算工作原理 1.1 存储程序计算机工作模型 1.2 x86-32汇编基础 1.3汇编一个简单的C语言程序并分析其汇编指令执行过程 因 ...
随机推荐
- struts2一个实例中遇到的问题
今天实现了一个登录功能的Struts2小程序. 期间遇到了许多问题,记忆犹新的是 (1)新版本的tomcat9和eclipse Neon Release (4.6.0) 发生了冲突,启动服务器的时候老 ...
- 【BZOJ4069】[Apio2015]巴厘岛的雕塑 按位贪心+DP
[BZOJ4069][Apio2015]巴厘岛的雕塑 Description 印尼巴厘岛的公路上有许多的雕塑,我们来关注它的一条主干道. 在这条主干道上一共有 N 座雕塑,为方便起见,我们把这些雕塑从 ...
- android菜鸟学习笔记20----Android数据存储(四))Android数据库操作
Android内置了一个名为SQLite的关系型数据库,这是一款轻量型的数据库,操作十分简便.SQLite与别的数据库不同的是,它没有数据类型.可以保存任何类型的数据到你所想要保存的任何表的任何列中. ...
- OLTP和OLAP
1 OLTP和OLAP online transaction processing,联机事务处理.业务类系统主要供基层人员使用,进行一线业务操作,通常被称为联机事务处理. online analyti ...
- iOS应用的执行原理
本文转自:http://www.cnblogs.com/oc-bowen/p/6061261.html http://www.cnblogs.com/oc-bowen/p/6061178.html 一 ...
- zip filter map 列表生成器
map map(function, list): 就是对list 中的每一个元素都调用function函数进行处理,返回一个map的对象 list一下就可以生成一个列表 或者for循环该对象就可以输出 ...
- python数据分析之:数据聚合与分组运算
在数据库中,我们可以对数据进行分类,聚合运算.例如groupby操作.在pandas中同样也有类似的功能.通过这些聚合,分组操作,我们可以很容易的对数据进行转换,清洗,运算.比如如下图,首先通过不同的 ...
- hash是什么?
最近读关于php内核的资料,发现php中 在实现变量以及数据类型的实现中大量使用哈希算法,并且非常细致做出了很多优秀的细节设计.比如:在 zend.hash.h 中 static inline ulo ...
- ABAP制作密码输入框
[转自 http://blog.csdn.net/saphome/article/details/6956911] 这几天做一个系统维护的程序,需要用户输入用户名和密码登录.可怎样实现输入密码显示星号 ...
- Python: generator, yield, yield from 详解
1.Generator Expressions 生成器表达式是用小括号表示的简单生成器标记法: generator_expression ::= "(" expression co ...