Stabilitas Antarmuka Biner Aplikasi (ABI) adalah prasyarat dari update khusus framework karena modul vendor mungkin bergantung pada Native Vendor Library bersama Development Kit (VNDK) yang berada di partisi sistem. Dalam rilis Android, library bersama VNDK yang baru dibuat harus Kompatibel dengan ABI dengan library bersama VNDK yang dirilis sebelumnya sehingga modul vendor dapat bekerja dengan library tersebut tanpa kompilasi ulang dan tanpa error runtime. Di antara rilis Android, library VNDK dapat diubah dan tidak ada ABI yang sama.
Untuk membantu memastikan kompatibilitas ABI, Android 9 menyertakan pemeriksa ABI header, seperti yang dijelaskan di bagian berikut.
Tentang kepatuhan VNDK dan ABI
VNDK adalah kumpulan library terbatas yang dapat ditautkan ke dan oleh modul vendor yang mengaktifkan update khusus framework. Kepatuhan ABI mengacu pada kemampuan pustaka bersama yang lebih baru untuk bekerja seperti yang diharapkan dengan yang ditautkan secara dinamis ke modul tersebut (mis. berfungsi sebagai versi lama {i>library<i}).
Tentang simbol yang diekspor
Simbol yang diekspor (juga dikenal sebagai simbol global) mengacu pada simbol yang memenuhi semua hal berikut:
- Diekspor oleh header publik library bersama.
- Muncul di tabel
.dynsym
dari file.so
sesuai dengan pustaka bersama. - Memiliki pengikatan WEAK atau GLOBAL.
- Visibilitas bersifat DEFAULT atau PROTECTED.
- Indeks bagian tidak DITENTUKAN.
- Jenisnya adalah FUNC atau OBJECT.
Header publik library bersama ditentukan sebagai header
yang tersedia untuk library/biner lain melalui
export_include_dirs
, export_header_lib_headers
,
export_static_lib_headers
,
export_shared_lib_headers
, dan
Atribut export_generated_headers
di Android.bp
definisi modul yang sesuai
dengan pustaka bersama.
Tentang jenis yang dapat dijangkau
Jenis yang dapat dijangkau adalah jenis C/C++ bawaan atau buatan pengguna yang
dapat dijangkau secara langsung atau tidak langsung melalui simbol yang diekspor DAN diekspor
melalui {i>header <i}publik. Misalnya, libfoo.so
memiliki fungsi
Foo
, yang merupakan simbol yang diekspor dan ditemukan di
Tabel .dynsym
. Library libfoo.so
menyertakan
berikut ini:
{i>foo_exported.h<i} | {i>foo.private.h<i} |
---|---|
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" ], } |
Tabel .dynsym | |||||||
---|---|---|---|---|---|---|---|
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
|
Dengan melihat Foo
, jenis akses langsung/tidak langsung yang dapat dijangkau mencakup:
Jenis | Deskripsi |
---|---|
bool
|
Jenis nilai yang ditampilkan Foo .
|
int
|
Jenis parameter Foo pertama.
|
bar_t *
|
Jenis parameter Foo kedua. Melalui bar_t * ,
bar_t diekspor melalui foo_exported.h .
bar_t berisi anggota mfoo , dengan jenis
foo_t , yang diekspor melalui foo_exported.h ,
yang menyebabkan lebih banyak jenis ekspor:
Namun, foo_private_t TIDAK dapat dijangkau karena tidak dapat dijangkau
diekspor melalui foo_exported.h . (foo_private_t * )
buram, oleh karena itu perubahan yang dilakukan pada foo_private_t diizinkan.)
|
Penjelasan serupa dapat diberikan untuk jenis yang dapat dijangkau melalui class dasar penentu serta parameter template.
Memastikan kepatuhan ABI
Kepatuhan ABI harus dipastikan untuk library yang ditandai
vendor_available: true
dan vndk.enabled: true
di
file Android.bp
yang sesuai. Contoh:
cc_library { name: "libvndk_example", vendor_available: true, vndk: { enabled: true, } }
Untuk jenis data yang dapat dijangkau secara langsung atau tidak langsung oleh fungsi yang diekspor, metode perubahan berikut pada library diklasifikasikan sebagai gangguan ABI:
Jenis data | Deskripsi |
---|---|
Struktur dan Class |
|
Serikat |
|
Enumerasi |
|
Simbol Global |
|
* Fungsi anggota publik dan pribadi harus tidak diubah atau dihapus karena fungsi inline publik dapat merujuk fungsi anggota pribadi. Referensi simbol ke fungsi anggota pribadi dapat disimpan dalam biner pemanggil. Mengubah atau menghapus fungsi anggota pribadi dari pustaka bersama dapat menghasilkan biner yang tidak kompatibel dengan versi sebelumnya.
** Offset untuk anggota data publik atau pribadi tidak boleh berubah karena fungsi {i>inline<i} dapat merujuk ke anggota data ini di isi fungsi. Mengubah offset anggota data dapat yang tidak kompatibel dengan versi sebelumnya.
*** Meskipun ini tidak mengubah tata letak memori jenis ini, ada perbedaan semantik yang dapat menyebabkan {i>library<i} tidak berfungsi seperti yang diharapkan.
Menggunakan alat kepatuhan ABI
Saat library VNDK dibangun, ABI library dibandingkan dengan yang sesuai dengan referensi ABI untuk versi VNDK yang sedang dibangun. Referensi Dump ABI terletak di:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/<PLATFORM_VNDK_VERSION>/<BINDER_BITNESS>/<ARCH>/source-based
Misalnya, pada membangun libfoo
untuk x86 pada level API 27,
ABI yang disimpulkan libfoo
dibandingkan dengan referensinya di:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/27/64/x86/source-based/libfoo.so.lsdump
Error kerusakan ABI
Jika kerusakan ABI, log build akan menampilkan peringatan dengan jenis peringatan dan
ke laporan perbedaan. Misalnya, jika ABI libbinder
memiliki
perubahan yang tidak kompatibel, sistem build akan menampilkan error dengan pesan
mirip dengan contoh berikut ini:
***************************************************** 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 ----
Membuat pemeriksaan ABI library VNDK
Saat library VNDK dibangun:
header-abi-dumper
memproses file sumber yang dikompilasi menjadi membangun pustaka VNDK (file sumber pustaka itu sendiri serta file sumber diwarisi melalui dependensi transitif statis), untuk menghasilkan.sdump
yang sesuai dengan setiap sumber.
Gambar 1. Membuat .sdump
fileheader-abi-linker
kemudian memproses.sdump
file (menggunakan skrip versi yang disediakan atau.so
file yang sesuai dengan library bersama) untuk menghasilkan.lsdump
yang mencatat semua informasi ABI yang terkait dengan pustaka bersama.
Gambar 2. Membuat .lsdump
fileheader-abi-diff
membandingkan.lsdump
dengan file.lsdump
referensi untuk menghasilkan laporan perbedaan yang menguraikan perbedaan ABI dari kedua library tersebut.
Gambar 3. Membuat laporan perbedaan
header-abi-dumper
Alat header-abi-dumper
akan mengurai file sumber C/C++ dan dump
ABI yang disimpulkan dari file sumber
tersebut menjadi file perantara. Build
sistem menjalankan header-abi-dumper
pada semua
file sumber yang dikompilasi saat
juga membangun library yang menyertakan file sumber dari
dependensi.
Masukan |
|
---|---|
Output | File yang menjelaskan ABI file sumber (misalnya,
foo.sdump merepresentasikan ABI foo.cpp ).
|
Saat ini .sdump
file dalam format JSON, yang tidak
dipastikan akan stabil di seluruh rilis mendatang. Dengan demikian, .sdump
pemformatan file harus dianggap sebagai detail implementasi sistem build.
Misalnya, libfoo.so
memiliki file sumber berikut
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; }
Anda dapat menggunakan header-abi-dumper
untuk membuat perantara
File .sdump
yang mewakili ABI yang ditampilkan oleh file sumber
menggunakan:
$ header-abi-dumper foo.cpp -I exported -o foo.sdump -- -I exported -x c++
Perintah ini memberi tahu header-abi-dumper
untuk mengurai
foo.cpp
dengan tanda compiler setelah --
, dan
menghasilkan informasi ABI yang diekspor oleh header publik di
Direktori exported
. Berikut adalah
foo.sdump
dibuat oleh
header-abi-dumper
:
{ "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
berisi informasi ABI yang diekspor oleh file sumber
foo.cpp
dan header publik, misalnya,
record_types
. Merujuk pada struct, union, atau class yang ditentukan di {i>header <i}publik. Setiap jenis kumpulan data memiliki informasi tentang {i>field<i}-nya, {i>size<i}, penentu akses, {i>file<i} {i>header<i} yang didefinisikan, dan .pointer_types
. Merujuk jenis pointer secara langsung/tidak langsung yang direferensikan oleh catatan/fungsi yang diekspor di {i>header<i} publik, beserta dengan jenis yang ditunjuk pointer (melaluireferenced_type
ditype_info
). Informasi serupa dicatat ke dalam log File.sdump
untuk jenis yang memenuhi syarat, jenis C/C++ bawaan, array jenis, serta jenis referensi nilai dan nilai. Informasi tersebut memungkinkan diffing rekursif.functions
. Merepresentasikan fungsi yang diekspor oleh header publik. Mereka juga memiliki informasi tentang nama {i> mangled<i}, jenis nilai yang ditampilkan, jenis parameter, penentu akses, dan atribut lainnya.
penaut-header-abi
Alat header-abi-linker
mengambil file perantara yang dihasilkan
oleh header-abi-dumper
sebagai input, lalu menautkan file tersebut:
Masukan |
|
---|---|
Output | File yang menjelaskan ABI library bersama (misalnya,
libfoo.so.lsdump merepresentasikan ABI libfoo ).
|
Alat ini menggabungkan grafik tipe di semua
file perantara yang diberikan,
mempertimbangkan satu definisi (tipe yang ditentukan pengguna dalam
unit terjemahan dengan nama yang sepenuhnya memenuhi syarat, mungkin secara semantik
berbeda) di seluruh unit terjemahan. Alat ini kemudian
menguraikan
skrip versi atau tabel .dynsym
dari library bersama
(file .so
) untuk membuat daftar simbol yang diekspor.
Misalnya, libfoo
terdiri dari foo.cpp
dan
bar.cpp
. header-abi-linker
dapat dipanggil ke
buat dump ABI tertaut lengkap dari libfoo
sebagai berikut:
header-abi-linker -I exported foo.sdump bar.sdump \ -o libfoo.so.lsdump \ -so libfoo.so \ -arch arm64 -api current
Contoh output perintah di libfoo.so.lsdump
:
{ "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" : [] }
Alat header-abi-linker
:
- Menautkan file
.sdump
yang disediakan untuknya (foo.sdump
danbar.sdump
), memfilter informasi ABI yang tidak ada di header yang berada di direktori:exported
. - Mengurai
libfoo.so
, dan mengumpulkan informasi tentang simbol diekspor oleh library melalui tabel.dynsym
-nya. - Menambahkan
_Z3FooiP3bar
dan_Z6FooBadiP3foo
.
libfoo.so.lsdump
adalah dump ABI akhir yang dihasilkan dari
libfoo.so
.
header-abi-diff
Alat header-abi-diff
membandingkan dua file .lsdump
yang merepresentasikan ABI dua library dan menghasilkan laporan perbedaan yang menyatakan
perbedaan antara kedua ABI tersebut.
Masukan |
|
---|---|
Output | Laporan perbedaan yang menyatakan perbedaan ABI yang ditawarkan oleh keduanya {i>shared library<i}. |
File diff ABI ada di format teks protobuf. Format dapat berubah dalam rilis mendatang.
Misalnya, Anda memiliki dua versi
libfoo
: libfoo_old.so
dan
libfoo_new.so
. Di libfoo_new.so
, di
bar_t
, Anda mengubah jenis mfoo
dari
foo_t
ke foo_t *
. Karena bar_t
adalah
jenis dapat dijangkau, hal ini harus ditandai sebagai
perubahan yang dapat menyebabkan gangguan ABI oleh
header-abi-diff
.
Untuk menjalankan header-abi-diff
:
header-abi-diff -old libfoo_old.so.lsdump \ -new libfoo_new.so.lsdump \ -arch arm64 \ -o libfoo.so.abidiff \ -lib libfoo
Contoh output perintah di libfoo.so.abidiff
:
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
berisi laporan semua pelanggaran ABI
perubahan di libfoo
. Pesan record_type_diffs
menunjukkan bahwa catatan telah berubah dan mencantumkan perubahan yang tidak kompatibel, yang
termasuk:
- Ukuran kumpulan data berubah dari
24
byte menjadi8
byte. - Jenis kolom
mfoo
berubah darifoo
menjadifoo *
(semua typedef dihilangkan).
Kolom type_stack
menunjukkan bagaimana header-abi-diff
mencapai jenis yang berubah (bar
). Bidang ini mungkin
ditafsirkan sebagai Foo
adalah fungsi yang diekspor
bar *
sebagai parameter, yang menunjuk ke bar
, yang merupakan
diekspor dan diubah.
Menerapkan ABI dan API
Untuk menerapkan ABI dan API library bersama VNDK, referensi ABI harus
di-check in ke ${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/vndk/
.
Untuk membuat referensi ini, jalankan perintah berikut:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py
Setelah membuat referensi, setiap perubahan dilakukan pada kode sumber yang menghasilkan dalam perubahan ABI/API yang tidak kompatibel dalam library VNDK sekarang akan menghasilkan error build.
Untuk mengupdate referensi ABI untuk library tertentu, jalankan perintah berikut:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l <lib1> -l <lib2>
Misalnya, untuk memperbarui referensi ABI libbinder
, jalankan:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder