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 pada waktu proses. 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 mengetahui informasi selengkapnya, lihat Antarmuka Biner Aplikasi System V ELF.
  • Cara nama C++ diuraikan. Untuk mengetahui informasi selengkapnya, lihat ABI C++ Generik/Itanium.

Halaman ini mengenumerasi 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 set instruksi yang didukung.

ABI Set Instruksi 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 Anda menggunakan LOCAL_ARM_MODE dalam Android.mk untuk ndk-build atau ANDROID_ARM_MODE saat mengonfigurasi CMake.

    Ekstensi lain seperti 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 menjalankan 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 menyediakan informasi selengkapnya tentang intrinsik Neon 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 dilibatkan dalam kode Anda. Versi Clang saat ini merupakan default untuk menggunakan opsi -ffixed-x18 di Android. Kecuali 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 ke seluruh CPU 32 bit Intel teratas.

      Untuk mengetahui informasi selengkapnya tentang flag compiler, khususnya yang berkaitan 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, selama Anda menggunakan pemeriksaan fitur waktu proses untuk mengaktifkannya, dan menyediakan fallback untuk perangkat yang tidak mendukungnya.

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

    Baca dokumen berikut untuk mengetahui detail selengkapnya:

    x86_64

    ABI ini ditujukan untuk CPU yang mendukung kumpulan petunjuk yang biasanya 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
    

    Flag ini menargetkan kumpulan petunjuk x86-64, sesuai dengan dokumentasi GCC, beserta ekstensi kumpulan petunjuk MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, dan POPCNT. Kode yang dihasilkan adalah pengoptimalan yang diseimbangkan ke seluruh CPU 32 bit Intel teratas.

    Untuk mengetahui informasi selengkapnya tentang flag compiler, khususnya yang berkaitan dengan pengoptimalan performa, lihat 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, selama Anda menggunakan pemeriksaan fitur waktu proses untuk mengaktifkannya, dan menyediakan fallback untuk perangkat yang tidak mendukungnya.

    Baca dokumen berikut untuk mengetahui 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 membuat 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 Anda tetapkan 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 untuk membuat build dengan NDK, lihat Panduan CMake.

    Perilaku default sistem build adalah menyertakan biner untuk setiap ABI dalam satu APK, juga disebut dengan fat APK. Fat APK secara signifikan berukuran lebih besar dari APK yang hanya memuat biner untuk satu ABI; kesepakatannya adalah, mendapatkan kompatibilitas yang lebih luas tetapi ukuran APK akan menjadi lebih besar. Sangat direkomendasikan agar Anda memanfaatkan App Bundle atau Pemisahan APK untuk mengurangi ukuran APK, tetapi kompatibilitas perangkat yang optimal tetap dipertahankan.

    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 pada Platform Android

    Bagian ini memberikan detail tentang cara platform Android mengelola kode native di 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 pada waktu proses ABI mana yang didukungnya, 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 Anda 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 semacam itu, ABI primernya akan menjadi x86, dan ABI sekundernya menjadi armeabi-v7a.

    Anda dapat menginstal otomatis apk bagi 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 pada waktu proses.