组装完整的工具链

简介

Clang 只是 C 家族编程语言完整工具链中的一环。为了组装完整的工具链,需要额外的工具和运行时库。Clang 被设计为与目标平台上现有的工具和库互操作,LLVM 项目为许多这些组件提供了替代方案。

本文档描述了完整工具链中必需的和可选的组件,以及它们在哪里可以找到,以及每个选项的受支持版本和限制。

警告

本文档目前描述了在具有 GCC 兼容 clang 驱动程序的类 POSIX 操作系统上的 Clang 配置。当使用 MSVC 兼容的 clang-cl 驱动程序将目标设为 Windows 时,某些细节会有所不同。

工具

C 家族编程语言的完整编译通常涉及以下工具管道,其中一些工具在某些编译中被省略

  • 预处理器:执行 C 预处理器的操作:扩展 #include 和 #define。 -E 标志指示 Clang 在此步骤后停止。

  • 解析:解析和语义分析源语言并构建源级中间表示(“AST”),生成 预编译头文件 (PCH),序言,或 预编译模块文件 (PCM),具体取决于输入。 -precompile 标志指示 Clang 在此步骤后停止。当输入是头文件时,这是默认行为。

  • IR 生成:将源级中间表示转换为优化器特定的中间表示 (IR);对于 Clang,这是 LLVM IR。 -emit-llvm 标志指示 Clang 在此步骤后停止。如果与 -S 结合使用,Clang 将生成文本 LLVM IR;否则,它将生成 LLVM IR 字节码。

  • 编译器后端:将中间表示转换为特定于目标的汇编代码。 -S 标志指示 Clang 在此步骤后停止。

  • 汇编器:将特定于目标的汇编代码转换为特定于目标的机器代码目标文件。 -c 标志指示 Clang 在此步骤后停止。

  • 链接器:将多个目标文件组合成一个映像(共享对象或可执行文件)。

Clang 提供了除链接器之外的所有这些部分。当多个步骤由同一个工具执行时,这些步骤通常会融合在一起,以避免创建中间文件。

当给定上述步骤之一的输出作为输入时,将跳过较早的步骤(例如,.s 文件输入将被汇编和链接)。

Clang 驱动程序可以与 -### 标志一起调用(此参数需要在大多数 shell 下转义),以查看它将为上述步骤运行哪些命令,而不实际运行它们。 -v(详细)标志将打印命令,并运行它们。

Clang 前端

Clang 前端 (clang -cc1) 用于编译 C 家族语言。前端的命令行界面被认为是实现细节,故意没有外部文档,并且可能会在不通知的情况下发生更改。

其他语言的语言前端

Clang 可以使用以非 C 家族语言编写的输入。在这种情况下,将使用外部工具来编译输入。当前支持的语言是

  • Ada (-x ada.ad[bs])

  • Fortran (-x f95.f.f9[05].for.fpp,不区分大小写)

  • Java (-x java)

在每种情况下,都会调用 GCC 来编译输入。

汇编器

Clang 可以使用 LLVM 的集成汇编器或外部特定于系统的工具(例如,GNU 操作系统上的 GNU 汇编器)从汇编代码生成机器代码。默认情况下,Clang 在所有支持它的目标上使用 LLVM 的集成汇编器。如果您希望改用系统汇编器,请使用 -fno-integrated-as 选项。

链接器

Clang 可以配置为使用以下几种不同的链接器

  • GNU ld

  • GNU gold

  • LLVM 的 lld

  • MSVC 的 link.exe

链接时优化由 lld 原生支持,并且在使用 gold 时通过 链接器插件 支持。

默认链接器因目标而异,可以通过 -fuse-ld=<linker name> 标志覆盖。

运行时库

需要许多不同的运行时库来为 C 家族程序提供不同级别的支持。Clang 将隐式链接每个运行时库的适当实现,该实现是根据目标默认值选择或通过 --rtlib=--stdlib= 标志显式选择。

隐式链接的库集取决于语言模式。因此,在链接 C++ 程序时,应该使用 clang++,以确保提供 C++ 运行时。

注意

这些组件可能存在下面没有描述的其他实现。请告知我们这些其他实现与 Clang 的配合程度,以便将其添加到此列表中!

编译器运行时

编译器运行时库提供了由编译器隐式调用的函数的定义,以支持底层硬件不支持的操作(例如,128 位整数乘法),以及操作的内联扩展被认为不适合的情况。

