目录

1. 链接举例

2. ELF文件类型

2.1 可重定位目标文件(.o文件)

2.2 可执行目标文件(a.out文件)

2.3 共享对象文件(.so文件)

3. ELF文件作用

4. ELF文件格式

4.1 从编译和链接角度看ELF文件(可重定位目标文件)

4.2 从程序执行角度看ELF文件(可执行文件)

5.总结


1. 链接举例

  在介绍ELF文件之前,我们先看下,一个.c程序是如何变成可执行目标文件的。下面举个例子。

  该程序由main.c和sum.c两个模块组成。sum.c接收数组和数组长度两个参数,最后将数组求和的结果返回。main.c调用sum函数,并传递一个两元素的int数组array,将计算结果保存在val中。

//main.c
int sum(int *a, int n);int array[2] = {1, 2};int main(int argc, char** argv)
{int val = sum(array, 2);return val;
}
//sum.c
int sum(int *a, int n)
{int i, s = 0;for (i = 0; i < n; i++) {s += a[i];}return s;
}

 让我们来看看如果我们使用GCC编译两个模块会发生什么?

  main.c和sum.c将分别通过翻译器将源文件处理为可重定位的目标文件main.o和sum.o。翻译器处理的过程包括了预处理(ccp)编译(ccl)汇编(as) 三个过程。最后,链接器(ld) 将可重定位的目标文件main.o和sum.o以及一些必要的系统文件组合起来,创建一个可执行目标文件prog。具体过程如下图所示。

由上面的过程,我们可以看出在经过汇编器后会输出一个.o文件,这个叫做可重定位的目标文件。将main.o和sum.o输入链接器后,链接器输出的prog文件叫做可执行目标文件。那这两个目标文件有什么样的区别呢?

2. ELF文件类型

2.1 可重定位目标文件(.o文件)

  包含二进制代码和数据,其形式可以和其他目标文件进行合并,创建一个可执行目标文件。例如lib*.o文件。

2.2 可执行目标文件(a.out文件)

  包含二进制代码和数据,可直接被加载器加载执行。例如编译好的可执行文件a.out。

2.3 共享对象文件(.so文件)

  用于和其他共享目标文件或者可重定位文件一起生成ELF目标文件或者和执行文件一起创建进程映像,例如lib*.so文件。

3. ELF文件作用

  ELF文件参与程序的连接(建立一个程序)和程序的执行(运行一个程序),所以可以从不同的角度来看待ELF格式的文件:

  1.如果用于编译和链接(可重定位文件),则编译器和链接器将把ELF文件看作是节头表描述的节的集合,程序头表可选。

  2.如果用于加载执行(可执行文件),则加载器则将把ELF文件看作是程序头表描述的段的集合,一个段可能包含多个节,节头表可选。

4. ELF文件格式

4.1 从编译和链接角度看ELF文件(可重定位目标文件)

ELF头

  每个ELF文件都必须存在一个ELF_He ader,这里存放了很多重要的信息用来描述整个文件的组织,如: 版本信息,入口信息,偏移信息等。程序执行也必须依靠其提供的信息。

段头表

  段头表。存放的是所有不同段将在内存中的位置

.text section

  代码段。存放已编译程序的机器代码,一般是只读的。

.rodata section

  只读数据段。此段的数据不可修改,存放常量。比如,printf中的格式化语句。

.data section

  数据段。存放已初始化的全局变量、常量。

.bss section

  bss段。未初始化全局变量,仅是占位符,不占据任何实际磁盘空间。目标文件格式区分初始化和非初始化是为了空间效率。

.symtab section

  符号表,它存放在程序中定义和引用的函数和全局变量的信息。

.rel.txt section

  .text节的重定位信息,用于重新修改代码段的指令中的地址信息。

.rel.data section

  .data节的重定位信息,用于对被模块使用或定义的全局变量进行重定位的信息。

.debug section

  调试用的符号表。

.strtab section

  包含 symtab和 debug节中符号及节名。

节头部表

  每个节的节名、偏移和大小。

  以下是32位系统对应的节头表数据结构,说明了每个节的节名、在文件中的偏移、大小、访问属性、对齐方式等。

typedef struct {Elf32_Word sh_name;   //节名字符串在.strtab节(字符串表)中的偏移Elf32_Word sh_type;   //节类型:无效/代码或数据/符号/字符串/...Elf32_Word sh_flags;  //节标志:该节在虚拟空间中的访问属性Elf32_Addr sh_addr;   //虚拟地址:若可被加载,则对应虚拟地址Elf32_Off  sh_offset; //在文件中的偏移地址,对.bss节而言则无意义Elf32_Word sh_size;   //节在文件中所占的长度Elf32_Word sh_link;   //sh_link和sh_info用于与链接相关的节(如 .rel.text节、.rel.data节、.symtab节等)Elf32_Word sh_info;Elf32_Word sh_addralign; //节的对齐要求Elf32_Word sh_entsize;   //节中每个表项的长度,0表示无固定长度表项
} Elf32_Shdr;

使用readelf命令命令查看节头表内容

[ubuntu@localhost interpositioning]$ readelf -S main.o
There are 13 section headers, starting at offset 0x3f8:Section Headers:[Nr] Name              Type             Address           OffsetSize              EntSize          Flags  Link  Info  Align[ 0]                   NULL             0000000000000000  000000000000000000000000  0000000000000000           0     0     0[ 1] .text             PROGBITS         0000000000000000  000000400000000000000071  0000000000000000  AX       0     0     1[ 2] .rela.text        RELA             0000000000000000  000002d00000000000000090  0000000000000018   I      11     1     8[ 3] .data             PROGBITS         0000000000000000  000000b10000000000000049  0000000000000000  WA       0     0     1[ 4] .bss              NOBITS           0000000000000000  000000b1000000000000000c  0000000000000000  WA       0     0     1[ 5] .rodata           PROGBITS         0000000000000000  000000b10000000000000019  0000000000000000   A       0     0     1[ 6] .comment          PROGBITS         0000000000000000  000000ca0000000000000035  0000000000000001  MS       0     0     1[ 7] .note.GNU-stack   PROGBITS         0000000000000000  000000ff0000000000000000  0000000000000000           0     0     1[ 8] .eh_frame         PROGBITS         0000000000000000  000001000000000000000058  0000000000000000   A       0     0     8[ 9] .rela.eh_frame    RELA             0000000000000000  000003600000000000000030  0000000000000018   I      11     8     8[10] .shstrtab         STRTAB           0000000000000000  000003900000000000000061  0000000000000000           0     0     1[11] .symtab           SYMTAB           0000000000000000  000001580000000000000150  0000000000000018          12     9     8[12] .strtab           STRTAB           0000000000000000  000002a80000000000000023  0000000000000000           0     0     1
Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), l (large)I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)O (extra OS processing required) o (OS specific), p (processor specific)

  可重定位目标文件中,每个可装入节的起始地址总是0。

  .bss节应占0x0c大小,但只有装入内存时才会分配。

4.2 从程序执行角度看ELF文件(可执行文件)

 与可重定位目标文件不同:

  1.ELF头中,字段 e_entry给出执行程序时第一条指令的地址,而在可重定位文件中,此字段为0。

  2.多一个init节,用于定义init函数,该函数用来进行可执行目标文件开始执行时的初始化工作。

  3.少两个.rel节(无需重定位)。

  4.多一个程序头表,也称段头表,是一个结构数组。

  使用readelf命令查看ELF头的内容:

[ubuntu@localhost interpositioning]$readelf -h main.o
ELF Header:Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00Class:                             ELF64Data:                              2's complement, little endianVersion:                           1 (current)OS/ABI:                            UNIX - System VABI Version:                       0Type:                              REL (Relocatable file)Machine:                           Advanced Micro Devices X86-64Version:                           0x1Entry point address:               0x0Start of program headers:          0 (bytes into file)Start of section headers:          1064 (bytes into file)Flags:                             0x0Size of this header:               64 (bytes)Size of program headers:           32 (bytes)         //程序头表每项32BNumber of program headers:         8                  //程序头表共8项Size of section headers:           64 (bytes)Number of section headers:         13Section header string table index: 10                //.strtab在节头表中的索引

 装入内存时,ELF头、程序头表、.init节、.rodata节会被装入只读代码段。.data节和.bss节会被装入读写数据段。

  段头表能够描述可执行文件中的节与虚拟空间中的存储段之间的映射关系。一个表项32B,说明虚拟地址空间中一个连续的片段或一个特殊的节。以下是32位系统对应的段头表数据结构:

