您的位置 首页 制造

了解在Linux下可执行文件格局

了解在Linux下可执行文件格式-Linux下面,目标文件、共享对象文件、可执行文件都是使用ELF文件格式来存储的。程序经过编译之后会输出目标文件,然后经过链接可以产生可执行文件或者共享对象文件。

Linux下面,方针文件、同享方针文件、可履行文件都是运用ELF文件格局来存储的。程序通过编译之后会输出方针文件,然后通过链接能够发生可履行文件或许同享方针文件。linux下面运用的ELF文件和Windows操作系统运用的PE文件都是从Unix系统的COFF文件格局演化来的。 

咱们先来了解一些根本的主意。

首要,最重要的思路是一个程序从人能读懂的格局转换为供操作系统履行的二进制格局之后,代码和数据是分隔寄存的,之所以这样规划有这么几个原因:

1、程序履行之后,代码和数据能够被映射到不同特点的虚拟内存中。因为代码一般是只读的,而数据是可读可写的;

2、现代CPU有强壮的缓存系统。程序和代码别离能够进步程序的局部性,添加缓存射中的概率;

3、还有最重要的一个原因是当有多个程序副本在运转的时分,只读部分能够只在内存中保存一份,这样大大节省了内存。

在ELF的界说中,把他们分隔寄存的当地称为一个 Section ,便是一个段。

一个ELF文件中重要的段包含:

.text 段:存储 只读程序

.data 段:存储 现已初始化的全局变量和静态变量

.bss 段:存储 未初始化的全局变量和静态变量,因为这些变量的值为0,所以这个段在文件傍边不占有空间

.rodata 段:存储 只读数据,比方字符串常量

咱们用一个比如来看一下ELF文件的格局究竟是什么。首要,在Linux下编写一个C程序:SimpleSecTIon.c

[cpp] view plain copy

int printf(const char *format, … );  

int global_init_var = 16;  

int global_unint_var;  

void func1 (int );  

int main()  

{  

staTIc int staTIc_var = -32;  

staTIc int static_var_uninit;  

int a = 1;  

int b;  

func1(static_var + global_init_var + a + b);  

return a;  

}  

void func1 (int i)  

{  

printf(“%d\n”, i);  

}  


然后,发生方针文件:

[cpp] view plain copy

[root@xuxingwang-centos Program]# gcc -c SimpleSection.c  

[root@xuxingwang-centos Program]# file SimpleSection.o  

SimpleSection.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个字节。

 

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/bandaoti/zhizao/89715.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部