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/plan9obj - Plan 9 对象文件格式

概述

debug/plan9obj 包提供了对 Plan 9 对象文件格式的读取支持。

Plan 9 对象文件是什么

  • 📋 Plan 9 标准格式:Plan 9 操作系统的对象文件格式
  • 🔧 简洁设计:相比 ELF/Mach-O 更简单的对象文件格式
  • 📦 Go 编译器使用:Go 编译器早期版本使用的对象文件格式
  • 🛠️ 历史意义:了解 Go 工具链演变的重要参考

主要用途

  • 🔍 分析 Plan 9 对象文件:读取节、符号信息
  • 🛠️ 链接器开发:处理 Plan 9 格式的目标文件
  • 📊 二进制分析:提取程序结构信息
  • 🐛 调试工具:配合调试器使用
  • 📚 学习参考:理解对象文件格式设计

重要说明

  • ⚠️ 只读访问:仅用于读取 Plan 9 对象文件
  • ⚠️ 历史格式:现代 Go 版本已使用其他格式
  • ⚠️ 特定平台:主要用于 Plan 9 系统
  • 标准库支持:Go 标准库保留支持

与其他格式的比较

  • ELF:Unix/Linux 标准,功能完整但复杂
  • Mach-O:macOS/iOS 标准,结构清晰
  • PE:Windows 标准,兼容性好
  • Plan 9:简洁设计,教学价值高

Plan 9 对象文件结构

文件布局

+------------------+
| File Header      |  <- 文件头(固定大小)
+------------------+
| Section Headers  |  <- 节头表
+------------------+
| Section Data     |  <- 各个节的数据
+------------------+
| Symbol Table     |  <- 符号表
+------------------+
| String Table     |  <- 字符串表
+------------------+

文件头结构

Magic: 4 bytes     - 魔术数字(标识文件类型)
Bss: 4 bytes       - BSS 段大小
Entry: 4 bytes     - 入口点地址

节头结构

Name: 4 bytes      - 节名称偏移
Type: 1 byte       - 节类型
Flags: 1 byte      - 节标志
Addr: 4 bytes      - 内存地址
Size: 4 bytes      - 节大小
Offset: 4 bytes    - 文件偏移

核心类型

1. File - Plan 9 对象文件

type File struct {
    FileHeader
    Sections []*Section
    Symbols []Symbol
    
    // 包含过滤或未导出的字段
}

功能:表示打开的 Plan 9 对象文件。

字段说明

  • FileHeader:Plan 9 文件头
  • Sections:节列表
  • Symbols:符号列表

主要方法

// 打开文件
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

// 获取符号
func (f *File) Symbols() ([]Symbol, error)

// 获取字符串
func (f *File) StringTable() ([]byte, error)

注意事项

  • ⚠️ Plan 9 对象文件相对简单
  • ⚠️ 符号表信息可能有限
  • ✅ 提供基础的节和符号访问

2. FileHeader - 文件头

type FileHeader struct {
    Magic uint32
    Bss   uint32
    Entry uint64
}

字段说明

  • Magic:魔术数字(标识文件类型和字节序)
  • Bss:BSS 段(未初始化数据)大小
  • Entry:程序入口点地址

常见魔术数字

Magic32  = 0x00008000  // 32 位小端
Magic64  = 0x00008001  // 64 位小端

3. Section - 节

type Section struct {
    SectionHeader
    io.ReaderAt
}

功能:表示 Plan 9 对象文件中的一个节。

字段说明

  • SectionHeader:节头信息
  • io.ReaderAt:用于读取节内容

主要方法

// 读取节数据
func (s *Section) Data() ([]byte, error)

// 读取重定位
func (s *Section) Relocs() ([]Reloc, error)

4. SectionHeader - 节头

type SectionHeader struct {
    Name   string
    Type   SectionType
    Flags  SectionFlag
    Addr   uint64
    Size   uint64
    Offset uint64
}

字段说明

  • Name:节名称
  • Type:节类型(代码、数据等)
  • Flags:节标志(可读、可写、可执行等)
  • Addr:内存地址
  • Size:节大小
  • Offset:文件偏移

5. Symbol - 符号

type Symbol struct {
    Name  string
    Type  SymType
    Value uint64
    Size  uint64
}

字段说明

  • Name:符号名称
  • Type:符号类型(函数、数据等)
  • Value:符号值(地址)
  • Size:符号大小

符号类型

STypeText   // 代码
STypeData   // 数据
STypeBSS    // BSS
STypeCommon // 公共符号

6. Reloc - 重定位

type Reloc struct {
    Offset uint64
    Sym    int
    Type   int
    Addend int64
}

字段说明

  • Offset:重定位偏移
  • Sym:符号索引
  • Type:重定位类型
  • Addend:加数

常量定义

Magic - 魔术数字

const (
    Magic32 uint32 = 0x00008000  // 32 位
    Magic64 uint32 = 0x00008001  // 64 位
)

SectionType - 节类型

const (
    TypeNull  SectionType = iota  // 无效
    TypeText                      // 代码段
    TypeData                      // 数据段
    TypeBSS                       // BSS 段
    TypeString                    // 字符串表
    TypeSymbol                    // 符号表
)

SectionFlag - 节标志

const (
    FlagNone   SectionFlag = 0x00
    FlagAlloc  SectionFlag = 0x01  // 分配内存
    FlagWrite  SectionFlag = 0x02  // 可写
    FlagExec   SectionFlag = 0x04  // 可执行
    FlagLoad   SectionFlag = 0x08  // 可加载
)

SymType - 符号类型

const (
    SymTypeNone   SymType = iota  // 无类型
    SymTypeText                   // 代码
    SymTypeData                   // 数据
    SymTypeBSS                    // BSS
    SymTypeCommon                 // 公共符号
)

常见节名称

.text          // 代码节
.data          // 数据节
.bss           // BSS 节
.symtab        // 符号表
.strtab        // 字符串表
.rodata        // 只读数据节

完整示例

示例 1:打开和读取 Plan 9 对象文件

package main

import (
    "debug/plan9obj"
    "fmt"
    "log"
)

func main() {
    // 1. 打开 Plan 9 对象文件
    f, err := plan9obj.Open("myprogram.9")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    
    // 2. 显示文件头信息
    fmt.Printf("魔术数字:0x%x\n", f.Magic)
    fmt.Printf("BSS 大小:%d 字节\n", f.Bss)
    fmt.Printf("入口点:0x%x\n", f.Entry)
    
    // 3. 显示节和符号数量
    fmt.Printf("节数量:%d\n", len(f.Sections))
    fmt.Printf("符号数量:%d\n", len(f.Symbols))
}

示例 2:遍历所有节

package main

import (
    "debug/plan9obj"
    "fmt"
    "log"
)

func main() {
    f, err := plan9obj.Open("myprogram.9")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    
    fmt.Println("Plan 9 节信息:")
    fmt.Println("=" + "=" * 79)
    
    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("    大小:%d 字节\n", section.Size)
        fmt.Printf("    偏移:0x%x\n", section.Offset)
        
        fmt.Println()
    }
}

示例 3:读取特定节内容

package main

import (
    "debug/plan9obj"
    "encoding/hex"
    "fmt"
    "log"
)

func main() {
    f, err := plan9obj.Open("myprogram.9")
    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. 读取 .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))
    }
    
    // 3. 读取 .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))
    }
}

示例 4:读取符号表

package main

import (
    "debug/plan9obj"
    "fmt"
    "log"
)

func main() {
    f, err := plan9obj.Open("myprogram.9")
    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("    类型:%v\n", sym.Type)
            fmt.Printf("    值:0x%x\n", sym.Value)
            fmt.Printf("    大小:%d\n", sym.Size)
            count++
        }
    }
    
    // 2. 按类型分组显示
    fmt.Println("\n按类型分组:")
    
    textSyms := make([]plan9obj.Symbol, 0)
    dataSyms := make([]plan9obj.Symbol, 0)
    bssSyms := make([]plan9obj.Symbol, 0)
    
    for _, sym := range symbols {
        switch sym.Type {
        case plan9obj.SymTypeText:
            textSyms = append(textSyms, sym)
        case plan9obj.SymTypeData:
            dataSyms = append(dataSyms, sym)
        case plan9obj.SymTypeBSS:
            bssSyms = append(bssSyms, sym)
        }
    }
    
    fmt.Printf("代码符号:%d\n", len(textSyms))
    fmt.Printf("数据符号:%d\n", len(dataSyms))
    fmt.Printf("BSS 符号:%d\n", len(bssSyms))
}

示例 5:分析文件结构

package main

import (
    "debug/plan9obj"
    "fmt"
    "log"
)

func main() {
    f, err := plan9obj.Open("myprogram.9")
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    
    fmt.Println("=== Plan 9 对象文件分析 ===\n")
    
    // 1. 文件头信息
    fmt.Println("文件头信息:")
    fmt.Printf("  魔术数字:0x%x\n", f.Magic)
    fmt.Printf("  格式:%s\n", formatString(f.Magic))
    fmt.Printf("  BSS 大小:%d 字节\n", f.Bss)
    fmt.Printf("  入口点:0x%x\n\n", f.Entry)
    
    // 2. 节统计
    fmt.Println("节统计:")
    var textSize, dataSize, bssSize uint64
    
    for _, section := range f.Sections {
        switch section.Type {
        case plan9obj.TypeText:
            textSize += section.Size
        case plan9obj.TypeData:
            dataSize += section.Size
        case plan9obj.TypeBSS:
            bssSize += section.Size
        }
        
        fmt.Printf("  %s: %d 字节\n", section.Name, section.Size)
    }
    
    fmt.Printf("\n总计:\n")
    fmt.Printf("  代码段:%d 字节\n", textSize)
    fmt.Printf("  数据段:%d 字节\n", dataSize)
    fmt.Printf("  BSS 段:%d 字节\n", bssSize)
    fmt.Printf("  总大小:%d 字节\n\n", textSize+dataSize+bssSize)
    
    // 3. 符号统计
    fmt.Println("符号统计:")
    symbols, _ := f.Symbols()
    
    textCount := 0
    dataCount := 0
    bssCount := 0
    
    for _, sym := range symbols {
        switch sym.Type {
        case plan9obj.SymTypeText:
            textCount++
        case plan9obj.SymTypeData:
            dataCount++
        case plan9obj.SymTypeBSS:
            bssCount++
        }
    }
    
    fmt.Printf("  总符号数:%d\n", len(symbols))
    fmt.Printf("  代码符号:%d\n", textCount)
    fmt.Printf("  数据符号:%d\n", dataCount)
    fmt.Printf("  BSS 符号:%d\n", bssCount)
}

// formatString 将魔术数字转换为格式字符串
func formatString(magic uint32) string {
    switch magic {
    case plan9obj.Magic32:
        return "32 位"
    case plan9obj.Magic64:
        return "64 位"
    default:
        return "未知"
    }
}

示例 6:查找特定符号

package main

import (
    "debug/plan9obj"
    "fmt"
    "log"
    "strings"
)

// SymbolFinder 符号查找器
type SymbolFinder struct {
    file *plan9obj.File
}

// NewSymbolFinder 创建查找器
func NewSymbolFinder(filename string) (*SymbolFinder, error) {
    f, err := plan9obj.Open(filename)
    if err != nil {
        return nil, err
    }
    
    return &SymbolFinder{file: f}, nil
}

// Close 关闭文件
func (f *SymbolFinder) Close() error {
    return f.file.Close()
}

// FindByName 按名称查找符号
func (f *SymbolFinder) FindByName(pattern string) []plan9obj.Symbol {
    symbols, _ := f.file.Symbols()
    var results []plan9obj.Symbol
    
    for _, sym := range symbols {
        if strings.Contains(sym.Name, pattern) {
            results = append(results, sym)
        }
    }
    
    return results
}

// FindByType 按类型查找符号
func (f *SymbolFinder) FindByType(typ plan9obj.SymType) []plan9obj.Symbol {
    symbols, _ := f.file.Symbols()
    var results []plan9obj.Symbol
    
    for _, sym := range symbols {
        if sym.Type == typ {
            results = append(results, sym)
        }
    }
    
    return results
}

// FindMain 查找 main 函数
func (f *SymbolFinder) FindMain() *plan9obj.Symbol {
    symbols, _ := f.file.Symbols()
    
    for _, sym := range symbols {
        if sym.Name == "main" || sym.Name == "_main" {
            return &sym
        }
    }
    
    return nil
}

// ListFunctions 列出所有函数
func (f *SymbolFinder) ListFunctions() []plan9obj.Symbol {
    return f.FindByType(plan9obj.SymTypeText)
}

// ListVariables 列出所有变量
func (f *SymbolFinder) ListVariables() []plan9obj.Symbol {
    return f.FindByType(plan9obj.SymTypeData)
}

func main() {
    if len(os.Args) < 2 {
        log.Fatal("用法:symfinder <plan9-file> [command] [pattern]")
    }
    
    filename := os.Args[1]
    
    finder, err := NewSymbolFinder(filename)
    if err != nil {
        log.Fatal(err)
    }
    defer finder.Close()
    
    if len(os.Args) > 2 {
        command := os.Args[2]
        
        switch command {
        case "find":
            if len(os.Args) > 3 {
                pattern := os.Args[3]
                results := finder.FindByName(pattern)
                
                fmt.Printf("找到 %d 个匹配符号:\n", len(results))
                for _, sym := range results {
                    fmt.Printf("  %-40s 类型:%v 值:0x%x 大小:%d\n",
                        sym.Name, sym.Type, sym.Value, sym.Size)
                }
            }
        case "functions":
            funcs := finder.ListFunctions()
            fmt.Printf("函数数量:%d\n\n", len(funcs))
            for _, fn := range funcs {
                fmt.Printf("  %-40s 0x%x (%d 字节)\n",
                    fn.Name, fn.Value, fn.Size)
            }
        case "variables":
            vars := finder.ListVariables()
            fmt.Printf("变量数量:%d\n\n", len(vars))
            for _, v := range vars {
                fmt.Printf("  %-40s 0x%x (%d 字节)\n",
                    v.Name, v.Value, v.Size)
            }
        case "main":
            main := finder.FindMain()
            if main != nil {
                fmt.Printf("找到 main 函数:\n")
                fmt.Printf("  名称:%s\n", main.Name)
                fmt.Printf("  地址:0x%x\n", main.Value)
                fmt.Printf("  大小:%d 字节\n", main.Size)
            } else {
                fmt.Println("未找到 main 函数")
            }
        default:
            // 显示所有符号
            symbols, _ := finder.file.Symbols()
            fmt.Printf("所有符号 (%d 个):\n\n", len(symbols))
            for _, sym := range symbols {
                fmt.Printf("  %-40s 类型:%v 值:0x%x 大小:%d\n",
                    sym.Name, sym.Type, sym.Value, sym.Size)
            }
        }
    } else {
        // 默认显示所有符号
        symbols, _ := finder.file.Symbols()
        fmt.Printf("所有符号 (%d 个):\n\n", len(symbols))
        for _, sym := range symbols {
            fmt.Printf("  %-40s 类型:%v 值:0x%x 大小:%d\n",
                sym.Name, sym.Type, sym.Value, sym.Size)
        }
    }
}

示例 7:比较两个对象文件

package main

import (
    "debug/plan9obj"
    "fmt"
    "log"
    "sort"
)

// compareFiles 比较两个对象文件
func compareFiles(file1, file2 *plan9obj.File) {
    fmt.Println("=== 文件比较 ===\n")
    
    // 1. 比较文件头
    fmt.Println("文件头比较:")
    fmt.Printf("  文件 1 魔术数字:0x%x\n", file1.Magic)
    fmt.Printf("  文件 2 魔术数字:0x%x\n", file2.Magic)
    fmt.Printf("  文件 1 BSS: %d 字节\n", file1.Bss)
    fmt.Printf("  文件 2 BSS: %d 字节\n", file2.Bss)
    fmt.Printf("  文件 1 入口点:0x%x\n", file1.Entry)
    fmt.Printf("  文件 2 入口点:0x%x\n", file2.Entry)
    fmt.Println()
    
    // 2. 比较节
    fmt.Println("节比较:")
    
    sections1 := make(map[string]*plan9obj.Section)
    sections2 := make(map[string]*plan9obj.Section)
    
    for _, s := range file1.Sections {
        sections1[s.Name] = s
    }
    for _, s := range file2.Sections {
        sections2[s.Name] = s
    }
    
    // 查找共同的节
    commonNames := make([]string, 0)
    for name := range sections1 {
        if _, ok := sections2[name]; ok {
            commonNames = append(commonNames, name)
        }
    }
    sort.Strings(commonNames)
    
    fmt.Printf("共同节:%d 个\n", len(commonNames))
    for _, name := range commonNames {
        s1 := sections1[name]
        s2 := sections2[name]
        
        diff := ""
        if s1.Size != s2.Size {
            diff = fmt.Sprintf(" (大小差异:%d vs %d)", s1.Size, s2.Size)
        }
        
        fmt.Printf("  %s: %d -> %d 字节%s\n", name, s1.Size, s2.Size, diff)
    }
    
    // 查找新增的节
    fmt.Println("\n新增的节:")
    for name := range sections2 {
        if _, ok := sections1[name]; !ok {
            fmt.Printf("  + %s (%d 字节)\n", name, sections2[name].Size)
        }
    }
    
    // 查找删除的节
    fmt.Println("\n删除的节:")
    for name := range sections1 {
        if _, ok := sections2[name]; !ok {
            fmt.Printf("  - %s (%d 字节)\n", name, sections1[name].Size)
        }
    }
    
    // 3. 比较符号
    fmt.Println("\n符号比较:")
    
    syms1, _ := file1.Symbols()
    syms2, _ := file2.Symbols()
    
    symMap1 := make(map[string]plan9obj.Symbol)
    symMap2 := make(map[string]plan9obj.Symbol)
    
    for _, sym := range syms1 {
        symMap1[sym.Name] = sym
    }
    for _, sym := range syms2 {
        symMap2[sym.Name] = sym
    }
    
    fmt.Printf("文件 1 符号数:%d\n", len(symMap1))
    fmt.Printf("文件 2 符号数:%d\n", len(symMap2))
    
    // 新增的符号
    added := make([]string, 0)
    for name := range symMap2 {
        if _, ok := symMap1[name]; !ok {
            added = append(added, name)
        }
    }
    sort.Strings(added)
    
    fmt.Printf("\n新增符号:%d 个\n", len(added))
    for i, name := range added {
        if i >= 20 {
            break
        }
        fmt.Printf("  + %s\n", name)
    }
    if len(added) > 20 {
        fmt.Printf("  ... 还有 %d 个\n", len(added)-20)
    }
    
    // 删除的符号
    removed := make([]string, 0)
    for name := range symMap1 {
        if _, ok := symMap2[name]; !ok {
            removed = append(removed, name)
        }
    }
    sort.Strings(removed)
    
    fmt.Printf("\n删除符号:%d 个\n", len(removed))
    for i, name := range removed {
        if i >= 20 {
            break
        }
        fmt.Printf("  - %s\n", name)
    }
    if len(removed) > 20 {
        fmt.Printf("  ... 还有 %d 个\n", len(removed)-20)
    }
    
    // 变化的符号
    changed := make([]string, 0)
    for name, sym1 := range symMap1 {
        sym2, ok := symMap2[name]
        if ok {
            if sym1.Value != sym2.Value || sym1.Size != sym2.Size {
                changed = append(changed, name)
            }
        }
    }
    sort.Strings(changed)
    
    fmt.Printf("\n变化的符号:%d 个\n", len(changed))
    for i, name := range changed {
        if i >= 20 {
            break
        }
        sym1 := symMap1[name]
        sym2 := symMap2[name]
        fmt.Printf("  ~ %s (0x%x/%d -> 0x%x/%d)\n",
            name, sym1.Value, sym1.Size, sym2.Value, sym2.Size)
    }
    if len(changed) > 20 {
        fmt.Printf("  ... 还有 %d 个\n", len(changed)-20)
    }
}

func main() {
    if len(os.Args) < 3 {
        log.Fatal("用法:compare <file1.9> <file2.9>")
    }
    
    file1, err := plan9obj.Open(os.Args[1])
    if err != nil {
        log.Fatal("打开文件 1 失败:", err)
    }
    defer file1.Close()
    
    file2, err := plan9obj.Open(os.Args[2])
    if err != nil {
        log.Fatal("打开文件 2 失败:", err)
    }
    defer file2.Close()
    
    compareFiles(file1, file2)
}

安全最佳实践

✅ 推荐做法

  1. 始终检查错误

    f, err := plan9obj.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, _ := plan9obj.Open("file")
    
    // ✅ 正确
    f, err := plan9obj.Open("file")
    if err != nil {
        // 处理错误
    }
    
  2. 不要忘记关闭文件

    // ❌ 错误
    f, _ := plan9obj.Open("file")
    
    // ✅ 正确
    f, _ := plan9obj.Open("file")
    defer f.Close()
    
  3. 不要假设节一定存在

    // ❌ 错误
    data, _ := f.Section(".text").Data()
    
    // ✅ 正确
    section := f.Section(".text")
    if section == nil {
        return error
    }
    data, err := section.Data()
    

总结

核心类型

File         // Plan 9 对象文件
FileHeader   // 文件头
Section      // 节
SectionHeader // 节头
Symbol       // 符号
Reloc        // 重定位

使用场景

场景推荐方法说明
打开文件plan9obj.Open()读取 Plan 9 对象文件
读取节File.Section()获取特定节
读取符号File.Symbols()获取符号表
获取字符串File.StringTable()获取字符串表

魔术数字

格式常量说明
32 位Magic3232 位对象文件
64 位Magic6464 位对象文件

节类型

类型常量说明
无效TypeNull无效节
代码TypeText代码段
数据TypeData数据段
BSSTypeBSSBSS 段
字符串TypeString字符串表
符号TypeSymbol符号表

符号类型

类型常量说明
无类型SymTypeNone无类型符号
代码SymTypeText函数/代码
数据SymTypeData数据对象
BSSSymTypeBSS未初始化数据
公共SymTypeCommon公共符号

常见节

节名用途
.text代码节
.data数据节
.bssBSS 节
.symtab符号表
.strtab字符串表
.rodata只读数据节

与其他格式比较

特性Plan 9ELFMach-OPE
复杂度简单复杂中等复杂
平台Plan 9UnixmacOSWindows
用途历史/教学通用AppleWindows
大小

参考资料


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