文章目录
- elf 程序示意图
- ELF文件格式概述
- ELF的组成结构
- 1. ELF头部(ELF Header)
- 2. 程序头表(Program Header Table)与程序头项(Program Header Entry)
- 3. 节区头表(Section Header Table)与节区头项(Section Header Entry)
elf 程序示意图
`
ELF文件格式概述
ELF文件格式是UNIX系统实验室开发的,用于定义应用程序二进制接口(ABI)。在Linux系统中,ELF文件主要分为三种类型:
- 可重定位文件(Relocatable):由编译器和汇编器生成的.o文件,供链接器处理。
- 可执行文件(Executable):链接器处理.o文件后生成的文件,用于创建进程映像。
- 共享对象文件(Shared Object):动态库文件,如.so文件。
ELF文件的结构包括以下几个部分:
- ELF头部(ELF Header):包含文件的基本信息,如体系结构和操作系统等,并指出节区头表和程序头表的位置。
- 程序头表(Program Header Table):包含运行时各个段(segment)的信息,主要用于运行时。
- 节区头表(Section Header Table):包含所有节区(section)的信息,主要用于编译和链接。
- 节区(Sections):程序员可见的节区,供链接器使用。
- 段(Segments):运行时的段,程序员不可见,由加载器使用。
ELF的组成结构
ELF文件的结构可以通过以下数据结构来描述:
1. ELF头部(ELF Header)
ELF头部包含了文件的基本信息,并指出了节区头表和程序头表的位置。以下是ELF头部的数据结构:
typedef struct {unsigned char e_ident[16]; // ELF标识信息Elf32_Half e_type; // 文件类型Elf32_Half e_machine; // 目标体系结构类型Elf32_Word e_version; // 文件版本Elf32_Addr e_entry; // 程序入口的虚拟地址Elf32_Off e_phoff; // 程序头表的偏移量Elf32_Off e_shoff; // 节区头表的偏移量Elf32_Word e_flags; // 特定于处理器的标志Elf32_Half e_ehsize; // ELF头部的大小Elf32_Half e_phentsize; // 程序头表每个表项的大小Elf32_Half e_phnum; // 程序头表表项的个数Elf32_Half e_shentsize; // 节区头表每个表项的大小Elf32_Half e_shnum; // 节区头表表项的数目Elf32_Half e_shstrndx; // 节区头表字符串表索引
} Elf32_Ehdr;
2. 程序头表(Program Header Table)与程序头项(Program Header Entry)
程序头表提供了运行时各个段的信息,如段的类型、偏移、虚拟地址等。以下是程序头项的数据结构:
typedef struct {Elf32_Word p_type; // 段的类型Elf32_Off p_offset; // 段在文件中的偏移Elf32_Addr p_vaddr; // 段的虚拟地址Elf32_Addr p_paddr; // 段的物理地址Elf32_Word p_filesz; // 段在文件中的大小Elf32_Word p_memsz; // 段在内存中的大小Elf32_Word p_flags; // 段标志Elf32_Word p_align; // 段对齐
} Elf32_Phdr;
3. 节区头表(Section Header Table)与节区头项(Section Header Entry)
节区头表包含了文件中的各个节区信息,每个节区都指定了类型和大小。以下是节区头项的数据结构:
typedef struct {Elf32_Word sh_name; // 节区名称Elf32_Word sh_type; // 节区类型Elf32_Word sh_flags; // 节区标志Elf32_Addr sh_addr; // 节区地址Elf32_Off sh_offset; // 节区偏移Elf32_Word sh_size; // 节区大小Elf32_Word sh_link; // 节区链接Elf32_Word sh_info; // 节区信息Elf32_Word sh_addralign; // 节区对齐Elf32_Word sh_entsize; // 节区项大小
} Elf32_Shdr;
-
.interp(解释器节区):
- 这个节区包含了动态链接器(解释器)的路径,是一个ASCII字符串。当一个程序需要动态链接库时,它会使用这个节区中指定的动态链接器来解析和加载所需的共享库。例如,许多Linux程序的.interp节区会指向
/lib/ld-linux.so.2
或/lib64/ld-linux-x86-64.so.2
,这是GNU C库的动态链接器。
- 这个节区包含了动态链接器(解释器)的路径,是一个ASCII字符串。当一个程序需要动态链接库时,它会使用这个节区中指定的动态链接器来解析和加载所需的共享库。例如,许多Linux程序的.interp节区会指向
-
.data(数据节区):
- 这个节区包含了程序的初始化数据,是程序中可修改的部分。它通常用于存储变量和数组等。在程序运行时,.data节区的内容会被加载到内存中,并且可以被程序读写。
-
.rodata(只读数据节区):
- 这个节区包含了程序的只读数据,它包含了那些在程序运行时不应该被修改的数据,如字符串常量。例如,printf函数中的字符串参数通常存储在.rodata节区。这个节区在程序运行时会被加载到内存中,但是只能被读取,不能被修改。
-
.init(初始化节区):
- 这个节区包含了程序启动时执行的代码,通常用于初始化全局变量和执行启动时的设置。当程序开始运行时,.init节区中的代码会在main函数执行之前被调用。
-
.fini(终止节区):
- 与.init节区相对应,.fini节区包含了程序终止时执行的代码,用于执行清理工作,如释放资源和关闭文件。当程序结束运行时,.fini节区中的代码会在main函数返回后被调用。
-
.gnu.hash(GNU散列节区):
- 这个节区是一个散列表,用于加速符号表的查找。它允许程序在不进行线性搜索的情况下快速定位符号(如函数和变量名)。这对于动态链接器来说非常重要,因为它需要快速解析程序和库中的符号引用。
这些节区是ELF文件格式的一部分,它们共同协作,确保程序能够正确地被加载、链接和执行。了解这些节区的功能对于深入理解程序的内部工作机制和进行系统编程至关重要。