Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:操作系统 ABI
  • Arch:目标架构
  • 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()
        }
    }
}

安全最佳实践

✅ 推荐做法

  1. 始终检查错误

    f, err := elf.Open("file")
    if err != nil {
        return err
    }
    defer f.Close()
    
  2. 检查 nil 指针

    section := f.Section(".text")
    if section != nil {
        data, _ := section.Data()
    }
    
  3. 验证数据大小

    data, err := section.Data()
    if err != nil {
        return err
    }
    
    if len(data) < expectedSize {
        return fmt.Errorf("数据太小")
    }
    

❌ 不安全做法

  1. 不要忽略错误

    // ❌ 错误
    f, _ := elf.Open("file")
    
    // ✅ 正确
    f, err := elf.Open("file")
    if err != nil {
        // 处理错误
    }
    
  2. 不要忘记关闭文件

    // ❌ 错误
    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()获取依赖库
读取 DWARFFile.DWARF()获取调试信息

ELF 文件类型

类型常量说明
可重定位文件ET_REL.o 文件
可执行文件ET_EXEC可执行程序
共享库ET_DYN.so 文件
核心转储ET_CORE核心文件

常见段类型

段名类型用途
.textSHT_PROGBITS代码段
.dataSHT_PROGBITS已初始化数据
.bssSHT_NOBITS未初始化数据
.symtabSHT_SYMTAB符号表
.strtabSHT_STRTAB字符串表
.rodataSHT_PROGBITS只读数据

参考资料


最后更新:2026-04-03
Go 版本:Go 1.23+