8.1. ELF

8.1.1. 概述

ELF(Executable and Linking Format)是一种对象文件的格式,用于定义不同类型的对象文件(Object files)中存放的对象、以及存放对象的格式。最初ELF由UNIX系统实验室(USL)开发和发布,作为应用程序二进制接口(ABI)的一部分。

ELF标准旨在通过为开发人员提供一套简化的软件开发流程二进制接口定义,可以在多种环境下运行。

8.1.2. 对象文件

对象文件(Object files)有如下三个种类

8.1.2.1. 可重定位的对象文件(Relocatable file)

这是由汇编器汇编生成的 .o 文件。后面的链接器(link editor)拿一个或一些 Relocatable object files 作为输入,经链接处理后,生成一个可执行的对象文件 (Executable file) 或者一个可被共享的对象文件(Shared object file)。

8.1.2.2. 可执行的对象文件(Executable file)

在Linux系统里面,存在两种可执行的东西。除了Executable file,另外一种就是可执行的脚本(如shell脚本)。注意这些脚本不是Executable file,它们只是文本文件,但是执行这些脚本所用的解释器就是 Executable file,比如 bash shell 程序。

8.1.2.3. 可被共享的对象文件(Shared object file)

这些就是所谓的动态库文件,也即.so文件。如果拿前面的静态库来生成可执行程序,那每个生成的可执行程序中都会有一份库代码的拷贝。如果在磁盘中存储这些可执行程序,那就会占用额外的磁盘空间;另外如果拿它们放到Linux系统上一起运行,也会浪费掉宝贵的物理内存。如果将静态库换成动态库,那么这些问题都不会出现。

8.1.3. ELF格式

ELF格式有两种

        Linking View
+--------------------------+
|       ELF header         |
+--------------------------+
|   Program header table   |
|        (optional)        |
+--------------------------+
|        Section 1         |
+--------------------------+
|           ...            |
+--------------------------+
|        Section n         |
+--------------------------+
|           ...            |
+--------------------------+
|   Section header table   |
+--------------------------+

       Execution View
+--------------------------+
|        ELF header        |
+--------------------------+
|   Program header table   |
+--------------------------+
|       Segment 1          |
+--------------------------+
|           ...            |
+--------------------------+
|       Segment n          |
+--------------------------+
|           ...            |
+--------------------------+
|   Section header table   |
|        (optional)        |
+--------------------------+

8.1.4.

8.1.4.1. .bss

bss段保存未初始化的全局变量和局部静态变量

8.1.4.2. .data and .data1

data段保存已初始化的全局变量和局部静态变量

8.1.4.3. .rodata

存放只读数据

8.1.4.4. .text

程序代码段

8.1.4.5. .comment

存放编译器版本信息等

8.1.4.6. .debug

存放调试信息

8.1.4.7. .dynamic

该段中保存了动态链接器所需要的基本信息,是一个结构数组,可以看做动态链接下ELF文件的文件头。存储了动态链接会用到的各个表的位置等信息。

8.1.4.8. .dynstr

该段是 .dynsym 段的辅助段

8.1.4.9. .dynsym

该段与 .symtab 段类似,但只保存了与动态链接相关的符号

8.1.4.10. .got

程序全局入口表

8.1.4.11. .plt

动态链接的跳转表

8.1.4.12. .hash

在动态链接下,需要在程序运行时查找符号,为了加快符号查找过程,增加了辅助的符号哈希表

8.1.4.13. .init

程序初始化代码段

8.1.4.14. .fini

程序终结代码段

8.1.4.15. .interp

该段里保存的是一个字符串,这个字符串就是可执行文件所需要的动态链接器的位置

8.1.5. 内存布局

----------------------------------
|        Kernel Space            |    1GB
----------------------------------
|                                |    Random Stack offset
----------------------------------
|       Stack (growth down)      |    RLIMIT_STACK
----------------------------------
|                                |    Random mmap offset
----------------------------------
|       Memory Map Segement      |
|  (including dynamic libraries) |
|      e.g. /lib/libc.so         |
|         (growth down)          |
----------------------------------
|                                |
----------------------------------
|        Heap (growth up)        |
----------------------------------
|                                |  Random brk offset
----------------------------------
|          BSS Segement          |
| Uninitalized static variables, |
|       filled with zeros.       |
|  e.g. static char *userName;   |
----------------------------------
|          Data Segement         |
|  Static variables initialized  |
|    by the programmer.          |
----------------------------------
|          Text Segement         |
----------------------------------

8.1.6. PLT与GOT

在编译和链接阶段,链接器无法知知道进程运行起来之后外部函数的加载地址。因此程序编译时会采用两种表进行辅助,一个为程序链接表(PLT,Procedure Link Table),一个为全局偏移表(GOT, Global Offset Table)。