如何使用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主机(如今大多数英特尔和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将在其上运行的系统的三元组 - 该标志的命名基于autoconf的构建/主机/目标命名法。(此标志隐式设置其他默认值,例如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的三元组。

  • '--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(在本例中默认为fpu=VFP3以及NEON),并强制使用硬浮点ABI。如果您使用Clang作为交叉编译器,则还必须设置--sysroot以确保它选择正确的链接器。

使用Clang时,重要的是您选择的三元组必须与GCC三元组和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

这将在安装目录中创建一个sysroot。然后,您可以将该目录打包成一个带有完整三元组名称(以便于识别)的二进制文件,例如:

$ 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技巧。