Stabilitas ABI

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:
  • int : adalah jenis m1.
  • int * : adalah jenis m2.
  • foo_private_t * : adalah jenis mPfoo.

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
  • Ubah ukuran jenis class atau jenis struct.
  • Class dasar
    • Menambahkan atau menghapus class dasar.
    • Menambahkan atau menghapus class dasar yang diwarisi secara virtual.
    • Ubah urutan class dasar.
  • Fungsi anggota
    • Hapus fungsi anggota*.
    • Menambahkan atau menghapus argumen dari fungsi anggota.
    • Mengubah jenis argumen atau jenis nilai yang ditampilkan anggota fungsi-fungsi*.
    • Mengubah tata letak tabel virtual.
  • Anggota data
    • Hapus anggota data statis.
    • Menambahkan atau menghapus anggota data non-statis.
    • Mengubah jenis anggota data.
    • Ubah offset menjadi anggota data non-statis**.
    • Mengubah const, volatile, dan/atau restricted penentu anggota data***.
    • Downgrade penentu akses anggota data***.
  • Ubah argumen template.
Serikat
  • Tambahkan atau hapus anggota data.
  • Mengubah ukuran jenis union.
  • Mengubah jenis anggota data.
Enumerasi
  • Ubah jenis dasar.
  • Mengubah nama enumerator.
  • Ubah nilai enumerator.
Simbol Global
  • Hapus simbol yang diekspor oleh header publik.
  • Untuk simbol global jenis FUNC
    • Tambahkan atau hapus argumen.
    • Mengubah jenis argumen.
    • Ubah jenis nilai yang ditampilkan.
    • Downgrade penentu akses***.
  • Untuk simbol global dari jenis OBJECT
    • Ubah jenis C/C++ yang sesuai.
    • Downgrade penentu akses***.

* 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:

  1. 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.
    pembuatan sdump
    Gambar 1. Membuat .sdump file
  2. header-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.
    pembuatan lsdump
    Gambar 2. Membuat .lsdump file
  3. header-abi-diff membandingkan .lsdump dengan file .lsdump referensi untuk menghasilkan laporan perbedaan yang menguraikan perbedaan ABI dari kedua library tersebut.
    pembuatan perbedaan abi
    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
  • File sumber C/C++
  • Direktori penyertaan telah diekspor
  • Flag compiler
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 (melalui referenced_type di type_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
  • File menengah yang dihasilkan oleh header-abi-dumper
  • Skrip versi/File peta (opsional)
  • .so file dari pustaka bersama
  • Direktori penyertaan telah diekspor
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 dan bar.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
  • File .lsdump yang mewakili ABI file bersama lama library.
  • File .lsdump yang mewakili ABI library bersama yang baru.
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 menjadi 8 byte.
  • Jenis kolom mfoo berubah dari foo menjadi foo * (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