คุณสามารถใช้เครื่องมือตรวจสอบ 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 (หากจำเป็น)
- ทำงานกับรายการสัญลักษณ์
คำแนะนำต่อไปนี้ใช้ได้กับ
เคอร์เนลที่คุณสามารถสร้างโดยใช้
Toolchain ที่รองรับ (เช่น Toolchain ของ 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 สามารถดึงข้อมูลจาก
ไบนารี การเปลี่ยนแปลงใดๆ ที่เกิดขึ้นกับไฟล์แหล่งที่มาของเคอร์เนลสามารถส่งผลกระทบต่อไบนารีและใน
Turn จะส่งผลต่อ .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
แก้ไขการหยุดทำงานของ Kernel ABI
คุณสามารถจัดการการหยุดทำงานของ ABI ของเคอร์เนลได้โดยการจัดระเบียบโค้ดใหม่เพื่อไม่ให้ ABI เปลี่ยนแปลงหรืออัปเดตการแสดง ABI ใช้แผนภูมิต่อไปนี้เพื่อพิจารณาแนวทางที่ดีที่สุดสำหรับสถานการณ์ของคุณ
รูปที่ 1 การแก้ปัญหาการหยุดทำงานของ ABI
ปรับโค้ดใหม่เพื่อหลีกเลี่ยงการเปลี่ยนแปลง ABI
พยายามหลีกเลี่ยงการแก้ไข ABI ที่มีอยู่ ในหลายกรณี คุณสามารถ เปลี่ยนโครงสร้างภายในโค้ดเพื่อนำการเปลี่ยนแปลงที่มีผลต่อ ABI ออก
การเปลี่ยนช่องโครงสร้างการเปลี่ยนโครงสร้างภายในโค้ด หากการเปลี่ยนแปลงแก้ไข 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 ใช้งานไม่ได้ ให้ทำตามขั้นตอนต่อไปนี้
รอรับ +2 การตรวจสอบโค้ดสำหรับชุดแพตช์
ผสานการเปลี่ยนแปลงโค้ดเข้ากับการเปลี่ยนแปลงการอัปเดต 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 โดยใช้สตับตามที่อธิบายไว้ใน หัวข้อต่อไปนี้
ใช้สแต็บสำหรับ ACK
สตับจําเป็นต้องใช้กับการเปลี่ยนแปลงหลักของเคิร์กัลเท่านั้น ซึ่งไม่ได้ให้ประโยชน์กับ ACK เช่น การเปลี่ยนแปลงด้านประสิทธิภาพและพลังงาน ตัวอย่างรายละเอียดรายการต่อไปนี้ ของต้นขั้วและที่เก็บซากุระบางส่วนใน ACK สำหรับ GKI
ต้นขั้วฟีเจอร์แบบไอโซเลต (aosp/1284493) ไม่จำเป็นต้องใช้ความสามารถใน ACK แต่ต้องมีสัญลักษณ์ ใน ACK เพื่อให้โมดูลใช้สัญลักษณ์เหล่านี้
สัญลักษณ์ตัวยึดตำแหน่งสำหรับโมดูลผู้ให้บริการ (aosp/1288860)
เลือกเฉพาะ ABI สำหรับฟีเจอร์การติดตามเหตุการณ์
mm
ในแต่ละกระบวนการ (aosp/1288454) เราได้เลือกแพตช์ต้นฉบับเพื่อยอมรับ แล้วตัดให้เหลือเฉพาะการเปลี่ยนแปลงที่จำเป็นเพื่อแก้ไขความแตกต่างของ ABI สำหรับtask_struct
และmm_event_count
แพตช์นี้ยังอัปเดต enummm_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
กม. ตัวอย่างเช่น ต่อไปนี้คือข้อผิดพลาดทั่วไปที่เกิดขึ้นเมื่อโหลดโมดูลเนื่องจาก 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 สำหรับ
โดยเรียกใช้คำสั่งต่อไปนี้บนทั้งเคอร์เนลและเปรียบเทียบ
เอาต์พุต:
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 ไม่ตรงกันโดยใช้ คำสั่งต่อไปนี้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 บรรทัด (อาจมีความยาวมาก) ต่อสัญลักษณ์ 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 Kernel ล่าสุดเข้ากับ
เคอร์เนล เพื่อให้คุณใช้ฐานเคอร์เนล 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 และรับ รวมแล้ว