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

คุณสามารถใช้เครื่องมือการตรวจสอบ 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 ของเคอร์เนลมีหลายขั้นตอน ซึ่งส่วนใหญ่เป็นแบบอัตโนมัติได้

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

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

ผังงานการเสียหายของ 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 ให้ทำตามขั้นตอนต่อไปนี้

  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

Stub ต้องใช้เฉพาะสำหรับการเปลี่ยนแปลงเคอร์เนลหลักที่ไม่เกิดประโยชน์ ACK เช่น การเปลี่ยนแปลงประสิทธิภาพและพลังงาน ตัวอย่างรายละเอียดรายการต่อไปนี้ ของต้นขั้วและที่เก็บซากุระบางส่วนใน ACK สำหรับ GKI

  • ต้นขั้วฟีเจอร์แบบไอโซเลต (aosp/1284493) ไม่จำเป็นต้องใช้ความสามารถใน ACK แต่ต้องมีสัญลักษณ์ ใน ACK เพื่อให้โมดูลใช้สัญลักษณ์เหล่านี้

  • สัญลักษณ์ตัวยึดตำแหน่งสำหรับโมดูลผู้ให้บริการ (aosp/1288860)

  • ฟีเจอร์การติดตามเหตุการณ์ mm ต่อกระบวนการที่เลือกเฉพาะ ABI เท่านั้น (aosp/1288454) แพตช์ต้นฉบับถูกเลือกไปยัง ACK จากนั้นตัดออกเพื่อให้มีเฉพาะ การเปลี่ยนแปลงที่จำเป็นเพื่อแก้ไขความแตกต่างของ ABI สำหรับ task_struct และ mm_event_count แพตช์นี้ยังอัปเดต enum mm_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 ไม่ตรงกันขณะโหลดโมดูล

  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, จะมีการตั้งค่าแฟล็ก 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 บรรทัด (อาจยาวมาก) ต่อสัญลักษณ์

    • [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 และรับ ที่ผสานรวมแล้ว