《Linux内核分析》第七周笔记 可执行程序的装载

20135132陈雨鑫 + 原创做品转载请注明出处 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ”linux

1、预处理、编译和连接和目标文件的格式shell

 

一、可执行程序是怎么来的?函数

理解编译连接的过程和ELF可执行文件格式spa

过程:命令行

​.c文件汇编成汇编代码.asm,3d

再汇编成目标码.o,调试

连接成可执行文件a.out,code

最后可执行文件就能够加载到内存中执行。blog

 

二、目标文件的格式ELF接口

1)主要有三种目标文件

可重定位文件、可执行文件、共享文件

2)ELF文件加载到内存是如何加载的呢?

代码、数据都加载到内存中,默认ELF文件从0x开始加载,开始加载的是头部,会有一些信息,因此入口地址0x(这个地方就是程序的实际入口,可执行文件加载到内存中开始执行的第一行代码从这里开始)

三、静态连接的ELF可执行文件和进程的地址空间

入口地址为0x8048*00

2、可执行程序、共享库和动态连接

一、使用exec*库函数加载一个可执行文件

(1)动态连接分为可执行程序转载时动态连接和运行时动态连接

1)在linux下动态连接的文件是.so

2)libshlibexample.so文件(生成一个共享库文件)

   libdllibexample.so(生成可动态加载文件)

(2)如果静态连接的,elf_entry就是指向可执行文件里边规定的那个头部,即main函数对应的位置,若这个可执行文件是须要依赖其它动态连接库的话,则elf_entry就是指向动态连接器的起点

二、

(1)execve:当前的可执行程序在执行execve这个系统调用的时候,它陷入到内核态,在内核态里它用这个execve加载的这个可执行文件把当前进程的可执行程序给覆盖掉,当execve这个系统调用返回的时候,返回的不是原来的那个可执行程序了,而是新的可执行程序了,它返回的是新的可执行程序的起点,即main函数大体的位置

(2)ELF文件加载到内存是如何加载的呢?

代码、数据都加载到内存中,默认ELF文件从0x开始加载,开始加载的是头部,会有一些信息,因此入口地址0x(这个地方就是程序的实际入口,可执行文件加载到内存中开始执行的第一行代码从这里开始)

•命令行参数和shell环境,通常咱们执行一个程序的Shell环境,咱们的实验直接使用execve系统调用。

•$ ls -l /usr/bin 列出/usr/bin下的目录信息

•Shell自己不限制命令行参数的个数, 命令行参数的个数受限于命令自身

•例如,int main(int argc, char *argv[])

•又如, int main(int argc, char *argv[], char *envp[])

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

•int execve(const char * filename,char * const argv[ ],char * const envp[ ]);

•库函数exec*都是execve的封装例程

 命令行参数和环境串都放在用户态堆栈中

装载时动态连接和运行时动态连接应用举例

动态连接分为可执行程序装载时动态连接和运行时动态连接,以下代码演示了这两种动态连接。

 •准备.so文件

shlibexample.h (1.3 KB) - Interface of Shared Lib Example

shlibexample.c (1.2 KB) - Implement of Shared Lib Example

 编译成libshlibexample.so文件

 1.$ gcc -shared shlibexample.c -o libshlibexample.so -m32

 dllibexample.h (1.3 KB) - Interface of Dynamical Loading Lib Example

dllibexample.c (1.3 KB) - Implement of Dynamical Loading Lib Example

 编译成libdllibexample.so文件

 1.$ gcc -shared dllibexample.c -o libdllibexample.so -m32

 •分别以共享库和动态加载共享库的方式使用libshlibexample.so文件和libdllibexample.so文件

 main.c  (1.9 KB) - Main program

 编译main,注意这里只提供shlibexample的-L(库对应的接口头文件所在目录)和-l(库名,如libshlibexample.so去掉lib和.so的部分),并无提供dllibexample的相关信息,只是指明了-ldl

