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
.so
dosyasının.dynsym
tablosunda 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.sdump
dosyaları oluşturur.
1. şekil. .sdump
dosyalarını oluşturmaheader-abi-linker
daha sonra, paylaşılan kitaplığa karşılık gelen tüm ABI bilgilerini 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 farklılıkları özetleyen bir fark raporu oluşturmak için.lsdump
dosyasını bir referans.lsdump
dosyası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_info
içindekireferenced_type
alanı 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.sdump
dosyası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
.sdump
dosyalarını (foo.sdump
vebar.sdump
) bağlar ve dizinde bulunan üstbilgilerde bulunmayan ABI bilgilerini filtreler:exported
. libfoo.so
öğesini ayrıştırır ve kitaplığın.dynsym
tablosu aracılığıyla dışa aktardığı sembollerle ilgili bilgileri toplar._Z3FooiP3bar
ve_Z6FooBadiP3foo
ekler.
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
24
bayttan8
bayta değişiyor. mfoo
alanının türüfoo
olarak 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