当前位置: 首页 > 工具软件 > musl > 使用案例 >

cgo静态编译不能用glibc,用musl

边健
2023-12-01

Golang 的一个动态链接依赖问题

upx 是一个压缩二进制的工具,如上图,经过压缩之后,这些 binary 的体积都减少了 46%。

静态链接 CGO 的依赖
如果使用 glibc 的是,是不能静态链接的:

root@f88271a666f9:/workspace# go build -ldflags "-linkmode external -extldflags '-static'" ./cmd/spex
# git.garena.com/shopee/platform/spex/cmd/spex
/usr/bin/ld: /go/pkg/mod/github.com/confluentinc/confluent-kafka-go@v1.5.2/kafka/librdkafka/librdkafka_glibc_linux.a(rddl.o): in function `rd_dl_open':
(.text+0x1d): warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
/usr/bin/ld: /tmp/go-link-883441031/000004.o: in function `_cgo_26061493d47f_C2func_getaddrinfo':
/tmp/go-build/cgo-gcc-prolog:58: warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
因为 glibc 依赖了 libnss ,libnss 支持不同的 provider,必须被 dynamic link.

所以这里只有使用 musl 替换 glibc 了。librdkafka 和 golang 的封装 confluent-kafka-go 都支持 musl 构建。只要在构建的时候指定 --tags musl 即可。alpine 是基于 musl 的发行版,这里直接使用 alpine Linux 进行构建。

然后指定使用外部 ld,指定 flags 使用 -static,编译出来的 binary 就完全是静态链接的了。编译过程如下:

$ docker run -it -v $(pwd):/workspace -v /Users/xintao.lai/.netrc:/root/.netrc golang:alpine3.14
/go $ cd /workspace/
/workspace $ apk add git alpine-sdk
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/community/x86_64/APKINDEX.tar.gz
(1/37) Installing fakeroot (1.25.3-r3)
(2/37) Installing openssl (1.1.1l-r0)
(3/37) Installing libattr (2.5.1-r0)
(4/37) Installing attr (2.5.1-r0)
(5/37) Installing libacl (2.2.53-r0)
(6/37) Installing tar (1.34-r0)
(7/37) Installing pkgconf (1.7.4-r0)
...
/workspace $ go build -ldflags "-linkmode external -extldflags '-static'" -tags musl ./cmd/spex
/workspace $ ldd spex
/lib/ld-musl-x86_64.so.1: spex: Not a valid dynamic program
$ docker run -it -v $(pwd):/workspace -v /Users/xintao.lai/.netrc:/root/.netrc golang:alpine3.14
/go $ cd /workspace/
/workspace $ apk add git alpine-sdk
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.14/community/x86_64/APKINDEX.tar.gz
(1/37) Installing fakeroot (1.25.3-r3)
(2/37) Installing openssl (1.1.1l-r0)
(3/37) Installing libattr (2.5.1-r0)
(4/37) Installing attr (2.5.1-r0)
(5/37) Installing libacl (2.2.53-r0)
(6/37) Installing tar (1.34-r0)
(7/37) Installing pkgconf (1.7.4-r0)
...
/workspace $ go build -ldflags "-linkmode external -extldflags '-static'" -tags musl ./cmd/spex
/workspace $ ldd spex
/lib/ld-musl-x86_64.so.1: spex: Not a valid dynamic program

静态编译 CGO 的依赖可以参考这篇教程:Using CGO bindings under Alpine, CentOS and Ubuntu 和这个例子:go-static-linking.

shadowmint/go-static-linking

https://github.com/shadowmint/go-static-linking

Go语言涉及CGO的交叉编译(跨平台编译)解决办法

在实际开发中,我们往往要使用交叉编译[1]来实现跨平台部署,这两天查阅资料,发现之所以TensorFlow跨平台编译失败,是因为使用了cgo来调用TensorFlow C。今天我们就来深入了解下,怎么处理带CGO的交叉编译。

在没有CGO调用的情况下,交叉编译只需带上三个参数便可以实现

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build
或者加上可选参数

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags ‘-s -w --extldflags “-static -fpic”’ main.go
CGO_ENABLED 这个参数默认为1,开启CGO。需要指定为0来关闭,因为CGO不支持交叉编译。

GOOS 和 GOARCH 用来指定要构建的平台为Linux

可选参数-ldflags 是编译选项:

-s -w 去掉调试信息,可以减小构建后文件体积,
–extldflags “-static -fpic” 完全静态编译[2],这样编译生成的文件就可以任意放到指定平台下运行,而不需要运行环境配置。
显然对于带CGO的交叉编译,CGO_ENABLED必须开启。这也就需要辅助编译器来帮我们实现交叉编译了。

如果你是mac平台,可以用这个工具 FiloSottile/musl-cross/musl-cross 直接通过brew安装就可以使用

brew install FiloSottile/musl-cross/musl-cross
安装成功后,有多种编译器可以帮我们实现交叉编译,使用时只需在编译对应参数下指定就可以了。

FiloSottile/musl-cross/musl-cross
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 CC=x86_64-linux-musl-gcc CGO_LDFLAGS=“-static” go build -a -v
通过CC=x86_64-linux-musl-gcc 来指定GCC编译器。而CGO_LDFLAGS="-static"来指定CGO部分的编译为静态编译。

这样就可以实现带CGO的交叉编译啦。如果你是其他平台,也可以通过跨平台编译的工具musl 下载对应平台工具,这里有支持多平台实现工具。下载解压,将解压好的目录下 bin 文件路径,放到PATH环境变量中就可以啦。

brew install FiloSottile/musl-cross/musl-cross
安装过程中出现如下问题:
Error: An exception occurred within a child process: NoMethodError: undefined method `path’ for nil:NilClass Did you mean? paths
解决方案
使用命令brew update-reset升级Homebrew即可[1]

References
[1] https://discourse.brew.sh/t/homebrew-installation-fail/7439

再次安装brew install FiloSottile/musl-cross/musl-cross
提示如下:

lizhongsu@lizhongdeMBP ~ % brew install FiloSottile/musl-cross/musl-cross
==> Downloading https://ghcr.io/v2/homebrew/portable-ruby/portable-ruby/blobs/sha256:b065e5e3783954f3e65d8d3a6377ca51649bfcfa21b356b0dd70490f74c6bd86
##################################################################################################################################################################################################### 100.0%
==> Pouring portable-ruby-2.6.3_2.yosemite.bottle.tar.gz
Error:
  homebrew-core is a shallow clone.
To `brew update`, first run:
  git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow
This command may take a few minutes to run due to the large size of the repository.
This restriction has been made on GitHub's request because updating shallow
clones is an extremely expensive operation due to the tree layout and traffic of
Homebrew/homebrew-core and Homebrew/homebrew-cask. We don't do this for you
automatically to avoid repeatedly performing an expensive unshallow operation in
CI systems (which should instead be fixed to not use shallow clones). Sorry for
the inconvenience!
==> Installing musl-cross from filosottile/musl-cross
==> Downloading https://f001.backblazeb2.com/file/filippo-public/musl-cross-0.9.9_1.catalina.bottle.tar.gz
######################################################################## 100.0%
==> Pouring musl-cross-0.9.9_1.catalina.bottle.tar.gz
  /usr/local/Cellar/musl-cross/0.9.9_1: 1,851 files, 246.3MB
==> `brew cleanup` has not been run in 30 days, running now...
Removing: /Users/lizhongsu/Library/Caches/Homebrew/autoconf--2.69.catalina.bottle.4.tar.gz... (871.6KB)
Removing: /Users/lizhongsu/Library/Caches/Homebrew/automake--1.16.2.catalina.bottle.tar.gz... (948.7KB)
Removing: /Users/lizhongsu/Library/Logs/Homebrew/autoconf... (64B)
Removing: /Users/lizhongsu/Library/Logs/Homebrew/automake... (64B)
Removing: /Users/lizhongsu/Library/Logs/Homebrew/telnet... (64B)
Pruned 2 symbolic links from /usr/local

有一个错误提示:
Error:
homebrew-core is a shallow clone.
To brew update, first run:
git -C /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core fetch --unshallow
This command may take a few minutes to run due to the large size of the repository.
This restriction has been made on GitHub’s request because updating shallow
clones is an extremely expensive operation due to the tree layout and traffic of
Homebrew/homebrew-core and Homebrew/homebrew-cask. We don’t do this for you
automatically to avoid repeatedly performing an expensive unshallow operation in
CI systems (which should instead be fixed to not use shallow clones). Sorry for
the inconvenience!

暂时没有进行根据提示进行处理,尝试编译,成功!
执行命令
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 CC=x86_64-linux-musl-gcc CGO_LDFLAGS=“-static” go build -a -v

成功交叉编译完成Go在linux下可运行文件。

 类似资料: