การตรวจสอบ ABI ของเคอร์เนล Android

คุณสามารถใช้เครื่องมือตรวจสอบอินเทอร์เฟซแบบไบนารีของแอปพลิเคชัน (ABI) ซึ่งมีอยู่ใน Android 11 ขึ้นไป เพื่อรักษาความเสถียรของ ABI ในเคอร์เนล ของเคอร์เนล Android เครื่องมือจะรวบรวมและเปรียบเทียบการแสดง ABI จากไบนารีของเคอร์เนลที่มีอยู่ (vmlinux+ โมดูล GKI) การแสดง ABI เหล่านี้คือไฟล์ .stg และรายการสัญลักษณ์ อินเทอร์เฟซที่การแสดงผลให้มุมมองเรียกว่าอินเทอร์เฟซโมดูลเคอร์เนล (KMI) คุณสามารถใช้เครื่องมือเพื่อติดตามและลดผลกระทบจากการเปลี่ยนแปลง KMI ได้

เครื่องมือตรวจสอบ ABI ได้รับการพัฒนาใน AOSP และใช้ STG (หรือ libabigail ใน Android 13 และต่ำกว่า) เพื่อสร้างและเปรียบเทียบการแสดง

หน้านี้อธิบายเครื่องมือ กระบวนการรวบรวมและวิเคราะห์การแสดง ABI และการใช้การแสดงดังกล่าวเพื่อมอบความเสถียรให้กับ ABI ในเคอร์เนล หน้านี้ยังมีข้อมูลสำหรับการมีส่วนร่วมในการเปลี่ยนแปลง เคอร์เนลของ Android ด้วย

กระบวนการ

การวิเคราะห์ ABI ของเคอร์เนลต้องใช้หลายขั้นตอน ซึ่งส่วนใหญ่สามารถทำให้เป็นอัตโนมัติได้ ดังนี้

  1. สร้างเคอร์เนลและการแสดง ABI
  2. วิเคราะห์ความแตกต่างของ ABI ระหว่างบิลด์กับข้อมูลอ้างอิง
  3. อัปเดตการแสดง ABI (หากจำเป็น)
  4. ทำงานกับรายการสัญลักษณ์

วิธีการต่อไปนี้ใช้ได้กับเคอร์เนลที่คุณสร้างได้โดยใช้ เครื่องมือเชนที่รองรับ (เช่น เครื่องมือเชน Clang ที่สร้างไว้ล่วงหน้า) repo manifests พร้อมใช้งานสำหรับสาขาเคอร์เนลทั่วไปของ Android ทั้งหมดและสำหรับเคอร์เนลเฉพาะอุปกรณ์หลายรายการ โดยจะยืนยันว่ามีการใช้เครื่องมือที่ถูกต้องเมื่อคุณ สร้างการกระจายเคอร์เนลเพื่อการวิเคราะห์

รายการสัญลักษณ์

KMI ไม่ได้รวมสัญลักษณ์ทั้งหมดในเคอร์เนล หรือแม้แต่สัญลักษณ์ที่ส่งออกทั้งหมดกว่า 30,000 รายการ แต่สัญลักษณ์ที่โมดูลของผู้ให้บริการใช้ได้จะแสดงอย่างชัดเจนในชุดไฟล์รายการสัญลักษณ์ที่ดูแลจัดการแบบสาธารณะใน โครงสร้างเคอร์เนล (gki/{ARCH}/symbols/* หรือ android/abi_gki_{ARCH}_* ใน Android 15 และเวอร์ชันที่ต่ำกว่า) การรวมสัญลักษณ์ทั้งหมดในไฟล์รายการสัญลักษณ์ทั้งหมด จะกำหนดชุดสัญลักษณ์ KMI ที่ถือว่าเสถียร ตัวอย่างไฟล์รายการสัญลักษณ์ คือ gki/aarch64/symbols/db845c ซึ่งประกาศสัญลักษณ์ที่จำเป็นสำหรับ DragonBoard 845c

เฉพาะสัญลักษณ์ที่แสดงในรายการสัญลักษณ์ รวมถึงโครงสร้างและคำจำกัดความที่เกี่ยวข้องเท่านั้นที่จะถือเป็นส่วนหนึ่งของ KMI คุณโพสต์การเปลี่ยนแปลงใน รายการสัญลักษณ์ได้หากไม่มีสัญลักษณ์ที่ต้องการ หลังจากที่อินเทอร์เฟซใหม่ปรากฏใน รายการสัญลักษณ์และเป็นส่วนหนึ่งของคำอธิบาย KMI แล้ว อินเทอร์เฟซเหล่านั้นจะได้รับการดูแลให้มีความเสถียร และต้องไม่ถูกนำออกจากรายการสัญลักษณ์หรือแก้ไขหลังจากที่สาขาถูก ตรึงไว้

แต่ละสาขาเคอร์เนล KMI ของ Android Common Kernel (ACK) จะมีชุดรายการสัญลักษณ์ของตัวเอง ไม่มีการพยายามให้ความเสถียรของ ABI ระหว่างสาขาเคอร์เนล KMI ที่แตกต่างกัน เช่น KMI สำหรับ android12-5.10 จะไม่ขึ้นอยู่กับ KMI สำหรับ android13-5.10

เครื่องมือ ABI ใช้รายการสัญลักษณ์ KMI เพื่อจำกัดอินเทอร์เฟซที่ต้องตรวจสอบเพื่อ ความเสถียร ผู้ให้บริการควรส่งและอัปเดตรายการสัญลักษณ์ของตนเองเพื่อ ยืนยันว่าอินเทอร์เฟซที่ใช้ยังคงมีความเข้ากันได้ของ ABI เช่น หากต้องการดูรายการรายการสัญลักษณ์สำหรับเคอร์เนล android16-6.12 โปรดดูที่ https://android.googlesource.com/kernel/common/+/refs/heads/android16-6.12/gki/aarch64/symbols

รายการสัญลักษณ์ประกอบด้วยสัญลักษณ์ที่รายงานว่าจำเป็นสำหรับผู้ให้บริการหรืออุปกรณ์นั้นๆ รายการที่สมบูรณ์ซึ่งเครื่องมือใช้คือการรวมไฟล์รายการสัญลักษณ์ KMI ทั้งหมด เครื่องมือ ABI จะกำหนดรายละเอียดของแต่ละสัญลักษณ์ ซึ่งรวมถึง ลายเซ็นฟังก์ชันและโครงสร้างข้อมูลที่ซ้อนกัน

เมื่อ KMI หยุดทำงาน จะไม่อนุญาตให้เปลี่ยนแปลงอินเทอร์เฟซ KMI ที่มีอยู่ ซึ่งจะมีความเสถียร อย่างไรก็ตาม ผู้ให้บริการสามารถเพิ่มสัญลักษณ์ลงใน KMI ได้ทุกเมื่อ ตราบใดที่การเพิ่มดังกล่าวไม่ส่งผลต่อความเสถียรของ ABI ที่มีอยู่ ระบบจะรักษาความเสถียรของสัญลักษณ์ที่เพิ่มเข้ามาใหม่ ทันทีที่รายการสัญลักษณ์ KMI อ้างอิงถึง ไม่ควรนำสัญลักษณ์ออกจากรายการสำหรับเคอร์เนล เว้นแต่จะยืนยันได้ว่า ไม่มีอุปกรณ์ใดที่จัดส่งโดยมีสัญลักษณ์นั้นเป็นทรัพยากร Dependency

คุณสร้างรายการสัญลักษณ์ KMI สำหรับอุปกรณ์ได้โดยใช้คำสั่งจากวิธีทำงานกับรายการสัญลักษณ์ พาร์ทเนอร์หลายรายส่งรายการสัญลักษณ์ 1 รายการต่อ ACK แต่ก็ไม่ได้เป็นข้อกำหนดที่เข้มงวด หากช่วยในการบำรุงรักษา คุณสามารถส่งรายการสัญลักษณ์หลายรายการได้

ขยาย KMI

แม้ว่าสัญลักษณ์ KMI และโครงสร้างที่เกี่ยวข้องจะได้รับการดูแลให้มีความเสถียร (หมายความว่า การเปลี่ยนแปลงที่ทำให้อินเทอร์เฟซที่มีเสถียรภาพในเคอร์เนลที่มี KMI ที่หยุดการเปลี่ยนแปลงไม่สามารถ ยอมรับได้) แต่เคอร์เนล GKI ยังคงเปิดให้ขยายได้ เพื่อให้อุปกรณ์ที่จัดส่ง ในภายหลังของปีนี้ไม่จำเป็นต้องกำหนดการขึ้นต่อทั้งหมดก่อนที่ KMI จะ หยุดการเปลี่ยนแปลง หากต้องการขยาย KMI คุณสามารถเพิ่มสัญลักษณ์ใหม่ลงใน KMI สำหรับฟังก์ชันเคอร์เนลใหม่หรือฟังก์ชันเคอร์เนลที่มีอยู่ได้ แม้ว่า KMI จะถูกระงับก็ตาม นอกจากนี้ เราอาจยอมรับแพตช์เคอร์เนลใหม่ หากแพตช์ดังกล่าวไม่ทำให้ KMI เสียหาย

เกี่ยวกับการหยุดทำงานของ KMI

เคอร์เนลมีแหล่งที่มาและไบนารีสร้างขึ้นจากแหล่งที่มาเหล่านั้น สาขาเคอร์เนลที่ ABI ตรวจสอบจะมีตัวแทน ABI ของ GKI ปัจจุบัน ABI (ในรูปแบบของไฟล์ .stg) หลังจากสร้างไบนารี (vmlinux, Image และ โมดูล GKI) แล้ว คุณจะแยกการแสดง ABI ออกจาก ไบนารีได้ การเปลี่ยนแปลงใดๆ ที่ทำกับไฟล์แหล่งที่มาของเคอร์เนลอาจส่งผลต่อไบนารี และส่งผลต่อ .stg ที่แยกออกมาด้วย การวิเคราะห์การปฏิบัติตามข้อกำหนดของ ABI จะเปรียบเทียบไฟล์ที่คอมมิต .stg กับไฟล์ที่แยกจากอาร์ติแฟกต์ของบิลด์ และติดป้ายกำกับ Lint-1 ให้กับการเปลี่ยนแปลงใน Gerrit หากพบความแตกต่างทางความหมาย

จัดการการหยุดทำงานของ ABI

ตัวอย่างเช่น แพตช์ต่อไปนี้ทำให้ ABI เสียหายอย่างเห็นได้ชัด

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.

เมื่อเรียกใช้ ABI ของบิลด์โดยใช้แพตช์นี้ เครื่องมือจะออกโดยมี รหัสข้อผิดพลาดที่ไม่ใช่ 0 และรายงานความแตกต่างของ ABI ที่คล้ายกับตัวอย่างต่อไปนี้

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

ตรวจพบความแตกต่างของ ABI ในเวลาบิลด์

สาเหตุที่พบบ่อยที่สุดของข้อผิดพลาดคือเมื่อไดรเวอร์ใช้สัญลักษณ์ใหม่จาก เคอร์เนลที่ไม่ได้อยู่ในรายการสัญลักษณ์ใดๆ

หากสัญลักษณ์ไม่ได้รวมอยู่ในรายการสัญลักษณ์ คุณต้องตรวจสอบก่อน ว่าได้ส่งออกด้วย EXPORT_SYMBOL_GPL(symbol_name) แล้วจึงอัปเดต รายการสัญลักษณ์และการแสดง ABI ตัวอย่างเช่น การเปลี่ยนแปลงต่อไปนี้จะเพิ่มฟีเจอร์ FS แบบเพิ่มใหม่ไปยังกิ่ง android-12-5.10 ซึ่งรวมถึงการอัปเดตรายการสัญลักษณ์และการแสดง ABI

  • ตัวอย่างการเปลี่ยนแปลงฟีเจอร์อยู่ใน aosp/1345659
  • ตัวอย่างรายการสัญลักษณ์อยู่ใน aosp/1346742
  • ตัวอย่างการเปลี่ยนแปลงการแสดง ABI อยู่ใน aosp/1349377

หากส่งออกสัญลักษณ์ (ไม่ว่าคุณจะเป็นผู้ส่งออกหรือมีการส่งออกก่อนหน้านี้) แต่ไม่มี ไดรเวอร์อื่นใช้สัญลักษณ์ดังกล่าว คุณอาจได้รับข้อผิดพลาดในการสร้างที่คล้ายกับข้อความต่อไปนี้

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

หากต้องการแก้ไข ให้อัปเดตรายการสัญลักษณ์ KMI ทั้งในเคอร์เนลและ ACK (ดูอัปเดตการแสดง ABI) ดูตัวอย่าง การอัปเดตรายการสัญลักษณ์และการแสดง ABI ใน ACK ได้ที่ aosp/1367601

แก้ไขการหยุดทำงานของ ABI ของเคอร์เนล

คุณสามารถจัดการการหยุดทำงานของ ABI ของเคอร์เนลได้โดยปรับโครงสร้างโค้ดเพื่อไม่ให้เปลี่ยน ABI หรืออัปเดตการแสดง ABI ใช้แผนภูมิต่อไปนี้เพื่อกำหนดแนวทางที่ดีที่สุดสำหรับสถานการณ์ของคุณ

โฟลว์ชาร์ตการหยุดทำงานของ ABI

รูปที่ 1 การแก้ไขการหยุดทำงานของ ABI

ปรับโครงสร้างโค้ดเพื่อหลีกเลี่ยงการเปลี่ยนแปลง ABI

พยายามอย่างเต็มที่เพื่อหลีกเลี่ยงการแก้ไข ABI ที่มีอยู่ ในหลายกรณี คุณสามารถ ปรับโครงสร้างโค้ดเพื่อนำการเปลี่ยนแปลงที่ส่งผลต่อ ABI ออกได้

  • การปรับโครงสร้างการเปลี่ยนแปลงฟิลด์ struct หากการเปลี่ยนแปลงแก้ไข ABI สำหรับฟีเจอร์การแก้ไขข้อบกพร่อง ให้เพิ่ม #ifdef รอบฟิลด์ (ในโครงสร้างและแหล่งข้อมูลอ้างอิง) และตรวจสอบว่าได้ปิดใช้ CONFIG ที่ใช้สำหรับ #ifdef สำหรับ defconfig ที่ใช้งานจริงและ gki_defconfig แล้ว ดูตัวอย่างวิธีเพิ่มการกำหนดค่าการแก้ไขข้อบกพร่อง ลงในโครงสร้างโดยไม่ทําให้ ABI เสียหายได้ที่ชุดแพตช์นี้

  • การปรับโครงสร้างฟีเจอร์เพื่อไม่ให้เปลี่ยนแปลงเคอร์เนลหลัก หากต้องเพิ่มฟีเจอร์ใหม่ลงใน ACK เพื่อรองรับโมดูลพาร์ทเนอร์ ให้ลองรีแฟกเตอร์ส่วน ABI ของการเปลี่ยนแปลงเพื่อหลีกเลี่ยงการแก้ไข ABI ของเคอร์เนล ดูตัวอย่างการใช้ ABI ของเคอร์เนลที่มีอยู่เพื่อเพิ่มความสามารถโดยไม่ต้องเปลี่ยน ABI ของเคอร์เนลได้ที่ aosp/1312213

แก้ไข ABI ที่ใช้งานไม่ได้ใน Android Gerrit

หากไม่ได้ตั้งใจทำให้ ABI ของเคอร์เนลใช้งานไม่ได้ คุณจะต้องตรวจสอบ โดยใช้คำแนะนำที่ได้รับจากเครื่องมือตรวจสอบ ABI สาเหตุที่พบบ่อยที่สุด ที่ทำให้เกิดการหยุดทำงานคือโครงสร้างข้อมูลที่เปลี่ยนแปลงและการเปลี่ยนแปลง CRC ของสัญลักษณ์ที่เกี่ยวข้อง หรือเกิดจากการเปลี่ยนแปลงตัวเลือกการกำหนดค่าที่นำไปสู่สาเหตุที่กล่าวมาข้างต้น เริ่มต้นด้วยการแก้ไขปัญหาที่เครื่องมือพบ

คุณสามารถสร้างผลการค้นหา ABI ในเครื่องได้ โปรดดูสร้างเคอร์เนลและการแสดง ABI

เกี่ยวกับป้ายกำกับ Lint-1

หากคุณอัปโหลดการเปลี่ยนแปลงไปยังสาขาที่มี KMI ที่หยุดใช้งานหรือ KMI ที่เสร็จสมบูรณ์แล้ว การเปลี่ยนแปลงจะต้องผ่านการวิเคราะห์การปฏิบัติตามข้อกำหนดและความเข้ากันได้ของ ABI เพื่อให้มั่นใจว่าการเปลี่ยนแปลงในการแสดง ABI จะแสดงถึง ABI จริงและไม่มีความไม่เข้ากัน (การนำสัญลักษณ์ออกหรือการเปลี่ยนแปลงประเภท)

การวิเคราะห์ ABI แต่ละรายการเหล่านี้อาจตั้งป้ายกำกับ Lint-1 และการส่งการเปลี่ยนแปลงที่ถูกบล็อก จนกว่าปัญหาทั้งหมดจะได้รับการแก้ไขหรือมีการลบล้างป้ายกำกับ

อัปเดต ABI ของเคอร์เนล

หากหลีกเลี่ยงการแก้ไข ABI ไม่ได้ คุณต้องใช้การเปลี่ยนแปลงโค้ด การแสดง ABI และรายการสัญลักษณ์กับ ACK หากต้องการให้ Lint นำ -1 ออกและไม่ทำให้ความเข้ากันได้ของ GKI เสียหาย ให้ทำตามขั้นตอนต่อไปนี้

  1. อัปโหลดการเปลี่ยนแปลงโค้ดไปยัง ACK

  2. รอรับ Code-Review +2 สำหรับชุดแพตช์

  3. อัปเดตการแสดง ABI อ้างอิง

  4. ผสานรวมการเปลี่ยนแปลงโค้ดและการเปลี่ยนแปลงการอัปเดต ABI

อัปโหลดการเปลี่ยนแปลงโค้ด ABI ไปยัง ACK

การอัปเดต ACK ABI จะขึ้นอยู่กับประเภทของการเปลี่ยนแปลงที่ทำ

  • หากการเปลี่ยนแปลง ABI เกี่ยวข้องกับฟีเจอร์ที่ส่งผลต่อการทดสอบ CTS หรือ VTS โดยปกติแล้ว คุณสามารถเลือกการเปลี่ยนแปลงเพื่อ ACK ได้ตามเดิม ตัวอย่างเช่น

    • aosp/1289677 จำเป็นต่อการทำงานของเสียง
    • aosp/1295945 จำเป็นต่อการทำงานของ USB
  • หากการเปลี่ยนแปลง ABI เป็นฟีเจอร์ที่แชร์กับ ACK ได้ คุณสามารถเลือกการเปลี่ยนแปลงนั้นเพื่อนำไปใช้กับ ACK ได้โดยตรง ตัวอย่างเช่น การเปลี่ยนแปลงต่อไปนี้ไม่จำเป็นสำหรับการทดสอบ CTS หรือ VTS แต่แชร์กับ ACK ได้

    • aosp/1250412 เป็นการเปลี่ยนแปลงฟีเจอร์ด้านความร้อน
    • aosp/1288857 คือการเปลี่ยนแปลง EXPORT_SYMBOL_GPL
  • หากการเปลี่ยนแปลง ABI ทำให้เกิดฟีเจอร์ใหม่ที่ไม่จำเป็นต้องรวมไว้ใน ACK คุณสามารถแนะนำสัญลักษณ์ไปยัง ACK โดยใช้ Stub ตามที่อธิบายไว้ในส่วนต่อไปนี้

ใช้ Stub สำหรับ ACK

โดยสแต็บจะต้องจำเป็นสำหรับการเปลี่ยนแปลงเคอร์เนลหลักเท่านั้น ซึ่งไม่เป็นประโยชน์ต่อ ACK เช่น การเปลี่ยนแปลงด้านประสิทธิภาพและพลังงาน รายการต่อไปนี้แสดงรายละเอียดตัวอย่าง ของ Stub และ Cherry-Pick บางส่วนใน ACK สำหรับ GKI

  • ฟีเจอร์ Core-isolate (aosp/1284493) ความสามารถใน ACK ไม่จำเป็น แต่ต้องมีสัญลักษณ์ใน ACK เพื่อให้โมดูลใช้สัญลักษณ์เหล่านี้ได้

  • สัญลักษณ์ตัวยึดตำแหน่งสำหรับโมดูลผู้ให้บริการ (aosp/1288860)

  • การเลือกเฉพาะ ABI ของmmฟีเจอร์การติดตามเหตุการณ์ (aosp/1288454) ต่อกระบวนการ เราได้เลือกแพตช์ต้นฉบับเพื่อ ACK แล้วตัดทอนให้เหลือเฉพาะ การเปลี่ยนแปลงที่จำเป็นเพื่อแก้ไขความแตกต่างของ ABI สำหรับ task_struct และ mm_event_count นอกจากนี้ แพตช์นี้ยังอัปเดต enum mm_event_type ให้มีสมาชิกสุดท้ายด้วย

  • การเลือกบางส่วนของการเปลี่ยนแปลง ABI โครงสร้างความร้อนที่ต้องใช้มากกว่าแค่ การเพิ่มฟิลด์ ABI ใหม่

    • แพตช์ aosp/1255544 แก้ไขความแตกต่างของ ABI ระหว่างเคอร์เนลของพาร์ทเนอร์กับ ACK

    • แพตช์ aosp/1291018 แก้ไขปัญหาด้านฟังก์ชันที่พบระหว่างการทดสอบ GKI ของแพตช์ก่อนหน้า การแก้ไขนี้รวมถึงการเริ่มต้นโครงสร้างพารามิเตอร์ของเซ็นเซอร์เพื่อลงทะเบียน โซนความร้อนหลายโซนกับเซ็นเซอร์เดียว

  • CONFIG_NL80211_TESTMODE การเปลี่ยนแปลง ABI (aosp/1344321) แพตช์นี้ได้เพิ่มการเปลี่ยนแปลงโครงสร้างที่จำเป็นสำหรับ ABI และตรวจสอบว่าฟิลด์เพิ่มเติมไม่ได้ทำให้เกิดความแตกต่างในการทำงาน ซึ่งช่วยให้พาร์ทเนอร์สามารถรวม CONFIG_NL80211_TESTMODE ไว้ในเคอร์เนลการผลิตและยังคงรักษาการปฏิบัติตามข้อกำหนดของ GKI ได้

บังคับใช้ KMI ที่รันไทม์

เคอร์เนล GKI ใช้ตัวเลือกการกำหนดค่า TRIM_UNUSED_KSYMS=y และ UNUSED_KSYMS_WHITELIST=<union of all symbol lists> ซึ่งจำกัดสัญลักษณ์ที่ส่งออก (เช่น สัญลักษณ์ที่ส่งออกโดยใช้ EXPORT_SYMBOL_GPL()) ให้เฉพาะสัญลักษณ์ที่แสดงใน รายการสัญลักษณ์ ส่วนสัญลักษณ์อื่นๆ ทั้งหมดจะไม่ได้ส่งออก และระบบจะปฏิเสธการโหลดโมดูลที่ต้องใช้สัญลักษณ์ที่ไม่ได้ส่งออก ข้อจำกัดนี้จะบังคับใช้ในเวลาที่สร้างและ มีการแจ้งว่าไม่มีรายการ

เพื่อวัตถุประสงค์ในการพัฒนา คุณสามารถใช้บิลด์เคอร์เนล GKI ที่ไม่มีการ ตัดแต่งสัญลักษณ์ (หมายความว่าสามารถใช้สัญลักษณ์ที่มักจะส่งออกทั้งหมดได้) หากต้องการค้นหา บิลด์เหล่านี้ ให้มองหาบิลด์ kernel_debug_aarch64ใน ci.android.com

บังคับใช้ KMI โดยใช้การกำหนดเวอร์ชันของโมดูล

เคอร์เนล Generic Kernel Image (GKI) ใช้การกำหนดเวอร์ชันของโมดูล (CONFIG_MODVERSIONS) เป็นมาตรการเพิ่มเติมในการบังคับใช้การปฏิบัติตามข้อกำหนด KMI ที่ รันไทม์ การกำหนดเวอร์ชันของโมดูลอาจทำให้การตรวจสอบความซ้ำซ้อนแบบวนรอบ (CRC) ไม่ตรงกัน ล้มเหลวในเวลาที่โหลดโมดูล หาก KMI ที่คาดไว้ของโมดูลไม่ตรงกับ vmlinux KMI ตัวอย่างเช่น ข้อผิดพลาดทั่วไปต่อไปนี้เกิดขึ้นในเวลาโหลดโมดูลเนื่องจาก CRC ไม่ตรงกันสำหรับสัญลักษณ์ 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 ''

การใช้การกำหนดเวอร์ชันโมดูล

การกำหนดเวอร์ชันของโมดูลมีประโยชน์ด้วยเหตุผลต่อไปนี้

  • การกำหนดเวอร์ชันของโมดูลจะตรวจจับการเปลี่ยนแปลงในระดับการมองเห็นของโครงสร้างข้อมูล หากโมดูล เปลี่ยนโครงสร้างข้อมูลแบบทึบ นั่นคือโครงสร้างข้อมูลที่ไม่ได้เป็นส่วนหนึ่งของ KMI โมดูลจะหยุดทำงานหลังจากการเปลี่ยนแปลงโครงสร้างในอนาคต

    ตัวอย่างเช่น ลองพิจารณาฟิลด์ fwnode ใน struct device ฟิลด์นี้ต้องไม่โปร่งใสต่อโมดูลเพื่อไม่ให้โมดูลทำการเปลี่ยนแปลงฟิลด์ของ device->fw_node หรือคาดเดาขนาดของฟิลด์ได้

    อย่างไรก็ตาม หากโมดูลมี <linux/fwnode.h> (ทั้งทางตรงและทางอ้อม) ฟิลด์ fwnode ใน struct device จะไม่ทึบแสงอีกต่อไป จากนั้นโมดูลจะทำการเปลี่ยนแปลงใน device->fwnode->dev หรือ device->fwnode->ops ได้ สถานการณ์นี้มีปัญหาด้วยเหตุผลหลายประการ ดังนี้

    • ซึ่งอาจทำให้การคาดการณ์ของโค้ดเคอร์เนลหลักเกี่ยวกับโครงสร้างข้อมูลภายใน ไม่ถูกต้อง

    • หากการอัปเดตเคอร์เนลในอนาคตเปลี่ยน struct fwnode_handle (ประเภทข้อมูลของ fwnode) โมดูลจะใช้กับเคอร์เนลใหม่ไม่ได้อีกต่อไป นอกจากนี้ stgdiff จะไม่แสดงความแตกต่างใดๆ เนื่องจากโมดูลจะทําลาย KMI โดยการจัดการโครงสร้างข้อมูลภายในโดยตรงในลักษณะที่ ไม่สามารถจับได้โดยการตรวจสอบการแสดงไบนารีเท่านั้น

  • โมดูลปัจจุบันจะถือว่าไม่รองรับ KMI เมื่อมีการโหลดในภายหลัง โดยเคอร์เนลใหม่ที่ไม่รองรับ การกำหนดเวอร์ชันของโมดูลจะเพิ่มการตรวจสอบรันไทม์เพื่อ หลีกเลี่ยงการโหลดโมดูลที่ไม่ใช่ KMI โดยไม่ตั้งใจด้วยเคอร์เนล การตรวจสอบนี้ช่วยป้องกันปัญหาที่รันไทม์ซึ่งแก้ไขข้อบกพร่องได้ยากและเคอร์เนลขัดข้องที่อาจ เกิดจากความไม่เข้ากันใน KMI ที่ตรวจไม่พบ

การเปิดใช้การควบคุมเวอร์ชันของโมดูลจะช่วยป้องกันปัญหาเหล่านี้ทั้งหมด

ตรวจสอบ CRC ที่ไม่ตรงกันโดยไม่ต้องบูตอุปกรณ์

stgdiff จะเปรียบเทียบและรายงานความไม่ตรงกันของ CRC ระหว่างเคอร์เนลพร้อมกับความแตกต่างอื่นๆ ของ ABI

นอกจากนี้ การสร้างเคอร์เนลแบบเต็มที่เปิดใช้ CONFIG_MODVERSIONS จะสร้างไฟล์ Module.symvers เป็นส่วนหนึ่งของกระบวนการสร้างปกติ ไฟล์นี้มีบรรทัดเดียวสำหรับทุกสัญลักษณ์ที่เคอร์เนล (vmlinux) และโมดูลส่งออก แต่ละบรรทัดประกอบด้วยค่า CRC, ชื่อสัญลักษณ์, เนมสเปซของสัญลักษณ์, vmlinux หรือ ชื่อโมดูลที่ส่งออกสัญลักษณ์ และประเภทการส่งออก (เช่น EXPORT_SYMBOL กับ EXPORT_SYMBOL_GPL)

คุณสามารถเปรียบเทียบไฟล์ Module.symvers ระหว่างบิลด์ GKI กับบิลด์ของคุณ เพื่อตรวจสอบความแตกต่างของ CRC ในสัญลักษณ์ที่ส่งออกโดย vmlinux หากค่า CRC ในสัญลักษณ์ใดก็ตามที่ส่งออกโดย vmlinux และแตกต่างกัน และโมดูลที่คุณโหลดในอุปกรณ์ใช้สัญลักษณ์ดังกล่าว โมดูลจะไม่โหลด

หากไม่มีอาร์ติแฟกต์การสร้างทั้งหมด แต่มีไฟล์ vmlinux ของ เคอร์เนล GKI และเคอร์เนลของคุณ คุณสามารถเปรียบเทียบค่า CRC สำหรับสัญลักษณ์ ที่เฉพาะเจาะจงได้โดยการเรียกใช้คำสั่งต่อไปนี้ในทั้ง 2 เคอร์เนลและเปรียบเทียบ เอาต์พุต

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

ตัวอย่างเช่น คำสั่งต่อไปนี้จะตรวจสอบค่า CRC สำหรับสัญลักษณ์ module_layout

nm vmlinux | grep __crc_module_layout
0000000008663742 A __crc_module_layout

แก้ไข CRC ที่ไม่ตรงกัน

ทำตามขั้นตอนต่อไปนี้เพื่อแก้ไข CRC ไม่ตรงกันเมื่อโหลดโมดูล

  1. สร้างเคอร์เนล GKI และเคอร์เนลของอุปกรณ์โดยใช้--kbuild_symtypes ตัวเลือกตามที่แสดงในคำสั่งต่อไปนี้

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

    คำสั่งนี้จะสร้างไฟล์ .symtypes สำหรับไฟล์ .o แต่ละไฟล์ ดูรายละเอียดได้ที่ KBUILD_SYMTYPES ใน Kleaf

    สำหรับ Android 13 และต่ำกว่า ให้สร้างเคอร์เนล GKI และเคอร์เนลของอุปกรณ์โดยการเพิ่ม KBUILD_SYMTYPES=1 ไว้หน้าคำสั่งที่คุณ ใช้สร้างเคอร์เนล ดังที่แสดงในคำสั่งต่อไปนี้

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

    เมื่อใช้ build_abi.sh, ระบบจะตั้งค่าสถานะ KBUILD_SYMTYPES=1 โดยปริยาย อยู่แล้ว

  2. ค้นหาไฟล์ .c ที่ส่งออกสัญลักษณ์ที่มี CRC ไม่ตรงกันโดยใช้คำสั่งต่อไปนี้

    git -C common grep EXPORT_SYMBOL.*module_layout
    kernel/module/version.c:EXPORT_SYMBOL(module_layout);
  3. ไฟล์ .c มีไฟล์ .symtypes ที่เกี่ยวข้องใน GKI และ อาร์ติแฟกต์การสร้างเคอร์เนลของอุปกรณ์ ค้นหาไฟล์ .symtypes โดยใช้คำสั่งต่อไปนี้

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

    ใน Android 13 และต่ำกว่า เมื่อใช้สคริปต์บิลด์เดิม ตำแหน่งน่าจะอยู่ที่ out/$BRANCH/common หรือ out_abi/$BRANCH/common

    .symtypesไฟล์แต่ละไฟล์เป็นไฟล์ข้อความธรรมดาที่ประกอบด้วยประเภทและคำอธิบายสัญลักษณ์

    • แต่ละบรรทัดจะมีรูปแบบ key description โดยคำอธิบายสามารถ อ้างอิงคีย์อื่นๆ ในไฟล์เดียวกันได้

    • คีย์ เช่น [s|u|e|t]#foo จะอ้างอิงถึง [struct|union|enum|typedef] foo เช่น

      t#bool typedef _Bool bool
      
    • คีย์ที่ไม่มีคำนำหน้า x# จะเป็นเพียงชื่อสัญลักษณ์ เช่น

      find_module s#module * find_module ( const char * )
      
  4. เปรียบเทียบไฟล์ทั้ง 2 ไฟล์และแก้ไขความแตกต่างทั้งหมด

เราขอแนะนำให้สร้าง symtypes ด้วยบิลด์ก่อนการเปลี่ยนแปลงที่ทำให้เกิดปัญหา และในการเปลี่ยนแปลงที่ทำให้เกิดปัญหา การบันทึกไฟล์ทั้งหมดหมายความว่าคุณจะเปรียบเทียบไฟล์หลายไฟล์พร้อมกันได้

ตัวอย่างเช่น

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

หรือจะเปรียบเทียบเฉพาะไฟล์ที่สนใจก็ได้

กรณีที่ 1: ความแตกต่างเนื่องจากระดับการมองเห็นของประเภทข้อมูล

#include ใหม่สามารถดึงคำจำกัดความประเภทใหม่ (เช่น ของ struct foo) ลงในไฟล์ต้นฉบับได้ ในกรณีเหล่านี้ คำอธิบายในไฟล์ .symtypes ที่เกี่ยวข้อง จะเปลี่ยนจาก structure_type foo { } ที่ว่างเปล่าเป็นคำจำกัดความแบบเต็ม

ซึ่งจะส่งผลต่อ CRC ทั้งหมดของสัญลักษณ์ทั้งหมดในไฟล์ .symtypes ซึ่งคำอธิบาย ขึ้นอยู่กับคำจำกัดความของประเภทโดยตรงหรือโดยอ้อม

เช่น การเพิ่มบรรทัดต่อไปนี้ลงในไฟล์ include/linux/device.h ในเคอร์เนลจะทำให้เกิดความไม่ตรงกันของ CRC ซึ่งหนึ่งในนั้น คือสำหรับ module_layout()

 #include <linux/fwnode.h>

การเปรียบเทียบ module/version.symtypes สำหรับสัญลักษณ์นั้นจะแสดงความแตกต่างต่อไปนี้

 $ 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 ] ; }
  ...

หากเคอร์เนล GKI มีคำจำกัดความประเภทแบบเต็ม แต่เคอร์เนลของคุณไม่มีคำจำกัดความดังกล่าว (ไม่น่าเป็นไปได้) ให้ผสานรวม Android Common Kernel เวอร์ชันล่าสุดเข้ากับเคอร์เนลของคุณเพื่อให้ คุณใช้ฐานเคอร์เนล GKI เวอร์ชันล่าสุด

ในกรณีส่วนใหญ่ เคอร์เนล GKI จะไม่มีคำจำกัดความประเภทแบบเต็มใน .symtypes แต่เคอร์เนลของคุณมีเนื่องจากมีคำสั่ง #include เพิ่มเติม

ความละเอียดสำหรับ Android 16 ขึ้นไป

ตรวจสอบว่าไฟล์ต้นทางที่ได้รับผลกระทบมีส่วนหัวการคงที่ของ KABI ของ Android

#include <linux/android_kabi.h>

สำหรับแต่ละประเภทที่ได้รับผลกระทบ ให้เพิ่ม ANDROID_KABI_DECLONLY(name); ที่ขอบเขตส่วนกลางลงใน ไฟล์ต้นฉบับที่ได้รับผลกระทบ

เช่น หากsymtypes Diff เป็นดังนี้

--- 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)

จากนั้นปัญหาคือตอนนี้ struct ubuf_info มีคำจำกัดความที่สมบูรณ์ใน symtypes วิธีแก้ปัญหาคือการเพิ่มบรรทัดใน drivers/android/vendor_hooks.c ดังนี้

ANDROID_KABI_DECLONLY(ubuf_info);

ซึ่งจะสั่งให้ gendwarfksyms ถือว่าประเภทที่ระบุชื่อเป็นประเภทที่ไม่ได้กำหนดไว้ในไฟล์

ความเป็นไปได้ที่ซับซ้อนกว่านั้นคือ #include ใหม่เองอยู่ในไฟล์ส่วนหัว ในกรณีนี้ คุณอาจต้องกระจายชุดการเรียกใช้มาโคร ANDROID_KABI_DECLONLY ที่แตกต่างกันในไฟล์ต้นฉบับที่ดึงคำจำกัดความของประเภทเพิ่มเติมโดยอ้อม เนื่องจากบางไฟล์อาจมีคำจำกัดความของประเภทอยู่แล้ว

วางการเรียกใช้มาโครดังกล่าวไว้ใกล้กับจุดเริ่มต้นของไฟล์ต้นฉบับเพื่อให้อ่านง่าย

ความละเอียดสำหรับ Android 15 และต่ำกว่า

โดยส่วนใหญ่แล้ว วิธีแก้ปัญหาคือการซ่อน #include ใหม่จาก genksyms

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

หรือหากต้องการระบุ #include ที่ทำให้เกิดความแตกต่าง ให้ทำตามขั้นตอนต่อไปนี้

  1. เปิดไฟล์ส่วนหัวที่กำหนดสัญลักษณ์หรือประเภทข้อมูลที่มีความแตกต่างนี้ เช่น แก้ไข include/linux/fwnode.h สำหรับ struct fwnode_handle

  2. เพิ่มโค้ดต่อไปนี้ที่ด้านบนของไฟล์ส่วนหัว

    #ifdef CRC_CATCH
    #error "Included from here"
    #endif
    
  3. ในไฟล์ .c ของโมดูลที่มี CRC ไม่ตรงกัน ให้เพิ่ม รายการต่อไปนี้เป็นบรรทัดแรกก่อนบรรทัด #include ใดๆ

    #define CRC_CATCH 1
    
  4. คอมไพล์โมดูล ข้อผิดพลาดที่เกิดขึ้นในเวลาบิลด์จะแสดงห่วงโซ่ของ ไฟล์ส่วนหัว #include ที่ทำให้เกิด CRC ไม่ตรงกันนี้ เช่น

    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"
    

    ลิงก์หนึ่งในเชนของ #include เกิดจากการเปลี่ยนแปลงในเคอร์เนลของคุณ ซึ่งไม่มีในเคอร์เนล GKI

กรณีที่ 2: ความแตกต่างเนื่องจากการเปลี่ยนแปลงประเภทข้อมูล

หาก CRC ไม่ตรงกันสำหรับสัญลักษณ์หรือประเภทข้อมูลไม่ได้เกิดจากความแตกต่างใน ระดับการมองเห็น แสดงว่าเกิดจากการเปลี่ยนแปลงจริง (การเพิ่ม การนำออก หรือการเปลี่ยนแปลง) ใน ประเภทข้อมูลนั้นๆ

ตัวอย่างเช่น การเปลี่ยนแปลงต่อไปนี้ในเคอร์เนลจะทำให้เกิดความไม่ตรงกันของ CRC หลายรายการ เนื่องจากสัญลักษณ์จำนวนมากได้รับผลกระทบทางอ้อมจากการเปลี่ยนแปลงประเภทนี้

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);

CRC ที่ไม่ตรงกัน 1 รายการคือ devm_of_platform_populate()

หากคุณเปรียบเทียบไฟล์ .symtypes สำหรับสัญลักษณ์นั้น อาจมีลักษณะดังนี้

 $ 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 * ) ; ...

หากต้องการระบุประเภทที่เปลี่ยนแปลง ให้ทำตามขั้นตอนต่อไปนี้

  1. ค้นหาคำจำกัดความของสัญลักษณ์ในซอร์สโค้ด (มักอยู่ในไฟล์ .h)

    • หากต้องการดูความแตกต่างของสัญลักษณ์ระหว่างเคอร์เนลของคุณกับเคอร์เนล GKI ให้ค้นหาคอมมิตโดยเรียกใช้คำสั่งต่อไปนี้
    git blame
    • สำหรับสัญลักษณ์ที่ถูกลบ (ในกรณีที่สัญลักษณ์ถูกลบในแผนผังหนึ่งและคุณต้องการลบสัญลักษณ์นั้นในแผนผังอื่นด้วย) คุณต้องค้นหาการเปลี่ยนแปลงที่ลบบรรทัด ใช้คำสั่งต่อไปนี้ในทรีที่บรรทัด ถูกลบ
    git log -S "copy paste of deleted line/word" -- <file where it was deleted>
  2. ตรวจสอบรายการคอมมิตที่แสดงเพื่อค้นหาการเปลี่ยนแปลงหรือการลบ คอมมิตแรกอาจเป็นคอมมิตที่คุณกำลังค้นหา หากไม่พบ ให้เลื่อนดูรายการจนกว่าจะพบคอมมิต

  3. หลังจากระบุการคอมมิตแล้ว ให้ย้อนกลับในเคอร์เนลหรืออัปเดต เพื่อไม่ให้มีการเปลี่ยนแปลง CRC แล้วอัปโหลดไปยัง ACK และรับการผสาน การเปลี่ยนแปลง ABI ที่เหลือแต่ละรายการจะต้องได้รับการตรวจสอบเพื่อความปลอดภัย และหากจำเป็น จะบันทึกการเปลี่ยนแปลงที่อนุญาตได้

ต้องการใช้ระยะเว้นที่มีอยู่

โครงสร้างบางอย่างใน GKI มีการเพิ่มพื้นที่ว่างเพื่อให้ขยายได้โดยไม่ทำให้โมดูลของผู้ให้บริการที่มีอยู่เสียหาย หากคอมมิตต้นทาง (เช่น) เพิ่มสมาชิกไปยัง โครงสร้างดังกล่าว คุณอาจเปลี่ยนให้ใช้ การเว้นวรรคแทนได้ จากนั้นระบบจะซ่อนการเปลี่ยนแปลงนี้จากการคำนวณ CRC

มาโครที่ได้มาตรฐานและสร้างเอกสารด้วยตนเอง ANDROID_KABI_RESERVE จะจองพื้นที่ (ที่สอดคล้องกัน) มูลค่า u64 โดยใช้แทนการประกาศความเป็นสมาชิก

เช่น

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

สามารถใช้ Padding ได้โดยไม่ส่งผลต่อ CRC ของสัญลักษณ์ด้วย ANDROID_KABI_USE (หรือ ANDROID_KABI_USE2 หรือตัวแปรอื่นๆ ที่อาจกำหนดไว้)

สมาชิก sekret จะพร้อมใช้งานราวกับว่ามีการประกาศโดยตรง แต่มาโครจะขยายเป็นสมาชิกสหภาพที่ไม่ระบุชื่อซึ่งมี sekret รวมถึง สิ่งต่างๆ ที่ gendwarfksyms ใช้เพื่อรักษาความเสถียรของ symtype

struct data {
        u64 handle;
        ANDROID_KABI_USE(1, void *sekret);
        ANDROID_KABI_RESERVE(2);
};
ความละเอียดสำหรับ Android 16 ขึ้นไป

gendwarfksyms จะคำนวณ CRC โดยใช้ข้อมูลการแก้ไขข้อบกพร่อง DWARF จึงรองรับทั้งประเภท C และ Rust ความละเอียดจะแตกต่างกันไปตามประเภท ของการเปลี่ยนแปลง ต่อไปนี้เป็นตัวอย่างบางส่วน

ผู้แจงนับใหม่หรือผู้แจงนับที่แก้ไข

บางครั้งระบบจะเพิ่มค่าแจงนับใหม่ และบางครั้งค่าแจงนับ MAX หรือค่าที่คล้ายกัน ก็จะได้รับผลกระทบด้วย การเปลี่ยนแปลงเหล่านี้จะปลอดภัยหากไม่ได้ "หลุด" ออกจาก GKI หรือหากเรามั่นใจว่าโมดูลของผู้ให้บริการจะไม่สนใจค่าของโมดูล

เช่น

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

การเพิ่ม TRY_HARDER และการเปลี่ยนเป็น OUTCOME_LIMIT สามารถซ่อนจากการคำนวณ CRC ด้วยการเรียกใช้มาโครที่ขอบเขตส่วนกลางได้โดยทำดังนี้

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

เพื่อให้อ่านง่าย ให้วางไว้หลังคำจำกัดความของ enum

สมาชิกโครงสร้างใหม่ที่เข้ามาแทนที่ตำแหน่งเดิม

เนื่องจากการจัดแนว จะมีไบต์ที่ไม่ได้ใช้ระหว่าง urgent กับ scratch

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

การเพิ่ม retry จะไม่ส่งผลต่อออฟเซ็ตของสมาชิกที่มีอยู่หรือขนาดของโครงสร้าง แต่การดำเนินการนี้อาจส่งผลต่อ CRC ของสัญลักษณ์หรือการแสดง ABI หรือทั้ง 2 อย่าง

การดำเนินการนี้จะซ่อนจาก CRC Calculation

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

สมาชิก retry จะพร้อมใช้งานราวกับว่ามีการประกาศโดยตรง แต่มาโครจะขยายเป็นสมาชิกสหภาพที่ไม่ระบุชื่อซึ่งมี retry รวมถึง สิ่งต่างๆ ที่ gendwarfksyms ใช้เพื่อรักษาความเสถียรของ symtype

การขยายโครงสร้างด้วยสมาชิกใหม่

บางครั้งระบบจะเพิ่มสมาชิกไว้ท้ายโครงสร้าง การดำเนินการนี้จะไม่ส่งผลต่อ ออฟเซ็ตของสมาชิกที่มีอยู่ หรือส่งผลต่อผู้ใช้ที่มีอยู่ของโครงสร้างที่ เข้าถึงได้โดยใช้พอยน์เตอร์เท่านั้น ขนาดของโครงสร้างมีผลต่อ CRC และการเปลี่ยนแปลงใน ส่วนนี้สามารถระงับได้ด้วยการเรียกใช้มาโครเพิ่มเติมที่ขอบเขตส่วนกลาง ดังนี้

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

ANDROID_KABI_BYTE_SIZE(data, 16);

เพื่อให้อ่านง่าย ให้วางไว้หลังคำจำกัดความ struct

การเปลี่ยนแปลงอื่นๆ ทั้งหมดในประเภทหรือประเภทของสัญลักษณ์

ในบางครั้งอาจมีการเปลี่ยนแปลงที่ไม่ได้อยู่ในหมวดหมู่ใดหมวดหมู่หนึ่งก่อนหน้า ซึ่งส่งผลให้เกิดการเปลี่ยนแปลง CRC ที่ไม่สามารถระงับได้โดยใช้ มาโครก่อนหน้า

ในกรณีเหล่านี้ คุณสามารถระบุsymtypesคำอธิบายเดิมของประเภทหรือสัญลักษณ์ANDROID_KABI_TYPE_STRINGพร้อมการเรียกใช้ที่ขอบเขตส่วนกลางได้

struct data {
        /* extensive changes */
};

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

เพื่อให้อ่านง่าย ให้วางไว้หลังคำจำกัดความประเภทหรือสัญลักษณ์

ความละเอียดสำหรับ Android 15 และต่ำกว่า

การเปลี่ยนแปลงประเภทและประเภทสัญลักษณ์ต้องซ่อนจาก genksyms ซึ่งทำได้โดย ควบคุมการประมวลผลล่วงหน้าด้วย __GENKSYMS__

การแปลงโค้ดที่กำหนดเองสามารถแสดงได้ด้วยวิธีนี้

ตัวอย่างเช่น หากต้องการซ่อนสมาชิกใหม่ที่เข้ามาแทนที่ตำแหน่งที่ว่างในโครงสร้างที่มีอยู่ ให้ทำดังนี้

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