如何使用 Clang/LLVM 交叉编译 Clang/LLVM

简介

本文档包含关于在主机上构建 LLVM 和 Clang,并以另一平台为目标的信息。

有关如何使用 Clang 作为交叉编译器的更多信息,请查看 https://clang.llvm.net.cn/docs/CrossCompilation.html

待办事项:将 MIPS 和其他平台添加到本文档。

从 x86_64 交叉编译到 ARM

在此用例中,我们将在基于 Debian 的 Linux 系统上使用 CMake 和 Ninja,从 x86_64 主机(目前大多数 Intel 和 AMD 芯片)交叉编译到硬浮点 ARM 目标(目前大多数 ARM 目标)。

您需要的软件包是

  • cmake

  • ninja-build(来自 Ubuntu 的 backports)

  • gcc-4.7-arm-linux-gnueabihf

  • gcc-4.7-multilib-arm-linux-gnueabihf

  • binutils-arm-linux-gnueabihf

  • libgcc1-armhf-cross

  • libsfgcc1-armhf-cross

  • libstdc++6-armhf-cross

  • libstdc++6-4.7-dev-armhf-cross

配置 CMake

有关如何为 LLVM/Clang 配置 CMake 的更多信息,请参阅 使用 CMake 构建 LLVM

您需要添加的 CMake 选项是

  • -DCMAKE_SYSTEM_NAME=<目标系统>

  • -DCMAKE_INSTALL_PREFIX=<安装目录>

  • -DLLVM_HOST_TRIPLE=arm-linux-gnueabihf

  • -DLLVM_TARGETS_TO_BUILD=ARM

注意:当设置 CMAKE_SYSTEM_NAME 时,CMAKE_CROSSCOMPILING 始终会自动设置。请勿在您的选项中放入 -DCMAKE_CROSSCOMPILING=TRUE

另请注意,LLVM_HOST_TRIPLE 指定交叉构建的 LLVM 将要运行的系统的 triple - 该标志是根据 autoconf build/host/target 命名法命名的。(此标志隐式设置其他默认值,例如 LLVM_DEFAULT_TARGET_TRIPLE。)

如果您正在使用 GCC 进行编译,则可以使用目标的架构选项,并且编译器驱动程序将检测到它需要的一切

  • -DCMAKE_CXX_FLAGS='-march=armv7-a -mcpu=cortex-a9 -mfloat-abi=hard'

但是,如果您正在使用 Clang,则驱动程序可能不是最新的,与您特定的 Linux 发行版、版本或 GCC 布局不符,因此您需要进行调整。

除了上面的选项,您还需要

  • --target=arm-linux-gnueabihf 或者您的交叉 GCC 的 triple。

  • '--sysroot=/usr/arm-linux-gnueabihf''--sysroot=/opt/gcc/arm-linux-gnueabihf' 或您的 GCC 的 sysroot 的位置(/lib、/bin 等所在的位置)。

  • 根据交叉 GCC 的安装方式以及库和头文件的位置,适当使用 -I-L

您可能还想设置 LLVM_NATIVE_TOOL_DIR 选项 - 指向一个包含预构建 LLVM 工具(llvm-tblgenclang-tblgen 等)的目录,以便在构建主机上使用,如果可用,允许您重用它们。例如 -DLLVM_NATIVE_TOOL_DIR=<path-to-native-llvm-build>/bin。如果未设置该选项(或目录不包含所有需要的工具),则 LLVM 交叉构建将自动启动嵌套构建以构建所需的工具。

CXX 标志定义了目标、cpu(在本例中默认为带有 NEON 的 fpu=VFP3)以及强制硬浮点 ABI。如果您正在使用 Clang 作为交叉编译器,您必须设置 --sysroot 以确保它选择正确的链接器。

使用 Clang 时,重要的是您选择的 triple 与 GCC triple 和 sysroot 完全相同。这将使 Clang 更容易找到正确的工具和包含头文件。但这并不意味着会找到所有头文件和库。您仍然需要使用 -I-L 来定位那些额外的文件,具体取决于您的发行版。

大多数时候,您想要的是拥有一个平台的原生编译器,而不是其他平台的。因此,编译所有后端几乎没有意义。因此,您还应该将 TARGETS_TO_BUILD 设置为仅构建您要针对的后端。

您必须设置 CMAKE_INSTALL_PREFIX,否则 ninja install 会将 ARM 二进制文件复制到您的根文件系统,这不是您想要的。

技巧

当前的 LLVM 中存在一些错误,需要在运行 CMake 之前进行一些调整

  1. 如果您正在使用 Clang 作为交叉编译器,则 LLVM ARM 后端存在一个问题,该问题在位置无关代码 (R_ARM_THM_MOVW_ABS_NC) 上产生绝对重定位,因此目前,您应该禁用 PIC

    -DLLVM_ENABLE_PIC=False
    

    这不是问题,因为 Clang/LLVM 库无论如何都是静态链接的,所以应该不会有太大影响。

  2. ARM 库不会安装在您的系统中。但是 CMake 准备步骤(检查依赖项)将检查主机库,而不是目标库。下面列出了一些依赖项,但您的项目可能具有更多依赖项,或者本文档可能已过时。您将在链接时看到错误,以此作为指示。

    基于 Debian 的发行版有一种添加 multiarch 的方法,该方法添加了一种新架构,并允许您为这些系统安装软件包。有关更多信息,请参见 https://wiki.debian.org/Multiarch/HOWTO

    但并非所有发行版都具有该功能,并且可能没有简单的方法以任何方式安装它们,因此您必须单独构建/下载它们。

    获取库的一种快速方法是从发行版存储库(如 Debian (http://packages.debian.org/jessie/))下载它们,并下载缺少的库。请注意,libXXX 将具有共享对象 (.so),而 libXXX-dev 将为您提供头文件和静态 (.a) 库。为了以防万一,请同时下载两者。

    您为 ARM 需要的是:libtinfozlib1glibxml2liblzma。在 Debian 存储库中,您将找到所有架构的下载。

    下载并解压缩所有 .deb 软件包后,将所有 .so.a 复制到一个目录,创建适当的符号链接(如果需要),并将相关的 -L-I 路径添加到上面的 -DCMAKE_CXX_FLAGS 中。

运行 CMake 和构建

最后,如果您正在使用您的平台编译器,请运行

$ cmake -G Ninja <source-dir> -DCMAKE_BUILD_TYPE=<type> <options above>

如果您正在使用 Clang 作为交叉编译器,请运行

$ CC='clang' CXX='clang++' cmake -G Ninja <source-dir> -DCMAKE_BUILD_TYPE=<type> <options above>

如果您的路径中有 clang/clang++,它应该可以正常工作,并且特殊的 Ninja 文件将在构建目录中创建。我强烈建议您在单独的构建目录中运行 cmake而不是在源代码树内部。

要构建,只需键入

$ ninja

它应该会自动找出您有多少个核心,哪些是需要构建的规则,并将构建整个内容。

您不能在此树上运行 ninja check-all,因为创建的二进制文件是针对 ARM 而不是 x86_64 的。

安装和使用

在 LLVM/Clang 成功构建后,您应该通过以下方式安装它

$ ninja install

这将在 install-dir 上创建一个 sysroot。然后,您可以将该目录压缩到一个具有完整 triple 名称的二进制文件中(为了便于识别),例如

$ ln -sf <install-dir> arm-linux-gnueabihf-clang
$ tar zchf arm-linux-gnueabihf-clang.tar.gz arm-linux-gnueabihf-clang

例如,如果您将该 tarball 复制到您的目标板,您将能够使用它来运行测试套件。请遵循 https://llvm.net.cn/docs/lnt/quickstart.html 上的指南,在测试目录中解压缩 tarball,并使用选项

$ ./sandbox/bin/python sandbox/bin/lnt runtest nt \
    --sandbox sandbox \
    --test-suite `pwd`/test-suite \
    --cc `pwd`/arm-linux-gnueabihf-clang/bin/clang \
    --cxx `pwd`/arm-linux-gnueabihf-clang/bin/clang++

记住将 -jN 选项添加到 lnt 中,以匹配您板上的 CPU 数量。此外,您的 clang 路径必须是绝对路径,因此您需要上面提到的 pwd 技巧。