Uygulama İkili Arayüzü (ABI) kararlılığı, yalnızca çerçeve güncellemelerinin bir ön koşuludur çünkü satıcı modülleri, sistem bölümünde bulunan Satıcı Yerel Geliştirme Kiti (VNDK) paylaşılan kitaplıklarına bağlı olabilir. Bir Android sürümünde, yeni oluşturulan VNDK paylaşılan kitaplıklarının, daha önce yayımlanan VNDK paylaşılan kitaplıklarıyla ABI uyumlu olması gerekir; böylece satıcı modüller, yeniden derleme olmadan ve çalışma zamanı hataları olmadan bu kitaplıklarla çalışabilir. Android sürümleri arasında VNDK kitaplıkları değiştirilebilir ve ABI garantisi yoktur.
ABI uyumluluğunun sağlanmasına yardımcı olmak için Android 9, aşağıdaki bölümlerde açıklandığı gibi bir başlık ABI denetleyicisi içerir.
VNDK ve ABI uyumluluğu hakkında
VNDK, satıcı modüllerinin bağlanabileceği ve yalnızca çerçeve güncellemelerini etkinleştiren kısıtlayıcı bir kitaplıklar kümesidir. ABI uyumluluğu, paylaşılan bir kitaplığın daha yeni bir sürümünün, kendisine dinamik olarak bağlı bir modülle beklendiği gibi çalışabilme yeteneğini ifade eder (yani, kitaplığın eski bir sürümünün yapacağı gibi çalışır).
Dışa aktarılan semboller hakkında
Dışa aktarılan bir sembol (aynı zamanda global sembol olarak da bilinir), aşağıdakilerin tümünü karşılayan bir sembolü ifade eder:
- Paylaşılan bir kitaplığın genel başlıkları tarafından dışa aktarılır.
- Paylaşılan kitaplığa karşılık gelen
.so
dosyasının.dynsym
tablosunda görünür. - ZAYIF veya GLOBAL bağlamaya sahiptir.
- Görünürlük VARSAYILAN veya KORUNMALI'dır.
- Bölüm dizini UNDEFINED değil.
- Tür ya FUNC ya da OBJECT'tir.
Paylaşılan bir kitaplığın genel üstbilgileri, paylaşılan kitaplığa karşılık gelen modülün Android.bp
tanımlarındaki export_include_dirs
, export_header_lib_headers
, export_static_lib_headers
, export_shared_lib_headers
ve export_generated_headers
nitelikleri aracılığıyla diğer kitaplıklar/ikili dosyalar için kullanılabilen başlıklar olarak tanımlanır.
Ulaşılabilir türler hakkında
Ulaşılabilir tür, dışa aktarılan bir sembol aracılığıyla doğrudan veya dolaylı olarak erişilebilen VE genel başlıklar aracılığıyla dışa aktarılan herhangi bir C/C++ yerleşik veya kullanıcı tanımlı türdür. Örneğin, libfoo.so
, .dynsym
tablosunda bulunan, dışa aktarılan bir sembol olan Foo
işlevine sahiptir. libfoo.so
kütüphanesi aşağıdakileri içerir:
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 tablosu | |||||||
---|---|---|---|---|---|---|---|
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 |
Foo
bakıldığında, doğrudan/dolaylı ulaşılabilir türler şunları içerir:
Tip | Tanım |
---|---|
bool | Foo dönüş türü. |
int | İlk Foo parametresinin türü. |
bar_t * | İkinci Foo parametresinin türü. bar_t * aracılığıyla bar_t , foo_exported.h aracılığıyla dışa aktarılır.bar_t foo_exported.h aracılığıyla dışa aktarılan foo_t türünde mfoo üyesini içerir, bu da daha fazla türün dışa aktarılmasına neden olur:
Ancak foo_private_t , foo_exported.h aracılığıyla dışa aktarılmadığından ulaşılamaz. ( foo_private_t * opaktır, bu nedenle foo_private_t üzerinde yapılan değişikliklere izin verilir.) |
Temel sınıf belirticileri ve şablon parametreleri aracılığıyla ulaşılabilen türler için de benzer bir açıklama yapılabilir.
ABI uyumluluğunun sağlanması
İlgili Android.bp
dosyalarında vendor_available: true
ve vndk.enabled: true
olarak işaretlenen kitaplıklar için ABI uyumluluğu sağlanmalıdır. Örneğin:
cc_library { name: "libvndk_example", vendor_available: true, vndk: { enabled: true, } }
Dışa aktarılan bir işlev tarafından doğrudan veya dolaylı olarak erişilebilen veri türleri için, bir kitaplıkta yapılan aşağıdaki değişiklikler ABI'yi bozan değişiklikler olarak sınıflandırılır:
Veri tipi | Tanım |
---|---|
Yapılar ve Sınıflar |
|
Sendikalar |
|
Numaralandırmalar |
|
Küresel Semboller |
|
* Genel satır içi işlevler özel üye işlevlerine atıfta bulunabileceğinden, hem genel hem de özel üye işlevleri değiştirilmemeli veya kaldırılmamalıdır. Özel üye işlevlerine yönelik sembol referansları arayan ikili dosyalarda tutulabilir. Özel üye işlevlerinin paylaşılan kitaplıklardan değiştirilmesi veya kaldırılması, ikili dosyaların geriye dönük olarak uyumsuz olmasına neden olabilir.
** Satır içi işlevler, işlev gövdelerinde bu veri üyelerine başvurabildiğinden genel veya özel veri üyelerine ilişkin uzaklıklar değiştirilmemelidir. Veri üyesi uzaklıklarının değiştirilmesi, geriye dönük olarak uyumsuz ikili dosyalara neden olabilir.
*** Bunlar türün bellek düzenini değiştirmese de kitaplıkların beklendiği gibi çalışmamasına yol açabilecek anlamsal farklılıklar vardır.
ABI uyumluluk araçlarını kullanma
Bir VNDK kitaplığı oluşturulduğunda, kitaplığın ABI'si, oluşturulmakta olan VNDK sürümüne karşılık gelen ABI referansıyla karşılaştırılır. Referans ABI dökümleri şurada bulunur:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/<PLATFORM_VNDK_VERSION>/<BINDER_BITNESS>/<ARCH>/source-based
Örneğin, API düzeyi 27'de x86 için libfoo
derlenirken, libfoo
çıkarılan ABI şu adresteki referansıyla karşılaştırılır:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/27/64/x86/source-based/libfoo.so.lsdump
ABI kırılma hatası
ABI kesintilerinde derleme günlüğü, uyarı türüyle birlikte uyarıları ve abi-diff raporuna giden yolu görüntüler. Örneğin, libbinder
ABI'sinde uyumsuz bir değişiklik varsa derleme sistemi aşağıdakine benzer bir mesajla bir hata atar:
***************************************************** 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 ----
VNDK kütüphanesi ABI kontrolleri oluşturma
Bir VNDK kütüphanesi oluşturulduğunda:
-
header-abi-dumper
her kaynağa karşılık gelen.sdump
dosyalarını üretmek için VNDK kitaplığını (kitaplığın kendi kaynak dosyalarının yanı sıra statik geçişli bağımlılıklar yoluyla devralınan kaynak dosyaları) oluşturmak için derlenen kaynak dosyalarını işler.Şekil 1. .sdump
dosyalarını oluşturma -
header-abi-linker
daha sonra.sdump
dosyalarını işler (kendisine sağlanan bir sürüm komut dosyasını veya paylaşılan kitaplığa karşılık gelen.so
dosyasını kullanarak), paylaşılan kitaplığa karşılık gelen tüm ABI bilgilerini günlüğe kaydeden bir.lsdump
dosyası üretir.Şekil 2. .lsdump
dosyasını oluşturma -
header-abi-diff
iki kitaplığın ABI'lerindeki farklılıkları özetleyen bir diff raporu oluşturmak için.lsdump
dosyasını bir referans.lsdump
dosyasıyla karşılaştırır.Şekil 3. Fark raporunun oluşturulması
başlık-abi-damper
header-abi-dumper
aracı bir C/C++ kaynak dosyasını ayrıştırır ve bu kaynak dosyadan çıkarılan ABI'yi bir ara dosyaya döker. Derleme sistemi, derlenmiş tüm kaynak dosyalar üzerinde header-abi-dumper
çalıştırırken aynı zamanda geçişli bağımlılıklardan gelen kaynak dosyaları içeren bir kitaplık da oluşturur.
Girişler |
|
---|---|
Çıktı | Kaynak dosyanın ABI'sini açıklayan bir dosya (örneğin foo.sdump , foo.cpp ABI'sini temsil eder). |
Şu anda .sdump
dosyaları JSON biçimindedir ve bu biçimin gelecek sürümlerde kararlı olacağı garanti edilmez. Bu nedenle, .sdump
dosya biçimlendirmesinin bir derleme sistemi uygulama ayrıntısı olarak düşünülmesi gerekir.
Örneğin, libfoo.so
aşağıdaki kaynak dosya foo.cpp
sahiptir:
#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; }
Aşağıdakileri kullanarak kaynak dosya tarafından sunulan ABI'yi temsil eden bir ara .sdump
dosyası oluşturmak için header-abi-dumper
kullanabilirsiniz:
$ header-abi-dumper foo.cpp -I exported -o foo.sdump -- -I exported -x c++
Bu komut header-abi-dumper
foo.cpp
--
izleyen derleyici bayraklarıyla ayrıştırmasını ve exported
dizindeki genel başlıklar tarafından dışa aktarılan ABI bilgilerini yaymasını söyler. header-abi-dumper
tarafından oluşturulan foo.sdump
aşağıdadır:
{ "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
foo.cpp
kaynak dosyası ve genel başlıklar tarafından dışa aktarılan ABI bilgilerini içerir; örneğin,
-
record_types
. Genel başlıklarda tanımlanan yapılara, birleşimlere veya sınıflara bakın. Her kayıt türü; alanları, boyutu, erişim belirteci, içinde tanımlandığı başlık dosyası ve diğer nitelikler hakkında bilgi içerir. -
pointer_types
. Genel başlıklarda dışa aktarılan kayıtlar/işlevler tarafından doğrudan/dolaylı olarak başvurulan işaretçi türlerine ve işaretçinin işaret ettiği türe (type_info
içindekireferenced_type
alanı aracılığıyla) bakın. Nitelikli türler, yerleşik C/C++ türleri, dizi türleri ve lvalue ve rvalue başvuru türleri için benzer bilgiler.sdump
dosyasına kaydedilir. Bu tür bilgiler özyinelemeli farklılığa izin verir. -
functions
. Genel başlıklar tarafından dışarı aktarılan işlevleri temsil eder. Ayrıca işlevin değiştirilmiş adı, dönüş türü, parametre türleri, erişim belirtici ve diğer nitelikler hakkında da bilgi içerirler.
başlık-abi-bağlayıcı
header-abi-linker
aracı, header-abi-dumper
tarafından üretilen ara dosyaları girdi olarak alır ve ardından bu dosyaları bağlar:
Girişler |
|
---|---|
Çıktı | Paylaşılan bir kitaplığın ABI'sini açıklayan bir dosya (örneğin, libfoo.so.lsdump libfoo ABI'sini temsil eder). |
Araç, çeviri birimleri arasındaki tek tanımlı (aynı tam nitelikli ada sahip farklı çeviri birimlerindeki kullanıcı tanımlı türler, anlamsal olarak farklı olabilir) farklılıkları dikkate alarak kendisine verilen tüm ara dosyalardaki tür grafiklerini birleştirir. Araç daha sonra dışa aktarılan sembollerin bir listesini oluşturmak için bir sürüm komut dosyasını veya paylaşılan kitaplığın ( .so
dosyası) .dynsym
tablosunu ayrıştırır.
Örneğin libfoo
, foo.cpp
ve bar.cpp
oluşur. libfoo
tam bağlantılı ABI dökümünü oluşturmak için header-abi-linker
şu şekilde çağrılabilir:
header-abi-linker -I exported foo.sdump bar.sdump \ -o libfoo.so.lsdump \ -so libfoo.so \ -arch arm64 -api current
libfoo.so.lsdump
örnek komut çıktısı:
{ "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" : [] }
header-abi-linker
aracı:
- Kendisine sağlanan
.sdump
dosyalarını (foo.sdump
vebar.sdump
) bağlar ve şu dizinde bulunan başlıklarda bulunmayan ABI bilgilerini filtreler:exported
. -
libfoo.so
dosyasını ayrıştırır ve.dynsym
tablosu aracılığıyla kitaplık tarafından dışa aktarılan semboller hakkında bilgi toplar. -
_Z3FooiP3bar
ve_Z6FooBadiP3foo
ekler.
libfoo.so.lsdump
, libfoo.so
oluşturulan son ABI dökümüdür.
başlık-abi-fark
header-abi-diff
aracı, iki kitaplığın ABI'sini temsil eden iki .lsdump
dosyasını karşılaştırır ve iki ABI arasındaki farkları belirten bir diff raporu üretir.
Girişler |
|
---|---|
Çıktı | Karşılaştırılan iki paylaşılan kitaplığın sunduğu ABI'lerdeki farklılıkları belirten bir fark raporu. |
ABI fark dosyası protobuf metin formatındadır . Format gelecek sürümlerde değişebilir.
Örneğin, libfoo
iki sürümü var: libfoo_old.so
ve libfoo_new.so
. libfoo_new.so
, bar_t
, mfoo
türünü foo_t
yerine foo_t *
olarak değiştirirsiniz. bar_t
erişilebilir bir tür olduğundan, bu header-abi-diff
tarafından ABI'yi bozan bir değişiklik olarak işaretlenmelidir.
header-abi-diff
çalıştırmak için:
header-abi-diff -old libfoo_old.so.lsdump \ -new libfoo_new.so.lsdump \ -arch arm64 \ -o libfoo.so.abidiff \ -lib libfoo
libfoo.so.abidiff
örnek komut çıktısı:
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
, libfoo
tüm ABI değişikliklerinin raporunu içerir. record_type_diffs
mesajı, bir kaydın değiştiğini belirtir ve aşağıdakileri içeren uyumsuz değişiklikleri listeler:
- Kaydın boyutu
24
bayttan8
bayta değişiyor. -
mfoo
alan türüfoo
foo *
ya değişiyor (tüm typedef'ler çıkarılmıştır).
type_stack
alanı header-abi-diff
değişen türe ( bar
) nasıl ulaştığını gösterir. Bu alan, Foo
parametre olarak bar *
ı alan, dışa aktarılan ve değiştirilen bar
işaret eden, dışa aktarılan bir işlev olduğu şeklinde yorumlanabilir.
ABI/API'yi zorunlu kılma
VNDK paylaşılan kitaplıklarının ABI/API'sini uygulamak için ABI referanslarının ${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/
içine kontrol edilmesi gerekir. Bu referansları oluşturmak için aşağıdaki komutu çalıştırın:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py
Referansları oluşturduktan sonra kaynak kodunda yapılan ve VNDK kitaplığında uyumsuz bir ABI/API değişikliğine yol açan herhangi bir değişiklik artık derleme hatasıyla sonuçlanıyor.
Belirli kitaplıkların ABI referanslarını güncellemek için aşağıdaki komutu çalıştırın:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l <lib1> -l <lib2>
Örneğin, libbinder
ABI referanslarını güncellemek için şunu çalıştırın:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder