Stabilitas Antarmuka Biner Aplikasi (ABI) merupakan prasyarat pembaruan kerangka saja karena modul vendor mungkin bergantung pada pustaka bersama Vendor Native Development Kit (VNDK) yang berada di partisi sistem. Dalam rilis Android, pustaka bersama VNDK yang baru dibuat harus kompatibel dengan ABI ke pustaka bersama VNDK yang dirilis sebelumnya sehingga modul vendor dapat bekerja dengan pustaka 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 hanya kerangka kerja. Kepatuhan ABI mengacu pada kemampuan versi yang lebih baru dari perpustakaan bersama untuk bekerja seperti yang diharapkan dengan modul yang secara dinamis terkait dengannya (yaitu berfungsi seperti versi perpustakaan 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 tajuk publik dari perpustakaan bersama.
- Muncul di tabel
.dynsym
dari file.so
yang terkait dengan pustaka bersama. - Memiliki ikatan yang LEMAH atau GLOBAL.
- Visibilitas adalah DEFAULT atau DILINDUNGI.
- Indeks bagian tidak TERDEFINISI.
- Jenisnya adalah FUNC atau OBJECT.
Header publik perpustakaan bersama didefinisikan sebagai header yang tersedia untuk perpustakaan/binari lain melalui export_include_dirs
, export_header_lib_headers
, export_static_lib_headers
, export_shared_lib_headers
, dan export_generated_headers
dalam definisi Android.bp
dari modul yang terkait dengan perpustakaan bersama.
Tentang tipe yang dapat dijangkau
Tipe yang dapat dijangkau adalah tipe bawaan C/C++ 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 di tabel .dynsym
. Pustaka libfoo.so
mencakup yang 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
, tipe yang dapat dijangkau langsung/tidak langsung meliputi:
Jenis | Keterangan |
---|---|
bool | Kembalikan 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 , bertipe foo_t , yang diekspor melalui foo_exported.h , yang menghasilkan lebih banyak tipe yang diekspor:
Namun, foo_private_t TIDAK dapat dijangkau karena tidak diekspor melalui foo_exported.h . ( foot_private_t * buram, oleh karena itu perubahan yang dilakukan 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 library yang ditandai vendor_available: true
dan vndk.enabled: true
di file Android.bp
terkait. 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 | Keterangan |
---|---|
Struktur dan Kelas |
|
serikat pekerja |
|
Enumerasi |
|
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 menghasilkan biner yang tidak kompatibel.
** Offset untuk anggota data publik atau pribadi tidak boleh diubah karena fungsi sebaris dapat merujuk ke anggota data ini di badan fungsinya. Mengubah offset anggota data dapat menghasilkan biner yang tidak kompatibel ke belakang.
*** Meskipun ini tidak mengubah jenis tata letak memori, ada perbedaan semantik yang dapat menyebabkan perpustakaan tidak berfungsi seperti yang diharapkan.
Menggunakan alat kepatuhan ABI
Saat pustaka VNDK dibuat, ABI pustaka dibandingkan dengan referensi ABI yang sesuai untuk versi VNDK yang sedang dibuat. Referensi dump ABI terletak di:
${ANDROID_BUILD_TOP}/prebuilts/abi-dumps/(v)ndk/<${PLATFORM_VNDK_VERSION}>/<BINDER_BITNESS>/<ARCH_ARCH-VARIANT>/source-based
Misalnya, saat libfoo
untuk API level 27 dari VNDK, asumsi 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 kesalahan dengan pesan yang mirip dengan 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 ----
Membangun pemeriksaan ABI perpustakaan VNDK
Saat pustaka VNDK dibuat:
-
header-abi-dumper
memproses file sumber yang dikompilasi untuk membangun perpustakaan VNDK (file sumber perpustakaan 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
(menggunakan skrip versi yang disediakan untuknya atau file.so
yang terkait dengan pustaka bersama) untuk menghasilkan file.lsdump
yang mencatat semua informasi ABI yang terkait dengan pustaka bersama.Gambar 2. Membuat file .lsdump
-
header-abi-diff
membandingkan file.lsdump
dengan file.lsdump
referensi untuk menghasilkan laporan perbedaan yang menguraikan perbedaan ABI dari dua perpustakaan.Gambar 3. Membuat laporan perbedaan
header-abi-dumper
Alat header-abi-dumper
mem-parsing 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 sambil juga membangun perpustakaan yang menyertakan file sumber dari dependensi transitif.
Saat ini file .sdump
diformat sebagai Protobuf TextFormatted , yang tidak dijamin stabil di rilis mendatang. Dengan demikian, pemformatan file .sdump
harus dianggap sebagai detail implementasi sistem pembangunan.
Misalnya, libfoo.so
memiliki file sumber foo.cpp
berikut :
#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 memancarkan informasi ABI yang diekspos 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, union, atau class yang diekspos oleh header publik. Setiap jenis catatan memiliki informasi tentang bidangnya, ukurannya, penentu akses, file header yang diekspos, dll. -
pointer_types
. Rujuk ke tipe pointer secara langsung/tidak langsung yang direferensikan oleh record/fungsi yang diekspos oleh header publik, bersama dengan tipe yang ditunjuk pointer (melalui bidangreferenced_type
ditype_info
). Informasi serupa dicatat dalam file.sdump
untuk tipe yang memenuhi syarat, tipe C/C++ bawaan, tipe array, dan tipe referensi lvalue dan rvalue (informasi logging semacam itu tentang tipe memungkinkan untuk diffing rekursif). -
functions
. Mewakili fungsi yang diekspos oleh header publik. Mereka juga memiliki informasi tentang nama fungsi yang rusak, tipe kembalian, tipe parameter, penentu akses, dll.
header-abi-linker
Alat header-abi-linker
mengambil file perantara yang dihasilkan oleh header-abi-dumper
sebagai input kemudian menautkan file-file itu:
masukan |
|
---|---|
Keluaran | File yang mencatat ABI dari perpustakaan bersama (misalnya libfoo.so.lsdump mewakili ABI libfoo ). |
Alat ini menggabungkan grafik tipe di semua file perantara yang diberikan padanya, dengan mempertimbangkan satu definisi (tipe yang ditentukan pengguna dalam unit terjemahan yang berbeda dengan nama yang sepenuhnya memenuhi syarat yang sama, mungkin berbeda secara semantik) perbedaan di seluruh unit terjemahan. Alat kemudian mem-parsing 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 bar
fungsi C ) ke kompilasinya, header-abi-linker
dapat dipanggil untuk membuat 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 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
:
- Menautkan file
.sdump
yang disediakan untuknya (foo.sdump
danbar.sdump
), memfilter informasi ABI yang tidak ada di header yang berada di direktori:exported
. -
libfoo.so
, dan mengumpulkan informasi tentang simbol yang diekspor oleh perpustakaan 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
yang mewakili ABI dari dua perpustakaan dan menghasilkan laporan perbedaan yang menyatakan perbedaan antara kedua ABI.
masukan |
|
---|---|
Keluaran | Laporan perbedaan yang menyatakan perbedaan ABI yang ditawarkan oleh dua perpustakaan bersama yang dibandingkan. |
File diff ABI dirancang agar bertele-tele dan mudah dibaca. Format dapat berubah di 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 dapat dijangkau secara langsung, ini harus ditandai sebagai perubahan yang melanggar 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 yang melanggar ABI di libfoo
. Pesan record_type_diffs
menunjukkan catatan telah berubah dan mencantumkan perubahan yang tidak kompatibel, yang meliputi:
- Ukuran record berubah dari
24
byte menjadi8
byte. - Jenis bidang
mfoo
berubah darifoo
menjadifoo *
(semua typedef dihilangkan).
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 diekspor dan diubah.
Menegakkan ABI/API
Untuk menerapkan ABI/API dari pustaka bersama VNDK dan LLNDK, referensi ABI harus diperiksa ke ${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, setiap perubahan yang dibuat pada kode sumber yang menghasilkan perubahan ABI/API yang tidak kompatibel di pustaka VNDK atau LLNDK sekarang menghasilkan kesalahan build.
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 memperbarui 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 memperbarui referensi libm
ABI, jalankan:
${ANDROID_BUILD_TOP}/development/vndk/tools/header-checker/utils/create_reference_dumps.py -l libm --llndk