CGO_ENABLED如何影响动态链接和静态链接?

10
我们正在将Go代码编译为docker可运行的代码,并调查为什么我们的二进制文件无法执行。我们发现它缺少一些动态库(虽然我们希望静态链接的二进制文件)。
以下是编译方式:
env GOOS=linux CGO_ENABLED=1 GO111MODULE=on GOPRIVATE=github.com/ourrepo GOPROXY=https://proxy.golang.org go build --installsuffix cgo --ldflags='-extldflags=-static' -o program main.go
使用相同的构建命令和CGO_ENABLED=0修复了问题,输出的二进制文件被静态链接。
现在奇怪的部分是我们有另一个程序,这次使用相同的构建命令CGO_ENABLED=1,但它是静态链接的!
所以我很困惑为什么在某些情况下,CGO_ENABLED=1会产生动态链接,而有时是静态链接。愿意提供更多细节。

4
只有当需要时,二进制文件才会动态链接。如果不使用cgo,则CGO_ENABLED=1没有任何影响。 - JimB
我不确定这里的问题是什么。如果您想要CGO_ENABLED=0的二进制文件,那么您使用原始的go build命令想要实现什么目标呢?(原始命令看起来像是随意拼凑在一起的组合) - JimB
2
@JimB 我正在努力理解为什么在一个情况下(cgo_enabled=1),二进制文件是动态链接的,而在另一个情况下则不是。对于你的问题“CGO_ENABLED=0是我们想要的吗?”并不完全是这样。我们想要静态链接。CGO_ENABLED=0只是神奇地实现了这一点,我不知道为什么。这样更有意义吗?您建议删除--installsuffix cgo和GO111MODULE以及CGO吗? - Thomas
1
因为一个使用了cgo,而另一个没有。命令本身没有意义,你正在将“-static”传递给一个外部链接器,但你没有调用它(没有“-linkmode external”),你似乎没有进行交叉编译,但你定义了GOOS,尽管没有定义GOARCH;并且你设置了“GO111MODULE=on”,但你没有构建一个包。看起来你添加了一堆选项,却不知道它们的作用,这导致了混乱。 - JimB
1
CGO_ENABLED=0并不能“神奇地”实现它,它只是防止使用cgo,从而产生一个静态二进制文件,这似乎是你想要的。 - JimB
显示剩余3条评论
1个回答

10

一些Go包在底层使用CGO,利用常见的C库以实现更广泛的兼容性,解决运行时遇到的边缘情况。

这些常见的库可以在大多数主流操作系统发行版中找到,但显然不包括Scratch镜像(由于自身特性完全为空)。

CGO_ENABLED默认设置为1,这意味着即使使用-static标志,也必须显式禁用它才能避免使用CGO。


4
值得注意的是,默认情况下 net 包会使用一些 libc 函数。 - Opnauticus
4
“减小编译后二进制文件的大小。”并非完全如此。在 Cgo 中,二进制文件包含两个解析器。纯 Go 解析器是默认的,除非您明确告知或操作系统配置中有纯 Go 解析器不支持的内容,否则始终使用纯 Go 解析器。Cgo 解析器的资源消耗要高得多,因为每个 Cgo 调用都会占用整个线程而不是 goroutine。它可能存在于兼容性方面(纯 Go 尝试模拟常见实现),因为在某些系统上,您无法自己发送 DNS 请求(macOS)。Go 不需要 libc。 - creker
1
有趣的观点!我进一步阅读了一下(https://github.com/golang/go/issues/25670),你是完全正确的。我知道调用Cgo的开销成本,但总是认为默认包含C钩子的好处是为了大小优化。感谢分享。我会更新答案以反映这一点。 - Opnauticus
我不认为这样做会改变答案的实质... @Flimzy的问题是在Scratch容器中尝试使用这些底层库,通过禁用CGO来解决。 - Opnauticus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接