使用 Clang/LLVM 交叉編譯 Clang/LLVM

簡介

本文檔包含在主機上構建針對另一個平台的 LLVM 和 Clang 的資訊。

有關如何將 Clang 用作交叉編譯器的更多資訊,請查看 https://clang.llvm.org/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 的系統的三元組 - 該標誌是根據 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 等所在的位置)的位置。

  • 適當使用 -I-L,具體取決於交叉 GCC 的安裝方式以及函式庫和標頭檔的位置。

您可能還想設定 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 時,選擇與 GCC 三元組和系統根目錄相同的目標三元組非常重要。這將使 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

如果您將該壓縮檔複製到目標板,您就可以使用它來執行測試套件,例如,請遵循 https://llvm.dev.org.tw/docs/lnt/quickstart.html 的指南,在測試目錄中解壓縮壓縮檔,並使用以下選項

$ ./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 中,其中 N 是您板子上 CPU 的數量。此外,clang 的路徑必須是絕對路徑,因此您需要使用上面提到的 pwd 技巧。