读书笔记--《程序员的自我修养》第3章:目标文件里有什么(1)

3.1、目标文件的格式

1、目标文件从结构上讲,它是已经编译后的可执行文件格式,只是还没有经过链接的过程,其中可能有些符号或有些地址还没有调整。其实它本身就是按照可执行文件格式存储的。

2、现在PC平台流行的可执行文件格式主要是windows下的PE和Linux下的ELF,他们都是COFF格式的变种。Windows的.obj和Linux的.o文件都是目标文件。其他不太常见的可执行文件格式有Intel/Microsoft的OMF、Unix的a.out格式和MS-DOS .COM格式等。

3、可执行文件、动态链接库文件(Windows的.dll和Linux的.so)和静态链接库文件(Windows的.lib和Linux的.a)都是按照可执行文件存储。其中,静态链接库稍有不同,它是把很多目标文件捆绑在一期形成一个文件,再加上一些索引,可以简单地理解为一个包含很多目标文件的文件包。
ELF文件标准里面把系统中采用的ELF格式的文件分为4类。
(1)可重定位文件:包含代码和数据,可以被用来链接成可执行文件或共享目标文件,静态链接库也可归为这一类。如Linux的.o和Windows的.obj
(2)可执行文件:可直接执行的程序。如/bin/bash文件、Windows的.exe
(3)共享目标文件:包含代码和数据,使用情况一:链接器可以使用这种文件和其他的可重定位文件和共享目标文件链接;使用情况二:动态链接器可以将几个共享目标文件与可执行文件结合,作为进程映像的一部分来运行。如Linux的.so、Windows的DLL
(4)核心转储文件:当进程意外终止时,系统可以将进程的地址空间的内容及终止时的一些其他信息转储到核心转储文件。如Linux的core dump

3.2 目标文件是什么样的

一般目标文件按照不同的属性,以节的形式存储信息,也叫段。一般情况下,它们都表示一个一定长度的区域,基本上不加以区别。
程序源代码编译后的机器指令经常被放在.text段;初始化的全局变量和局部静态变量保存在.data;未初始化的全局变量和局部静态变量一般放在.bss段里。.bss段只是为未初始化的全局变量和局部静态变量预留位置而已,并没有内容,在文件中也不占空间。

3.3 分析目标文件

以下面程序为例说明:
这里写图片描述
用GCC编译这个文件: g c c c S i m p l e S e c t i o n . c ( c ) o b j d u m p objdump -h SimpleSection.o(参数-h表示只把ELF文件的各个段的信息打印出来)
这里写图片描述
其中,第2行的CONTENTS、ALLOC等表示段的属性。CONTENTS表示该段在文件中存在,可以看到.bss段没有这个属性,因为它实际上在ELF文件中不存在。
使用size命令可以直接查看ELF文件的各个段的长度:$size SimpleSection.o
这里写图片描述
其中都是十进制表示,只有hex是十六进制,dec表示3个段的和。

3.3.1 代码段
输入命令:$objdump -s -d SimpleSection.o(参数-s可以将段的内容用十六进制打印出来,参数-d可以将所有包含指令的段反汇编)
这里写图片描述

3.3.2 数据段和只读数据段
.data段保存的是初始化了的全局静态变量和局部静态变量,即global_init_varabal和static_var。这两个变量一共8个字节,因此,.data这个段的大小为8个字节。
程序调用printf时,用到了一个字符串常量”%d\n”,它是一种只读数据,所以它被放到了.rodata段。且这个字符串常量以\0结尾。
注意到.data的前4个字节是0x54、0x00、0x00、0x00。这个值刚好是golbao_init_varabal,即十进制的84,后一个字节是static_init_var,是85.
这里写图片描述
这里写图片描述

3.3.3 BSS段
.bss段存放的是未初始化的全局变量和局部静态变量。即上述代码中的global_uninit_var和static_var2就是被存放在.bss段。但我们看到的是4个字节而不是8个字节。
这里写图片描述
这是因为static_var2被存放在了.bss段,而global_uninit_var却没有被存放在任何段,只是一个未定义的”COMMON符号”。这其实跟不同的语言和不同的编译器实现有关,有些编译器会将全局静态未初始化变量存放在.bss段,有些则不放,只是预留一个未定义的全局变量符号,等到最终链接成可执行文件的时候再在.bss段分配空间。如果将global_uninit_var加上staic就会被放到.bss段。

3.3.4 其他段
除了.text、.data、.bss这3个最常用的段之外,ELF文件也有可能包含其他的段,用来保存与程序相关的其他信息。
这里写图片描述

3.3.5 自定义段
正常情况下,GCC编译出来的目标文件中,代码会被放到.text段,全局变量和静态变量会被放到.data和.bss段,但有时候你可能希望变量或部分代码放到指定的段中,以实现某些特定的功能。
GCC提供了一个扩展基址,使得程序员可以指定变量所处的段:
attribute((section(“FOO”))) int global = 42;
attribute((section(“BAR”))) void foo(){}
我们在全局变量或函数之前加上“attribute((section(“name”)))”属性就可以把相应的变量或函数放到以”name”作为段名的段中。

猜你喜欢

转载自blog.csdn.net/qq_15727809/article/details/82623216