ABI 管理

Android ハンドセットが異なると使用される CPU も異なります。そのため、サポート対象の命令セットも異なります。CPU と命令セットの組み合わせごとに、独自のアプリケーション バイナリ インターフェース ABI が用意されています。 ABI は、アプリケーションのマシンコードが実行時にシステムとやりとりする方法を詳細に定義したものです。 アプリで使用したい CPU アーキテクチャごとに、ABI を指定する必要があります。

一般的な ABI には次の情報が含まれます。

  • マシンコードが使用する CPU 命令セット。
  • 実行時のメモリストアとメモリロードのエンディアン。
  • プログラム、共有ライブラリ、これらがサポートするコンテンツのタイプなど、実行可能バイナリのフォーマット。
  • コードとシステムの間でデータを受け渡すためのさまざまな規則。規則には、アライメント制約や、システムが関数の呼び出し時にスタックやレジスタを使用する方法が含まれます。
  • 一般的に非常に限られたライブラリ セットから、ランタイム時のマシンコードに使用できる関数シンボルの一覧。

このページには NDK がサポートする ABI と各 ABI の仕組みに関する情報が記載されています。

サポート対象 ABI

各 ABI は 1 つ以上の命令セットをサポートします。表 1 は、各 ABI がサポートする命令セットの概要です。

表 1 ABI とサポート対象命令セット

