Farklı Android cihazlar farklı CPU'lar kullanır. Bu da farklı talimat gruplarını destekler. Her CPU ve talimat kümesi kombinasyonunun kendi Uygulama İkili Arabirimi (ABI) vardır. ABI, aşağıdaki bilgileri içerir:
- Kullanılabilen CPU talimat grubu (ve uzantılar).
- Çalışma zamanında bellek depolama ve yükleme bitişi. Android her zaman küçük endian bir iştir.
- Hizalama kısıtlamaları da dahil olmak üzere uygulamalar ile sistem arasında veri aktarımı yapma kuralları ve sistemin, işlevleri çağırırken yığını nasıl kullandığını ve nasıl kaydettiğini öğrenin.
- Programlar ve paylaşılan kitaplıklar gibi yürütülebilir ikili programların biçimi ve destekledikleri içerik türleri. Android her zaman ELF'yi kullanır. Daha fazla bilgi için ELF System V Uygulama İkili Arabirimi bölümüne bakın.
- C++ adlarının nasıl karıştırıldığı. Daha fazla bilgi için Genel/Itanyum C++ ABI sayfasına bakın.
Bu sayfada, NDK'nın desteklediği ABI'ler sıralanmakta ve her bir ABI'nin çalışma şekli hakkında bilgi sağlanmaktadır.
ABI, platform tarafından desteklenen yerel API'ye de işaret edebilir. 32 bit sistemleri etkileyen bu tür ABI sorunlarının listesi için 32 bit ABI hataları bölümüne bakın.
Desteklenen ABI'ler
ABI | Desteklenen Talimat Grupları | Notlar |
---|---|---|
armeabi-v7a |
|
ARMv5/v6 cihazlarla uyumlu değildir. |
arm64-v8a |
Yalnızca Armv8.0. | |
x86 |
MOVBE veya SSE4 için destek yoktur. | |
x86_64 |
|
Yalnızca x86-64-v1. |
Not: NDK, geçmişte ARMv5 (armeabi) ile 32 bit ve 64 bit MIPS'yi 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'u içerir.
ABI'nın Android'e özel olmayan bölümleri hakkında bilgi edinmek için ARM Mimarisi için Uygulama İkili Arabirimi (ABI) sayfasına bakın.
NDK'nın derleme sistemleri, CMake'i yapılandırırken Android.mk
içinde LOCAL_ARM_MODE
, CMake'ı yapılandırırken ANDROID_ARM_MODE
kullanmazsanız varsayılan olarak Thumb-2 kodu oluşturur.
Neon'un geçmişi hakkında daha fazla bilgi edinmek için Neon Desteği sayfasını ziyaret edin.
Geçmişteki nedenlerden dolayı bu ABI, -mfloat-abi=softfp
işlevini kullanarak tüm float
değerlerinin tam sayı kayıtlarında geçirilmesine ve tüm double
değerlerinin işlev çağrıları yapılırken tam sayı kayıt çiftlerinde geçirilmesine 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 bitlik bir long double
(double
ile aynı IEEE ikili programı64) kullanır.
kol64-v8a
Bu ABI, 64 bit ARM CPU'lar içindir.
ABI'nın Android'e özel olmayan bölümlerinin tüm ayrıntıları için Arm'ın Mimariyi Öğrenme sayfasına bakın. Arm, ayrıca 64 bit Android Geliştirme konusunda bazı taşıma önerileri de sunmaktadır.
Gelişmiş SIMD uzantısından yararlanmak için C ve C++ kodunda Neon intrinsics özelliğini kullanabilirsiniz. Neon Programmer's Guide for Armv8-A (Arv8-A İçin Neon Programcı Kılavuzu) belgesinde, genel olarak Neon iç yapısı ve Neon programlama hakkında daha fazla bilgi yer alır.
Android'de, platforma özel x18 kaydı ShadowCallStack için ayrılmıştır ve kodunuza dokunulmamalıdır. Clang'ın mevcut sürümleri, Android'de varsayılan olarak -ffixed-x18
seçeneğini kullanır. Bu nedenle, elle yazabileceğiniz bir derleyiciniz (veya çok eski bir derleyiciniz) yoksa bu konuda endişelenmeniz gerekmez.
Bu ABI, 128 bitlik bir long double
(IEEE ikili programı128) kullanır.
86 kat
Bu ABI, genellikle "x86", "i386" veya "IA-32" olarak bilinen talimat grubunu destekleyen CPU'lar içindir.
Android'in ABI'sı, 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 isteğe bağlı başka IA-32 talimat grubu uzantıları içermez. Bu uzantıları etkinleştirmek için çalışma zamanı özellik kontrolü kullandığınız ve bunları desteklemeyen cihazlar için yedekler sağladığınız sürece bunları kullanmaya devam edebilirsiniz.
NDK araç zinciri, işlev çağrısından önce 16 baytlık yığın hizalamasının olduğunu varsayar. Varsayılan araçlar ve seçenekler bu kuralı uygular. 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 belgeleri inceleyin:
- Farklı C++ derleyicileri ve işletim sistemleri için çağrı kuralları
- Intel IA-32 Intel Architecture Software Developer's Manual, 2. Cilt: Talimat Seti Referansı
- Intel IA-32 Intel Architecture Software Developer's Manual, Volume 3: Sistem Programlama Kılavuzu
- Sistem V Uygulama İkili Programı Arabirimi: Intel386 İşlemci Mimari Eki
Bu ABI, 64 bitlik bir long double
kullanır (IEEE ikili programı64 double
ile aynıdır ve daha yaygın olan 80 bit Intel-yalnızca Intel long double
ile aynı değildir).
x86_64
Bu ABI, genellikle "x86-64" olarak adlandırılan talimat grubunu destekleyen CPU'lar içindir.
Android'in ABI'sı, 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 isteğe bağlı diğer x86-64 talimat grubu uzantılarını içermez. Bu uzantıları, etkinleştirmek için çalışma zamanı özellik kontrolü kullandığınız ve bunları desteklemeyen cihazlar için yedekler sağladığınız sürece bunları kullanmaya devam edebilirsiniz.
Daha fazla bilgi için aşağıdaki belgeleri inceleyin:
- Farklı C++ derleyicileri ve işletim sistemleri için çağrı kuralları
- Intel64 ve IA-32 Architectures Software Developer's Kılavuz, Cilt 2: Talimat Seti Referans
- Intel64 ve IA-32 Intel Architecture Software Developer's Kılavuz Cilt 3: Sistem Programlama
Bu ABI, 128 bitlik bir long double
(IEEE ikili programı128) kullanır.
Belirli bir ABI için kod oluşturma
Gradle
Gradle (ister Android Studio üzerinden ister komut satırından kullanılsın), varsayılan olarak kullanımdan kaldırılmamış tüm ABI'ler için derleme yapar. Uygulamanızın desteklediği ABI grubunu kısıtlamak için abiFilters
değerini kullanın. Örneğin, yalnızca 64 bit ABI'ler için derleme yapmak üzere build.gradle
dosyanızda aşağıdaki yapılandırmayı ayarlayın:
android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a', 'x86_64'
}
}
}
ndk-build
Varsayılan olarak kullanımdan kaldırılmayan tüm ABI'ler için ndk-build derlemeleri. Application.mk dosyanızda APP_ABI
değerini ayarlayarak belirli bir ABI'leri hedefleyebilirsiniz. Aşağıdaki snippet'te APP_ABI
kullanımına dair 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 bilgiyi Application.mk adresinde bulabilirsiniz.
CMake
CMake ile, bir seferde tek bir ABI için derlersiniz ve ABI'nızı açık bir şekilde 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 derlemek amacıyla CMake'e iletilmesi gereken diğer işaretler için CMake kılavuzuna bakın.
Derleme sisteminin varsayılan davranışı, her ABI için ikili programları tek bir APK'ya eklemektir. Bu, şişman APK olarak da bilinir. Şişman bir APK, tek bir ABI için yalnızca ikili programları içeren bir APK'dan çok daha büyüktür; bunun karşılığında daha fazla uyumluluk sağlanır, ancak bunun karşılığında daha büyük bir APK gerekir. Maksimum cihaz uyumluluğunu korurken APK'larınızın boyutunu küçültmek için Uygulama Paketleri veya APK Bölmelerinden yararlanmanız önemle tavsiye edilir.
Yükleme sırasında paket yöneticisi, hedef cihaz için yalnızca en uygun makine kodunu açar. Ayrıntılar için Yükleme sırasında yerel kodun otomatik olarak çıkarılması bölümünü inceleyin.
Android platformunda ABI yönetimi
Bu bölümde, Android platformunun APK'larda yerel kodu nasıl yönettiğine dair ayrıntılar sağlanmaktadır.
Uygulama paketlerindeki yerel kod
Hem Play Store hem de Paket Yöneticisi, aşağıdaki kalıpla eşleşen APK içindeki dosya yollarında NDK tarafından oluşturulan kitaplıkları bulmayı bekler:
/lib/<abi>/lib<name>.so
Burada <abi>
, Desteklenen ABI'ler başlığı altında listelenen ABI adlarından biridir. <name>
ise kitaplığın, Android.mk
dosyasında LOCAL_MODULE
değişkeni için tanımladığınız adıdır. APK dosyaları sadece zip dosyaları olduğundan, bunları açmak ve paylaşılan yerel kitaplıkların ait oldukları yerde olduklarını onaylamak çok önemlidir.
Sistem, beklendiği yerde yerel paylaşılan kitaplıkları bulamazsa bunları kullanamaz. Böyle bir durumda, uygulamanın kendisi kitaplıkları kopyalamalı ve ardından dlopen()
işlemi yapmalıdır.
Yağ APK'sında her kitaplık, adı karşılık gelen bir ABI ile eşleşen bir dizin altında bulunur. Örneğin, şişman 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 daha eski 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/
kelimesinin APK'da /lib/armeabi-v7a/
kısmından
sonra gelmesidir. Bu sorun 4.0.4 sürümünde düzeltilmiştir.
Android platformu ABI desteği
Derlemeye özel sistem özellikleri aşağıdakileri gösterdiğinden Android sistemi, çalışma zamanında hangi ABI'leri desteklediğini bilir:
- Sistem görüntüsünün kendisinde kullanılan makine koduna karşılık gelen, cihazın birincil ABI'sı.
- İ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 yükleme sırasında paketten en iyi makine kodunu çıkarmasını sağlar.
En iyi performansı elde etmek istiyorsanız doğrudan birincil ABI için derleme yapmanız gerekir. Örneğin, ARMv5TE tabanlı tipik bir cihaz yalnızca birincil ABI'yi tanımlar: armeabi
. Buna karşılık, ARMv7 tabanlı tipik bir cihaz, birincil ABI'yi armeabi-v7a
ve ikincil ABI'yı armeabi
olarak tanımlar. Bunun nedeni, her biri için oluşturulan uygulama yerel ikili kodlarını çalıştırabilmesidir.
64 bit cihazlar, 32 bit varyantlarını da destekler. Örnek olarak arm64-v8a cihazları ele alındığında, cihaz armeabi ve armeabi-v7a kodunu da çalıştırabilir. Ancak uygulamanızın arm64-v8a'yı hedeflemesi yerine 64 bit cihazlarda çok daha iyi performans göstereceğini unutmayın.
x86 tabanlı cihazların çoğu, armeabi-v7a
ve armeabi
NDK ikili programlarını da çalıştırabilir. Bu tür cihazlar için birincil ABI x86
, ikinci ABI ise armeabi-v7a
olur.
Belirli bir ABI için apk'yi zorunlu olarak yükleyebilirsiniz. Bu, test için yararlıdır. Şu komutu kullanın:
adb install --abi abi-identifier path_to_apk
Yükleme sırasında yerel kodun otomatik olarak çıkarılması
Bir uygulamayı yüklerken, paket yöneticisi hizmeti APK'yı tarar ve formun paylaşılan kitaplıklarını arar:
lib/<primary-abi>/lib<name>.so
Hiçbiri bulunmazsa ve ikincil bir ABI tanımladıysanız hizmet, şu biçimde paylaşılan kitaplıklar için tarama yapar:
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
konumuna kopyalar. Aşağıdaki snippet'ler nativeLibraryDir
bilgisini 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 );
Hiçbir paylaşılan nesne dosyası yoksa uygulama derlenir ve yüklenir ancak çalışma zamanında kilitlenir.
ARMv9: C/C++ için PAC ve BTI'yi etkinleştirme
PAC/BTI'nın etkinleştirilmesi bazı saldırı vektörlerine karşı koruma sağlar. PAC, iade adreslerini işlevin prolog'unda kriptografik olarak imzalayarak ve dönüş adresinin hâlâ epilogda doğru şekilde imzalanıp imzalanmadığını kontrol ederek korur. BTI, her dal hedefinin işlemciye oraya gitmenin sorun olmadığını söylemekten başka bir işlevi olmayan özel bir talimat olmasını gerektirerek kodunuzdaki rastgele konumlara atlamayı önler.
Android, yeni talimatları desteklemeyen eski işlemcilerde hiçbir işlem yapmayan PAC/BTI talimatlarını kullanır. PAC/BTI koruması yalnızca ARMv9 cihazlarında 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'nın etkinleştirilmesi kod boyutunda küçük bir artışa (genellikle %1) neden olur.
PAC/BTI hedefi saldırı vektörleri ve korumanın işleyiş şekliyle ilgili ayrıntılı bir açıklama için Arm'ın Mimariyi öğrenme - Karmaşık yazılımlar için koruma sağlama (PDF) bölümüne bakın.
Derleme değişiklikleri
ndk-build
Android.mk'nizin her modülünde LOCAL_BRANCH_PROTECTION := standard
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 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 sorun olduğunu bilmiyoruz ancak:
- Bağlama işlemi sırasında BTI ile BTI olmayan kodları karıştırmamaya dikkat edin. Aksi takdirde BTI korumasının etkinleştirilmediği bir kitaplık ortaya çıkar. Elde ettiğiniz kitaplıkta BTI notu olup olmadığını kontrol etmek için llvm-readelf 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), elle yazılmış derleyicide PAC hatalarına neden olan bir hata vardır. Mevcut OpenSSL'ye geçin.
Bazı uygulama DRM sistemlerinin eski sürümleri, PAC/BTI gereksinimlerini ihlal eden kodlar oluşturur. Uygulama DRM kullanıyorsanız ve PAC/BTI'yı etkinleştirirken sorunlarla karşılaşıyorsanız düzeltilmiş bir sürüm için DRM tedarikçi firmanızla iletişime geçin.