ABI Android

Perangkat Android yang berbeda menggunakan CPU yang berbeda pula, sehingga kumpulan petunjuk yang didukungnya juga berbeda. Setiap kombinasi CPU dan kumpulan petunjuk memiliki Antarmuka Biner Aplikasi (ABI)-nya sendiri. ABI mencakup informasi berikut:

  • Kumpulan petunjuk CPU (dan ekstensi) yang dapat digunakan.
  • Endianess penyimpanan dan pemuatan memori saat runtime. Android selalu bersifat little-endian.
  • Konvensi untuk meneruskan data antara aplikasi dan sistem, termasuk batasan perataan, serta cara sistem menggunakan stack dan register saat memanggil fungsi.
  • Format biner yang dapat dieksekusi, seperti program dan library bersama, serta jenis konten yang didukungnya. Android selalu menggunakan ELF. Untuk informasi selengkapnya, lihat Antarmuka Biner Aplikasi System V ELF.
  • Cara nama C++ dimodifikasi. Untuk mengetahui informasi selengkapnya, lihat ABI C++ Generik/Itanium.

Halaman ini mencantumkan ABI yang didukung NDK, dan memberikan informasi tentang cara kerja masing-masing ABI.

ABI juga dapat merujuk ke API native yang didukung oleh platform. Untuk daftar jenis masalah ABI yang memengaruhi sistem 32-bit, lihat Bug ABI 32-bit.

ABI yang Didukung

Tabel 1. ABI dan kumpulan petunjuk yang didukung.

