ElF 是什么?
- 可用于链接的可从定位文件,与其他目标文件生成可执行文件或者共享文件
- 可执行文件
- 目标共享文件
通俗的说 在c++或者 C 编译型语言 经过编译阶段生成可从定位的二进制文件可以说是一种ELF文件,第二种就是在Linux下可执行的文件.elf(一个或者多个.o 文件经过链接过程生成的可执行文件)同时要具有可执行权限,就能在Linux下运行。第三种就是.so库 。
对于ELF文件提供两种视图:
- 链接视图
- 执行视图
按照我的理解 两种视图两种规范,上面所说的3种ELF文件形式,那么系统如何区分?在于这个文件的具体作用,例如ELF文件是个用于链接的可从定位文件,那么就遵循链接视图,如果是可执行文件就要遵循执行视图,也就是说 基于不同类型的ELF文件所产生的规则。
具体视图:(图片来源网络)
链接视图以节为单位,执行视图以段为单位
- ELF 头部: 描述的是整个文件的组织
ELF header/格式代码如下:#define EI_NIDENT 16
typedef struct{ unsigned char e_ident[EI_NIDENT]; Elf32_Half e_type; //它标识的是该文件的类型。 Elf32_Half e_machine; //表明运行该程序需要的体系结构。 Elf32_Word e_version; // 表示文件的版本。 Elf32_Addr e_entry; //程序的入口地址。 Elf32_Off e_phoff; //表示Program header table 在文件中的偏移量 如果没有程序头表为0 Elf32_Off e_shoff; //表示Section header table 在文件中的偏移量 如果没有节头表为0 Elf32_Word e_flags; // 对IA32而言,此项为0。 Elf32_Half e_ehsize; //表示ELF header大小 Elf32_Half e_phentsize; //表示Program header table中每一个条目的大小。 Elf32_Half e_phnum; //表示Program header table中有多少个条目。 Elf32_Half e_shentsize; // 表示Section header table中的每一个条目的大小 Elf32_Half e_shnum; //表示Section header table中有多少个条目。 Elf32_Half e_shstrndx; //包含节名称的字符串是第几个节(从零开始计数 }Elf32_Ehdr; 64位基本同理
- e_ident :16字节 主要是描述ELF文件的解码信息(指明是32位还是64位)
- e_type :2字节 描述ELF文件的类型
ET_NONE, 0, No file type ET_REL, 1, Relocatable file(可重定位文件,通常是文件名以.o结尾,目标文件) ET_EXEC, 2, Executable file (可执行文件) ET_DYN, 3, Shared object file (动态库文件,) ET_CORE, 4, Core file (core文件) ET_NUM, 5,表示已经定义了5种文件类型 ET_LOPROC, 0xff00, Processor-specific ET_HIPROC, 0xffff, Processor-specific
其他字段看上面代码中描述
回到上面 那么节和段有什么区别呢? c++代码在链接阶段的第一个阶段 主要是段合并和符号表输出,那么要想生成一个可执行文件,段是程序执行的必要条件,则节的粒度相对就小
再看看节区头部表(Elf_Shdr):描述了其所对应的节的信息,如节名、节大小、在文件中的偏移、读写权限等。编译器、链接器、装载器都是通过节头表来定位和访问各个节的属性的。
sh_name 节名 :节名是一个字符串,保存在一个名为.shstrtab的字符串表(可通过Section Header索引到)。sh_name的值实际上是其节名字符串在.shstrtab中的偏移值
sh_type 节类型
sh_flags 节标志位
sh_addr 节地址:节的虚拟地址 如果该节可以被加载,则sh_addr为该节被加载后在进程地址空间中的虚拟地址;否则sh_addr为0
sh_offset 节偏移 如果该节存在于文件中,则表示该节在文件中的偏移;否则无意义,如sh_offset对于BSS 节来说是没有意义的
sh_size 节大小
sh_link、sh_info 节链接信息
sh_addralign 节地址对齐方式
sh_entsize 节项大小 有些节包含了一些固定大小的项,如符号表,其包含的每个符号所在的大小都一样的,对于这种节,sh_entsize表示每个项的大小。如果为0,则表示该节不包含固定大小的项。
节的分类
- .text 保存了程序代码指令的代码节。一段可执行程序
- .rodata节 保存只读数据
- .plt过程链接表,包含了动态链接器从共享库导入函数所必需的代码
- .data 存在于data段中,保存变量信息
- .bss 保存未初始化的全局变量,初始化为0的
- .got 保存了全局偏移表(和.plt一同表示动态库的入口地址),由动态链接器修改
- .dynsym节(动态链接符号表)保存在text中,保存了从动态库导入的符号表
- .dynstr节(动态链接字符串表)
- .rel 重定位表
- .symtab 符号表是一个Elfn_sym中的一个数组,保存了符号信息
- .strtab 保存符号字符串表,表中内容被Elfn_sym结构体中的st_name 引用
- .ctors(构造器) .dtors(析构器)保存了指向构造函数和析构函数的指针
符号表 :符号表是对某些数据或代码的符号的引用 .dynsym 保存了外部文件符号的全局符号 例如printf库函数 ,.symtab还保存了可执行文件的本地符号,如全局变量或者函数等
.dynsym 和 .symtab 的区别是 .dynsym是.symtab 的子集 .dynsym 被标记为ALLOC 表示运行时分配并加载内存
符号表中的每一项都是一个Elf_sym结构体
st_name 符号名。该值为该符号名在字符串表中的偏移地址。 st_value 符号对应的值。存放符号的值(可能是地址或位置偏移量)。 st_size 符号的大小。 st_other 0 st_shndx 符号所在的节 st_info 符号类型及绑定属性
还有一个重要的表: 重定位表Elf_Rel
r_offset 重定位入口的偏移。
对于可重定位文件来说,这个值是该重定位入口所要修正的位置的第一个字节相对于节起始的偏移
对于可执行文件或共享对象文件来说,这个值是该重定位入口所要修正的位置的第一个字节的虚拟地址
r_info 重定位入口的类型和符号
因为不同处理器的指令系统不一样,所以重定位所要修正的指令地址格式也不一样。每种处理器都有自己的一套重定位入口的类型。
对于可执行文件和共享目标文件来说,它们的重定位入口是动态链接类型的。
Elf 文件中 基本重要的结构就这些,如有不对 请指教