Go 源码编译流程

Go 编译源代码需要经过编译,链接过程。通过以下示例看 Go (Go 版本为 v1.24.1)语言是如何编译源码的。

首先,代码目录结构如下:

➜  demo1 git:(main) ✗ tree ./
./
├── cmd
│   └── app1
│   └── main.go
└── pkg
└── pkg1
└── pkg1.go // main.go
package main import "github.com/TroyXia/effective-go-book/chapter3/demo1/pkg/pkg1" func main() {
pkg1.Func1()
} // pkg1.go
package pkg1 import "fmt" func Func1() {
fmt.Println("pkg1.Func1 invoked")
}

编译 app1 main.go 为可执行文件:

➜  demo1 git:(main) ✗ go build -x -v ./cmd/app1
WORK=/var/folders/xl/5zdz2b514tg_fmsn0k9h0b200000gn/T/go-build2927962297 ...
cat >/var/folders/xl/5zdz2b514tg_fmsn0k9h0b200000gn/T/go-build2927962297/b001/importcfg.link << 'EOF' # internal
packagefile github.com/TroyXia/effective-go-book/chapter3/demo1/cmd/app1=/Users/hxia/Library/Caches/go-build/4c/4c4a8646f05704995bdb9ecacc59745b0c87e54e0b3f134ebc787ffa86349e24-d
...
modinfo "0w\xaf\f\x92t\b\x02A\xe1\xc1\a\xe6\xd6\x18\xe6path\tgithub.com/TroyXia/effective-go-book/chapter3/demo1/cmd/app1\nmod\tgithub.com/TroyXia/effective-go-book\tv0.0.0-20250726040104-4c9ceff7dcec+dirty\t\nbuild\t-buildmode=exe\nbuild\t-compiler=gc\nbuild\tCGO_ENABLED=1\nbuild\tCGO_CFLAGS=\nbuild\tCGO_CPPFLAGS=\nbuild\tCGO_CXXFLAGS=\nbuild\tCGO_LDFLAGS=\nbuild\tGOARCH=arm64\nbuild\tGOOS=darwin\nbuild\tGOARM64=v8.0\nbuild\tvcs=git\nbuild\tvcs.revision=4c9ceff7dcecb5af5c6e76869f02e15c5a71d2b2\nbuild\tvcs.time=2025-07-26T04:01:04Z\nbuild\tvcs.modified=true\n\xf92C1\x86\x18 r\x00\x82B\x10A\x16\xd8\xf2"
EOF mkdir -p $WORK/b001/exe/
cd .
GOROOT='/Users/hxia/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.4.darwin-arm64' /Users/hxia/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.4.darwin-arm64/pkg/tool/darwin_arm64/link -o $WORK/b001/exe/a.out -importcfg $WORK/b001/importcfg.link -buildmode=pie -buildid=CoYhGGCXfSwQX5HjxsS7/ocfceuFgMPhkI7fMX0t9/gBNCu5T0ndxhcTWp_FbS/CoYhGGCXfSwQX5HjxsS7 -extld=clang /Users/hxia/Library/Caches/go-build/4c/4c4a8646f05704995bdb9ecacc59745b0c87e54e0b3f134ebc787ffa86349e24-d
/Users/hxia/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.4.darwin-arm64/pkg/tool/darwin_arm64/buildid -w $WORK/b001/exe/a.out # internal
mv $WORK/b001/exe/a.out app1
rm -rf $WORK/b001/

go build 命令对应用/包进行编译。其中,-x 选项打印编译的执行命令,-v 选项打印编译时的包名。

go build 输出信息可以看出,编译主要分为以下几步:

  1. 构建工作空间 WORK 目录,该目录在编译完会删除。-work 选项可以保留工作空间(不过临时的编译文件会被删除)。
  2. 构造编译的链接包信息 importcfg.link
  3. 链接器 link 链接 importcfg.link 中的包目标文件(.a 文件)为可执行文件 a.out

上述流程中少了编译过程,那么编译是在哪里发生的呢?

go build 中添加 -a 选项,-a 选项会重新编译所依赖的包(部分内容和上述编译输出类似,为减少冗余,这里重点关注编译部分):

cd /Users/hxia/project/effective-go-book/chapter3/demo1
/Users/hxia/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.4.darwin-arm64/pkg/tool/darwin_arm64/compile -o $WORK/b013/_pkg_.a -trimpath "$WORK/b013=>" -p internal/byteorder -lang=go1.24 -std -complete -buildid c6rtfNe70CDveX5CsxT3/c6rtfNe70CDveX5CsxT3 -goversion go1.24.4 -c=4 -shared -nolocalimports -importcfg $WORK/b013/importcfg -pack /Users/hxia/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.4.darwin-arm64/src/internal/byteorder/byteorder.go
/Users/hxia/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.4.darwin-arm64/pkg/tool/darwin_arm64/compile -o $WORK/b015/_pkg_.a -trimpath "$WORK/b015=>" -p internal/coverage/rtcov -lang=go1.24 -std -complete -buildid w3eNR5jLvNw3wS6UVtCI/w3eNR5jLvNw3wS6UVtCI -goversion go1.24.4 -c=4 -shared -nolocalimports -importcfg $WORK/b015/importcfg -pack /Users/hxia/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.4.darwin-arm64/src/internal/coverage/rtcov/rtcov.go
/Users/hxia/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.4.darwin-arm64/pkg/tool/darwin_arm64/compile -o $WORK/b016/_pkg_.a -trimpath "$WORK/b016=>" -p internal/godebugs -lang=go1.24 -std -complete -buildid n8Me6R799Ql_sSkrNPi9/n8Me6R799Ql_sSkrNPi9 -goversion go1.24.4 -c=4 -shared -nolocalimports -importcfg $WORK/b016/importcfg -pack /Users/hxia/go/pkg/mod/golang.org/toolchain@v0.0.1-go1.24.4.darwin-arm64/src/internal/godebugs/table.go

可以看到 compile 编译包为 _pkg_.a 目标文件。该文件会作为缓存存在于 go env 缓存目录:

➜  demo1 git:(main) ✗ go env | grep cache -i
GOCACHE='/Users/hxia/Library/Caches/go-build' // 进入缓存目录,缓存目录中存放的是编译包的目标文件(静态库,以 -a 结尾)和依赖描述文件(以 -d 结尾,记录包之间的依赖关系和编译参数)
➜ go-build pwd
/Users/hxia/Library/Caches/go-build
➜ go-build cd 04
➜ 04 ls
040220f430a0f504e501643d86f6973881ff5c1ddac97cda7419062ffebf2c9a-d 0466cee1e7f91da1b64696743011bf7315f38d07f501651dd54095fc0f4f25a2-a

看到这里大致理解了,之所以刚开始没有出现编译是因为依赖包已经编译好加载到缓存中了,其引用的是缓存中的依赖描述文件 /Users/hxia/Library/Caches/go-build/4c/4c4a8646f05704995bdb9ecacc59745b0c87e54e0b3f134ebc787ffa86349e24-d

Go 1.9 源码编译流程

如上节所示,在 Go 1.11 版本之后的静态库都是加载到缓存中。这里以 Go 1.9 版本为例更直观的查看编译过程。

首先通过 go install 安装包,go install 会编译包为静态库。如下:

# ./go install -x github.com/TroyXia/effective-go-book/chapter3/demo1/pkg/pkg1
...
/root/hxia/go/go/pkg/tool/linux_amd64/compile -o $WORK/github.com/TroyXia/effective-go-book/chapter3/demo1/pkg/pkg1.a -trimpath $WORK -goversion go1.9.7 -p github.com/TroyXia/effective-go-book/chapter3/demo1/pkg/pkg1 -complete -buildid 31988b53e81eee1d2b33fbaf9b29322314015d4a -D _/root/go/src/github.com/TroyXia/effective-go-book/chapter3/demo1/pkg/pkg1 -I $WORK -pack ./pkg1.go
mkdir -p /root/go/pkg/linux_amd64/github.com/TroyXia/effective-go-book/chapter3/demo1/pkg/
cp $WORK/github.com/TroyXia/effective-go-book/chapter3/demo1/pkg/pkg1.a /root/go/pkg/linux_amd64/github.com/TroyXia/effective-go-book/chapter3/demo1/pkg/pkg1.a

go install 调用 compile 编译包 github.com/TroyXia/effective-go-book/chapter3/demo1/pkg/pkg1 为静态库 $WORK/github.com/TroyXia/effective-go-book/chapter3/demo1/pkg/pkg1.a,接着 cp 拷贝该静态库到 /root/go/pkg/linux_amd64/github.com/TroyXia/effective-go-book/chapter3/demo1/pkg/pkg1.a

继续编 go 应用查看应用是如何加载 pkg1 静态库的:

