2009年(11)
分类: LINUX
2009-05-14 10:10:11
目标文件全部是程序的二进制表示,目的是直接在某种处理器上直接执行。
NOTE:尽管图中显示的各个组成部分是有顺序的,实际上除了 ELF 头部表以外,其他节区和段都没有规定的顺序 |
下面用readelf
工具读出目标文件hello.o的ELF Header和Section Header Table,然后我们逐段分析。
arm-linux-readelf -a hello.o
ELF Header中描述了操作系统是ARM Linux,体系结构是ARM。Section Header Table中有7个Section Header,在文件中的位置(或者叫文件地址)从112(0x70)开始,每个40字节,共280字节,到文件地址0x187结束。这个目标文件没有Program Header。文件类型是重定位文件,和实际情况相符。
.text、
.data、.bss
是我们在汇编程序中声明的Section,而其它Section是汇编器自动添加的。Addr
是这些段加载到内存中的地址(我们讲过程序中的地址都是虚拟地址),加载地址要在链接时填写,现在空缺,所以是全0。Off
和Size
两列指出了各Section的文件地址,比如.text
从文件地址0x34开始,一共0xc个字节,回去翻一下程序,.text
中定义了3个4字节的整数,一共是12个字节,也就是0xc个。根据以上信息可以描绘出整个目标文件的布局。目标文件的布局
起始文件地址 | Section或Header |
---|---|
0 | ELF Header |
0x34 | .text |
0x40 | .data |
0x44 | .bss (此段为空) |
0x44 | .shstrtab |
0x70 | Section Header Table |
0x188 | .symtab |
0x228 | .strtab |
这个文件不大,我们直接用hexdump
工具把目标文件的字节全部打印出来看。
hexdump -C hello.o
左边一列是文件中的地址,中间是每个字节的16进制表示,右边是把这些字节解释成ASCII码所对应的字符。中间有一个*号表示省略的部分全是0。.shstrtab
和.strtab
这两个Section中存放的都是ASCII码.这些对照section headers的地址,和上图看出来。
我们知道,C语言的全局变量如果在代码中没有初始化,就会在程序加载时用0初始化。这种数据属于.bss
段,在加载时它和.data
段一样都是可读可写的数据,但是在ELF文件中.data
段需要占用一部分空间保存初始值,而.bss
段则不需要。也就是说,.bss
段在文件中只占一个Section Header而没有对应的Section,程序加载时.bss
段占多大内存空间在Section Header中描述。上例中在section headers中发现bss加载时使用1024个字节的空间。
Section Headers: ... [ 3] .bss NOBITS 00000000 000044 000400 00 WA 0 0 1 ... |
继续分析readelf
输出的最后一部分,是从.rel.text
和.symtab
这两个Section中读出的信息。
rel.text告诉链接器指令中的哪些地方需要重定位,这里没有。
.symtab
是符号表。Ndx
列是每个符号所在的Section编号,例如data
在第3个Section里(也就是.bss
),各Section的编号见Section Header Table。Value
列是每个符号所代表的地址,在目标文件中,符号地址都是相对于该符号所在Section的相对地址。比如data在bss第一个所以是0,sqr在.text中第一个所以也是0。从Bind
这一列可以看出_relocation、num、sqr
这个符号是GLOBAL
的,而其它符号是LOCAL
的,GLOBAL
符号是在汇编程序中用.globl
指示声明过的符号。
.text段没有分析,objdump
工具可以把程序中的机器指令反汇编(Disassemble),那么反汇编的结果是否跟原来写的汇编代码一模一样呢?我们对比分析一下。
|
左边是机器指令的字节,右边是反汇编结果。一般汇编过后符号都要改变成地址。