Stabilność ABI

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

Stabilność interfejsu binarnego aplikacji (ABI) jest warunkiem wstępnym aktualizacji tylko struktury, ponieważ moduły dostawców mogą zależeć od bibliotek współdzielonych VNDK (Vendor Native Development Kit), które znajdują się na partycji systemowej. W ramach jednej wersji systemu Android nowo zbudowane biblioteki współdzielone VNDK muszą być kompatybilne z ABI z wcześniej wydanymi bibliotekami współdzielonymi VNDK, aby moduły dostawców mogły współpracować z tymi bibliotekami bez ponownej kompilacji i bez błędów w czasie wykonywania. Pomiędzy wersjami Androida biblioteki VNDK można zmieniać i nie ma gwarancji ABI.

Aby zapewnić zgodność z ABI, system Android 9 zawiera moduł sprawdzania nagłówka ABI, zgodnie z opisem w poniższych sekcjach.

O zgodności VNDK i ABI

VNDK to restrykcyjny zestaw bibliotek, z którymi mogą łączyć się moduły dostawców i które umożliwiają aktualizacje tylko dla platformy. Zgodność z ABI odnosi się do zdolności nowszej wersji biblioteki współdzielonej do działania zgodnie z oczekiwaniami z modułem, który jest z nią dynamicznie powiązany (tj. działa tak, jak działałaby starsza wersja biblioteki).

Informacje o wyeksportowanych symbolach

Wyeksportowany symbol (znany również jako symbol globalny ) odnosi się do symbolu, który spełnia wszystkie poniższe warunki:

  • Eksportowane przez publiczne nagłówki udostępnionej biblioteki.
  • Pojawia się w tabeli .dynsym pliku .so odpowiadającego udostępnionej bibliotece.
  • Ma wiązanie SŁABE lub GLOBALNE.
  • Widoczność jest DOMYŚLNA lub CHRONIONA.
  • Indeks sekcji nie jest NIEZDEFINIOWANY.
  • Typ to FUNC lub OBJECT.

Publiczne nagłówki biblioteki współużytkowanej są definiowane jako nagłówki dostępne dla innych bibliotek/plików binarnych za pośrednictwem atrybutów export_include_dirs , export_header_lib_headers , export_static_lib_headers , export_shared_lib_headers i export_generated_headers w definicjach modułu Android.bp odpowiadającej udostępnionej bibliotece.

O osiągalnych typach

Typ osiągalny to dowolny wbudowany lub zdefiniowany przez użytkownika typ C/C++, który jest osiągalny bezpośrednio lub pośrednio poprzez wyeksportowany symbol ORAZ wyeksportowany przez publiczne nagłówki. Na przykład libfoo.so ma funkcję Foo , która jest wyeksportowanym symbolem znajdującym się w tabeli .dynsym . Biblioteka libfoo.so zawiera następujące elementy:

foo_exported.h foo.prywatny.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

Patrząc na Foo , typy osiągalne bezpośrednio/pośrednio obejmują:

Typ Opis
bool Zwróć typ Foo .
int Typ pierwszego parametru Foo .
bar_t * Typ drugiego parametru Foo. Za pomocą bar_t * bar_t jest eksportowany przez foo_exported.h .

bar_t zawiera element mfoo , typu foo_t , który jest eksportowany przez foo_exported.h , co skutkuje eksportowaniem większej liczby typów:
  • int : jest typem m1 .
  • int * : jest typem m2 .
  • foo_private_t * : jest typem mPfoo .

Jednak foo_private_t NIE jest osiągalny, ponieważ nie jest eksportowany przez foo_exported.h . ( foo_private_t * jest nieprzezroczyste, dlatego dozwolone są zmiany wprowadzone w foo_private_t .)

Podobne wyjaśnienie można podać dla typów osiągalnych za pomocą specyfikatorów klasy bazowej i parametrów szablonu.

Zapewnienie zgodności z ABI

Należy zapewnić zgodność z ABI dla bibliotek oznaczonych vendor_available: true i vndk.enabled: true w odpowiednich plikach Android.bp . Na przykład:

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

W przypadku typów danych, do których można dotrzeć bezpośrednio lub pośrednio przez wyeksportowaną funkcję, następujące zmiany w bibliotece są klasyfikowane jako naruszające ABI:

Typ danych Opis
Struktury i klasy
  • Zmień rozmiar typu klasy lub typu struktury.
  • Klasy bazowe
    • Dodaj lub usuń klasy podstawowe.
    • Dodaj lub usuń praktycznie odziedziczone klasy podstawowe.
    • Zmień kolejność klas podstawowych.
  • Funkcje członkowskie
    • Usuń funkcje składowe*.
    • Dodaj lub usuń argumenty z funkcji składowych.
    • Zmień typy argumentów lub zwracane typy funkcji składowych*.
    • Zmień układ wirtualnego stołu.
  • Członkowie danych
    • Usuń elementy danych statycznych.
    • Dodaj lub usuń niestatyczne elementy danych.
    • Zmień typy członków danych.
    • Zmień przesunięcia na niestatyczne składowe danych**.
    • Zmień const , volatile i/lub restricted kwalifikatory członków danych***.
    • Zmniejsz specyfikatory dostępu członków danych***.
  • Zmień argumenty szablonu.
Związki
  • Dodaj lub usuń członków danych.
  • Zmień rozmiar typu unii.
  • Zmień typy członków danych.
wyliczenia
  • Zmień typ bazowy.
  • Zmień nazwy wyliczaczy.
  • Zmień wartości wyliczaczy.
Symbole globalne
  • Usuń symbole wyeksportowane przez nagłówki publiczne.
  • Dla symboli globalnych typu FUNC
    • Dodaj lub usuń argumenty.
    • Zmień typy argumentów.
    • Zmień typ zwrotu.
    • Zmień wersję specyfikatora dostępu***.
  • Dla globalnych symboli typu OBJECT
    • Zmień odpowiedni typ C/C++.
    • Zmień wersję specyfikatora dostępu***.

* Zarówno publiczne, jak i prywatne funkcje składowe nie mogą być zmieniane ani usuwane, ponieważ publiczne funkcje wbudowane mogą odwoływać się do prywatnych funkcji składowych. Odwołania symboli do prywatnych funkcji składowych mogą być przechowywane w plikach binarnych wywołującego. Zmiana lub usunięcie prywatnych funkcji składowych z bibliotek współużytkowanych może spowodować powstanie niekompatybilnych wstecznie plików binarnych.

** Przesunięcia do publicznych lub prywatnych elementów danych nie mogą być zmieniane, ponieważ funkcje wbudowane mogą odwoływać się do tych elementów danych w treści ich funkcji. Zmiana przesunięć elementu członkowskiego danych może spowodować, że pliki binarne będą niezgodne z poprzednimi wersjami.

*** Chociaż nie zmieniają one układu pamięci typu, istnieją różnice semantyczne, które mogą spowodować, że biblioteki nie będą działać zgodnie z oczekiwaniami.

Korzystanie z narzędzi zgodności ABI

Kiedy budowana jest biblioteka VNDK, ABI biblioteki jest porównywane z odpowiednim odwołaniem ABI dla wersji tworzonej biblioteki VNDK. Referencyjne zrzuty ABI znajdują się w:

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

Na przykład, budując libfoo dla x86 na poziomie API 27, wywnioskowany ABI libfoo jest porównywany z jego referencją pod adresem:

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

Błąd uszkodzenia ABI

W przypadku awarii ABI dziennik kompilacji wyświetla ostrzeżenia z typem ostrzeżenia i ścieżką do raportu abi-diff. Na przykład, jeśli ABI libbinder ma niekompatybilną zmianę, system kompilacji zgłasza błąd z komunikatem podobnym do następującego:

*****************************************************
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 ----

Budowanie kontroli ABI biblioteki VNDK

Gdy budowana jest biblioteka VNDK:

  1. header-abi-dumper przetwarza pliki źródłowe skompilowane w celu zbudowania biblioteki VNDK (własne pliki źródłowe biblioteki, a także pliki źródłowe odziedziczone przez statyczne zależności przechodnie), aby utworzyć pliki .sdump odpowiadające każdemu źródłu.
    sdump creation
    Rysunek 1. Tworzenie plików .sdump
  2. header-abi-linker następnie przetwarza pliki .sdump (przy użyciu dostarczonego mu skryptu wersji lub pliku .so odpowiadającego udostępnionej bibliotece) w celu utworzenia pliku .lsdump , który rejestruje wszystkie informacje ABI odpowiadające udostępnionej bibliotece.
    lsdump creation
    Rysunek 2. Tworzenie pliku .lsdump
  3. header-abi-diff porównuje plik .lsdump z referencyjnym plikiem .lsdump w celu utworzenia raportu różnic, który przedstawia różnice w ABI obu bibliotek.
    abi diff creation
    Rysunek 3. Tworzenie raportu różnic

header-abi-dumper

Narzędzie header-abi-dumper analizuje plik źródłowy C/C++ i zrzuca ABI wywnioskowane z tego pliku źródłowego do pliku pośredniego. System kompilacji uruchamia header-abi-dumper na wszystkich skompilowanych plikach źródłowych, jednocześnie budując bibliotekę zawierającą pliki źródłowe z zależności przechodnich.

Wejścia
  • Plik źródłowy AC/C++
  • Wyeksportowane obejmują katalogi
  • Flagi kompilatora
Wyjście Plik opisujący ABI pliku źródłowego (na przykład foo.sdump reprezentuje ABI foo.cpp ).

Obecnie pliki .sdump są w formacie JSON, co nie gwarantuje stabilności w przyszłych wersjach. W związku z tym formatowanie pliku .sdump powinno być traktowane jako szczegół implementacji systemu kompilacji.

Na przykład 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;
}

Możesz użyć header-abi-dumper do wygenerowania pośredniego pliku .sdump reprezentującego ABI prezentowanego przez plik źródłowy przy użyciu:

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

To polecenie mówi header-abi-dumper , aby przeanalizował foo.cpp z flagami kompilatora następującymi po -- i wyemitował informacje ABI, które są eksportowane przez publiczne nagłówki w exported katalogu. Poniżej plik foo.sdump wygenerowany 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 ABI wyeksportowane przez plik źródłowy foo.cpp i publiczne nagłówki, na przykład

  • record_types . Odwołaj się do struktur, unii lub klas zdefiniowanych w publicznych nagłówkach. Każdy typ rekordu zawiera informacje o swoich polach, rozmiarze, specyfikatorze dostępu, pliku nagłówkowym, w którym jest zdefiniowany, oraz innych atrybutach.
  • pointer_types . Odwołaj się do typów wskaźników, do których odwołują się bezpośrednio/pośrednio wyeksportowane rekordy/funkcje w nagłówkach publicznych, wraz z typem, na który wskazuje wskaźnik (poprzez pole referenced_type w type_info ). Podobne informacje są rejestrowane w pliku .sdump dla typów kwalifikowanych, wbudowanych typów C/C++, typów tablic oraz typów referencyjnych lvalue i rvalue. Takie informacje umożliwiają różnicowanie rekurencyjne.
  • functions . Reprezentuj funkcje eksportowane przez nagłówki publiczne. Zawierają również informacje o zniekształconej nazwie funkcji, zwracanym typie, typach parametrów, specyfikatorze dostępu i innych atrybutach.

nagłówek-abi-linker

Narzędzie header-abi-linker pobiera pliki pośrednie utworzone przez header-abi-dumper jako dane wejściowe, a następnie łączy te pliki:

Wejścia
  • Pliki pośrednie utworzone przez header-abi-dumper
  • Skrypt wersji/plik mapy (opcjonalnie)
  • .so plik udostępnionej biblioteki
  • Wyeksportowane obejmują katalogi
Wyjście Plik opisujący ABI biblioteki współdzielonej (na przykład libfoo.so.lsdump reprezentuje ABI biblioteki libfoo ).

Narzędzie łączy wykresy typów we wszystkich przekazanych mu plikach pośrednich, biorąc pod uwagę różnice w jednej definicji (typy zdefiniowane przez użytkownika w różnych jednostkach tłumaczeniowych o tej samej pełnej nazwie, mogą być semantycznie różne) różnice między jednostkami tłumaczeniowymi. Następnie narzędzie analizuje skrypt wersji lub tabelę .dynsym biblioteki współdzielonej (plik .so ), aby utworzyć listę wyeksportowanych symboli.

Na przykład libfoo składa się z foo.cpp i bar.cpp . header-abi-linker można wywołać, aby utworzyć kompletny połączony 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 wyjście 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 :

  • Łączy dostarczone pliki .sdump ( foo.sdump i bar.sdump ), odfiltrowując informacje ABI nieobecne w nagłówkach znajdujących się w katalogu: exported .
  • Analizuje plik libfoo.so i zbiera informacje o symbolach wyeksportowanych przez bibliotekę za pośrednictwem tabeli .dynsym .
  • Dodaje _Z3FooiP3bar i _Z6FooBadiP3foo .

libfoo.so.lsdump to ostateczny wygenerowany zrzut ABI libfoo.so .

różnica-nagłówka

Narzędzie header-abi-diff porównuje dwa pliki .lsdump reprezentujące ABI dwóch bibliotek i tworzy raport różnicowy określający różnice między tymi dwoma ABI.

Wejścia
  • Plik .lsdump reprezentujący ABI starej biblioteki współdzielonej.
  • Plik .lsdump reprezentujący ABI nowej udostępnionej biblioteki.
Wyjście Raport różnic określający różnice w ABI oferowanych przez dwie porównywane biblioteki współdzielone.

Plik diff ABI jest w formacie tekstowym protobuf . Format może ulec zmianie w przyszłych wydaniach.

Na przykład masz dwie wersje libfoo : libfoo_old.so i libfoo_new.so . W libfoo_new.so , w bar_t zmieniasz typ mfoo z foo_t na foo_t * . Ponieważ bar_t jest typem osiągalnym, należy go oznaczyć jako zmianę przerywającą 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 wyjście 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
    }
  }
}

Plik libfoo.so.abidiff zawiera raport o wszystkich przełomowych zmianach ABI w libfoo . Komunikat record_type_diffs wskazuje, że rekord został zmieniony i zawiera listę niezgodnych zmian, które obejmują:

  • Rozmiar rekordu zmienia się z 24 bajtów na 8 bajtów.
  • Typ pola mfoo zmienia się z foo na foo * (wszystkie definicje typów są usunięte).

Pole type_stack wskazuje, w jaki sposób header-abi-diff osiągnął typ, który się zmienił ( bar ). To pole może być interpretowane jako Foo jest wyeksportowaną funkcją, która przyjmuje bar * jako parametr, który wskazuje na bar , który został wyeksportowany i zmieniony.

Wymuszanie ABI/API

Aby wymusić ABI/API bibliotek współdzielonych VNDK, odwołania ABI muszą zostać sprawdzone w ${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/ . Aby utworzyć te odniesienia, uruchom następujące polecenie:

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

Po utworzeniu odwołań każda zmiana w kodzie źródłowym, która powoduje niezgodną zmianę ABI/API w bibliotece VNDK, powoduje teraz błąd kompilacji.

Aby zaktualizować odniesienia ABI dla określonych bibliotek, uruchom następującą komendę:

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

Na przykład, aby zaktualizować referencje ABI libbinder , uruchom:

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