2018-2019-1 20189213《Linux内核原理与分析》第八周作业

可执行程序工作原理

书本重要知识总结

1.ELF文件
ELF(Excutable and Linking Format)即可执行的和可链接的格式,是一个目标文件格式的标准。通过readelf -h hello查看可执行文件hello的头部(-a查看全部信息,-h只查看头部信息),头部里面注明了目标文件类型ELF32。Entry point address是程序入口,地址为0x400400,即可执行文件加载到内存中开始执行的第一行代码地址。头部后还有一些代码数据等等。可执行文件的格式和进程的地址空间有一个映射的关系,当程序要加载到内存中运行时,将ELF文件的代码段和数据段加载到进程的地址空间。
1508946-20181130132059462-236506720.png
ELF文件里面分为三种目标文件:
可重定位文件——文件中保存着代码和适当的数据,用来和其它的目标文件一起来创建一个可执行文件、静态库文件或者是一个共享目标文件(主要是.o文件);
可执行文件——文件中保存着一个用来执行的程序,该文件指出了exec(BA_OS)如何来创建程序进程映象(操作系统怎么样把可执行文件加载起来并且从哪里开始执行);
共享文件——文件中保存着代码和合适的数据,用来被两个链接器链接。第一个是链接编辑器(静态链接),可以和其它的可重定位和共享目标文件来创建其它的object。第二个是动态链接器,联合一个可执行文件和其它的共享目标文件来创建一个进程映象。
ELF格式简介:
①ELF文件的索引表
②ELF Header结构
③Section Header结构
④Program Header结构

2.可执行程序的预处理、编译、汇编、链接
gcc –E hello.c -o hello.i //预处理
gcc -S hello.i -o hello.s -m32//编译
gcc -c hello.s -o hello.o -m32 //汇编
gcc hello.o -o hello -m32 //链接
1508946-20181130173121523-1803048577.png
用gcc hello.o -o hello.static -m32 -static进行静态编译,得到的hello.static把C库里需要的东西也放到可执行文件里了。用命令ls –l,可以看到hello只有5K左右,hello.static比700K还有多一点。

3.动态链接
动态链接有装载时动态链接和运行时动态链接两种方式。
下面对动态链接实例进行分析:
首先是各个对应头文件和函数:
shlibexample.h:
1508946-20181130184639234-215352158.png
shlibexample.c:
1508946-20181130184808568-155575925.png
dlllibexample.h:
1508946-20181130184857810-6511749.png
dlllibexample.c:
1508946-20181130184943457-1793504830.png
分别以共享库和动态加载共享库的方式使用libshlibexample.so文件和libdllibexample.so文件:
1508946-20181130183123349-471886406.png
编译main.c,注意这里只提供shlibexample的-L(库对应的接口头文件所在目录)和-l(库名,如libshlibexample.so去掉lib和.so的部分),并没有提供dllibexample的相关信息,只是指明了-ldl:
主函数main.c:
1508946-20181130185015449-86076057.png
1508946-20181130184507180-1596155399.png

实验:使用gdb跟踪分析execve系统调用内核处理函数sys_execve。

首先还是将menu目录删除,用git命令复制一个新的menu目录,用test_exec.c将test.c覆盖,然后重新编译rootfs:
1508946-20181201094002662-215300980.png
发现在MenuOS中使用help命令可以看到增加了exec命令,执行exec指令发现比fork指令增加了一行输出“helloworld!”,实际上是新加载了一个可执行程序来输出了一行语句:
1508946-20181201093943027-578390237.png
查看代码我们发现,在test.c中新增了exec函数:
1508946-20181201094417713-347955346.png
在Makefile中不仅编译了hello.c,还在生成根文件系统时把init和hello都放到rootfs.img中:
1508946-20181201094808149-1506017033.jpg
下面我们使用对gdb进行跟踪分析:
首先还是冻结内核,加载符号表并设置端口,准备单步调试:
1508946-20181201100410840-975724276.png
然后分别在sys_execve、load_elf_binary、start_thread处设置断点:
1508946-20181201101121542-1968541208.png
然后三次执行后,在执行exec命令后停在如下图所示位置:
1508946-20181201101329618-1618400086.png
使用list列出相关代码,使用step进入sys_execve函数内部发现调用了“do_execve()”函数继续执行到“load_elf_binary”处的断点:
1508946-20181201101620998-2049234803.png
1508946-20181201101938296-255859200.png
继续执行到“start_thread”处的断点,因为是静态链接,“elf_entry”指向了可执行文件中定义的入口地址,使用po new_ip指令打印其指向的地址,“new_ip”是返回到用户态的第一条指令的地址:
1508946-20181201102036922-1424154220.png
1508946-20181201102219684-655527995.png
查看hello的elf头部,看定义的入口地址与new_ip所指向的地址是否一致:
1508946-20181201103025113-25799881.png
发现确实是一致的。

问题

1.32位可执行文件Addr会显示类似0x8048000的地址,而我的这里程序入口地址是0x400400,可能与因为这是64位的系统有关。
2.进行预处理编译汇编链接时,使用自己的虚拟机在链接这一步时无法成功,初次感觉时gcc版本原因,但更换最新版gcc之后还是不行,不知道原因在哪。
1508946-20181130134354465-126284747.png
1508946-20181130171726945-1034038767.png
1508946-20181130173403314-1962568869.png

总结

对“Linux内核装载和启动一个可执行程序”的理解:
当linux内核或程序使用fork函数创建子进程后,子进程往往要调用一种exec函数(exec家族的一种)以执行另一个程序;在调用一种exec函数时,该进程执行的程序完全被替换为新程序,而新程序则从其main函数处开始执行,因为调用exec函数并不创建新进程,所以前后的进程ID并未改变,或者说exec函数只是用了一个全新的程序替换了当前进程的正文、数据段和堆栈段。

转载于:https://www.cnblogs.com/aiYY/p/10042359.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值