Pemantauan ABI kernel Android

Anda dapat menggunakan alat pemantauan antarmuka biner aplikasi (ABI), yang tersedia di Android 11 dan yang lebih tinggi, untuk menstabilkan ABI dalam kernel kernel Android. Alat ini mengumpulkan dan membandingkan representasi ABI dari biner kernel yang ada (modul GKI vmlinux+). Representasi ABI ini adalah file .stg dan daftar simbol. Antarmuka tempat representasi memberikan tampilan disebut Kernel Module Interface (KMI). Anda dapat menggunakan alat ini untuk melacak dan memitigasi perubahan pada KMI.

Alat pemantauan ABI dikembangkan di AOSP dan menggunakan STG (atau libabigail di Android 13 dan yang lebih rendah) untuk membuat dan membandingkan representasi.

Halaman ini menjelaskan alat, proses pengumpulan dan analisis representasi ABI, serta penggunaan representasi tersebut untuk memberikan stabilitas pada ABI dalam kernel. Halaman ini juga memberikan informasi untuk berkontribusi dalam melakukan perubahan pada kernel Android.

Proses

Menganalisis ABI kernel memerlukan beberapa langkah, yang sebagian besar dapat diotomatiskan:

  1. Bangun kernel dan representasi ABI-nya.
  2. Menganalisis perbedaan ABI antara build dan referensi.
  3. Perbarui representasi ABI (jika diperlukan).
  4. Bekerja dengan daftar simbol.

Petunjuk berikut berfungsi untuk kernel yang dapat Anda bangun menggunakan toolchain yang didukung (seperti toolchain Clang yang telah dibuat sebelumnya). repo manifests tersedia untuk semua cabang kernel umum Android dan untuk beberapa kernel khusus perangkat, yang memverifikasi bahwa toolchain yang benar digunakan saat Anda membuat distribusi kernel untuk analisis.

Daftar simbol

KMI tidak menyertakan semua simbol dalam kernel atau bahkan semua simbol yang diekspor yang berjumlah lebih dari 30.000. Sebagai gantinya, simbol yang dapat digunakan oleh modul vendor dicantumkan secara eksplisit dalam serangkaian file daftar simbol yang dikelola secara publik di struktur kernel (gki/{ARCH}/symbols/* atau android/abi_gki_{ARCH}_* di Android 15 dan yang lebih lama). Gabungan semua simbol dalam semua file daftar simbol menentukan kumpulan simbol KMI yang dipertahankan sebagai stabil. Contoh file daftar simbol adalah gki/aarch64/symbols/db845c, yang mendeklarasikan simbol yang diperlukan untuk DragonBoard 845c.

Hanya simbol yang tercantum dalam daftar simbol serta struktur dan definisi terkaitnya yang dianggap sebagai bagian dari KMI. Anda dapat memposting perubahan pada daftar simbol jika simbol yang Anda butuhkan tidak ada. Setelah antarmuka baru berada dalam daftar simbol, dan menjadi bagian dari deskripsi KMI, antarmuka tersebut akan dipertahankan sebagai stabil dan tidak boleh dihapus dari daftar simbol atau diubah setelah cabang dibekukan.

Setiap cabang kernel KMI Android Common Kernel (ACK) memiliki kumpulan daftar simbolnya sendiri. Tidak ada upaya untuk memberikan stabilitas ABI antara cabang kernel KMI yang berbeda. Misalnya, KMI untuk android12-5.10 sepenuhnya independen dari KMI untuk android13-5.10.

Alat ABI menggunakan daftar simbol KMI untuk membatasi antarmuka yang harus dipantau stabilitasnya. Vendor diharapkan mengirimkan dan memperbarui daftar simbol mereka sendiri untuk memverifikasi bahwa antarmuka yang mereka andalkan mempertahankan kompatibilitas ABI. Misalnya, untuk melihat daftar daftar simbol untuk kernel android16-6.12, lihat https://android.googlesource.com/kernel/common/+/refs/heads/android16-6.12/gki/aarch64/symbols

Daftar simbol berisi simbol yang dilaporkan diperlukan untuk vendor atau perangkat tertentu. Daftar lengkap yang digunakan oleh alat ini adalah gabungan dari semua file daftar simbol KMI. Alat ABI menentukan detail setiap simbol, termasuk tanda tangan fungsi dan struktur data bertingkat.

Saat KMI dibekukan, tidak ada perubahan yang diizinkan pada antarmuka KMI yang ada; antarmuka tersebut stabil. Namun, vendor bebas menambahkan simbol ke KMI kapan saja selama penambahan tidak memengaruhi stabilitas ABI yang ada. Simbol yang baru ditambahkan dipertahankan kestabilannya segera setelah dikutip oleh daftar simbol KMI. Simbol tidak boleh dihapus dari daftar untuk kernel kecuali jika dapat dikonfirmasi bahwa tidak ada perangkat yang pernah dikirim dengan dependensi pada simbol tersebut.

Anda dapat membuat daftar simbol KMI untuk perangkat menggunakan petunjuk dari Cara menggunakan daftar simbol. Banyak partner mengirimkan satu daftar simbol per ACK, tetapi ini bukan persyaratan yang ketat. Jika membantu pemeliharaan, Anda dapat mengirimkan beberapa daftar simbol.

Memperpanjang KMI

Meskipun simbol KMI dan struktur terkait dipertahankan sebagai stabil (artinya perubahan yang merusak antarmuka stabil di kernel dengan KMI yang dibekukan tidak dapat diterima), kernel GKI tetap terbuka untuk ekstensi sehingga perangkat yang diluncurkan pada tahun ini tidak perlu menentukan semua dependensinya sebelum KMI dibekukan. Untuk memperluas KMI, Anda dapat menambahkan simbol baru ke KMI untuk fungsi kernel yang diekspor baru atau yang sudah ada, meskipun KMI dibekukan. Patch kernel baru juga dapat diterima jika tidak merusak KMI.

Tentang gangguan KMI

Kernel memiliki sumber dan biner dibuat dari sumber tersebut. Cabang kernel yang dipantau ABI mencakup representasi ABI GKI saat ini (dalam bentuk file .stg). Setelah biner (vmlinux, Image, dan modul GKI apa pun) dibuat, representasi ABI dapat diekstrak dari biner. Setiap perubahan yang dilakukan pada file sumber kernel dapat memengaruhi biner dan pada gilirannya juga memengaruhi .stg yang diekstrak. Analisis Kepatuhan ABI membandingkan file .stg yang di-commit dengan file yang diekstrak dari artefak build dan menetapkan label Lint-1 pada perubahan di Gerrit jika menemukan perbedaan semantik.

Menangani kerusakan ABI

Sebagai contoh, patch berikut memperkenalkan kerusakan ABI yang sangat jelas:

diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index 42786e6364ef..e15f1d0f137b 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -657,6 +657,7 @@ struct mm_struct {
                ANDROID_KABI_RESERVE(1);
        } __randomize_layout;

+       int tickle_count;
        /*
         * The mm_cpumask needs to be at the end of mm_struct, because it
         * is dynamically sized based on nr_cpu_ids.

Saat Anda menjalankan build ABI dengan patch ini diterapkan, alat akan keluar dengan kode error bukan nol dan melaporkan perbedaan ABI yang mirip dengan ini:

function symbol 'struct block_device* I_BDEV(struct inode*)' changed
  CRC changed from 0x8d400dbd to 0xabfc92ad

function symbol 'void* PDE_DATA(const struct inode*)' changed
  CRC changed from 0xc3c38b5c to 0x7ad96c0d

function symbol 'void __ClearPageMovable(struct page*)' changed
  CRC changed from 0xf489e5e8 to 0x92bd005e

... 4492 omitted; 4495 symbols have only CRC changes

type 'struct mm_struct' changed
  byte size changed from 992 to 1000
  member 'int tickle_count' was added
  member 'unsigned long cpu_bitmap[0]' changed
    offset changed by 64

Perbedaan ABI terdeteksi pada waktu build

Penyebab paling umum terjadinya error adalah saat driver menggunakan simbol baru dari kernel yang tidak ada dalam daftar simbol mana pun.

Jika simbol tidak disertakan dalam daftar simbol, Anda harus memverifikasi terlebih dahulu bahwa simbol diekspor dengan EXPORT_SYMBOL_GPL(symbol_name), lalu perbarui daftar simbol dan representasi ABI. Misalnya, perubahan berikut menambahkan fitur FS Inkremental baru ke cabang android-12-5.10, yang mencakup pembaruan daftar simbol dan representasi ABI.

Jika simbol diekspor (baik oleh Anda atau diekspor sebelumnya) tetapi tidak ada driver lain yang menggunakannya, Anda mungkin mendapatkan error build yang mirip dengan berikut ini.

Comparing the KMI and the symbol lists:
+ build/abi/compare_to_symbol_list out/$BRANCH/common/Module.symvers out/$BRANCH/common/abi_symbollist.raw
ERROR: Differences between ksymtab and symbol list detected!
Symbols missing from ksymtab:
Symbols missing from symbol list:
 - simple_strtoull

Untuk mengatasinya, perbarui daftar simbol KMI di kernel dan ACK (lihat Memperbarui representasi ABI). Untuk contoh memperbarui daftar simbol dan representasi ABI di ACK, lihat aosp/1367601.

Mengatasi kerusakan ABI kernel

Anda dapat menangani kerusakan ABI kernel dengan memfaktorkan ulang kode agar tidak mengubah ABI atau memperbarui representasi ABI. Gunakan diagram berikut untuk menentukan pendekatan terbaik untuk situasi Anda.

Diagram Alur Kerusakan ABI

Gambar 1. Penyelesaian kerusakan ABI

Memfaktorkan ulang kode untuk menghindari perubahan ABI

Lakukan segala upaya untuk menghindari modifikasi ABI yang ada. Dalam banyak kasus, Anda dapat memfaktorkan ulang kode untuk menghapus perubahan yang memengaruhi ABI.

  • Memfaktorkan ulang perubahan kolom struct. Jika perubahan memodifikasi ABI untuk fitur debug, tambahkan #ifdef di sekitar kolom (dalam struct dan referensi sumber) dan pastikan CONFIG yang digunakan untuk #ifdef dinonaktifkan untuk defconfig produksi dan gki_defconfig. Untuk contoh cara menambahkan konfigurasi debug ke struct tanpa merusak ABI, lihat patchset ini.

  • Memfaktorkan ulang fitur agar tidak mengubah kernel inti. Jika fitur baru perlu ditambahkan ke ACK untuk mendukung modul partner, coba refaktor bagian ABI dari perubahan untuk menghindari modifikasi ABI kernel. Untuk contoh penggunaan ABI kernel yang ada untuk menambahkan kemampuan tambahan tanpa mengubah ABI kernel, lihat aosp/1312213.

Memperbaiki ABI yang rusak di Android Gerrit

Jika Anda tidak sengaja merusak ABI kernel, Anda perlu menyelidikinya menggunakan panduan yang diberikan oleh alat pemantauan ABI. Penyebab paling umum kerusakan adalah perubahan struktur data dan perubahan CRC simbol terkait, atau karena perubahan opsi konfigurasi yang menyebabkan salah satu hal di atas. Mulailah dengan mengatasi masalah yang ditemukan oleh alat.

Anda dapat mereproduksi temuan ABI secara lokal, lihat Membangun kernel dan representasi ABI-nya.

Tentang label Lint-1

Jika Anda mengupload perubahan ke cabang yang berisi KMI yang dibekukan atau difinalisasi, perubahan harus lulus analisis Kepatuhan dan Kompatibilitas ABI untuk memastikan perubahan pada representasi ABI mencerminkan ABI sebenarnya dan tidak berisi ketidakcocokan (penghapusan simbol atau perubahan jenis).

Setiap analisis ABI ini dapat menetapkan label Lint-1 dan memblokir pengiriman perubahan hingga semua masalah diselesaikan atau label diganti.

Memperbarui ABI kernel

Jika modifikasi ABI tidak dapat dihindari, Anda harus menerapkan perubahan kode, representasi ABI, dan daftar simbol ke ACK. Untuk membuat Lint menghapus -1 dan tidak merusak kompatibilitas GKI, ikuti langkah-langkah berikut:

  1. Upload perubahan kode ke ACK.

  2. Tunggu hingga Anda menerima Code-Review +2 untuk patchset.

  3. Perbarui representasi ABI referensi.

  4. Gabungkan perubahan kode dan perubahan update ABI Anda.

Mengupload perubahan kode ABI ke ACK

Memperbarui ABI ACK bergantung pada jenis perubahan yang dilakukan.

  • Jika perubahan ABI terkait dengan fitur yang memengaruhi pengujian CTS atau VTS, perubahan biasanya dapat dipilih untuk ACK apa adanya. Misalnya:

  • Jika perubahan ABI ditujukan untuk fitur yang dapat dibagikan dengan ACK, perubahan tersebut dapat dipilih ke ACK apa adanya. Misalnya, perubahan berikut tidak diperlukan untuk pengujian CTS atau VTS, tetapi dapat dibagikan dengan ACK:

  • Jika perubahan ABI memperkenalkan fitur baru yang tidak perlu disertakan dalam ACK, Anda dapat memperkenalkan simbol ke ACK menggunakan stub seperti yang dijelaskan di bagian berikut.

Menggunakan stub untuk ACK

Stub hanya diperlukan untuk perubahan kernel inti yang tidak mendapatkan manfaat dari ACK, seperti perubahan performa dan daya. Daftar berikut menjelaskan contoh stub dan cherry-pick parsial di ACK untuk GKI.

  • Stub fitur isolasi inti (aosp/1284493). Kemampuan di ACK tidak diperlukan, tetapi simbol harus ada di ACK agar modul Anda dapat menggunakan simbol ini.

  • Simbol placeholder untuk modul vendor (aosp/1288860).

  • Pilihan cherry-pick khusus ABI untuk fitur pelacakan peristiwa mm per proses (aosp/1288454). Patch asli dipilih ke ACK, lalu dipangkas agar hanya menyertakan perubahan yang diperlukan untuk menyelesaikan perbedaan ABI untuk task_struct dan mm_event_count. Patch ini juga mengupdate enum mm_event_type untuk berisi anggota akhir.

  • Cherry-pick parsial perubahan ABI struktur termal yang memerlukan lebih dari sekadar menambahkan kolom ABI baru.

    • Patch aosp/1255544 menyelesaikan perbedaan ABI antara kernel partner dan ACK.

    • Patch aosp/1291018 memperbaiki masalah fungsional yang ditemukan selama pengujian GKI pada patch sebelumnya. Perbaikan ini mencakup inisialisasi struct parameter sensor untuk mendaftarkan beberapa zona termal ke satu sensor.

  • Perubahan ABI CONFIG_NL80211_TESTMODE (aosp/1344321). Patch ini menambahkan perubahan struct yang diperlukan untuk ABI dan memastikan kolom tambahan tidak menyebabkan perbedaan fungsional, sehingga memungkinkan partner menyertakan CONFIG_NL80211_TESTMODE dalam kernel produksi mereka dan tetap mempertahankan kepatuhan GKI.

Menerapkan KMI saat runtime

Kernel GKI menggunakan opsi konfigurasi TRIM_UNUSED_KSYMS=y dan UNUSED_KSYMS_WHITELIST=<union of all symbol lists>, yang membatasi simbol yang diekspor (seperti simbol yang diekspor menggunakan EXPORT_SYMBOL_GPL()) ke simbol yang tercantum dalam daftar simbol. Semua simbol lainnya tidak diekspor, dan pemuatan modul yang memerlukan simbol yang tidak diekspor akan ditolak. Pembatasan ini diterapkan pada waktu build dan entri yang tidak ada akan ditandai.

Untuk tujuan pengembangan, Anda dapat menggunakan build kernel GKI yang tidak menyertakan penghapusan simbol (artinya semua simbol yang biasanya diekspor dapat digunakan). Untuk menemukan build ini, cari build kernel_debug_aarch64 di ci.android.com.

Menerapkan KMI menggunakan pembuatan versi modul

Kernel Generic Kernel Image (GKI) menggunakan pembuatan versi modul (CONFIG_MODVERSIONS) sebagai langkah tambahan untuk menerapkan kepatuhan KMI saat runtime. Pemberian versi modul dapat menyebabkan kegagalan ketidakcocokan pemeriksaan redundansi siklik (CRC) pada waktu pemuatan modul jika KMI yang diharapkan dari modul tidak cocok dengan KMI vmlinux. Misalnya, berikut adalah kegagalan umum yang terjadi pada waktu pemuatan modul karena ketidakcocokan CRC untuk simbol module_layout():

init: Loading module /lib/modules/kernel/.../XXX.ko with args ""
XXX: disagrees about version of symbol module_layout
init: Failed to insmod '/lib/modules/kernel/.../XXX.ko' with args ''

Penggunaan pembuatan versi modul

Pembuatan versi modul berguna karena alasan berikut:

  • Pembuatan versi modul mencatat perubahan dalam visibilitas struktur data. Jika modul mengubah struktur data buram, yaitu struktur data yang bukan bagian dari KMI, modul tersebut akan rusak setelah perubahan struktur di masa mendatang.

    Sebagai contoh, pertimbangkan kolom fwnode di struct device. Kolom ini HARUS buram bagi modul sehingga modul tidak dapat melakukan perubahan pada kolom device->fw_node atau membuat asumsi tentang ukurannya.

    Namun, jika modul menyertakan <linux/fwnode.h> (secara langsung atau tidak langsung), maka kolom fwnode di struct device tidak lagi buram baginya. Modul kemudian dapat membuat perubahan pada device->fwnode->dev atau device->fwnode->ops. Skenario ini bermasalah karena beberapa alasan, seperti yang dinyatakan berikut:

    • Hal ini dapat melanggar asumsi yang dibuat oleh kode kernel inti tentang struktur data internalnya.

    • Jika update kernel mendatang mengubah struct fwnode_handle (jenis data fwnode), maka modul tidak lagi berfungsi dengan kernel baru. Selain itu, stgdiff tidak akan menunjukkan perbedaan apa pun karena modul melanggar KMI dengan memanipulasi struktur data internal secara langsung dengan cara yang tidak dapat ditangkap hanya dengan memeriksa representasi biner.

  • Modul saat ini dianggap tidak kompatibel dengan KMI jika dimuat di kemudian hari oleh kernel baru yang tidak kompatibel. Pemberian versi modul menambahkan pemeriksaan run-time untuk menghindari pemuatan modul yang tidak kompatibel dengan KMI secara tidak sengaja dengan kernel. Pemeriksaan ini mencegah masalah runtime yang sulit di-debug dan error kernel yang mungkin diakibatkan oleh ketidakcocokan yang tidak terdeteksi dalam KMI.

Mengaktifkan pembuatan versi modul akan mencegah semua masalah ini.

Memeriksa ketidakcocokan CRC tanpa melakukan booting perangkat

stgdiff membandingkan dan melaporkan ketidakcocokan CRC antar-kernel bersama dengan perbedaan ABI lainnya.

Selain itu, build kernel lengkap dengan CONFIG_MODVERSIONS yang diaktifkan akan menghasilkan file Module.symvers sebagai bagian dari proses build normal. File ini memiliki satu baris untuk setiap simbol yang diekspor oleh kernel (vmlinux) dan modul. Setiap baris terdiri dari nilai CRC, nama simbol, namespace simbol, vmlinux atau nama modul yang mengekspor simbol, dan jenis ekspor (misalnya, EXPORT_SYMBOL versus EXPORT_SYMBOL_GPL).

Anda dapat membandingkan file Module.symvers antara build GKI dan build Anda untuk memeriksa apakah ada perbedaan CRC dalam simbol yang diekspor oleh vmlinux. Jika ada perbedaan nilai CRC dalam simbol apa pun yang diekspor oleh vmlinux dan, dan simbol tersebut digunakan oleh salah satu modul yang Anda muat di perangkat, modul tidak akan dimuat.

Jika Anda tidak memiliki semua artefak build, tetapi memiliki file vmlinux dari kernel GKI dan kernel Anda, Anda dapat membandingkan nilai CRC untuk simbol tertentu dengan menjalankan perintah berikut di kedua kernel dan membandingkan outputnya:

nm <path to vmlinux>/vmlinux | grep __crc_<symbol name>

Misalnya, perintah berikut memeriksa nilai CRC untuk simbol module_layout:

nm vmlinux | grep __crc_module_layout
0000000008663742 A __crc_module_layout

Mengatasi ketidakcocokan CRC

Gunakan langkah-langkah berikut untuk menyelesaikan ketidakcocokan CRC saat memuat modul:

  1. Bangun kernel GKI dan kernel perangkat Anda menggunakan opsi --kbuild_symtypes seperti yang ditunjukkan dalam perintah berikut:

    tools/bazel run --kbuild_symtypes //common:kernel_aarch64_dist

    Perintah ini akan menghasilkan file .symtypes untuk setiap file .o. Lihat KBUILD_SYMTYPES di Kleaf untuk mengetahui detailnya.

    Untuk Android 13 dan yang lebih lama, bangun kernel GKI dan kernel perangkat Anda dengan menambahkan KBUILD_SYMTYPES=1 ke perintah yang Anda gunakan untuk membangun kernel, seperti yang ditunjukkan dalam perintah berikut:

    KBUILD_SYMTYPES=1 BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

    Saat menggunakan build_abi.sh,, flag KBUILD_SYMTYPES=1 sudah ditetapkan secara implisit.

  2. Temukan file .c tempat simbol dengan CRC tidak cocok diekspor, menggunakan perintah berikut:

    git -C common grep EXPORT_SYMBOL.*module_layout
    kernel/module/version.c:EXPORT_SYMBOL(module_layout);
  3. File .c memiliki file .symtypes yang sesuai di GKI, dan artefak build kernel perangkat Anda. Temukan file .symtypes menggunakan perintah berikut:

    cd bazel-bin/common/kernel_aarch64/symtypes
    ls -1 kernel/module/version.symtypes

    Di Android 13 dan yang lebih rendah, menggunakan skrip build lama, lokasinya kemungkinan adalah out/$BRANCH/common atau out_abi/$BRANCH/common.

    Setiap file .symtypes adalah file teks biasa yang terdiri dari deskripsi jenis dan simbol:

    • Setiap baris memiliki bentuk key description dengan deskripsi yang dapat merujuk ke kunci lain dalam file yang sama.

    • Tombol seperti [s|u|e|t]#foo merujuk ke [struct|union|enum|typedef] foo. Contoh:

      t#bool typedef _Bool bool
      
    • Kunci tanpa awalan x# hanyalah nama simbol. Contoh:

      find_module s#module * find_module ( const char * )
      
  4. Bandingkan kedua file dan perbaiki semua perbedaannya.

Sebaiknya buat symtypes dengan build tepat sebelum perubahan yang bermasalah dan kemudian pada perubahan yang bermasalah. Menyimpan semua file berarti file tersebut dapat dibandingkan secara massal.

Misalnya,

for f in $(find good bad -name '*.symtypes' | sed -r 's;^(good|bad)/;;' | LANG=C sort -u); do
  diff -N -U0 --label good/"$f" --label bad/"$f" <(LANG=C sort good/"$f") <(LANG=C sort bad/"$f")
done

Jika tidak, cukup bandingkan file tertentu yang diinginkan.

Kasus 1: Perbedaan karena visibilitas jenis data

#include baru dapat menarik definisi jenis baru (misalnya struct foo) ke dalam file sumber. Dalam kasus ini, deskripsinya dalam file .symtypes yang sesuai akan berubah dari structure_type foo { } kosong menjadi definisi lengkap.

Hal ini akan memengaruhi semua CRC dari semua simbol dalam file .symtypes yang deskripsinya bergantung secara langsung atau tidak langsung pada definisi jenis.

Misalnya, menambahkan baris berikut ke file include/linux/device.h di kernel Anda menyebabkan ketidakcocokan CRC, salah satunya adalah untuk module_layout():

 #include <linux/fwnode.h>

Membandingkan module/version.symtypes untuk simbol tersebut akan menunjukkan perbedaan berikut:

 $ diff -u <GKI>/kernel/module/version.symtypes <your kernel>/kernel/module/version.symtypes
  --- <GKI>/kernel/module/version.symtypes
  +++ <your kernel>/kernel/module/version.symtypes
  @@ -334,12 +334,15 @@
  ...
  -s#fwnode_handle structure_type fwnode_handle { }
  +s#fwnode_reference_args structure_type fwnode_reference_args { s#fwnode_handle * fwnode ; unsigned int nargs ; t#u64 args [ 8 ] ; }
  ...

Jika kernel GKI memiliki definisi jenis lengkap, tetapi kernel Anda tidak memilikinya (sangat tidak mungkin), gabungkan Android Common Kernel terbaru ke kernel Anda sehingga Anda menggunakan basis kernel GKI terbaru.

Dalam sebagian besar kasus, kernel GKI tidak memiliki definisi jenis lengkap di .symtypes, tetapi kernel Anda memilikinya karena adanya direktif #include tambahan.

Resolusi untuk Android 16 dan yang lebih tinggi

Pastikan file sumber yang terpengaruh menyertakan header stabilisasi KABI Android:

#include <linux/android_kabi.h>

Untuk setiap jenis yang terpengaruh, tambahkan ANDROID_KABI_DECLONLY(name); pada cakupan global ke file sumber yang terpengaruh.

Misalnya, jika perbedaan symtypes adalah ini:

--- good/drivers/android/vendor_hooks.symtypes
+++ bad/drivers/android/vendor_hooks.symtypes
@@ -1051 +1051,2 @@
-s#ubuf_info structure_type ubuf_info { }
+s#ubuf_info structure_type ubuf_info { member pointer_type { const_type { s#ubuf_info_ops } } ops data_member_location(0) , member t#refcount_t refcnt data_member_location(8) , member t#u8 flags data_member_location(12) } byte_size(16)
+s#ubuf_info_ops structure_type ubuf_info_ops { member pointer_type { subroutine_type ( formal_parameter pointer_type { s#sk_buff } , formal_parameter pointer_type { s#ubuf_info } , formal_parameter t#bool ) -> base_type void } complete data_member_location(0) , member pointer_type { subroutine_type ( formal_parameter pointer_type { s#sk_buff } , formal_parameter pointer_type { s#ubuf_info } ) -> base_type int byte_size(4) encoding(5) } link_skb data_member_location(8) } byte_size(16)

Kemudian, masalahnya adalah struct ubuf_info kini memiliki definisi lengkap di symtypes. Solusinya adalah menambahkan baris ke drivers/android/vendor_hooks.c:

ANDROID_KABI_DECLONLY(ubuf_info);

Hal ini menginstruksikan gendwarfksyms untuk memperlakukan jenis bernama sebagai tidak ditentukan dalam file.

Kemungkinan yang lebih rumit adalah bahwa #include baru itu sendiri ada dalam file header. Dalam hal ini, Anda mungkin perlu mendistribusikan berbagai set pemanggilan makro ANDROID_KABI_DECLONLY di seluruh file sumber yang secara tidak langsung menarik definisi jenis tambahan karena beberapa di antaranya mungkin sudah memiliki beberapa definisi jenis.

Agar mudah dibaca, tempatkan pemanggilan makro tersebut di dekat awal file sumber.

Resolusi untuk Android 15 dan yang lebih rendah

Sering kali, perbaikannya hanya menyembunyikan #include baru dari genksyms.

#ifndef __GENKSYMS__
#include <linux/fwnode.h>
#endif

Jika tidak, untuk mengidentifikasi #include yang menyebabkan perbedaan, ikuti langkah-langkah berikut:

  1. Buka file header yang menentukan simbol atau jenis data yang memiliki perbedaan ini. Misalnya, edit include/linux/fwnode.h untuk struct fwnode_handle.

  2. Tambahkan kode berikut di bagian atas file header:

    #ifdef CRC_CATCH
    #error "Included from here"
    #endif
    
  3. Dalam file .c modul yang memiliki ketidakcocokan CRC, tambahkan berikut sebagai baris pertama sebelum baris #include.

    #define CRC_CATCH 1
    
  4. Kompilasi modul Anda. Error waktu build yang dihasilkan menunjukkan rangkaian file header #include yang menyebabkan ketidakcocokan CRC ini. Contoh:

    In file included from .../drivers/clk/XXX.c:16:`
    In file included from .../include/linux/of_device.h:5:
    In file included from .../include/linux/cpu.h:17:
    In file included from .../include/linux/node.h:18:
    .../include/linux/device.h:16:2: error: "Included from here"
    #error "Included from here"
    

    Salah satu link dalam rangkaian #include ini disebabkan oleh perubahan yang dilakukan di kernel Anda, yang tidak ada di kernel GKI.

Kasus 2: Perbedaan karena perubahan jenis data

Jika ketidakcocokan CRC untuk simbol atau jenis data bukan karena perbedaan visibilitas, maka hal itu disebabkan oleh perubahan aktual (penambahan, penghapusan, atau perubahan) dalam jenis data itu sendiri.

Misalnya, melakukan perubahan berikut di kernel Anda menyebabkan beberapa ketidakcocokan CRC karena banyak simbol yang terpengaruh secara tidak langsung oleh jenis perubahan ini:

diff --git a/include/linux/iommu.h b/include/linux/iommu.h
  --- a/include/linux/iommu.h
  +++ b/include/linux/iommu.h
  @@ -259,7 +259,7 @@ struct iommu_ops {
     void (*iotlb_sync)(struct iommu_domain *domain);
     phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova);
     phys_addr_t (*iova_to_phys_hard)(struct iommu_domain *domain,
  -        dma_addr_t iova);
  +        dma_addr_t iova, unsigned long trans_flag);
     int (*add_device)(struct device *dev);
     void (*remove_device)(struct device *dev);
     struct iommu_group *(*device_group)(struct device *dev);

Satu ketidakcocokan CRC adalah untuk devm_of_platform_populate().

Jika Anda membandingkan file .symtypes untuk simbol tersebut, file tersebut mungkin terlihat seperti ini:

 $ diff -u <GKI>/drivers/of/platform.symtypes <your kernel>/drivers/of/platform.symtypes
  --- <GKI>/drivers/of/platform.symtypes
  +++ <your kernel>/drivers/of/platform.symtypes
  @@ -399,7 +399,7 @@
  ...
  -s#iommu_ops structure_type iommu_ops { ... ; t#phy
  s_addr_t ( * iova_to_phys_hard ) ( s#iommu_domain * , t#dma_addr_t ) ; int
    ( * add_device ) ( s#device * ) ; ...
  +s#iommu_ops structure_type iommu_ops { ... ; t#phy
  s_addr_t ( * iova_to_phys_hard ) ( s#iommu_domain * , t#dma_addr_t , unsigned long ) ; int ( * add_device ) ( s#device * ) ; ...

Untuk mengidentifikasi jenis yang diubah, ikuti langkah-langkah berikut:

  1. Temukan definisi simbol dalam kode sumber (biasanya dalam file .h).

    • Untuk mengetahui perbedaan simbol antara kernel Anda dan kernel GKI, temukan commit dengan menjalankan perintah berikut:
    git blame
    • Untuk simbol yang dihapus (jika simbol dihapus di satu pohon dan Anda juga ingin menghapusnya di pohon lain), Anda harus menemukan perubahan yang menghapus baris. Gunakan perintah berikut pada pohon tempat baris dihapus:
    git log -S "copy paste of deleted line/word" -- <file where it was deleted>
  2. Tinjau daftar commit yang ditampilkan untuk menemukan perubahan atau penghapusan. Commit pertama mungkin adalah commit yang Anda cari. Jika tidak, lihat daftar hingga Anda menemukan commit.

  3. Setelah mengidentifikasi commit, batalkan perubahannya di kernel atau perbarui untuk menekan perubahan CRC dan upload ke ACK dan gabungkan. Setiap jeda ABI residual harus ditinjau keamanannya dan jika perlu, jeda yang diizinkan dapat dicatat.

Lebih memilih untuk menggunakan padding yang ada

Beberapa struktur di GKI diisi untuk memungkinkan ekstensinya tanpa merusak modul vendor yang ada. Jika commit upstream (misalnya) menambahkan anggota ke struktur tersebut, maka commit tersebut dapat diubah untuk menggunakan beberapa padding. Kemudian, perubahan ini disembunyikan dari penghitungan CRC.

Makro yang distandardisasi dan mendokumentasikan dirinya sendiri ANDROID_KABI_RESERVE mencadangkan ruang (yang disejajarkan) senilai u64. Pernyataan ini digunakan sebagai pengganti pernyataan anggota.

Contoh:

struct data {
        u64 handle;
        ANDROID_KABI_RESERVE(1);
        ANDROID_KABI_RESERVE(2);
};

Padding dapat digunakan, tanpa memengaruhi CRC simbol, dengan ANDROID_KABI_USE (atau ANDROID_KABI_USE2 atau varian lain yang dapat ditentukan).

Anggota sekret tersedia seolah-olah dideklarasikan secara langsung, tetapi makro sebenarnya diperluas ke anggota gabungan anonim yang berisi sekret serta hal-hal yang digunakan oleh gendwarfksyms untuk mempertahankan stabilitas symtype.

struct data {
        u64 handle;
        ANDROID_KABI_USE(1, void *sekret);
        ANDROID_KABI_RESERVE(2);
};
Resolusi untuk Android 16 dan yang lebih tinggi

CRC dihitung oleh gendwarfksyms yang menggunakan informasi debug DWARF, sehingga mendukung jenis C dan Rust. Resolusi bervariasi menurut jenis perubahan huruf. Berikut ini beberapa contohnya.

Enumerator baru atau yang diubah

Terkadang enumerator baru ditambahkan dan sesekali nilai enumerator MAX atau yang serupa juga terpengaruh. Perubahan ini aman jika tidak "keluar" dari GKI atau jika kita dapat memastikan bahwa modul vendor tidak dapat memedulikan nilainya.

Contoh:

 enum outcome {
       SUCCESS,
       FAILURE,
       RETRY,
+      TRY_HARDER,
       OUTCOME_LIMIT
 };

Penambahan TRY_HARDER dan perubahan pada OUTCOME_LIMIT dapat disembunyikan dari penghitungan CRC dengan pemanggilan makro pada cakupan global:

ANDROID_KABI_ENUMERATOR_IGNORE(outcome, TRY_HARDER);
ANDROID_KABI_ENUMERATOR_VALUE(outcome, OUTCOME_LIMIT, 3);

Agar mudah dibaca, tempatkan tepat setelah definisi enum.

Anggota struktur baru yang menempati lubang yang ada

Karena penyelarasan, akan ada byte yang tidak digunakan antara urgent dan scratch.

        void *data;
        bool urgent;
+       bool retry;
        void *scratch;

Tidak ada offset anggota yang ada atau ukuran struktur yang terpengaruh oleh penambahan retry. Namun, hal ini dapat memengaruhi CRC simbol atau representasi ABI atau keduanya.

Tindakan ini akan menyembunyikannya dari perhitungan CRC:

        void *data;
        bool urgent;
+       ANDROID_KABI_IGNORE(1, bool retry);
        void *scratch_space;

Anggota retry tersedia seolah-olah dideklarasikan secara langsung, tetapi makro sebenarnya diperluas ke anggota gabungan anonim yang berisi retry serta hal-hal yang digunakan oleh gendwarfksyms untuk mempertahankan stabilitas symtype.

Perluasan struktur dengan anggota baru

Anggota terkadang ditambahkan di akhir struktur. Hal ini tidak memengaruhi offset anggota yang ada atau memengaruhi pengguna struktur yang ada yang hanya mengaksesnya dengan pointer. Ukuran struktur memengaruhi CRC-nya dan perubahan pada CRC ini dapat dihentikan dengan pemanggilan makro tambahan pada cakupan global, sebagai berikut:

struct data {
        u64 handle;
        u64 counter;
        ANDROID_KABI_IGNORE(1, void *sekret);
};

ANDROID_KABI_BYTE_SIZE(data, 16);

Agar mudah dibaca, tempatkan ini tepat setelah definisi struct.

Semua perubahan lain pada jenis atau jenis simbol

Terkadang, ada perubahan yang tidak termasuk dalam salah satu kategori sebelumnya, sehingga menyebabkan perubahan CRC yang tidak dapat dihentikan menggunakan makro sebelumnya.

Dalam kasus ini, deskripsi symtypes asli dari jenis atau simbol dapat disediakan dengan pemanggilan ANDROID_KABI_TYPE_STRING pada cakupan global.

struct data {
        /* extensive changes */
};

ANDROID_KABI_TYPE_STRING("s#data", "original s#data symtypes definition");

Agar mudah dibaca, tempatkan tepat setelah definisi jenis atau simbol.

Resolusi untuk Android 15 dan yang lebih rendah

Perubahan jenis dan jenis simbol harus disembunyikan dari genksyms. Hal ini dapat dilakukan dengan mengontrol praproses menggunakan __GENKSYMS__.

Transformasi kode arbitrer dapat dinyatakan dengan cara ini.

Misalnya, untuk menyembunyikan anggota baru yang menempati lubang dalam struktur yang ada:

struct parcel {
        void *data;
        bool urgent;
#ifndef __GENKSYMS__
        bool retry;
#endif
        void *scratch_space;
};