菜单

链接与可执行文件生成

相关源文件

目的与范围

本文档描述了 Go 链接器如何将编译器生成的对象文件转换为可执行二进制文件。它涵盖了链接过程、符号解析、重定位以及特定于平台的​​可执行格式(ELF、Mach-O、PE)的生成。有关生成链接器使用的对象文件的编译过程的信息,请参阅Go 编译器

链接过程概述

Go 链接器(通常调用为 go tool link)是构建过程的最后阶段。它接收包含已编译 Go 代码的对象文件,并将它们合并为可执行二进制文件或共享库。链接过程包括几个阶段:

  1. 加载输入文件和目标代码
  2. 设置运行时符号和构建信息
  3. 符号解析和死代码消除
  4. 内存段布局
  5. 重定位和地址修复
  6. 根据目标平台格式生成输出文件

图示:高层链接过程

来源:src/cmd/link/internal/ld/main.go160-493 src/cmd/link/internal/ld/lib.go483-687

Go 链接器支持多种构建模式和链接模式,这些模式会影响最终可执行文件的生成方式。

构建模式

构建模式描述
exe标准可执行文件(默认)
pie位置无关可执行文件
c-archiveC 归档文件
c-sharedC 共享库
sharedGo 共享库
pluginGo 插件
链接模式描述
internal使用 Go 的内部链接器
external使用外部主机链接器(gcc/clang)
自动版 (auto)在可能的情况下使用内部链接,否则使用外部链接

来源:src/cmd/link/internal/ld/lib.go282-288 src/cmd/link/doc.go54-62

输入处理与目标文件加载

链接器首先加载命令行指定的输入目标文件和库。目标文件加载的主要入口点是 loadlib 函数。

图示:目标文件加载过程

来源:src/cmd/link/internal/ld/lib.go524-690

在此阶段,链接器会:

  1. 加载所有 Go 对象文件并解析其符号
  2. 加载所需的运行时包
  3. 确定适当的链接模式
  4. 加载任何主机对象(用于 cgo)
  5. 根据需要设置外部链接

符号解析与管理

加载所有输入文件后,链接器会处理符号,解析它们之间的引用。这包括处理 Go 运行时的特殊符号、设置入口点以及准备 TLS(线程局部存储)符号。

图示:符号处理流程

来源:src/cmd/link/internal/ld/lib.go804-930 src/cmd/link/internal/ld/main.go420-436

关键的符号处理步骤

  1. 移除不可达代码(死代码消除)
  2. 类型符号的名称修饰(用于共享库)
  3. 为运行时系统设置内置符号
  4. 处理 Go 特有的信息,例如构建信息

内存布局和段组织

链接器根据输出格式(ELF、Mach-O、PE)将可执行文件的内存布局组织成各种段。

典型的段组织

章节目的
.text可执行代码
.rodata只读数据
.data已初始化数据
.bss未初始化数据
.noptrdata不含指针的数据(用于 GC)
.noptrbss不含指针的未初始化数据
.pclntab用于运行时的函数信息
.gosymtab用于调试的符号表
.gopclntab行号信息

来源:src/cmd/link/internal/ld/data.go57-60 src/cmd/link/internal/ld/elf.go243-275

重定位处理

重定位会调整可执行文件内的地址,以反映代码和数据实际加载的内存位置。Go 链接器同时处理内部重定位(Go 符号之间)和外部重定位(用于 C/系统库)。

重定位类型

链接器处理多种重定位类型:

  • 直接调用/跳转重定位
  • 数据地址引用
  • TLS(线程局部存储)重定位
  • 位置无关代码重定位
  • 外部符号引用

对于分支范围有限的架构(ARM、LOONG64、PPC64、RISCV64),链接器可以为超出直接分支距离的调用插入跳转。

来源:src/cmd/link/internal/ld/data.go96-138 src/cmd/link/internal/ld/data.go177-634

输出文件生成

链接的最后阶段是为目标平台生成适当格式的输出文件。

图示:输出文件生成过程

来源:src/cmd/link/internal/ld/main.go437-493 src/cmd/link/internal/ld/elf.go234-546 src/cmd/link/internal/ld/macho.go291-607 src/cmd/link/internal/ld/pe.go301-625

特定平台的格式

Go 链接器支持多种可执行格式,适用于不同的平台。主要格式包括:

ELF(可执行与可链接格式)

用于 Linux、FreeBSD 和大多数类 Unix 系统。ELF 格式包括:

  • 包含特定于机器的信息的 ELF 头
  • 描述内存段的程序头
  • 包含详细段信息的段头
  • 各种标准段(.text、.data、.bss 等)
  • 在需要时包含动态链接信息

来源:src/cmd/link/internal/ld/elf.go234-440

Mach-O(macOS/iOS)

用于基于 Darwin 的系统(macOS、iOS)。Mach-O 的特性包括:

  • 包含目标架构信息的 Mach-O 头
  • 描述文件结构的加载命令
  • 段和节的组织
  • 对动态库的特殊处理
  • 代码签名要求

来源:src/cmd/link/internal/ld/macho.go291-477

PE(可移植可执行文件)

用于 Windows。PE 格式的特性包括:

  • DOS 兼容头
  • 包含特定于 Windows 的信息的 PE 头
  • 带有标准段的段表
  • DLL 的导入和导出表
  • 资源和调试信息

来源:src/cmd/link/internal/ld/pe.go301-625

内部链接与外部链接

Go 链接器可以以两种模式运行:内部链接和外部链接。

内部链接

内部链接的使用场景:

  • 程序不使用 cgo(纯 Go)
  • 不需要主机对象文件
  • 目标平台得到 Go 链接器的完全支持

优点

  • 链接过程更快
  • 没有外部依赖
  • 交叉编译无需 C 工具链

来源:src/cmd/link/internal/ld/lib.go584-600

外部链接

外部链接的使用场景:

  • 程序使用 cgo
  • 需要主机目标文件
  • 使用了影响链接的某些编译器标志

外部链接在准备好目标文件后,会调用主机 C 链接器 (gcc/clang)。此过程由 hostlink 函数管理。

出于安全原因,Go 链接器限制了可以传递给外部工具的编译器和链接器标志。

来源: src/cmd/link/internal/ld/lib.go482-596 src/cmd/go/internal/work/security.go10-144

特殊功能

构建 ID 和符号版本控制

Go 链接器会在可执行文件中嵌入唯一的构建 ID,可用于调试和版本识别。对于共享库和插件,链接器还处理符号版本控制以确保兼容性。

来源: src/cmd/link/internal/ld/lib.go1005-1046

跳板生成

对于分支距离有限的架构(ARM、ARM64、LOONG64、PPC64、RISCV64),当函数调用或跳转超过最大直接分支距离时,链接器会自动插入跳板(小的代码桥接)。

来源: src/cmd/link/internal/ld/data.go62-138

动态链接支持

在构建共享库或插件时,链接器会生成动态链接所需的元数据

  • 导出符号的符号表
  • 动态重定位
  • 版本信息
  • 初始化和终止函数

来源: src/cmd/link/internal/ld/lib.go278-301

命令行用法

链接器通常由 Go 构建系统调用,但也可以直接调用,使用

go tool link [flags] main.a

常用标志包括

  • -o:指定输出文件名
  • -buildmode:设置构建模式(exe、shared 等)
  • -linkmode:设置链接模式(internal、external)
  • -extld:指定外部链接器
  • -L:添加库搜索路径
  • -H:设置可执行文件格式类型

来源: src/cmd/link/doc.go12-46