Uygulama İkili Arabirimi (ABI) kararlılığı, yalnızca çerçeve güncellemelerinin ön koşuludur. Bunun nedeni, satıcı modüllerinin sistem bölümünde bulunan Satıcı Yerel Geliştirme Seti (VNDK) paylaşılan kitaplıklarına bağımlı olabilmesidir. Bir Android sürümünde, yeni oluşturulan VNDK paylaşılan kitaplıkları, daha önce yayınlanan VNDK paylaşılan kitaplıklarıyla ABI uyumlu olmalıdır. Böylece satıcı modülleri, bu kitaplıklarla yeniden derleme ve çalışma zamanı hatası olmadan çalışabilir. 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 üstbilgi 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 yeni sürümünün, kendisine dinamik olarak bağlanmış bir modülle beklendiği gibi çalışabilme (ör. kitaplığın eski bir sürümü gibi çalışabilme) özelliğini ifade eder.
Dışa aktarılan semboller hakkında
Dışa aktarılan sembol (küresel sembol olarak da bilinir), aşağıdakilerin tümünü karşılayan bir sembolü ifade eder:
- Paylaşılan bir kitaplığın public üstbilgileri tarafından dışa aktarılır.
- Paylaşılan kitaplığa karşılık gelen
.sodosyasının.dynsymtablosunda görünür. - WEAK (ZAYIF) veya GLOBAL (GENEL) bağlama içeriyorsa
- Görünürlük DEFAULT veya PROTECTED olmalıdır.
- Bölüm dizini UNDEFINED değil.
- Tür, FUNC veya OBJECT olmalıdır.
Paylaşılan kitaplığın public headers'ı, 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 özellikleri aracılığıyla diğer kitaplıklar/ikili dosyalar tarafından kullanılabilen üstbilgiler 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 ulaşılabilen VE herkese açık başlıklar aracılığıyla dışa aktarılan herhangi bir C/C++ yerleşik ya da kullanıcı tanımlı türdür. Örneğin, libfoo.so, .dynsym tablosunda bulunan dışa aktarılmış bir sembol olan Foo işlevine sahiptir. libfoo.so kitaplığında şunlar bulunur:
| 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'ya baktığımızda doğrudan/dolaylı olarak erişilebilen türler şunlardır:
| Tür | Açıklama |
|---|---|
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 üzerinden dışa aktarılır.
bar_t, foo_exported.h üzerinden dışa aktarılan foo_t türünde bir mfoo üyesi içerir.
Bu durum, daha fazla türün dışa aktarılmasına neden olur:
Ancak foo_private_t, foo_exported.h üzerinden dışa aktarılmadığı için ulaşılamıyor. (foo_private_t *
opak olduğundan foo_private_t üzerinde yapılan değişikliklere izin verilir.)
|
Temel sınıf belirleyicileri ve şablon parametreleri aracılığıyla erişilebilen türler için de benzer bir açıklama verilebilir.
ABI uygunluğunu sağlama
İlgili Android.bp dosyalarında vendor_available: true ve vndk.enabled: true ile 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, 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 |
|
| Sendikalar |
|
| Numaralandırmalar |
|
| Global Symbols |
|
* Herkese açık satır içi işlevler, özel üye işlevlerine başvurabileceğinden hem herkese açık hem de özel üye işlevleri değiştirilmemeli veya kaldırılmamalıdır. Özel üye işlevlerine yapılan sembol referansları, arayan ikili dosyalarında tutulabilir. Paylaşılan kitaplıklardaki özel üye işlevlerinin değiştirilmesi veya kaldırılması, geriye dönük olarak uyumsuz ikili dosyalarla sonuçlanabilir.
** Satır içi işlevler, işlev gövdelerinde bu veri üyelerine başvurabileceğinden, herkese açık veya özel veri üyelerine yönelik ofsetler değiştirilmemelidir. Veri üyesi ofsetlerinin değiştirilmesi, eski sürümlerle uyumsuz ikili dosyalarla sonuçlanabilir.
*** 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 uyumluluk araçlarını kullanma
Bir VNDK kitaplığı oluşturulduğunda kitaplığın ABI'si, oluşturulan 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 konumda 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 oluşturulurken libfoo'nın çıkarılan ABI'si ş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 bozulması hatası
ABI bozulmalarında, derleme günlüğünde uyarı türü ve abi-diff raporunun yoluyla ilgili uyarılar gösterilir. Örneğin, libbinder'nın ABI'sinde uyumsuz bir değişiklik varsa derleme sistemi, aşağıdakine benzer bir mesajla 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ı (kitaplığın kendi kaynak dosyalarının yanı sıra statik geçişli bağımlılıklar aracılığıyla devralınan kaynak dosyalar) işleyerek her kaynağa karşılık gelen.sdumpdosyaları oluşturur.
Şekil 1. .sdumpdosyalarını oluşturmaheader-abi-linker, daha sonra.sdumpdosyalarını (kendisine sağlanan bir sürüm komut dosyasını veya paylaşılan kitaplığa karşılık gelen.sodosyasını kullanarak) işleyerek paylaşılan kitaplığa karşılık gelen tüm ABI bilgilerini kaydeden bir.lsdumpdosyası oluşturur.
Şekil 2. .lsdumpdosyasını oluşturmaheader-abi-diff, iki kitaplığın ABI'lerindeki farklılıkları özetleyen bir karşılaştırma raporu oluşturmak için.lsdumpdosyasını bir referans.lsdumpdosyasıyla karşılaştırır.
Şekil 3. Karşılaştırma raporu oluşturma
header-abi-dumper
header-abi-dumper aracı, bir C/C++ kaynak dosyasını ayrıştırır ve bu kaynak dosyasından çıkarılan ABI'yi bir ara dosyaya boşaltır. Derleme sistemi, derlenen tüm kaynak dosyalar üzerinde header-abi-dumper çalıştırırken geçişli 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 represents foo.cpp'nin ABI'sini gösterir).
|
Şu anda .sdump dosyaları, gelecekteki sürümlerde kararlılığının garanti edilmediği JSON biçimindedir. Bu nedenle, .sdump
dosya biçimlendirmesi, derleme sistemi uygulama ayrıntısı olarak kabul edilmelidir.
Örneğin, libfoo.so aşağıdaki kaynak dosyaya sahip:
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; }
Kaynak dosya tarafından sunulan ABI'yi temsil eden bir ara .sdump dosyası oluşturmak için header-abi-dumper'yı kullanabilirsiniz. Bunun için:
$ header-abi-dumper foo.cpp -I exported -o foo.sdump -- -I exported -x c++
Bu komut, header-abi-dumper'ya foo.cpp dosyasını -- sonrasındaki derleyici işaretleriyle ayrıştırmasını ve exported dizinindeki herkese açık üstbilgiler tarafından dışa aktarılan ABI bilgilerini yayınlamasını söyler. Aşağıdaki foo.sdump, header-abi-dumper tarafından oluşturuldu:
{ "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 foo.cpp 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, birleşimler veya sınıflara bakın. Her kayıt türü; alanları, boyutu, erişim belirleyicisi, tanımlandığı üst dosyası ve diğer özellikleriyle ilgili bilgiler içerir.pointer_types. Genel başlıklardaki dışa aktarılan kayıtlar/işlevler tarafından doğrudan/dolaylı olarak referans verilen işaretçi türlerinin yanı sıra işaretçinin işaret ettiği türü (type_infoiçindekireferenced_typealanı aracılığıyla) ifade eder. Nitelikli türler, yerleşik C/C++ türleri, dizi türleri, lvalue ve rvalue referans türleri için benzer bilgiler.sdumpdosyasına kaydedilir. Bu tür bilgiler, yinelemeli farklılaştırmaya olanak tanır.functions. Ortak başlıklar tarafından dışa aktarılan işlevleri temsil edin. Ayrıca, işlevin bozulmuş adı, dönüş türü, parametre türleri, erişim belirleyici ve diğer özellikler hakkında bilgi içerirler.
header-abi-linker
header-abi-linker aracı, header-abi-dumper tarafından oluşturulan 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'nin ABI'sini temsil eder). |
Araç, kendisine verilen tüm ara dosyalardaki tür grafiklerini birleştirir ve çeviri birimleri arasındaki tek tanımlı (tam nitelikli adı aynı olan farklı çeviri birimlerindeki kullanıcı tanımlı türler, anlamsal olarak farklı olabilir) farklılıkları dikkate alır. Araç daha sonra 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 öğelerinden oluşur. header-abi-linker, libfoo'nin bağlantılı ABI dökümünü oluşturmak için aşağıdaki ş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 içindeki örnek komut çıkışı:
{ "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
.sdumpdosyalarını (foo.sdumpvebar.sdump) bağlar ve dizinde bulunan üstbilgilerde bulunmayan ABI bilgilerini filtreler:exported. libfoo.soöğesini ayrıştırır ve kitaplığın.dynsymtablosu aracılığıyla dışa aktarılan sembollerle ilgili bilgileri toplar._Z3FooiP3barve_Z6FooBadiP3fooeklenir.
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 diff 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 diff raporu. |
ABI diff dosyası, protobuf metin biçimindedir. Biçim, gelecekteki sürümlerde değişebilir.
Örneğin, libfoo için iki sürümünüz var: libfoo_old.so ve libfoo_new.so. libfoo_new.so içinde, bar_t bölümünde mfoo türünü foo_t olarak değiştiriyorsunuz.foo_t * 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 uygulamasını ç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 içindeki örnek komut çıkışı:
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 içindeki tüm ABI uyumluluğunu bozan değişikliklerin raporunu içerir. record_type_diffs mesajı, bir kaydın değiştiğini belirtir ve aşağıdakiler gibi uyumsuz değişiklikleri listeler:
- Kaydın boyutu
24bayttan8bayta değişiyor. mfooalanının türüfooolarak değiştirildi.foo *(tüm typedef'ler kaldırıldı).
type_stack alanı, header-abi-diff
değişen türe (bar) nasıl ulaşıldığını gösterir. Bu alan, Foo, bar * parametresini alan, bar'ı işaret eden, dışa aktarılmış ve değiştirilmiş bir işlev olarak yorumlanabilir.
ABI/API'yi zorunlu kılma
VNDK paylaşılan kitaplıklarının ABI/API'sini zorunlu kılmak için ABI referanslarının ${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/'ya kaydedilmesi 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ıyla sonuçlanıyor.
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 şunu çalıştırın:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder