Android ABI'lar

Farklı Android cihazlar farklı CPU'lar kullanır ve bu CPU'lar da farklı talimat gruplarını destekler. Her CPU ve talimat seti kombinasyonunun kendi Uygulama İkili Arayüzü (ABI) vardır. ABI aşağıdaki bilgileri içerir:

  • Kullanılabilen CPU talimat grubu (ve uzantıları).
  • Çalışma zamanında bellek depolarının ve yüklemelerin büyük endian veya küçük endian olması. Android her zaman küçük endian'dır.
  • Uygulamalar ile sistem arasında veri aktarımı için kullanılan kurallar (hizalama kısıtlamaları ve sistemin işlevleri çağırırken yığını ve kayıtları nasıl kullandığı dahil).
  • Programlar ve paylaşılan kitaplıklar gibi yürütülebilir ikili dosyaların biçimi ve destekledikleri içerik türleri. Android her zaman ELF kullanır. Daha fazla bilgi için ELF System V Application Binary Interface başlıklı makaleyi inceleyin.
  • C++ adlarının nasıl bozulduğunu gösteren resim. Daha fazla bilgi için Genel/Itanium C++ ABI başlıklı makaleyi inceleyin.

Bu sayfada, NDK'nın desteklediği ABI'ler listelenmekte ve her ABI'nin işleyiş şekli hakkında bilgi verilmektedir.

ABI, platform tarafından desteklenen yerel API'yi de ifade edebilir. 32 bit sistemleri etkileyen bu tür ABI sorunlarının listesi için 32 bit ABI hataları başlıklı makaleyi inceleyin.

Desteklenen ABI'lar

Tablo 1. ABI'ler ve desteklenen talimat kümeleri.

ABI Desteklenen Talimat Grupları Notlar
armeabi-v7a
  • armeabi
  • Thumb-2
  • Neon
  • ARMv5/v6 cihazlarla uyumlu değildir.
    arm64-v8a
  • AArch64
  • Yalnızca Armv8.0.
    x86
  • x86 (IA-32)
  • MMX
  • SSE/2/3
  • SSSE3
  • MOVBE veya SSE4 desteklenmez.
    x86_64
  • x86-64
  • MMX
  • SSE/2/3
  • SSSE3
  • SSE4.1, 4.2
  • POPCNT
  • CMPXCHG16B
  • LAHF-SAHF
  • Tam x86-64-v2.

    Not: NDK geçmişte ARMv5 (armeabi) ve 32 bit ile 64 bit MIPS'i desteklemesine rağmen bu ABI'ler için destek NDK r17'de kaldırılmıştır.

    armeabi-v7a

    Bu ABI, 32 bit ARM CPU'lar içindir. Thumb-2 ve Neon da bu kapsamdadır.

    ABI'nin Android'e özgü olmayan bölümleri hakkında bilgi edinmek için ARM Mimarisi için Uygulama İkili Arabirimi (ABI) başlıklı makaleyi inceleyin.

    NDK'nın derleme sistemleri, ndk-build için Android.mk dosyanızda LOCAL_ARM_MODE veya CMake'i yapılandırırken ANDROID_ARM_MODE kullanmadığınız sürece varsayılan olarak Thumb-2 kodu oluşturur.

    Neon'un geçmişi hakkında daha fazla bilgi için Neon Destek sayfasını inceleyin.

    Geçmiş nedenlerden dolayı bu ABI, -mfloat-abi=softfp kullanır. Bu da işlev çağrıları yapılırken tüm float değerlerinin tam sayı kaydedicilerinde ve tüm double değerlerinin tam sayı kaydedici çiftlerinde iletilmesine neden olur. Adına rağmen bu, yalnızca kayan nokta çağrı kuralını etkiler: Derleyici, aritmetik için donanım kayan nokta talimatlarını kullanmaya devam eder.

    Bu ABI, 64 bit long double (double ile aynı IEEE binary64) kullanır.

    arm64-v8a

    Bu ABI, 64 bit ARM CPU'lar içindir.

    ABI'nin Android'e özgü olmayan bölümleriyle ilgili tüm ayrıntılar için Arm'ın Mimariyi Öğrenin başlıklı makalesine bakın. Arm, 64 bit Android Geliştirme bölümünde de bazı taşıma önerileri sunar.

    Gelişmiş SIMD uzantısından yararlanmak için C ve C++ kodunda Neon iç işlevlerini kullanabilirsiniz. Armv8-A için Neon Programcı Kılavuzu, Neon dahili işlevleri ve genel olarak Neon programlama hakkında daha fazla bilgi sağlar.

    Android'de platforma özgü x18 kaydedicisi ShadowCallStack için ayrılmıştır ve kodunuz tarafından değiştirilmemelidir. Clang'ın mevcut sürümleri Android'de varsayılan olarak -ffixed-x18 seçeneğini kullanır. Bu nedenle, elle yazılmış bir derleyiciniz (veya çok eski bir derleyiciniz) yoksa bu konuda endişelenmenize gerek yoktur.

    Bu ABI, 128 bitlik bir long double (IEEE binary128) kullanır.

    x86

    Bu ABI, genellikle "x86", "i386" veya "IA-32" olarak bilinen talimat setini destekleyen CPU'lar içindir.

    Android'in ABI'si, temel talimat setinin yanı sıra MMX, SSE, SSE2, SSE3 ve SSSE3 uzantılarını içerir.

    ABI, MOVBE veya SSE4'ün herhangi bir varyantı gibi isteğe bağlı başka IA-32 talimat seti uzantıları içermez. Bu uzantıları etkinleştirmek için çalışma zamanında özellik tarama özelliğini kullandığınız ve bunları desteklemeyen cihazlar için yedek çözümler sağladığınız sürece kullanmaya devam edebilirsiniz.

    NDK araç zinciri, işlev çağrısından önce 16 baytlık yığın hizalaması olduğunu varsayar. Varsayılan araçlar ve seçenekler bu kuralı zorunlu kılar. Birleştirme kodu yazıyorsanız yığın hizalamasını koruduğunuzdan ve diğer derleyicilerin de bu kurala uyduğundan emin olmanız gerekir.

    Daha fazla bilgi için aşağıdaki dokümanlara bakın:

    Bu ABI, 64 bit long double (double ile aynı olan IEEE binary64, daha yaygın olan yalnızca Intel 80 bit long double değildir) kullanır.

    x86_64

    Bu ABI, genellikle "x86-64" olarak adlandırılan talimat setini destekleyen CPU'lar içindir.

    Android'in ABI'si, temel talimat setinin yanı sıra MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2 ve POPCNT talimatlarını içerir.

    ABI, MOVBE, SHA veya AVX'nin herhangi bir varyantı gibi isteğe bağlı diğer x86-64 talimat seti uzantılarını içermez. Bu uzantıları etkinleştirmek için çalışma zamanında özellik araştırması kullandığınızda ve bunları desteklemeyen cihazlar için yedek seçenekler sağladığınızda kullanmaya devam edebilirsiniz.

    Daha fazla bilgi için aşağıdaki dokümanlara bakın:

    Bu ABI, 128 bitlik bir long double (IEEE binary128) kullanır.

    Belirli bir ABI için kod oluşturma

    Gradle

    Gradle (Android Studio üzerinden veya komut satırından kullanılsın), varsayılan olarak desteği sonlandırılmamış tüm ABI'ler için derleme yapar. Uygulamanızın desteklediği ABI grubunu kısıtlamak için abiFilters öğesini kullanın. Örneğin, yalnızca 64 bit ABI'ler için derleme yapmak istiyorsanız build.gradle dosyanızda aşağıdaki yapılandırmayı ayarlayın:

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

    ndk-build

    ndk-build, varsayılan olarak desteği sonlandırılmamış tüm ABI'ler için derleme yapar. Application.mk dosyanızda APP_ABI ayarlayarak belirli ABI'leri hedefleyebilirsiniz. Aşağıdaki snippet'te, APP_ABI kullanımıyla ilgili birkaç örnek gösterilmektedir:

    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.
    

    APP_ABI için belirtebileceğiniz değerler hakkında daha fazla bilgi için Application.mk dosyasını inceleyin.

    CMake

    CMake ile tek seferde tek bir ABI için derleme yaparsınız ve ABI'nizi açıkça belirtmeniz gerekir. Bunu, komut satırında belirtilmesi gereken ANDROID_ABI değişkeniyle yaparsınız (CMakeLists.txt dosyanızda ayarlanamaz). Örneğin:

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

    NDK ile derleme yapmak için CMake'e iletilmesi gereken diğer işaretler için CMake kılavuzuna bakın.

    Derleme sisteminin varsayılan davranışı, her ABI'nin ikili dosyalarını büyük APK olarak da bilinen tek bir APK'ya dahil etmektir. Büyük APK, yalnızca tek bir ABI'nin ikili dosyalarını içeren APK'dan önemli ölçüde daha büyüktür. Bu durumda, daha geniş uyumluluk elde edilir ancak APK daha büyük olur. Cihaz uyumluluğunu en üst düzeyde tutarken APK'larınızın boyutunu küçültmek için uygulama paketlerinden veya APK bölmelerinden yararlanmanız önemle tavsiye edilir.

    Paket yöneticisi, yükleme sırasında yalnızca hedef cihaz için en uygun makine kodunu açar. Ayrıntılı bilgi için Yerli kodun yükleme sırasında otomatik olarak ayıklanması başlıklı makaleyi inceleyin.

    Android platformunda ABI yönetimi

    Bu bölümde, Android platformunun APK'lardaki yerel kodu nasıl yönettiği hakkında ayrıntılı bilgi verilmektedir.

    Uygulama paketlerindeki yerel kod

    Hem Play Store hem de Paket Yöneticisi, APK'daki dosya yollarında aşağıdaki kalıpla eşleşen NDK tarafından oluşturulmuş kitaplıklar bulmayı bekler:

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

    Burada <abi>, Desteklenen ABI'ler altında listelenen ABI adlarından biridir ve <name>, Android.mk dosyasındaki LOCAL_MODULE değişkeni için tanımladığınız şekilde kitaplığın adıdır. APK dosyaları yalnızca ZIP dosyaları olduğundan, bunları açmak ve paylaşılan yerel kitaplıkların ait oldukları yerde olduğunu onaylamak çok kolaydır.

    Sistem, yerel paylaşılan kitaplıkları beklediği yerde bulamazsa bunları kullanamaz. Bu durumda, uygulamanın kitaplıkları kopyalayıp dlopen() işlemini gerçekleştirmesi gerekir.

    Büyük APK'larda her kitaplık, adı ilgili ABI ile eşleşen bir dizin altında bulunur. Örneğin, büyük APK'lar şunları içerebilir:

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

    Not: 4.0.3 veya önceki sürümleri çalıştıran ARMv7 tabanlı Android cihazlar, her iki dizin de mevcutsa yerel kitaplıkları armeabi-v7a dizini yerine armeabi dizininden yükler. Bunun nedeni, /lib/armeabi/'ün APK'da /lib/armeabi-v7a/'ten sonra gelmesidir. Bu sorun 4.0.4 sürümünden itibaren düzeltilmiştir.

    Android platform ABI desteği

    Derlemeye özgü sistem özellikleri aşağıdakileri belirttiğinden Android sistemi, çalışma zamanında hangi ABI'leri desteklediğini bilir:

    • Sistem görüntüsünde kullanılan makine koduna karşılık gelen, cihazın birincil ABI'si.
    • İsteğe bağlı olarak, sistem görüntüsünün desteklediği diğer ABI'lere karşılık gelen ikincil ABI'ler.

    Bu mekanizma, sistemin kurulum sırasında paketten en iyi makine kodunu çıkarmasını sağlar.

    En iyi performans için doğrudan birincil ABI için derlemeniz gerekir. Örneğin, tipik bir ARMv5TE tabanlı cihaz yalnızca birincil ABI'yi tanımlar: armeabi. Buna karşılık, ARMv7 tabanlı tipik bir cihaz, her biri için oluşturulan uygulama yerel ikililerini çalıştırabileceğinden birincil ABI'yi armeabi-v7a ve ikincil ABI'yi armeabi olarak tanımlar.

    64 bit cihazlar, 32 bit varyantlarını da destekler. Örnek olarak arm64-v8a cihazları kullanacak olursak cihaz, armeabi ve armeabi-v7a kodunu da çalıştırabilir. Bununla birlikte, uygulamanızın armeabi-v7a sürümünü çalıştıran cihaza güvenmek yerine arm64-v8a'yı hedeflemesi durumunda 64 bit cihazlarda çok daha iyi performans göstereceğini unutmayın.

    Birçok x86 tabanlı cihaz, armeabi-v7a ve armeabi NDK ikililerini de çalıştırabilir. Bu tür cihazlarda birincil ABI x86, ikinci ABI ise armeabi-v7a olur.

    Belirli bir ABI için APK'yı zorla yükleyebilirsiniz. Bu işlem, test için yararlı olabilir. Aşağıdaki komutu kullanın:

    adb install --abi abi-identifier path_to_apk
    

    Yükleme sırasında yerel kodun otomatik olarak ayıklanması

    Paket yöneticisi hizmeti, bir uygulamayı yüklerken APK'yı tarar ve aşağıdaki biçimdeki paylaşılan kitaplıkları arar:

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

    Hiçbiri bulunamazsa ve ikincil bir ABI tanımladıysanız hizmet aşağıdaki biçimdeki paylaşılan kitaplıkları tarar:

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

    Paket yöneticisi, aradığı kitaplıkları bulduğunda bunları uygulamanın yerel kitaplık dizininin (<nativeLibraryDir>/) altındaki /lib/lib<name>.so konumuna kopyalar. Aşağıdaki snippet'ler nativeLibraryDir öğesini alır:

    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 );

    Ortak nesne dosyası yoksa uygulama derlenip yüklenir ancak çalışma zamanında kilitlenir.

    ARMv9: C/C++ için PAC ve BTI'yi etkinleştirme

    PAC/BTI'yi etkinleştirmek, bazı saldırı vektörlerine karşı koruma sağlar. PAC, işlevin prologunda kriptografik olarak imzalayarak ve epilogda dönüş adresinin hâlâ doğru şekilde imzalandığını kontrol ederek dönüş adreslerini korur. BTI, her dal hedefinin işlemciye oraya inmenin uygun olduğunu söylemekten başka bir şey yapmayan özel bir talimat olmasını zorunlu kılarak kodunuzda rastgele konumlara atlamayı önler.

    Android, yeni talimatları desteklemeyen eski işlemcilerde hiçbir şey yapmayan PAC/BTI talimatlarını kullanır. Yalnızca ARMv9 cihazlarda PAC/BTI koruması bulunur ancak aynı kodu ARMv8 cihazlarda da çalıştırabilirsiniz. Bu durumda kitaplığınızın birden fazla varyantına gerek yoktur. ARMv9 cihazlarda bile PAC/BTI yalnızca 64 bit kod için geçerlidir.

    PAC/BTI'yi etkinleştirmek, kod boyutunda genellikle %1 oranında küçük bir artışa neden olur.

    PAC/BTI hedefi saldırı vektörlerinin ve korumanın işleyiş şeklinin ayrıntılı bir açıklaması için Arm'ın Mimarisi öğrenin: Karmaşık yazılımlar için koruma sağlama (PDF) başlıklı makalesine bakın.

    Derleme değişiklikleri

    ndk-build

    Android.mk dosyanızı her modülünde LOCAL_BRANCH_PROTECTION := standard değerini ayarlayın.

    CMake

    CMakeLists.txt dosyanızdaki her hedef için target_compile_options($TARGET PRIVATE -mbranch-protection=standard) kullanın.

    Diğer derleme sistemleri

    Kodunuzu -mbranch-protection=standard kullanarak derleyin. Bu işaret yalnızca arm64-v8a ABI için derleme yaparken çalışır. Bağlantı oluştururken bu işareti kullanmanız gerekmez.

    Sorun giderme

    PAC/BTI için derleyici desteğiyle ilgili herhangi bir sorunla karşılaşmadık ancak:

    • Bağlantı oluştururken BTI kodu ile BTI dışı kodu karıştırmamaya dikkat edin. Aksi takdirde, BTI koruması etkinleştirilmemiş bir kitaplık oluşturulur. Elde ettiğiniz kitaplıkta BTI notunun olup olmadığını kontrol etmek için llvm-readelf'i kullanabilirsiniz.
    $ 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
    [...]
    $
    
    • OpenSSL'nin eski sürümlerinde (1.1.1i öncesi), elle yazılmış derleyicide PAC hatalarına neden olan bir hata vardır. Mevcut OpenSSL sürümüne yükseltin.

    • Bazı uygulama DRM sistemlerinin eski sürümleri, PAC/BTI şartlarını ihlal eden kodlar oluşturur. Uygulama DRM'si kullanıyorsanız ve PAC/BTI'yi etkinleştirirken sorun yaşıyorsanız düzeltilmiş bir sürüm için DRM tedarikçinizle iletişime geçin.