ELF

ELF文件全名为Executable and Linking Format files,即可执行与可链接格式文件,用于可执行文件、目标代码、共享库、可重定位文件、核心转储等类型的文件。

下面以64位ELF文件为例。

ELF文件结构

ELF文件由ELF头部、程序头部表或节头部表(或两者)组成。ELF 头部位于文件的零偏移处,程序头部表和节头部表在文件中的偏移量在 ELF 头部中定义。这两个表格描述了文件其余部分的特性。在linux/C环境下,

1
#include <elf.h>

头文件可以将ELF的文件结构获取为结构体的形式,这个结构体是4字节对齐的。ELF的基本数据类型如下(u代表unsigned):

Elf64_Addr 无符号程序地址,uint64_t

Elf64_Off 无符号文件偏移,uint64_t

Elf64_Section 无符号节索引,uint16_t

Elf64_Versym 无符号版本符号信息,uint16_t

Elf_Byte unsigned char

Elf64_Half uint16_t

Elf64_Sword int32_t

Elf64_Word uint32_t

Elf64_Sxword int64_t

Elf64_Xword uint64_t

ELF头部

ELF头部的结构体定义如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#define EI_NIDENT 16

typedef struct {
unsigned char e_ident[EI_NIDENT];
uint16_t e_type;
uint16_t e_machine;
uint32_t e_version;
Elf64_Addr e_entry;
Elf64_Off e_phoff;
Elf64_Off e_shoff;
uint32_t e_flags;
uint16_t e_ehsize;
uint16_t e_phentsize;
uint16_t e_phnum;
uint16_t e_shentsize;
uint16_t e_shnum;
uint16_t e_shstrndx;
} Elf64_Ehdr;

e_ident

e_ident记录了一些基础信息。

EI_MAG0:魔数第 0 字节,必须为 ELFMAG0 (0x7f)

EI_MAG1:第 1 字节,’E’

EI_MAG2:第 2 字节,’L’

EI_MAG3:第 3 字节,’F’

EI_CLASS:第 5 字节,标识架构:

ELFCLASSNONE:无效

ELFCLASS32:32 位架构

ELFCLASS64:64 位架构

EI_DATA:第 6 字节,数据编码:

ELFDATANONE:未知

ELFDATA2LSB:小端

ELFDATA2MSB:大端

EI_VERSION:第 7 字节,ELF 规范版本:

EV_NONE:无效

EV_CURRENT:当前版本

EI_OSABI:第 8 字节,目标操作系统/ABI:

ELFOSABI_NONE / ELFOSABI_SYSV

ELFOSABI_HPUX、ELFOSABI_NETBSD、ELFOSABI_LINUX、ELFOSABI_SOLARIS 等

EI_ABIVERSION:第 9 字节,ABI 版本。ABI全程Application Binary Interface,应用程序二进制接口。

EI_PAD:填充起始,填充的值为0.

EI_NIDENT:e_ident 数组大小

e_type

e_type标注了目标文件的类型,包括

ET_NONE         An unknown type.

ET_REL          A relocatable file.

ET_EXEC         An executable file.

ET_DYN          A shared object.

ET_CORE         A core file.

e_machine

这个字段标注了运行这个文件所需要的架构。

e_version

标注了文件版本

e_entry

系统转交控制权给 ELF 中相应代码的虚拟地址。如果没有相关的入口项,则这一项为0。

e_phoff

程序头部表在文件中的偏移(字节),若无则为0。

e_shoff

节头部表在文件中的偏移(字节),若无则为0。

e_flags

目前未定义,为0。

e_ehsize

ELF头部大小,单位为字节。

e_phentsize

该成员以字节为单位保存文件程序头表中一个条目的大小,所有条目大小相同。

e_phnum

程序头部表中的条目数。若 ≥ PN_XNUM (0xffff),则此处为 PN_XNUM,实际数目在节头部表初始条目的 sh_info 中。

e_shentsize

节头部表中每个条目的大小(字节)。每个条目大小都相等。

e_shnum

节头部表中的条目数。若 ≥ SHN_LORESERVE (0xff00),则此处为 0,实际数目在初始节头部的 sh_size 中。

e_shstrndx

节名字符串表所在节在节头部表中的索引。若 ≥ SHN_LORESERVE,则此处为 SHN_XINDEX (0xffff),实际索引在初始节头部的 sh_link 中。

程序头部(Phdr)

程序头部表是一个结构体数组,每一项描述一个段(segment)。仅对可执行文件和共享对象有意义。

1
2
3
4
5
6
7
8
9
10
typedef struct {
uint32_t p_type;
uint32_t p_flags;
Elf64_Off p_offset;
Elf64_Addr p_vaddr;
Elf64_Addr p_paddr;
uint64_t p_filesz;
uint64_t p_memsz;
uint64_t p_align;
} Elf64_Phdr;

p_type

段类型:

PT_NULL:未使用

PT_LOAD:可加载段

PT_DYNAMIC:动态链接信息