typedef struct {Elf32_Word p_type;   //此数组元素描述的段的类型,或者如何解释此数组元素的信息。Elf32_Off p_offset;  //此成员给出从文件头到该段第一个字节的偏移Elf32_Addr p_vaddr;  //此成员给出段的第一个字节将被放到内存中的虚拟地址Elf32_Addr p_paddr;  //此成员仅用于与物理地址相关的系统中。System V忽略所有应用程序的物理地址信息。Elf32_Word p_filesz; //此成员给出段在文件映像中所占的字节数。可以为0。Elf32_Word p_memsz;  //此成员给出段在内存映像中占用的字节数。可以为0。Elf32_Word p_flags;  //此成员给出与段相关的标志。Elf32_Word p_align;  //此成员给出段在文件中和内存中如何对齐。
} Elf32_phdr;

  使用readelf命令查看某可执行目标文件的程序头表。

[ubuntu@localhost interpositioning]$readelf -l mainElf file type is EXEC (Executable file)
Entry point 0x400550
There are 9 program headers, starting at offset 64Program Headers:Type           Offset             VirtAddr           PhysAddrFileSiz            MemSiz              Flags  AlignPHDR           0x0000000000000040 0x0000000000400040 0x00000000004000400x00000000000001f8 0x00000000000001f8  R E    8INTERP         0x0000000000000238 0x0000000000400238 0x00000000004002380x000000000000001c 0x000000000000001c  R      1[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]LOAD           0x0000000000000000 0x0000000000400000 0x00000000004000000x00000000000008ac 0x00000000000008ac  R E    200000LOAD           0x0000000000000e10 0x0000000000600e10 0x0000000000600e100x0000000000000240 0x0000000000000248  RW     200000DYNAMIC        0x0000000000000e28 0x0000000000600e28 0x0000000000600e280x00000000000001d0 0x00000000000001d0  RW     8NOTE           0x0000000000000254 0x0000000000400254 0x00000000004002540x0000000000000044 0x0000000000000044  R      4GNU_EH_FRAME   0x0000000000000780 0x0000000000400780 0x00000000004007800x0000000000000034 0x0000000000000034  R      4GNU_STACK      0x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000  RW     10GNU_RELRO      0x0000000000000e10 0x0000000000600e10 0x0000000000600e100x00000000000001f0 0x00000000000001f0  R      1

程序头表信息有9个表项,其中两个为可装入段(即Type=LOAD):

  第一可装入段:第0x0000 0~0x0x8ab的长度为0x8ac字节的ELF头、程序头表、.init、.text和.rodata节,映射到虚拟地址0x400000开始长度为0x8ac字节的区域 ,按0x200000=2MB对齐,具有只读/执行权限(Flg=RE),是只读代码段。

  第二可装入段:第0xe10 ~0x104f的长度为0x240字节的.data节和磁盘中不占存储空间的.bss节,映射到虚拟地址0x600e10开始长度为0x248字节的存储区域,在0x248=584B存储区中,前0x240= 576B用.data节内容初始化,后面584-576= 8B对应.bss节,初始化为0 ,按0x200000 =2MB对齐,具有可读可写权限(Flg=RW),是可读写数据段。

  由此看出.bss节在文件中不占用磁盘空间,但在存储器中需要给它分配相应大小的空间

5.总结

  1.链接处理涉及到三种目标文件格式:可重定位目标文件、可执行目标文件和共享目标文件。共享库文件是一种特殊的可重定位目标。

  2.ELF目标文件格式可以从编译链接角度程序执行角度两个角度看,前者是可重定位目标格式,后者是可执行目标格式。从编译链接角度看,可重定位目标文件中包含ELF头、各个节以及节头表。可执行目标文件中包含ELF头、程序头表(段头表)以及各种节组成的段。

  3.bss段在可执行目标文件中不会有它的空间,只有当可执行目标文件装载运行时,才会被分配内存(并且位于data段内存块之后),并且初始化为0

本文参考《深入理解计算机系统》

推荐阅读

【1】18.基于Cortex-A9 SPI、MCP2515详解必读

【2】Linux 虚拟文件系统四大对象:超级块、inode、dentry、file之间关系

【3】【粉丝问答10】关键字static的使用详解必读

【4】apt 和 apt-get 之间有什么区别?必读

【5】16.从0学arm,基于Cortex-A9 ADC裸机驱动详解

【6】17.基于Cortex-A9,i2c 外设详解必读

【7】【粉丝问答8】用C语言在Linux下实现CC2530上位机-1

【8】CAN】嵌入式CAN总线入门篇(底层细节)必读

【9】19. Cortex-A9 uboot启动代码详解必读

【10】【粉丝问答9】一起入职的同事能力不如我,只因学历比我高,工资是我的两倍必读

