Clang/LLVM を使用して Clang/LLVM をクロスコンパイルする方法

はじめに

このドキュメントには、あるプラットフォームをターゲットにホストマシンで LLVM と Clang を構築する方法に関する情報が記載されています。

Clang をクロスコンパイラとして使用する方法の詳細については、https://clang.llvm.org/docs/CrossCompilation.html を参照してください。

TODO: MIPS とその他のプラットフォームをこのドキュメントに追加します。

x86_64 から ARM へのクロスコンパイル

このユースケースでは、Debian ベースの Linux システムで CMake と Ninja を使用し、x86_64 ホスト (ほとんどの最新の Intel と AMD チップ) からハードフロートの ARM ターゲット (ほとんどの最新の ARM ターゲット) にクロスコンパイルします。

必要なパッケージは次のとおりです。

  • cmake

  • ninja-build (Ubuntu のバックポートから)

  • 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=<target-system>

  • -DCMAKE_INSTALL_PREFIX=<install-dir>

  • -DLLVM_HOST_TRIPLE=arm-linux-gnueabihf

  • -DLLVM_TARGETS_TO_BUILD=ARM

注: CMAKE_CROSSCOMPILINGCMAKE_SYSTEM_NAME が設定されている場合は常に自動的に設定されます。オプションに -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 ツール(llvm-tblgenclang-tblgen など)があるディレクトリを指す LLVM_NATIVE_TOOL_DIR オプションを設定することもできます。これにより、利用可能な場合はそれらを使用できます。例:-DLLVM_NATIVE_TOOL_DIR=<path-to-native-llvm-build>/bin。このオプションが設定されていない場合(またはディレクトリに必要なすべてのツールが含まれていない場合)、LLVM クロスビルドは、必要なツールを自動的にビルドして、ネステッドビルドを開始します。

CXX フラグは、ターゲット、CPU(この場合は、NEON を使用した fpu=VFP3 が既定値)、ハード浮動小数点数を強制的に使用することを定義します。Clang をクロスコンパイラとして使用している場合、正しいリンカーが選択されるように --sysroot も設定する必要があります。

Clang を使用する場合、GCC トリプルと sysroot とまったく同じトリプルを選択することが重要です。これにより、Clang が正しいツールを見つけてヘッダーを含めることが容易になります。しかし、すべてのヘッダーとライブラリが見つかるという意味ではありません。ディストリビューションに応じて -I-L を使用してそれらを見つける必要があります。

ほとんどの場合、プラットフォーム自体に対してネイティブコンパイラが必要ですが、他のプラットフォームは必要ありません。そのため、すべてのバックエンドをコンパイルする意味はほとんどありません。このため、対象とするバックエンドのみをビルドするように TARGETS_TO_BUILD も設定する必要があります。

CMAKE_INSTALL_PREFIX を設定する必要があります。そうしないと、ninja install によって ARM バイナリがルートファイルシステムにコピーされますが、これは望ましくありません。

ハック

現在の LLVM には、CMake を実行する前に少し調整が必要になるいくつかのバグがあります。

  1. クロスコンパイラとして Clang を使用している場合、位置に依存しないコード(R_ARM_THM_MOVW_ABS_NC)で絶対再配置を生成する LLVM ARM バックエンドに問題があるため、現時点では 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に必要なものは次のとおりです。 libtinfozlib1glibxml2 および liblzma。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

自動的にコアの数、ビルドが必要なルールがわかり、全体がビルドされます。

作成されたバイナリはx86_64ではなくARMをターゲットにしているため、このツリーでは ninja check-all を実行できません。

インストールと使用

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.dokyumento.jp/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-jN オプションを追加して、ボードのCPU数になるようにしてください。また、clangへのパスは絶対パスにする必要があるため、上記の pwd トリックが必要になります。