ABI Android

Setiap perangkat Android memiliki CPU yang berbeda sehingga set instruksi yang didukungnya juga berbeda. Setiap kombinasi CPU dan set instruksi memiliki Antarmuka Biner Aplikasi (ABI) sendiri. ABI mencakup informasi berikut:

  • Set instruksi 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 penjajaran, serta cara sistem menggunakan stack dan register saat memanggil fungsi.
  • Format biner yang dapat dijalankan, 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 merangkum ABI yang didukung NDK, dan memberikan informasi tentang cara kerja setiap 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 di NDK r17.

    armeabi-v7a

    ABI ini ditujukan bagi CPU berbasis ARM 32-bit. Varian untuk Android menyertakan instruksi 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 mengeksekusi kode floating-point, kompilator harus meneruskan semua nilai float dalam register bilangan bulat dan semua nilai double dalam pasangan register bilangan bulat 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 Pemrogram Neon untuk Armv8-A menyediakan informasi selengkapnya tentang intrinsik Neon dan pemrograman Neon secara umum.

    Lihat Pelajari Arsitekturnya dari Arm untuk detail selengkapnya 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; jadi, kecuali Anda memiliki assembler yang ditulis secara manual (atau versi kompilator yang sangat lama), Anda tidak perlu khawatir tentang hal ini.

    x86

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

    • Instruksi biasanya dihasilkan oleh GCC dengan tanda kompilator seperti berikut:
          -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32
          

      Tanda ini menargetkan set instruksi Pentium Pro, beserta ekstensi set instruksi 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 tanda kompilator, khususnya yang berkaitan dengan pengoptimalan performa, lihat Petunjuk performa GCC x86.

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

    ABI ini tidak mencakup ekstensi set instruksi IA-32 opsional lainnya, seperti:

    • MOVBE
    • Semua varian SSE4.

    Anda masih dapat menggunakan ekstensi ini, selama Anda menggunakan pendeteksian fitur waktu proses untuk mengaktifkannya, dan menyediakan fallback untuk perangkat yang tidak mendukungnya.

    Toolchain NDK mengasumsikan penyelarasan tumpukan 16-byte sebelum panggilan fungsi. Fitur 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.

    Lihat dokumen berikut untuk detail selengkapnya:

    x86_64

    ABI ini ditujukan untuk CPU yang mendukung set instruksi yang biasanya disebut "x86-64". ABI ini mendukung instruksi yang biasanya dihasilkan oleh GCC dengan tanda kompilator berikut:

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

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

    Untuk mengetahui informasi selengkapnya tentang tanda kompilator, khususnya yang berkaitan dengan pengoptimalan performa, lihat Performa GCC x86.

    ABI ini tidak mencakup ekstensi set instruksi x86-64 opsional lainnya, seperti:

    • MOVBE
    • SHA
    • AVX
    • AVX2

    Anda masih dapat menggunakan ekstensi ini, selama Anda menggunakan pendeteksian fitur waktu proses untuk mengaktifkannya, dan menyediakan fallback untuk perangkat yang tidak mendukungnya.

    Lihat dokumen berikut untuk detail selengkapnya:

    Menghasilkan 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 rangkaian ABI yang didukung aplikasi Anda, gunakan abiFilters. Misalnya, untuk membuat ABI 64-bit saja, tetapkan konfigurasi berikut dalam build.gradle Anda:

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

    ndk-build

    ndk-build melakukan 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 harus 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 tanda lain yang harus diteruskan ke CMake agar dibuat 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 Paket Aplikasi atau Pembagian 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 menjelaskan tentang bagaimana 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 tidak menemukan library native bersama di tempat yang seharusnya, sistem tidak akan dapat menggunakannya. Dalam situasi seperti ini, aplikasi sendiri harus menyalin semua library, lalu melakukan 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

    Pada waktu proses, sistem Android dapat mengetahui ABI mana yang didukungnya, karena properti sistem khusus build menunjukkan:

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

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

    Untuk performa terbaik, Anda harus melakukan kompilasi secara langsung bagi ABI primer. Misalnya, perangkat berbasis ARMv5TE umum hanya akan menentukan ABI utama: armeabi. Sebaliknya, perangkat berbasis ARMv7 umum akan menentukan ABI utama armeabi-v7a dan ABI sekunder armeabi, karena perangkat dapat menjalankan biner native aplikasi yang dibuat untuk tiap-tiapnya.

    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 tersebut, 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 mencari 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.