北京网站手机站建设公司电话,宁波建设集团几个分公司,抖音开放平台是干什么的,咸阳网站推广理解了进程的描述和创建之后#xff0c;自然会想到我们编写的可执行程序是如何作为一个进程工作的#xff1f;这就涉及可执行文件的格式、编译、链接和装载等相关知识。 这里先提一个常见的名词“目标文件”#xff0c;是指编译器生成的文件。“目标”指目标平台#xff0c… 理解了进程的描述和创建之后自然会想到我们编写的可执行程序是如何作为一个进程工作的这就涉及可执行文件的格式、编译、链接和装载等相关知识。 这里先提一个常见的名词“目标文件”是指编译器生成的文件。“目标”指目标平台 例如 x86 或 x86-64它决定了编译器使用的机器指令集。目标文件一般也叫作 ABI(Application Binary Interface应用程序二进制接口)目标文件和目标平台是二进制兼容的。二进制兼容 即指该目标文件已经是适应某一种 CPU 体系结构上的二进制指令。例如一个编译出来的x86-64目标文件是无法链接成ARM上的可执行文件的。 最古老的目标文件格式是 a.out后来发展成 COFF现在常用的有 PE (Windows)和 ELF(Linux)。 ELF(Executable and Linkable Format)即可执行的和可链接的格式 是一个文件格式的标准。ELF 格式的文件用于存储 Linux 程序。ELF 是一 种对象文件的格式用于定义不同类型的对象文件中都有什么内容、以什么样的格式放这些内容。ELF 首部会描绘整个文件的组织结构它还包括很多节(sections是在 ELF 文件里用以装载内容数据的最小容器)这些节有些是系统定义好的有些是用户在文件中通过.section 命令自定义的链接器会将多个输入目标文件中相同的节合并。
我们先来看一个例子直观感受一下 test.c原文件内容如下
#include stdio.hint g(int x)
{return x 3;
}int f(int x)
{return g(x);
}int main()
{return f(8) 1;
}ELF 文件的 3 种类型
以 ELF 格式为例来看在可执行文件格式里的 3 种不同类型的目标文件。
1可重定位文件这种文件一般是中间文件还需要继续处理。由编译器和汇编器创建一个源代码文件会生成一个可重定位文件。文件中保存着代码和适当的数据用来和其他的目标文件一起来创建一个可执行文件或者动态链接库文件。在编译 Linux 内核时可能会注意到每个内核源代码.c 文件都会生成一个同名的.o 文件该文件即为可重定位目标文件最后所有.o 文件会链接为一个文件即 Linux 内核。另外静态链接库文件实际上就是可重定位文件的打包也是可重定位文件一般以.a作为文件名后缀。
2可执行文件一般由多个可重定位文件结合生成是完成了所有重定位工作和符号解析的文件动态链接库符号是在运行时解析的文件中保存着一个用来执行的程序。重定位和符号解析会在链接部分详细介绍。
3动态链接库文件也称为共享目标文件是已经经过链接处理可以直接加载运行的库文件是可以被可执行文件或其他动态链接库文件加载使用的库文件例如标准 C 的库文件 libc.so。可以简单理解为没有主函数 main 的“可执行”文件只有一堆函数可供其他程序调用。Linux 下动态链接库文件文件名后缀为.so 的文件so 代表 shared object。
ELF 文件的作用 ELF 文件参与程序的链接(构建一个可执行程序)和程序的执行(加载可执行程序)所以可以从不同的角度来看待 ELF 格式的文件。
如果用于编译和链接(可重定位文件)则编译器和链接器将把 ELF 文件看作节的集合所有节由节头表描述程序头表可选。如果用于加载执行(可执行文件)则加载器将把 ELF 文件看作程序头表描述的段的集合一个段可能包含多个节和节头表可选。如果是动态链接库文件则两者都含有
ELF文件格式
ELF文件的索引表 ELF文件的索引表。ELF文件的主体是各种节典型的如代码节.text还有描述这些节属性的信息Program header table和Section header table以及ELF文件的整体描述信息ELF header整体如图所示。 ELF Header结构 ELF Header在文件最开始描述了该文件的组织情况。ELF文件头会指出可执行文件是32位还是64位的e_ident数组的第五个字节是1表示是32位2表示是64位。ELF Header的其他部分主要说明了其他文件内容的位置、大小等信息。ELF Header长度为64字节在/usr/include/elf.h文件中可以看到其C语言格式的定义如下 ELF表头首先会给出很多关于本ELF文件的属性信息如上面提及到的3种ELF类型就是通过e_type来体现的。e_type的值1234分别代表可重定位目标文件可执行文件共享目标文件和核心文件。如上面的ELF文件索引表所示其中最重要的是段头表Program header table和节头表Section header table的位置。 段头表存储于文件的e_phoffELF header的字段下同位置有e_phnum项内容每项大小为e_phentsize字节。 节头表基本定义了整个ELF文件的组成可以说是整个ELF就是由若干个节Section组成的。段只是对节区进行了重新组合将连续的多个节区描述为一段连续区域对应到一块连续的内存地址中。
Section Header结构 节头表是由Section Header组成的表包含了描述文件节区的信息每个节区在表中都有一项每一项给出诸如节区名称、节区大小这类信息。用于链接的目标文件必须包含节区头部表其他目标文件有没有这个表都可以。每个节区头部结构的描述如下 我们可以查看hello这个可执行文件的sections、指令及主要输出内容如下节区信息。前6列分别是[Nr]索引、Name 节名、Type 类型、Addr 虚拟地址、Off 偏移和Size 节大小。简单来说该节描述了将可执行文件中起始位置位Off、大小为Size的一段数据加载到内存地址Addr。
pshello.c文件中只是单纯的打印了一句Hello World! 我们以[6].text来理解每一节头的内容一行是一个节的描述。
Type PROGBITS表示该节存储的是代码。Addr为080490a0 是该部分将加载到内存中的虚拟地址。Off为节在可执行文件中的偏移。后半部分的Key to Flags是对Flg中标识的说明。如.text节Flg为AXA(Alloc)表示需要加载到内存中X(eXecute)表示对应内存需要可执行权限。
Program Header结构 段头Program Header表是和创建进程相关的描述了连续的几个节在文件中的位置、大小以及它被放进内存后的位置和大小告诉系统如何创建进程映像可执行文件加载器就可以按这个说明将可执行文件搬到内存中。用来构造进程映像的目标文件必须具有段头表可重定位文件不需要这个表。 我们可以查看生成的hello这个可执行文件的段头表指令及主要输出内容如下段头表示例。 8列分别是Type类型、Offset文件偏移、VirtAddr虚拟地址、PhysAddr物理地址、FileSiz可执行文件中该区域的大小、MemSiz内存中该区域的大小、Flg属性标识和Align对齐方式。和节头表相似该表描述了可执行文件中起始位置为Offset、大小为FileSiz的一段数据加载到内存地址VirtAddr中。两者的虚拟地址信息是一致的但节头表的Addr可以没有信息可重定位目标文件的Addr就是全0。
我们以第一行为例进行说明Type值为LOAD表示该段Segment需要加载到内存Offset全0表示其内容为从可执行文件头开始共0x001e8FileSiz个字节加载到虚拟地址0x08048000VirtAddr处该段为可读R权限4kAlign0x1000对齐。
再往下看为节与段的映射关系说明Section to Segment mapping:00即第一行描述的段一共包括了.note、.gnu、.ABI-tag等多个节。
相关操作指令
我们可以使用如下指令对ELF进行更多的研究实践。
1man elf在Linux下输入此指令即可查看详细的格式定义。
2readelf用于显示一个或多个elf格式的目标文件的信息可以通过它的选项来控制显示哪些信息。 -a 等价于 -h -l -S -s -r -d -V -A - I-h 显示elf文件开始的文件头信息-S 显示节头信息如果有-l 显示Program Header。小写的L-s 显示符号表段中的项如果有-r 显示可重定位段的信息-H 显示readelf所支持的命令行选项3objdump显示二进制文件信息用于查看目标文件或者可执行的目标文件的构成的gcc工具选项如下。 -f 显示objfile中每个文件的整体头部摘要信息-h 显示目标文件各个section的头部摘要信息-r 显示文件的重定位入口。如果和-d或者-D一起使用重定位部分以反汇编后的格式显示出来-s 显示指定section的完整内容。默认所有的非空section都会被显示-t 显示文件的符号表入口-x 显示所可用的头信息包括符号表、重定位入口。-x等价于-a -f -h -r -t同时指定4hexdump用十六进制的数字来显示elf的内容。 以上内容为中科大软件学院《Linux操作系统分析》课后总结感谢孟宁老师的倾心教授老师讲的太好啦^_^
参考资料《庖丁解牛Linux内核分析》 孟宁 编著