Android-ABIs

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

Tabelle 1 ABIs und unterstützte Anweisungssätze.

ABI Unterstützte Anweisungssätze Hinweise
armeabi-v7a
  • Armeabi
  • Daumen-2
  • Neon
  • Nicht kompatibel mit ARMv5/v6-Geräten.
    arm64-v8a
  • AArch64
  • Nur Armv8.0.
    x86
  • x86 (IA-32)
  • MMX
  • SSE/2/3
  • SSSE3
  • Keine Unterstützung für MOVBE oder SSE4.
    x86_64
  • x86–64
  • MMX
  • SSE/2/3
  • SSSE3
  • SSE4.1, 4.2
  • POPCNT
  • 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:

    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:

    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.