Stabilność interfejsu binarnego aplikacji (ABI) jest warunkiem wstępnym tylko na potrzeby platformy, ponieważ moduły dostawcy mogą zależeć od reklamy natywnej Biblioteki udostępnione pakietu SDK (VNDK) znajdujące się w partycji systemowej. W wersji na Androida nowo utworzone biblioteki udostępnione VNDK muszą być Zgodność z interfejsem ABI z opublikowanymi wcześniej bibliotekami udostępnionymi VNDK, dzięki czemu moduły dostawców mogą działać z tymi bibliotekami bez ponownej kompilacji i bez błędów podczas działania. Między wersjami Androida można zmieniać biblioteki VNDK, nie ma też interfejsu ABI i gwarancjami.
Aby zapewnić zgodność z interfejsem ABI, Android 9 zawiera narzędzia do sprawdzania interfejsu ABI nagłówka, jak opisano w kolejnych sekcjach.
Informacje o zgodności VNDK i ABI
VNDK to ograniczony zestaw bibliotek, do których moduły dostawców mogą zamieszczać linki które umożliwiają aktualizacje tylko za pomocą platformy. Zgodność z ABI odnosi się do jest możliwe, że nowsza wersja biblioteki udostępnionej będzie działać zgodnie z oczekiwaniami który jest z nim dynamicznie połączony (tzn. działa jako starsza wersja modułu biblioteka).
Wyeksportowane symbole
Wyeksportowany symbol (nazywany też symbolem globalnym) odnosi się do symbol, który spełnia wszystkie poniższe warunki:
- Wyeksportowane przez nagłówki publiczne biblioteki udostępnionej.
- Pojawia się w tabeli
.dynsym
pliku.so
odpowiadające zasobom wspólnych. - Ma wiązanie słabe lub GLOBALNE.
- Widoczność to DEFAULT lub PROTECTED.
- Indeks sekcji nie jest NIEDEFINED.
- Typ to FUNC lub OBJECT.
Nagłówki publiczne biblioteki udostępnionej są zdefiniowane jako nagłówki.
dostępnych dla innych bibliotek/plików binarnych za pośrednictwem
export_include_dirs
, export_header_lib_headers
,
export_static_lib_headers
,
export_shared_lib_headers
i
Atrybuty export_generated_headers
w Android.bp
definicje modułu odpowiadające zasobom wspólnych.
Typy osiągalne
Typ osiągalny to dowolny wbudowany lub zdefiniowany przez użytkownika typ w języku C/C++, który jest
dostępne bezpośrednio lub pośrednio za pomocą wyeksportowanego symbolu ORAZ wyeksportowany
za pomocą nagłówków publicznych. Na przykład libfoo.so
ma funkcję
Foo
, który jest eksportowanym symbolem znalezionym w
Tabela .dynsym
. Biblioteka libfoo.so
zawiera
:
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" ], } |
tabela .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
|
W kategorii Foo
dostępne są następujące typy osiągalne bezpośrednio i pośrednio:
Typ | Opis |
---|---|
bool
|
Typ zwrotu: Foo .
|
int
|
Typ pierwszego parametru Foo .
|
bar_t *
|
Typ drugiego parametru Foo. W drodze bar_t * ,
Dane bar_t są eksportowane przez usługę foo_exported.h .
bar_t zawiera element mfoo typu
foo_t , który jest eksportowany przez foo_exported.h ,
co powoduje eksport większej liczby typów:
Jednak domena foo_private_t NIE jest dostępna, ponieważ nie jest
wyeksportowane przez usługę foo_exported.h . (foo_private_t *
jest nieprzezroczysty, więc zmiany w foo_private_t są dozwolone).
|
Podobne wyjaśnienie można podać dla typów osiągalnych przez klasę bazową specyfikatory i parametry szablonu.
Zadbaj o zgodność z ABI
Biblioteki oznaczone jako muszą być zgodne z ABI
vendor_available: true
i vndk.enabled: true
w
odpowiednich Android.bp
plików. Na przykład:
cc_library { name: "libvndk_example", vendor_available: true, vndk: { enabled: true, } }
W przypadku typów danych, które są dostępne bezpośrednio lub pośrednio przez eksportowaną funkcję, następujące zmiany w bibliotece są klasyfikowane jako powodujące uszkodzenia w interfejsie ABI:
Typ danych | Opis |
---|---|
Struktury i zajęcia |
|
Związki |
|
Wyliczenia |
|
Symbole globalne |
|
* Zarówno publiczne, jak i prywatne funkcje członka muszą nie można zmienić ani usunąć, ponieważ publiczne funkcje wbudowane mogą odnosić się do funkcji członków prywatnych. Odniesienia do symboli do funkcji członków prywatnych mogą w plikach binarnych rozmówcy. Zmienianie lub usuwanie funkcji członków prywatnych z bibliotek udostępnionych może spowodować brak zgodności wstecznej plików binarnych.
** Przesunięcia na elementy publicznych lub prywatnych danych nie mogą być została zmieniona, ponieważ funkcje wbudowane mogą odwoływać się do tych elementów danych w swoich treść funkcji. Zmiana przesunięcia składu danych może spowodować niezgodne wstecznie pliki binarne.
*** Bez zmian w układzie pamięci tego typu, występują różnice semantyczne, które mogą sprawić, że biblioteki więc działa zgodnie z oczekiwaniami.
Korzystanie z narzędzi zgodności z ABI
Po utworzeniu biblioteki VNDK jej interfejs ABI jest porównywany z odpowiednie odwołanie do interfejsu ABI dla tworzonej wersji VNDK. Źródła wiedzy Zrzuty interfejsu ABI znajdują się w:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/<PLATFORM_VNDK_VERSION>/<BINDER_BITNESS>/<ARCH>/source-based
Na przykład w kompilacji libfoo
dla x86 na poziomie API 27:
Przypuszczalny interfejs ABI użytkownika libfoo
jest porównywany z jego plikiem referencyjnym pod adresem:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/27/64/x86/source-based/libfoo.so.lsdump
Błąd uszkodzenia interfejsu ABI
W przypadku awarii interfejsu ABI log kompilacji wyświetla ostrzeżenia z typem ostrzeżenia i
do raportu abi-diff. Jeśli na przykład interfejs ABI interfejsu libbinder
ma
niezgodną zmianę, system kompilacji zwróci błąd z komunikatem
podobnie jak poniżej:
***************************************************** 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 ----
Kompilowanie testów ABI biblioteki VNDK
Po utworzeniu biblioteki VNDK:
header-abi-dumper
przetwarza pliki źródłowe skompilowane do utworzyć bibliotekę VNDK (własne pliki źródłowe biblioteki, a także pliki źródłowe dziedziczone przez statyczne zależności przejściowe), aby wygenerować Liczba plików, które odpowiadają każdemu źródłom:.sdump
.
Rysunek 1. Tworzę .sdump
plikiheader-abi-linker
następnie przetwarza.sdump
(za pomocą udostępnionego skryptu wersji lub tagu.so
odpowiadający bibliotece współdzielonej), aby wygenerować.lsdump
który zawiera wszystkie informacje związane z interfejsem ABI powiązane z biblioteką współdzieloną.
Rysunek 2. Tworzę .lsdump
plikheader-abi-diff
porównuje.lsdump
plik zawierający odwołanie do pliku.lsdump
w celu wygenerowania raportu różnic które pokazują różnice w interfejsach ABI tych 2 bibliotek.
Rysunek 3. Tworzenie raportu różnic
nagłówek-abi-dumper,
Narzędzie header-abi-dumper
analizuje plik źródłowy w języku C/C++ i zrzuty
interfejs ABI uzyskany z tego pliku źródłowego do pliku pośredniego. Kompilacja
system uruchamia header-abi-dumper
na wszystkich skompilowanych plikach źródłowych podczas
stworzenie biblioteki zawierającej pliki źródłowe z przechodnich
zależności.
Wejścia |
|
---|---|
Wyjście | Plik opisujący interfejs ABI pliku źródłowego (na przykład
foo.sdump reprezentuje interfejs ABI interfejsu foo.cpp ).
|
Obecnie .sdump
pliki są w formacie JSON, który nie jest
musi być stabilna we wszystkich przyszłych wersjach. Z tego powodu .sdump
formatowanie plików należy uznać za szczegóły implementacji systemu kompilacji.
Na przykład plik libfoo.so
ma następujący plik źródłowy
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; }
Aby wygenerować poziom średniozaawansowany, możesz użyć atrybutu header-abi-dumper
Plik .sdump
reprezentujący interfejs ABI prezentowany w pliku źródłowym
przy użyciu:
$ header-abi-dumper foo.cpp -I exported -o foo.sdump -- -I exported -x c++
To polecenie informuje header-abi-dumper
o konieczności przeanalizowania
foo.cpp
z flagami kompilatora zgodnymi z zasadą --
oraz
wysyła informacje ABI eksportowane przez publiczne nagłówki w pliku
katalogu exported
. Oto
foo.sdump
wygenerowane przez
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
zawiera informacje o interfejsie ABI wyeksportowane przez plik źródłowy
foo.cpp
oraz nagłówki publiczne, na przykład
record_types
Odwołaj się do zdefiniowanych elementów struct, sumów lub klas w nagłówkach publicznych. Każdy typ rekordu ma informacje o jego polach, rozmiar, specyfikator dostępu, plik nagłówka, w którym jest zdefiniowany, .pointer_types
Odwoływać się bezpośrednio lub pośrednio do typów wskaźników do których odwołują się wyeksportowane rekordy/funkcje w nagłówkach publicznych, z typem, na który wskazuje wskaźnik (za pomocą funkcjireferenced_type
wtype_info
). Podobne informacje są rejestrowane na stronie Plik.sdump
dla kwalifikujących się typów, wbudowanych typów C/C++, tablica oraz odwołania do lvalue i rvalue. Takie informacje pozwalają różnicowanie rekurencyjne.functions
Reprezentuj funkcje wyeksportowane przez nagłówki publiczne. Zawierają również informacje o zniekształconej nazwie funkcji, zwracanego typu typy parametrów, specyfikator dostępu i inne atrybuty.
nagłówek-abi-linker,
Narzędzie header-abi-linker
wykorzystuje utworzone pliki pośrednie
przez header-abi-dumper
jako dane wejściowe, a następnie łączy te pliki:
Wejścia |
|
---|---|
Wyjście | Plik opisujący interfejs ABI udostępnianej biblioteki (np.
libfoo.so.lsdump reprezentuje interfejs ABI interfejsu libfoo ).
|
Narzędzie scala wykresy typów we wszystkich przekazanych mu plikach pośrednich,
biorąc pod uwagę jedną definicję (zdefiniowane przez użytkownika typy w różnych
jednostek tłumaczeniowych o tej samej pełnej i jednoznacznej nazwie mogą być semantyczne
różnych jednostek tłumaczeniowych. Następnie narzędzie analizuje
skrypt wersji lub tabela .dynsym
zasobów wspólnych;
(.so
), aby utworzyć listę wyeksportowanych symboli.
Na przykład libfoo
składa się z foo.cpp
i
bar.cpp
Funkcja header-abi-linker
może zostać wywołana do
utwórz pełny powiązany zrzut ABI libfoo
w następujący sposób:
header-abi-linker -I exported foo.sdump bar.sdump \ -o libfoo.so.lsdump \ -so libfoo.so \ -arch arm64 -api current
Przykładowe dane wyjściowe polecenia w 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" : [] }
Narzędzie header-abi-linker
:
- Zawiera link do pliku
.sdump
(foo.sdump
) ibar.sdump
), odfiltrowując informacje dotyczące interfejsu ABI, których nie ma w nagłówki znajdujące się w katalogu:exported
. - Przetwarza element
libfoo.so
i zbiera informacje o symbolach wyeksportowane przez bibliotekę przy użyciu tabeli.dynsym
. - Dodaje
_Z3FooiP3bar
i_Z6FooBadiP3foo
.
libfoo.so.lsdump
to ostateczny wygenerowany zrzut ABI
libfoo.so
nagłówek-abi-diff
Narzędzie header-abi-diff
porównuje 2 pliki .lsdump
interfejsu ABI dwóch bibliotek i tworzy raport różnic zawierający
różnic między tymi dwoma interfejsami ABI.
Wejścia |
|
---|---|
Wyjście | raport różnic zawierający różnice w interfejsach ABI oferowanych przez te 2 usługi; biblioteki udostępnione. |
Plik diff ABI znajduje się w format tekstowy protobuf. Format może ulec zmianie. w kolejnych wersjach.
Na przykład masz 2 wersje
libfoo
: libfoo_old.so
i
libfoo_new.so
Za libfoo_new.so
, w:
bar_t
, zmieniasz typ elementu mfoo
z
foo_t
do foo_t *
. Ponieważ bar_t
to
typ osiągalny, powinien być oznaczony jako zmiana powodująca niezgodność interfejsu ABI przez
header-abi-diff
Aby uruchomić header-abi-diff
:
header-abi-diff -old libfoo_old.so.lsdump \ -new libfoo_new.so.lsdump \ -arch arm64 \ -o libfoo.so.abidiff \ -lib libfoo
Przykładowe dane wyjściowe polecenia w 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
zawiera raport o wszystkich problemach z interfejsem ABI
zmiany w miesiącu: libfoo
. Wiadomość record_type_diffs
wskazuje, że rekord został zmieniony, i wyświetla listę niezgodnych zmian, która
uwzględnij:
- Rozmiar rekordu zmienia się z
24
B na8
B. - Typ pola
mfoo
zmienia się zfoo
nafoo *
(wszystkie definicje typów są usuwane).
Pole type_stack
wskazuje, jak header-abi-diff
osiągnięto typ, który uległ zmianie (bar
). To pole może być
interpretowana jako Foo
to wyeksportowana funkcja, która przyjmuje
bar *
jako parametru, który wskazuje bar
, która była
wyeksportowany i zmieniony.
Wymuszaj interfejsy ABI i API
Aby egzekwować interfejsy ABI i API w bibliotekach udostępnionych VNDK, odwołania do ABI muszą
zameldowany(a) w ${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/
.
Aby utworzyć te odwołania, uruchom następujące polecenie:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py
Po utworzeniu odwołań wszystkie zmiany wprowadzone w kodzie źródłowym, które prowadzą do niekompatybilna zmiana interfejsu ABI/API w bibliotece VNDK powoduje teraz błąd kompilacji.
Aby zaktualizować odwołania do ABI dla określonych bibliotek, uruchom to polecenie:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l <lib1> -l <lib2>
Aby na przykład zaktualizować odwołania do interfejsu ABI libbinder
, uruchom polecenie:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder