如何使用 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 等所在位置)。

  • 根據交叉 GCC 的安裝方式以及程式庫和標頭檔的位置,適當使用 -I-L

您可能還想設定 LLVM_NATIVE_TOOL_DIR 選項 - 指向包含用於建置主機的預先建置 LLVM 工具(llvm-tblgenclang-tblgen 等)的目錄,以便您在可用時重複使用它們。例如,-DLLVM_NATIVE_TOOL_DIR=<原生-llvm-建置路徑>/bin。如果未設定此選項(或目錄不包含所有需要的工具),則 LLVM 交叉建置將自動啟動巢狀建置以建置所需的工具。

CXX 標誌定義了目標、CPU(在本例中預設為帶有 NEON 的 fpu=VFP3)以及強制硬體浮點 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.dev.org.tw/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++

請記住在 lnt 中為您的板上的 CPU 數量添加 -jN 選項。此外,您的 clang 路徑必須是絕對路徑,因此您需要上面提到的 pwd 技巧。