compiler-rt
compiler-rt 提供了一个中间层,用来不支持某些语言特征的机器上执行软件模拟运算。例如 compiler-rt 的 __muldi3
函数能够在不支持 64 位整数的计算机上执行 64 位乘法。
对于 GNU 而言,这个库是 libgcc 。
大多数 C 编辑器会带有这个库。比如在 Ununtu 上, build_essential 包,你就可以在 /lib/x86_64-linux-gnu/libgcc_s.so.1
发现它。
如果你下载了 clang+llvm,你会发现随包附带的并没有这个库,这是因为 clang 依赖于系统提供的 libgcc,其中一个原因是这个库是系统强相关的。
和 clang 的策略不同。zig 参考 llvm 自己实现了一套 compiler-rt 库。并将其附带在二进制包中,然后依赖于缓存系统而无需每次都从源码构建。
libc
libc 是一个 C 语言库,它提供了 C 语言的标准库。对于 FreeBSD 和 macOS 而言。libc 是一个系统库,提供了系统调用接口。而对于 Linux 和 Windows 而言,libc 是可选的,并存在多个可相互替代的 libc 实现。
zig 的 libc 参考了 musl 的实现,和 compiler-rt 的策略相同,通过附带源码的形式解决了交叉编译问题。
glibc
zig 通过预处理工具将 glibc 和 linux 的源码进行分类成平台相关代码和平台无关代码,然后生成了三个文件:
如果用户程序没有要求 glibc 版本,则 zig 首先通过查看自己的二进制文件来查找系统的 glibc,如果找不到就通过 /usr/bin/env 的 shebang 查找。最后连接到系统的 glibc 版本。
如果用户请求特定的 glibc 版本,那么 zig 会链接到一个 dummy 库上,然后在目标系统上动态查找符号。
最后还有 “C 运行时启动文件”:
Scrt1.o
crti.o
crtn.o
由于它们的 ABI 相当稳定,所以直接静态编译。
这样 zig 就不再与任何系统绑定了。
musl
zig 对 musl 的处理和 glibc 类似。但是由于 musl 支持静态链接,因此 zig 附带了 musl 的大部分源码。
交叉编译
和普通编译相同,交叉编译也涉及到了三个阶段:
预处理阶段
编译阶段
链接阶段
clang 预处理阶段天然支持交叉编译,第二个阶段用来将代码编译成汇编,通过 llvm-mc(LLVM 集成汇编处理器)进行支持。第三个阶段通过 lld(通用 linker)进行支持。
更准确来讲,lld 分为四个不同的 linker:
不论如何,这四个 linker 本身就是跨平台软件。 |
除非代码本身不依赖任何其它库(包括 libc),否则链接阶段还需要指定依赖的库所在的路径。这一过程通过 sysroot 受支持。sysroot 结构同 /usr
路径结构相同。包括了目标平台的头文件和库文件。
获取 sysroot 最简单的办法就是直接从目标平台上将相关文件夹拷贝过来:
|
clang 通过 target triple 来指明交叉编译的目标平台。
例如,将代码编译到 aarch64-pc-freebsd 上的命令为:
clang++ --target=aarch64-pc-freebsd --sysroot=$HOME/bsd_sysroot -fuse-ld=lld -stdlib=libc++ -o zpipe zpipe.cc -lz
对于 CMake + clang 的交叉编译。参阅 [1]