Verschiedene Android-Geräte verwenden unterschiedliche CPUs, die wiederum unterschiedliche Anweisungssätze. Jede Kombination aus CPU und Befehlssatz hat ihre eigene Binärschnittstelle (Application Binary Interface, ABI) Ein ABI enthält die folgenden Informationen:
- Der CPU-Befehlssatz (und die Erweiterungen), der verwendet werden kann.
- Die Endlosigkeit des Arbeitsspeichers speichert und lädt zur Laufzeit. Android ist immer Little-Endian.
- Konventionen für die Übergabe von Daten zwischen Anwendungen und dem System, einschließlich Ausrichtungsbeschränkungen und wie das System den Stapel und werden beim Aufrufen von Funktionen registriert.
- Das Format ausführbarer Binärdateien wie Programme und gemeinsam genutzte Bibliotheken, und welche Arten von Inhalten sie unterstützen. Android verwendet immer ELF. Weitere Informationen finden Sie unter ELF System V-Binärschnittstelle.
- Wie werden C++-Namen manipuliert? Weitere Informationen finden Sie unter Generisch/Itanium C++ ABI.
Auf dieser Seite sind die ABIs aufgeführt, die vom NDK unterstützt werden, sowie Informationen dazu, wie jedes ABI funktioniert.
ABI kann sich auch auf die native API beziehen, die von der Plattform unterstützt wird. Für eine Liste dieser Arten von ABI-Problemen, die 32-Bit-Systeme betreffen, siehe 32-Bit-ABI-Programmfehler
Unterstützte ABIs
ABI | Unterstützte Anweisungssätze | Hinweise |
---|---|---|
armeabi-v7a |
|
Nicht kompatibel mit ARMv5/v6-Geräten. |
arm64-v8a |
Nur Armv8.0. | |
x86 |
Keine Unterstützung für MOVBE oder SSE4. | |
x86_64 |
|
Vollständiges x86-64-v1, aber nur teilweise für x86-64-v2 (kein CMPXCHG16B oder LAHF-SAHF). |
Hinweis:Bisher unterstützte das NDK ARMv5. (Armeabi) sowie 32-Bit- und 64-Bit-MIPS. Die Unterstützung für diese ABIs wurde jedoch in NDK r17.
Armeabi-V7a
Diese ABI gilt für 32-Bit-ARM-CPUs. Sie enthält „Mag ich“-2 und „Neon“.
Informationen zu den Bereichen des ABI, die nicht Android-spezifisch sind, findest du unter Application Binary Interface (ABI) für die ARM-Architektur
Die Build-Systeme des NDK generieren standardmäßig Thumb-2-Code, es sei denn, Sie verwenden
LOCAL_ARM_MODE
in deinem Android.mk
für
ndk-build oder ANDROID_ARM_MODE
wenn Sie CMake konfigurieren.
Weitere Informationen zur Geschichte von Neon finden Sie unter Neon-Unterstützung.
Aus historischen Gründen verwendet dieses ABI -mfloat-abi=softfp
und verursacht alle float
Werte, die in Ganzzahlregistern übergeben werden sollen, und alle double
-Werte, die übergeben werden sollen
in Ganzzahlregisterpaaren verwendet werden. Trotz des Namens
wirkt sich nur auf die Aufrufkonvention für Gleitkommazahlen aus: Der Compiler macht weiterhin
Hardware-Gleitkommaanweisungen
für die Arithmetik verwenden.
Diese ABI verwendet eine 64-Bit-long double
(IEEE binary64 wie double
).
arm64-v8a
Diese ABI gilt für 64-Bit-ARM-CPUs.
Arms ansehen Architektur kennenlernen . Verzweigung gibt auch Tipps zur Rufnummernmitnahme 64-Bit-Android-Entwicklung
Sie können Neon-Intrinsiken verwenden. in C und C++ Code, um die Vorteile der erweiterten SIMD-Erweiterung zu nutzen. Die Neon-Programmiererhandbuch für Armv8-A finden Sie weitere Informationen zur Neon-Intrinsik und zur Neon-Programmierung im Allgemeinen.
Unter Android ist das plattformspezifische x18-Register für
ShadowCallStack
und darf von Ihrem Code nicht berührt werden. Aktuelle Versionen von Clang sind standardmäßig auf
die Option -ffixed-x18
unter Android verwenden, es sei denn, Sie haben handschriftliche
Assembler (oder einen sehr alten Compiler) benötigen,
sollten Sie sich darüber keine Gedanken machen.
Diese ABI verwendet eine 128-Bit-long double
(IEEE-Binärprogramm128).
x86
Dieses ABI ist für CPUs, die den Befehlssatz unterstützen, der allgemein als „x86“ bezeichnet wird. „i386“ oder „IA-32“.
Das ABI von Android enthält den Basisanweisungssatz plus MMX, SSE SSE2 SSE3 und SSSE3-Erweiterungen.
Die ABI enthält keinen anderen optionalen IA-32-Anweisungssatz wie MOVBE oder eine beliebige Variante von SSE4. Sie können diese Erweiterungen trotzdem verwenden, solange Sie die Prüfung auf aktivieren und Fallbacks für Geräte bereitstellen, die diese Funktionen nicht unterstützen.
Die NDK-Toolchain geht vor einem Funktionsaufruf von einer 16-Byte-Stack-Ausrichtung aus. Die Standardtools und Optionen diese Regel erzwingen. Wenn Sie Assembly-Code schreiben, müssen Sie sicherstellen, und sicherzustellen, dass auch andere Compiler diese Regel befolgen.
Weitere Informationen finden Sie in den folgenden Dokumenten:
- Anrufe Konventionen für verschiedene C++-Compiler und Betriebssysteme
- Intel IA-32 Intel Architecture Software Developer's Handbuch, Volume 2: Anleitungssatz-Referenz
- Informationen IA-32 Intel Architecture Software Developer's Handbuch, Volume 3: System Programmierungsanleitung
- Binärprogramm V-Anwendung Schnittstelle: Ergänzung zur Intel386-Prozessorarchitektur
Diese ABI verwendet ein 64-Bit-long double
(IEEE binary64 wie double
, und nicht das
gängige 80-Bit-Nur-Intel-long double
).
x86_64
Diese ABI ist für CPUs vorgesehen, die den Befehlssatz unterstützen, der allgemein als „x86-64“.
Das ABI von Android enthält den Basisanweisungssatz plus MMX SSE SSE2 SSE3 SSSE3 SSE4.1 SSE4.2 und der POPCNT-Anweisung.
Die ABI enthält keinen anderen optionalen x86-64-Anweisungssatz wie MOVBE, SHA oder eine beliebige Variante von AVX. Sie können diese Erweiterungen trotzdem verwenden, solange Sie die Prüfung auf aktivieren und Fallbacks für Geräte bereitstellen, die diese Funktionen nicht unterstützen.
Weitere Informationen finden Sie in den folgenden Dokumenten:
- Aufrufkonventionen für verschiedene C++-Compiler und Betriebssysteme
- <ph type="x-smartling-placeholder"></ph> Handbuch für Intel64- und IA-32-Architekturen-Softwareentwickler, Volume 2: Anleitungssatz Referenz
- <ph type="x-smartling-placeholder"></ph> Intel64 and IA-32 Intel Architecture Software Developer's Handbuch Volume 3: Systemprogramming
Diese ABI verwendet eine 128-Bit-long double
(IEEE-Binärprogramm128).
Code für ein bestimmtes ABI generieren
Logo: Gradle
Gradle (ob über Android Studio oder über die Befehlszeile verwendet) erstellt
standardmäßig alle nicht eingestellten ABIs. Um die Gruppe von ABIs einzuschränken,
App unterstützt, verwenden Sie abiFilters
. Wenn Sie beispielsweise nur für
64-Bit-ABIs legen Sie in Ihrem build.gradle
die folgende Konfiguration fest:
android {
defaultConfig {
ndk {
abiFilters 'arm64-v8a', 'x86_64'
}
}
}
NK-Build
ndk-build erstellt standardmäßig für alle nicht verworfenen ABIs. Sie können ein Targeting auf
bestimmte ABIs, indem du APP_ABI
in deiner Application.mk-Datei festlegst. Die
Das folgende Snippet zeigt einige Beispiele für die Verwendung von 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.
Weitere Informationen zu den Werten, die Sie für APP_ABI
angeben können, finden Sie unter
Application.mk aus.
CMake
Mit CMake erstellen Sie jeweils nur ein ABI und müssen Ihr ABI angeben
explizit auf. Dazu verwenden Sie die Variable ANDROID_ABI
, die
in der Befehlszeile angegeben werden (kann nicht in der Datei CMakeLists.txt festgelegt werden). Für
Beispiel:
$ cmake -DANDROID_ABI=arm64-v8a ...
$ cmake -DANDROID_ABI=armeabi-v7a ...
$ cmake -DANDROID_ABI=x86 ...
$ cmake -DANDROID_ABI=x86_64 ...
Die anderen Flags, die zum Erstellen mit dem NDK an CMake übergeben werden müssen, finden Sie unter CMake-Leitfaden
Das Standardverhalten des Build-Systems besteht darin, die Binärdateien für jedes ABI einzuschließen in einem einzelnen APK, auch als Fat-APK bezeichnet. Ein fettes APK ist deutlich größer als eines, das nur die Binärdateien für eine einzelne ABI enthält; immer mehr breitere Kompatibilität, allerdings auf Kosten eines größeren APK. Es ist stark wird empfohlen, entweder App Bundles oder APK-Aufteilungen zu nutzen, um die Größe deiner APKs verringern und gleichzeitig die maximale Anzahl an Geräten beibehalten Kompatibilität.
Bei der Installation entpackt der Paketmanager nur die am besten geeigneten Maschinencode für das Zielgerät. Weitere Informationen hierzu finden Sie unter Automatische Extraktion von nativen Code zum Zeitpunkt der Installation.
ABI-Verwaltung auf der Android-Plattform
Dieser Abschnitt enthält Details zur Verwaltung nativer Anzeigen auf der Android-Plattform in APKs verwenden.
Nativer Code in App-Paketen
Sowohl der Play Store als auch der Paketmanager erwarten, dass durch NDK generierte Bibliotheken auf Dateipfaden innerhalb des APK, die folgendem Muster entsprechen:
/lib/<abi>/lib<name>.so
Hier ist <abi>
einer der ABI-Namen, die unter Unterstützte ABIs aufgeführt sind.
und <name>
der Name der Bibliothek ist, wie Sie ihn für LOCAL_MODULE
definiert haben,
Variable in der Datei Android.mk
. Seit
Bei APK-Dateien handelt es sich nur um ZIP-Dateien. Sie müssen sie ganz einfach öffnen und überprüfen,
Bibliotheken sind, wo sie hingehören.
Wenn das System die nativen gemeinsam genutzten Bibliotheken nicht dort findet, wo es erwartet wird, kann es keine
. In einem solchen Fall muss die App die Bibliotheken selbst kopieren
dlopen()
ausführen
In einem Fett-APK befindet sich jede Bibliothek in einem Verzeichnis, dessen Name mit einer entsprechenden ABI übereinstimmt. Ein fettes APK kann beispielsweise Folgendes enthalten:
/lib/armeabi/libfoo.so /lib/armeabi-v7a/libfoo.so /lib/arm64-v8a/libfoo.so /lib/x86/libfoo.so /lib/x86_64/libfoo.so
Hinweis:ARMv7-basierte Android-Geräte mit Android 4.0.3 oder niedriger
Native Bibliotheken aus dem Verzeichnis armeabi
statt aus armeabi-v7a
installieren
wenn beide Verzeichnisse vorhanden sind. Der Grund dafür ist, dass /lib/armeabi/
nach
/lib/armeabi-v7a/
im APK. Dieses Problem wurde in Version 4.0.4 behoben.
ABI-Unterstützung der Android-Plattform
Das Android-System weiß während der Laufzeit, welche ABIs unterstützt werden, da das build-spezifische System stehen für Folgendes:
- Die primäre ABI für das Gerät, die dem Maschinencode entspricht, der in verwendet wird das System-Image selbst.
- Optional: sekundäre ABIs, die anderen ABIs entsprechen, die das System-Image unterstützt.
Dieser Mechanismus sorgt dafür, dass das System den besten Maschinencode aus das Paket bei der Installation.
Für eine optimale Leistung sollten Sie direkt für das primäre ABI kompilieren. Beispiel:
Ein typisches ARMv5TE-basiertes Gerät definiert nur das primäre ABI: armeabi
. Im Gegensatz dazu
In der Regel definieren ARMv7-basierte Geräte die primäre ABI als armeabi-v7a
und das sekundäre
eine als armeabi
, da sie anwendungsnative Binärdateien ausführen kann, die für jede Binärdatei generiert wurden.
64-Bit-Geräte unterstützen auch ihre 32-Bit-Varianten. arm64-v8a-Geräte verwenden Beispielsweise kann das Gerät auch den Code armeabi und armeabi-v7a ausführen. Hinweis: dass Ihre Anwendung auf 64-Bit-Geräten viel besser funktioniert, wenn sie Sie richtet sich an arm64-v8a, anstatt sich auf das Gerät zu verlassen, auf dem armeabi-v7a läuft. Version Ihrer Anwendung.
Auf vielen x86-basierten Geräten können auch armeabi-v7a
- und armeabi
-NDK-Binärdateien ausgeführt werden. Für
solchen Geräten wäre das primäre ABI x86
und das zweite armeabi-v7a
.
Sie können die Installation eines APK für ein bestimmtes ABI erzwingen. Dies ist zum Testen nützlich. Verwenden Sie den folgenden Befehl:
adb install --abi abi-identifier path_to_apk
Automatische Extraktion von nativem Code bei der Installation
Bei der Installation einer App scannt der Paketmanager-Dienst das APK und sucht nach gemeinsam genutzten Bibliotheken im Format:
lib/<primary-abi>/lib<name>.so
Wenn keine gefunden wird und Sie ein sekundäres ABI definiert haben, sucht der Dienst nach gemeinsam genutzten Bibliotheken von das Formular:
lib/<secondary-abi>/lib<name>.so
Wenn die gesuchten Bibliotheken gefunden werden, kopiert der Paketmanager
sie in /lib/lib<name>.so
im nativen Bibliotheksverzeichnis der Anwendung
(<nativeLibraryDir>/
) Die folgenden Snippets rufen das nativeLibraryDir
ab:
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 );
Falls keine Datei mit gemeinsam genutzten Objekten vorhanden ist, wird die App erstellt und installiert, stürzt jedoch bei Laufzeit.
ARMv9: PAC und BTI für C/C++ aktivieren
Die Aktivierung von PAC/BTI bietet Schutz vor einigen Angriffsvektoren. PAC schützt Rückgabeadressen durch kryptografisch im und prüfen Sie, ob die Rücksendeadresse im epilog. BTI verhindert das Springen an beliebige Stellen in Ihrem Code, indem es die dass jedes Branch-Ziel eine spezielle Anweisung ist, die nur Informationen und dem Prozessor nach, ob sie dort landen darf.
Android verwendet PAC/BTI-Anweisungen, die auf älteren Prozessoren, die die neue Anleitung nicht unterstützen. Nur ARMv9-Geräte haben den PAC/BTI. Sie können den gleichen Code aber auch auf ARMv8-Geräten ausführen: Ihrer Bibliothek mehrere Varianten Ihrer Bibliothek enthält. Auch auf ARMv9-Geräten gilt nur PAC/BTI. in 64-Bit-Code umwandeln.
Das Aktivieren von PAC/BTI führt zu einer leichten Erhöhung der Codegröße, normalerweise um 1%.
Siehe Arms Learn the Architect komplexe Software (PDF) finden Sie eine detaillierte Erläuterung der Angriffsvektoren funktioniert der Schutz.
Build-Änderungen
NK-Build
Legen Sie in jedem Modul Ihrer Android.mk-Datei LOCAL_BRANCH_PROTECTION := standard
fest.
CMake
target_compile_options($TARGET PRIVATE -mbranch-protection=standard)
verwenden
für jedes Ziel in der Datei CMakeLists.txt.
Andere Build-Systeme
Kompilieren Sie Ihren Code mit -mbranch-protection=standard
. Dieses Flag funktioniert nur
beim Kompilieren für das ABI arm64-v8a. Sie müssen dieses Flag nicht verwenden,
Verknüpfung.
Fehlerbehebung
Uns sind keine Probleme mit der Compiler-Unterstützung für PAC/BTI bekannt, aber:
- Achte darauf, beim Verknüpfen keine BTI- und Nicht-BTI-Codes zu verwenden, führt zu einer Bibliothek, für die der BTI-Schutz nicht aktiviert ist. Sie können llvm-readelf, um zu überprüfen, ob die resultierende Bibliothek den BTI-Hinweis enthält oder nicht.
$ 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 [...] $
In älteren Versionen von OpenSSL (vor Version 1.1.1i) gibt es einen Fehler im handschriftlichen Assembler die zu PAC-Fehlern führen. Führen Sie ein Upgrade auf die aktuelle OpenSSL-Version durch.
Ältere Versionen einiger App-DRM-Systeme generieren Code, der gegen PAC/BTI verstößt Anforderungen. Wenn Sie die digitale Rechteverwaltung in Apps verwenden und beim Aktivieren von PAC/BTI Probleme auftreten, wenden Sie sich an Ihren DRM-Anbieter, um eine korrigierte Version zu erhalten.