ELF
是Executable and Linking Format
的缩写,即可执行和可链接的格式,是Unix/Linux
系统ABI (Application Binary Interface)
规范的一部分。Unix/Linux
下的可执行二进制文件、目标代码文件、共享库文件和core dump文件都属于ELF
文件。
ELF格式视图
ELF文件有链接视图和执行视图,两种视图形式:
链接视图:
静态链接器(即编译后参与生成最终ELF过程的链接器,如ld )会以链接视图解析ELF。编译时生成的 .o(目标文件)以及链接后的 .so (共享库)均可通过链接视图解析,链接视图可以没有段表(如目标文件不会有段表)。
执行视图:
动态链接器(即加载器,如x86架构 linux下的 /lib/ld-linux.so.2或者安卓系统下的 /system/linker均为动态链接器)会以执行视图解析ELF并动态链接,执行视图可以没有节表。
左边是ELF
的链接视图,可以理解为是目标代码文件的内容布局。右边是ELF
的执行视图,可以理解为可执行文件的内容布局。
对于两种视图来说,ELF Header
是两种说共有的。
同时,在两个视图的区别上,对于链接视图来说section
是主要特征,同时对于Section Header Table
在链接视图中也是必要的,但Program Header Table
来说是非必要的。但对于执行视图来说Segment
是主要特征,同时对于Program Header Table
在链接视图中也是必要的,但Section Header Table
是非必要的。
segments
与sections
区分与联系
segments
与sections
区别在于:
- 节(section)
- 在汇编中经常提到的
.text
,.bss
,.data
这些都属于section
层面上的。 .text
:保存程序代码。.data
:保存已经初始化的全局变量和局部静态变量.bss
: 保存未初始化的全局变量和局部静态变量- 目标代码文件中的
section
和section header table
中的条目是一一对应的。section
的信息用于链接器对代码重定位。
- 在汇编中经常提到的
- 段(segment)
- 我们平常说的代码段与数据段这些都是是
segment
层面上的。 - 目标代码中的
section
会被链接器组织到可执行文件的各个segment
中。.text section
的内容会组装到代码段中,.data
,.bss
等节的内容会包含在数据段中。 - 而文件载入内存执行时,是以
segment
组织的,每个segment
对应ELF
文件中program header table
中的一个条目,用来建立可执行文件的进程映像。
- 我们平常说的代码段与数据段这些都是是
段(segments
)与节(sections
)同时又是是包含的关系,一个segment
包含若干个section
。当ELF
文件被操作系统加载到内存中后(加载到内存中也就是说这个elf
要运行),系统会将多个具有相同权限(flg
值)section
合并成一个segment
(优化空间利用),减少内存碎片。
ELF Header 分析
之前说,在ELF文件中无论说基于执行视图还是链接视图,ELF Header
是都有的结构。在elf文件中
ELF header
的定义可以在Linux系统的 /usr/include
目录下elf.h
文件中找到。(用vs 装上c/c++相关插件后,可以直接定位到)
在32位与64位系统下,ELF header
的定义是不同的:
Elf32_Ehdr
是32位 ELF header的结构体。定义如下:
1 | typedef struct |
Elf64_Ehdr
是64位ELF header的结构体。定义如下:
1 | typedef struct |
Elf64_Addr
和 Elf64_Off
都是64位无符号整数。而Elf32_Addr
和 Elf32_Off
是32位无符号整数。这导致ELF header的所占的字节数不同。32位的ELF header占52个字节,64位的ELF header占64个字节。
e_ident
e_ident
占16个字节。前四个字节被称作ELF的Magic Number。
1 | unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ |
如上图,前4个字节是ELF的Magic Number
,固定为7f 45 4c 46
,也对应着字符串\177ELF
第5个字节为EI_CLASS
代表当前ELF文件是32位还是64位的。值为 ELFCLASS32(0x1)表32位,数值为 ELFCLASS64 (0x2)表64位。
第6个字节为EI_DATA
了数据的编码方式,即我们通常说的little endian或是big endian。值 ELFDATA2LSB
表little endian,即为 小端排序,低位字节在前,或者直接说低位字节在低位地址,比如0x7f454c46
,存储顺序就是46 4c 45 7f
。 值 ELFDATA2MSB
表big endian就是大端排序,高位字节在前,直接说就是高位字节在低位地址,比如0x7f454c46
,在文件中的存储顺序是7f 45 4c 46
。
第7个字节为EI_VERSION
指明了ELF header的版本号,目前值都是EV_CURRENT(1)。
第8个字节为EI_OSABI
表操作系统ABI
标识,现在默认为0,
第9-16个字节,都填充为0。
e_type
e_type
代表文件类。
1 | Elf32_Half e_type; /* Object file type */ |
当其值为ET_REL
(1)表可重定位文 件(如目标文件)
当其值为ET_EXEC
(2)表可执行文件(可直接执行的文件)
当其值为ET_DYN
(3)表共享目标文件(如SO库)
当其值为ET_CORE
(4)表Core文件(吐核文件)
e_machine
e_machine
为架构信息。
1 | Elf32_Half e_machine; /* Architecture */ |
当值为EM_X86_64
(62)表x86-64架构,
e_verison
e_version
为文件版本,目前常见的ELF 文件版本均为EV_CURRENT(1)
。
1 | Elf32_Word e_version; /* Object file version */ |
e_entry
e_entry
表入口虚拟地址(RVA)。即_start
函数所在的地方(地址)。
1 | Elf32_Addr e_entry; /* Entry point virtual address */ |
e_phoff
e_phoff
为程序头表(段表)的偏移,程序头表离启始位置的值。
1 | Elf32_Off e_phoff; /* Program header table file offset */ |
e_shoff
e_shoff
为节头表的偏移,节头表离启始位置的值。
1 | Elf32_Off e_shoff; /* Section header table file offset */ |
e_flags
处理器特定的标志,一般为0
。
1 | Elf32_Word e_flags; /* Processor-specific flags */ |
e_ehsize
Elf_Header
的大小(字节),64
位则为64
,如果是32
位则为52
。
1 | Elf32_Half e_ehsize; /* ELF header size in bytes */ |
e_phentsize
·e_phentsize
表程序头表/段表(Program Header)
的大小(字节)
1 | Elf32_Half e_phentsize; /* Program header table entry size */ |
e_phnum
e_phnum
表段的数量。
1 | Elf32_Half e_phnum; /* Program header table entry count */ |
e_shentsize
e_shentsize
表节头(Section Header)
的大小(字节)。当ELF
文件被操作系统加载到内存中后(加载到内存中也就是说这个elf
要运行),系统会将多个具有相同权限(flg
值)section
合并成一个segment
(优化空间利用),在这个过程中section
的数量可能会发生改变。
1 | Elf32_Half e_shentsize; /* Section header table entry size */ |
e_shnum
e_shnum
表节头数量
1 | Elf32_Half e_shnum; /* Section header table entry count */ |
e_shstrndx
e_shstrndx
表节字符串表的节索引。
1 | Elf32_Half e_shstrndx; /* Section header string table index */ |
代码解析Elf头
结合上面知识,我们可以用c语言,来解析Elf 头解析。效果如下:
原代码如下
1 |
|
参考链接
https://ch3nye.top/Linux%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%88%86%E6%9E%90%E7%AC%94%E8%AE%B0(ELF)/
https://copyright1999.github.io/2021/10/10/%E8%A7%A3%E6%9E%90ELF%E6%96%87%E4%BB%B6-%E4%B8%80/