20135239 益西拉姆 linux内核分析 可执行程序的装载

益西拉姆 + 原创做品请勿转载 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000 ”

week 7 可执行程序的装载

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

  • 从c语言到可执行程序的由来过程
  • 可执行文件的建立——预处理、编译和连接
  • 以helloworld为例shell

    • -s assembler 汇编
    • gcc -o hello hello.o -m32 是把hello.o连接成可执行文件。
    • ELF格式的文件是怎么回事?
    • vi hello 是使用共享库的。
    • static是静态库的意思。
    • 静态连接和动态连接是怎么回事?
    • 可执行文件的内部是什么?

2. 目标文件的格式ELF

目标文件及连接

  • 目标文件是什么样的?编辑器

    目标文件中的内容至少有编译后的机器指令代码、数据。没错,除了这些内容之外,目标文件中还包括了连接时所需要的一些信息,好比符号表、调试信息、字符串等。函数

ELF:exectable and linkable format.可执行和可连接的格式。工具

  • 它是文件格式的标准,可执行链接格式是UNIX系统实验室(USL)做为应用程序二进制接口(Application Binary Interface(ABI)而开发和发布的。工具接口标准委员会(TIS)选择了正在发展中的ELF标准做为工做在32位INTEL体系上不一样操做系统之间可移植的二进制文件格式。布局

  • ABI和目标文件格式是怎么回事?操作系统

    • ABI 又称目标文件,应用程序二进制接口。二进制兼容的问题。复杂来说就是:命令行

      - 符号修饰标准、变量内层布局、函数调用方式等这些跟可执行代码二进制兼容性相关的内容称为ABI(Application Binary Interface)。
    • 常见的ABI格式:

ELF( ELF: 可执行链接格式 )中的三种目标文件3d

  • 一个可重定位文件保存着代码和适当的数据,用来和其余的object文件一块儿来建立一个可执行文件或者一个共享文件。(**主要是.o文件 **)
  • 一个可执行文件保存着一个用来执行的程序;该文件指出了exec如何来建立程序进程映像。
  • 一个共享object(目标)文件保存着代码和合适的数据,用来被下面的两个连接器连接。第一个是连接编辑器,能够和其余的可重定位和共享object文件来建立其余的object。第二个是动态连接器,联合一个可执行文件和其余的共享object文件来文件来建立一个进程映像。
  • ELF目标文件的格式: 
  • ELF文件结构描述 
  • ELF文件头: 如何查看ELF文件的头部调试

    shiyanlou:Code/ $ readelf -h hellocode

  • 段头表

    - 目标文件中各节的位置和大小

    - 处于目标文件的末尾

  • 连接:
    • 连接是一个收集、组织程序所需的不一样代码 和数据的过程,以便程序能被装入内存并被执行。
  • 连接过程分为两步: - 空间与地址分配: - 扫描全部的输入目标文件,得到它们的各个段的长度、属性和位置,而且将输入目标文件中的符号定义和符号引用收集起来,统一放到一个全局符号表。这一步中,连接器将能得到全部输入目标文件的段长度,而且将它们合并,计算出输出文件中各个段合并后的长度与位置,并创建映射关系。 - 符号解析与重定位: - 使用上面第一步中收集到的全部信息,读取输入文件中段的数据、重定位信息,而且进行符号解析与重定位、调整代码中的地址等。事实上第二步是连接过程的核心,特别是重定位过程。

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

目标文件、可执行文件与进程空间

- 通常静态连接都会将全部代码放在一个代码段。 - 动态连接的进程会有多个代码段。

可执行目标文件及装入

  • 可执行目标文件与可重定位目标文件格式相似
  • 可执行目标文件的装入由装载器完成

典型的ELF可执行目标文件

处理目标文件的一些工具

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

装载可执行程序以前的工做。 - 可执行程序的执行环境: - 命令行参数和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的封装例程 - - 命令行参数和环境变量是如何保存和传递的? - 命令行参数和环境串都放在用户态堆栈中

- shell程序->>execve->> sys_execve
- 而后在初始化新程序堆栈时拷贝进去
- 先函数调用参数传递,在系统调用参数传递

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

动态连接分为可执行程序装载时动态连接和运行时动态连接

3.可执行程序的装载

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

sysexecve内核处理过程 - sysexecve内部会解析可执行文件格式

  • doexecve -> doexecvecommon -> execbinprm
  • searchbinaryhandler符合寻找文件格式对应的解析模块,以下(根据文件头部信息寻找对应的文件格式处理模块):
  • 下面的地方至关于被观察者代码里面的一部分:
  • 对于ELF格式的可执行文件fmt->loadbinary(bprm);执行的应该是loadelf_binary其内部是和ELF文件格式解析的部分须要和ELF文件格式标准结合起来阅读
  • Linux内核是如何支持多种不一样的可执行文件格式的?
  • 下面的属于观察者: 
  • ---elfformat 和 initelf_binfmt,这里是否是就是观察者模式中的观察者?(上面有答案。)
  • 可执行文件开始执行的起点在哪里?如何才能让execve系统调用返回到用户态时执行新程序?
  • 修改int 0x80压入内核堆栈的EIP
  • loadelfbinary -> ** start_thread** 经过修改内核堆栈中EIP的值做为新程序的起点。

sys_execve的内部处理过程

  • ELF可执行文件会被默认映射到0x8048000这个地址。
  • 须要动态连接的可执行文件先加载连接器ld 
  • 将CPU控制权交给ld来加载依赖库并完成动态连接。
  • 对于静态连接库的文件elf_entry是新程序执行的起点。

使用gdb跟踪sys_execve内核函数的处理过程

可执行程序的装载与庄生梦蝶的故事

  • 庄生梦蝶 —— 醒来迷惑是庄周梦见了蝴蝶仍是蝴蝶梦见了庄周?
  • 庄周(调用execve的可执行程序)入睡(调用execve陷入内核),醒来(系统调用execve返回用户态)发现本身是蝴蝶(被execve加载的可执行程序)

浅析动态连接的可执行程序的装载

  • 实际上动态连接库的依赖关系会造成一个图。
  • 是由内核负责加载可执行程序依赖的动态连接库吗?
  • 当一个问件是Interpreter是是依赖的。
  • 动态连接库的装载过程是一个图的遍历。
  • 装载和连接以后ld将CPU的控制权交给可执行程序。
相关文章
相关标签/搜索