目标文件分析

目标文件的格式

现在PC平台流行的可执行文件格式(Executable)主要是Windows 下的PE(Portable Executable)和Linux的ELF(ExecutableLinkable Format),它们都是COFF(Common file format)格式的变种。目标文件就是源代码编译后但未进行链接的那些中间文件(Windows是.obj和Linux是.o),它们跟可执行文件的内容和结构很相似,所以一般跟可执行文件一起采用一种格式存储。

不光可执行文件(Windows的.exe和Linux下的ELF可执行文件)按照可执行文件格式存储。动态链接库(DLL,Dynamic Linking Library)(Windows的.dll和Linux的.so)以及静态链接库(Static Linking Library)(Windows的.lib和Linux的.a)文件都按照可执行文件格式存储。

 

目标文件是什么样的?

目标文件中的信息按不同的属性,以“节”(Section)的形式存储,有时候也叫“段”(Segment)。

以C语言为例,我们的程序在编译之后,执行语句都会编译成机器代码,保存在.text段;已初始化的全局变量和局部静态变量都保存在.data段;未初始化的全局变量和局部静态变量一般放在一个叫.bss的段里。我们知道未初始化的全局变量和局部静态变量默认值都是0,本来它们应该也可以被放在.data段的,但是因为它们都是0,所以为它们在.data段分配空间并且存放数据0是没有必要的。程序运行的时候它们的确要占用内存空间的,并且可执行文件必须记录所有未初始化的全局变量和局部变量的大小总和,记为.bss段。所以.bss段只是未初始化的全局变量和局部静态变量预留位置而已,它并没有内容,所以它在文件中也不占据空间

总体来说,程序源代码被编译以后主要分为两种段:程序指令和程序数据。代码段属于程序指令,而数据段和.bss段属于程序数据。

 

那么,为什么要采用这种分离的设计呢

1、程序在被装载后,数据和指令分别被映射到两个虚拟区域。对于数据来说,我们一般是可读写,而指令一般是只读。分开存储,可以防止程序指令被有意或无意的篡改。

2、对于现在的CPU来说,他们有着强大的缓冲(cache)体系。由于缓存在现代的计算机中地位非常重要,所以程序必须提高缓冲的命中率。分开存储有利于命中率的提高。

3、当系统运行着多个该程序的副本时,它们指令都是一样的,所以内存中有一份指令就够了。这样可以大大的解决储存空间。

 

目标文件分析


以上面的代码为例,我们对其进行编译,生成.o的目标文件。通过objdump -h SimpleSection.o命令,可以看到目标文件的结构及内容。出现了我们前面说到的代码段、数据段、BSS段,除了这三个之外,还有只读数据段(.rodata)、注释信息段(.comment)和堆栈提示段(.note.GUN-stack)。其中File off表示段所在的位置,“CONTENTS”表示该段在文件中存在,所以我们可以看到,在.bss与.rodata中,它的段所在位置都是9c,因为.bss没有文件存在,不占据空间。

通过$objdump -sSimpleSection.o,看到代码段的内容以十六进制的方式显示如下,可以看到它的大小为51,它从左到右分别为:偏移量、十六进制内容、.text段的ASCII码形式。代码段的大小是51,为什么上面到.data的所在位置从40到了94呢?这个是由于字节对齐的原因。

对于数据段来说,我们看到.data的大小是8,从前面得知,.data段是用来存放初始化了的全局静态变量和局部静态变量的,在我们的程序中,global_init_varabal与static_var。Int型,每个4个字节,一共刚好8个字节。而对于.rodata段,它存放只读变量和字符串常量,在我们代码中,就是printf中的“%d\n”了。通过$objdump -s SimpleSection.o命令,我们看到数据段存放的数据如下:

对于.data段,两个数分别就是十进制的84、85了,这样的存放方式是因为采用的小端模式。

 

什么是大端(Big-endian)模式?什么是小端(Little-endian)模式?

小端模式就是低位字节排放在内存的低地址端,高位字节都放在内存的高地址端。大端模式则相反。所以对于54000000来说,它就是大端模式,因为它将低位放在高位地址上。

对于.bss段来说,它是存放未初始化的全局变量和局部静态变量。所以global_uninit_var和static_var2被存放在该段,更准确的说是.bss为它们预留了空间。但是它们应该是8个字节的,为什么只有4个字节呢?通过$readelf -s SimpleSection.o可以看到具体存放方式。

其中Ndx表示符号所属的段,可以看到global_uninit_var和static_var2两个中只有static_var2在4中,也就是.bss段,而global_uninit_var在COM段,所以上面显示的.bss是4个字节。

通过$objdump -x SimpleSection.o命令可以看到它的存放位置。

对于整个代码反编译$objdump -d SimpleSection.o,得到的汇编代码如下:


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值