ABI サポート対象命令セット
armeabi
  • ARMV5TE 以降
  • Thumb-1
  • hard-float なし
    armeabi-v7a
  • armeabi
  • Thumb-2
  • VFPv3-D16
  • その他、オプション
  • ARMv5、v6 端末と互換性なし
    arm64-v8a
  • AArch-64
  • x86
  • x86(IA-32)
  • MMX
  • SSE/2/3
  • SSSE3
  • MOVBE または SSE4 に対するサポートなし
    x86_64
  • x86-64
  • MMX
  • SSE/2/3
  • SSSE3
  • SSE4.1、4.2
  • POPCNT
  • mips
  • MIPS32r1 以降
  • hard-float を使用し、CPU:FPU のクロック比が 2:1 で互換性が最大になると仮定します。 microMIPS と MIPS16 のいずれも提供しません。
    mips64
  • MIPS64r6
  • 各 ABI の詳細については以下をご覧ください。

    armeabi

    この ABI は少なくとも ARMv5TE 命令セットをサポートする ARM ベース CPU 向けです。 詳細については以下のドキュメントをご覧ください。

    AAPCS 標準は、類似する別々の ABI のファミリーとして EABI を定義します。 さらに、Android はリトル エンディアン ARM GNU/Linux ABI に準拠します。

    この ABI はハードウェア補助の浮動小数点演算をサポートしません。 すべての浮動小数点演算には、コンパイラーの libgcc.a 静的ライブラリからソフトウェア ヘルパー関数を使用します。

    armeabi ABI は ARM の Thumb(別名 Thumb-1)命令セットをサポートします。Android.mk ファイルで LOCAL_ARM_MODE 変数を使用して別の動作を指定しない限り、NDK はデフォルトで Thumb コードを生成します。

    armeabi-v7a

    armeabi を拡張したこの ABI には、複数の CPU 命令セット拡張機能が含まれます。 この Android 固有の ABI がサポートする命令の拡張機能は次のとおりです。

    • Thumb-2 命令セットの拡張機能。Thumb-1 同様の簡潔な 32 ビット ARM 命令と同等のパフォーマンスを提供します。
    • VFP ハードウェア FPU 命令。具体的には、VFPv3-D16 は、16 個の専用 64 ビット浮動小数点レジスタと、ARM コアからの別の 16 個の 32 ビット レジスタを含みます。

    v7-a ARM 仕様に含まれる、拡張 SIMD(別名 NEON)、VFPv3-D32、ThumbEE などのその他の拡張機能は、この ABI ではオプションです。 その他の拡張機能が含まれていることは保証されていないため、システムは実行時にこれらが利用可能かどうかを確認します。 利用不可の場合は別のコードパスを使用する必要があります。この確認は、システムが MMXSSE2、x86 CPU での他の専用命令セットを確認または使用するために通常行う動作と同様です。

    ランタイム チェックの実行方法の詳細は、cpufeatures ライブラリを参照してください。NEON 向けマシンコードのビルドに関する NDK サポートの詳細については、NEON サポートをご覧ください。

    armeabi-v7a ABI は -mfloat-abi=softfp スイッチを使用して、コンパイラーが関数呼び出し時に専用浮動小数点レジスター ペアではなくコアレジスタ ペアのすべての倍精度浮動小数点の値を渡す規則を適用します。 システムは FP レジスタを使用してすべての内部計算を実行できます。 この方法で計算速度が大幅に上がります。

    arm64-v8a

    この ABI は AArch64 をサポートする ARMv8 ベースの CPU 向けです。NEON 命令セットと VFPv4 命令セットも含まれます。

    詳しくは、ARMv8 テクノロジー プレビューをご覧ください。さらに詳しい内容については ARM にお問い合わせください。

    x86

    この ABI は、一般に「x86」や「IA-32」と呼ばれる命令セットをサポートする CPU 向けです。 この ABI の特徴は次のとおりです。

    • 次のようなコンパイラー フラグを使用して、通常 GCC から命令が生成される。
      -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32
      

      これらのフラグは、MMXSSESSE2SSE3SSSE3 命令セットの拡張機能に加えて、Pentium Pro 命令セットをターゲットとしています。生成されたコードは上位の Intel 32 ビット CPU に最適に分散されます。

      コンパイラー フラグ、特にパフォーマンスの最適化に関連するフラグの詳細は、GCC x86 パフォーマンスのヒントをご覧ください。

    • SVR 向けの規則ではなく、標準 Linux x86 32 ビット呼び出し規則を使用する。詳しくは、さまざまな C++ コンパイラーおよびオペレーティング システム向けの呼び出し規則のセクション 6「レジスターの使用」をご覧ください。

    ABI には、次のようなその他の任意の IA-32 命令セットの拡張機能は含まれません。

    • MOVBE
    • SSE4 のバリアント

    これらの拡張機能使用するには、ランタイム機能プロービングを使用してこれらの機能を有効にし、これらをサポートしない端末にフォールバックを提供します。

    NDK ツールチェーンは関数を呼び出す前に 16 バイトのスタック アライメントを想定します。既定のツールとオプションで、この規則が適用されます。 アセンブリ コードを記述している場合、スタック アライメントを維持する必要があります。また、他のコンパイラーもこの規則に従う必要があります。

    詳細については以下のドキュメントをご覧ください。

    x86_64

    この ABI は、一般に「x86-64」と呼ばれる命令セットをサポートする CPU 向けです。 GCC が通常次のコンパイラー フラグを使用して生成する命令をサポートします。

    -march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel
    

    GCC ドキュメントにあるように、これらのフラグは MMXSSESSE2SSE3SSSE3SSE4.1SSE4.2POPCNT 命令セットの拡張機能に加えて、x86-64 命令セットをターゲットとしています。 生成されたコードは上位の Intel 64 ビット CPU に最適に分散されます。

    コンパイラー フラグ、特にパフォーマンスの最適化に関連するフラグの詳細は、GCC x86 パフォーマンスをご覧ください。

    この ABI には、次のような他の任意の x86-64 命令セットの拡張機能は含まれません。

    • MOVBE
    • SHA
    • AVX
    • AVX2

    これらの拡張機能使用するには、ランタイム機能プロービングを使用してこれらの機能を有効にし、これらをサポートしない端末にフォールバックを提供します。

    詳細については以下のドキュメントをご覧ください。

    mips

    この ABI は少なくとも MIPS32r1 命令セットをサポートする MIPS ベース CPU 向けです。次の機能が含まれています。

    • MIPS32 リビジョン 1 ISA
    • リトルエンディアン
    • O32
    • Hard-float
    • DSP アプリケーション固有の拡張機能なし

    詳細については以下のドキュメントをご覧ください。

    詳細については MIPS32 アーキテクチャをご覧ください。一般的な質問に対する回答は MIPS FAQ にあります。

    mips64

    この ABI は MIPS64 R6 向けです。詳細については、MIPS64 アーキテクチャをご覧ください。

    特定の ABI のコードの生成

    デフォルトでは、NDK は armeabi ABI 向けマシンコードを生成します。Application.mk ファイルに次の行を追加すると、ARMv7 対応のマシンコードを生成できます。

    APP_ABI := armeabi-v7a
    

    複数の ABI にマシンコードをビルドするには、空白を区切り記号として使用します。次に例を示します。

    APP_ABI := armeabi armeabi-v7a
    

    この設定で、NDK が上記の行に記載されている ABI ごとに 1 つ、合計 2 つのバージョンのマシンコードをビルドするように指示しています。 APP_ABI 変数に指定できる値の詳細については、Android.mk をご覧ください。

    複数のマシンコードのバージョンをビルドすると、ビルドシステムはライブラリをアプリケーションのプロジェクト パスにコピーし、APK にパッケージ化して、ファット バイナリを作成します。 ファット バイナリは単一システム向けのマシンコードのみ含むものよりも大きくなります。よって APK のサイズは大きくなりますが、互換性が拡張されるという利点があります。

    Package Manager はインストール時にターゲット端末に最も適したマシンコードのみを解凍します。 詳細については、 インストール時のネイティブ コードの自動抽出をご覧ください。

    Android プラットフォームでの ABI 管理

    このセクションでは、Android プラットフォームで APK でネイティブ コードを管理する方法について詳しく説明します。

    アプリケーション パッケージのネイティブ コード

    Play ストアと Package Manager の両方とも、次のパターンに一致する APK 内のファイルパスで、NDK により生成されたライブラリを検索します。

    /lib/<abi>/lib<name>.so
    

    この例では、<abi>サポート対象 ABI の一覧に含まれる ABI 名の 1 つで、<name>Android.mk ファイルで LOCAL_MODULE 変数に定義したライブラリ名です。 APK ファイルは zip 形式のファイルにすぎないため、簡単に開くことができ、共有ネイティブ ライブラリの場所もすぐ確認できます。

    共有ネイティブ ライブラリが予想される場所で見つからないと、システムはライブラリを使用できません。 この場合は、アプリケーション自体がライブラリをコピーし、dlopen() を実行する必要があります。

    ファット バイナリでは、該当する ABI と名前が一致するディレクトリの下に各ライブラリが置かれます。以下はファット バイナリの中身の例です。

    /lib/armeabi/libfoo.so
    /lib/armeabi-v7a/libfoo.so
    /lib/arm64-v8a/libfoo.so
    /lib/x86/libfoo.so
    /lib/x86_64/libfoo.so
    /lib/mips/libfoo.so
    /lib/mips64/libfoo.so
    

    : 4.0.3 以前を実行している ARMv7 ベースの Android 端末は、armeabi-v7a ディレクトリと armeabi ディレクトリの両方が存在する場合、armeabi ディレクトリからネイティブ ライブラリをインストールします。 これは、APK 内で /lib/armeabi//lib/armeabi-v7a/ の後にくるためです。 この問題は 4.0.4 以降で修正されています。

    Android プラットフォーム ABI サポート

    ビルド専用のシステム プロパティが示す以下の内容をもとに、Android システムは実行時にサポートする API を認識します。

    • システム イメージ自体で使用されるマシンコードに対応する、端末向けのプライマリ ABI。
    • システム イメージもサポートする別の ABI に対応する、任意のセカンダリ ABI。

    このメカニズムによって、システムはインストール時にパッケージから最良のマシンコードを抽出します。

    最良のパフォーマンスを実現するには、プライマリ ABI 用に直接コンパイルすることをお勧めします。たとえば、一般的な ARMv5TE ベース端末はプライマリ ABI armeabi のみを定義します。 一方、一般的な ARMv7 ベース端末は、各 ABI 用に生成されたアプリケーション ネイティブ バイナリを実行できるため、 プライマリ ABI を armeabi-v7a、セカンダリ ABI を armeabi に定義します。

    多くの x86 ベース端末も armeabi-v7a NDK バイナリと armeabi NDK バイナリを実行できます。このような端末では、プライマリ ABI は x86 で、セカンダリ ABI は armeabi-v7a です。

    一般的な MIPS ベース端末は、プライマリ ABI mips のみを定義します。

    インストール時のネイティブ コードの自動抽出

    アプリケーションのインストール時に、Package Manager サービスは APK をスキャンし、次の形式の共有ライブラリを検索します。

    lib/<primary-abi>/lib<name>.so
    

    ライブラリが見つからず、かつセカンダリ ABI を定義している場合、このサービスは次の形式の共有ライブラリがないかスキャンします。

    lib/<secondary-abi>/lib<name>.so
    

    探しているライブラリが見つかると、Package Manager はライブラリをアプリケーションの data ディレクトリ(data/data/<package_name>/lib/)の下にある /lib/lib<name>.so にコピーします。

    共有オブジェクト ファイルがまったく存在しない場合も、アプリケーションのビルドとインストールは行われますが、実行時にクラッシュします。