装配一个完整的工具链
本文为译文,点击 此处查看原文。
1. 介绍
Clang 只是 C 家族编程语言完整工具链(tool chain)中的一个组件。为了装配一个完整的工具链,需要额外的工具和运行时库。Clang 被设计为与用于其目标平台的现有工具和库进行交互操作,并且 LLVM 项目为许多这些组件提供了替代方案。
本文档描述了完整工具链中所需的和可选的组件,在哪里可以找到它们,以及每个选项支持的版本和限制。
警告
本文档目前描述了使用 GCC-compatible 的clang
驱动程序在类 POSIX 操作系统上的 Clang 配置。当使用 MSVC-compatible 的clang-cl
驱动程序在目标系统Windows上时,有些细节是不同的。
2. 工具
C 家族编程语言的完整编译通常涉及以下工具管道,其中一些在某些编译中被省略:
预处理器(Preprocessor)
:执行 C 预处理器的操作:展开#include
和#defines
。-E
标志指示 Clang 在此步骤之后停止。解析(Parsing)
:根据输入,解析和语义分析源语言,并构建一个源代码级中间表示(“AST”),生成预编译头文件(precompiled header,PCH)、序言(preamble)或预编译模块文件(precompiled module file,PCM)。-precompile
标志指示 Clang 在此步骤之后停止。当输入是一个头文件时,这是默认值。IR生成(IR generation)
:将源代码级中间表示转换为一个特定于优化器(optimizer)的中间表示(IR);对于 Clang,这是LLVM IR。-emit-llvm
标志指示 Clang 在此步骤之后停止。如果与-S
结合,Clang 将产生文本形式的 LLVM IR;否则,将产生 LLVM IR 字节码。编译器后端(Compiler backend)
:这将中间表示转换为特定于目标的汇编代码。-S
标志指示 Clang 在此步骤之后停止。汇编器(Assembler)
:将特定于目标的汇编代码转换为特定于目标的机器码目标文件。-c
标志指示 Clang 在此步骤之后停止。链接器(Linker)
:它将多个目标文件组合成一个映像(一个共享对象或一个可执行文件)。
Clang提供了除链接器之外的所有这些部分。当同一工具执行多个步骤时,通常将这些步骤合并在一起,以避免创建中间文件。
当给定上述步骤之一的输出作为输入时,将跳过前面的步骤(例如,输入一个.s
文件将会进行汇编和链接)。
可以使用-###
标记(在大多数 shell 中需要转义这个参数)调用 Clang 驱动程序,以查看将为上述步骤运行哪些命令,在不运行这些命令的情况下。除了运行命令外,-v
(verbose)标志还将打印命令。
2.1 Clang 前端
Clang 前端(clang -cc1
)用于编译 C 家族语言。前端的命令行接口被认为是一个实现细节,故意没有外部文档,并且可以在不注意的情况下进行更改。
2.2 其他语言的语言前端
Clang可以提供用非 C 家族语言编写的输入。在这种情况下,将使用一个外部工具编译输入。目前支持的语言有:
- Ada (
-x ada
、.ad[bs]
) - Fortran (
-x f95
、.f
、.f9[05]
、.for
、.fpp
、不区分大小写) - Java (
-x java
)
在每种情况下,都会调用 GCC 来编译输入。
2.3 汇编器
Clang 既可以使用 LLVM 的集成汇编器,也可以使用外部特定于系统的工具(例如,GNU 操作系统上的 GNU 汇编器),以从汇编代码生成机器码。默认情况下,Clang 在支持 LLVM 的所有目标上使用 LLVM 的集成汇编器。如果想使用系统汇编器,请使用-fno-integrated-as
选项。
2.4 链接器
Clang可以配置为使用几个不同的链接器其中一个:
GNU ld
GNU gold
LLVM lld
MSVC link.exe
lld 原生支持链接时优化,使用 gold 时通过一个链接器插件以支持链接时优化。
默认链接器在不同的目标之间是不同的,可以通过-fuse-ld=<linker name>
标志覆盖它。
3. 运行时库
需要许多不同的运行时库来为 C 家族程序提供不同的支持层。Clang 将隐式地链接每个运行时库的一个适当实现,这些实现是根据目标默认值选择的,或者由 --rtlib=
和 --stdlib=
标志显式地选择。
隐式链接库的集合依赖于语言模式。因此,在链接 C++ 程序时应该使用clang++
,以确保提供了 C++ 运行时。
注意
对于这些组件,可能存在下面没有描述的其他实现。请让我们知道这些其他实现与 Clang 的工作效果如何,以便将它们添加到这个列表中!
3.1 编译器运行时(Compiler runtime)
编译器运行时库提供编译器隐式调用的函数的定义,以支持底层硬件不支持的操作(例如,128位整数乘法),以及认为操作的内联展开不合适的地方。
默认的运行时库是特定于目标的。对于 GCC 是主要编译器的目标,Clang 目前默认使用libgcc_s
。在大多数其他目标上,默认情况下使用compiler-rt
。
compiler-rt (LLVM)
LLVM的编译器运行时库提供了一组完整的运行时库函数,其中包含 Clang 将隐式调用的所有函数,在libclang_rt.builtins.<arch>.a
中。
您可以指示 Clang 使用带有--rtlib=compiler-rt
标志的compiler-rt
,并非所有平台都支持此功能。
如果使用libc++
和/或libc++abi
,您可能需要将它们配置为使用compiler-rt
而不是libgcc_s
,方法是将-DLIBCXX_USE_COMPILER_RT=YES
和/或-DLIBCXXABI_USE_COMPILER_RT=YES
传递给cmake
。否则,您可能会将两个运行时库链接到您的程序中(这通常是无害的,但很浪费)。libgcc_s (GNU)
GCC的运行时库可以用来代替compiler-rt
。但是,它缺少几个LLVM可能发出引用的函数,特别是在使用Clang的内置函数家族的__builtin_*_overflow
时。
您可以指示 Clang 使用带有--rtlib=libgcc
标志的libgcc_s
,并非所有平台都支持此功能。
3.2 原子库(Atomics library)
如果您的程序使用了原子操作,编译器无法直接降低他们到机器指令(因为没有合适的机器指令或操作数不知道适当对齐),将会生成对一个运行时库__atomic_*
函数的一次调用。这些程序需要一个包含这些原子函数的运行时库。
compiler-rt (LLVM)
compiler-rt 包含一个原子库的实现。libatomic (GNU)
libgcc_s 不提供原子库的实现。相反,GCC 的 libatomic library 可以在使用 libgcc_s 时提供这些原子库。
注意
当使用libgcc_s
时,Clang当前不会自动链接到libatomic
。在使用非本机原子操作时(如果您看到引用了__atomic_*
函数的链接错误),可能需要手动添加-latomic
来支持此配置。
3.3 Unwind 库
unwind
库提供了一系列_Unwind_*
函数,实现了Itanium C++ ABI
(第I级)的语言无关的堆栈unwind部分,它是 C++ ABI 库的依赖项,有时是其他运行时的依赖项。
libunwind (LLVM)
LLVM 的 unwinder 库是 llvm-project git 存储库的一部分。要构建它,请将-DLLVM_ENABLE_PROJECTS=libunwind
传递给 cmake 调用。
如果使用libc++abi
,您可能需要将其配置为使用libunwind
而不是libgcc_s
,方法是将-DLIBCXXABI_USE_LLVM_UNWINDER=YES
传递给 cmake。如果将libc++abi
配置为使用某个版本的libunwind
,则该库将会被隐式链接到二进制文件,这些二进制文件链接到libc++abi。libgcc_s (GNU)
libgcc_s 有一个集成的 unwinder,不需要提供外部的 unwind 库。libunwind (nongnu.org)
这是 libunwind 规范的另一个实现。请参阅 libunwind (nongnu.org)。libunwind (PathScale)
这是 libunwind 规范的另一个实现。请参阅 libunwind (pathscale)。
3.4 Sanitizer 运行时
Clang 的sanitizers
(-fsanitize=…)添加的工具隐式地调用一个运行时库,以便维护程序执行的侧状态,并在检测到问题时发出诊断消息。
这些运行时的唯一支持实现由 LLVM 的compiler-rt
提供,以及这个库(libclang_rt.<sanitizer>.<arch>.a
) 的相关部分将会被链接当使用一个-fsanitize=...
标志链接时。
3.5 C 标准库
Clang 支持多种 C 标准库实现。
3.6 C++ ABI 库
C++ ABI 库
提供了Itanium C++ ABI
库部分的实现,包括 main Itanium c++ ABI文档中的支持功能和异常处理支持的 Level II。在编译 C++ 代码时,Clang 隐式地生成对这个库中函数和对象的引用。
虽然有可能使用libstdc++
链接 C++ 代码和使用libc++
链接代码一起到相同的程序(只要你不要试图通过 C++ 标准库对象的边界),它通常在一个程序不大可能有超过一个 C++ ABI 库。
Clang 使用的 C++ ABI 库的版本将是所选 C++ 标准库所链接的版本。有几种实现可用:
libc + + abi (LLVM)
libc++abi 是 LLVM 对该规范的实现。libsupc++ (GNU)
libsupc++
是 GCC 对该规范的实现。但是,只有在静态链接libstdc++
时才使用这个库。libstdc++
的动态库版本包含libsupc++
的一个副本。注意
当静态链接libstdc++
时,Clang当前不会自动链接到libatomic
。在使用-static
或-static-libstdc++
时,可能需要手动添加-lsupc++
来支持此配置。libcxxrt (PathScale)
这是 Itanium C++ ABI 规范的另一个实现。请参阅 libcxxrt。
3.7 C++ 标准库
Clang 支持使用 LLVM 的libc++
或 GCC 的libstdc++
C++标准库实现。
libc++ (LLVM)
libc++ 是 LLVM 的 C++ 标准库实现,旨在从 C++ 11 开始成为全面的 C++ 标准实现。
您可以指示 Clang 用-stdlib=libc++
标志来使用 libc++。libstdc++ (GNU)
libstdc++ 是 GCC 的 C++ 标准库实现。Clang 支持各种版本的 libstdc++,从4.2版本开始,并将隐式地处理旧版本libstdc++中的一些bug。
您可以指示 Clang 用-stdlib=libstdc++
标志来使用 libstdc++。