大家好,我是本期栏目编辑小友,现在为大家讲解了解在Linux下可执行文件格式问题。
在Linux下,目标文件、共享目标文件和可执行文件都是以ELF文件格式存储的。程序编译完成后,会输出目标文件,再通过链接生成可执行文件或共享对象文件。linux中使用的ELF文件和Windows操作系统中使用的PE文件都是从Unix系统的COFF文件格式演变而来的。
让我们先学习一些基本的思想。
首先,最重要的思想是,程序从人们可以理解的格式转换成二进制格式供操作系统执行后,代码和数据是分开存储的。这种设计有几个原因:
1.程序执行后,代码和数据可以映射到不同属性的虚拟内存中。因为代码一般是只读的,而数据是可读可写的;
2.现代CPU拥有强大的缓存系统。代码分离可以提高程序的局部性,增加缓存命中的概率。
3.另一个重要原因是,当程序有多个副本运行时,只读部分只有一个副本可以保留在内存中,这大大节省了内存。
在ELF的定义中,它们分开存放的地方叫做一个section,也就是一个Section。
ELF文件中的重要段落包括:文本部分:存储只读程序。数据段:存储初始化的全局变量和静态变量。bss段:存储未初始化的全局变量和静态变量。因为这些变量的值为0,所以该段不占用文件中的空间。rodata segment:存储只读数据,如字符串常量。
我们举个例子看看ELF文件的格式是什么。首先,在Linux下编写一个c程序:SimpleSecTIon.c
[CPP]查看平面图
int printf(const char *格式,);
intglobal _ init _ var=16
intglobal _ unint _ var
void func 1(int);
intmain()
{
stationstatic _ var=-32;
staTIcintstatic _ var _ uninit
inta=1;
intb
func 1(static _ var global _ init _ var a b);
返回;
}
无效函数1(inti)
{
printf('%d\n ',I);
}
然后,生成一个目标文件:
[CPP]查看平面图
[root @ Xu xingwang-centosProgram]# gcc-csimplesection . c
[root@x
uxingwang-centos Program]# file SimpleSection.oSimpleSection.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
file命令的结果也告诉我们,这是一个32位ELF的文件,类型是 relocatable ,就是可重定位。所以目标文件又叫做可重定位文件。
elf文件的最开始是elf文件头信息,32位有52个字节组成。我们可以使用 readelf 工具来查看一下:
[cpp] view plain copy
[root@xuxingwang-centos Program]# readelf -h SimpleSection.o
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: REL (Relocatable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x0
Start of program headers: 0 (bytes into file)
Start of section headers: 224 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 0 (bytes)
Number of program headers: 0
Size of section headers: 40 (bytes)
Number of section headers: 11
Section header string table index: 8
Entry point address 指的是程序入口地址,如果是可执行文件,这个字段会有值;
他之前的字段是一些说明字段;
Start of program headers 指的是 程序头表 的起始位置。程序头表 是从装载视图的角度对elf的各个段进行的分类信息;结构和段表相似;
Start of section headers 指出了elf除文件头以外的最重要的信息:段表 的起始位置。段表包含了各个段的名称、属性、大小、位置等重要信息。操作系统首先找到段表,然后根据段表的信息去找到各个段。段表是一个类似数组的结构,一个段的信息是这个数组的一个元素。
Size of this header 指的是头文件大小,32位都是 52 个字节,0x34个字节。
Size of program headers 指的是每个 程序头表 的大小。
Number of program headers 指的是 程序头表 的数目。
Size of sections headers 指的是每个 段表 的大小;
Number of section headers 指的是 段表的数量;
Section header string table index 指出了段表当中用到的字符串表在段表中的下标。
文件头之后,紧跟着的是 程序头,因为目标文件没有链接,所以没有装载信息。我们这里可以先不理会这个东西,以后专门再说他。
程序头之后就是各个段的数据,我们用工具查看一下:
[cpp] view plain copy
[root@xuxingwang-centos Program]# readelf -S SimpleSection.o
There are 11 section headers, starting at offset 0xe0:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 00000000 000034 000020 00 AX 0 0 4
[ 2] .rel.text REL 00000000 0003f4 000010 08 9 1 4
[ 3] .data PROGBITS 00000000 000054 000008 00 WA 0 0 4
[ 4] .bss NOBITS 00000000 00005c 000004 00 WA 0 0 4
[ 5] .rodata PROGBITS 00000000 00005c 000004 00 A 0 0 1
[ 6] .comment PROGBITS 00000000 000060 00002d 01 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 00000000 00008d 000000 00 0 0 1
[ 8] .shstrtab STRTAB 00000000 00008d 000051 00 0 0 1
[ 9] .symtab SYMTAB 00000000 000298 0000f0 10 10 10 4
[10] .strtab STRTAB 00000000 000388 00006b 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings)
I (info), L (link order), G (group), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
各个字段意思依次是:段序号、段名称、段类型、段虚拟地址、偏移量、大小、ES、标志、Lk、Inf、对齐。
没有解释的列可以先不考虑,我们先关注其他几个列。
第0个段是为了读取的时候下标不用减1。
紧跟着的就是代码段,偏移量为0x34,就是说在文件头结尾之后马上就是代码段;
代码段之后,偏移量 0x54 的地方就是 数据段,占8个字节,就是程序中已经被赋值的一个全局变量和一个静态变量;
紧接着是.bss段,这里只存储了一个static变量,因为 未初始化的那个全局变量被一种优化机制存储到了 .common 段,这里可以不做理会;
然后是只读数据段.rodata,这里存储的是 printf 里面的 %d\n 这三个字符,外加结束符\0,总共4个字节的空间
我们根据Size这一列来算一下这些段总共占据的空间,(.bss由于不占空间,不用算进来):
.text 0x20
.data 0x8
.rodata 0x4
.comment 0x2d
.shstrtab 0x51
.rel.text 0x10
.symtab 0xf0
.strtab 0x6b
这里的每一个段都有一个段表元素来描述,总共11个。从头文件得知,每个元素的大小为40字节。也就是说段表总共占了 0x1b8 个字节的空间。而且段表的开始地址由于内存对齐需要,中间空了2个字节。因为段表的开始地址是第224个字节;
.rel.text 的开始地址也由于内存对齐的要求,补了一个空字节。
在加上头文件的 0x34 个字节,总共加起来是 1028 字节。
[cpp] view plain copy
[root@xuxingwang-centos Program]# ls -al SimpleSection.o
-rw-r--r-- 1 root root 1028 Aug 21 16:09 SimpleSection.o
这个目标文件的大小恰好是1028个字节。
标签:
版权声明:转载此文是出于传递更多信息之目的。若有来源标注错误或侵犯了您的合法权益,请作者持权属证明与本网联系,我们将及时更正、删除,谢谢您的支持与理解。