1.$ gcc main.c -o main -L/path/to/your/dir -lshlibexample -ldl -m32

2.$ export LD_LIBRARY_PATH=$PWD #将当前目录加入默认路径,不然main找不到依赖的库文件,固然也能够将库文件copy到默认路径下。

3.$ ./main

4.This is a Main program!

5.Calling SharedLibApi() function of libshlibexample.so!

6.This is a shared libary!

7.Calling DynamicalLoadingLibApi() function of libdllibexample.so!

8.This is a Dynamical Loading libary!

 3、可执行程序的装载

1.可执行程序的装载相关关键问题分析 

(1)可执行程序的装载实际上至关于系统调用。execve系统调用比较特殊。

(2)sys_execve内核处理过程:

  • do_execve -> do_execve_common -> exec_binprm
  • 最后根据给出的文件名加载文件头部信息寻找对应的文件格式处理模块。

(3)fmt->load_binary(bprm):用来解析ELF格式文件的执行的位置,这个位置是load_elf_binary。

(4)内核是如何支持多种不一样可执行文件格式的?

本质上是观察者模式,经过修改内核堆栈中EIP的值做为新程序的起点。

(5)庄周(调用execve的可执行程序)入睡(调用execve陷入内核),醒来(系统调用execve返回用户态)发现本身是蝴蝶(被execve加载的可执行程序)。

2. sys_execve的内部处理过程

do_ execve调用do_ execve_ common,do_ execve_ common主要依靠exec_ binprm,其中重要的函数:search_binary_handler(bprm)。

  • 打开file文件,找到文件头部,把命令行参数和环境变量copy到结构体中:retval=copy_strings(bprm->envc, envp, bprm);
  • 寻找打开的可执行文件处理函数:ret= search_binary_handler(bprm);
  • 寻找可以解析当前可执行文件的模块,load_ binary加载这个模块,实际调用的是binfmt_ elf.c:retval=fmt->load_binary(bprm);
  • ELF可执行文件会被默认映射到0X8048000这个地址;
  • 须要动态连接的可执行文件先加载链接器ld;不然直接把ELF文件entry地址赋值给entry;
  • start_ thread(regs, elf_ entry, bprm->p)将CPU控制权交给ld来加载依赖库并完成动态连接。对于静态连接的文件elf_entry是新程序执行的起点

经过实验用gdb跟踪分析一个execve系统调用内核处理函数sys_execve ,验证对Linux系统加载可执行程序所需处理过程的理解

实验过程:

把menu删掉,从新克隆一份

 

进入test.c中查看:

进入makefile中查看:

开始使用gdb跟踪​:

 new_ip是返回到用户态的第一条指令的地址 看该可执行程序的入口点地址,发现和new_ip的位置是同样的。

退出调试状态,输入redelf -h hello能够查看hello的EIF头部:

 4、浅谈Linux内核装载和启动一个可执行程序

  检查ELF可执行文件的有效性,寻找动态连接的“.interp”段,设置动态连接器路径(与动态连接有关),根据ELF可执行文件的程序头表的描述,对ELF文件进行映射,好比代码,数据,只读数据,代码、数据都加载到内存中,默认ELF文件从0x开始加载,开始加载的是头部,会有一些信息,因此入口地址0x(这个地方就是程序的实际入口,可执行文件加载到内存中开始执行的第一行代码从这里开始),初始化ELF进程环境,好比进程启动时EDX寄存器的地址应该是DT_FINI的地址(和动态连接有关),将系统调用的返回地址修改成ELF可执行文件的入口,这个入口点取决于程序的连接方式,对于静态连接的可执行文件,如果静态连接的,elf_entry就是指向可执行文件里边规定的那个头部,即main函数对应的位置,若这个可执行文件是须要依赖其它动态连接库的话,则elf_entry就是指向动态连接器的起点。

相关文章
相关标签/搜索