Linux下ELF文件类型分为以下几种:
1、可重定位文件,例如SimpleSection.o;
2、可执行文件,例如/bin/bash;
3、共享目标文件,例如/lib/libc.so。
本文章中,我们会使用objdump,readelf,hexdump,nm等来分析一个linux中可重定位文件SimpleSection.o。
首先附上SimpleSection.c源代码:
使用命令:
gcc -c SimpleSection.c
得到SimpleSection.o,下面我们首先附上SimpleSection.o的二进制内容以及整体轮廓。
使用命令:
hexdump -C SimpleSection.o,得到SimpleSection.o的二进制内容。
计算机科学中,二进制0 1可以代表代码,字母,数字(十进制数和十六进制数)。
图1
SimpleSection.o的整体轮廓图如下,可能读者会想为什么会得到这样一张图,随着我们深入分析每个段的内容,答案自然会揭晓。
图 2
我们看到0x758是所有段结束的位置,换算成十进制就是1880个字节。和我们刚才获取的文件大小一样。
下面我们来利用命令来分析ELF文件结构的每个部分:
1、ELF Header
使用命令readelf -h SimpleSection.o,得到下图。
图 3
ELF文件头结构及相关参数被定义在“/usr/include/elf.h”中,如下:
Type:ELF文件类型,本例中为REL(Relocatable File),可重定位文件。
Start of section headers,段表在文件中偏移,就是图2中Section Table的位置为392(0x188)。
Size of section headers,ELF文件头的大小为64个字节。
Number of section headers,ELF拥有多少个断,本例为13个段。见图 7。
Section header string table index,段表字符串表所在的段在段表中的下标。本例中等于10,见图 7。
2、.text
使用命令:
objdump -d SimpleSection.o,得到了下图,由于是代码段,所以二进制代表汇编代码。
图 4
3、.data
使用命令objdump -s SimpleSection.o,得到数据段,如下图:
图 5
本例中存入数据段的是
共8个字节,一个是0x00000054,十进制是84;一个是0x0000000055,十进制是85。
4、.bss
使用命令objdump -h SimpleSection.o,得到下图:
图 6
本例中存入数据段的是
大家会注意到int global_uninit_var;既没有在.data段中,也没有在.bss段中。如果在前面加上static,那么则存在.bss段中。
5、.rodata
.rodata存放的只读数据。25640a00,查看ASCII表代表的就是%d\n。
6、.shstrtab(段表字符串表)
如图1,存放的是
..symtab..strtab..shstrtab..rela.text..data..bs..rodata..comment..note.GNU-stak..rela.eh_frame
7、.strtab(字符串表)
如图1,存放的是
SimpleSection.c.static_var.1594.static_var2.1595.global_init_var.global_uninit_var.func1.printf.main
8、Section Table
使用命令,readelf -S SimpleSection.o,得到下图:
图 7
这就解释了图1,为什么要那么画。
Offset表示段偏移,Size表示段大小。
Type,PROGBITS表示段,NOBITS表示不占空间,.RELA表示重定位段,STRTAB表示字符串表,SYMTAB表示符号表。
EntSize表示如果段中有重复的内容,则表示重复内容大小。比如下面要介绍的符号表就是重复内容组成的。
在TYPE为RELA时,Link表示该段所使用的相应符号表在段表中,本例中为11。Info表示该重定位表所作用的段在段表中的下标。.rela.text为1,.rela.eh_frame为8。
9、.symtab(符号表)
使用命令,readelf -s SimpleSection.o,得到下图:
图 8
Name,表示字符串在字符串表中的下标;
Ndx,SimpleSection.c为ABS,global_uninit_var为COM,表示这个变量是强引用或者弱引用,目前即不在.data段中,也不在.bss段中,等待链接时会确定。
printf为UND,表示没有定义,即引用了外部的函数。
global_init_var,NDX为3,表示在.data中,其余类似。表示符号所在的段在段表中的下标;参考图 7。
Bind GLOBAL表示可以被外部引用或者引用外部的函数和变量。
TYPE为OBJECT表示对象,FUNC表示函数,SECTION表示段,FILE表示文件,printf为NOTYPE,表示没有定义,是引用外部函数。
SIZE表示大小。
Value表示在本段中的偏移,比如static_var.1594表示在.data段中的偏移为4。 main在.data段中的偏移为21。
最后介绍个命令,nm SimpleSection.o,结果如下:
可以看出示所有可以被外部引用或者引用外部的函数和变量。
T表示text;D和d表示data;b表示bss,C表示Common,U表示Undef。
程序中的非静态局部变量即不在数据段也不在代码段,可能在堆栈段。
至此,所有段都分析完了。