Stabilitas Antarmuka Biner Aplikasi (ABI) merupakan prasyarat pembaruan kerangka kerja saja karena modul vendor mungkin bergantung pada pustaka bersama Vendor Native Development Kit (VNDK) yang berada di partisi sistem. Dalam rilis Android, library bersama VNDK yang baru dibuat harus kompatibel dengan ABI untuk library bersama VNDK yang dirilis sebelumnya, sehingga modul vendor dapat bekerja dengan library tersebut tanpa kompilasi ulang dan tanpa kesalahan waktu proses. Di antara rilis Android, pustaka VNDK dapat diubah dan tidak ada jaminan ABI.
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 pustaka terbatas yang dapat ditautkan oleh modul vendor dan yang memungkinkan pembaruan kerangka saja. Kepatuhan ABI mengacu pada kemampuan versi yang lebih baru dari pustaka bersama untuk bekerja seperti yang diharapkan dengan modul yang ditautkan secara dinamis (yaitu, berfungsi seperti versi pustaka yang lebih lama).
Tentang simbol yang diekspor
Simbol yang diekspor (juga dikenal sebagai simbol global ) mengacu pada simbol yang memenuhi semua hal berikut:
- Diekspor oleh header publik dari perpustakaan bersama.
- Muncul di tabel
.dynsym
dari file.so
sesuai dengan pustaka bersama. - Memiliki ikatan LEMAH atau GLOBAL.
- Visibilitas DEFAULT atau DILINDUNGI.
- Indeks bagian tidak DITETAPKAN.
- Jenisnya adalah FUNC atau OBJECT.
Header publik dari pustaka bersama didefinisikan sebagai tajuk yang tersedia untuk pustaka / biner lain melalui export_include_dirs
, export_header_lib_headers
, export_static_lib_headers
, export_shared_lib_headers
, dan export_generated_headers
dalam definisi modul Android.bp
sesuai dengan pustaka bersama.
Tentang tipe yang bisa dijangkau
Jenis yang dapat dijangkau adalah jenis C / C ++ bawaan atau yang ditentukan pengguna yang dapat dijangkau secara langsung atau tidak langsung melalui simbol yang diekspor DAN diekspor melalui header publik. Misalnya, libfoo.so
memiliki fungsi Foo
, yang merupakan simbol yang diekspor yang ditemukan dalam tabel .dynsym
. Pustaka libfoo.so
mencakup berikut ini:
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 : [ "include" ], } |
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 |
Melihat Foo
, jenis yang dapat dijangkau langsung / tidak langsung meliputi:
Tipe | Deskripsi |
---|---|
bool | Jenis 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 , berjenis foo_t , yang diekspor melalui foo_exported.h , yang menghasilkan lebih banyak jenis yang diekspor:
Namun, foo_private_t TIDAK dapat dijangkau karena tidak diekspor melalui foo_exported.h . ( foot_private_t * tidak tembus cahaya, oleh karena itu perubahan yang dibuat pada foo_private_t diperbolehkan.) |
Penjelasan serupa dapat diberikan untuk tipe yang dapat dijangkau melalui penentu kelas dasar dan parameter template juga.
Memastikan kepatuhan ABI
Kepatuhan ABI harus dipastikan untuk pustaka yang ditandai vendor_available: true
dan vndk.enabled: true
di file Android.bp
sesuai. Sebagai contoh:
cc_library { name: "libvndk_example", vendor_available: true, vndk: { enabled: true, } }
Untuk tipe data yang dapat dijangkau secara langsung atau tidak langsung oleh fungsi yang diekspor, perubahan berikut pada pustaka diklasifikasikan sebagai pemecah ABI:
Tipe data | Deskripsi |
---|---|
Struktur dan Kelas |
|
Serikat pekerja |
|
Pencacahan |
|
Simbol Global |
|
* Fungsi anggota publik dan pribadi tidak boleh diubah atau dihapus karena fungsi sebaris publik dapat merujuk ke fungsi anggota pribadi. Referensi simbol ke fungsi anggota pribadi dapat disimpan dalam biner pemanggil. Mengubah atau menghapus fungsi anggota pribadi dari pustaka bersama dapat mengakibatkan biner yang tidak kompatibel dengan versi sebelumnya.
** Offset untuk anggota data publik atau pribadi tidak boleh diubah karena fungsi inline dapat merujuk ke anggota data ini di badan fungsinya. Mengubah offset anggota data dapat mengakibatkan biner yang tidak kompatibel dengan versi sebelumnya.
*** Meskipun ini tidak mengubah jenis tata letak memori, ada perbedaan semantik yang dapat menyebabkan pustaka tidak berfungsi seperti yang diharapkan.
Menggunakan alat kepatuhan ABI
Saat library VNDK dibuat, ABI library tersebut akan dibandingkan dengan referensi ABI yang sesuai untuk versi VNDK yang sedang dibuat. Referensi dump ABI berada di:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/(v)ndk/<${PLATFORM_VNDK_VERSION}>/<BINDER_BITNESS>/<ARCH_ARCH-VARIANT>/source-based
Misalnya, saat membuat libfoo
untuk API level 27 dari VNDK, libfoo
ABI libfoo
dibandingkan dengan referensinya di:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/(v)ndk/27/64/<ARCH_ARCH-VARIANT>/source-based/libfoo.so.lsdump
Kesalahan kerusakan ABI
Pada kerusakan ABI, log build menampilkan peringatan dengan jenis peringatan dan jalur ke laporan abi-diff. Misalnya, jika ABI libbinder
memiliki perubahan yang tidak kompatibel, sistem build akan menampilkan error dengan pesan yang mirip dengan berikut:
***************************************************** 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 ----
Membangun pustaka VNDK pemeriksaan ABI
Saat pustaka VNDK dibuat:
-
header-abi-dumper
memproses file sumber yang dikompilasi untuk membuat library VNDK (file sumber library itu sendiri serta file sumber yang diwarisi melalui dependensi transitif statis), untuk menghasilkan file.sdump
yang sesuai dengan setiap sumber.Gambar 1. Membuat file .sdump
-
header-abi-linker
kemudian memproses file.sdump
(baik menggunakan skrip versi yang disediakan atau file.so
sesuai dengan pustaka bersama) untuk menghasilkan file.lsdump
yang mencatat semua informasi ABI yang sesuai dengan pustaka bersama.Gambar 2. Membuat file .lsdump
-
header-abi-diff
membandingkan.lsdump
file dengan referensi.lsdump
file untuk menghasilkan laporan diff yang menguraikan perbedaan dalam ABI dari dua perpustakaan.Gambar 3. Membuat laporan perbedaan
header-abi-dumper
Alat header-abi-dumper
mengurai file sumber C / C ++ dan membuang ABI yang disimpulkan dari file sumber tersebut ke file perantara. Sistem build menjalankan header-abi-dumper
pada semua file sumber yang dikompilasi sekaligus membuat library yang menyertakan file sumber dari dependensi transitif.
Saat ini file .sdump
diformat sebagai Protobuf TextFormatted , yang tidak dijamin akan stabil di rilis mendatang. Dengan demikian, pemformatan file .sdump
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 menghasilkan file .sdump
perantara yang mewakili ABI yang disajikan oleh file sumber menggunakan:
$ header-abi-dumper foo.cpp -I exported -o foo.sdump -- -x c++
Perintah ini memberi tahu header-abi-dumper
untuk mengurai foo.cpp
dan mengirimkan informasi ABI yang ditampilkan di header publik di direktori yang exported
. Ini adalah kutipan (bukan representasi lengkap) dari foo.sdump
dihasilkan oleh header-abi-dumper
:
record_types { type_info { name: "foo" size: 12 alignment: 4 referenced_type: "type-1" source_file: "foo/include/foo_exported.h" linker_set_key: "foo" self_type: "type-1" } fields { referenced_type: "type-2" field_offset: 0 field_name: "m1" access: public_access } fields { referenced_type: "type-3" field_offset: 32 field_name: "m2" access: public_access } fields { referenced_type: "type-5" field_offset: 64 field_name: "mPfoo" access: public_access } access: public_access record_kind: struct_kind tag_info { unique_id: "_ZTS3foo" } } record_types { type_info { name: "bar" size: 12 alignment: 4 referenced_type: "type-6" … pointer_types { type_info { name: "bar *" size: 4 alignment: 4 referenced_type: "type-6" source_file: "foo/include/foo_exported.h" linker_set_key: "bar *" self_type: "type-8" } } builtin_types { type_info { name: "int" size: 4 alignment: 4 referenced_type: "type-2" source_file: "" linker_set_key: "int" self_type: "type-2" } is_unsigned: false is_integral: true } functions { return_type: "type-7" function_name: "Foo" source_file: "foo/include/foo_exported.h" parameters { referenced_type: "type-2" default_arg: false } parameters { referenced_type: "type-8" default_arg: false } linker_set_key: "_Z3FooiP3bar" access: public_access }
foo.sdump
berisi informasi ABI yang diekspos oleh file sumber foo.cpp
, misalnya:
-
record_types
. Lihat struct, serikat, atau kelas yang diekspos oleh header publik. Setiap jenis catatan memiliki informasi tentang bidangnya, ukurannya, penentu akses, file header tempat ia diekspos, dll. -
pointer_types
. Merujuk ke jenis penunjuk secara langsung / tidak langsung yang direferensikan oleh catatan / fungsi yang diekspos oleh header publik, bersama dengan jenis yang ditunjuk penunjuk (melalui bidangreferenced_type
ditype_info
). Informasi serupa dicatat dalam file.sdump
untuk tipe yang memenuhi syarat, tipe C / C ++.sdump
, tipe larik, dan tipe referensi lvalue dan rvalue (informasi pencatatan tentang tipe memungkinkan untuk diffing rekursif). -
functions
. Mewakili fungsi yang diekspos oleh header publik. Mereka juga memiliki informasi tentang nama fungsi yang rusak, jenis kembalian, jenis parameter, penentu akses, dll.
header-abi-linker
Alat header-abi-linker
mengambil file perantara yang dihasilkan oleh header-abi-dumper
sebagai input lalu menautkan file tersebut:
Masukan |
|
---|---|
Keluaran | File yang mencatat ABI dari perpustakaan bersama (misalnya libfoo.so.lsdump mewakili ABI libfoo ). |
Alat ini menggabungkan jenis grafik di semua file perantara yang diberikan, dengan mempertimbangkan satu definisi (jenis yang ditentukan pengguna di unit terjemahan berbeda dengan nama yang sepenuhnya memenuhi syarat yang sama, mungkin berbeda secara semantik) perbedaan di seluruh unit terjemahan. Alat tersebut kemudian mengurai skrip versi atau tabel .dynsym
dari pustaka bersama (file .so
) untuk membuat daftar simbol yang diekspor.
Misalnya, ketika libfoo
menambahkan file bar.cpp
(yang memperlihatkan sebuah function bar
C) ke kompilasinya, header-abi-linker
dapat dipanggil untuk membuat dump ABI terkait 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 keluaran perintah di libfoo.so.lsdump
:
record_types { type_info { name: "foo" size: 24 alignment: 8 referenced_type: "type-1" source_file: "foo/include/foo_exported.h" linker_set_key: "foo" self_type: "type-1" } fields { referenced_type: "type-2" field_offset: 0 field_name: "m1" access: public_access } fields { referenced_type: "type-3" field_offset: 64 field_name: "m2" access: public_access } fields { referenced_type: "type-4" field_offset: 128 field_name: "mPfoo" access: public_access } access: public_access record_kind: struct_kind tag_info { unique_id: "_ZTS3foo" } } record_types { type_info { name: "bar" size: 24 alignment: 8 ... builtin_types { type_info { name: "void" size: 0 alignment: 0 referenced_type: "type-6" source_file: "" linker_set_key: "void" self_type: "type-6" } is_unsigned: false is_integral: false } functions { return_type: "type-19" function_name: "Foo" source_file: "foo/include/foo_exported.h" parameters { referenced_type: "type-2" default_arg: false } parameters { referenced_type: "type-20" default_arg: false } linker_set_key: "_Z3FooiP3bar" access: public_access } functions { return_type: "type-6" function_name: "FooBad" source_file: "foo/include/foo_exported_bad.h" parameters { referenced_type: "type-2" default_arg: false } parameters { referenced_type: "type-7" default_arg: false } linker_set_key: "_Z6FooBadiP3foo" access: public_access } elf_functions { name: "_Z3FooiP3bar" } elf_functions { name: "_Z6FooBadiP3foo" }
Alat header-abi-linker
:
-
.sdump
file.sdump
diberikan padanya (foo.sdump
danbar.sdump
), memfilter informasi ABI yang tidak ada di header yang berada di direktori:exported
. -
libfoo.so
, dan kumpulkan informasi tentang simbol yang diekspor oleh library melalui tabel.dynsym
-nya. - Menambahkan
_Z3FooiP3bar
danBar
.
libfoo.so.lsdump
adalah dump ABI akhir dari libfoo.so
.
header-abi-diff
Alat header-abi-diff
membandingkan dua file .lsdump
mewakili ABI dari dua pustaka dan menghasilkan laporan diff yang menyatakan perbedaan antara kedua ABI.
Masukan |
|
---|---|
Keluaran | Laporan perbedaan yang menyatakan perbedaan dalam ABI yang ditawarkan oleh dua pustaka bersama yang dibandingkan. |
File ABI diff dirancang agar verbose dan dapat dibaca sebanyak mungkin. Formatnya 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
menjadi foo_t *
. Karena bar_t
adalah tipe yang bisa langsung dijangkau, ini harus ditandai sebagai perubahan pemutusan 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 keluaran 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 perubahan ABI yang merusak di libfoo
. Pesan record_type_diffs
menunjukkan bahwa record telah berubah dan mencantumkan perubahan yang tidak kompatibel, yang meliputi:
- Ukuran record berubah dari
24
byte menjadi8
byte. - Jenis
mfoo
berubah darifoo
kefoo *
(semua typedef dihapus).
Bidang type_stack
menunjukkan bagaimana header-abi-diff
mencapai jenis yang diubah ( bar
). Bidang ini dapat diartikan sebagai Foo
adalah fungsi yang diekspor yang menggunakan bar *
sebagai parameter, yang menunjuk ke bar
, yang telah diekspor dan diubah.
Menerapkan ABI / API
Untuk menerapkan ABI / API library bersama VNDK dan LLNDK, referensi ABI harus diperiksa ke dalam ${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/(v)ndk/
. Untuk membuat referensi ini, jalankan perintah berikut:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py
Setelah membuat referensi, perubahan apa pun yang dilakukan pada kode sumber yang mengakibatkan perubahan ABI / API yang tidak kompatibel di pustaka VNDK atau LLNDK sekarang menghasilkan kesalahan versi.
Untuk memperbarui referensi ABI untuk pustaka inti VNDK tertentu, jalankan perintah berikut:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l <lib1> -l <lib2>
Misalnya, untuk mengupdate referensi ABI libbinder
, jalankan:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libbinder
Untuk memperbarui referensi ABI untuk pustaka LLNDK tertentu, jalankan perintah berikut:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l <lib1> -l <lib2> --llndk
Misalnya, untuk mengupdate referensi ABI libm
, jalankan:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libm --llndk