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:
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 |
|
Sindacati |
|
Enumerazioni |
|
Simboli globali |
|
* 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:
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.
Figura 1. Creazione di .sdump
in corso... fileheader-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.
Figura 2. Creazione di .lsdump
in corso... fileheader-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.
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 |
|
---|---|
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 (tramitereferenced_type
intype_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.
intestazione-abi-linker
Lo strumento header-abi-linker
prende i file intermedi prodotti
da header-abi-dumper
come input, quindi collega i file:
Ingressi |
|
---|---|
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
) ebar.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 |
|
---|---|
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 a8
byte. - Tipo di campo
mfoo
cambiato dafoo
afoo *
(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