Tedarikçi modülleri, sistem bölümünde bulunan Tedarikçi Yerel Geliştirme Kiti (VNDK) paylaşılan kitaplıklarına bağlı olabileceğinden, Yalnızca Çerçeve Güncellemeleri için Uygulama İkili Arayüzü (ABI) kararlılığı ön koşuldur. Yeni oluşturulan VNDK paylaşılan kitaplıkları, bir Android sürümünde daha önce yayınlanan VNDK paylaşılan kitaplıklarıyla ABI uyumlu olmalıdır. Böylece tedarikçi 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 verilmez.
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ışabilmesini (ör. kitaplığın eski bir sürümü gibi çalışabilmesini) 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 bir 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ığı ş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'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 yapılabilir.
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 düzeyi 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ı ve 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.
1. şekil. .sdumpdosyalarını oluşturmaheader-abi-linkerdaha sonra, paylaşılan kitaplığa karşılık gelen tüm ABI bilgilerini kaydeden bir.lsdumpdosyası oluşturmak için.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şler.
Şekil 2. .lsdumpdosyasını oluşturmaheader-abi-diff, iki kitaplığın ABI'lerindeki farklılıkları özetleyen bir fark raporu oluşturmak için.lsdumpdosyasını bir referans.lsdumpdosyasıyla karşılaştırır.
3.şekil Fark 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 , foo.cpp'nin ABI'sini temsil eder).
|
Ş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 için aşağıdaki kaynak dosya
foo.cpp'sı vardır:
#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 aracını kullanabilirsiniz.
$ header-abi-dumper foo.cpp -I exported -o foo.sdump -- -I exported -x c++
Bu komut, header-abi-dumper'ya --'dan sonra gelen derleyici işaretleriyle foo.cpp'ı 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ığı başlık 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ürlerine ve işaretçinin işaret ettiği türe (type_infoiçindekireferenced_typealanı aracılığıyla) bakın. 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. Genel 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 aktardığı sembollerle ilgili bilgileri toplar._Z3FooiP3barve_Z6FooBadiP3fooekler.
libfoo.so.lsdump, libfoo.so'nin 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ık tarafından sunulan ABI'lerdeki farklılıkları belirten bir diff raporu. |
ABI farklılık 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 içinde, 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 uyumsuz değişiklikleri listeler. Bu değişiklikler şunlardır:
- 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 öğesinin değişen türe (bar) nasıl ulaştığını gösterir. Bu alan, Foo öğesinin, bar * öğesini parametre olarak alan, dışa aktarılmış ve değiştirilmiş bar öğesini işaret eden bir işlev olarak 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ı ${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/'ya kaydedilmelidir.
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 her değişiklik 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