ABI Kumpulan Petunjuk yang Didukung Catatan
armeabi-v7a
  • armeabi
  • Thumb-2
  • VFPv3-D16
  • Tidak kompatibel dengan perangkat ARMv5/v6.
    arm64-v8a
  • AArch64
  • x86
  • x86 (IA-32)
  • MMX
  • SSE/2/3
  • SSSE3
  • Tidak ada dukungan untuk MOVBE atau SSE4.
    x86_64
  • x86-64
  • MMX
  • SSE/2/3
  • SSSE3
  • SSE4.1, 4.2
  • POPCNT
  • Catatan: Secara historis, NDK mendukung ARMv5 (armeabi) serta MIPS 32-bit dan 64-bit, tetapi dukungan untuk ABI ini telah dihapus pada NDK r17.

    armeabi-v7a

    ABI ini ditujukan bagi CPU berbasis ARM 32-bit. Varian Android menyertakan petunjuk floating point hardware VFP dan Thumb-2, khususnya VFPv3-D16, yang menyertakan 16 register floating point 64-bit khusus.

    Untuk mengetahui informasi tentang bagian ABI yang bukan khusus Android, lihat Antarmuka Biner Aplikasi (ABI) untuk Arsitektur ARM

    Sistem build NDK membuat kode Thumb-2 secara default, kecuali jika Anda menggunakan LOCAL_ARM_MODE dalam Android.mk untuk ndk-build atau ANDROID_ARM_MODE saat mengonfigurasi CMake.

    Ekstensi lain termasuk Advanced SIMD (Neon) dan VFPv3-D32 bersifat opsional. Untuk mengetahui informasi selengkapnya, lihat Dukungan Neon.

    ABI armeabi-v7a menggunakan -mfloat-abi=softfp untuk menerapkan aturan bahwa, meskipun sistem dapat mengeksekusi kode floating point, compiler harus meneruskan semua nilai float dalam register integer dan semua nilai double dalam pasangan register integer saat melakukan panggilan fungsi.

    arm64-v8a

    ABI ini ditujukan bagi CPU berbasis ARMv8-A, yang mendukung arsitektur AArch64 64-bit. ABI ini menyertakan ekstensi arsitektur Advanced SIMD (Neon).

    Anda dapat menggunakan intrinsik Neon dalam kode C dan C++ untuk memanfaatkan ekstensi Advanced SIMD. Panduan Programmer Neon untuk Armv8-A memberikan informasi selengkapnya tentang intrinsik dan pemrograman Neon secara umum.

    Lihat Mempelajari Arsitektur Arm untuk detail lengkap tentang bagian-bagian ABI yang bukan khusus Android. Arm juga menawarkan beberapa saran porting dalam Pengembangan Android 64-bit.

    Di Android, register x18 khusus platform disediakan untuk ShadowCallStack dan tidak boleh disentuh oleh kode. Versi Clang saat ini merupakan default untuk menggunakan opsi -ffixed-x18 di Android. Kecuali jika Anda memiliki assembler yang ditulis secara manual (atau versi compiler yang sangat lama), Anda tidak perlu khawatir tentang hal ini.

    x86

    ABI ini ditujukan bagi CPU yang mendukung kumpulan petunjuk yang umumnya dikenal sebagai "x86", "i386", atau "IA-32". Karakteristik ABI ini antara lain:

    • Petunjuk biasanya dihasilkan oleh GCC dengan flag compiler seperti berikut:
      -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32
      

      Flag ini menargetkan kumpulan petunjuk Pentium Pro, beserta ekstensi kumpulan petunjuk MMX, SSE, SSE2, SSE3, dan SSSE3. Kode yang dihasilkan adalah pengoptimalan yang diseimbangkan di seluruh CPU 32-bit Intel terbaik.

      Untuk mengetahui informasi selengkapnya tentang flag compiler, khususnya yang terkait dengan pengoptimalan performa, lihat Petunjuk performa GCC x86.

    • Gunakan konvensi pemanggilan Linux x86 32-bit standar, bukan yang ditujukan untuk SVR. Untuk mengetahui informasi selengkapnya, lihat bagian 6, "Penggunaan Register", pada Konvensi pemanggilan untuk berbagai compiler C++ dan sistem operasi.

    ABI ini tidak mencakup ekstensi kumpulan petunjuk IA-32 opsional lainnya, seperti:

    • MOVBE
    • Semua varian SSE4.

    Anda masih dapat menggunakan ekstensi ini asalkan Anda menggunakan pemeriksaan fitur runtime untuk mengaktifkannya, dan menyediakan fallback untuk perangkat yang tidak mendukungnya.

    Toolchain NDK mengasumsikan penyelarasan stack 16 byte sebelum panggilan fungsi. Alat dan opsi default menerapkan aturan ini. Jika menulis kode assembly, Anda harus memastikan untuk mempertahankan keselarasan stack, dan memastikan bahwa compiler lain juga mematuhi aturan ini.

    Lihat dokumen berikut untuk detail selengkapnya:

    x86_64

    ABI ini ditujukan untuk CPU yang mendukung kumpulan petunjuk yang umumnya disebut "x86-64". ABI ini mendukung petunjuk yang biasanya dihasilkan oleh GCC dengan flag compiler berikut:

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

    Menurut dokumentasi GCC, flag ini menargetkan kumpulan petunjuk x86-64, beserta ekstensi kumpulan petunjuk MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, dan POPCNT. Kode yang dihasilkan adalah pengoptimalan yang diseimbangkan di seluruh CPU 64-bit Intel terbaik.

    Untuk mengetahui informasi selengkapnya tentang flag compiler, khususnya yang terkait dengan pengoptimalan performa, lihat Petunjuk performa GCC x86.

    ABI ini tidak mencakup ekstensi kumpulan petunjuk x86-64 opsional lainnya, seperti:

    • MOVBE
    • SHA
    • AVX
    • AVX2

    Anda masih dapat menggunakan ekstensi ini asalkan Anda menggunakan pemeriksaan fitur runtime untuk mengaktifkannya, dan menyediakan fallback untuk perangkat yang tidak mendukungnya.

    Lihat dokumen berikut untuk detail selengkapnya:

    Membuat kode untuk ABI tertentu

    Gradle

    Gradle (baik digunakan melalui Android Studio maupun dari command line) dibuat untuk semua ABI yang masih digunakan secara default. Untuk membatasi kumpulan ABI yang didukung aplikasi Anda, gunakan abiFilters. Misalnya, untuk membuat build bagi ABI 64-bit saja, tetapkan konfigurasi berikut dalam build.gradle Anda:

    android {
        defaultConfig {
            ndk {
                abiFilters 'arm64-v8a', 'x86_64'
            }
        }
    }
    

    ndk-build

    ndk-build melakukan proses build untuk semua ABI yang masih digunakan secara default. Anda dapat menargetkan ABI tertentu dengan menetapkan APP_ABI dalam file Application.mk. Cuplikan berikut menunjukkan beberapa contoh penggunaan APP_ABI:

    APP_ABI := arm64-v8a  # Target only arm64-v8a
    APP_ABI := all  # Target all ABIs, including those that are deprecated.
    APP_ABI := armeabi-v7a x86_64  # Target only armeabi-v7a and x86_64.
    

    Untuk mengetahui informasi selengkapnya tentang nilai yang dapat ditetapkan untuk APP_ABI, lihat Application.mk.

    CMake

    Dengan CMake, Anda membuat satu ABI dalam satu waktu dan harus menetapkan ABI secara eksplisit. Lakukan hal ini dengan variabel ANDROID_ABI, yang harus ditetapkan pada command line (tidak dapat ditetapkan dalam CMakeLists.txt). Contoh:

    $ cmake -DANDROID_ABI=arm64-v8a ...
    $ cmake -DANDROID_ABI=armeabi-v7a ...
    $ cmake -DANDROID_ABI=x86 ...
    $ cmake -DANDROID_ABI=x86_64 ...
    

    Untuk flag lain yang harus diteruskan ke CMake guna membuat build dengan NDK, lihat Panduan CMake.

    Secara default, perilaku sistem build menyertakan biner untuk setiap ABI dalam satu APK, yang juga disebut fat APK. Fat APK secara signifikan berukuran lebih besar dari APK yang hanya memuat biner untuk satu ABI. Kompatibilitas yang lebih luas mengorbankan ukuran APK-nya yang menjadi lebih besar. Sebaiknya Anda memanfaatkan App Bundle atau Pembagian APK untuk mengurangi ukuran APK dengan tetap mempertahankan kompatibilitas perangkat yang optimal.

    Pada waktu penginstalan, pengelola paket hanya mengekstrak kode mesin yang paling sesuai untuk perangkat target. Untuk detailnya, lihat Ekstraksi otomatis kode native pada waktu penginstalan.

    Pengelolaan ABI di platform Android

    Bagian ini memberikan detail tentang cara platform Android mengelola kode native dalam APK.

    Kode native dalam paket aplikasi

    Play Store maupun Pengelola Paket akan selalu berupaya menemukan library yang dihasilkan NDK pada jalur file dalam APK yang sesuai dengan pola berikut:

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

    Di sini, <abi> adalah salah satu nama ABI yang tercantum dalam ABI yang didukung, dan <name> adalah nama library seperti yang Anda tetapkan untuk variabel LOCAL_MODULE dalam file Android.mk. Karena hanya berupa file zip, file APK mudah dibuka untuk mengonfirmasi bahwa library native bersama berada di tempat yang seharusnya.

    Jika sistem tidak menemukan library native bersama di tempat yang seharusnya, maka sistem tidak akan dapat menggunakannya. Dalam kasus semacam ini, aplikasi itu sendiri harus menyalin semua library, lalu menjalankan dlopen().

    Dalam fat APK, setiap library berada di bagian direktori yang namanya cocok dengan ABI yang terkait. Misalnya, fat APK dapat memuat:

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

    Catatan: Perangkat Android berbasis ARMv7 yang menjalankan 4.0.3 atau yang lebih lama akan menginstal library native dari direktori armeabi, bukan armeabi-v7a, jika kedua direktori tersebut ada. Hal ini dikarenakan /lib/armeabi/ ditempatkan setelah /lib/armeabi-v7a/ dalam APK. Masalah ini telah diperbaiki sejak versi 4.0.4.

    Dukungan ABI platform Android

    Sistem Android mengetahui ABI yang didukungnya pada runtime, karena properti sistem khusus build menunjukkan:

    • ABI primer untuk perangkat, yang sesuai dengan kode mesin yang digunakan dalam image sistem itu sendiri.
    • ABI sekunder (opsional), yang sesuai dengan ABI lain yang juga didukung image sistem.

    Mekanisme ini memastikan sistem mengekstrak kode mesin terbaik dari paket pada waktu penginstalan.

    Untuk performa terbaik, Anda harus mengompilasi secara langsung untuk ABI primer. Misalnya, perangkat berbasis ARMv5TE umum hanya akan menentukan ABI primer: armeabi. Sebaliknya, perangkat berbasis ARMv7 standar akan menentukan ABI primer sebagai armeabi-v7a dan ABI sekunder sebagai armeabi, karena perangkat ini dapat menjalankan biner native aplikasi yang dihasilkan untuk setiap ABI.

    Perangkat 64-bit juga mendukung varian 32-bit. Menggunakan perangkat arm64-v8a sebagai contoh, perangkat ini juga dapat menjalankan kode armeabi dan armeabi-v7a. Namun, perlu diketahui bahwa aplikasi akan berperforma jauh lebih baik di perangkat 64-bit jika menargetkan arm64-v8a daripada mengandalkan perangkat yang menjalankan versi armeabi-v7a aplikasi.

    Banyak perangkat berbasis x86 juga dapat menjalankan biner NDK armeabi-v7a dan armeabi. Untuk perangkat tersebut, ABI utamanya adalah x86, dan ABI sekundernya adalah armeabi-v7a.

    Anda dapat menginstal otomatis apk untuk ABI tertentu. Hal ini berguna untuk melakukan pengujian. Gunakan perintah berikut:

    adb install --abi abi-identifier path_to_apk
    

    Ekstraksi otomatis kode native pada waktu penginstalan

    Saat menginstal aplikasi, layanan pengelola paket memindai APK, dan mencari setiap library bersama yang berformat:

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

    Jika tidak ada yang ditemukan, dan Anda telah menentukan ABI sekunder, layanan akan memindai library bersama yang berformat:

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

    Setelah menemukan library yang dicari, pengelola paket akan menyalinnya ke /lib/lib<name>.so dalam direktori library native aplikasi (<nativeLibraryDir>/). Cuplikan berikut akan mengambil nativeLibraryDir:

    Kotlin

    import android.content.pm.PackageInfo
    import android.content.pm.ApplicationInfo
    import android.content.pm.PackageManager
    ...
    val ainfo = this.applicationContext.packageManager.getApplicationInfo(
            "com.domain.app",
            PackageManager.GET_SHARED_LIBRARY_FILES
    )
    Log.v(TAG, "native library dir ${ainfo.nativeLibraryDir}")
    

    Java

    import android.content.pm.PackageInfo;
    import android.content.pm.ApplicationInfo;
    import android.content.pm.PackageManager;
    ...
    ApplicationInfo ainfo = this.getApplicationContext().getPackageManager().getApplicationInfo
    (
        "com.domain.app",
        PackageManager.GET_SHARED_LIBRARY_FILES
    );
    Log.v( TAG, "native library dir " + ainfo.nativeLibraryDir );
    

    Jika tidak ada file objek bersama, aplikasi akan membuat dan menginstalnya, tetapi akan mengalami error saat runtime.