ABI Android

Dispositivi Android diversi utilizzano CPU differenti, che a loro volta supportano diverse set di istruzioni. Ogni combinazione di CPU e set di istruzioni ha il proprio Application Binary Interface (ABI). Un'ABI include le seguenti informazioni:

  • Il set di istruzioni della CPU (e le estensioni) che è possibile utilizzare.
  • L'endianità degli archivi e dei caricamenti della memoria in fase di runtime. Android è sempre Litt-endian.
  • Convenzioni per il trasferimento di dati tra le applicazioni e il sistema, tra cui: vincoli di allineamento e come il sistema utilizza si registra quando chiama funzioni.
  • Il formato dei file binari eseguibili, come programmi e librerie condivise, e i tipi di contenuti supportati. Android utilizza sempre ELF. Per maggiori informazioni le informazioni, vedi Interfaccia binaria dell'applicazione ELF System V.
  • Come vengono alterati i nomi in C++. Per ulteriori informazioni, vedi Generic/Itanium C++ ABI.

Questa pagina elenca le ABI supportate dall'NDK e fornisce informazioni sul funzionamento di ciascuna ABI.

ABI può anche fare riferimento all'API nativa supportata dalla piattaforma. Per un di questi tipi di problemi ABI che interessano i sistemi a 32 bit, vedere Bug ABI a 32 bit.

ABI supportate

Tabella 1. ABI e set di istruzioni supportati.

ABI Set di istruzioni supportati Note
armeabi-v7a
  • Armeabi
  • Pollice-2
  • Neon
  • Incompatibile con i dispositivi ARMv5/v6.
    arm64-v8a
  • AArch64
  • Solo ARMv8.0.
    x86
  • x86 (IA-32)
  • MMX
  • SSE/2/3
  • SSSE3
  • Nessun supporto per MOVBE o SSE4.
    x86_64
  • x86-64
  • MMX
  • SSE/2/3
  • SSSE3
  • SSE4.1, 4.2
  • POPCNT
  • Completa x86-64-v1, ma solo parziale x86-64-v2 (senza CMPXCHG16B o LAHF-SAHF).

    Nota: storicamente, NDK supportava ARMv5 (armeabi) e MIPS a 32 e 64 bit, ma il supporto per queste ABI è stato rimosso in NDK r17.

    Armeabi-V7a

    Questa ABI è per CPU ARM a 32 bit. Sono inclusi Pollice-2 e Neon.

    Per informazioni sulle parti dell'ABI che non sono specifiche per Android, vedi ABI (Application Binary Interface) per l'architettura ARM

    I sistemi di build dell'NDK generano il codice Thumb-2 per impostazione predefinita, a meno che non utilizzi LOCAL_ARM_MODE in Android.mk per ndk-build o ANDROID_ARM_MODE durante la configurazione di CMake.

    Per ulteriori informazioni sulla storia di Neon, vedi Assistenza Neon.

    Per motivi storici, questa ABI utilizza -mfloat-abi=softfp e genera tutti i float valori da passare in registri interi e tutti i double valori da passare in coppie di registri interi quando si effettuano chiamate di funzione. Nonostante il nome, influisce solo sulla convenzione di chiamata con rappresentazione in virgola mobile: il compilatore continuerà usare istruzioni hardware in virgola mobile per l'aritmetica.

    Questa ABI utilizza un'istruzione long double a 64 bit (codice binario IEEE64 uguale a double).

    arm64-v8a

    Questa ABI è per CPU ARM a 64 bit.

    Visualizza Arm's Impara l'architettura per i dettagli completi delle parti dell'ABI che non sono specifiche per Android. Gruppo offre anche alcuni consigli sul trasferimento Sviluppo Android a 64 bit.

    Puoi utilizzare gli strumenti Neon Intrinsics in codice C e C++ per sfruttare l'estensione SIMD avanzata. La Guida per programmatori neon per ARMv8-A Fornisce ulteriori informazioni sulle funzioni intrinseche e sulla programmazione Neon in generale.

    Su Android, il registro x18 specifico della piattaforma è riservato ShadowCallStack e non deve essere influenzato dal tuo codice. Le versioni correnti di Clang sono impostate su usando l'opzione -ffixed-x18 su Android, quindi a meno che tu non abbia o un compilatore molto vecchio, non dovresti preoccuparti di questo.

    Questa ABI utilizza un'istruzione long double a 128 bit (IEEE binari128).

    x86

    Questa ABI è destinata alle CPU che supportano il set di istruzioni comunemente noto come "x86", "i386" o "IA-32".

    L'ABI di Android include il set di istruzioni di base più l'MMX, SSE, SSE2, SSE3 e SSSE3.

    L'ABI non include nessun altro set di istruzioni IA-32 facoltativo come MOVBE o qualsiasi variante di SSE4. Puoi continuare a usare queste estensioni, a condizione che usi il probe delle caratteristiche di runtime per abilitarli e fornire dei fallback per i dispositivi che non li supportano.

    La toolchain NDK presuppone un allineamento dello stack a 16 byte prima di una chiamata di funzione. Gli strumenti predefiniti e opzioni per applicare questa regola. Se scrivi codice Assembly, devi assicurarti di mantenere lo stack e assicurati che anche gli altri compilatori rispettino questa regola.

    Per ulteriori dettagli, consulta i seguenti documenti:

    Questa ABI utilizza un'istruzione long double a 64 bit (binario IEEE64 come double, ma non superiore comune long double solo Intel a 80 bit).

    x86_64

    Questa ABI è destinata alle CPU che supportano il set di istruzioni comunemente chiamato "x86-64".

    L'ABI di Android include il set di istruzioni di base più MMX SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2 e l'istruzione POPCNT.

    L'ABI non include nessun altro set di istruzioni x86-64 facoltativo come MOVBE, SHA o qualsiasi variante di AVX. Puoi continuare a utilizzare queste estensioni, a condizione che usi il probe delle funzionalità di runtime per abilitarli e fornire dei fallback per i dispositivi che non li supportano.

    Per ulteriori dettagli, consulta i seguenti documenti:

    Questa ABI utilizza un'istruzione long double a 128 bit (IEEE binari128).

    Genera codice per un'ABI specifica

    Gradle

    Le build Gradle (utilizzate tramite Android Studio o dalla riga di comando) tutte le ABI non deprecate per impostazione predefinita. Per limitare l'insieme di ABI che i tuoi l'applicazione supporta abiFilters. Ad esempio, per creare ABI a 64 bit, imposta la seguente configurazione in build.gradle:

    android {
        defaultConfig {
            ndk {
                abiFilters 'arm64-v8a', 'x86_64'
            }
        }
    }
    

    build-ndk

    build ndk-build per tutte le ABI non deprecate per impostazione predefinita. Puoi scegliere come target ABI specifiche impostando APP_ABI nel file Application.mk. La il seguente snippet mostra alcuni esempi di utilizzo di 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.
    

    Per ulteriori informazioni sui valori che puoi specificare per APP_ABI, consulta Application.mk:

    Marca

    Con CMake, crei per una singola ABI alla volta e devi specificarla. in modo esplicito. Puoi farlo usando la variabile ANDROID_ABI, che deve essere specificato nella riga di comando (non può essere impostato nel file CMakeLists.txt). Per esempio:

    $ cmake -DANDROID_ABI=arm64-v8a ...
    $ cmake -DANDROID_ABI=armeabi-v7a ...
    $ cmake -DANDROID_ABI=x86 ...
    $ cmake -DANDROID_ABI=x86_64 ...
    

    Per gli altri flag che devono essere passati a CMake per creare con NDK, vedi consulta la guida di CMake.

    Il comportamento predefinito del sistema di compilazione è includere i file binari per ogni ABI in un singolo APK, chiamato anche APK grosso. Un APK grasso è molto più grande di una che contiene solo i file binari di una singola ABI; il compromesso sta guadagnando una maggiore compatibilità, ma a scapito di un APK più grande. È fortemente ti consigliamo di utilizzare gli app bundle o le divisioni dell'APK per Ridurre le dimensioni degli APK mantenendo al contempo il numero massimo di dispositivi la compatibilità.

    Al momento dell'installazione, il gestore di pacchetti decomprime solo i componenti più appropriati. per il dispositivo di destinazione. Per maggiori dettagli, consulta Estrazione automatica dei codice nativo al momento dell'installazione.

    Gestione delle ABI sulla piattaforma Android

    Questa sezione fornisce dettagli su come la piattaforma Android gestisce gli annunci nativi il codice negli APK.

    Codice nativo nei pacchetti dell'app

    Sia il Play Store sia il gestore di pacchetti si aspettano di trovare le risorse generate librerie sui percorsi dei file all'interno dell'APK corrispondenti al seguente pattern:

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

    Qui, <abi> è uno dei nomi ABI elencati in ABI supportate, e <name> è il nome della libreria come l'hai definita per LOCAL_MODULE nel file Android.mk. Dal giorno I file APK sono semplicemente file ZIP, è banale aprirli e verificare che il file nativo condiviso sono il luogo in cui appartengono.

    Se il sistema non trova le librerie condivise native dove previsto, non può utilizzarle che li rappresentano. In questo caso, è l'app stessa a copiare le librerie e quindi esegui dlopen().

    In un APK fat, ogni libreria si trova in una directory il cui nome corrisponde a un'ABI corrispondente. Ad esempio, un APK fat può contenere:

    /lib/armeabi/libfoo.so
    /lib/armeabi-v7a/libfoo.so
    /lib/arm64-v8a/libfoo.so
    /lib/x86/libfoo.so
    /lib/x86_64/libfoo.so
    

    Nota:dispositivi Android basati su ARMv7 con la versione 4.0.3 o precedente installa le librerie native dalla directory armeabi anziché dalla armeabi-v7a se esistono entrambe le directory. Questo perché /lib/armeabi/ segue /lib/armeabi-v7a/ nell'APK. Il problema è stato risolto dalla versione 4.0.4.

    Supporto ABI della piattaforma Android

    Il sistema Android sa in fase di runtime quali ABI supporta perché indicano:

    • L'ABI principale del dispositivo, corrispondente al codice macchina utilizzato in l'immagine di sistema stessa.
    • Facoltativamente, ABI secondarie corrispondenti ad altre ABI incluse nell'immagine di sistema .

    Questo meccanismo assicura che il sistema estragga il miglior codice macchina il pacchetto al momento dell'installazione.

    Per ottenere prestazioni ottimali, devi compilare direttamente per l'ABI principale. Ad esempio, un tipico dispositivo basato su ARMv5TE definirebbe solo l'ABI principale: armeabi. Al contrario, un tipico dei dispositivi basati su ARMv7 definirebbe l'ABI principale come armeabi-v7a e quella secondaria uno come armeabi, poiché può eseguire programmi binari nativi dell'applicazione generati per ciascuno di essi.

    I dispositivi a 64 bit supportano anche le relative varianti a 32 bit. Utilizzo di dispositivi arm64-v8a come esempio, il dispositivo può anche eseguire codice armeabi e armeabi-v7a. Tieni presente che ma che l'applicazione avrà prestazioni molto migliori sui dispositivi a 64 bit sceglie come target arm64-v8a anziché affidarsi al dispositivo che esegue armeabi-v7a della tua applicazione.

    Molti dispositivi basati su x86 supportano anche file binari NDK armeabi-v7a e armeabi. Per su questi dispositivi, l'ABI principale è x86 e la seconda armeabi-v7a.

    Puoi forzare l'installazione di un APK per un'ABI specifica. È utile per i test. Utilizza questo comando:

    adb install --abi abi-identifier path_to_apk
    

    Estrazione automatica del codice nativo al momento dell'installazione

    Durante l'installazione di un'applicazione, il servizio di gestione dei pacchetti analizza l'APK e cerca eventuali raccolte condivise del modulo:

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

    Se non ne viene trovata alcuna e hai definito un'ABI secondaria, il servizio analizza le librerie condivise di il modulo:

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

    Quando trova le librerie che sta cercando, il gestore di pacchetti copia in /lib/lib<name>.so, nella directory della libreria nativa dell'applicazione (<nativeLibraryDir>/). I seguenti snippet recuperano il nativeLibraryDir:

    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 );
    

    Se non è presente alcun file di oggetti condivisi, l'applicazione viene creata e installata, ma si arresta in modo anomalo in runtime.

    ARMv9: Abilitazione di PAC e BTI per C/C++

    L'abilitazione di PAC/BTI fornirà protezione contro alcuni vettori di attacco. PAC protegge gli indirizzi di ritorno firmandoli in modo crittografico nella e controllare che l'indirizzo di restituzione sia ancora registrato correttamente epilogo. BTI impedisce di passare a posizioni arbitrarie nel codice richiedendo che ogni ramo target è un'istruzione speciale che non fa altro che indicare al processore che va bene arrivare lì.

    Android utilizza istruzioni PAC/BTI che non fanno nulla sui processori meno recenti che non supportano le nuove istruzioni. Solo i dispositivi ARMv9 avranno il PAC/BTI ma puoi eseguire lo stesso codice anche sui dispositivi ARMv8: più varianti della tua raccolta. Anche sui dispositivi ARMv9, PAC/BTI si applica solo a 64 bit.

    L'attivazione di PAC/BTI causerà un leggero aumento delle dimensioni del codice, in genere dell'1%.

    Consulta la sezione Scopri l'architettura di ARM - Fornire protezione per un software complesso (PDF) per una spiegazione dettagliata degli obiettivi PAC/BTI dei vettori d'attacco, e di come la protezione funziona.

    Modifiche build

    build-ndk

    Imposta LOCAL_BRANCH_PROTECTION := standard in ogni modulo del tuo file Android.mk.

    Marca

    Usa target_compile_options($TARGET PRIVATE -mbranch-protection=standard) per ogni target nel file CMakeLists.txt.

    Altri sistemi di compilazione

    Compila il codice utilizzando -mbranch-protection=standard. Questo flag funziona solo durante la compilazione per l'ABI arm64-v8a. Non è necessario utilizzare questo flag quando collegamento.

    Risoluzione dei problemi

    Non siamo a conoscenza di eventuali problemi con il supporto del compilatore per PAC/BTI, ma:

    • Fai attenzione a non combinare codice BTI e non BTI durante il collegamento, perché restituisce una libreria in cui la protezione BTI non è abilitata. Puoi utilizzare la modalità llvm-readelf per verificare se la libreria risultante contiene o meno la nota BTI.
    $ 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
    [...]
    $
    
    • Le vecchie versioni di OpenSSL (prima della 1.1.1i) hanno un bug in un assembler scritto a mano causa degli errori PAC. Esegui l'upgrade all'attuale versione di OpenSSL.

    • Le versioni precedenti di alcuni sistemi DRM delle app generano un codice che viola le norme PAC/BTI i tuoi requisiti. Se utilizzi DRM dell'app e riscontri problemi durante l'attivazione di PAC/BTI, contatta il fornitore di DRM per ricevere una versione fissa.