Stabilità ABI

La stabilità dell'ABI (Application Binary Interface) è un prerequisito di gli aggiornamenti solo del framework perché i moduli del fornitore possono dipendere Librerie condivise del kit di sviluppo (VNDK) che risiedono nella partizione di sistema. All'interno di una release Android, le librerie condivise VNDK appena create devono essere Compatibile con ABI con le librerie condivise VNDK rilasciate in precedenza, quindi i moduli del fornitore può lavorare con queste librerie senza ricompilazione e senza errori di runtime. Tra le release Android, le librerie VNDK possono essere modificate e non sono presenti ABI garantiti.

Per garantire la compatibilità con ABI, Android 9 include un controllo ABI dell'intestazione, come descritto nelle sezioni seguenti.

Informazioni sulla conformità VNDK e ABI

VNDK è un insieme restrittivo di librerie a cui i moduli del fornitore possono collegarsi e che abilitano gli aggiornamenti solo framework. La conformità ABI si riferisce alla il funzionamento previsto di una versione più recente di una libreria condivisa con una collegato dinamicamente (ovvero che funziona come una versione precedente libreria di Google).

Informazioni sui simboli esportati

Un simbolo esportato (noto anche come simbolo globale) si riferisce a un simbolo che soddisfi tutti i requisiti seguenti:

  • Esportato dalle intestazioni pubbliche di una libreria condivisa.
  • Appare nella tabella .dynsym del file .so corrispondenti alla libreria condivisa.
  • Con associazione WEAK o GLOBAL.
  • La visibilità è PREDEFINITA o PROTETTA.
  • L'indice della sezione non è NON DEFINITO.
  • Il tipo è FUNC o OBJECT.

Le intestazioni pubbliche di una libreria condivisa sono definite come intestazioni disponibili ad altre librerie/programmi binari tramite export_include_dirs export_header_lib_headers export_static_lib_headers, export_shared_lib_headers e export_generated_headers attributi in Android.bp le definizioni del modulo corrispondente alla libreria condivisa.

Informazioni sui tipi raggiungibili

Un tipo raggiungibile è qualsiasi tipo C/C++ integrato o definito dall'utente che raggiungibile direttamente o indirettamente tramite un simbolo esportato E tramite intestazioni pubbliche. Ad esempio, libfoo.so ha funzione Foo, che è un simbolo esportato che si trova Tabella .dynsym. La libreria libfoo.so include seguenti:

foo_exported.h foo.privato.h
typedef struct foo_private foo_private_t;

typedef struct foo {
  int m1;
  int *m2;
  foo_private_t *mPfoo;
} foo_t;

typedef struct bar {
  foo_t mfoo;
} bar_t;

bool Foo(int id, bar_t *bar_ptr);
typedef struct foo_private {
  int m1;
  float mbar;
} foo_private_t;
Android.bp
cc_library {
  name : libfoo,
  vendor_available: true,
  vndk {
    enabled : true,
  }
  srcs : ["src/*.cpp"],
  export_include_dirs : [
    "exported"
  ],
}
Tabella .dynsym
Num Value Size Type Bind Vis Ndx Name
1 0 0 FUNC GLOB DEF UND dlerror@libc
2 1ce0 20 FUNC GLOB DEF 12 Foo

Se guardiamo Foo, i tipi raggiungibili direttamente/indiretti includono:

Tipo Descrizione
bool Tipo restituito Foo.
int Tipo del primo parametro Foo.
bar_t * Tipo di secondo parametro Foo. A proposito di bar_t *, bar_t esportato tramite foo_exported.h.

bar_t contiene un membro mfoo, di tipo foo_t, che viene esportato tramite foo_exported.h, che comportano l'esportazione di un maggior numero di tipi:
  • int : è il tipo di m1.
  • int * : è il tipo di m2.
  • foo_private_t * : è il tipo di mPfoo.
di Gemini Advanced.
Tuttavia, foo_private_t NON è raggiungibile perché non lo è esportato tramite foo_exported.h. (foo_private_t * è opaco, pertanto sono consentite le modifiche apportate a foo_private_t.)

È possibile dare una spiegazione simile per i tipi raggiungibili tramite la classe base degli identificatori e dei parametri del modello.

Garantire la conformità all'ABI

È necessario garantire la conformità all'ABI per le librerie contrassegnate vendor_available: true e vndk.enabled: true nel Android.bp file corrispondenti. Ad esempio:

cc_library {
    name: "libvndk_example",
    vendor_available: true,
    vndk: {
        enabled: true,
    }
}

Per i tipi di dati raggiungibili direttamente o indirettamente da una funzione esportata, il valore le seguenti modifiche a una libreria sono classificate come ABI-breaking:

Tipo di dati Descrizione
Strutture e classi
  • Modifica la dimensione del tipo di classe o di struct.
  • Classi di base
    • Aggiungere o rimuovere classi di base.
    • Aggiungi o rimuovi le classi di base ereditate virtualmente.
    • Modifica l'ordine delle classi base.
  • Funzioni per i membri
    • Rimuovi le funzioni membro*.
    • Aggiungi o rimuovi argomenti dalle funzioni membro.
    • Modifica i tipi di argomento o i tipi restituiti del membro funzioni*.
    • Modificare il layout della tabella virtuale.
  • Membri dei dati
    • Rimuovi i membri di dati statici.
    • Aggiungi o rimuovi i membri di dati non statici.
    • Modifica i tipi di membri dei dati.
    • Modifica gli offset in membri di dati non statici**.
    • Cambia const, volatile e/o Qualificatori restricted dei membri dei dati***.
    • Esegui il downgrade degli indicatori di accesso dei membri dei dati***.
  • Modifica gli argomenti del modello.
Sindacati
  • Aggiungere o rimuovere membri dei dati.
  • Modifica la dimensione del tipo di unione.
  • Modifica i tipi di membri dei dati.
Enumerazioni
  • Modifica il tipo sottostante.
  • Modifica i nomi degli enumeratori.
  • Modifica i valori degli enumeratori.
Simboli globali
  • Rimuovi i simboli esportati dalle intestazioni pubbliche.
  • Per i simboli globali di tipo FUNC
    • Aggiungi o rimuovi argomenti.
    • Cambia i tipi di argomento.
    • Modifica il tipo restituito.
    • Esegui il downgrade dell'indicatore di accesso***.
  • Per i simboli globali di tipo OBJECT
    • Modifica il tipo C/C++ corrispondente.
    • Esegui il downgrade dell'indicatore di accesso***.

* Le funzioni di membro pubbliche e private devono non possono essere modificate o rimosse perché le funzioni in linea pubbliche possono fare riferimento funzioni di membri privati. I riferimenti ai simboli alle funzioni membro private possono nei file binari dei chiamanti. Modificare o rimuovere le funzioni dei membri privati da librerie condivise possono causare file binari incompatibili con le versioni precedenti.

** Gli scostamenti rispetto ai membri di dati pubblici o privati non devono essere è cambiato perché le funzioni in linea possono fare riferimento a questi membri di dati del corpo della funzione. La modifica degli offset dei membri dati può file binari non compatibili con le versioni precedenti.

*** Anche se queste funzionalità non modificano il layout della memoria del tipo, esistono differenze semantiche che potrebbero far sì che le librerie non funzionando come previsto.

Usa gli strumenti per la conformità ABI

Quando viene creata una libreria VNDK, l'ABI della libreria viene confrontato con riferimento ABI corrispondente alla versione del VNDK in fase di creazione. Riferimento I dump dell'ABI si trovano in:

${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/<PLATFORM_VNDK_VERSION>/<BINDER_BITNESS>/<ARCH>/source-based

Ad esempio, in merito alla creazione di libfoo per x86 al livello API 27, L'ABI dedotta di libfoo viene confrontata con il suo riferimento all'indirizzo:

${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/27/64/x86/source-based/libfoo.so.lsdump

Errore di interruzione dell'ABI

Per le interruzioni delle ABI, il log di build mostra avvisi con il tipo di avviso e un del report abi-diff. Ad esempio, se l'ABI di libbinder ha una modifica incompatibile, il sistema di compilazione genera un errore con un messaggio simile al seguente:

*****************************************************
error: VNDK library: libbinder.so's ABI has INCOMPATIBLE CHANGES
Please check compatibility report at:
out/soong/.intermediates/frameworks/native/libs/binder/libbinder/android_arm64_armv8-a_cortex-a73_vendor_shared/libbinder.so.abidiff
******************************************************
---- Please update abi references by running
platform/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder ----

Creare controlli ABI della libreria VNDK

Quando viene creata una libreria VNDK:

  1. header-abi-dumper elabora i file di origine compilati in creare la libreria VNDK (i file sorgente della libreria e i file sorgente ereditati attraverso dipendenze transitive statiche), per produrre .sdump file che corrispondono a ciascuna origine.
    creazione sdump
    Figura 1. Creazione di .sdump in corso... file
  2. header-abi-linker elabora quindi .sdump file (utilizzando uno script di versione fornitogli o il metodo .so corrispondente alla libreria condivisa) per produrre un .lsdump che registra tutte le informazioni ABI corrispondenti alla libreria condivisa.
    creazione lsdump
    Figura 2. Creazione di .lsdump in corso... file
  3. header-abi-diff confronta: .lsdump file con un file .lsdump di riferimento per produrre un report sulle differenze che evidenzi le differenze nelle ABI delle due librerie.
    creazione diff abi
    Figura 3. Creazione del report sulle differenze

intestazione-abi-dumper

Lo strumento header-abi-dumper analizza un file di origine C/C++ ed esegue il dump l'ABI dedotto da quel file di origine in un file intermedio. La build il sistema esegue header-abi-dumper su tutti i file di origine compilati mentre anche creando una libreria che includa i file sorgente delle dipendenze.

Ingressi
  • Un file sorgente C/C++
  • Directory di inclusione esportate
  • Flag del compilatore
Output Un file che descrive l'ABI del file di origine (ad esempio, foo.sdump rappresenta l'ABI di foo.cpp).

Attualmente i file .sdump sono in formato JSON, diverso da quello la stabilità nelle release future. Di conseguenza, .sdump deve essere considerata un dettaglio di implementazione del sistema di compilazione.

Ad esempio, libfoo.so ha il seguente file di origine foo.cpp:

#include <stdio.h>
#include <foo_exported.h>

bool Foo(int id, bar_t *bar_ptr) {
    if (id > 0 && bar_ptr->mfoo.m1 > 0) {
        return true;
    }
    return false;
}

Puoi utilizzare header-abi-dumper per generare una intermedia File .sdump che rappresenta l'ABI presentata dal file di origine con:

$ header-abi-dumper foo.cpp -I exported -o foo.sdump -- -I exported -x c++

Questo comando indica a header-abi-dumper di analizzare foo.cpp con i flag del compilatore che seguono -- e emettono le informazioni ABI esportate dalle intestazioni pubbliche Directory exported. Quello che segue è foo.sdump generato da header-abi-dumper:

{
 "array_types" : [],
 "builtin_types" :
 [
  {
   "alignment" : 4,
   "is_integral" : true,
   "linker_set_key" : "_ZTIi",
   "name" : "int",
   "referenced_type" : "_ZTIi",
   "self_type" : "_ZTIi",
   "size" : 4
  }
 ],
 "elf_functions" : [],
 "elf_objects" : [],
 "enum_types" : [],
 "function_types" : [],
 "functions" :
 [
  {
   "function_name" : "FooBad",
   "linker_set_key" : "_Z6FooBadiP3foo",
   "parameters" :
   [
    {
     "referenced_type" : "_ZTIi"
    },
    {
     "referenced_type" : "_ZTIP3foo"
    }
   ],
   "return_type" : "_ZTI3bar",
   "source_file" : "exported/foo_exported.h"
  }
 ],
 "global_vars" : [],
 "lvalue_reference_types" : [],
 "pointer_types" :
 [
  {
   "alignment" : 8,
   "linker_set_key" : "_ZTIP11foo_private",
   "name" : "foo_private *",
   "referenced_type" : "_ZTI11foo_private",
   "self_type" : "_ZTIP11foo_private",
   "size" : 8,
   "source_file" : "exported/foo_exported.h"
  },
  {
   "alignment" : 8,
   "linker_set_key" : "_ZTIP3foo",
   "name" : "foo *",
   "referenced_type" : "_ZTI3foo",
   "self_type" : "_ZTIP3foo",
   "size" : 8,
   "source_file" : "exported/foo_exported.h"
  },
  {
   "alignment" : 8,
   "linker_set_key" : "_ZTIPi",
   "name" : "int *",
   "referenced_type" : "_ZTIi",
   "self_type" : "_ZTIPi",
   "size" : 8,
   "source_file" : "exported/foo_exported.h"
  }
 ],
 "qualified_types" : [],
 "record_types" :
 [
  {
   "alignment" : 8,
   "fields" :
   [
    {
     "field_name" : "mfoo",
     "referenced_type" : "_ZTI3foo"
    }
   ],
   "linker_set_key" : "_ZTI3bar",
   "name" : "bar",
   "referenced_type" : "_ZTI3bar",
   "self_type" : "_ZTI3bar",
   "size" : 24,
   "source_file" : "exported/foo_exported.h"
  },
  {
   "alignment" : 8,
   "fields" :
   [
    {
     "field_name" : "m1",
     "referenced_type" : "_ZTIi"
    },
    {
     "field_name" : "m2",
     "field_offset" : 64,
     "referenced_type" : "_ZTIPi"
    },
    {
     "field_name" : "mPfoo",
     "field_offset" : 128,
     "referenced_type" : "_ZTIP11foo_private"
    }
   ],
   "linker_set_key" : "_ZTI3foo",
   "name" : "foo",
   "referenced_type" : "_ZTI3foo",
   "self_type" : "_ZTI3foo",
   "size" : 24,
   "source_file" : "exported/foo_exported.h"
  }
 ],
 "rvalue_reference_types" : []
}

foo.sdump contiene informazioni ABI esportate dal file di origine foo.cpp e le intestazioni pubbliche, ad esempio,

  • record_types. Fai riferimento a struct, unioni o classi definiti nelle intestazioni pubbliche. Ogni tipo di record contiene informazioni sui campi, dimensioni, l'indicatore di accesso, il file di intestazione in cui è definito attributi.
  • pointer_types. Fai riferimento ai tipi di puntatori direttamente/indirettamente a cui fanno riferimento le funzioni/record esportati nelle intestazioni pubbliche, insieme con il tipo a cui punta il puntatore (tramite referenced_type in type_info). Informazioni simili vengono registrate nel .sdump per tipi qualificati, tipi C/C++ integrati, array e tipi di riferimento lvalue e rvalue. Queste informazioni consentono le differenze ricorsive.
  • functions. Rappresentare le funzioni esportate da intestazioni pubbliche. Contengono anche informazioni sul nome modificato della funzione, sul tipo restituito i tipi di parametri, l'indicatore di accesso e altri attributi.
di Gemini Advanced.

intestazione-abi-linker

Lo strumento header-abi-linker prende i file intermedi prodotti da header-abi-dumper come input, quindi collega i file:

Ingressi
  • File intermedio prodotto da header-abi-dumper
  • Script della versione/file mappa (facoltativo)
  • .so file della libreria condivisa
  • Directory di inclusione esportate
Output Un file che descrive l'ABI di una libreria condivisa (ad esempio, libfoo.so.lsdump rappresenta l'ABI di libfoo).

Lo strumento unisce i grafici dei tipi in tutti i file intermedi forniti, prendendo in considerazione un'unica definizione (i tipi definiti dall'utente in di traduzione con lo stesso nome completo, potrebbero essere semanticamente diverse) differenze tra le unità di traduzione. Lo strumento analizza quindi uno script di versione o la tabella .dynsym della libreria condivisa (.so file) per creare un elenco dei simboli esportati.

Ad esempio, libfoo è composto da foo.cpp e bar.cpp. header-abi-linker potrebbe essere richiamato per crea il dump completo dell'ABI collegato di libfoo come segue:

header-abi-linker -I exported foo.sdump bar.sdump \
                  -o libfoo.so.lsdump \
                  -so libfoo.so \
                  -arch arm64 -api current

Output comando di esempio in libfoo.so.lsdump:

{
 "array_types" : [],
 "builtin_types" :
 [
  {
   "alignment" : 1,
   "is_integral" : true,
   "is_unsigned" : true,
   "linker_set_key" : "_ZTIb",
   "name" : "bool",
   "referenced_type" : "_ZTIb",
   "self_type" : "_ZTIb",
   "size" : 1
  },
  {
   "alignment" : 4,
   "is_integral" : true,
   "linker_set_key" : "_ZTIi",
   "name" : "int",
   "referenced_type" : "_ZTIi",
   "self_type" : "_ZTIi",
   "size" : 4
  }
 ],
 "elf_functions" :
 [
  {
   "name" : "_Z3FooiP3bar"
  },
  {
   "name" : "_Z6FooBadiP3foo"
  }
 ],
 "elf_objects" : [],
 "enum_types" : [],
 "function_types" : [],
 "functions" :
 [
  {
   "function_name" : "Foo",
   "linker_set_key" : "_Z3FooiP3bar",
   "parameters" :
   [
    {
     "referenced_type" : "_ZTIi"
    },
    {
     "referenced_type" : "_ZTIP3bar"
    }
   ],
   "return_type" : "_ZTIb",
   "source_file" : "exported/foo_exported.h"
  },
  {
   "function_name" : "FooBad",
   "linker_set_key" : "_Z6FooBadiP3foo",
   "parameters" :
   [
    {
     "referenced_type" : "_ZTIi"
    },
    {
     "referenced_type" : "_ZTIP3foo"
    }
   ],
   "return_type" : "_ZTI3bar",
   "source_file" : "exported/foo_exported.h"
  }
 ],
 "global_vars" : [],
 "lvalue_reference_types" : [],
 "pointer_types" :
 [
  {
   "alignment" : 8,
   "linker_set_key" : "_ZTIP11foo_private",
   "name" : "foo_private *",
   "referenced_type" : "_ZTI11foo_private",
   "self_type" : "_ZTIP11foo_private",
   "size" : 8,
   "source_file" : "exported/foo_exported.h"
  },
  {
   "alignment" : 8,
   "linker_set_key" : "_ZTIP3bar",
   "name" : "bar *",
   "referenced_type" : "_ZTI3bar",
   "self_type" : "_ZTIP3bar",
   "size" : 8,
   "source_file" : "exported/foo_exported.h"
  },
  {
   "alignment" : 8,
   "linker_set_key" : "_ZTIP3foo",
   "name" : "foo *",
   "referenced_type" : "_ZTI3foo",
   "self_type" : "_ZTIP3foo",
   "size" : 8,
   "source_file" : "exported/foo_exported.h"
  },
  {
   "alignment" : 8,
   "linker_set_key" : "_ZTIPi",
   "name" : "int *",
   "referenced_type" : "_ZTIi",
   "self_type" : "_ZTIPi",
   "size" : 8,
   "source_file" : "exported/foo_exported.h"
  }
 ],
 "qualified_types" : [],
 "record_types" :
 [
  {
   "alignment" : 8,
   "fields" :
   [
    {
     "field_name" : "mfoo",
     "referenced_type" : "_ZTI3foo"
    }
   ],
   "linker_set_key" : "_ZTI3bar",
   "name" : "bar",
   "referenced_type" : "_ZTI3bar",
   "self_type" : "_ZTI3bar",
   "size" : 24,
   "source_file" : "exported/foo_exported.h"
  },
  {
   "alignment" : 8,
   "fields" :
   [
    {
     "field_name" : "m1",
     "referenced_type" : "_ZTIi"
    },
    {
     "field_name" : "m2",
     "field_offset" : 64,
     "referenced_type" : "_ZTIPi"
    },
    {
     "field_name" : "mPfoo",
     "field_offset" : 128,
     "referenced_type" : "_ZTIP11foo_private"
    }
   ],
   "linker_set_key" : "_ZTI3foo",
   "name" : "foo",
   "referenced_type" : "_ZTI3foo",
   "self_type" : "_ZTI3foo",
   "size" : 24,
   "source_file" : "exported/foo_exported.h"
  }
 ],
 "rvalue_reference_types" : []
}

Lo strumento header-abi-linker:

  • Collega i file .sdump forniti (foo.sdump) e bar.sdump), filtrando le informazioni ABI non presenti in le intestazioni che si trovano nella directory: exported.
  • Analizza libfoo.so e raccoglie informazioni sui simboli esportate dalla libreria tramite la tabella .dynsym.
  • Aggiunge _Z3FooiP3bar e _Z6FooBadiP3foo.

libfoo.so.lsdump è il dump ABI finale generato di libfoo.so.

intestazione-abi-diff

Lo strumento header-abi-diff confronta due file .lsdump che rappresenta l'ABI di due librerie e produce un report delle differenze indicante le differenze tra le due ABI.

Ingressi
  • .lsdump file che rappresenta l'ABI di una vecchia condivisione libreria.
  • .lsdump file che rappresenta l'ABI di una nuova libreria condivisa.
Output Un report delle differenze indicante le differenze nelle ABI offerte dai due servizi con le librerie condivise.

Il file delle differenze ABI è in formato di testo protobuf. Il formato è soggetto a modifiche nelle release future.

Ad esempio, hai due versioni libfoo: libfoo_old.so e libfoo_new.so. Tra libfoo_new.so, in bar_t, cambi il tipo di mfoo da Da foo_t a foo_t *. Poiché bar_t è un raggiungibile, deve essere contrassegnata come una modifica che provoca un errore nell'ABI header-abi-diff.

Per eseguire header-abi-diff:

header-abi-diff -old libfoo_old.so.lsdump \
                -new libfoo_new.so.lsdump \
                -arch arm64 \
                -o libfoo.so.abidiff \
                -lib libfoo

Output comando di esempio in libfoo.so.abidiff:

lib_name: "libfoo"
arch: "arm64"
record_type_diffs {
  name: "bar"
  type_stack: "Foo-> bar *->bar "
  type_info_diff {
    old_type_info {
      size: 24
      alignment: 8
    }
    new_type_info {
      size: 8
      alignment: 8
    }
  }
  fields_diff {
    old_field {
      referenced_type: "foo"
      field_offset: 0
      field_name: "mfoo"
      access: public_access
    }
    new_field {
      referenced_type: "foo *"
      field_offset: 0
      field_name: "mfoo"
      access: public_access
    }
  }
}

L'elemento libfoo.so.abidiff contiene un report relativo a tutte le violazioni dell'ABI modifiche in libfoo. Il messaggio record_type_diffs indica che un record è stato modificato ed elenca le modifiche non compatibili, include:

  • Le dimensioni del record passate da 24 byte a 8 byte.
  • Tipo di campo mfoo cambiato da foo a foo * (tutti i valori typedef vengono rimossi).

Il campo type_stack indica in che modo header-abi-diff è stato raggiunto il tipo che è stato modificato (bar). Questo campo può essere interpretata come Foo è una funzione esportata che prende in bar * come parametro, questo rimanda a bar, che era esportate e modificate.

Applica ABI e API

Per applicare in modo forzato ABI e API delle librerie condivise VNDK, i riferimenti ABI devono fare il check-in in ${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/. Per creare questi riferimenti, esegui questo comando:

${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py

Dopo aver creato i riferimenti, qualsiasi modifica apportata al codice sorgente risultante in una modifica ABI/API non compatibile in una libreria VNDK ora provoca un errore di generazione.

Per aggiornare i riferimenti ABI per librerie specifiche, esegui questo comando:

${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l <lib1> -l <lib2>

Ad esempio, per aggiornare libbinder riferimenti ABI, esegui:

${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder