readelf elf文件格式分析
背景
目标文件
首先需要介绍的概念是目标文件(Object file)的概念。目标文件是计算机科学中编译器或汇编器处理源代码后所生成的代码(目标代码,Object code)的计算机文件,它常被称作二进制文件(binaries)。这个文件类型主要是区别于你看得懂的用人话写的代码文件(.c、.cpp etc.)、中间文件(.i)、汇编文件(.s)。常见的.exe、.dll、.so啥的都算目标文件。
目标文件有三种类型:
- 可重定位的对象文件(Relocatable file)
由汇编器汇编生成的 .o 文件(下面会详细讲到) - 可执行的对象文件(Executable file)
可执行应用程序 - 可被共享的对象文件(Shared object file)
动态库文件
编译过程
编译的过程如下图所示
代码文件经过语言预处理器、编译器、汇编器和链接器处理,最终生成可执行目标文件。
下面以一个简单的c语言文件为例:
sum.c源文件内容如下:
1 | int sum(int *a, int n) |
通过运行C预处理器(cpp)可以生成sum.i中间文件:
1 | # 1 "sum.c" |
通过运行C编译器(cc1),将中间文件生成为sum.s汇编文件:
1 | .file›"sum.i" |
最终生成通过汇编器(as)生成一个可重定位目标文件(reloacatable object file)。
什么是ELF
系统里的目标文件是按照特定的目标文件格式来组织的,各个系统的目标文件格式都不相同。
从贝尔实验室诞生的第一个Unix系统使用的是a.out格式(直到今天,可执行文件仍然称为a.out文件)。Windows使用可移植可执行(PortableExecutable,PE)格式。Mac OS-X使用Mach-O格式。现代x86-64Linux和Unix系统使用可执行可链接格式(Executable and Linkable Format,ELF)。
ELF格式的文件在Linux系统下有.axf、 .bin、 .elf、 .o、 .prx、 .puff、 .ko、 .mod和.so等等
readelf指令
前面介绍了这么多ELF的背景知识,下面回来来说说readelf这个指令。这个指令正是用来查看目标文件的内容的。
ELF可重定位目标文件的格式
典型格式
典型的ELF可重定位目标文件的格式如下图:
其中:
- .text 节里装载了程序的可执行机器码
- .rodata 节里装载了只读数据
- .data 节里面装载了被初始化的数据,包括全局和静态C变量
- .bss 节里面装载了未被初始化的全局和静态C变量(在目标文件中只是占位符,不占空间)
- .symtab 或者 .dynsym 节里面装载了符号信息
- 以 .rel 打头的 节里面装载了重定位条目
- .debug 一个调试符号表,只有使用了
-g
参数编译时才会有,用于debug - .line 用于记录C源程序的行号和.text节中机器指令之间的映射,也是只有使用了
-g
参数编译时才会有 - .strtab 或者 .dynstr 节里面装载了字符串信息(以null结尾的字符串信息)
符号表部分解析
符号表每节定义如下:
1 | typedef struct { |
具体解释如下:
举个例子🌰
以上面的sum.c生成的sum.o为例,我们选取readelf
的-all
参数输出全部内容:
1 | $readelf -all sum.o |
其中第一部分是ELF头(ELF header)中的描述信息。(用-h
参数可以单独得到)。
最后一部分是符号表部分(用-s
参数可以单独得到该部分),前面八个条目是链接器内部使用的局部符号,最后一行是全局符号sum
定义的条目。可以通过最后一行看出,它是一个位于.text节中偏移量为0处的27字节函数。(Ndx部分表示在哪个节中,1表示.text节,3表示.data节,对应上面输出的Section Headers
部分)