คุณสามารถใช้เครื่องมือตรวจสอบ 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 รายการ แต่สัญลักษณ์ที่โมดูลผู้ให้บริการจะใช้ได้จะแสดงอยู่ในชุดไฟล์รายการสัญลักษณ์ที่เปิดเผยต่อสาธารณะในรูทของเคอร์เนล การรวมสัญลักษณ์ทั้งหมดในไฟล์รายการสัญลักษณ์ทั้งหมดจะกำหนดชุดสัญลักษณ์ KMI ที่คงที่ ตัวอย่างไฟล์รายการสัญลักษณ์คือ abi_gki_aarch64_db845c ซึ่งประกาศสัญลักษณ์ที่จําเป็นสําหรับ DragonBoard 845c
เฉพาะสัญลักษณ์ที่แสดงในรายการสัญลักษณ์ รวมถึงโครงสร้างและคำจำกัดความที่เกี่ยวข้องเท่านั้นที่จะถือว่าเป็นส่วนหนึ่งของ KMI คุณสามารถโพสต์การเปลี่ยนแปลงในรายการสัญลักษณ์ได้หากไม่มีสัญลักษณ์ที่ต้องการ หลังจากอินเทอร์เฟซใหม่อยู่ในรายการสัญลักษณ์และเป็นส่วนหนึ่งของคำอธิบาย KMI แล้ว ระบบจะดูแลรักษาอินเทอร์เฟซดังกล่าวให้เสถียรและจะต้องไม่นำออกจากรายการสัญลักษณ์หรือแก้ไขหลังจากแชนเนลถูกแช่แข็ง
แต่ละสาขาเคอร์เนล KMI ของ Android Common Kernel (ACK) จะมีชุดรายการสัญลักษณ์ของตัวเอง ไม่มีการพยายามทำให้ ABI คงที่ระหว่างสาขาเคอร์เนล KMI ต่างๆ เช่น KMI ของ android12-5.10
จะไม่เกี่ยวข้องกับ KMI ของ android13-5.10
แต่อย่างใด
เครื่องมือ ABI ใช้รายการสัญลักษณ์ KMI เพื่อจำกัดอินเทอร์เฟซที่ต้องตรวจสอบความเสถียร รายการสัญลักษณ์หลักมีสัญลักษณ์ที่โมดูลเคอร์เนล GKI ต้องการ ผู้ให้บริการควรส่งและอัปเดตรายการสัญลักษณ์เพิ่มเติมเพื่อให้มั่นใจว่าอินเทอร์เฟซที่ตนใช้นั้นคงความสามารถในการทำงานร่วมกับ ABI ได้ เช่น หากต้องการดูรายการสัญลักษณ์ของ android13-5.15
ให้ดูที่ https://android.googlesource.com/kernel/common/+/refs/heads/android13-5.15/android
รายการสัญลักษณ์ประกอบด้วยสัญลักษณ์ที่รายงานว่าจําเป็นสําหรับผู้ให้บริการหรืออุปกรณ์หนึ่งๆ รายการทั้งหมดที่เครื่องมือใช้คือชุดรวมของไฟล์รายการสัญลักษณ์ KMI ทั้งหมด เครื่องมือ ABI จะกำหนดรายละเอียดของสัญลักษณ์แต่ละรายการ ซึ่งรวมถึงลายเซ็นฟังก์ชันและโครงสร้างข้อมูลที่ฝังอยู่
เมื่อ KMI หยุดทำงาน ระบบจะไม่อนุญาตให้เปลี่ยนแปลงอินเทอร์เฟซ KMI ที่มีอยู่ เนื่องจากอินเทอร์เฟซเหล่านี้มีความเสถียร อย่างไรก็ตาม ผู้ให้บริการสามารถเพิ่มสัญลักษณ์ลงใน KMI ได้ทุกเมื่อ ตราบใดที่การเพิ่มดังกล่าวไม่ส่งผลต่อความเสถียรของ ABI ที่มีอยู่ สัญลักษณ์ที่เพิ่มเข้ามาใหม่จะคงเดิมเมื่อมีการอ้างถึงในรายการสัญลักษณ์ KMI คุณไม่ควรนำสัญลักษณ์ออกจากรายการสำหรับเคอร์เนล เว้นแต่จะสามารถยืนยันได้ว่าไม่มีอุปกรณ์ใดที่เคยจัดส่งโดยอาศัยสัญลักษณ์นั้น
คุณสามารถสร้างรายการสัญลักษณ์ KMI สำหรับอุปกรณ์โดยใช้วิธีการจากวิธีใช้รายการสัญลักษณ์ พาร์ทเนอร์จํานวนมากส่งรายการสัญลักษณ์ 1 รายการต่อ ACK แต่นี่ไม่ใช่ข้อกําหนด คุณส่งรายการสัญลักษณ์หลายรายการได้หากจะช่วยเรื่องการบำรุงรักษาได้
ขยาย 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 ขณะสร้าง
สาเหตุที่พบบ่อยที่สุดของข้อผิดพลาดคือเมื่อไดรเวอร์ใช้สัญลักษณ์ใหม่จากเคอร์เนลซึ่งไม่อยู่ในรายการสัญลักษณ์
หากสัญลักษณ์ไม่ได้อยู่ในรายการสัญลักษณ์ (android/abi_gki_aarch64
) คุณจะต้องยืนยันว่ามีการส่งออกสัญลักษณ์ดังกล่าวด้วย EXPORT_SYMBOL_GPL(symbol_name)
ก่อน แล้วจึงอัปเดตการแสดง ABI XML และรายการสัญลักษณ์ ตัวอย่างเช่น การเปลี่ยนแปลงต่อไปนี้จะเพิ่มฟีเจอร์ FS ที่เพิ่มขึ้นใหม่ไปยังสาขา android-12-5.10
ซึ่งรวมถึงการอัปเดตรายการสัญลักษณ์และการนําเสนอ ABI XML
- ตัวอย่างการเปลี่ยนแปลงฟีเจอร์อยู่ใน aosp/1345659
- ตัวอย่างรายการสัญลักษณ์อยู่ใน aosp/1346742
- ตัวอย่างการเปลี่ยนแปลง ABI XML อยู่ใน 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 XML และรายการสัญลักษณ์ใน ACK ได้ที่ aosp/1367601
แก้ไขข้อขัดข้องของ ABI ของเคอร์เนล
คุณสามารถจัดการการหยุดทำงานของ ABI ของเคอร์เนลได้โดยการจัดระเบียบโค้ดใหม่เพื่อไม่ให้ ABI เปลี่ยนแปลงหรืออัปเดตการแสดง ABI ใช้แผนภูมิต่อไปนี้เพื่อกำหนดวิธีที่ดีที่สุดสำหรับสถานการณ์ของคุณ
รูปที่ 1 การแก้ปัญหา ABI
ปรับโค้ดใหม่เพื่อหลีกเลี่ยงการเปลี่ยนแปลง ABI
พยายามหลีกเลี่ยงการแก้ไข ABI ที่มีอยู่ ในหลายกรณี คุณสามารถรีแฟกทอริกโค้ดเพื่อนำการเปลี่ยนแปลงที่ส่งผลต่อ ABI ออก
การจัดระเบียบการเปลี่ยนแปลงช่อง Struct หากการเปลี่ยนแปลงแก้ไข ABI สําหรับฟีเจอร์การแก้ไขข้อบกพร่อง ให้เพิ่ม
#ifdef
รอบๆ ช่อง (ในโครงสร้างและข้อมูลอ้างอิงแหล่งที่มา) และตรวจสอบว่าCONFIG
ที่ใช้สําหรับ#ifdef
ปิดใช้สําหรับ defconfig และgki_defconfig
เวอร์ชันที่ใช้งานจริง หากต้องการดูตัวอย่างวิธีเพิ่มการกำหนดค่าการแก้ไขข้อบกพร่องไปยัง Struct โดยไม่ทำให้ 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
Stub ต้องใช้เฉพาะสำหรับการเปลี่ยนแปลง Kernel หลักที่ไม่เป็นประโยชน์ต่อ ACK เช่น ประสิทธิภาพและการเปลี่ยนพลังงาน รายการต่อไปนี้แสดงตัวอย่างตัวอย่างของ Stub และการเลือกบางส่วนใน ACK สำหรับ GKI
ต้นกำเนิดขององค์ประกอบแบบไอโซเลต (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 ที่รันไทม์ การกำหนดเวอร์ชันโมดูลอาจทําให้การตรวจสอบการทำซ้ำแบบวนซ้ำ (CRC) ไม่ตรงกันในเวลาที่ใช้ในการโหลดโมดูล หาก KMI ที่คาดไว้ของโมดูลไม่ตรงกับ KMI ของ vmlinux
ตัวอย่างเช่น ต่อไปนี้คือข้อผิดพลาดทั่วไปที่เกิดขึ้นเมื่อโหลดโมดูลเนื่องจาก 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,
จะมีการตั้งค่าแฟล็กKBUILD_SYMTYPES=1
โดยปริยายค้นหาไฟล์
.c
ที่มีการส่งออกสัญลักษณ์ที่มี CRC ไม่ตรงกันโดยใช้คำสั่งต่อไปนี้cd common && git grep EXPORT_SYMBOL.*module_layout kernel/module.c:EXPORT_SYMBOL(module_layout);
ไฟล์
.c
มีไฟล์.symtypes
ที่เกี่ยวข้องใน GKI และอาร์ติแฟกต์การสร้างเคอร์เนลของอุปกรณ์ ค้นหาไฟล์.c
โดยใช้คำสั่งต่อไปนี้cd out/$BRANCH/common && ls -1 kernel/module.* kernel/module.o kernel/module.o.symversions kernel/module.symtypes
ลักษณะของไฟล์
.c
มีดังนี้รูปแบบของไฟล์
.c
คือ 1 บรรทัด (อาจยาวมาก) ต่อสัญลักษณ์[s|u|e|etc]#
ที่จุดเริ่มต้นของบรรทัดหมายความว่าสัญลักษณ์นั้นเป็นประเภทข้อมูล[struct|union|enum|etc]
เช่นt#bool typedef _Bool bool
หากไม่มีคำนำหน้า
#
ที่จุดเริ่มต้นของบรรทัด แสดงว่าสัญลักษณ์นั้นคือฟังก์ชัน เช่นfind_module s#module * find_module ( const char * )
เปรียบเทียบ 2 ไฟล์และแก้ไขความแตกต่างทั้งหมด
กรณีที่ 1: ความแตกต่างเนื่องจากระดับการเข้าถึงประเภทข้อมูล
หากเคอร์เนลหนึ่งเก็บสัญลักษณ์หรือประเภทข้อมูลที่มองไม่เห็นสำหรับโมดูล แต่อีกเคอร์เนลหนึ่งไม่เก็บ ความแตกต่างนั้นจะปรากฏระหว่างไฟล์ .symtypes
ของเคอร์เนล 2 รายการ ไฟล์ .symtypes
จากเคอร์เนลหนึ่งมี UNKNOWN
สำหรับสัญลักษณ์ และไฟล์ .symtypes
จากเคอร์เนลอีกตัวหนึ่งมีมุมมองแบบขยายของสัญลักษณ์หรือประเภทข้อมูล
ตัวอย่างเช่น การเพิ่มบรรทัดต่อไปนี้ลงในไฟล์ include/linux/device.h
ในเคอร์เนลทําให้ CRC ไม่ตรงกัน โดยหนึ่งในนั้นสําหรับ module_layout()
#include <linux/fwnode.h>
การเปรียบเทียบ module.symtypes
กับสัญลักษณ์ดังกล่าวจะแสดงความแตกต่างต่อไปนี้
$ diff -u <GKI>/kernel/module.symtypes <your kernel>/kernel/module.symtypes
--- <GKI>/kernel/module.symtypes
+++ <your kernel>/kernel/module.symtypes
@@ -334,12 +334,15 @@
...
-s#fwnode_handle struct fwnode_handle { UNKNOWN }
+s#fwnode_reference_args struct fwnode_reference_args { s#fwnode_handle * fwnode ; unsigned int nargs ; t#u64 args [ 8 ] ; }
...
หากเคอร์เนลของคุณมีค่าเป็น UNKNOWN
และเคิร์น GKI มีมุมมองแบบขยายของสัญลักษณ์ (ซึ่งไม่น่าจะเกิดขึ้น) ให้ผสานเคอร์เนล Android Common เวอร์ชันล่าสุดเข้ากับเคอร์เนลของคุณเพื่อให้ใช้ฐานเคอร์เนล GKI เวอร์ชันล่าสุด
ในกรณีส่วนใหญ่ เคอร์เนล GKI จะมีค่าเป็น UNKNOWN
แต่เคอร์เนลของคุณจะมีรายละเอียดภายในของสัญลักษณ์เนื่องจากมีการเปลี่ยนแปลงในเคอร์เนล เนื่องจากไฟล์หนึ่งในเคอร์เนลของคุณเพิ่ม #include
ที่ไม่มีอยู่ในเคอร์เนล GKI
บ่อยครั้งที่การแก้ไขเป็นเพียงการซ่อน #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ระบุการเปลี่ยนแปลง เปลี่ยนกลับในเคอร์เนล หรืออัปโหลดไปยัง ACK และผสานรวม
กรณี 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 struct 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 struct 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>
ตรวจสอบรายการคอมมิตที่ส่งคืนเพื่อค้นหาการเปลี่ยนแปลงหรือการลบ คอมมิตแรกอาจเป็นคอมมิตที่คุณค้นหาอยู่ หากไม่อยู่ ให้เลื่อนดูรายการจนกว่าจะพบการคอมมิต
หลังจากระบุการเปลี่ยนแปลงแล้ว ให้เปลี่ยนกลับในเคอร์เนลหรืออัปโหลดไปยัง ACK และผสานรวม