实验七 可执行程序的装载

王康 + 原创作品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

1,预处理,编译,链接和目标文件的格式:

1,

wpsFF54.tmp

wpsFF74.tmp

预处理为cpp预处理文件---hello.s汇编代码--hello.o二进制目标文件(已经含有了机器指令)--链接为可执行文件如下图,hello为二进制文件(hello和hello.o都是ELF格式文件)

wpsFF75.tmp

均是使用共享库的,如print使用lib.c

如果静态编译就加一个-static,是把所有需要的依赖文件都放入文件内部:从文件大小可以看出

wpsFF76.tmp,2,文件格式

wpsFF77.tmp

wpsFF78.tmp

目标文件一般也叫abi,已经是二进制兼容的格式(兼容指已经适用到了某一种cpu体系结构)

ELF主要有3种主要文件:

wpsFF98.tmp

第一个是.o文件,第二个就是操作系统如何加载可执行文件以及从哪里开始执行

wpsFF99.tmp

很多section和segment;header保存roadmap

wpsFF9A.tmp

TYPE指明是三种ELF文件中哪一种;入口地址;最后是数据代码段的源数据

wpsFF9B.tmp

Text segment拷贝到进程地址空间时,要映射拷贝到进程的0x8048100,这样可执行文件和进程地址空间就有了映射关系。

wpsFFAC.tmp

wpsFFAD.tmp

x86是4G进程地址空间,最上1G是内核用,0xc000之上的是内核访问,之下就是用户可访问;

一般来讲头部大小入口点位置是0x8048到0x8048300,启动一个刚加载过新的可执行文件进程就是从这里开始执行(因为不同于fork的只是从原进程开始执行);

wpsFFAE.tmp

wpsFFAF.tmp

wpsFFB0.tmp

2,可执行程序,共享库和动态链接

wpsFFB1.tmpshell所做的工作:

wpsFFB2.tmp

wpsFFC2.tmp

实际ls就是一个可执行程序;envp还可接收shell的环境变量

wpsFFC3.tmp

为了防止shell被覆盖所以先fork一个子进程,然后execlp加载可执行程序ls,这里环境变量是NULL(取决于你main函数接收与否环境变量)。

wpsFFC4.tmp

wpsFFC5.tmp

fork一个子进程完全复制父进程,调用exec时候要加载的可执行程序把原来进程堆栈覆盖掉,用户态堆栈也被清空,那么argv和envp是如何进入新的可执行程序堆栈呢?

wpsFFC6.tmp

传给内核处理函数execve后,内核处理函数会在创建一个新的可执行程序用户态堆栈时会帮我们拷贝到新的可执行程序的上下文环境。

wpsFFC7.tmp

即先execve再sys_execve

wpsFFC8.tmp

wpsFFE9.tmp

.so文件(windows为.dll),对于动态链接库来讲因为实现都是一样的如上图两种编译,所以在装载和运行时都可以加载它。

wpsFFEA.tmp

wpsFFEB.tmp

wpsFFEC.tmp

wpsFFED.tmp

wpsFFEE.tmp

dlfcn是动态加载

wpsFFEF.tmp

加载动态装载库就需要dlopen,然后声明一个函数指针,根据函数名找到然后使用func()

wpsFFFF.tmp

编译执行main:

wps1.tmp

-L指明.h文件目录,-l指明库名;export把当前目录加入,否则要放入默认的lib和usrlib下;

-ldl动态加载器加载进来

3,可执行程序的装载

1,wps2.tmp

可执行程序装载也是系统调用,但是execve内核处理过程和fork一样比较特殊。(fork两次返回,子进程从特定的ret_from_fork开始执行然后返回用户态;执行execve陷入内核态,加载可执行文件,覆盖当前进程的可执行程序覆盖掉,当execve返回已经不是原来的可执行而是新的可执行程序了,执行起点也到了main)

wps3.tmp

wps4.tmp

最后根据给出的可执行文件,search_binary_hadler加载文件头部,需要能够解析文件格式例如ELF格式的内核模块

wps14.tmp

fmt是链表一个节点,可以load_binary(bprm)解析ELF文件位置

wps15.tmp

load_binary对应的函数是全局变量elf_format,把load_elf_binary赋给了函数指针load_binary(结构体的一个成员);再init_elf_binfmt,把elf_format变量注册进了内核链表&fmt

当出现ELF格式文件时候,观察者可以自动执行

wps16.tmp

被观察者

wps17.tmp

wps18.tmp

这里有一个start_thread:

wps19.tmp

pt_regs

wps1A.tmp

用newip替换掉,这个newip是如何来的呢?

wps1B.tmp

对于静态文件,elf_entry就是可执行文件格式头里的entry

wps1C.tmp

2,sys_execve的内部处理过程

wps1D.tmp

有获得文件名和参数,环境变量

wps1E.tmp

wps1F.tmp

wps20.tmp

wps31.tmp

创建了结构体bprm,还把环境变量和参数copy进去

wps32.tmp

之后是可执行文件的处理过程:

wps33.tmp

wps34.tmp

寻找可执行文件处理函数

wps35.tmp

wps36.tmp

下边是赋值的地方,结构体变量elf_format加载到链表register

wps37.tmp

wps38.tmp

wps39.tmp

wps3A.tmp

wps3B.tmp

interpreter如果需要动态链接,那么会加载interp动态链接器的起点,把它作为文件起点;

如果是静态链接直接把elf_entry作为起点

wps3C.tmp

所以start_thread有2种可能

wps3D.tmp

wps3E.tmp

所以start_kernel作用就是把下一条的0x80位置变成我们新加载可执行文件的entry的位置

3,

wps3F.tmp

wps4F.tmp

wps50.tmp

wps51.tmp

在makefile中:编译了hello.c,然后把hello和init都放入rootfs里了,就自动加载hello可执行文件

wps52.tmp

wps53.tmp

下面开始跟踪:

wps54.tmp

wps55.tmp

发现执行到chile process停止了,

wps56.tmp

wps57.tmpwps58.tmp

wps59.tmp

wps5A.tmp

发现newip和hello的entry地址是一样的

wps5B.tmp

4,

wps5C.tmp

wps5D.tmpwps6E.tmp

wps6F.tmp

wps70.tmp

wps71.tmp

返回的就是lld的地址了

wps72.tmp

一般是广度遍历

wps73.tmp

4,实验

wps74.tmp

wps75.tmp

wps76.tmp

wps77.tmp

5,总结

1、可执行程序的产生:C语言代码-->编译器预处理-->编译成汇编代码-->汇编器编译成目标代码-->链接成可执行文件,再由操作系统加载到内存中执行。

2、ELF格式中主要有3种可执行文件:可重定位文件.o,可执行文件,共享目标文件。

3、ELF可执行文件会被默认映射到0x8048000这个地址。

4、命令行参数和环境变量是如何进入新程序的堆栈的?

Shell程序-->execve-->sys_execve,然后在初始化新程序堆栈时拷贝进去。

先函数调用参数传递,再系统调用参数传递。

5、当前程序执行到execve系统调用时陷入内核态,在内核中用execve加载可执行文件,把当前进程的可执行文件覆盖掉,execve系统调用返回到新的可执行程序的起点。

6、动态链接库的装载过程是一个图的遍历过程,

ELF格式中的.interp和.dynamic需要依赖动态链接器来解析,entry返回到用户态时不是返回到可执行程序规定的起点,返回到动态链接器的程序入口。

posted on 2017-04-02 23:43  wk2016just  阅读(476)  评论(0编辑  收藏  举报