# ./go build -x -v github.com/TroyXia/effective-go-book/chapter3/demo1/cmd/app1
WORK=/tmp/go-build326903613
...
/root/hxia/go/go/pkg/tool/linux_amd64/compile -o $WORK/github.com/TroyXia/effective-go-book/chapter3/demo1/cmd/app1.a -trimpath $WORK -goversion go1.9.7 -p main -complete -buildid 4994d4113529a2a372aab06efa535b1ffff36829 -D _/root/go/src/github.com/TroyXia/effective-go-book/chapter3/demo1/cmd/app1 -I $WORK -I /root/go/pkg/linux_amd64 -pack ./main.go
cd . /root/hxia/go/go/pkg/tool/linux_amd64/link -o $WORK/github.com/TroyXia/effective-go-book/chapter3/demo1/cmd/app1/_obj/exe/a.out -L $WORK -L /root/go/pkg/linux_amd64 -extld=gcc -buildmode=exe -buildid=4994d4113529a2a372aab06efa535b1ffff36829 $WORK/github.com/TroyXia/effective-go-book/chapter3/demo1/cmd/app1.a
cp $WORK/github.com/TroyXia/effective-go-book/chapter3/demo1/cmd/app1/_obj/exe/a.out app1

流程比较清晰,compile 编译 app 包到静态库 $WORK/github.com/TroyXia/effective-go-book/chapter3/demo1/cmd/app1.a(其中包括了应用依赖的 pkg1.a 静态库)。最终通过 link 链接静态库成可执行文件 a.out

这里有一个问题是 app 依赖的静态库是 go install 提前编译好的 pkg1.a 还是 $WORK 目录下的静态库呢?

我们更新 pkg1.go 的内容,查看内容变化后静态库链接的是旧的还是更新后的 pkg1.a

package pkg

import "fmt"

func Func1() {
fmt.Println("pkg1.Func1 invoked already")
}

编译后运行应用输出 pkg1.Func1 invoked alreadygo install 提前编译的静态库并未被链接。

因为在 link 链接这里指定的链接目录顺序为 -L $WORK -L /root/go/pkg/linux_amd64。链接器现在 $WORK 搜索链接的静态库,如果没有找到再到 /root/go/pkg/linux_amd64 目录下搜索,而 go install 编译的静态库是在 /root/go/pkg/linux_amd64 目录下。

调换链接器的链接库目录顺序,手动执行,输出 pkg1.Func1 invoked。代码编译用的是老的静态库。

静态可执行文件

继续看静态可执行文件。在 macOS 系统编译应用为可执行文件。如下:

➜  demo1 git:(main) ✗ go build -o app cmd/app1/main.go
➜ demo1 git:(main) ✗ ls -la
total 4600
drwxr-xr-x@ 5 hxia staff 160 Jul 27 21:52 .
drwxr-xr-x@ 3 hxia staff 96 Jul 26 11:51 ..
-rwxr-xr-x@ 1 hxia staff 2351842 Jul 27 21:52 app
drwxr-xr-x@ 3 hxia staff 96 Jul 26 11:52 cmd
drwxr-xr-x@ 3 hxia staff 96 Jul 26 11:52 pkg
➜ demo1 git:(main) ✗ otool -L app
app:
/usr/lib/libSystem.B.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libresolv.9.dylib (compatibility version 0.0.0, current version 0.0.0)

otool 显示静态可执行文件还需要调用动态库 libSystem.B.dyliblibresolv.9.dylib,这个静态可执行文件看起来不静态啊。

这是由于 macOS 操作系统的缘故,编译的可执行文件并不是完整的静态可执行。

通过 go build 编译成 Linux amd64 系统的可执行文件 app:

GOOS=linux GOARCH=amd64 go build -o app cmd/app1/main.go 

// 拷贝 app 到 Linux amd64 机器上

// ldd 查看 app 是否依赖动态库
# ldd app
不是动态可执行文件

在 Linux amd64 机器上可以看到编译出的是完整的静态可执行文件,可以直接运行。

参考资料


