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

คุณสามารถใช้เครื่องมือตรวจสอบ Application Binary Interface (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 คุณไม่ควรนำสัญลักษณ์ออกจากรายการสำหรับเคอร์เนล เว้นแต่จะสามารถยืนยันได้ว่าไม่มีอุปกรณ์ใดที่เคยจัดส่งโดยอาศัยสัญลักษณ์นั้น

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

ขยาย KMI

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

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

เคอร์เนลมีซอร์ส และไบนารีจะสร้างขึ้นจากซอร์สเหล่านั้น สาขาเคอร์เนลที่ตรวจสอบ ABI จะรวมการแสดง ABI ของ GKI ABI ปัจจุบัน (ในรูปแบบไฟล์ .stg) หลังจากสร้างไบนารี (vmlinux, Image และข้อบังคับ GKI ทั้งหมด) แล้ว คุณจะดึงข้อมูลการนำเสนอ ABI ออกจากไบนารีได้ การเปลี่ยนแปลงใดๆ ในไฟล์ซอร์สเคอร์เนลอาจส่งผลต่อไบนารีและส่งผลต่อ .stg ที่ดึงออกมาด้วย เครื่องมือวิเคราะห์ AbiAnalyzer จะเปรียบเทียบไฟล์ .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 ที่หยุดทำงานหรือเสร็จสมบูรณ์แล้ว การเปลี่ยนแปลงต้องผ่าน AbiAnalyzer เพื่อให้แน่ใจว่าการเปลี่ยนแปลงจะไม่ส่งผลต่อ ABI ที่เสถียรในลักษณะที่เข้ากันไม่ได้ ในระหว่างกระบวนการนี้ AbiAnalyzer จะค้นหารายงาน ABI ที่สร้างขึ้นระหว่างการสร้าง (การสร้างแบบขยายที่ทําการสร้างตามปกติ แล้วดึงข้อมูล ABI และทําการเปรียบเทียบในบางขั้นตอน

หาก AbiAnalyzer พบรายงานที่ไม่ใช่ค่าว่าง ระบบจะตั้งค่าป้ายกำกับ Lint-1 และระบบจะบล็อกการเปลี่ยนแปลงไม่ให้ส่งจนกว่าจะแก้ไขได้ หรือจนกว่าชุดแพตช์จะได้รับป้ายกำกับ 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 โดยทั่วไปแล้ว คุณสามารถเลือกการเปลี่ยนแปลงเพื่อยอมรับได้ตามต้องการ เช่น

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

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

ใช้สแต็บสำหรับ ACK

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

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

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

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

  • การเปลี่ยนแปลง 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 ที่รันไทม์ การกำหนดเวอร์ชันของโมดูลอาจทําให้การตรวจสอบ redundancy แบบวนซ้ำ (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 เป็นส่วนหนึ่งของกระบวนการสร้างตามปกติ ไฟล์นี้มี 1 บรรทัดสำหรับสัญลักษณ์ทั้งหมดที่เคอร์เนล (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, ระบบจะตั้งค่า Flag 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 เวอร์ชันล่าสุดเข้ากับเคอร์เนลของคุณเพื่อให้ใช้ฐานเคอร์เนล GKI เวอร์ชันล่าสุด

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

การแก้ปัญหาสำหรับ Android 16 ขึ้นไป

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

#include <linux/android_kabi.h>

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

เช่น หากความแตกต่างของ symtypes เป็นดังนี้

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

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

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

struct data {
        u64 handle;
        ANDROID_KABI_USE(1, void *sekret);
        ANDROID_KABI_RESERVE(2);
};
การแก้ปัญหาสำหรับ Android 16 ขึ้นไป

CRC จะคํานวณโดย gendwarfksyms ซึ่งใช้ข้อมูลการแก้ไขข้อบกพร่อง 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

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

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

โครงสร้างที่ขยายออกไปโดยมีสมาชิกใหม่

บางครั้งสมาชิกจะเพิ่มไว้ท้ายโครงสร้าง ซึ่งจะไม่ส่งผลต่อออฟเซตของสมาชิกที่มีอยู่ หรือส่งผลต่อผู้ใช้ปัจจุบันของโครงสร้างที่เข้าถึงโครงสร้างดังกล่าวโดยใช้พอยน์เตอร์เท่านั้น ขนาดของโครงสร้างจะส่งผลต่อ 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;
};