คุณสามารถใช้เครื่องมือการตรวจสอบ Application Branch 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 และสำหรับ
เคอร์เนลเฉพาะอุปกรณ์ จะทำให้มั่นใจได้ว่ามีการใช้ Toolchain ที่ถูกต้องเมื่อคุณ
สร้างการกระจายเคอร์เนลสำหรับการวิเคราะห์
รายการสัญลักษณ์
KMI ไม่ได้รวมสัญลักษณ์ทั้งหมดในเคอร์เนลหรือแม้แต่สัญลักษณ์ทั้งหมดของ 30,000+ สัญลักษณ์ที่ส่งออก แต่สัญลักษณ์ที่โมดูลของผู้ให้บริการสามารถใช้ได้มีดังนี้ แสดงอย่างชัดเจนในชุดของไฟล์รายการสัญลักษณ์ที่เก็บรักษาไว้แบบสาธารณะในราก ของต้นไม้เคอร์เนล การรวมสัญลักษณ์ทั้งหมดในไฟล์รายการสัญลักษณ์ทั้งหมด กำหนดชุดของสัญลักษณ์ KMI ที่มีค่าคงที่ ตัวอย่างไฟล์รายการสัญลักษณ์ เท่ากับ abi_gki_arch64_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 จะยังคงเปิดให้ใช้ส่วนขยายเพื่อให้อุปกรณ์จัดส่งได้ ในช่วงท้ายของปี คุณไม่จําเป็นต้องระบุทรัพยากร Dependency ทั้งหมดก่อน 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 ที่เพิ่มขึ้นใหม่ให้กับ Branch ของ android-12-5.10
ซึ่ง
รวมถึงการอัปเดตรายการสัญลักษณ์และการแทน ABI XML
- ตัวอย่างการเปลี่ยนแปลงของฟีเจอร์อยู่ใน aosp/1345659
- ตัวอย่างรายการสัญลักษณ์อยู่ใน aosp/1346742
- ตัวอย่างการเปลี่ยนแปลง XML ของ 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 XML และรายการสัญลักษณ์ใน ACK โปรดดู aosp/1367601
แก้ไขการหยุดทำงานของ Kernel ABI
คุณสามารถจัดการความเสียหายของ Kernel ABI ได้โดยการเปลี่ยนโครงสร้างภายในโค้ดเพื่อไม่ให้มีการเปลี่ยนแปลง ABI หรือการอัปเดตการนำเสนอ ABI ใช้รายการต่อไปนี้ เพื่อกำหนดแนวทางที่ดีที่สุดสำหรับสถานการณ์ของคุณ
รูปที่ 1 การแก้ปัญหาการหยุดทำงานของ ABI
เปลี่ยนโครงสร้างโค้ดเพื่อหลีกเลี่ยงการเปลี่ยนแปลง ABI
พยายามหลีกเลี่ยงการแก้ไข ABI ที่มีอยู่ ในหลายกรณี คุณสามารถ เปลี่ยนโครงสร้างภายในโค้ดเพื่อนำการเปลี่ยนแปลงที่มีผลต่อ ABI ออก
การเปลี่ยนช่องโครงสร้างการเปลี่ยนโครงสร้างภายในโค้ด หากการเปลี่ยนแปลงแก้ไข ABI สำหรับการแก้ไขข้อบกพร่อง เพิ่ม
#ifdef
รอบช่อง (ในโครงสร้างและแหล่งที่มา ) และตรวจสอบว่าCONFIG
ที่ใช้สำหรับ#ifdef
ปิดใช้อยู่สำหรับ defconfig เวอร์ชันที่ใช้งานจริงและgki_defconfig
ตัวอย่างของวิธีการแก้ไขข้อบกพร่อง สามารถเพิ่มการกำหนดค่าไปยัง Struct โดยไม่ทำให้ ABI เสียหายได้ โปรดดู Patchsetการเปลี่ยนโครงสร้างภายในโค้ดเพื่อไม่ให้มีการเปลี่ยนเคอร์เนลหลัก หากต้องการฟีเจอร์ใหม่ เพื่อเพิ่มลงใน 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
Stub ต้องใช้เฉพาะสำหรับการเปลี่ยนแปลงเคอร์เนลหลักที่ไม่เกิดประโยชน์ ACK เช่น การเปลี่ยนแปลงประสิทธิภาพและพลังงาน ตัวอย่างรายละเอียดรายการต่อไปนี้ ของต้นขั้วและที่เก็บซากุระบางส่วนใน ACK สำหรับ GKI
ต้นขั้วฟีเจอร์แบบไอโซเลต (aosp/1284493) ไม่จำเป็นต้องใช้ความสามารถใน ACK แต่ต้องมีสัญลักษณ์ ใน ACK เพื่อให้โมดูลใช้สัญลักษณ์เหล่านี้
สัญลักษณ์ตัวยึดตำแหน่งสำหรับโมดูลผู้ให้บริการ (aosp/1288860)
ฟีเจอร์การติดตามเหตุการณ์
mm
ต่อกระบวนการที่เลือกเฉพาะ ABI เท่านั้น (aosp/1288454) แพตช์ต้นฉบับถูกเลือกไปยัง ACK จากนั้นตัดออกเพื่อให้มีเฉพาะ การเปลี่ยนแปลงที่จำเป็นเพื่อแก้ไขความแตกต่างของ ABI สำหรับtask_struct
และmm_event_count
แพตช์นี้ยังอัปเดต enummm_event_type
ให้มี สมาชิกคนสุดท้ายการเปลี่ยนแปลง ABI ที่มีการเปลี่ยนแปลงโครงสร้างทางความร้อนบางส่วนซึ่งจำเป็นต้องมีมากกว่าแค่ กำลังเพิ่มช่อง ABI ใหม่
แผ่นแปะ aosp/1255544 แก้ไขความแตกต่างของ ABI ระหว่างเคอร์เนลของพาร์ทเนอร์และ ACK
แผ่นแปะ aosp/1291018 แก้ไขปัญหาด้านการทำงานที่พบในระหว่างการทดสอบ GKI ของแพตช์ก่อนหน้า การแก้ไขรวมถึงการเริ่มต้นโครงสร้างพารามิเตอร์เซ็นเซอร์เพื่อลงทะเบียน โซนความร้อนหลายโซนให้เป็นเซ็นเซอร์เดียว
การเปลี่ยนแปลง ABI
CONFIG_NL80211_TESTMODE
รายการ (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 โดยใช้การกำหนดเวอร์ชันโมดูล
เคอร์เนลทั่วไปสำหรับอิมเมจเคอร์เนล (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, ชื่อสัญลักษณ์, Namespace ของสัญลักษณ์, 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,
จะมีการตั้งค่าแฟล็ก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 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 และรับ ที่ผสานรวมแล้ว