如何為 Arm 交叉編譯 Compiler-rt 內建函式庫¶
簡介¶
本文件包含從 x86_64 Linux 機器建置和測試 Arm 目標平台的 Compiler-rt 內建函式庫部分的資訊。
雖然本文件著重於 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 子目錄中,但這並非必要,如果您使用 BaremetalARM.cmake 快取記憶體作為 v6-M、v7-M 和 v7-EM,則 compiler-rt 必須放置在 runtimes 目錄中。
qemu-arm
應該可以作為 Linux 發行版的套件使用。
最複雜的先決條件是 arm-linux-gnueabihf sysroot。理論上可以使用 Linux 發行版的 multiarch 支援來滿足建置的相依項目,但不幸的是,由於添加了 /usr/local/include,因此選擇了一些主機標頭檔。提供 sysroot 的最簡單方法是下載 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 內建函式庫¶
我們將使用以下 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
構建內建函數。
使用 qemu-arm 測試 compiler-rt 內建函數¶
要測試內建函數庫,我們需要添加一些 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 定義(例如 CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN
)在構建測試時不適用。如果您已將所有這些內容都放在 “build-c-flags” 中,則可以重複使用。如果要使用 lld 鏈接測試,請添加 "-fuse-ld=lld
。
cmake 完成後,可以使用 ninja check-builtins
構建和運行測試。
故障排除¶
cmake 試用編譯階段失敗¶
在早期階段,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
,導致在編譯主機標頭檔時出錯。
多架構支援不足以建置內建元件,您需要使用單獨的 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 原始程式檔中使用的屏障指令。
原因通常是 CMAKE_ASM_FLAGS
中缺少 -march=armv7a
。
Compiler-rt 建置成功,但測試建置失敗¶
用於建置測試的旗標與用於建置內建元件的旗標不同。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 硬浮點目標的說明可以通過用軟浮點等效項替換 sysroot 和目標來用於軟浮點目標。要使用的目標是
-DCMAKE_C_COMPILER_TARGET=arm-linux-gnueabi
根據您是否要使用浮點指令,您可能需要額外的 c-flags,例如使用浮點指令時使用 -mfloat-abi=softfp
,以及使用軟體浮點模擬時使用 -mfloat-abi=soft -mfpu=none
。
您需要使用 arm-linux-gnueabi GNU 工具鏈來處理軟浮點。
AArch64 目標¶
透過將系統根目錄、模擬器和目標替換為 AArch64 等效項,Arm 的指令可以用於 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 編譯的內建函式,從而獲得執行測試的大部分價值。這將測試內建函式是否可以連結到二進制檔案並正確執行測試,但如果內建函式使用 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 內建函式將使用軟浮點 ABI。在為 Armv7-A 編譯測試時,我們必須在測試 c-flags 中包含 "-mthumb -mfloat-abi=soft -mfpu=none"
。我們必須為 qemu-arm
使用 Armv7-A 軟浮點 abi 系統根目錄。
根據用於測試案例的連結器,您可能會在來自 compiler-rt 的 M 配置文件物件和來自測試的 A 配置文件物件之間遇到 BuildAttribute 不匹配。lld 連結器不會檢查配置文件 BuildAttribute,因此可以透過在 COMPILER_RT_TEST_COMPILER_CFLAGS
中添加 -fuse-ld=lld 來連結測試。
使用 cmake 快取的替代方案¶
如果您希望建置但不想測試 Armv6-M、Armv7-M 或 Armv7E-M 的 compiler-rt,最簡單的方法是使用 clang/cmake/caches 中的 BaremetalARM.cmake 配方。
您將需要一個裸機系統根目錄,例如 GNU ARM 嵌入式工具鏈提供的目錄。
可以使用 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。