Tedarikçi firma modülleri, sistem bölümünde bulunan Tedarikçi Yerel Geliştirme Kiti (VNDK) paylaşılan kitaplıklarına bağlı olabileceğinden, Uygulama İkili Arabirimi (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ğunu sağlamak için 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, tedarikçi modüllerinin bağlanabileceği ve yalnızca çerçeve güncellemelerine olanak tanıyan 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 bir kitaplığın herkese açık 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. - WEAK veya GLOBAL bağlamaya sahiptir.
- Görüş mesafesi VARSAYILAN veya KORUYOR.
- 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 adlı öğeye erişilemiyor. (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. Özel üye işlevlerine yönelik simge referansları, arayan ikili dosyalarında tutulabilir. Gizli üye işlevlerinin paylaşılan kitaplıklardan değiştirilmesi veya kaldırılması, geriye dönük olarak uyumsuz ikili programlara 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ığı oluşturulduğunda kitaplığın ABI'sı, oluşturulmakta olan 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üretilmiş 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 kesinti 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 uyumsuz 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ı 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ını 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
başlık-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 dosyalarında header-abi-dumper
çalıştırırken geçişli bağımlılıklardan gelen 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 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
'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.
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ünün alanları, boyutu, erişim tanımlayıcısı, tanımlandığı başlık dosyası ve diğer özelliklerle ilgili bilgiler vardır.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 ile 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 başlıklar tarafından dışa aktarılan işlevleri temsil edin. 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 oluşturulan ara dosyaları giriş olarak alır ve ardından 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. Daha sonra araç, dışa aktarılan simgelerin bir 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ı şu şekilde 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 üstbilgilerde bulunmayan ABI bilgilerini filtreler. libfoo.so
öğesini ayrıştırır ve kitaplık tarafından dışa aktarılan.dynsym
tablosu aracılığıyla simgeler hakkında bilgi 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. Bu biçim, gelecekteki sürümlerde değiştirilebilir.
Ö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:
24
bayt olan kaydın boyutu,8
bayt olarak değişir.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
öğesinin değiştirilen türe (bar
) nasıl eriştiğini belirtir. Bu alan, Foo
öğesini parametre olarak alan, dışa aktarılan ve değiştirilen bar
değerini gösteren dışa aktarılmış bir işlevdir.bar *
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