默认运行时库是特定于目标的。对于 GCC 是主要编译器的目标,Clang 目前默认使用 libgcc_s。在大多数其他目标上,默认使用 compiler-rt。

compiler-rt (LLVM)

LLVM 的编译器运行时库 提供了一套完整的运行时库函数,包含 Clang 将隐式调用的所有函数,位于 libclang_rt.builtins.<arch>.a 中。

您可以指示 Clang 使用 compiler-rt,方法是使用 --rtlib=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 使用 libgcc_s,方法是使用 --rtlib=libgcc 标志。这并非在所有平台上都支持。

原子库

如果您的程序使用了原子操作,并且编译器无法将所有操作直接降低到机器指令(因为要么没有已知的合适的机器指令,要么操作数已知不适合对齐),则将生成对运行时库 __atomic_* 函数的调用。这样的程序需要包含这些原子函数的运行时库。

compiler-rt (LLVM)

compiler-rt 包含原子库的实现。

libatomic (GNU)

libgcc_s 没有提供原子库的实现。相反,可以使用 GCC 的 libatomic 库 在使用 libgcc_s 时提供这些库。

注意

Clang 目前在使用 libgcc_s 时不会自动链接到 libatomic。您可能需要手动添加 -latomic 来支持在使用非本地原子操作时(如果您看到引用 __atomic_* 函数的链接错误)此配置。

展开库

展开库提供了一系列 _Unwind_* 函数,实现了 Itanium C++ ABI 的语言中立堆栈展开部分 (一级)。它是 C++ ABI 库的依赖项,有时也是其他运行时的依赖项。

libunwind (LLVM)

LLVM 的解旋器库是 llvm-project git 仓库的一部分。要构建它,请在 cmake 调用中传递 -DLLVM_ENABLE_RUNTIMES=libunwind

如果使用 libc++abi,您可能需要将其配置为使用 libunwind 而不是 libgcc_s,方法是将 -DLIBCXXABI_USE_LLVM_UNWINDER=YES 传递给 cmake。如果 libc++abi 被配置为使用 libunwind 的某个版本,那么该库将被隐式链接到链接到 libc++abi 的二进制文件中。

libgcc_s (GNU)

libgcc_s 具有集成的解旋器,不需要提供外部解旋器库。

libunwind (nongnu.org)

这是 libunwind 规范的另一个实现。请参阅 libunwind (nongnu.org)

libunwind (PathScale)

这是 libunwind 规范的另一个实现。请参阅 libunwind (pathscale)

Sanitizer 运行时

Clang 的 sanitizers (-fsanitize=...) 添加的检测会隐式地调用运行时库,以维护有关程序执行的辅助状态,并在检测到问题时发出诊断消息。

这些运行时库的唯一支持的实现由 LLVM 的 compiler-rt 提供,并且该库的相关部分 (libclang_rt.<sanitizer>.<arch>.a) 在使用 -fsanitize=... 标志链接时将被隐式链接。

C 标准库

Clang 支持各种各样的 C 标准库 实现。

C++ ABI 库

C++ ABI 库提供了 Itanium C++ ABI 库部分的实现,涵盖了 Itanium C++ ABI 主要文档中的支持功能异常处理支持的 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++ 的副本。

注意

Clang 目前不会在静态链接 libstdc++ 时自动链接到 libsupc++。在使用 -static-static-libstdc++ 时,您可能需要手动添加 -lsupc++ 来支持此配置。

libcxxrt (PathScale)

这是 Itanium C++ ABI 规范的另一个实现。请参阅 libcxxrt

C++ 标准库

Clang 支持使用 LLVM 的 libc++ 或 GCC 的 libstdc++ 实现的 C++ 标准库

libc++ (LLVM)

libc++ 是 LLVM 对 C++ 标准库的实现,旨在成为从 C++11 开始的 C++ 标准的完整实现。

您可以使用 -stdlib=libc++ 标志指示 Clang 使用 libc++。

libstdc++ (GNU)

libstdc++ 是 GCC 对 C++ 标准库的实现。Clang 支持 libstdc++ 4.8.3(2014 年 5 月 22 日发布)及更高版本。历史上,Clang 为在 libstdc++ 中发现的问题实现了变通方法,随着 libstdc++ 的修复版本变得足够旧,这些变通方法将被删除。

您可以使用 -stdlib=libstdc++ 标志指示 Clang 使用 libstdc++。