ELF文件格式详解
目录
ELF文件格式详解
什么是ELF
ELF(Executable and Linkable Format)是Linux平台的可执行和可链接文件的文件格式标准。ELF文件以文件开头的ELF字符串为标识,这个Magic String用于标记文件类型。
ELF基本结构
ELF文件主要由四部分组成:
- ELF Header:文件头,包含文件的元信息
- ELF Program Header Table(程序头表):执行视角的基本单位
- ELF Sections(节):链接视角的基本单位
- ELF Section Header Table(节头表):描述所有节的表

ELF基本结构
两种视角
ELF文件具有两种不同的视角:
- Linking View(链接视角):从链接的角度考虑,以section为基本单位
- Execution View(执行视角):从运行加载的角度考虑,以segment为基本单位
.o文件是不可执行的,因此没有program header。
常用指令
| |
ELF 详细结构

ELF 详细结构
ELF 头结构
| |
ELF Header是一个Elf_Ehdr结构体,包含以下关键字段:
| 字段 | 含义 | 说明 |
|---|---|---|
| e_ident | Magic魔数标识 | 前4字节为7f 45 4c 46(ELF),后续标识架构、数据格式等 |
| e_type | 文件类型 | ET_REL(可重定位)、ET_EXEC(可执行)、ET_DYN(共享对象)、ET_CORE(核心转储) |
| e_machine | 目标架构 | 如X86-64、AMD GPU等 |
| e_version | 版本号 | 一般为常数1 |
| e_entry | 入口地址 | 程序执行的起始地址 |
| e_phoff | 程序头表偏移 | Program Header Table在文件中的偏移 |
| e_shoff | 节头表偏移 | Section Header Table在文件中的偏移 |
| e_flags | ELF标志位 | 与具体处理器相关 |
| e_ehsize | 文件头大小 | ELF Header本身的大小 |
| e_phentsize | 程序头大小 | Program Header entry 大小 |
| e_phnum | 程序头数量 | Program Header entry的数目 |
| e_shnum | 节头数量 | Section Header entry的数目 |
| e_shstrndx | 节名字符串表索引 | 指向节名字符串表在Section Header Table中的索引 |
文件类型
通过e_type字段区分:
- ET_REL(Relocatable File):可重定位文件,
.o文件、静态库.a - ET_EXEC(Executable File):位置相关的可执行文件
- ET_DYN(Shared Object File):位置无关的可执行文件或共享目标文件,
.so文件 - ET_CORE(Core file):核心转储文件
Section Header解析
Section Header是一个Elf_Shdr结构体,描述每个节的详细信息:
| 字段 | 含义 | 说明 |
|---|---|---|
| sh_name | 节名 | 指向.shstrtab的偏移值 |
| sh_type | 节类型 | 标识节的类型(SHT_NULL、SHT_PROGBITS、SHT_SYMTAB等) |
| sh_addr | 节地址 | 被加载后的虚拟地址,未加载则为0 |
| sh_offset | 节偏移 | 在文件中的偏移位置 |
| sh_size | 节大小 | 节占用的字节数 |
| sh_entsize | 节项大小 | 有些节包含了一些固定大小的项,如符号表,sh_entsize表示每个项的大小。如果为0,则表示该节不包含固定大小的项。 |
| sh_flags | 节标志 | 标识节的属性(SHF_WRITE、SHF_ALLOC、SHF_EXECINSTR等) |
| sh_link、sh_info | 链接信息 | 相关节的索引或附加信息 |
| sh_addralign | 地址对齐 | 节的地址对齐方式 |
节类型(sh_type)
| 常量 | 含义 |
|---|---|
| SHT_NULL | 无效节 |
| SHT_PROGBITS | 程序节(代码、数据等) |
| SHT_SYMTAB | 符号表 |
| SHT_STRTAB | 字符串表 |
| SHT_HASH | 符号表的哈希表 |
| SHT_DYNAMIC | 动态链接信息 |
| SHT_NOTE | 提示性信息 |
| SHT_NOBITS | 文件中无内容(如.bss) |
| SHT_RELA / SHT_REL | 重定位表 |
| SHT_DNYSYM | 动态链接的符号表 |
节标志位(sh_flags)
| 常量 | 含义 |
|---|---|
| SHF_WRITE (W) | 执行期间可被写入 |
| SHF_ALLOC (A) | 执行期间需要分配内存 |
| SHF_EXECINSTR (X) | 包含可执行指令 |
| SHF_MERGE (M) | 可被合并 |
| SHF_STRINGS (S) | 内容为字符串 |
| SHF_GROUP (G) | 属于section group |
| SHF_TLS (T) | 线程本地存储 |
| SHF_COMPRESSED (C) | 含有压缩数据 |
常见Section详解
代码和数据段
| 节名 | 作用 |
|---|---|
.text | 代码节,存放可执行指令 |
.data | 数据节,存放已初始化的数据 |
.rodata | 只读数据节,存放只读数据 |
.bss | 未初始化数据节,不占据文件空间 |
动态链接相关
| 节名 | 作用 |
|---|---|
.dynsym | 动态链接符号表 |
.dynstr | 动态链接字符串表 |
.plt | 过程链接表,用于延迟加载 |
.got | 全局偏移表,保存外部符号地址 |
.got.plt | PLT专用的全局偏移表 |
.dynamic | 动态链接器信息 |
.hash | 符号查找的哈希表 |
重定位表
| 节名 | 作用 |
|---|---|
.rel.a_section | 节的重定位信息 |
程序执行控制
| 节名 | 作用 |
|---|---|
.init | 程序初始化代码(main之前) |
.fini | 程序结束代码(main之后) |
.init_array | 多个初始化动作 |
.fini_array | 多个结束动作 |
.interp | 动态链接器路径 |
符号和字符串表
| 节名 | 作用 |
|---|---|
.symtab | 符号表(包含所有符号) |
.strtab | 字符串表 |
.shstrtab | 节头表字符串表 |
异常处理和调试信息
| 节名 | 作用 |
|---|---|
.eh_frame | C++异常处理和栈回退信息 |
.eh_frame_hdr | 二分查找表,加速eh_frame查找 |
.debug_* | 调试信息节(编译时加-g产生) |
符号表(Symbol Table)
符号表用于记录程序中的符号信息(函数、变量等)。
符号属性
| 属性 | 类型 | 含义 |
|---|---|---|
| Type | NOTYPE | 未指定类型 |
| OBJECT | 数据对象(变量、数组等) | |
| FUNC | 可执行代码(函数) | |
| COMMON | 未初始化的数据块 | |
| Bind | LOCAL | 只能被当前文件可见 |
| GLOBAL | 可被所有文件可见 | |
| Weak | 类似于GLOBAL,但可被覆盖 | |
| Vis | default | 可被其他组件可见 |
| protected | 可见但不可覆盖 | |
| hidden | 不可被其他组件可见 | |
| Ndx | ABS | 绝对地址,不随重定位改变 |
| UND | 未定义符号 |
查看符号表
| |
注意:C++符号名是经过重整的,可以使用c++filt查看原始名称。
Section Name解析示例
解析section name的步骤:
| |
输出示例:
| |
异构ELF(Fat Binary)
异构程序(如HIP/ROCm)将CPU代码和GPU代码打包在同一个ELF文件中,形成fat binary或multiarchitecture binary。
编译流程
- CPU主机代码编译为
.cui→.bc→.out - GPU设备代码按架构编译(gfx906、gfx926等)
- 使用
clang-offload-bundler打包成.hipfb文件 - 通过
clang -fcuda-include-gpubinary将.hipfb插入主机ELF的.hip_fatbin节
异构ELF的特有节
| 节名 | 作用 |
|---|---|
.hip_fatbin | 存储打包的设备端代码 |
.hipFatBinSegment | 设备端代码段 |
hipfb文件结构
| |
每个Bundle Struct包含:
- Code Object Offset:代码对象在文件中的偏移
- Code Object Size:代码对象大小
- Entry ID Length:入口ID长度
- Entry ID:入口标识
设备端代码对象
设备端ELF(如gfx906)的特点:
- Machine:AMD GPU
- OS/ABI:unknown(设备端专用)
- ABI Version:1(HSA_V3)、2(HSA_V4)、3(HSA_V5)
- Entry point:通常为0x0
- Flags:包含GPU架构信息(如
EF_AMDGPU_MACH_AMDGCN_GFX906)
设备端常见的section:
.note:metadata.AMDGPU.csdata:记录kernel信息(FloatMode、IeeeMode等)- 注意:设备端没有
.eh_frame节,不支持异常处理
异构ELF的Symbol解析
异构程序的符号表存在特殊性:
主机端符号表:
| |
设备端符号表:
| |
关键点:
_Z30__device_stub__matrixTransposePfS_i:编译器生成的辅助函数,用于启动设备端函数_Z15matrixTransposePfS_i.kd:kernel descriptor,包含kernel执行必要信息- 主机端的
_Z15matrixTransposePfS_i(OBJECT类型)实际指向设备端的kernel descriptor
Kernel Descriptor
设备端的kernel descriptor(.kd)包含kernel执行所需的所有信息:
| |
解析异构ELF的步骤
- 解析ELF Header,找到Section Header Table位置
- 通过Section Header Table找到
.hip_fatbin的位置和大小 - 读取
clang_offload_bundle_header,校验Magic String - 按顺序读取每个Bundle Struct,解析code object
- 解析code object的metadata
实践工具
查看设备端代码
| |
该指令会根据GPU架构生成ISA文件,包含metadata、kernel descriptor和kernel函数的反汇编。
反汇编操作
1 2 3llvm-objdump -d # 反汇编命令,默认cpu, extractkernel -i # 反汇编命令,dcu llvm-amdgpu-objdump --inputs= # 反汇编命令,amdgpu