ELF文件结构
无限期停更
ELF文件格式分析 | Sp4n9x’s Blog
手拆ELF32(一,文件头)【转】 - Sky&Zhang - 博客园 (cnblogs.com)
ELF解析01 - ELF头和程序头 - 掘金 (juejin.cn)
ELF Section Header的结构 (mudongliang.github.io)
因为上面几篇讲的太详细了,后面不写了
其中第三篇为加载时需要关注的点,前两篇都是讲结构特点
上次了解了windows系统上的PE文件结构,这里在网上总结了一些(抄了一些)内容
序
ELF分为大致分为三种:
- 可重定位文件(relocatable):编译器和汇编器产生的.o文件,被Linker所处理
- 可执行文件(executable):Linker对.o文件进行处理输出的文件,进程映像
- 共享对象文件(shared object):动态库文件.so
文件布局大致为:
- ELF header
- program header table(描述段)
- 段和区
- section header table(描述区)
以上的布局只有ELF header在固定位置,其它的位置不定
ELF header
e_ident
Magic Number
7F 45 4C 46
EI_class:(1字节)
- 无效类
- 1 32位
- 2 64位
EI_DATA:(1字节)
- 0 无效数据编码
- 1 小端序
- 2 大端序
EI_VERSION:只有01这个值
EI_OSABI:操作系统/ABI标识
宏名称 | 值 | 含义 |
---|---|---|
ELFOSABI_NONE | 0 | No extensions or unspecified |
ELFOSABI_SYSV | 0 | UNIX System V ABI |
ELFOSABI_HPUX | 1 | HP-UX ABI |
ELFOSABI_NETBSD | 2 | NetBSD ABI |
ELFOSABI_LINUX | 3 | Linux ABI |
ELFOSABI_HURD | 4 | GNU Hurd ABI |
ELFOSABI_SOLARIS | 6 | Sun Solaris ABI |
ELFOSABI_AIX | 7 | IBM AIX ABI |
ELFOSABI_IRIX | 8 | SGI Irix ABI |
ELFOSABI_FREEBSD | 9 | FreeBSD ABI |
ELFOSABI_TRU64 | 10 | Compaq TRU64 UNIX ABI |
ELFOSABI_MODESTO | 11 | Novell Modesto ABI |
ELFOSABI_OPENBSD | 12 | OpenBSD ABI |
ELFOSABI_OPENVMS | 13 | OpenVMS ABI |
ELFOSABI_NSK | 14 | NonStop Kernel ABI |
ELFOSABI_AROS | 15 | AROS ABI |
ELFOSABI_FENIXOS | 16 | Fenix OS ABI |
ELFOSABI_CLOUDABI | 17 | CloudABI |
ELFOSABI_ARM_AEABI | 64 | ARM EABI |
ELFOSABI_ARM | 97 | ARM ABI |
ELFOSABI_STANDALONE | 255 | Standalone (embedded) application |
EI_ABIVERSION:
进一步指定ABI版本
elf pad:填充字节,置0
E_TYPE
标明文件类型(2字节)
宏名称 | 值 | 含义 |
---|---|---|
ET_NONE | 0 | 无文件类型 |
ET_REL | 1 | 可重定位文件 |
ET_EXEC | 2 | 可执行文件 |
ET_DYN | 3 | 共享目标文件 |
ET_CORE | 4 | 核心转储文件 |
ET_NUM | 5 | 定义的文件类型数量 |
ET_LOOS | 0xFE00 | 特定操作系统目标文件类型值范围的下限 |
ET_HIOS | 0xFEFF | 特定操作系统目标文件类型值范围的上限 |
ET_LOPROC | 0xFF00 | 特定处理器目标文件类型值范围的下限 |
ET_HIPROC | 0xFFFF | 特定处理器目标文件类型值范围的上限 |
e_machine
标明处理器架构和指令集(2字节)
宏名称 | 值 | 含义 |
---|---|---|
EM_NONE | 0 | 无机器类型 |
EM_M32 | 1 | AT&T WE 32100 |
EM_SPARC | 2 | SUN SPARC |
EM_386 | 3 | Intel 80386(x86) |
EM_68K | 4 | Motorola 68000(M68k) |
EM_88K | 5 | Motorola 88000(M88k) |
EM_860 | 7 | Intel 80860 |
EM_MIPS | 8 | MIPS R3000 big-endian |
EM_MIPS_RS3_LE | 10 | MIPS R3000 little-endian |
EM_MIPS_RS4_BE | 10 | MIPS R4000 big-endian |
EM_PPC | 20 | PowerPC |
EM_PPC64 | 21 | PowerPC 64-bit |
EM_ARM | 40 | ARM 32-bit(up to ARMv7/Aarch32) |
EM_SPARCV9 | 43 | SPARC v9 64-bit |
EM_IA_64 | 50 | HP/Intel IA-64 |
EM_X86_64 | 62 | AMD x86-64 |
EM_MSP430 | 105 | Texas Instruments msp430 |
EM_ALTERA_NIOS2 | 113 | Altera Nios II |
EM_AARCH64 | 183 | ARM 64-bit(ARMv8/Aarch64) |
EM_AVR32 | 185 | Amtel 32-bit microprocessor |
EM_STM8 | 186 | STMicroelectronics STM8 |
EM_RISCV | 243 | RISC-V |
EM_BPF | 247 | Linux BPF – in-kernel virtual machine |
e_version
当前文件版本(四字节)
0 无版本
其它 当前版本号
程序入口
8字节,标明入口点
程序头表偏移
8字节,描述段表在文件中的偏移
区表偏移
8字节,描述区表在文件中的偏移
e_flag
处理器指定的与文件相关联的flag(4字节,一般是0)
ELF_header 大小
2字节,指明头大小。一般为0x40
程序头表大小
2字节,如果没有程序头表,则为0
程序头内容数量
2字节,标明程序头表中的内容个数
节区头表大小
2字节,如果没有节区头表,则为0
节区头内容数量
2字节,标明节区头表中的内容个数
节区头字符表索引
2字节,有以下可选值
/* Special section indices. */
#define SHN_UNDEF 0 /* Undefined section /
#define SHN_LORESERVE 0xff00 / Start of reserved indices /
#define SHN_LOPROC 0xff00 / Start of processor-specific /
#define SHN_BEFORE 0xff00 / Order section before all others
(Solaris). /
#define SHN_AFTER 0xff01 / Order section after all others
(Solaris). /
#define SHN_HIPROC 0xff1f / End of processor-specific /
#define SHN_LOOS 0xff20 / Start of OS-specific /
#define SHN_HIOS 0xff3f / End of OS-specific /
#define SHN_ABS 0xfff1 / Associated symbol is absolute /
#define SHN_COMMON 0xfff2 / Associated symbol is common /
#define SHN_XINDEX 0xffff / Index is in extra table. /
#define SHN_HIRESERVE 0xffff / End of reserved indices */
program_header_table
是一个结构体数组
1 |
|
段类型
p_type,四字节
宏名称 | 值 | 含义 |
---|---|---|
PT_NULL | 0 | 该数组元素未使用。除p_type外,其他结构体成员的值都是未定义的。这种类型可以使程序头表(Program Header Table)忽略此条目。 |
PT_LOAD | 1 | 此类型段为一个可加载的段,大小由p_filesz和p_memsz描述。文件中该段的内容被映射到相应内存段的开始处。如果p_memsz大于p_filesz,“剩余”的字节都要被置为0并跟踪段的初始化区域。p_filesz不能大于p_memsz。可加载的段在程序头表中按照p_vaddr升序排列。 |
PT_DYNAMIC | 2 | 此类型段给出动态链接信息,具体参见ELF手册Book III。 |
PT_INTERP | 3 | 此类型段给出了一个以Null结尾的字符串的位置和长度,该字符串将被当作解释器的路径名进行调用。这种段类型仅对可执行文件有意义(也可能出现在共享目标文件中)。此外,这种段在一个文件中最多出现一次。而且该段类型的数组元素存在的话,它必须在所有可加载段条目的前面。 |
PT_NOTE | 4 | 此类型段给出附加信息的位置和大小。 |
PT_SHLIB | 5 | 该段类型被保留,不过语义未指定。而且,包含这种类型数组元素的程序不符合Unix System V的ELF标准,具体参见ELF手册Book III。 |
PT_PHDR | 6 | 该段类型的数组元素如果存在的话,则给出了程序头表自身在文件和程序内存映像中的的位置和大小。此类型的段在文件中最多出现一次。此外,只有程序头表是程序内存映像的一部分时,该段类型的数组元素才会存在。如果该段类型的数组元素存在,则必须在所有可加载段条目的前面。 |
PT_TLS | 7 | 该段类型的数组元素给出线程本地存储段(TLS)的信息。 |
PT_LOOS | 0x60000000 | 特定操作系统段类型值的下限。 |
PT_GNU_EH_FRAME | 0x6474E550 | 该段类型数组元素指定异常处理信息的位置和大小(由.eh_frame_hdr节定义)[^5]。 |
PT_GNU_STACK | 0x6474E551 | 该段类型数组元素中的p_flags成员指定包含栈的段的权限,并用于指示栈是否应该是可执行的。没有此段类型的数组元素,则表示该栈将是可执行的[^5]。 |
PT_GNU_RELRO | 0x6474E552 | 该段类型数组元素指定了一个在重定位后可以被置为只读的段的位置和大小[^5]。 |
PT_GNU_PROPERTY | 0x6474E553 | 该段类型数组元素指定.note.gnu.property节的位置和大小。 |
PT_HIOS | 0x6FFFFFFF | 特定操作系统段类型值的上限。 |
PT_LOPROC | 0x70000000 | 特定处理器段类型值的下限。 |
PT_HIPROC | 0x7FFFFFFF | 特定处理器段类型值的上限。 |
段权限
p_flags,四字节
宏名称 | 值 | 含义 |
---|---|---|
PF_X | 0x1 | 段具有可执行权限 |
PF_W | 0x2 | 段具有写权限 |
PF_R | 0x4 | 段具有读权限 |
PF_MASKOS | 0x0FF00000 | 为特定操作系统预留 |
PF_MASKPROC | 0xF0000000 | 为特定处理器预留 |
如果是0,表明拒绝访问
以上权限可组合,且一般情况,段没有写权限
宏名称 | 值 | 准确的权限 | 允许的权限 |
---|---|---|---|
none | 0 | 拒绝所有访问 | 拒绝所有访问 |
PF_X | 1 | 只执行 | 读,执行 |
PF_W | 2 | 只写 | 读,写,执行 |
PF_W + PF_X | 3 | 写,执行 | 读,写,执行 |
PF_R | 4 | 只读 | 读,执行 |
PF_R + PF_X | 5 | 读,执行 | 读,执行 |
PF_R + PF_W | 6 | 读,写 | 读,写,执行 |
PF_R + PF_W + PF_X | 7 | 读,写,执行 | 读,写,执行 |
段内容
Segment Contents标明了一个段的多种节的类型
段偏移
在文件中的偏移,8字节
段虚拟地址
表示加载后的相对偏移。真实地址=虚拟地址基址+段虚拟地址,8字节
段物理地址
不知道作用,可能是在虚拟内存中的地址。8字节
段文件长度
段在文件中的长度,8字节
段虚拟长度
段在内存中的长度,8字节
段对齐方式
8字节,一般为4096
参考:
ELF 标识 - 链接程序和库指南 (oracle.com)