如何為 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_FLAGS
和 CMAKE_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_FLAGS
和 CMAKE_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
不包含 --target
或 CMAKE_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_TARGET
、CMAKE_ASM_COMPILER_TARGET
、CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN
和 CMAKE_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。