如何為 Arm 交叉編譯 Compiler-rt Builtins

簡介

本文档包含從 x86_64 Linux 機器為 Arm 目標建置和測試 compiler-rt 的 builtins 部分的資訊。

雖然本文檔重點在 Arm 和 Linux,但一般原則應適用於 compiler-rt 支援的其他目標。 歡迎針對其他目標的進一步貢獻。

本文檔中的說明依賴於 LLVM 外部的函式庫和程式,安裝和配置這些依賴項的方法有很多種,因此您可能需要調整此處的說明以適應您自己的本地情況。

先決條件

在這個用例中,我們將在基於 Debian 的 Linux 系統上使用 cmake,從 x86_64 主機交叉編譯到硬體浮點 Armv7-A 目標。 我們將盡可能多地使用 LLVM 工具,但也可以使用 GNU 等效工具。

  • 用於 llvm-tools llvm-config LLVM/clang 建置

  • 支援 ARM 目標的 clang 可執行檔

  • compiler-rt 原始碼

  • qemu-arm 使用者模式 模擬器

  • arm-linux-gnueabihf 系統根目錄 (sysroot)

在本範例中,我們將使用 ninja。

有關 clang 和 LLVM 上的依賴關係的更多資訊,請參閱 https://compiler-rt.llvm.org/

有關取得 LLVM 和 compiler-rt 原始碼的資訊,請參閱 https://llvm.dev.org.tw/docs/GettingStarted.html。 請注意,入門指南將 compiler-rt 放置在 projects 子目錄中,但這不是必要的,如果您使用適用於 v6-M、v7-M 和 v7-EM 的 BaremetalARM.cmake 快取,則必須將 compiler-rt 放置在 runtimes 目錄中。

qemu-arm 應作為套件適用於您的 Linux 發行版。

最複雜的先決條件是滿足 arm-linux-gnueabihf 系統根目錄。 理論上,可以使用 Linux 發行版的多架構支援來滿足建置的依賴關係,但不幸的是,由於添加了 /usr/local/include,因此選擇了一些主機包含檔。 提供系統根目錄最簡單的方法是下載 arm-linux-gnueabihf 工具鏈。 可以在以下位置找到: * https://developer.arm.com/open-source/gnu-toolchain/gnu-a/downloads 適用於 gcc 8 及更高版本 * https://releases.linaro.org/components/toolchain/binaries/ 適用於 gcc 4.9 到 7.3

為 Arm 建置 compiler-rt builtins

我們將使用以下 cmake 選項執行 compiler-rt 的獨立建置。

  • path/to/compiler-rt

  • -G Ninja

  • -DCMAKE_AR=/path/to/llvm-ar

  • -DCMAKE_ASM_COMPILER_TARGET="arm-linux-gnueabihf"

  • -DCMAKE_ASM_FLAGS="build-c-flags"

  • -DCMAKE_C_COMPILER=/path/to/clang

  • -DCMAKE_C_COMPILER_TARGET="arm-linux-gnueabihf"

  • -DCMAKE_C_FLAGS="build-c-flags"

  • -DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld"

  • -DCMAKE_NM=/path/to/llvm-nm

  • -DCMAKE_RANLIB=/path/to/llvm-ranlib

  • -DCOMPILER_RT_BUILD_BUILTINS=ON

  • -DCOMPILER_RT_BUILD_LIBFUZZER=OFF

  • -DCOMPILER_RT_BUILD_MEMPROF=OFF

  • -DCOMPILER_RT_BUILD_PROFILE=OFF

  • -DCOMPILER_RT_BUILD_SANITIZERS=OFF

  • -DCOMPILER_RT_BUILD_XRAY=OFF

  • -DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON

  • -DLLVM_CONFIG_PATH=/path/to/llvm-config

build-c-flags 需要足以通過 C-make 編譯器檢查、編譯 compiler-rt,如果您要執行測試,則需要編譯和連結測試。 當使用 clang 進行交叉編譯時,我們需要傳遞足夠的資訊來為我們目標的 Arm 架構產生程式碼。 我們需要選擇 Arm 目標、選擇 Armv7-A 架構,並在使用 Arm 或 Thumb 指令之間進行選擇。 例如

  • --target=arm-linux-gnueabihf

  • -march=armv7a

  • -mthumb

當使用 GCC arm-linux-gnueabihf 工具鏈時,需要以下標誌來選取包含檔和函式庫

  • --gcc-toolchain=/path/to/dir/toolchain

  • --sysroot=/path/to/toolchain/arm-linux-gnueabihf/libc

在本範例中,我們將所有命令列選項添加到 CMAKE_C_FLAGSCMAKE_ASM_FLAGS 中。 有一些 cmake 標誌可以單獨傳遞其中一些選項,可以用於簡化 build-c-flags

  • -DCMAKE_C_COMPILER_TARGET="arm-linux-gnueabihf"

  • -DCMAKE_ASM_COMPILER_TARGET="arm-linux-gnueabihf"

  • -DCMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN=/path/to/dir/toolchain

  • -DCMAKE_SYSROOT=/path/to/dir/toolchain/arm-linux-gnueabihf/libc

一旦 cmake 完成,就可以使用 ninja builtins 建置 builtins

使用 qemu-arm 測試 compiler-rt builtins

要測試 builtins 函式庫,我們需要添加一些 cmake 標誌來啟用測試,並設定測試案例的編譯器和標誌。 我們還必須告訴 cmake 我們希望在 qemu-arm 上執行測試。

  • -DCOMPILER_RT_EMULATOR="qemu-arm -L /path/to/armhf/sysroot

  • -DCOMPILER_RT_INCLUDE_TESTS=ON

  • -DCOMPILER_RT_TEST_COMPILER="/path/to/clang"

  • -DCOMPILER_RT_TEST_COMPILER_CFLAGS="test-c-flags"

/path/to/armhf/sysroot 應與傳遞給 “build-c-flags” 中的 --sysroot 的路徑相同。

“test-c-flags” 需要包含目標、架構、gcc-toolchain、sysroot 和 arm/thumb 狀態。 諸如 CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN 之類的額外 cmake 定義在建置測試時不適用。 如果您已將所有這些都放在 “build-c-flags” 中,則可以重複使用。 如果您希望使用 lld 來連結測試,請添加 "-fuse-ld=lld

一旦 cmake 完成,就可以使用 ninja check-builtins 建置和執行測試

疑難排解

cmake try_compile 階段失敗

在早期階段,cmake 將嘗試編譯和連結一個簡單的 C 程式,以測試工具鏈是否正常工作。

如果沒有將 --sysroot=--gcc-toolchain= 選項傳遞給編譯器,則此階段通常會在連結時失敗。 檢查 CMAKE_C_FLAGSCMAKE_C_COMPILER_TARGET 標誌。

使用您的工具鏈在 cmake 外部建置一個簡單範例以確保其正常工作可能會很有用。 例如: clang --target=arm-linux-gnueabi -march=armv7a --gcc-toolchain=/path/to/gcc-toolchain --sysroot=/path/to/gcc-toolchain/arm-linux-gnueabihf/libc helloworld.c

Clang 使用主機標頭檔

在基於 debian 的系統上,可以為 arm-linux-gnueabi 和 arm-linux-gnueabihf 安裝多架構支援。 在許多情況下,當未提供 --gcc-toolchain=--sysroot= 時,clang 可以成功使用此多架構支援。 不幸的是,clang 在 /usr/include/arm-linux-gnueabihf 之前添加了 /usr/local/include,從而在編譯主機標頭檔時導致錯誤。

多架構支援不足以建置 builtins,您需要使用單獨的 arm-linux-gnueabihf 工具鏈。

沒有目標傳遞給 clang

如果沒有為 clang 指定目標,它通常會使用主機目標,這將無法理解 Arm 組合語言檔案,從而導致錯誤訊息,例如 error: unknown directive .syntax unified

您可以檢查錯誤訊息中的 clang 調用,以查看是否沒有 --target 或是否設定不正確。 原因通常是 CMAKE_ASM_FLAGS 不包含 --targetCMAKE_ASM_COMPILER_TARGET 不存在。

未給定 Arm 架構

--target=arm-linux-gnueabihf 將預設為 arm 架構 v4t,它無法組裝 synch_and_fetch 原始檔中使用的 barrier 指令。

原因通常是 CMAKE_ASM_FLAGS 中缺少 -march=armv7a

Compiler-rt 建置成功,但測試建置失敗

用於建置測試的標誌與用於建置 builtins 的標誌不同。 c 標誌由 COMPILER_RT_TEST_COMPILE_CFLAGS 提供,並且不應用 CMAKE_C_COMPILER_TARGETCMAKE_ASM_COMPILER_TARGETCMAKE_C_COMPILER_EXTERNAL_TOOLCHAINCMAKE_SYSROOT 標誌。

確保 COMPILER_RT_TEST_COMPILE_CFLAGS 包含所有必要的資訊。

針對其他目標的修改

Arm 軟體浮點目標

Arm 硬體浮點目標的說明可以用於軟體浮點目標,方法是用軟體浮點等效項替換系統根目錄和目標。 要使用的目標是

  • -DCMAKE_C_COMPILER_TARGET=arm-linux-gnueabi

根據您是否要使用浮點指令,您可能需要額外的 c-flags,例如 -mfloat-abi=softfp 用於使用浮點指令,以及 -mfloat-abi=soft -mfpu=none 用於軟體浮點模擬。

您將需要使用 arm-linux-gnueabi GNU 工具鏈進行軟體浮點。

AArch64 目標

Arm 的說明可以用於 AArch64,方法是用 AArch64 等效項替換系統根目錄、模擬器和目標。

  • -DCMAKE_C_COMPILER_TARGET=aarch64-linux-gnu

  • -DCOMPILER_RT_EMULATOR="qemu-aarch64 -L /path/to/aarch64/sysroot

CMAKE_C_FLAGS 和 COMPILER_RT_TEST_COMPILER_CFLAGS 可能還需要: "--sysroot=/path/to/aarch64/sysroot --gcc-toolchain=/path/to/gcc-toolchain"

Armv6-m、Armv7-m 和 Armv7E-M 目標

使用與 Armv7-A 類似的方法來建置和測試函式庫是可能的,但更困難。 主要問題是

  • 沒有適用於裸機系統的 qemu-arm 使用者模式模擬器。 可以使用 qemu-system-arm,但這在設定上要困難得多。

  • 用於編譯 compiler-rt 的目標具有後綴 -none-eabi。 這在 clang 中使用 BareMetal 驅動程式,預設情況下找不到通過 cmake 編譯器檢查所需的函式庫。

由於 compiler-rt 的 Armv6-M、Armv7-M 和 Armv7E-M 建置僅使用 Armv7-A 上支援的指令,因此我們仍然可以透過使用與 Armv7-A 相同的 qemu-arm 來獲得執行測試的大部分價值,方法是建置和執行 Armv7-A 的測試案例,但使用為 Armv6-M、Armv7-M 或 Armv7E-M 編譯的 builtins。 這將測試 builtins 是否可以連結到二進制檔案中並正確執行測試,但如果 builtins 使用 Armv7-A 上支援但不適用於 Armv6-M、Armv7-M 和 Armv7E-M 的指令,則不會捕獲。

為了使 cmake 編譯測試通過,您需要透過 CMAKE_CFLAGS 傳遞成功連結 cmake 測試所需的函式庫。 強烈建議您使用 3.6 或更高版本的 cmake,以便您可以使用 CMAKE_TRY_COMPILE_TARGET=STATIC_LIBRARY 來跳過連結步驟。

  • -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY

  • -DCOMPILER_RT_OS_DIR="baremetal"

  • -DCOMPILER_RT_BUILD_BUILTINS=ON

  • -DCOMPILER_RT_BUILD_SANITIZERS=OFF

  • -DCOMPILER_RT_BUILD_XRAY=OFF

  • -DCOMPILER_RT_BUILD_LIBFUZZER=OFF

  • -DCOMPILER_RT_BUILD_PROFILE=OFF

  • -DCMAKE_C_COMPILER=${host_install_dir}/bin/clang

  • -DCMAKE_C_COMPILER_TARGET="您的 *-none-eabi 目標"

  • -DCMAKE_ASM_COMPILER_TARGET="您的 *-none-eabi 目標"

  • -DCMAKE_AR=/path/to/llvm-ar

  • -DCMAKE_NM=/path/to/llvm-nm

  • -DCMAKE_RANLIB=/path/to/llvm-ranlib

  • -DCOMPILER_RT_BAREMETAL_BUILD=ON

  • -DCOMPILER_RT_DEFAULT_TARGET_ONLY=ON

  • -DLLVM_CONFIG_PATH=/path/to/llvm-config

  • -DCMAKE_C_FLAGS="build-c-flags"

  • -DCMAKE_ASM_FLAGS="build-c-flags"

  • -DCOMPILER_RT_EMULATOR="qemu-arm -L /path/to/armv7-A/sysroot"

  • -DCOMPILER_RT_INCLUDE_TESTS=ON

  • -DCOMPILER_RT_TEST_COMPILER="/path/to/clang"

  • -DCOMPILER_RT_TEST_COMPILER_CFLAGS="test-c-flags"

Armv6-M builtins 將使用軟體浮點 ABI。 當編譯 Armv7-A 的測試時,我們必須在 test-c-flags 中包含 "-mthumb -mfloat-abi=soft -mfpu=none"。 我們必須為 qemu-arm 使用 Armv7-A 軟體浮點 abi 系統根目錄。

根據用於測試案例的連結器,您可能會遇到來自 compiler-rt 的 M-profile 物件與來自測試的 A-profile 物件之間的 BuildAttribute 不匹配。 lld 連結器不檢查 profile BuildAttribute,因此可以透過將 -fuse-ld=lld 添加到 COMPILER_RT_TEST_COMPILER_CFLAGS 來使用它來連結測試。

使用 cmake 快取的替代方案

如果您希望建置 compiler-rt 但不測試適用於 Armv6-M、Armv7-M 或 Armv7E-M 的 compiler-rt,最簡單的方法是使用 clang/cmake/caches 中的 BaremetalARM.cmake 配方。

您將需要一個裸機系統根目錄,例如 GNU ARM Embedded 工具鏈提供的系統根目錄。

可以使用 cmake 選項建置函式庫

  • -DBAREMETAL_ARMV6M_SYSROOT=/path/to/bare/metal/toolchain/arm-none-eabi

  • -DBAREMETAL_ARMV7M_SYSROOT=/path/to/bare/metal/toolchain/arm-none-eabi

  • -DBAREMETAL_ARMV7EM_SYSROOT=/path/to/bare/metal/toolchain/arm-none-eabi

  • -C /path/to/llvm/source/tools/clang/cmake/caches/BaremetalARM.cmake

  • /path/to/llvm

請注意,為了使配方正常工作,必須將 compiler-rt 原始碼簽出到目錄 llvm/runtimes 中。 您還需要簽出 clang 和 lld。