zig[1] 是一门新的底层语言,去年已经有安全人员公开用 zig 开发的攻防项目,大家应该都有所耳闻。
但很多人不知道的是,zig 还自带了一套完整的 cc / c++ 编译链,并且具备强大的交叉编译能力,可以无缝替代 gcc / clang。
zig cc 可以用来交叉编译原生 C / C++,或是依赖 cc 编译链的其它语言。
Golang 的一大特点是交叉编译,但一旦用到 CGO,交叉编译就会麻烦很多。以 HackBrowserData 举例,其解析浏览器本地数据存储时依赖 sqlite3,而 Golang 最流行的 sqlite3 库 go-sqlite3[2] 是一个典型的 CGO 项目。
> GOOS=windows GOARCH=amd64 CGO_ENABLED=1 go build
# runtime/cgo
gcc_libinit_windows.c:8:10: fatal error: 'windows.h' file not found
> GOOS=linux GOARCH=amd64 CGO_ENABLED=1 go build
# runtime/cgo
linux_syscall.c:67:13: error: implicit declaration of function 'setresgid' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
linux_syscall.c:67:13: note: did you mean 'setregid'?
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/unistd.h:593:6: note: 'setregid' declared here
linux_syscall.c:73:13: error: implicit declaration of function 'setresuid' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
linux_syscall.c:73:13: note: did you mean 'setreuid'?
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/unistd.h:595:6: note: 'setreuid' declared here
因为 CGO 默认会用当前系统的 cc 编译链,所以这种情况的解决办法是安装对应架构的编译链,比如 MinGW:
> CC="x86_64-w64-mingw32-gcc" CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build
> file hack-browser-data.exe
hack-browser-data.exe: PE32+ executable (console) x86-64, for MS Windows
需要交叉编译到多少架构,就需要安装多少编译链。
使用 zig cc 的话,只需要:
> CC="zig cc -target x86_64-linux-musl" CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build
> file hack-browser-data
hack-browser-data: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, Go BuildID=BAyS6mMhA1gWvDMsJucZ/Ezu43jyXvCI5lsSn4DrY/uwy290WsjU7IqN0A-Jhn/6Ubko4EhzpMvTPLH4NgH, with debug_info, not stripped
> CC="zig cc -target aarch64-linux-musl" CGO_ENABLED=1 GOOS=linux GOARCH=arm64 go build
> file hack-browser-data
hack-browser-data: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, Go BuildID=uhoy_ughFoJ1WKdpH5RQ/BxRyuA71TrSWu4TV56MJ/NRIUpm74LYrbhaGlltMr/LjXFS_iq2QS0Wbi-MCUj, with debug_info, not stripped
> CC="zig cc -target x86_64-windows" CGO_ENABLED=1 GOOS=windows GOARCH=amd64 go build
> file hack-browser-data.exe
hack-browser-data.exe: PE32+ executable (console) x86-64, for MS Windows
> CC="zig cc -target i386-windows" CGO_ENABLED=1 GOOS=windows GOARCH=386 go build
> file hack-browser-data.exe
hack-browser-data.exe: PE32 executable (console) Intel 80386, for MS Windows
> CC="zig cc -target aarch64-windows" CGO_ENABLED=1 GOOS=windows GOARCH=arm64 go build
> file hack-browser-data.exe
hack-browser-data.exe: PE32+ executable (console) Aarch64, for MS Windows
相比安装多种架构的 cc 编译链,zig 只需要下载一个 45 MB 的单压缩文件,无需任何依赖和配置。
zig 编译器具体做了哪些工作可以阅读开发组的文章 zig cc: a Powerful Drop-In Replacement for GCC/Clang[3] 。
至于稳定性,zig cc 并不是从头做起,而是建立在 clang 之上的。并且 Uber 已经将 zig cc 用作编译 C/C++ 代码 How Uber Uses Zig[4],对于普遍规模较小的安全工具来说,基本不用担心稳定性的问题。
[1]
zig: https://ziglang.org/[2]
go-sqlite3: https://github.com/mattn/go-sqlite3[3]
zig cc: a Powerful Drop-In Replacement for GCC/Clang: https://andrewkelley.me/post/zig-cc-powerful-drop-in-replacement-gcc-clang.html[4]
How Uber Uses Zig: https://jakstys.lt/2022/how-uber-uses-zig/