目标文件ELF

程序源文件经过编译后将会生产目标文件,对于linux系统,生成的目标文件是以.o结尾的ELF文件。
假设有源文件test.c,如图1所示,代码中有函数,有全局变量,有局部静态变量,有初始化变量,有未初始化变量。
在这里插入图片描述
图1 test.c源文件

经过gcc -c test.c 编译后将生成目标文件test.o,test.o的二进制内容如图2所示
在这里插入图片描述
图2 目标文件test.o 的二进制内容

对于上述二进制内容,能难知其所云,可以用readelf或者objdump查看目标文件的格式化内容。目标文件是以段(section)存放的,而段表是各个段信息的描述。
使用命令:
readelf -h -S test.o
读取目标文件中的文件头和段表信息。输出的内容如图3所示:
在这里插入图片描述
图3 文件头和段表内容

可以看到文件头(ELF Header),包含着该目标文件的描述,其对应数据结构为图4所示,其中包含了魔法数,文件类型,数据存放格式,版本号,ABI类型,ABI版本,CPU类型,程序信息和段表信息等。其中,魔法数为7f, 45, 4c, 46, 02 ,01, 01数据开头的16byte的数据,标识着ELF文件,也由图2可以看到文件开始就是7f, 45, 4c, 46, 01, 01的数据。
在这里插入图片描述
图4 文件头数据结构

由图3中打印了段表的信息,包括段名,段类型,段大小,段所在文件的偏移等。而段表所在文件的偏移由文件头e_shoff表示。
使用命令
objdump -s -d test.o
查看各段的二进制和汇编代码,如图5所示
在这里插入图片描述
图5 段二进制和汇编代码

结合图3的段表信息和图5输出的段的内容来分析各个段:
.text:代码段,有图3可以看到,它在文件中偏移为0x40,而文件头的大小为64bytes,所以在文件头之后紧接着是代码段。代码段的大小为0x4e, 加上偏移之后是0x9e,再算上地址对齐,应该为0xa0,可以知道接在代码段之后为.data(数据段);
.rela.text:代码重定位段,存放着代码段中在链接阶段需要重定位的符号;
.data:数据段,其大小为0x08,存放着已经初始化的全局变量和静态变量,图1知ELF存储为小端模式,而图5显示数据为54000000和55000000,即global_init_var 0x54和static_init_var 0x55,而未初始化的内容存放在.bss段中;
.rodata:只读数据段,存放的为%d\n的字符常量
.bss:存放未初始化的全局变量和静态变量,图3中其大小0x04,而源代码中未初始化的变量有两个,实际上.bss中,暂时只存放了静态变量static_uint_var, 因为未初始化的全局变量global_uint_var设计的弱符号的问题,暂时还不能确定该变量的大小,只是在符号表中标识为common类型,待链接阶段确定了符号的大小,才为其在.bss分配大小;
.shstrtab:段名字串符号表,存放段名,实际上段表中存放的只是段名在段名字符串标的下标;
.symtab:符号表;
使用命令:
readelf -s test.o
得到目标文件的符号表如图6所示,符号表有notype, section, object, func等类型,可以看出未初始化global_uint_var全局变量标识为Common类型。Value的值对于变量和函数对应着符号的地址。
在这里插入图片描述
图6 符号表

.strtab:字符串表,实际上符号表也只存放符号名的下标

由上分析可知:在目标文件中存放文件头,代码段,数据段,只读数据段,段名表, 段表,符号表,字符串标,重定位段等。
在这里插入图片描述
图7 目标文件结构

参考文献:
《程序员的自我修养》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值