Arm 用 Compiler-rt ビルトインのクロスコンパイル方法

はじめに

このドキュメントには、x86_64 Linux マシンから Arm ターゲット向けの compiler-rt のビルトイン部分をビルドおよびテストする方法に関する情報が含まれています。

このドキュメントでは Arm と Linux に焦点を当てていますが、一般的な原則は compiler-rt でサポートされている他のターゲットにも適用されるはずです。他のターゲットに関するさらなる貢献は大歓迎です。

このドキュメントの手順は、LLVM 以外のライブラリとプログラムに依存しています。これらの依存関係をインストールおよび構成するには多くの方法があるため、ここでの手順を自分のローカル環境に合わせて調整する必要がある場合があります。

前提条件

このユースケースでは、Debian ベースの Linux システムで cmake を使用し、x86_64 ホストからハードフロート Armv7-A ターゲットへのクロスコンパイルを行います。可能な限り多くの LLVM ツールを使用しますが、GNU 同等のツールを使用することも可能です。

  • LLVM/clang ビルド (llvm-tools llvm-config 用)

  • ARM ターゲットをサポートする clang 実行ファイル

  • compiler-rt ソースコード

  • qemu-arm ユーザーモードエミュレータ

  • arm-linux-gnueabihf sysroot

この例では、ninja を使用します。

clang と LLVM への依存関係の詳細については、https://compiler-rt.llvm.org/ を参照してください。

LLVM と compiler-rt のソースコードの入手方法については、https://llvm.dokyumento.jp/docs/GettingStarted.html を参照してください。はじめにガイドでは compiler-rt を projects サブディレクトリに配置していますが、これは必須ではありません。v6-M、v7-M、v7-EM 用の BaremetalARM.cmake キャッシュを使用する場合は、compiler-rt を runtimes ディレクトリに配置する必要があります。

qemu-arm は、お使いの Linux ディストリビューションのパッケージとして入手できるはずです。

満たすのが最も複雑な前提条件は arm-linux-gnueabihf sysroot です。理論的には、Linux ディストリビューションのマルチアーキテクチャサポートを使用してビルドの依存関係を満たすことができますが、残念ながら /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_FLAGSCMAKE_ASM_FLAGS の両方に追加します。build-c-flags を簡素化するために使用できる、これらのオプションの一部を個別に渡すための cmake フラグがあります。

  • -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 フラグを追加する必要があります。qemu-arm でテストを実行したいことも cmake に伝える必要があります。

  • -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」に渡されたものと同じである必要があります。

「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 の試行コンパイル段階が失敗する

初期段階で、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 を追加するため、ホストのヘッダーファイルをコンパイルするときにエラーが発生します。

ビルトインをビルドするには、マルチアーキテクチャサポートは不十分です。別の arm-linux-gnueabihf ツールチェーンを使用する必要があります。

clang にターゲットが渡されない

clang にターゲットが指定されない場合、通常はホストターゲットが使用されます。これにより、Arm アセンブリ言語ファイルが理解されず、error: unknown directive .syntax unified などのエラーメッセージが表示されます。

--target がないか、正しく設定されていないかどうかを確認するには、エラーメッセージの clang の呼び出しを確認してください。原因は通常、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_TARGETCMAKE_ASM_COMPILER_TARGETCMAKE_C_COMPILER_EXTERNAL_TOOLCHAINCMAKE_SYSROOT フラグは適用されません。

COMPILER_RT_TEST_COMPILE_CFLAGS に必要なすべての情報が含まれていることを確認してください。

他のターゲットへの変更

Arm ソフトフロートターゲット

Arm ハードフロートターゲットの指示は、sysroot とターゲットをソフトフロート相当のものに置き換えることで、ソフトフロートターゲットに使用できます。使用するターゲットは次のとおりです。

  • -DCMAKE_C_COMPILER_TARGET=arm-linux-gnueabi

浮動小数点命令を使用するかどうかによっては、浮動小数点命令を使用する場合に-mfloat-abi=softfpなどの追加のc-flagsが必要になる場合があり、ソフトウェア浮動小数点エミュレーションには-mfloat-abi=soft -mfpu=noneが必要です。

ソフトフロートには、arm-linux-gnueabi GNUツールチェーンを使用する必要があります。

AArch64 ターゲット

Arm の指示は、sysroot、エミュレータ、ターゲットを 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を使用できますが、設定がはるかに困難です。

  • コンパイラrtをコンパイルするターゲットには、サフィックス-none-eabiが付いています。これはclangのBareMetalドライバを使用し、デフォルトではcmakeコンパイラチェックに合格するために必要なライブラリを見つけません。

compiler-rtのArmv6-M、Armv7-M、Armv7E-MビルドはArmv7-Aでサポートされている命令のみを使用するため、Armv7-A用にビルドして実行されたテストケースを使用して、Armv7-Aで使用したのと同じqemu-armを使用してテストを実行することのメリットのほとんどを得ることができます。これにより、組み込み関数がバイナリにリンクされ、テストが正しく実行されるかどうかがテストされますが、組み込み関数がArmv7-AではサポートされているがArmv6-M、Armv7-M、Armv7E-Mではサポートされていない命令を使用しているかどうかは検出されません。

cmakeコンパイルテストに合格するには、CMAKE_CFLAGSを介して、cmakeテストのリンクに成功するために必要なライブラリを渡す必要があります。CMAKE_TRY_COMPILE_TARGET=STATIC_LIBRARYを使用してリンクステップをスキップできるため、cmakeバージョン3.6以降を使用することを強くお勧めします。

  • -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 sysrootを使用する必要があります。

テストケースに使用されるリンカーによっては、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 Embeddedツールチェーンによって提供されるような、ベアメタルsysrootが必要です。

ライブラリは、次の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もチェックアウトする必要があります。