主页 > 开源代码  > 

ELF,链接,加载

ELF,链接,加载
ELF:

ELF类型的文件主要有四种:

1.可重定位文件:即XXX.o,包含与其他目标文件链接来创建可执行文件或者目标共享文件的代码和数据

2.可执行文件:即可执行程序

3.共享目标文件:即XXX.so

4.内核转储:存放当前进程的执行上下文,用于dump信号触发

ELF文件包含一下四个部分:

1.ELF头:描述文件的主要特性,位于文件的头部,用来定位文件的其他部分。

2.程序头表:列举了所有有效的段(段是由节合并而来的)和他们的属性。表里记录的每一个段开始的位置,位移和长度,毕竟这些段都是紧密的放在二进制的文件中,需要段表的描述信息来将他们分开

3.节头表:包含对节的描述

4.节:ELF文件的基本组成单位,包含了特定类型的数据。ELF文件的各种信息和数据都存储在不同的节中,如代码节存储了可执行程序,数据节存储了全局变量的静态数据等。

常见的节:代码节,数据节

看到上面的section headers一共是30个节

ELF文件主要包含代码节(.text)已初始化数据节(.data)未初始化数据节(.bss)。全局变量是在程序的数据段的,局部变量是在栈上分配的,全局变量是在程序启动的时候就加载到内存中的,局部变量时调用到相应的函数的时候在会在栈上分配。所以说局部变量是不会在ELF文件中的数据段的,初始化的全局变量放到.data数据节中,未初始化的全局变量放到.bss数据节中。bss(better save space更好的节省空间),因为我们知道未初始化的全局变量都是默认初始化为0的,所以我们不需要将其数据存起来,我们只需要知道有几个未初始化的全局变量,后面加载到内存的时候统一分配空间一律初始化为0即可。所以节省的空间实际上是磁盘空间。

“ELF中存不存在局部变量的数据?”,如果局部变量是非静态的,那ELF里没有它们的实际数据,而是只有关于如何分配栈空间或寄存器的指令。而如果是静态局部变量,那么ELF的(.data)有它们的初始化值。

ELF形成可执行

1.先将多份的C/C++源代码编译为.o文件

2.将多份的.o文件的节进行和合并(实际上的合并就是链接的过程,并不是简单的合并,也会涉及对库的合并)

ELF可执行文件的加载

1.一个ELF会有多个不同的section,在加载到内存的时候这些Section也会进行合并,形成segment

2.合并原则:相同的属性,比如可读可写可执行

3.很显然,这个合并工作的合并方式在进行链接之后就已经确定了,其合并方式被记录在了ELF的程序头表中(还记得吗,程序头表列举了所有有效的段(合并的节)和他们的属性。表里记录的每一个段开始的位置,位移和长度)

可以看到一共有九个段,section to segment mapping中指明了是哪几个节合并到一起形成的段。

链接器在链接阶段(生成可执行文件时)会 显式记录 Section 到 Segment 的映射关系,并将这些信息写入 ELF 文件的 Program Header Table 中。操作系统加载程序时,直接通过 Program Header 中的 Segment 定义(而非原始的 Section)将程序映射到内存。

操作系统加载程序时,完全依赖 Program Header Table:

读取每个 Segment 的权限、文件偏移和内存地址。

将 Segment 对应的文件内容直接映射到内存(如通过 mmap 系统调用)。

设置内存页的权限(如代码段设为可读+可执行,数据段设为可读+可写)。

注意:加载器不关心原始的 Section 划分,仅按 Segment 加载。因此,Section 信息(如 .text, .data)在调试或反编译时有用,但对程序运行非必需。

编译链接视图(Linking view) - 对应节头表 Section header table

1.⽂件结构的粒度更细,将⽂件按功能模块的差异进⾏划分,静态链接分析的时候⼀般关注的 是链接视图,能够理解 ELF ⽂件中包含的各个部分的信息。 2.为了空间布局上的效率,将来在链接⽬标⽂件时,链接器会把很多节(section)合并,规整 成可执⾏的段(segment)、可读写的段、只读段等。合并了后,空间利⽤率就⾼了,否 则,很⼩的很⼩的⼀段,未来物理内存⻚浪费太⼤(物理内存⻚分配⼀般都是整数倍⼀块给 你,⽐如4k),所以,链接器趁着链接就把⼩块们都合并了。 加载器视图(execution view) - 对应程序头表 Program header table 告诉操作系统,如何加载可执⾏⽂件,完成进程内存的初始化。⼀个可执⾏程序的格式中, ⼀定有 program header table

那么为什么要将section合并成为segment?

Section合并的主要原因是为了 减少⻚⾯碎⽚ ,提⾼内存使⽤效率。如果不进⾏合并, 假设⻚⾯大小为4096字节(内存块基本⼤⼩,加载,管理的基本单位),如果.text部分 为4097字节,.init部分为512字节,那么它们将占⽤3个⻚⾯,⽽合并后,它们只需2个⻚⾯。 此外,操作系统在加载程序时,会将具有相同属性的section合并成⼀个⼤的 segment,这样就可以实现不同的访问权限,从⽽优化内存管理和权限访问控制。
标签:

ELF,链接,加载由讯客互联开源代码栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“ELF,链接,加载