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 |
|
ARMv5/v6 cihazlarla uyumlu değildir. |
arm64-v8a |
Yalnızca Armv8.0. | |
x86 |
MOVBE veya SSE4 desteklenmez. | |
x86_64 |
|
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:
- Farklı C++ derleyicileri ve işletim sistemleri için çağrı kuralları
- Intel IA-32 Intel Architecture Software Developer's Manual, Volume 2: Instruction Set Reference
- Intel IA-32 Intel Architecture Software Developer's Manual, Volume 3: System Programming Guide
- System V Uygulama İkili Arayüzü: Intel386 İşlemci Mimarisi Ek
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:
- Farklı C++ derleyicileri ve işletim sistemleri için çağrı kuralları
- Intel64 ve IA-32 Mimarileri Yazılım Geliştirici Kılavuzu, Cilt 2: Komut Kümesi Referansı
- Intel64 ve IA-32 Intel Architecture Yazılım Geliştirici Kılavuzu Cilt 3: Sistem Programlama
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.