ELF(Executable and Linkable Format,可执行与可链接格式)是一种用于二进制文件、可执行文件、目标代码、共享库和核心转储的标准文件格式。它最初由 Unix 系统实验室(USL)设计,现已成为类 Unix 系统(如 Linux、FreeBSD、Android 等)中最常见的文件格式之一。
ELF 文件的主要类型
1.可执行文件(Executable)
可直接由操作系统加载运行的程序(如 /bin/ls)。
包含程序代码(text)、数据(data)、以及如何加载到内存的信息。
2.目标文件(Relocatable Object File)
由编译器生成(.o 文件),需通过链接器(linker)处理生成可执行文件或共享库。
包含代码和数据,但地址未最终确定(可重定位)。
3.共享库(Shared Object)
动态链接库(如 .so 文件),在运行时被加载到进程内存中(如 libc.so)。
允许多个程序共享同一份代码,节省内存。
4.核心转储文件(Core Dump)
程序崩溃时生成的内存快照,用于调试。
ELF 文件格式详解
1. ELF 文件基本结构
ELF 文件由以下几部分组成:
-
ELF 头(ELF Header)
-
描述文件的总体信息,如文件类型(可执行/目标文件)、目标架构(x86/ARM)、程序入口地址、节头表(Section Header)和程序头表(Program Header)的位置等。
-
通过
readelf -h <file>
可查看。
-
-
程序头表(Program Header Table,对可执行文件重要)
-
告诉操作系统如何将文件加载到内存(例如哪些段需要映射到内存)。
-
每个条目描述一个段(Segment),如代码段(
LOAD
)、动态链接信息(INTERP
)等。 -
通过
readelf -l <file>
查看。
-
-
节头表(Section Header Table,对链接重要)
-
包含文件的所有节(Section)信息,如代码节(
.text
)、数据节(.data
)、符号表(.symtab
)、字符串表(.strtab
)等。 -
链接器使用此表处理目标文件。
-
通过
readelf -S <file>
查看。
-
2. ELF 头部 (ELF Header)
ELF 头部位于文件开头,包含文件的基本信息。
主要字段包括:
-
Magic Number:标识 ELF 文件的魔数(7f 45 4c 46)
-
Class:32位(01)或64位(02)
-
Data:字节序(小端序/大端序)
-
Version:ELF 版本
-
OS/ABI:目标操作系统和应用二进制接口
-
Type:文件类型(可执行、共享库、目标文件等)
-
Machine:目标架构(x86、ARM等)
-
Entry point address:程序入口点
-
Program header table offset:程序头表偏移
-
Section header table offset:节头表偏移
-
Flags:处理器特定标志
-
Header size:ELF 头部大小
-
Program header size:程序头表中每个条目的大小
-
Number of program headers:程序头表中条目数量
-
Section header size:节头表中每个条目的大小
-
Number of section headers:节头表中条目数量
-
Section name string table index:包含节名称的字符串表的索引
3. 程序头表 (Program Header Table)
描述如何创建进程内存映像,仅存在于可执行文件和共享库中。使用 readelf -l <file>
查看。
包含多个程序头(Program Header),每个描述一个段(Segment),常见类型包括:
-
PT_LOAD:可加载段
-
PT_DYNAMIC:动态链接信息
-
PT_INTERP:程序解释器路径(通常是动态链接器)
-
PT_NOTE:辅助信息
-
PT_PHDR:程序头表自身的位置和大小
每个程序头包含:
-
段类型
-
文件偏移
-
虚拟地址
-
物理地址(系统相关)
-
文件中的大小
-
内存中的大小
-
权限标志(读/写/执行)
-
对齐方式
4. 节头表 (Section Header Table)
描述文件的各个节(Section),用于链接和调试。使用 readelf -S <file>
查看。
常见节包括:
-
.text:可执行代码
-
.data:已初始化的数据
-
.bss:未初始化的数据
-
.rodata:只读数据
-
.symtab:符号表
-
.strtab:字符串表
-
.shstrtab:节名称字符串表
-
.rel. / .rela.**:重定位信息
-
.dynamic:动态链接信息
-
.interp:程序解释器路径名
-
.got / .plt:全局偏移表和过程链接表(用于动态链接)
每个节头包含:
-
节名称(字符串表索引)
-
节类型
-
标志(可写、可分配、可执行等)
-
虚拟地址
-
文件偏移
-
节大小
-
链接到其他节的索引
-
额外信息
-
对齐要求
-
条目大小(对于表格类型的节)
工具和命令
-
readelf:查看 ELF 文件信息
-
objdump:反汇编和查看目标文件信息
-
nm:列出符号表
-
ldd:列出动态依赖
-
file:识别文件类型
-
strip:删除符号表和调试信息
以"Hello World" 程序为例,展示如何在 Linux 下分析 ELF 文件。
1. 创建 Hello World 程序
首先创建一个简单的 C 程序:
// hello.c
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
编译它(不优化并包含调试信息以便更好观察):
gcc -g -o hello hello.c
2. 查看 ELF 文件类型
file hello
输出:
hello: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=..., for GNU/Linux 3.2.0, with debug_info, not stripped
这告诉我们:
64位 ELF 文件,小端序 (LSB),动态链接,使用 /lib64/ld-linux-x86-64.so.2 作为解释器,包含调试信息,符号表未被剥离
3. 查看 ELF 头部
readelf -h hello
输出示例(部分):
ELF Header:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
Class: ELF64
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: DYN (Shared object file)
Machine: Advanced Micro Devices X86-64
Version: 0x1
Entry point address: 0x1060
Start of program headers: 64 (bytes into file)
Start of section headers: 14712 (bytes into file)
Flags: 0x0
Size of this header: 64 (bytes)
Size of program headers: 56 (bytes)
Number of program headers: 13
Size of section headers: 64 (bytes)
Number of section headers: 36
Section header string table index: 35
4. 反汇编查看代码
objdump -d hello
输出示例(部分):
0000000000001060 <main>:
1060: 55 push %rbp
1061: 48 89 e5 mov %rsp,%rbp
1064: 48 8d 3d 99 0f 00 00 lea 0xf99(%rip),%rdi # 2004 <_IO_stdin_used+0x4>
106b: e8 e0 fe ff ff callq f50 <puts@plt>
1070: b8 00 00 00 00 mov $0x0,%eax
1075: 5d pop %rbp
1076: c3 retq
5. 查看依赖的动态库
ldd hello
输出示例:
linux-vdso.so.1 (0x00007ffd5b3e6000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8c4a6c4000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8c4a8d7000)
6. 查看其他的(篇幅原因不做演示)
查看程序头表(段信息):readelf -l hello
查看节头表(节信息):readelf -S hello
查看符号表:readelf -s hello
查看动态段信息:readelf -d hello
通过这些工具和命令,可以全面了解一个 ELF 可执行文件的结构和内容。从头部信息到各个段和节,从符号表到动态链接信息,这些构成了 Linux 下可执行文件的基本要素。