Go 源码编译流程的更多相关文章

  1. spring5源码编译过程中必经的坑

    spring源码编译流程:Spring5 源码下载 第 一 步 : https://github.com/spring-projects/spring-framework/archive/v5.0.2 ...

  2. [笔记] Ubuntu 18.04源码编译安装OpenCV 4.0流程

    标准常规安装方法安装的OpenCV版本比较低,想尝鲜使用4.0版本,只好源码安装. 安装环境 OS:Ubuntu 18.04 64 bit 显卡:NVidia GTX 1080 CUDA:10.0 c ...

  3. hadoop-2.6.0源码编译问题汇总

    在上一篇文章中,介绍了hadoop-2.6.0源码编译的一般流程,因个人计算机环境的不同, 编译过程中难免会出现一些错误,下面是我编译过程中遇到的错误. 列举出来并附上我解决此错误的方法,希望对大家有 ...

  4. Ubantu16.04进行Android 8.0源码编译

    参考这篇博客 经过测试,8.0源码下载及编译之后,占用100多G的硬盘空间,尽量给ubantu系统多留一些硬盘空间,如果后续需要在编译好的源码上进行开发,需要预留更多的控件,为了防止后续出现文件权限问 ...

  5. Android源码浅析(四)——我在Android开发中常用到的adb命令,Linux命令,源码编译命令

    Android源码浅析(四)--我在Android开发中常用到的adb命令,Linux命令,源码编译命令 我自己平时开发的时候积累的一些命令,希望对你有所帮助 adb是什么?: adb的全称为Andr ...

  6. 【流媒体开发】VLC Media Player - Android 平台源码编译 与 二次开发详解 (提供详细800M下载好的编译源码及eclipse可调试播放器源码下载)

    作者 : 韩曙亮  博客地址 : http://blog.csdn.net/shulianghan/article/details/42707293 转载请注明出处 : http://blog.csd ...

  7. Python 多线程、多进程 (一)之 源码执行流程、GIL

    Python 多线程.多进程 (一)之 源码执行流程.GIL Python 多线程.多进程 (二)之 多线程.同步.通信 Python 多线程.多进程 (三)之 线程进程对比.多线程 一.python ...

  8. Android系统定制和源码开发以及源码编译(附视频)

    Android系统定制配套视频: 为了把Android系统源码定制和编译的课程讲完,从准备到录制完所有的视频,一共花去了近半年的时间,前前后后各种下载源码,编译源码,系统不兼容,版本适配,虚拟机配置困 ...

  9. Linux 下源码编译安装 vim 8.1

    前言 目前 linux 的各个发行版基本上都是带了一个 vi 编辑器的,而本文要说的 vim 编辑器对 vi 做了一些优化升级,更好用.当我们需要远程操作一台 linux 服务器的时候,只能使用命令行 ...

  10. 日常工作之Zabbix源码编译,兼容mysql5.6

    原文链接:http://www.leleblog.top/daily/more?id=6 Zabbix源码编译 环境: centOS7.mysql5.6.21(已存在). 任务简述: 服务器搭建zab ...

随机推荐

  1. mcp~客户端与服务端的通讯技术

    mcp通讯协议 stdio sse streamable http JSON_RPC MCP 的传输层负责将 MCP 协议消息转换为 JSON-RPC 格式进行传输,并将接收到的 JSON-RPC 消 ...

  2. SSI注入

    .stm,.shtm和.shtml后缀文件中可以如此执行命令 <!--#exec cmd="ls"-->

  3. 把 PySide6 移植到安卓上去!

    官方教程在此:https://www.qt.io/blog/taking-qt-for-python-to-android 寥寥几句,其实不少坑.凭回忆写的,可能不是很全(无招胜有招) 仅支持 Lin ...

  4. 如何在FastAPI中构建一个既安全又灵活的多层级权限系统?

    title: 如何在FastAPI中构建一个既安全又灵活的多层级权限系统? date: 2025/06/14 12:43:05 updated: 2025/06/14 12:43:05 author: ...

  5. Arduino从零开始的高手之路——初识Arduino

    初识Arduino 时隔若干年,重启Arduino入门教程~ Arduino自2005年推出以来,广受好评,如今已成为最热门的开源硬件之一.在全球最大的开源社区Github上,Arduino已经成为了 ...

  6. 【HarmonyOS5】DevEco Studio 使用指南:代码阅读与编辑功能详解

    前言 DevEco Studio 是一款功能强大的开发工具,支持 ArkTS.JavaScript.TypeScript 等多种语言,具备智能代码补全.语法高亮.实时错误检查等特性,极大提升了鸿蒙应用 ...

  7. 【6】树的DFS序、直径、重心

    前言 树上操作是 OI 重要的一环,树的 DFS 序.直径.重心这一堆东西也是树上操作的基础.树的 DFS 序可以把树上问题转化为区间问题,树的直径的性质经常是解题的关键,树的重心可以防止一些树上算法 ...

  8. Codigger 吉祥物家族:Boby 天团来袭

    Boby 家族 正式登场--四位性格迥异但同样可爱的吉祥物,将化身你的编程小伙伴,让开发之旅充满乐趣! HappyBoby:你的快乐代码向导 性格:阳光开朗.元气满满 特长:在你成功时欢呼,在你迷茫时 ...

  9. 工作中常见的OOM?你了解JVM调优吗?

    工作中常见的6种OOM问题 堆内存OOM 堆内存OOM是最常见的OOM了. 出现堆内存OOM问题的异常信息如下: java.lang.OutOfMemoryError: Java heap space ...

  10. SciTech-BigDataAIML-LLM-Generative model

    https://statproofbook.github.io/D/gm Definition: Generative model Index: The Book of Statistical Pro ...