使用Clang作为编译器 —— Assembling a Complete Toolchain


本文为译文,点击 此处查看原文。

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可以配置为使用几个不同的链接器其中一个:

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++。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值