Tedarikçi firma modülleri, sistem bölümünde bulunan Tedarikçi Firma Yerel Geliştirme Kiti (VNDK) paylaşılan kitaplıklarına bağlı olabileceğinden, uygulama ikili arayüzü (ABI) kararlılığı yalnızca çerçeve güncellemelerinin ön koşuludur. Android sürümünde, yeni oluşturulan VNDK paylaşılan kitaplıkları, tedarikçi modüllerinin yeniden derleme yapmadan ve çalışma zamanında hata vermeden bu kitaplıklarla çalışabilmesi için daha önce yayınlanan VNDK paylaşılan kitaplıklarıyla ABI uyumlu olmalıdır. Android sürümleri arasında VNDK kitaplıkları değiştirilebilir ve ABI garantisi yoktur.
Android 9, ABI uyumluluğunun sağlanmasına yardımcı olmak için aşağıdaki bölümlerde açıklandığı şekilde bir üstbilgi ABI kontrol aracı içerir.
VNDK ve ABI uyumluluğu hakkında
VNDK, tedarikçi modüllerinin bağlayabileceği ve yalnızca çerçeve güncellemelerini etkinleştiren kısıtlayıcı bir kitaplık grubudur. ABI uyumluluğu, paylaşılan bir kitaplığın yeni sürümünün, kendisine dinamik olarak bağlı bir modülle beklendiği gibi çalışma (ör. kitaplığın eski sürümünün çalıştığı gibi çalışma) özelliğini ifade eder.
Dışa aktarılan simgeler hakkında
Dışa aktarılan simge (küresel simge olarak da bilinir), aşağıdakilerin tümünü karşılayan bir simgeyi ifade eder:
- Paylaşılan kitaplığın herkese açık üstbilgileri 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. - WEAK veya GLOBAL bağlamaya sahiptir.
- Görünürlük VARSAYILAN veya KORUMALILI olmalıdır.
- Bölüm dizini UNDEFINED değil.
- Tür, FUNC veya OBJECT olmalıdır.
Paylaşılan bir kitaplığın herkese açık üstbilgileri, paylaşılan kitaplığa karşılık gelen modülün Android.bp
tanımındaki export_include_dirs
, export_header_lib_headers
, export_static_lib_headers
, export_shared_lib_headers
ve export_generated_headers
özellikleri aracılığıyla diğer kitaplıklar/ikili dosyalar tarafından kullanılabilen üstbilgiler olarak tanımlanır.
Erişilebilir türler hakkında
Erişilebilir tür, doğrudan veya dolaylı olarak dışa aktarılan bir simge aracılığıyla erişilebilen VE herkese açık üstbilgiler aracılığıyla dışa aktarılan tüm C/C++ yerleşik veya kullanıcı tanımlı türlerdir. Örneğin, libfoo.so
tablosunda .dynsym
tablosunda bulunan ve dışa aktarılan bir simge olan Foo
işlevi vardır. libfoo.so
kitaplığı şunları 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
'e bakıldığında, doğrudan/dolaylı olarak erişilebilen türler şunlardır:
Tür | Açıklama |
---|---|
bool
|
Foo değerinin 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 üzerinden dışa aktarılır.
bar_t , foo_t türüne sahip bir mfoo üyesi içerir. Bu üye, foo_exported.h üzerinden dışa aktarılır. Bu da daha fazla türün dışa aktarılmasına neden olur:
Ancak foo_exported.h üzerinden dışa aktarılmadığı için foo_private_t 'ye ULAŞILAMAZ. (foo_private_t *
opak olduğundan foo_private_t 'ta yapılan değişikliklere izin verilir.)
|
Temel sınıf belirteçleri ve şablon parametreleri aracılığıyla erişilebilen türler için de benzer bir açıklama verilebilir.
ABI'ye uygunluğu sağlama
İlgili Android.bp
dosyalarında vendor_available: true
ve vndk.enabled: true
ile işaretlenmiş kitaplıklar için ABI uyumluluğu sağlanmalıdır. Örnek:
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 kitaplıkta yapılan aşağıdaki değişiklikler ABI'yi bozan değişiklikler olarak sınıflandırılır:
Veri türü | Açıklama |
---|---|
Yapılar ve Sınıflar |
|
Birlikler |
|
Listeleme |
|
Küresel Semboller |
|
* Herkese açık satır içi işlevler gizli üye işlevlerine atıfta bulunabileceğinden hem herkese açık hem de gizli üye işlevleri değiştirilmemeli veya kaldırılmamalıdır. Gizli üye işlevlerine yönelik simge referansları, arayan ikili dosyalarında tutulabilir. Paylaşılan kitaplıklardaki gizli üye işlevlerini değiştirmek veya kaldırmak, geriye dönük olarak uyumsuz ikili dosyalara neden olabilir.
** Satır içi işlevler işlev gövdelerinde bu veri üyelerine referans verebileceğinden, herkese açık veya özel veri üyelerinin ofsetleri değiştirilmemelidir. Veri üyesi ofsetlerinin değiştirilmesi, eski sürümlerle uyumlu olmayan ikili dosyalara neden olabilir.
*** Bunlar türün bellek düzenini değiştirmese de kitaplıkların beklendiği gibi çalışmamasına neden olabilecek anlamsal farklılıklar vardır.
ABI uygunluk araçlarını kullanma
Bir VNDK kitaplığı derlenirken kitaplığın ABI'si, derlenen VNDK sürümünün karşılık gelen ABI referansıyla karşılaştırılır. Referans ABI dökümleri şu konumlarda bulunur:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/<PLATFORM_VNDK_VERSION>/<BINDER_BITNESS>/<ARCH>/source-based
Örneğin, API seviyesi 27'de x86 için libfoo
derlenirken, libfoo
'ün türetilen ABI'si şu referansla karşılaştırılır:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/27/64/x86/source-based/libfoo.so.lsdump
ABI bozulma hatası
ABI bozulmalarında, derleme günlüğünde uyarı türü ve abi-diff raporunun yolu içeren uyarılar gösterilir. Örneğin, libbinder
'nin ABI'sinde uyumlu olmayan bir değişiklik varsa derleme sistemi aşağıdakine benzer bir mesaj içeren bir hata verir:
***************************************************** 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 kitaplığı ABI kontrolleri oluşturma
Bir VNDK kitaplığı oluşturulduğunda:
header-abi-dumper
, VNDK kitaplığını oluşturmak için derlenen kaynak dosyalarını (kitaplığın kendi kaynak dosyalarının yanı sıra statik geçişli bağımlılıklarla devralınan kaynak dosyaları) işleyip her kaynağa karşılık gelen.sdump
dosyaları oluşturur.
Şekil 1. .sdump
dosyalarını oluşturmaheader-abi-linker
daha sonra, paylaşılan kitaplığa karşılık gelen tüm ABI bilgilerini günlüğe kaydeden bir.lsdump
dosyası oluşturmak için.sdump
dosyalarını (kendisine sağlanan bir sürüm komut dosyasını veya paylaşılan kitaplığa karşılık gelen.so
dosyasını kullanarak) işler.
Şekil 2. .lsdump
dosyası oluşturmaheader-abi-diff
, iki kitaplığın ABI'lerindeki farkları özetleyen bir fark raporu oluşturmak için.lsdump
dosyasını bir referans.lsdump
dosyasıyla karşılaştırır.
Şekil 3. Fark raporu oluşturma
header-abi-dumper
header-abi-dumper
aracı, bir C/C++ kaynak dosyasını ayrıştırır ve bu kaynak dosyadan elde edilen ABI'yi bir ara dosyaya aktarır. Derleme sistemi, tüm derlenmiş kaynak dosyalarda header-abi-dumper
'yi çalıştırırken, aktarmalı bağımlılıklardaki kaynak dosyaları içeren bir kitaplık da oluşturur.
Girişler |
|
---|---|
Çıkış | Kaynak dosyanın ABI'sini açıklayan bir dosya (örneğin, foo.sdump , foo.cpp 'un ABI'sini temsil eder).
|
Şu anda .sdump
dosyaları JSON biçimindedir. Bu biçimin gelecekteki sürümlerde de kullanılacağı garanti edilmez. Bu nedenle, .sdump
dosya biçimlendirmesi bir derleme sistemi uygulama ayrıntısı olarak kabul edilmelidir.
Örneğin, libfoo.so
aşağıdaki kaynak dosyaya 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 header-abi-dumper
.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
'e --
'yi izleyen derleyici işaretleriyle foo.cpp
'ü ayrıştırmasını ve exported
dizinindeki herkese açık üstbilgilerle dışa aktarılan ABI bilgilerini yayınlamasını söyler. Aşağıdaki foo.sdump
, header-abi-dumper
tarafından oluşturulmuştur:
{ "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
, kaynak dosya tarafından dışa aktarılan ABI bilgilerini ve herkese açık üstbilgileri (ör.foo.cpp
record_types
. Herkese açık başlıklarda tanımlanan yapıları, birimleri veya sınıfları referans olarak kullanın. Her kayıt türünde, alanları, boyutu, erişim belirteci, tanımlandığı başlık dosyası ve diğer özelliklerle ilgili bilgiler bulunur.pointer_types
. Herkese açık üstbilgilerdeki dışa aktarılan kayıtlar/işlevler tarafından doğrudan/dolaylı olarak başvurulan işaretçi türlerini ve işaretçinin gösterdiği türü (type_info
içindekireferenced_type
alanından) belirtir. Nitelikli türler, yerleşik C/C++ türleri, dizi türleri ve sol değer ve sağ değer referans türleri için benzer bilgiler.sdump
dosyasına kaydedilir. Bu tür bilgiler, yinelenen karşılaştırma yapılmasına olanak tanır.functions
. Herkese açık üstbilgilerle dışa aktarılan işlevleri temsil eder. Ayrıca işlevin bozuk adı, döndürülen tür, parametre türleri, erişim belirteci ve diğer özellikler hakkında bilgi de bulunur.
header-abi-linker
header-abi-linker
aracı, header-abi-dumper
tarafından üretilen ara dosyaları giriş olarak alır ve bu dosyaları bağlar:
Girişler |
|
---|---|
Çıkış | Paylaşılan bir kitaplığın ABI'sini açıklayan bir dosya (örneğin, libfoo.so.lsdump , libfoo 'un ABI'sini temsil eder).
|
Araç, kendisine verilen tüm ara dosyalardaki tür grafiklerini birleştirir. Bu sırada, ç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 alır. Ardından araç, dışa aktarılan sembollerin listesini oluşturmak için bir sürüm komut dosyasını veya paylaşılan kitaplığın .dynsym
tablosunu (.so
dosyası) ayrıştırır.
Örneğin, libfoo
, foo.cpp
ve bar.cpp
'den oluşur. header-abi-linker
, libfoo
'un bağlı ABI'sinin tamamını aşağıdaki gibi oluşturmak için ç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
komut çıkışı örneği:
{ "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. Bu sırada,exported
dizinindeki başlıklarda bulunmayan ABI bilgilerini filtreler. libfoo.so
öğesini ayrıştırır ve kitaplığın.dynsym
tablosu aracılığıyla dışa aktardığı simgelerle ilgili bilgileri toplar._Z3FooiP3bar
ve_Z6FooBadiP3foo
ekler.
libfoo.so.lsdump
, libfoo.so
için oluşturulan son ABI dökümüdür.
header-abi-diff
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 fark raporu oluşturur.
Girişler |
|
---|---|
Çıkış | 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 biçiminde olmalıdır. Biçim, gelecekteki sürümlerde değişebilir.
Örneğin, libfoo
'nin iki sürümü vardır: libfoo_old.so
ve libfoo_new.so
. libfoo_new.so
'te, bar_t
'te mfoo
türünü foo_t
yerine foo_t *
olarak değiştirirseniz 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
komut çıkışı örneği:
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
'daki tüm ABI'yi bozan değişikliklerin raporunu içerir. record_type_diffs
mesajı, bir kaydın değiştiğini gösterir ve aşağıdakiler gibi uyumsuz değişiklikleri listeler:
- Kaydın boyutu
24
bayttan8
bayta değişiyor. mfoo
alanının türüfoo
yerinefoo *
olarak değişiyor (tüm typedef'ler kaldırılıyor).
type_stack
alanı, header-abi-diff
'un değişen türe (bar
) nasıl ulaştığını gösterir. Bu alan, Foo
'ın parametre olarak bar *
alan ve dışa aktarılıp değiştirilen bar
'yi işaret eden dışa aktarılmış bir işlev olduğu şeklinde yorumlanabilir.
ABI ve API'yi zorunlu kılma
VNDK paylaşılan kitaplıklarının ABI ve API'sini zorunlu kılmak 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şturulduktan sonra, kaynak kodda yapılan ve VNDK kitaplığında uyumsuz bir ABI/API değişikliğine neden olan tüm değişiklikler artık derleme hatasına neden oluyor.
Belirli kitaplıklar için ABI referanslarını güncellemek üzere 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 şunları çalıştırın:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder