Stabilitas ABI

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

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
  • Ubah ukuran tipe kelas atau tipe struct.
  • Kelas dasar
    • Menambah atau menghapus kelas dasar.
    • Menambah atau menghapus kelas dasar yang diwariskan secara virtual.
    • Ubah urutan kelas dasar.
  • Fungsi anggota
    • Hapus fungsi anggota*.
    • Menambah atau menghapus argumen dari fungsi anggota.
    • Ubah tipe argumen atau tipe pengembalian fungsi anggota*.
    • Ubah tata letak tabel virtual.
  • Anggota data
    • Hapus anggota data statis.
    • Menambah atau menghapus anggota data non-statis.
    • Mengubah tipe data anggota.
    • Ubah offset menjadi anggota data non-statis**.
    • Ubah const , volatile , dan/atau qualifier restricted dari anggota data***.
    • Turunkan penentu akses anggota data***.
  • Ubah argumen template.
serikat pekerja
  • Tambah atau hapus data anggota.
  • Ubah ukuran jenis serikat pekerja.
  • Mengubah tipe data anggota.
  • Ubah urutan anggota data.
Enumerasi
  • Ubah jenis yang mendasarinya.
  • Mengubah nama enumerator.
  • Ubah nilai enumerator.
Simbol Global
  • Hapus simbol yang diekspor oleh tajuk publik.
  • Untuk simbol global tipe FUNC
    • Tambahkan atau hapus argumen.
    • Ubah tipe argumen.
    • Ubah jenis pengembalian.
    • Turunkan penentu akses***.
  • Untuk simbol global tipe OBYEK
    • Ubah tipe C/C++ yang sesuai.
    • Turunkan penentu akses***.

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

  1. 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.
    sdump creation
    Gambar 1. Membuat file .sdump
  2. 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.
    lsdump creation
    Gambar 2. Membuat file .lsdump
  3. header-abi-diff membandingkan file .lsdump dengan file .lsdump referensi untuk menghasilkan laporan perbedaan yang menguraikan perbedaan ABI dari dua perpustakaan.
    abi diff creation
    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 bidang referenced_type di type_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
  • File perantara diproduksi oleh header-abi-dumper
  • Skrip versi/file Peta (opsional)
  • .so file perpustakaan bersama
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 dan bar.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 dan Bar .

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
  • File .lsdump mewakili ABI dari perpustakaan bersama yang lama.
  • .lsdump file yang mewakili ABI dari perpustakaan bersama yang baru.
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 menjadi 8 byte.
  • Jenis bidang mfoo berubah dari foo menjadi foo * (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