Die Stabilität der Application Binary Interface (ABI) ist eine Voraussetzung für Nur Framework-Updates, da Anbietermodule möglicherweise vom nativen System des Anbieters abhängen Gemeinsam genutzte Bibliotheken des Development Kits (VNDK), die sich in der Systempartition befinden. Innerhalb eines Android-Release müssen neu erstellte gemeinsam genutzte VNDK-Bibliotheken ABI-kompatibel mit zuvor veröffentlichten VNDK-Bibliotheken, sodass Anbietermodule mit diesen Bibliotheken ohne Neukompilierung und ohne Laufzeitfehler arbeiten. Zwischen Android-Releases können VNDK-Bibliotheken geändert werden und es gibt kein ABI. Garantien.
Um die ABI-Kompatibilität sicherzustellen, umfasst Android 9 ABI-Header-Prüfung, wie in den folgenden Abschnitten beschrieben.
VNDK- und ABI-Compliance
Das VNDK ist ein restriktiver Satz von Bibliotheken, mit denen Anbietermodule verknüpft werden können. die reine Framework-Updates ermöglichen. Die ABI-Konformität bezieht sich auf die eine neuere Version einer gemeinsam genutzten Bibliothek mit einem -Modul, das dynamisch verknüpft ist (d.h. als ältere Version des Bibliothek).
Exportierte Symbole
Ein exportiertes Symbol (auch als globales Symbol bezeichnet) bezieht sich auf ein Symbol, das alle folgenden Anforderungen erfüllt:
- Sie werden über die öffentlichen Header einer gemeinsam genutzten Bibliothek exportiert.
- Erscheint in der Tabelle
.dynsym
der Datei.so
die der gemeinsam genutzten Bibliothek entspricht. - Die Bindung hat SCHWACH oder GLOBALE.
- Die Sichtbarkeit ist STANDARD oder GESCHÜTZT.
- Abschnittsindex ist nicht UNEFFEKT.
- Der Typ ist entweder FUNC oder OBJECT.
Die öffentlichen Header einer gemeinsam genutzten Bibliothek sind als Header definiert.
für andere Bibliotheken/Binärprogramme über die
export_include_dirs
, export_header_lib_headers
,
export_static_lib_headers
,
export_shared_lib_headers
und
Attribute vom Typ export_generated_headers
in Android.bp
des Moduls für die gemeinsam genutzte Bibliothek.
Erreichbare Typen
Ein erreichbarer Typ ist jeder integrierte oder benutzerdefinierte C/C++-Typ, der
direkt oder indirekt über ein exportiertes Symbol erreichbar UND exportiert
über öffentliche Header. Beispiel: libfoo.so
hat die Funktion
Foo
, ein exportiertes Symbol aus der
.dynsym
. Die libfoo.so
-Bibliothek enthält Folgendes:
Folgendes:
foo_exported.h | foo.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" ], } |
.dynsym-Tabelle | |||||||
---|---|---|---|---|---|---|---|
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
|
Zu den direkt/indirekt erreichbaren Typen von Foo
gehören:
Typ | Beschreibung |
---|---|
bool
|
Rückgabetyp von Foo .
|
int
|
Typ des ersten Foo -Parameters.
|
bar_t *
|
Typ des zweiten Foo-Parameters. Bis zum bar_t * ,
bar_t wird über foo_exported.h exportiert.
bar_t enthält ein Mitglied mfoo vom Typ
foo_t , das über foo_exported.h exportiert wird,
Dadurch werden weitere Typen exportiert:
foo_private_t ist jedoch NICHT erreichbar,
exportiert über foo_exported.h . (foo_private_t * )
ist opak, daher sind Änderungen an foo_private_t zulässig.)
|
Eine ähnliche Erklärung kann für Typen gegeben werden, die über die Basisklasse erreichbar sind auch Spezifizierer und Vorlagenparameter.
ABI-Konformität sicherstellen
Für die gekennzeichneten Bibliotheken muss die ABI-Konformität sichergestellt werden.
vendor_available: true
und vndk.enabled: true
im
entsprechende Android.bp
-Dateien. Beispiel:
cc_library { name: "libvndk_example", vendor_available: true, vndk: { enabled: true, } }
Bei Datentypen, die direkt oder indirekt über eine exportierte Funktion erreichbar sind, gilt der Wert Die folgenden Änderungen an einer Bibliothek werden als ABI-absteigend eingestuft:
Datentyp | Beschreibung |
---|---|
Strukturen und Klassen |
|
Gewerkschaften |
|
Aufzählungen |
|
Globale Symbole |
|
* Sowohl öffentliche als auch private Mitgliedsfunktionen müssen geändert oder entfernt werden, da öffentliche Inline-Funktionen auf private Member-Funktionen. Symbolverweise auf Funktionen für private Mitglieder in den Binärdateien der Aufrufer gespeichert werden. Funktionen für private Mitglieder ändern oder entfernen aus gemeinsam genutzten Bibliotheken können zu nicht abwärtskompatiblen Binärprogrammen führen.
** Die Offsets für öffentliche oder private Datenmitglieder dürfen geändert, da Inline-Funktionen in ihrem Funktionstext. Wenn Sie Offsets für Datenelemente ändern, kann dies zu nicht abwärtskompatible Binärdateien.
*** Das Arbeitsspeicher-Layout wird dadurch nicht geändert. gibt es semantische Unterschiede, die dazu führen können, dass Bibliotheken und wie erwartet funktioniert.
ABI-Compliance-Tools verwenden
Beim Erstellen einer VNDK-Bibliothek wird das ABI der Bibliothek mit dem entsprechende ABI-Referenz für die Version des VNDK, das erstellt wird. Nachschlagewerke Hier findest du die ABI-Dumps:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/<PLATFORM_VNDK_VERSION>/<BINDER_BITNESS>/<ARCH>/source-based
Wenn Sie beispielsweise libfoo
für x86 auf API-Ebene 27 erstellen,
Das abgeleitete ABI von libfoo
wird mit der Referenz hier verglichen:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/27/64/x86/source-based/libfoo.so.lsdump
ABI-Ausfallfehler
Bei ABI-Fehlern werden im Build-Log Warnungen mit dem Warntyp und einer
zum Bericht „abi-diff“. Wenn das ABI von libbinder
beispielsweise
nicht kompatibel ist, gibt das Build-System einen Fehler mit einer Meldung
etwa so:
***************************************************** 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 ----
ABI-Prüfungen der VNDK-Bibliothek erstellen
Wenn eine VNDK-Bibliothek erstellt wird:
header-abi-dumper
verarbeitet die zu kompilierten Quelldateien die VNDK-Bibliothek erstellen (eigene Quelldateien und -dateien der Bibliothek) die über statische transitive Abhängigkeiten übernommen werden),.sdump
-Dateien, die der jeweiligen Quelle entsprechen.
Abbildung 1: .sdump
erstellen Dateienheader-abi-linker
verarbeitet dann.sdump
-Dateien (mit einem Versionsskript oder dem.so
entsprechenden Datei der gemeinsam genutzten Bibliothek), um ein.lsdump
-Objekt zu erstellen. -Datei, in der alle ABI-Informationen protokolliert werden, die der gemeinsam genutzten Bibliothek entsprechen.
Abbildung 2: .lsdump
erstellen Dateiheader-abi-diff
vergleicht die.lsdump
Datei mit einer.lsdump
-Referenzdatei zum Erstellen eines Differenzberichts in dem die Unterschiede zwischen den ABIs der beiden Bibliotheken beschrieben werden.
Abbildung 3: Bericht „Unterschiede“ erstellen
Header-Abi-Dump
Das header-abi-dumper
-Tool parst eine C/C++-Quelldatei und speichert sie aus
die aus dieser Quelldatei
abgeleitete ABI in eine Zwischendatei. Der Build
führt das System header-abi-dumper
für alle kompilierten Quelldateien aus, während
Erstellen einer Bibliothek, die die Quelldateien aus transitiven
Abhängigkeiten.
Eingänge |
|
---|---|
Ausgabe | Eine Datei, die die ABI der Quelldatei beschreibt (z. B.
foo.sdump für das ABI von foo.cpp steht).
|
Derzeit liegen .sdump
-Dateien im JSON-Format vor, das nicht
und bleibt auch bei zukünftigen
Releases stabil. Daher .sdump
sollte als Details zur Implementierung des Build-Systems betrachtet werden.
libfoo.so
hat beispielsweise die folgende Quelldatei
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; }
Mit header-abi-dumper
können Sie eine Zwischenstufe
.sdump
-Datei, die das von der Quelldatei bereitgestellte ABI darstellt
mit:
$ header-abi-dumper foo.cpp -I exported -o foo.sdump -- -I exported -x c++
Dieser Befehl weist header-abi-dumper
an,
foo.cpp
mit den Compiler-Flags nach --
und
die ABI-Informationen ausgeben, die von den öffentlichen Headern im
exported
. Folgendes ist
foo.sdump
generiert von
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
enthält von der Quelldatei exportierte ABI-Informationen
foo.cpp
und die öffentlichen Header, z. B.
record_types
Auf definierte Strukturen, Unions oder Klassen verweisen in den öffentlichen Headern. Jeder Eintragstyp enthält Informationen zu den Feldern, Größe, Zugriffsbezeichner, der Headerdatei, in der es definiert ist, Attribute.pointer_types
Direkt/indirekt auf Zeigertypen verweisen in den öffentlichen Headern durch die exportierten Datensätze/Funktionen referenziert wird, sowie durch den Typ, auf den der Zeiger verweist (über diereferenced_type
intype_info
. Ähnliche Informationen werden im.sdump
-Datei für qualifizierte Typen, integrierte C/C++-Typen, Array - und l-Wert- und r-Wert-Referenztypen. Diese Informationen können rekursive Differenz.functions
Stellt Funktionen dar, die durch öffentliche Header exportiert wurden. Sie enthalten auch Informationen über den gekürzten Namen der Funktion, den Rückgabetyp, die Parametertypen, den Zugriffsbezeichner und andere Attribute.
Header-Abi-Linker
Das header-abi-linker
-Tool nimmt die erstellten Zwischendateien
durch header-abi-dumper
als Eingabe und verknüpft dann diese Dateien:
Eingänge |
|
---|---|
Ausgabe | Eine Datei, die die ABI einer gemeinsam genutzten Bibliothek beschreibt (z. B.
libfoo.so.lsdump für das ABI von libfoo steht).
|
Das Tool führt die Typendiagramme in allen ihm übergebenen Zwischendateien zusammen,
unter Berücksichtigung einer Definition (benutzerdefinierte Typen in unterschiedlichen
Übersetzungseinheiten mit demselben voll qualifizierten Namen, könnten semantisch
Unterschiede zwischen Übersetzungseinheiten. Das Tool parst entweder
ein Versionsskript oder die Tabelle .dynsym
der gemeinsam genutzten Bibliothek
(Datei .so
), um eine Liste der exportierten Symbole zu erstellen.
libfoo
besteht beispielsweise aus foo.cpp
und
bar.cpp
header-abi-linker
könnte aufgerufen werden für
Erstellen Sie den vollständigen verknüpften ABI-Dump von libfoo
so:
header-abi-linker -I exported foo.sdump bar.sdump \ -o libfoo.so.lsdump \ -so libfoo.so \ -arch arm64 -api current
Beispiel für die Befehlsausgabe 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" : [] }
Das header-abi-linker
-Tool:
- Verknüpft die bereitgestellten
.sdump
-Dateien (foo.sdump
) undbar.sdump
). Dabei werden die ABI-Informationen herausgefiltert, die nicht in die Header im Verzeichnisexported
. - Parst
libfoo.so
und erfasst Informationen zu den Symbolen von der Bibliothek über ihre Tabelle.dynsym
exportiert wurde. - Fügt
_Z3FooiP3bar
und_Z6FooBadiP3foo
hinzu.
libfoo.so.lsdump
ist der abschließend generierte ABI-Dump von
libfoo.so
.
Header-abi-diff
Das header-abi-diff
-Tool vergleicht zwei .lsdump
-Dateien
die das ABI von zwei Bibliotheken repräsentiert, und erstellt einen Differenzbericht, aus dem hervorgeht,
Unterschiede zwischen den beiden ABIs.
Eingänge |
|
---|---|
Ausgabe | Einen Diff-Bericht, der die Unterschiede zwischen den von den beiden angebotenen ABIs ausgibt gemeinsam genutzten Bibliotheken verglichen werden. |
Die ABI-Differenzdatei befindet sich in <ph type="x-smartling-placeholder"></ph> protobuf-Textformat. Das Format kann sich ändern. in zukünftigen Versionen.
Sie haben beispielsweise zwei Versionen
libfoo
: libfoo_old.so
und
libfoo_new.so
Im libfoo_new.so
, in
bar_t
, Sie ändern den Typ von mfoo
von
foo_t
bis foo_t *
. Da bar_t
ein
erreichbar sind, sollte dies als funktionsgefährdende Änderung
header-abi-diff
.
So führen Sie header-abi-diff
aus:
header-abi-diff -old libfoo_old.so.lsdump \ -new libfoo_new.so.lsdump \ -arch arm64 \ -o libfoo.so.abidiff \ -lib libfoo
Beispiel für die Befehlsausgabe 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 } } }
libfoo.so.abidiff
enthält einen Bericht über alle ABI-Probleme
Änderungen bei libfoo
. Die record_type_diffs
-Nachricht
zeigt an, dass ein Datensatz geändert wurde, und listet die inkompatiblen Änderungen auf, die
umfassen:
- Die Größe des Eintrags ändert sich von
24
Byte in8
Byte. - Der Feldtyp von
mfoo
ändert sich vonfoo
zufoo *
(alle Typedefs werden entfernt).
Das Feld type_stack
gibt an, wie header-abi-diff
hat den Typ erreicht, der sich geändert hat (bar
). Dieses Feld kann
interpretiert als Foo
ist eine exportierte Funktion,
bar *
als Parameter, der auf bar
verweist, was
exportiert und geändert werden.
ABI und API erzwingen
Um die ABI und API von gemeinsam genutzten VNDK-Bibliotheken zu erzwingen, müssen ABI-Referenzen
bei ${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/
eingecheckt werden.
Führen Sie den folgenden Befehl aus, um diese Referenzen zu erstellen:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py
Nach dem Erstellen der Verweise: alle Änderungen am Quellcode, die sich daraus ergeben in einer inkompatiblen ABI/API-Änderung in einer VNDK-Bibliothek führt jetzt zu einem Build-Fehler.
Führen Sie den folgenden Befehl aus, um ABI-Referenzen für bestimmte Bibliotheken zu aktualisieren:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l <lib1> -l <lib2>
Führen Sie beispielsweise folgenden Befehl aus, um libbinder
ABI-Referenzen zu aktualisieren:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder