คุณสามารถใช้เครื่องมือตรวจสอบ 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 ของเคอร์เนลต้องดำเนินการหลายขั้นตอน ซึ่งส่วนใหญ่สามารถทําแบบอัตโนมัติได้ ดังนี้
- สร้างเคอร์เนลและการแสดง ABI ของเคิร์นเนล
- วิเคราะห์ความแตกต่างของ ABI ระหว่างบิลด์กับข้อมูลอ้างอิง
- อัปเดตการแสดง ABI (หากจำเป็น)
- ทำงานกับรายการสัญลักษณ์
วิธีการต่อไปนี้ใช้ได้กับเคอร์เนลที่คุณสร้างได้โดยใช้ชุดเครื่องมือที่รองรับ (เช่น ชุดเครื่องมือ 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 ใช้แผนภูมิต่อไปนี้เพื่อพิจารณาแนวทางที่ดีที่สุดสำหรับสถานการณ์ของคุณ
รูปที่ 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 ใช้งานไม่ได้ ให้ทำตามขั้นตอนต่อไปนี้
รอรับสถานะ Code-Review +2 สำหรับชุดแพตช์
ผสานการเปลี่ยนแปลงโค้ดเข้ากับการเปลี่ยนแปลงการอัปเดต 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 ไม่ตรงกันเมื่อโหลดโมดูล
สร้างเคอร์เนล 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,
ระบบจะตั้งค่า FlagKBUILD_SYMTYPES=1
โดยปริยายค้นหาไฟล์
.c
ที่ส่งออกสัญลักษณ์ที่มี CRC ไม่ตรงกันโดยใช้คำสั่งต่อไปนี้git -C common grep EXPORT_SYMBOL.*module_layout kernel/module/version.c:EXPORT_SYMBOL(module_layout);
ไฟล์
.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 * )
เปรียบเทียบไฟล์ 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
ที่ทำให้เกิดความแตกต่าง ให้ทำตามขั้นตอนต่อไปนี้
เปิดไฟล์ส่วนหัวที่กําหนดสัญลักษณ์หรือประเภทข้อมูลที่มีความแตกต่างนี้ เช่น แก้ไข
include/linux/fwnode.h
สำหรับstruct fwnode_handle
เพิ่มโค้ดต่อไปนี้ที่ด้านบนของไฟล์ส่วนหัว
#ifdef CRC_CATCH #error "Included from here" #endif
ในไฟล์
.c
ของโมดูลที่มี CRC ไม่ตรงกัน ให้เพิ่มบรรทัดต่อไปนี้เป็นบรรทัดแรกก่อนบรรทัด#include
#define CRC_CATCH 1
คอมไพล์โมดูล ข้อผิดพลาดที่เกิดขึ้นขณะสร้างจะแสดงลําดับไฟล์ส่วนหัว
#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 * ) ; ...
หากต้องการระบุประเภทที่เปลี่ยนแปลง ให้ทําตามขั้นตอนต่อไปนี้
ค้นหาคําจํากัดความของสัญลักษณ์ในซอร์สโค้ด (มักจะอยู่ในไฟล์
.h
)- หากต้องการดูความแตกต่างของสัญลักษณ์ระหว่างเคอร์เนลของคุณกับเคอร์เนล GKI ให้ค้นหาการคอมมิตโดยเรียกใช้คำสั่งต่อไปนี้
git blame
- สําหรับสัญลักษณ์ที่ลบไปแล้ว (ซึ่งมีการลบสัญลักษณ์ในแผนภูมิ และคุณต้องการลบสัญลักษณ์นั้นในแผนภูมิอื่นด้วย) คุณต้องค้นหาการเปลี่ยนแปลงที่ลบบรรทัดนั้น ใช้คำสั่งต่อไปนี้ในต้นไม้ที่มีการลบบรรทัด
git log -S "copy paste of deleted line/word" -- <file where it was deleted>
ตรวจสอบรายการคอมมิตที่แสดงเพื่อหาการเปลี่ยนแปลงหรือการลบ การคอมมิตแรกอาจเป็นรายการที่คุณค้นหาอยู่ หากไม่อยู่ ให้เลื่อนดูรายการจนกว่าจะพบการคอมมิต
หลังจากระบุการคอมมิตแล้ว ให้เปลี่ยนกลับในเคอร์เนลหรืออัปเดตเพื่อระงับการเปลี่ยนแปลง 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;
};