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

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

เครื่องมือตรวจสอบ ABI พัฒนาใน AOSP และใช้ STG (หรือ libabigail ใน Android 13 และต่ำกว่า) เพื่อสร้างและเปรียบเทียบการแสดงผล

หน้านี้อธิบายเครื่องมือ กระบวนการรวบรวมและวิเคราะห์ ABI และการใช้การเป็นตัวแทนดังกล่าวเพื่อให้ความมั่นคงแก่ ABI ในเคอร์เนล หน้านี้ยังมีข้อมูลเกี่ยวกับการมีส่วนร่วมในการเปลี่ยนแปลงเคอร์เนล Android ด้วย

กระบวนการ

การวิเคราะห์ ABI ของเคอร์เนลมีหลายขั้นตอน ซึ่งส่วนใหญ่เป็นแบบอัตโนมัติได้

  1. สร้างเคอร์เนลและการนำเสนอ ABI ของเคอร์เนล
  2. วิเคราะห์ความแตกต่างของ ABI ระหว่างบิลด์กับข้อมูลอ้างอิง
  3. อัปเดตการนำเสนอของ ABI (หากจำเป็น)
  4. ทำงานกับรายการสัญลักษณ์

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

แผนภาพขั้นตอน 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 ใช้งานไม่ได้ ให้ทำตามขั้นตอนต่อไปนี้

  1. อัปโหลดการเปลี่ยนแปลงโค้ดไปยัง ACK

  2. รอรับ +2 การตรวจสอบโค้ดสำหรับชุดแพตช์

  3. อัปเดตการนำเสนอ ABI ของข้อมูลอ้างอิง

  4. ผสานการเปลี่ยนแปลงโค้ดเข้ากับการเปลี่ยนแปลงการอัปเดต 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 แพตช์นี้ยังอัปเดต enum mm_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 ไม่ตรงกันขณะโหลดโมดูล

  1. สร้างเคอร์เนล GKI และเคิร์นัลของอุปกรณ์โดยใช้ตัวเลือก --kbuild_symtypes ดังที่แสดงในคำสั่งต่อไปนี้

    tools/bazel run --kbuild_symtypes //common:kernel_aarch64_dist

    คำสั่งนี้จะสร้างไฟล์ .symtypes สำหรับไฟล์ .o แต่ละไฟล์ โปรดดู KBUILD_SYMTYPES ใน Kleaf เพื่อดูรายละเอียด

    สำหรับ Android 13 และต่ำกว่า ให้สร้างเคอร์เนล GKI และเคอร์เนลของอุปกรณ์โดยใส่ KBUILD_SYMTYPES=1 ไว้หน้าคำสั่งที่คุณใช้สร้างเคอร์เนล ดังที่แสดงในคำสั่งต่อไปนี้

    KBUILD_SYMTYPES=1 BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

    เมื่อใช้ build_abi.sh, ระบบจะตั้งค่า Flag KBUILD_SYMTYPES=1 โดยปริยาย

  2. ค้นหาไฟล์ .c ที่มีการส่งออกสัญลักษณ์ที่มี CRC ไม่ตรงกันโดยใช้ คำสั่งต่อไปนี้

    cd common && git grep EXPORT_SYMBOL.*module_layout
    kernel/module.c:EXPORT_SYMBOL(module_layout);
  3. ไฟล์ .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 * )
      
  4. เปรียบเทียบ 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 ที่ทำให้เกิดความแตกต่าง ให้ทำตามขั้นตอนต่อไปนี้

  1. เปิดไฟล์ส่วนหัวที่กำหนดสัญลักษณ์หรือประเภทข้อมูลที่มี แตกต่างกัน ตัวอย่างเช่น แก้ไข include/linux/fwnode.h สำหรับฟิลด์ struct fwnode_handle

  2. เพิ่มโค้ดต่อไปนี้ที่ด้านบนของไฟล์ส่วนหัว

    #ifdef CRC_CATCH
    #error "Included from here"
    #endif
    
  3. ในไฟล์ .c ของโมดูลที่มี CRC ไม่ตรงกัน ให้เพิ่ม ตามเป็นบรรทัดแรกก่อนบรรทัด #include

    #define CRC_CATCH 1
    
  4. คอมไพล์โมดูล ข้อผิดพลาดที่เกิดขึ้นขณะสร้างจะแสดงลําดับไฟล์ส่วนหัว #include ที่ทําให้ CRC ไม่ตรงกัน เช่น

    In file included from .../drivers/clk/XXX.c:16:`
    In file included from .../include/linux/of_device.h:5:
    In file included from .../include/linux/cpu.h:17:
    In file included from .../include/linux/node.h:18:
    .../include/linux/device.h:16:2: error: "Included from here"
    #error "Included from here"
    

    หนึ่งในลิงก์ในเครือข่ายของ #include นี้เกิดจากการเปลี่ยนแปลงใน เคอร์เนลที่ขาดหายไปในเคอร์เนล GKI

  5. ระบุการเปลี่ยนแปลง เปลี่ยนกลับในเคอร์เนล หรือ อัปโหลดไปยัง 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 * ) ; ...

หากต้องการระบุประเภทที่เปลี่ยนแปลง ให้ทําตามขั้นตอนต่อไปนี้

  1. ค้นหาคำจำกัดความของสัญลักษณ์ในซอร์สโค้ด (โดยปกติจะอยู่ในไฟล์ .h)

    • หากต้องการดูความแตกต่างของสัญลักษณ์ระหว่างเคอร์เนลของคุณกับเคอร์เนล GKI ให้ค้นหาการคอมมิตโดยเรียกใช้คำสั่งต่อไปนี้
    git blame
    • สําหรับสัญลักษณ์ที่ลบไปแล้ว (ซึ่งมีการลบสัญลักษณ์ในแผนภูมิ และคุณต้องการลบสัญลักษณ์นั้นในแผนภูมิอื่นด้วย) คุณต้องค้นหาการเปลี่ยนแปลงที่ลบบรรทัดนั้น ใช้คำสั่งต่อไปนี้ในต้นไม้ที่มีการลบบรรทัด
    git log -S "copy paste of deleted line/word" -- <file where it was deleted>
  2. ตรวจสอบรายการคอมมิตที่ส่งคืนเพื่อค้นหาการเปลี่ยนแปลงหรือการลบ คอมมิตแรกอาจเป็นคอมมิตที่คุณค้นหาอยู่ หากไม่ได้เลือก ให้ไปที่ ผ่านรายการไปเรื่อยๆ จนกว่าคุณจะพบคอมมิต

  3. หลังจากระบุการเปลี่ยนแปลงแล้ว ให้เปลี่ยนค่าในเคอร์เนลหรือ อัปโหลดไปยัง ACK และรับ รวมแล้ว