debug/elf - ELF 文件格式
概述
debug/elf 包提供了 ELF(Executable and Linkable Format)文件的读取支持。
ELF 是什么:
- 📋 可执行文件格式:Unix/Linux 系统的标准格式
- 🔧 多种文件类型:可执行文件、目标文件、共享库、核心转储
- 📦 包含多个段:代码段、数据段、符号表、重定位信息等
- 🛠️ 跨平台标准:广泛用于 Linux、BSD、Solaris 等系统
主要用途:
- 🔍 分析可执行文件:读取段、符号、重定位信息
- 🛠️ 链接器开发:处理目标文件和符号解析
- 📊 二进制分析:提取程序结构信息
- 🔐 安全工具:检查二进制文件完整性
- 🐛 调试工具:配合 DWARF 调试信息
重要说明:
- ⚠️ 只读访问:仅用于读取 ELF 文件
- ⚠️ 底层格式:需要了解 ELF 规范
- ✅ 标准库支持:Go 标准库提供完整支持
ELF 文件结构
ELF 文件布局
+------------------+
| ELF Header | <- 文件头(固定大小)
+------------------+
| Program Header | <- 程序头表(可选,用于执行)
| Table |
+------------------+
| Section 1 | <- 各个段
| Section 2 |
| ... |
+------------------+
| Section Header | <- 段头表(用于链接)
| Table |
+------------------+
核心类型
1. File - ELF 文件
type File struct {
FileHeader
Sections []*Section
Progs []*Prog
// 包含过滤或未导出的字段
}
功能:表示打开的 ELF 文件。
字段说明:
FileHeader:ELF 文件头Sections:段列表Progs:程序头列表
主要方法:
// 打开文件
func Open(name string) (*File, error)
func NewFile(r io.ReaderAt) (*File, error)
// 关闭文件
func (f *File) Close() error
// 获取段
func (f *File) Section(name string) *Section
// 获取 DWARF 数据
func (f *File) DWARF() (*dwarf.Data, error)
// 获取符号
func (f *File) Symbols() (symbols []Symbol, err error)
func (f *File) DynamicSymbols() (symbols []Symbol, err error)
// 获取重定位
func (f *File) Relocations() (relocs []Reloc, err error)
// 获取库依赖
func (f *File) ImportedLibraries() ([]string, error)
func (f *File) ImportedSymbols() ([]string, error)
// 获取 TLS 信息
func (f *File) TLS() *TLSHeader
2. FileHeader - 文件头
type FileHeader struct {
Class Class
Data Data
Version Version
OSABI OSABI
ABIVersion int
Arch Machine
Type Type
Entry uint64 // 入口点地址
Flags uint32
}
字段说明:
Class:文件类别(32 位/64 位)Data:字节序(小端/大端)Version:ELF 版本OSABI:操作系统 ABIArch:目标架构Type:文件类型Entry:程序入口点地址Flags:处理器特定标志
3. Section - ELF 段
type Section struct {
SectionHeader
io.ReaderAt
}
功能:表示 ELF 文件中的一个段。
字段说明:
SectionHeader:段头信息io.ReaderAt:用于读取段内容
主要方法:
// 读取段数据
func (s *Section) Data() ([]byte, error)
// 读取字符串表
func (s *Section) Strings() ([]string, error)
4. SectionHeader - 段头
type SectionHeader struct {
Name string // 段名
Type SectionType // 段类型
Flags SectionFlag // 段标志
Addr uint64 // 内存地址
Offset uint64 // 文件偏移
Size uint64 // 段大小
Link uint32 // 链接信息
Info uint32 // 附加信息
Addralign uint64 // 对齐要求
Entsize uint64 // 条目大小
}
字段说明:
Name:段名称(如 .text、.data)Type:段类型(代码、数据、符号表等)Flags:段标志(可读、可写、可执行)Addr:加载到内存时的地址Offset:在文件中的偏移Size:段大小Link/Info:链接和附加信息Addralign:对齐要求Entsize:固定大小条目的大小
5. Prog - 程序头
type Prog struct {
ProgHeader
io.ReaderAt
}
功能:表示 ELF 文件中的一个程序头(用于执行)。
字段说明:
ProgHeader:程序头信息io.ReaderAt:用于读取内容
主要方法:
// 读取程序头内容
func (p *Prog) Data() ([]byte, error)
6. ProgHeader - 程序头
type ProgHeader struct {
Type ProgType
Flags ProgFlag
Offset uint64 // 文件偏移
Vaddr uint64 // 虚拟地址
Paddr uint64 // 物理地址
Filesz uint64 // 文件大小
Memsz uint64 // 内存大小
Align uint64 // 对齐要求
}
7. Symbol - 符号
type Symbol struct {
Name string
Info byte
Other byte
Section int
Value uint64
Size uint64
}
字段说明:
Name:符号名称Info:符号信息(绑定和类型)Section:所在段索引Value:符号值(地址)Size:符号大小
辅助函数:
// 获取符号绑定
func STB(info byte) SymBind
// 获取符号类型
func STT(info byte) SymType
// 创建符号信息
func STP(bind SymBind, typ SymType) byte
8. Reloc - 重定位
type Reloc struct {
Off uint64 // 偏移
Sym uint64 // 符号索引
Type int // 重定位类型
Addend int64 // 加数
}
常量定义
Class - 文件类别
const (
ELFCLASSNONE Class = iota
ELFCLASS32 // 32 位
ELFCLASS64 // 64 位
)
Data - 字节序
const (
ELFDATANONE Data = iota
ELFDATA2LSB // 小端序
ELFDATA2MSB // 大端序
ELFDATA2LSBOS // 小端序(OS 特定)
)
Type - 文件类型
const (
ET_NONE Type = iota // 无类型
ET_REL // 可重定位文件
ET_EXEC // 可执行文件
ET_DYN // 共享目标文件
ET_CORE // 核心文件
)
Machine - 目标架构
const (
EM_NONE Machine = 0
EM_SPARC Machine = 2
EM_386 Machine = 3 // x86
EM_68K Machine = 4
EM_88K Machine = 5
EM_486 Machine = 6
EM_860 Machine = 7
EM_MIPS Machine = 8
EM_S370 Machine = 9
EM_MIPS_RS3_LE Machine = 10
EM_PPC Machine = 20 // PowerPC
EM_PPC64 Machine = 21 // PowerPC 64 位
EM_S390 Machine = 22 // IBM S/390
EM_ARM Machine = 40 // ARM
EM_SH Machine = 42 // SuperH
EM_SPARCV9 Machine = 43 // SPARC V9
EM_IA_64 Machine = 50 // Intel Itanium
EM_X86_64 Machine = 62 // x86-64
EM_AARCH64 Machine = 183 // ARM 64 位
EM_RISCV Machine = 243 // RISC-V
)
SectionType - 段类型
const (
SHT_NULL SectionType = iota
SHT_PROGBITS // 程序信息
SHT_SYMTAB // 符号表
SHT_STRTAB // 字符串表
SHT_RELA // 重定位(带加数)
SHT_HASH // 哈希表
SHT_DYNAMIC // 动态链接信息
SHT_NOTE // 注释信息
SHT_NOBITS // 未初始化数据
SHT_REL // 重定位
SHT_SHLIB // 保留
SHT_DYNSYM // 动态符号表
SHT_INIT_ARRAY // 初始化函数数组
SHT_FINI_ARRAY // 终止函数数组
SHT_PREINIT_ARRAY // 预初始化函数数组
SHT_GROUP // 段组
SHT_SYMTAB_SHNDX // 符号表段索引
)
特殊段类型
const (
SHT_LOOS SectionType = 0x60000000
SHT_HIOS SectionType = 0x6fffffff
SHT_LOPROC SectionType = 0x70000000
SHT_HIPROC SectionType = 0x7fffffff
SHT_LOUSER SectionType = 0x80000000
SHT_HIUSER SectionType = 0xffffffff
)
SectionFlag - 段标志
const (
SHF_WRITE SectionFlag = 0x1 // 可写
SHF_ALLOC SectionFlag = 0x2 // 占用内存
SHF_EXECINSTR SectionFlag = 0x4 // 可执行
SHF_MERGE SectionFlag = 0x10 // 可合并
SHF_STRINGS SectionFlag = 0x20 // 字符串表
SHF_INFO_LINK SectionFlag = 0x40 // 链接信息
SHF_LINK_ORDER SectionFlag = 0x80 // 链接顺序
SHF_OS_NONCONFORMING SectionFlag = 0x100 // OS 特定
SHF_GROUP SectionFlag = 0x200 // 段组成员
SHF_TLS SectionFlag = 0x400 // 线程局部存储
)
ProgType - 程序头类型
const (
PT_NULL ProgType = iota
PT_LOAD // 可加载段
PT_DYNAMIC // 动态链接信息
PT_INTERP // 解释器路径
PT_NOTE // 注释信息
PT_SHLIB // 保留
PT_PHDR // 程序头表
PT_TLS // 线程局部存储
PT_GNU_EH_FRAME // GCC 异常处理
PT_GNU_STACK // 栈标志
PT_GNU_RELRO // 只写时重定位
)
ProgFlag - 程序头标志
const (
PF_X ProgFlag = 0x1 // 可执行
PF_W ProgFlag = 0x2 // 可写
PF_R ProgFlag = 0x4 // 可读
PF_MASKOS ProgFlag = 0x0ff00000
PF_MASKPROC ProgFlag = 0xf0000000
)
SymBind - 符号绑定
const (
STB_LOCAL SymBind = iota // 局部符号
STB_GLOBAL // 全局符号
STB_WEAK // 弱符号
STB_LOOS SymBind = 10 // OS 特定
STB_HIOS SymBind = 12
STB_LOPROC SymBind = 13 // 处理器特定
STB_HIPROC SymBind = 15
)
SymType - 符号类型
const (
STT_NOTYPE SymType = iota // 无类型
STT_OBJECT // 数据对象
STT_FUNC // 函数
STT_SECTION // 段
STT_FILE // 文件
STT_COMMON // 公共符号
STT_TLS // 线程局部存储
STT_LOOS SymType = 10 // OS 特定
STT_HIOS SymType = 12
STT_LOPROC SymType = 13 // 处理器特定
STT_HIPROC SymType = 15
)
常见段名称
// 代码段
.text // 可执行代码
.init // 初始化代码
.fini // 终止代码
// 数据段
.data // 已初始化数据
.bss // 未初始化数据
.rodata // 只读数据
// 符号表
.symtab // 符号表
.strtab // 字符串表
.dynsym // 动态符号表
.dynstr // 动态字符串表
// 重定位
.rela.text // 代码段重定位
.rela.data // 数据段重定位
// 调试信息
.debug_info // DWARF 调试信息
.debug_line // DWARF 行号信息
.debug_abbrev // DWARF 缩写表
// 动态链接
.dynamic // 动态链接信息
.dynstr // 动态字符串
.hash // 符号哈希表
.got // 全局偏移表
.plt // 过程链接表
// 其他
.note // 注释信息
.shstrtab // 段名字符串表
.interp // 解释器路径
完整示例
示例 1:打开和读取 ELF 文件
package main
import (
"debug/elf"
"fmt"
"log"
)
func main() {
// 1. 打开 ELF 文件
f, err := elf.Open("myprogram")
if err != nil {
log.Fatal(err)
}
defer f.Close()
// 2. 显示文件头信息
fmt.Printf("文件类型:%v\n", f.Type)
fmt.Printf("目标架构:%v\n", f.Machine)
fmt.Printf("ELF 类别:%v\n", f.Class)
fmt.Printf("字节序:%v\n", f.Data)
fmt.Printf("入口点:0x%x\n", f.Entry)
// 3. 显示段数量
fmt.Printf("段数量:%d\n", len(f.Sections))
// 4. 显示程序头数量
fmt.Printf("程序头数量:%d\n", len(f.Progs))
}
示例 2:遍历所有段
package main
import (
"debug/elf"
"fmt"
"log"
)
func main() {
f, err := elf.Open("myprogram")
if err != nil {
log.Fatal(err)
}
defer f.Close()
fmt.Println("ELF 段信息:")
fmt.Println("=" * 80)
for i, section := range f.Sections {
fmt.Printf("%2d. 名称:%s\n", i, section.Name)
fmt.Printf(" 类型:%v\n", section.Type)
fmt.Printf(" 标志:%v\n", section.Flags)
fmt.Printf(" 地址:0x%x\n", section.Addr)
fmt.Printf(" 偏移:0x%x\n", section.Offset)
fmt.Printf(" 大小:%d 字节\n", section.Size)
fmt.Printf(" 对齐:%d\n", section.Addralign)
if section.Entsize > 0 {
fmt.Printf(" 条目大小:%d\n", section.Entsize)
}
fmt.Println()
}
}
示例 3:读取特定段内容
package main
import (
"debug/elf"
"encoding/hex"
"fmt"
"log"
)
func main() {
f, err := elf.Open("myprogram")
if err != nil {
log.Fatal(err)
}
defer f.Close()
// 1. 读取 .text 段(代码段)
textSection := f.Section(".text")
if textSection != nil {
data, err := textSection.Data()
if err != nil {
log.Fatal(err)
}
fmt.Printf(".text 段:\n")
fmt.Printf(" 大小:%d 字节\n", len(data))
fmt.Printf(" 前 64 字节:%x\n", data[:64])
}
// 2. 读取 .rodata 段(只读数据)
rodataSection := f.Section(".rodata")
if rodataSection != nil {
data, err := rodataSection.Data()
if err != nil {
log.Fatal(err)
}
fmt.Printf("\n.rodata 段:\n")
fmt.Printf(" 大小:%d 字节\n", len(data))
// 尝试作为字符串读取
if len(data) > 0 {
fmt.Printf(" 内容预览:%s\n", data[:min(100, len(data))])
}
}
// 3. 读取 .data 段(已初始化数据)
dataSection := f.Section(".data")
if dataSection != nil {
data, err := dataSection.Data()
if err != nil {
log.Fatal(err)
}
fmt.Printf("\n.data 段:\n")
fmt.Printf(" 大小:%d 字节\n", len(data))
}
// 4. 读取字符串表
strtabSection := f.Section(".strtab")
if strtabSection != nil {
strings, err := strtabSection.Strings()
if err != nil {
log.Fatal(err)
}
fmt.Printf("\n字符串表:\n")
fmt.Printf(" 字符串数量:%d\n", len(strings))
// 显示前 20 个字符串
count := 0
for _, str := range strings {
if count >= 20 {
break
}
if str != "" {
fmt.Printf(" %s\n", str)
count++
}
}
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
示例 4:读取符号表
package main
import (
"debug/elf"
"fmt"
"log"
)
func main() {
f, err := elf.Open("myprogram")
if err != nil {
log.Fatal(err)
}
defer f.Close()
// 1. 读取静态符号表
fmt.Println("静态符号表:")
symbols, err := f.Symbols()
if err != nil {
log.Printf("读取符号表失败:%v", err)
} else {
fmt.Printf("符号数量:%d\n", len(symbols))
// 显示前 20 个符号
count := 0
for _, sym := range symbols {
if count >= 20 {
break
}
// 跳过空符号
if sym.Name == "" {
continue
}
fmt.Printf(" %s\n", sym.Name)
fmt.Printf(" 值:0x%x\n", sym.Value)
fmt.Printf(" 大小:%d\n", sym.Size)
fmt.Printf(" 绑定:%v\n", elf.STB(sym.Info))
fmt.Printf(" 类型:%v\n", elf.STT(sym.Info))
fmt.Printf(" 段:%d\n", sym.Section)
count++
}
}
// 2. 读取动态符号表
fmt.Println("\n动态符号表:")
dynSymbols, err := f.DynamicSymbols()
if err != nil {
log.Printf("读取动态符号表失败:%v", err)
} else {
fmt.Printf("符号数量:%d\n", len(dynSymbols))
// 显示函数符号
fmt.Println("\n函数符号:")
for _, sym := range dynSymbols {
if elf.STT(sym.Info) == elf.STT_FUNC {
fmt.Printf(" %s (0x%x)\n", sym.Name, sym.Value)
}
}
}
}
示例 5:读取重定位信息
package main
import (
"debug/elf"
"fmt"
"log"
)
func main() {
f, err := elf.Open("myprogram")
if err != nil {
log.Fatal(err)
}
defer f.Close()
// 读取重定位
relocs, err := f.Relocations()
if err != nil {
log.Printf("读取重定位失败:%v", err)
return
}
fmt.Printf("重定位数量:%d\n", len(relocs))
for i, reloc := range relocs {
if i >= 20 {
break
}
fmt.Printf("重定位 %d:\n", i+1)
fmt.Printf(" 偏移:0x%x\n", reloc.Off)
fmt.Printf(" 符号索引:%d\n", reloc.Sym)
fmt.Printf(" 类型:%d\n", reloc.Type)
fmt.Printf(" 加数:%d\n", reloc.Addend)
}
}
示例 6:读取导入的库和符号
package main
import (
"debug/elf"
"fmt"
"log"
)
func main() {
f, err := elf.Open("myprogram")
if err != nil {
log.Fatal(err)
}
defer f.Close()
// 1. 读取导入的库
fmt.Println("依赖的共享库:")
libs, err := f.ImportedLibraries()
if err != nil {
log.Printf("读取库列表失败:%v", err)
} else {
for _, lib := range libs {
fmt.Printf(" %s\n", lib)
}
}
// 2. 读取导入的符号
fmt.Println("\n导入的符号:")
symbols, err := f.ImportedSymbols()
if err != nil {
log.Printf("读取导入符号失败:%v", err)
} else {
for _, sym := range symbols {
fmt.Printf(" %s\n", sym)
}
}
// 3. 读取 .interp 段(解释器路径)
interpSection := f.Section(".interp")
if interpSection != nil {
data, err := interpSection.Data()
if err == nil {
fmt.Printf("\n解释器:%s\n", string(data[:len(data)-1]))
}
}
}
示例 7:分析程序头
package main
import (
"debug/elf"
"fmt"
"log"
)
func main() {
f, err := elf.Open("myprogram")
if err != nil {
log.Fatal(err)
}
defer f.Close()
fmt.Println("程序头信息:")
fmt.Println("=" * 80)
for i, prog := range f.Progs {
fmt.Printf("%2d. 类型:%v\n", i, prog.Type)
fmt.Printf(" 标志:%v\n", prog.Flags)
fmt.Printf(" 偏移:0x%x\n", prog.Offset)
fmt.Printf(" 虚拟地址:0x%x\n", prog.Vaddr)
fmt.Printf(" 物理地址:0x%x\n", prog.Paddr)
fmt.Printf(" 文件大小:%d 字节\n", prog.Filesz)
fmt.Printf(" 内存大小:%d 字节\n", prog.Memsz)
fmt.Printf(" 对齐:%d\n", prog.Align)
// 读取程序头内容(如果存在)
if prog.Filesz > 0 {
data, err := prog.Data()
if err == nil && len(data) > 0 {
fmt.Printf(" 内容预览:%x\n", data[:min(32, len(data))])
}
}
fmt.Println()
}
}
func min(a, b int) int {
if a < b {
return a
}
return b
}
示例 8:ELF 文件分析工具
package main
import (
"debug/elf"
"encoding/binary"
"fmt"
"io"
"log"
"os"
"strings"
)
// ELFAnalyzer ELF 文件分析器
type ELFAnalyzer struct {
file *elf.File
}
// NewELFAnalyzer 创建分析器
func NewELFAnalyzer(filename string) (*ELFAnalyzer, error) {
f, err := elf.Open(filename)
if err != nil {
return nil, err
}
return &ELFAnalyzer{file: f}, nil
}
// Close 关闭文件
func (a *ELFAnalyzer) Close() error {
return a.file.Close()
}
// ShowHeader 显示文件头
func (a *ELFAnalyzer) ShowHeader() {
f := a.file
fmt.Println("=== ELF 文件头 ===")
fmt.Printf("文件类型:%v\n", f.Type)
fmt.Printf("目标架构:%v\n", f.Machine)
fmt.Printf("ELF 版本:%v\n", f.Version)
fmt.Printf("OS/ABI:%v\n", f.OSABI)
fmt.Printf("ABI 版本:%d\n", f.ABIVersion)
fmt.Printf("类别:%v\n", f.Class)
fmt.Printf("字节序:%v\n", f.Data)
fmt.Printf("入口点:0x%x\n", f.Entry)
fmt.Printf("标志:0x%x\n", f.Flags)
fmt.Println()
}
// ShowSections 显示段信息
func (a *ELFAnalyzer) ShowSections() {
fmt.Println("=== ELF 段 ===")
for i, section := range a.file.Sections {
fmt.Printf("%2d. %-15s 类型:%v 大小:%6d 字节",
i, section.Name, section.Type, section.Size)
if section.Flags != 0 {
fmt.Printf(" 标志:%v", section.Flags)
}
if section.Addr != 0 {
fmt.Printf(" 地址:0x%x", section.Addr)
}
fmt.Println()
}
fmt.Println()
}
// ShowSymbols 显示符号
func (a *ELFAnalyzer) ShowSymbols() {
fmt.Println("=== 符号表 ===")
symbols, err := a.file.Symbols()
if err != nil {
fmt.Printf("读取符号失败:%v\n", err)
return
}
fmt.Printf("符号数量:%d\n\n", len(symbols))
// 按类型分组
funcs := make([]elf.Symbol, 0)
objects := make([]elf.Symbol, 0)
others := make([]elf.Symbol, 0)
for _, sym := range symbols {
if sym.Name == "" {
continue
}
switch elf.STT(sym.Info) {
case elf.STT_FUNC:
funcs = append(funcs, sym)
case elf.STT_OBJECT:
objects = append(objects, sym)
default:
others = append(others, sym)
}
}
fmt.Printf("函数:%d 个\n", len(funcs))
fmt.Printf("数据对象:%d 个\n", len(objects))
fmt.Printf("其他:%d 个\n\n", len(others))
// 显示前 10 个函数
if len(funcs) > 0 {
fmt.Println("函数示例:")
for i, sym := range funcs {
if i >= 10 {
break
}
fmt.Printf(" %-40s 0x%x (%d 字节)\n",
sym.Name, sym.Value, sym.Size)
}
fmt.Println()
}
}
// ShowLibraries 显示库依赖
func (a *ELFAnalyzer) ShowLibraries() {
fmt.Println("=== 依赖库 ===")
libs, err := a.file.ImportedLibraries()
if err != nil {
fmt.Printf("读取库失败:%v\n", err)
return
}
for _, lib := range libs {
fmt.Printf(" %s\n", lib)
}
fmt.Println()
}
// ShowProgramHeaders 显示程序头
func (a *ELFAnalyzer) ShowProgramHeaders() {
fmt.Println("=== 程序头 ===")
for i, prog := range a.file.Progs {
fmt.Printf("%2d. 类型:%-15s 标志:%v 大小:%6d 字节\n",
i, prog.Type, prog.Flags, prog.Memsz)
}
fmt.Println()
}
// FindSymbol 查找符号
func (a *ELFAnalyzer) FindSymbol(name string) error {
symbols, err := a.file.Symbols()
if err != nil {
return err
}
for _, sym := range symbols {
if strings.Contains(sym.Name, name) {
fmt.Printf("找到符号:%s\n", sym.Name)
fmt.Printf(" 值:0x%x\n", sym.Value)
fmt.Printf(" 大小:%d\n", sym.Size)
fmt.Printf(" 绑定:%v\n", elf.STB(sym.Info))
fmt.Printf(" 类型:%v\n", elf.STT(sym.Info))
fmt.Printf(" 段:%d\n", sym.Section)
fmt.Println()
}
}
return nil
}
// DumpSection 转储段内容
func (a *ELFAnalyzer) DumpSection(name string, output string) error {
section := a.file.Section(name)
if section == nil {
return fmt.Errorf("未找到段:%s", name)
}
data, err := section.Data()
if err != nil {
return err
}
file, err := os.Create(output)
if err != nil {
return err
}
defer file.Close()
_, err = file.Write(data)
if err != nil {
return err
}
fmt.Printf("已将 %s 段保存到 %s (%d 字节)\n", name, output, len(data))
return nil
}
func main() {
if len(os.Args) < 2 {
log.Fatal("用法:elf-analyzer <elf-file> [command]")
}
filename := os.Args[1]
analyzer, err := NewELFAnalyzer(filename)
if err != nil {
log.Fatal(err)
}
defer analyzer.Close()
if len(os.Args) > 2 {
command := os.Args[2]
switch command {
case "header":
analyzer.ShowHeader()
case "sections":
analyzer.ShowSections()
case "symbols":
analyzer.ShowSymbols()
case "libs":
analyzer.ShowLibraries()
case "progs":
analyzer.ShowProgramHeaders()
case "find":
if len(os.Args) > 3 {
analyzer.FindSymbol(os.Args[3])
}
case "dump":
if len(os.Args) > 4 {
err := analyzer.DumpSection(os.Args[3], os.Args[4])
if err != nil {
log.Fatal(err)
}
}
default:
// 显示所有信息
analyzer.ShowHeader()
analyzer.ShowSections()
analyzer.ShowSymbols()
analyzer.ShowLibraries()
analyzer.ShowProgramHeaders()
}
} else {
// 默认显示所有信息
analyzer.ShowHeader()
analyzer.ShowSections()
analyzer.ShowSymbols()
analyzer.ShowLibraries()
analyzer.ShowProgramHeaders()
}
}
示例 9:读取 DWARF 调试信息
package main
import (
"debug/elf"
"debug/dwarf"
"fmt"
"io"
"log"
)
func main() {
f, err := elf.Open("myprogram")
if err != nil {
log.Fatal(err)
}
defer f.Close()
// 读取 DWARF 数据
data, err := f.DWARF()
if err != nil {
log.Fatal("无 DWARF 信息:", err)
}
// 创建读取器
reader := data.Reader()
// 遍历 DWARF 条目
fmt.Println("DWARF 调试信息:")
for {
entry, err := reader.Next()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
// 查找函数
if entry.Tag == dwarf.TagSubprogram {
name := entry.Val(dwarf.AttrName)
if name != nil {
fmt.Printf("函数:%v\n", name)
}
}
if entry.Children {
reader.SkipChildren()
}
}
}
安全最佳实践
✅ 推荐做法
-
始终检查错误
f, err := elf.Open("file") if err != nil { return err } defer f.Close() -
检查 nil 指针
section := f.Section(".text") if section != nil { data, _ := section.Data() } -
验证数据大小
data, err := section.Data() if err != nil { return err } if len(data) < expectedSize { return fmt.Errorf("数据太小") }
❌ 不安全做法
-
不要忽略错误
// ❌ 错误 f, _ := elf.Open("file") // ✅ 正确 f, err := elf.Open("file") if err != nil { // 处理错误 } -
不要忘记关闭文件
// ❌ 错误 f, _ := elf.Open("file") // ✅ 正确 f, _ := elf.Open("file") defer f.Close()
总结
核心类型
File // ELF 文件
FileHeader // 文件头
Section // ELF 段
SectionHeader // 段头
Prog // 程序头
ProgHeader // 程序头信息
Symbol // 符号
Reloc // 重定位
使用场景
| 场景 | 推荐方法 | 说明 |
|---|---|---|
| 打开文件 | elf.Open() | 读取 ELF 文件 |
| 读取段 | File.Section() | 获取特定段 |
| 读取符号 | File.Symbols() | 获取符号表 |
| 读取重定位 | File.Relocations() | 获取重定位 |
| 获取库依赖 | File.ImportedLibraries() | 获取依赖库 |
| 读取 DWARF | File.DWARF() | 获取调试信息 |
ELF 文件类型
| 类型 | 常量 | 说明 |
|---|---|---|
| 可重定位文件 | ET_REL | .o 文件 |
| 可执行文件 | ET_EXEC | 可执行程序 |
| 共享库 | ET_DYN | .so 文件 |
| 核心转储 | ET_CORE | 核心文件 |
常见段类型
| 段名 | 类型 | 用途 |
|---|---|---|
.text | SHT_PROGBITS | 代码段 |
.data | SHT_PROGBITS | 已初始化数据 |
.bss | SHT_NOBITS | 未初始化数据 |
.symtab | SHT_SYMTAB | 符号表 |
.strtab | SHT_STRTAB | 字符串表 |
.rodata | SHT_PROGBITS | 只读数据 |
参考资料
最后更新:2026-04-03
Go 版本:Go 1.23+