ELF(Executable Linkable Format)文件存储格式相关推荐

  1. 大数据系统常用文件存储格式File format

    常见的文件存储格式有行式存储,列式存储和混合式存储.在大数据系统中,列式存储和混合式存储更常见.对于HDFS这样的文件存储系统来说,其实并不关心存储的是什么形式的存储文件.只是在解析数据时,需要知道是 ...

  2. (转载)hive文件存储格式

    对于hive格式的认识,先转载后实践再加深认识. 转载地址:http://blog.csdn.net/yfkiss/article/details/7787742 hive在建表是,可以通过'STOR ...

  3. hive-02-hive文件存储格式

    1.概述 hive在建表是,可以通过'STORED AS FILE_FORMAT' 指定存储文件格式 例如: > CREATE EXTERNAL TABLE MYTEST(num INT, na ...

  4. Hive文件存储格式和hive数据压缩

    一.存储格式行存储和列存储 二.Hive文件存储格式 三.创建语句和压缩 一.存储格式行存储和列存储 行存储可以理解为一条记录存储一行,通过条件能够查询一整行数据. 列存储,以字段聚集存储,可以理解为 ...

  5. linux怎么看文件是否orc格式,hive文件存储格式orc,parquet,avro对比

    orc文件存储格式 ORC文件也是以二进制方式列式存储的,所以是不可以直接读取,ORC文件也是自解析的,它包含许多的元数据,这些元数据都是同构ProtoBuffer进行序列化的.文件结构如下 ORC文 ...

  6. RC ORC Parquet之大数据文件存储格式的一哥之争

    背 景   大数据如火如荼的发展中,以hadoop集群为基础的数据存储和计算框架也日新月异的精进,而如何减少存储空间又提升计算效率,一直是大数据集群老生常谈的问题,今天就一起聊聊最基本的大数据文件存储 ...

  7. hive的四种文件存储格式

    hive分为四种存储格式: 1.TEXTFILE 2.SEQUENCEFILE 3.RCFILE 4.ORCFILE 在其中textfile为默认格式,如果在创建hive表不提及,会默认启动textf ...

  8. sqlserver exec查询存储过程取某一个字段_ORC文件存储格式的深入探究

    因为生病,另外还在做牙齿的根管治疗,痛不欲生,短更一篇. 年前都在梳理<大数据成神之路>的目录还有内容,另外Flink的公开课程也在规划大纲和目录.不知道我在说什么,看一下这里<20 ...

  9. 安装jdk后出现bash: ./java: /lib/ld-linux.so.2: bad ELF interpreter: 没有那个文件或目录

    cenos安装jdk会遇到的问题: 配置完环境变量后用java -version测试出现"bash: ./java: /lib/ld-linux.so.2: bad ELF interpre ...

最新文章

  1. mysql答题表设计_PHP+MYSQL问答系统中的提问和回答的表怎么设计
  2. java回顾之继承 二
  3. 写给大数据开发初学者的话
  4. Kettle使用_0 Windows下安装图解
  5. 如何安装最新版本的ABAP Development tools
  6. cmake导入so库_cmake编译.so库体积非常大,求解答
  7. ssas 分层维度_通过SSAS维度层次结构增强数据分析
  8. LED —— 发光二极管
  9. Java SE (5)之 线程使用
  10. 爬虫——selenium模块的基本使用(qq空间的登录)
  11. Github优质项目推荐(附国内Gitee地址)
  12. IBM X3650 安装ESXi 5.1u2 需要更改BIOS缺省设置
  13. 17.光照(点光源)
  14. CentOS7 通过 cups 与 Windows 共享 HP 1020 打印机
  15. 正则匹配所有的a标签
  16. 通过selenium抓取新浪微博
  17. CoolFormat源代码格式化工具
  18. HTML中如何改变多选框的背景色,如何用css更改输入复选框的背景颜色?
  19. 工业视觉 一 工业视觉初识
  20. 大小写换算(大小写转换是哪个键)

热门文章

  1. 总结MyBatis+Spring的整合
  2. springboot undertow替换tomcat方式
  3. python中执行shell命令的几个方法
  4. Java随机数与定时器
  5. HDU5470 Typewriter SAM 动态规划 单调队列
  6. 将可执行文件加入到系统启动项中
  7. Intellij 中的git操作 转!
  8. Convert Sorted Array to Binary Search Tree With Minimal Height
  9. 添加Silverlight应用到HTML
  10. postgreSQL源码分析——索引的建立与使用——GIN索引(3)