Android ABI'lar

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

  • Kullanılabilecek CPU talimat grubu (ve uzantıları).
  • Bellek depolarının ve yüklemelerinin çalışma zamanındaki endianness'ı. Android her zaman little-endian'dır.
  • Uyum kısıtlamaları da dahil olmak üzere uygulamalar ve sistem arasında veri aktarmayla ilgili kurallar ve sistemin işlevleri çağırırken yığını ve kayıtları nasıl kullandığı.
  • 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 Uygulama İkili Arabirimi başlıklı makaleyi inceleyin.
  • C++ adları nasıl karıştırılır? Daha fazla bilgi için Generic/Itanium C++ ABI başlıklı makaleyi inceleyin.

Bu sayfada, NDK'nın desteklediği ABI'ler listelenmekte ve her bir ABI'nin nasıl çalıştığı 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'ler

Tablo 1. ABI'ler ve desteklenen talimat grupları.

ABI Desteklenen Talimat Grupları Notlar
armeabi-v7a
  • armeabi
  • Başparmak-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 için destek yoktur.
    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) ile 32 bit ve 64 bit MIPS'i destekliyordu ancak bu ABI'ler için destek NDK r17'de kaldırıldı.

    armeabi-v7a

    Bu ABI, 32 bit ARM CPU'lar içindir. Thumb-2 ve Neon bu kategoride yer alı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 kullanmadığınız 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ına bakın.

    Geçmişteki nedenlerden dolayı bu ABI, -mfloat-abi=softfp kullanır. Bu nedenle, işlev çağrıları yapılırken tüm float değerleri tam sayı kayıtlarına, tüm double değerleri ise tam sayı kayıt çiftlerine geçirilir. Adına rağmen bu yalnızca kayan nokta çağrı kuralını etkiler: Derleyici, aritmetik işlemler için donanım kayan nokta talimatlarını kullanmaya devam eder.

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

    arm64-v8a

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

    ABI'nin Android'e özgü olmayan kısımlarıyla ilgili tüm ayrıntılar için Arm'ın Learn the Architecture (Mimarinin Temelleri) başlıklı makalesine bakın. Arm ayrıca 64 Bit Android Geliştirme adlı dokümanda bazı taşıma tavsiyeleri sunmaktadır.

    Advanced SIMD uzantısından yararlanmak için C ve C++ kodunda Neon intrinsics kullanabilirsiniz. Neon Programmer's Guide for Armv8-A (Armv8-A için Neon Programcı Kılavuzu), Neon yerleşik özellikleri ve genel olarak Neon programlama hakkında daha fazla bilgi sağlar.

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

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

    x86

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

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

    ABI, MOVBE veya SSE4'ün herhangi bir varyantı gibi diğer isteğe bağlı IA-32 talimat grubu uzantılarını içermez. Bu uzantıları, etkinleştirmek için çalışma zamanı özelliği yoklaması kullandığınız ve bunları desteklemeyen cihazlar için geri dönüşler sağladığınız sürece kullanmaya devam edebilirsiniz.

    NDK araç zinciri, bir 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. Derleme 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 belgelere bakın:

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

    x86_64

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

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

    ABI, MOVBE, SHA veya AVX'in herhangi bir varyantı gibi diğer isteğe bağlı x86-64 talimat grubu uzantılarını içermez. Bu uzantıları, etkinleştirmek için çalışma zamanı özelliği yoklaması kullandığınız ve bunları desteklemeyen cihazlar için geri dönüşler sağladığınız sürece kullanmaya devam edebilirsiniz.

    Daha fazla bilgi için aşağıdaki belgelere bakın:

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

    Belirli bir ABI için kod oluşturma

    Gradle

    Gradle (Android Studio veya komut satırı üzerinden kullanılsın ya da kullanılmasın) varsayılan olarak, desteği sonlandırılmamış tüm ABI'ler için derleme yapar. Uygulamanızın desteklediği ABI'ler kümesini kısıtlamak için abiFilters kullanın. Örneğin, yalnızca 64 bit ABI'ler için derleme oluşturmak 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 kullanımdan kaldırılmamış tüm ABI'ler için derleme yapar. Application.mk dosyanızda APP_ABI değerini ayarlayarak belirli bir ABI'yi hedefleyebilirsiniz. Aşağıdaki snippet'te APP_ABI karakterinin kullanımına ilişkin 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 belirleyebileceğiniz değerler hakkında daha fazla bilgi için Application.mk konusuna bakın.

    CMake

    CMake ile her seferinde 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 hakkında bilgi edinmek için CMake kılavuzu'na bakın.

    Derleme sisteminin varsayılan davranışı, her ABI'nin ikili dosyalarını fat APK olarak da bilinen tek bir APK'ya dahil etmektir. Fat APK, yalnızca tek bir ABI'ye ait ikili dosyaları içeren bir APK'dan önemli ölçüde daha büyüktür. Bu durumda, daha geniş uyumluluk elde edilir ancak APK boyutu artar. Maksimum cihaz uyumluluğunu korurken 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 Yükleme sırasında yerel kodun 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, NDK tarafından oluşturulan kitaplıkların APK'nın içindeki dosya yollarında aşağıdaki kalıba uygun şekilde bulunmasını 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 kitaplığın adıdır. APK dosyaları yalnızca ZIP dosyaları olduğundan, bunları açmak ve paylaşılan yerel kitaplıkların doğru yerde olduğunu onaylamak çok kolaydır.

    Sistem, yerel paylaşılan kitaplıkları beklediği yerde bulamazsa bunları kullanamaz. Bu durumda, uygulamanın kendisi kitaplıkları kopyalamalı ve ardından dlopen() işlemi gerçekleştirmelidir.

    Fat APK'da her kitaplık, adı ilgili ABI ile eşleşen bir dizin altında bulunur. Örneğin, kalın bir APK ş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ümlerin yüklü olduğu ARMv7 tabanlı Android cihazlarda, her iki dizin de varsa yerel kitaplıklar armeabi-v7a dizini yerine armeabi dizininden yüklenir. Bunun nedeni, /lib/armeabi/ öğesinin APK'da /lib/armeabi-v7a/ öğesinden sonra gelmesidir. Bu sorun 4.0.4 sürümünden itibaren düzeltilmiştir.

    Android platformu ABI desteği

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

    • Cihazın birincil ABI'si, sistem görüntüsünde kullanılan makine diline karşılık gelir.
    • İsteğe bağlı olarak, sistem görüntüsünün de 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ı elde etmek için doğrudan birincil ABI için derleme yapmanız gerekir. Örneğin, tipik bir ARMv5TE tabanlı cihaz yalnızca birincil ABI'yi tanımlar: armeabi. Buna karşılık, tipik bir ARMv7 tabanlı cihaz, her biri için oluşturulan uygulama yerel ikililerini çalıştırabildiğinden birincil ABI'yi armeabi-v7a, ikincil ABI'yi ise armeabi olarak tanımlar.

    64 bit cihazlar, 32 bit varyantlarını da destekler. Örnek olarak arm64-v8a cihazları ele alırsak cihaz, armeabi ve armeabi-v7a kodunu da çalıştırabilir. Ancak 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 da armeabi-v7a ve armeabi NDK ikili dosyalarını çalıştırabilir. Bu tür cihazlarda birincil ABI x86, ikincil ABI ise armeabi-v7a olur.

    Belirli bir ABI için bir APK'yı yüklemeye zorlayabilirsiniz. Bu, test için yararlıdır. 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 şu biçimde paylaşılan kitaplıkları arar:

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

    Hiçbiri bulunamazsa ve ikincil bir ABI tanımladıysanız hizmet, şu 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 dizini (<nativeLibraryDir>/) altındaki /lib/lib<name>.so dizinine kopyalar. Aşağıdaki snippet'ler nativeLibraryDir değerini 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 );

    Paylaşılan nesne dosyası yoksa uygulama oluşturulup yüklenir ancak çalışma zamanında çöker.

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

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

    Android, yeni talimatları desteklemeyen eski işlemcilerde hiçbir işe yaramayan PAC/BTI talimatlarını kullanır. PAC/BTI koruması yalnızca ARMv9 cihazlarda bulunur ancak aynı kodu ARMv8 cihazlarda da çalıştırabilirsiniz. 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'nin etkinleştirilmesi, kod boyutunda genellikle %1 oranında küçük bir artışa neden olur.

    PAC/BTI'nın hedeflediği saldırı vektörleri ve korumanın işleyiş şekli hakkında ayrıntılı açıklama için Arm'ın Learn the architecture - Providing protection for complex software (PDF) başlıklı makalesine göz atın.

    Derleme değişiklikleri

    ndk-build

    Android.mk dosyanızın 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

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

    Sorun giderme

    PAC/BTI için derleyici desteğiyle ilgili herhangi bir sorun bilmiyoruz ancak:

    • Bağlantı oluştururken BTI ve BTI dışı kodları karıştırmamaya dikkat edin. Aksi takdirde, BTI koruması etkinleştirilmemiş bir kitaplık elde edilir. Elde ettiğiniz kitaplığın BTI notu içerip içermediğini 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'den önceki sürümler) el yazısıyla yazılmış assembler'da 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 sağlayıcınızla iletişime geçin.