PT_INTERP:解释器路径

PT_NOTE:注释信息

PT_SHLIB:保留

PT_PHDR:程序头部自身

PT_LOPROC / PT_HIPROC:处理器专用

PT_GNU_STACK:GNU 扩展,控制栈状态

p_offset

段在文件中的偏移

p_vaddr

段在内存中的虚拟地址

p_paddr

物理地址(通常未使用),该字段仅用于物理地址寻址相关的系统中。

p_filesz

段在文件中的大小

p_memsz

段在内存中的大小

p_flags

段标志:

PF_X:可执行

PF_W:可写

PF_R:可读

p_align

段对齐方式。该成员保存了段在内存和文件中对齐所依据的值。可加载的进程段必须使 p_vaddr 和 p_offset 在模页大小下同余。值为 0 或 1 表示不需要对齐。否则,p_align 应为 2 的正整数次幂,且 p_vaddr 应等于 p_offset 模 p_align。

节头部 Shdr

节头部的结构体定义如下。

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct {
uint32_t sh_name;
uint32_t sh_type;
uint64_t sh_flags;
Elf64_Addr sh_addr;
Elf64_Off sh_offset;
uint64_t sh_size;
uint32_t sh_link;
uint32_t sh_info;
uint64_t sh_addralign;
uint64_t sh_entsize;
} Elf64_Shdr;

sh_name

节名在节头部字符串表中的索引

sh_type

节类型:

SHT_NULL:无效

SHT_PROGBITS:程序定义的数据

SHT_SYMTAB:符号表

SHT_STRTAB:字符串表

SHT_RELA:带加数的重定位表

SHT_HASH:哈希表

SHT_DYNAMIC:动态链接信息

SHT_NOTE:注释

SHT_NOBITS:无文件内容(如 .bss)

SHT_REL:无加数的重定位

SHT_DYNSYM:动态符号表

SHT_LOPROC / SHT_HIPROC:处理器专用

sh_flags

节标志:

SHF_WRITE:可写

SHF_ALLOC:运行时占用内存

SHF_EXECINSTR:包含可执行指令

SHF_MASKPROC:处理器专用

sh_addr

如果该段出现在进程的内存映像中,则该成员保存段第一个字节应处的地址。否则为 0

sh_offset

保存了从文件开头到段第一个字节的偏移字节数

sh_size

节大小,以字节为单位保存

sh_link、sh_info

链接信息,含义依赖节类型

sh_addralign

地址对齐要求

sh_entsize

表中条目大小(如符号表)

常见节类型

.bss:未初始化数据(SHT_NOBITS)

.comment:版本控制信息

.ctors / .dtors:C++ 构造/析构函数指针

.data / .data1:已初始化数据

.debug、.line:调试信息

.dynamic:动态链接信息

.dynstr:动态链接字符串表

.dynsym:动态符号表

.fini:进程终止代码

.init:进程初始化代码

.got:全局偏移表

.hash:符号哈希表

.interp:解释器路径

.note、.note.ABI-tag、.note.gnu.build-id、.note.GNU-stack 等

.plt:过程链接表

.rel* / .rela*:重定位信息

.rodata / .rodata1:只读数据

.shstrtab:节名字符串表

.strtab:字符串表

.symtab:符号表

.text:可执行指令

字符串表与符号表

字符串表节存放以空字符结尾的字符序列,通常称为字符串。符号表保存了定位和重定位程序符号定义及符号引用所需的信息。

1
2
3
4
5
6
7
8
typedef struct {
uint32_t st_name;
unsigned char st_info;
unsigned char st_other;
uint16_t st_shndx;
Elf64_Addr st_value;
uint64_t st_size;
} Elf64_Sym;

字段含义如下:

st_name:名称索引

st_value:符号值

st_size:大小

st_info:类型和绑定(STT_*、STB_*)

    类型:STT_NOTYPE、STT_OBJECT、STT_FUNC、STT_SECTION、STT_FILE

    绑定:STB_LOCAL、STB_GLOBAL、STB_WEAK

st_other:可见性(STV_DEFAULT、STV_INTERNAL、STV_HIDDEN、STV_PROTECTED)

st_shndx:所在节索引

重定位条目

重定位是将符号引用与符号定义连接起来的过程。 可重定位文件必须包含描述如何修改其节内容的信息,从而使得可执行文件和共享目标文件能够为进程的程序映像保存正确的信息。重定位条目就是这些数据。

1
2
3
4
5
6
7
8
9
10
typedef struct {
Elf64_Addr r_offset;
uint64_t r_info;
} Elf64_Rel;

typedef struct {
Elf64_Addr r_offset;
uint64_t r_info;
int64_t r_addend;
} Elf64_Rela;

参数含义:

r_offset:重定位位置

r_info:符号表索引 + 重定位类型

r_addend:加数(仅 Rela)

动态标签

注释


ELF
http://example.com/2026/05/20/elf/
作者
myslqyr
发布于
2026年5月20日
许可协议