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.
  • Endianness 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
  • Neon
  • Tidak kompatibel dengan perangkat ARMv5/v6.
    arm64-v8a
  • AArch64
  • Khusus Armv8.0.
    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
  • x86-64-v1 lengkap, tetapi hanya sebagian x86-64-v2 (tidak ada CMPXCHG16B atau LAHF-SAHF).

    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 ARM 32-bit. Ini mencakup Thumb-2 dan Neon.

    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.

    Untuk mengetahui informasi selengkapnya tentang histori Neon, lihat Dukungan Neon.

    Karena alasan historis, ABI ini menggunakan -mfloat-abi=softfp yang menyebabkan semua float nilai yang akan diteruskan dalam register bilangan bulat dan semua nilai double yang akan diteruskan dalam pasangan register integer saat melakukan panggilan fungsi. Terlepas dari namanya, hanya memengaruhi konvensi panggilan floating point: compiler akan tetap menggunakan instruksi floating point perangkat keras untuk aritmetika.

    ABI ini menggunakan long double 64-bit (IEEE binary64 sama seperti double).

    arm64-v8a

    ABI ini ditujukan bagi CPU ARM 64-bit.

    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.

    Anda dapat menggunakan intriksik 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.

    Di Android, register x18 khusus platform disediakan untuk ShadowCallStack dan tidak boleh disentuh oleh kode. Secara default, versi Clang saat ini 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.

    ABI ini menggunakan long double 128-bit (IEEE binary128).

    x86

    ABI ini ditujukan untuk CPU yang mendukung kumpulan petunjuk yang umumnya dikenal sebagai "x86", "i386", atau "IA-32".

    ABI Android mencakup kumpulan petunjuk dasar serta ekstensi MMX, SSE, SSE2, SSE3, dan SSSE3.

    ABI ini tidak mencakup ekstensi kumpulan petunjuk IA-32 opsional lainnya seperti MOVBE atau varian SSE4 apa pun. Anda masih dapat menggunakan ekstensi ini, selama 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:

    ABI ini menggunakan long double 64-bit (IEEE binary64, sama seperti double, dan bukan long double 80-bit khusus Intel yang lebih umum).

    x86_64

    ABI ini ditujukan untuk CPU yang mendukung kumpulan petunjuk yang umumnya disebut "x86-64".

    ABI Android menyertakan kumpulan petunjuk dasar serta MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, dan petunjuk POPCNT.

    ABI ini tidak mencakup ekstensi kumpulan petunjuk x86-64 opsional lainnya seperti MOVBE, SHA, atau varian AVX. Anda masih dapat menggunakan ekstensi ini selama Anda menggunakan pemeriksaan fitur runtime untuk mengaktifkannya, dan menyediakan fallback untuk perangkat yang tidak mendukungnya.

    Lihat dokumen berikut untuk detail selengkapnya:

    ABI ini menggunakan long double 128-bit (IEEE binary128).

    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 dibangun dan diinstal, tetapi akan mengalami error saat runtime.

    ARMv9: Mengaktifkan PAC dan BTI untuk C/C++

    Mengaktifkan PAC/BTI akan memberikan perlindungan terhadap beberapa vektor serangan. PAC melindungi alamat pengembalian dengan menandatanganinya secara kriptografis di prolog fungsi dan memeriksa apakah alamat pengembalian masih ditandatangani dengan benar di epilog. BTI mencegah pengarahan langsung ke lokasi arbitrer dalam kode dengan mewajibkan setiap target cabang berupa petunjuk khusus yang tidak melakukan apa pun selain memberi tahu prosesor bahwa pengarahan tersebut boleh dilakukan.

    Android menggunakan petunjuk PAC/BTI yang tidak melakukan apa pun pada prosesor lama yang tidak mendukung petunjuk baru ini. Hanya perangkat ARMv9 yang akan memiliki perlindungan PAC/BTI, tetapi Anda juga dapat menjalankan kode yang sama di perangkat ARMv8: tidak perlu beberapa varian library. Bahkan pada perangkat ARMv9, PAC/BTI hanya berlaku untuk kode 64-bit.

    Mengaktifkan PAC/BTI akan menyebabkan sedikit peningkatan ukuran kode, biasanya sebesar 1%.

    Lihat bagian Mempelajari arsitektur Arm - Memberikan perlindungan untuk software kompleks (PDF) untuk mengetahui penjelasan mendetail terkait target PAC/BTI vektor serangan dan cara kerja perlindungannya.

    Perubahan build

    ndk-build

    Setel LOCAL_BRANCH_PROTECTION := standard di setiap modul Android.mk Anda.

    CMake

    Gunakan target_compile_options($TARGET PRIVATE -mbranch-protection=standard) untuk setiap target dalam CMakeLists.txt.

    Sistem build lainnya

    Kompilasi kode Anda menggunakan -mbranch-protection=standard. Flag ini hanya berfungsi saat melakukan kompilasi untuk ABI arm64-v8a. Anda tidak perlu menggunakan flag ini saat melakukan penautan.

    Pemecahan masalah

    Kami tidak mengetahui adanya masalah terkait dukungan compiler untuk PAC/BTI, tetapi:

    • Berhati-hatilah agar tidak menggabungkan kode BTI dan non-BTI saat melakukan penautan, karena hal ini akan menghasilkan library yang tidak mengaktifkan perlindungan BTI. Anda dapat menggunakan llvm-readelf untuk memeriksa apakah library yang dihasilkan memiliki catatan BTI atau tidak.
    $ llvm-readelf --notes LIBRARY.so
    [...]
    Displaying notes found in: .note.gnu.property
      Owner                Data size    Description
      GNU                  0x00000010   NT_GNU_PROPERTY_TYPE_0 (property note)
        Properties:    aarch64 feature: BTI, PAC
    [...]
    $
    
    • Versi lama OpenSSL (sebelum 1.1.1i) memiliki bug di assembler yang ditulis tangan dan menyebabkan kegagalan PAC. Upgrade ke OpenSSL terbaru.

    • Versi lama beberapa sistem DRM aplikasi menghasilkan kode yang melanggar persyaratan PAC/BTI. Jika Anda menggunakan DRM aplikasi dan melihat masalah saat mengaktifkan PAC/BTI, hubungi vendor DRM Anda untuk mendapatkan versi yang telah diperbaiki.