La stabilité de l'interface binaire d'application (ABI) est une condition préalable des mises à jour propres au framework, car les modules des fournisseurs peuvent dépendre de la couche des bibliothèques partagées du kit de développement (VNDK) qui se trouvent dans la partition système. Dans une version Android, les bibliothèques partagées VNDK nouvellement créées doivent être Compatible avec l'ABI avec les bibliothèques partagées VNDK précédemment publiées, afin que les modules des fournisseurs peuvent fonctionner avec ces bibliothèques sans recompilation et sans erreurs d'exécution. Entre les versions d'Android, les bibliothèques VNDK peuvent être modifiées et il n'y a pas d'ABI garanties.
Pour assurer la compatibilité avec les ABI, Android 9 inclut un vérificateur d'ABI d'en-tête, comme décrit dans les sections suivantes.
À propos du VNDK et de la conformité avec les ABI
Le VNDK est un ensemble restreint de bibliothèques que les modules des fournisseurs peuvent associer qui permettent d'effectuer des mises à jour propres au framework. La conformité de l'ABI fait référence aux la capacité d'une version plus récente d'une bibliothèque partagée à fonctionner comme prévu avec qui est lié dynamiquement (c'est-à-dire qui fonctionne comme une ancienne version du de la bibliothèque.
À propos des symboles exportés
Un symbole exporté (également appelé symbole global) désigne Un symbole qui remplit toutes les conditions suivantes:
- Exportées par les en-têtes publics d'une bibliothèque partagée.
- Cet élément figure dans la table
.dynsym
du fichier.so
. correspondant à la bibliothèque partagée. - Présente une liaison FAIBLE ou GÉNÉRALE.
- La visibilité est PAR DÉFAUT ou PROTÉGÉE.
- L'index de section n'est pas UNDEFINED.
- Le type est FUNC ou OBJECT.
Les en-têtes publics d'une bibliothèque partagée sont définis comme les en-têtes
à la disposition d'autres bibliothèques/binaires via
export_include_dirs
, export_header_lib_headers
export_static_lib_headers
,
export_shared_lib_headers
et
Attributs export_generated_headers
dans Android.bp
les définitions du module correspondant à la bibliothèque partagée.
À propos des types accessibles
Un type accessible est tout type C/C++ intégré ou défini par l'utilisateur
accessibles directement ou indirectement par le biais d'un symbole exporté ET issu de l'exportation
via des en-têtes publics. Par exemple, libfoo.so
possède la fonction
Foo
, qui est un symbole exporté présent dans
.dynsym
. La bibliothèque libfoo.so
inclut les
suivantes:
toto_exported.h | toto.private.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" ], } |
Table .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
|
Si l'on considère Foo
, les types accessibles directement/indirects sont les suivants:
Type | Description |
---|---|
bool
|
Type renvoyé : Foo .
|
int
|
Type du premier paramètre Foo .
|
bar_t *
|
Type de deuxième paramètre Foo. À l'adresse bar_t * ,
bar_t est exporté via foo_exported.h .
bar_t contient un membre mfoo , de type
foo_t , qui est exportée via foo_exported.h
ce qui entraîne l'exportation de types supplémentaires:
Toutefois, foo_private_t n'est PAS accessible, car il n'est pas accessible.
exportés via foo_exported.h . (foo_private_t * )
est opaque. Par conséquent, les modifications apportées à foo_private_t sont autorisées.)
|
Une explication similaire peut être donnée pour les types accessibles via la classe de base. des spécificateurs et des paramètres de modèle.
Assurer la conformité de l'ABI
La conformité de l'ABI doit être garantie pour les bibliothèques marquées
vendor_available: true
et vndk.enabled: true
dans le
les fichiers Android.bp
correspondants. Exemple :
cc_library { name: "libvndk_example", vendor_available: true, vndk: { enabled: true, } }
Pour les types de données accessibles directement ou indirectement par une fonction exportée, le Les modifications suivantes apportées à une bibliothèque sont considérées comme destructives pour l'ABI:
Type de données | Description |
---|---|
Structures et classes |
|
Union |
|
Énumérations |
|
Symboles globaux |
|
* Les fonctions de membres publics et privés doivent toutes deux ne peuvent pas être modifiées ni supprimées, car les fonctions intégrées publiques peuvent faire référence les fonctions de membre privé. Les références de symboles à des fonctions de membres privés peuvent être conservés dans les binaires de l'appelant. Modifier ou supprimer des fonctions de membres privés des bibliothèques partagées peut donner lieu à des binaires incompatibles avec les versions antérieures.
** Les décalages accordés aux membres de données publiques ou privées ne doivent pas être car les fonctions intégrées peuvent faire référence à ces membres de données dans le corps de la fonction. La modification des décalages des membres des données peut entraîner binaires incompatibles avec les versions antérieures.
*** Bien que celles-ci ne modifient pas l'organisation de la mémoire , il existe des différences sémantiques qui pourraient empêcher les bibliothèques fonctionne comme prévu.
Utiliser les outils de conformité de l'ABI
Lorsqu'une bibliothèque VNDK est créée, son ABI est comparée à référence d'ABI correspondante pour la version du VNDK en cours de compilation. Références Les fichiers de vidage ABI se trouvent dans:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/<PLATFORM_VNDK_VERSION>/<BINDER_BITNESS>/<ARCH>/source-based
Par exemple, si vous créez libfoo
pour x86 au niveau d'API 27,
L'ABI déduite de libfoo
est comparée à sa référence à l'emplacement suivant:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/27/64/x86/source-based/libfoo.so.lsdump
Erreur de faille de l'ABI
En cas de défaillance de l'ABI, le journal de compilation affiche des avertissements indiquant le type d'avertissement
au rapport abi-diff. Par exemple, si l'ABI de libbinder
a
une modification incompatible, le système de compilation génère une erreur avec un message
semblable à ce qui suit:
***************************************************** 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 ----
Créer des vérifications d'ABI de la bibliothèque VNDK
Lorsqu'une bibliothèque VNDK est créée:
header-abi-dumper
traite les fichiers sources compilés dans créer la bibliothèque VNDK (les fichiers sources de la bibliothèque ainsi que ses fichiers sources) ; (héritées via des dépendances transitives statiques) pour produire Fichiers.sdump
correspondant à chaque source.
Figure 1. Créer le .sdump
fichiersheader-abi-linker
traite ensuite.sdump
. (à l'aide d'un script de version qui lui est fourni ou de la.so
fichier correspondant à la bibliothèque partagée) pour générer un.lsdump
qui consigne toutes les informations d'ABI correspondant à la bibliothèque partagée.
Figure 2. Créer le .lsdump
fichierheader-abi-diff
compare.lsdump
fichier avec un fichier de référence.lsdump
pour générer un rapport de différences qui décrit les différences entre les ABI des deux bibliothèques.
Figure 3. Créer le rapport de différences
en-tête-abi-dumper
L'outil header-abi-dumper
analyse un fichier source C/C++ et effectue les vidages
l'ABI déduite de ce fichier source dans un fichier intermédiaire. La compilation
le système exécute header-abi-dumper
sur tous les fichiers sources compilés, tout en
une bibliothèque qui inclut les fichiers sources
les dépendances.
Entrées |
|
---|---|
Sortie | Un fichier qui décrit l'ABI du fichier source (par exemple,
foo.sdump représente l'ABI de foo.cpp .
|
Actuellement, les fichiers .sdump
sont au format JSON, ce qui n'est pas le cas
d'être stable dans les prochaines versions. À ce titre, .sdump
le formatage du fichier doit être considéré comme un détail d'implémentation du système de compilation.
Par exemple, libfoo.so
contient le fichier source suivant :
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; }
Vous pouvez utiliser header-abi-dumper
pour générer une classe intermédiaire
Fichier .sdump
qui représente l'ABI présentée par le fichier source
avec:
$ header-abi-dumper foo.cpp -I exported -o foo.sdump -- -I exported -x c++
Cette commande indique à header-abi-dumper
d'analyser
foo.cpp
avec les indicateurs de compilation qui suivent --
; et
émettre les informations sur l'ABI exportées par les en-têtes publics dans
exported
. Ce qui suit est
foo.sdump
généré par
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
contient les informations sur l'ABI exportées par le fichier source
foo.cpp
et les en-têtes publics, par exemple,
record_types
Faire référence aux structures, unions ou classes définis dans les en-têtes publics. Chaque type d'enregistrement contient des informations sur ses champs, ses le spécificateur d'accès, le fichier d'en-tête dans lequel il est défini et d'autres .pointer_types
Faire référence aux types de pointeur directement/indirectement référencés par les enregistrements/fonctions exportés dans les en-têtes publics, ainsi que avec le type vers lequel pointe le pointeur (via lereferenced_type
danstype_info
). Des informations similaires sont enregistrées Fichier.sdump
pour les types qualifiés, types C/C++ intégrés, tableau et les types de référence lvalue et rvalue. De telles informations permettent diffing récursive.functions
Représente les fonctions exportées par des en-têtes publics. Ils contiennent également des informations sur le nom déformé de la fonction, le type renvoyé, les types de paramètres, le spécificateur d'accès et d'autres attributs.
en-tête-abi-linker
L'outil header-abi-linker
récupère les fichiers intermédiaires générés
par header-abi-dumper
en entrée, puis associe ces fichiers:
Entrées |
|
---|---|
Sortie | Un fichier qui décrit l'ABI d'une bibliothèque partagée (par exemple,
libfoo.so.lsdump représente l'ABI de libfoo .
|
L'outil fusionne les graphiques de type dans tous les fichiers intermédiaires qui lui sont fournis,
en tenant compte de la définition unique (des types définis par l'utilisateur
d'unités de traduction portant le même nom complet, peuvent être sémantiquement
différentes) selon les unités de traduction. L'outil analyse ensuite
Un script de version ou la table .dynsym
de la bibliothèque partagée
(fichier .so
) pour créer la liste des symboles exportés.
Par exemple, libfoo
est composé des éléments foo.cpp
et
bar.cpp
header-abi-linker
pourrait être appelé pour
Créez le vidage complet de l'ABI associé de libfoo
comme suit:
header-abi-linker -I exported foo.sdump bar.sdump \ -o libfoo.so.lsdump \ -so libfoo.so \ -arch arm64 -api current
Exemple de résultat de la commande dans 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" : [] }
L'outil header-abi-linker
:
- Associe les fichiers
.sdump
qui lui sont fournis (foo.sdump
etbar.sdump
), en filtrant les informations sur l'ABI qui ne sont pas présentes dans les en-têtes résidant dans le répertoire:exported
. - Analyse
libfoo.so
et collecte des informations sur les symboles exporté par la bibliothèque via sa table.dynsym
. - Ajout de
_Z3FooiP3bar
et_Z6FooBadiP3foo
.
libfoo.so.lsdump
est le vidage final de l'ABI généré pour
libfoo.so
en-tête-abi-diff
L'outil header-abi-diff
compare deux fichiers .lsdump
.
représentant l'ABI de deux bibliothèques et génère un rapport de différences indiquant le
entre les deux ABI.
Entrées |
|
---|---|
Sortie | Rapport de différences indiquant les différences entre les ABI proposées par les deux les bibliothèques partagées. |
Le fichier de différences d'ABI se trouve <ph type="x-smartling-placeholder"></ph> protobuf text format. Ce format est susceptible d'être modifié. dans les prochaines versions.
Par exemple, vous avez
deux versions de
libfoo
: libfoo_old.so
et
libfoo_new.so
Dans libfoo_new.so
, dans
bar_t
, vous remplacez le type de mfoo
foo_t
à foo_t *
. bar_t
étant un
accessible, il doit être signalé comme une modification destructive de l'ABI
header-abi-diff
Pour exécuter header-abi-diff
:
header-abi-diff -old libfoo_old.so.lsdump \ -new libfoo_new.so.lsdump \ -arch arm64 \ -o libfoo.so.abidiff \ -lib libfoo
Exemple de résultat de la commande dans 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 } } }
libfoo.so.abidiff
contient un rapport sur toutes les ruptures de l'ABI
modifications dans libfoo
. Message record_type_diffs
indique qu'un enregistrement a été modifié et répertorie les modifications incompatibles, ce qui
incluent:
- La taille de l'enregistrement passe de
24
octets à8
octets. - Le type de champ de
mfoo
passe defoo
àfoo *
(toutes les définitions de type sont supprimées).
Le champ type_stack
indique comment header-abi-diff
a atteint le type qui a été modifié (bar
). Ce champ peut être
interprétée comme Foo
est une fonction exportée qui reçoit
bar *
comme paramètre, qui pointe vers bar
, qui était
exportées et modifiées.
Appliquer l'ABI et l'API
Pour appliquer l'ABI et l'API des bibliothèques partagées du VNDK, les références d'ABI doivent
être enregistré dans ${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/
.
Pour créer ces références, exécutez la commande suivante:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py
Après avoir créé les références, toute modification apportée au code source qui entraîne Une modification incompatible d'ABI ou d'API dans une bibliothèque VNDK entraîne désormais une erreur de compilation.
Pour mettre à jour les références d'ABI pour des bibliothèques spécifiques, exécutez la commande suivante:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l <lib1> -l <lib2>
Par exemple, pour mettre à jour les références d'ABI libbinder
, exécutez la commande suivante:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder