การตรวจสอบ Android Kernel ABI

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

ABI การตรวจสอบเครื่องมือถูก พัฒนาขึ้นใน AOSP และการใช้ libabigail ในการสร้างและเปรียบเทียบการแสดง

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

ไดเรกทอรีนี้ มีเครื่องมือเฉพาะสำหรับการวิเคราะห์ ABI ใช้มันกับการสร้างสคริปต์ที่มีให้โดย build_abi.sh .)

กระบวนการ

การวิเคราะห์ ABI ของเคอร์เนลนั้นมีหลายขั้นตอน ซึ่งส่วนใหญ่สามารถทำได้โดยอัตโนมัติ:

  1. ได้รับ toolchain ที่สร้างสคริปต์และแหล่งที่มาของเคอร์เนลผ่าน repo
  2. ให้ข้อกำหนดเบื้องต้นใด ๆ (เช่น libabigail ห้องสมุดและคอลเลกชันของเครื่องมือ)
  3. สร้างเคอร์เนลและการเป็นตัวแทนของ ABI
  4. วิเคราะห์ความแตกต่าง ABI ระหว่างการสร้างและการอ้างอิง
  5. อัพเดทตัวแทน ABI (ถ้าจำเป็น)
  6. การทำงานกับรายการสัญลักษณ์

คำแนะนำต่อไปทำงานสำหรับการใด ๆ เคอร์เนลที่คุณสามารถสร้าง ใช้ toolchain สนับสนุน (เช่นที่สร้างไว้ล่วงหน้าเสียงดังกราว toolchain) repo manifests ที่ใช้ได้สำหรับทุกสาขา Android เคอร์เนลที่พบบ่อยและเมล็ดเฉพาะอุปกรณ์หลายพวกเขามั่นใจว่า toolchain ที่ถูกต้องเมื่อคุณสร้างการกระจายเคอร์เนลสำหรับการวิเคราะห์

การใช้เครื่องมือตรวจสอบ ABI

1. รับ toolchain, สร้างสคริปต์ และแหล่งเคอร์เนลผ่าน repo

คุณสามารถได้รับ toolchain ที่สร้างสคริปต์ (สคริปต์เหล่านี้) และแหล่งที่มาของเคอร์เนลกับ repo สำหรับเอกสารรายละเอียดโปรดดูข้อมูลที่สอดคล้องกันสำหรับ การสร้าง Android เมล็ด

แสดงให้เห็นถึงกระบวนการขั้นตอนต่อไปจะใช้ common-android12-5.10 , สาขาเคอร์เนล Android ซึ่งเป็นรุ่นล่าสุดที่ปล่อยออกมาเคอร์เนล GKI ในขณะที่เขียนนี้ ที่จะได้รับในสาขานี้ผ่าน repo ดำเนินการต่อไปนี้:

repo init -u https://android.googlesource.com/kernel/manifest -b common-android12-5.10
repo sync

2. จัดเตรียมข้อกำหนดเบื้องต้น

เครื่องมือ ABI ใช้ libabigail ไลบรารีและชุดเครื่องมือเพื่อวิเคราะห์ไบนารี ชุดที่เหมาะสมของไบนารีที่สร้างไว้ล่วงหน้ามาพร้อมกับ เคอร์เนลสร้างเครื่องมือ และถูกนำมาใช้โดยอัตโนมัติด้วย build_abi.sh

ที่จะใช้เครื่องมือในระดับต่ำกว่า (เช่น dump_abi ), เพิ่มเครื่องมือเคอร์เนล build- ไปยัง PATH

3. สร้างเคอร์เนลและการแทนค่า ABI ของมัน

ณ จุดนี้คุณก็พร้อมที่จะสร้างเคอร์เนลกับ toolchain ที่ถูกต้องและเพื่อดึงตัวแทนจาก ABI ไบนารีของมัน ( vmlinux + โมดูล)

คล้ายกับปกติ Android เคอร์เนลสร้างกระบวนการ (ใช้ build.sh ) ขั้นตอนนี้ต้องทำงาน build_abi.sh

BUILD_CONFIG=common/build.config.gki.aarch64 build/build_abi.sh

ที่สร้างเคอร์เนลและสารสกัดจากตัวแทน ABI เข้าไปใน out_abi ไดเรกทอรีย่อย ในกรณีนี้ out/android12-5.10/dist/abi.xml เป็น symbolic link ไป out_abi/android12-5.10/dist/abi-<id>.xml < id> คำนวณโดยการดำเนินการ git describe กับต้นไม้มาเคอร์เนล

4. วิเคราะห์ความแตกต่างของ ABI ระหว่างบิลด์และการแทนค่าอ้างอิง

build_abi.sh วิเคราะห์และรายงานความแตกต่างใด ๆ เมื่อ ABI อ้างอิงมีให้ผ่านตัวแปรสภาพแวดล้อม ABI_DEFINITION ABI_DEFINITION ต้องชี้ไปอ้างอิงแฟ้มเทียบกับต้นไม้มาเคอร์เนลและสามารถระบุในบรรทัดคำสั่งหรือมากกว่าปกติเป็นค่าใน build.config ต่อไปนี้เป็นตัวอย่าง:

BUILD_CONFIG=common/build.config.gki.aarch64 build/build_abi.sh

ในคำสั่งดังกล่าวข้างต้น build.config.gki.aarch64 กำหนดไฟล์อ้างอิง (ตาม ABI_DEFINITION=android/abi_gki_aarch64.xml ) และ diff_abi โทร abidiff เพื่อเปรียบเทียบการแสดง ABI สร้างสดกับไฟล์อ้างอิง build_abi.sh พิมพ์สถานที่ตั้งของรายงานและส่งเสียงรายงานสั้น ๆ สำหรับการใด ๆ แตก ABI หากมีการตรวจพบการแตกหัก, build_abi.sh ยุติและส่งกลับรหัสภัณฑ์ทางออก

5. อัปเดตการแสดง ABI (ถ้าจำเป็น)

การอัปเดตการแสดง ABI ขอร้อง build_abi.sh กับ --update ธง การปรับปรุงที่สอดคล้อง abi.xml ไฟล์ที่กำหนดโดย build.config หากต้องการพิมพ์แตกต่าง ABI เนื่องจากการปรับปรุงวิงวอนขอสคริปต์ด้วย --print-report ต้องแน่ใจว่ามีรายงานในการกระทำผิดพลาดเมื่อปรับปรุง abi.xml ไฟล์

6. การทำงานกับรายการสัญลักษณ์

parameterize build_abi.sh กับ KMI รายการสัญลักษณ์สัญลักษณ์กรองในระหว่างการสกัด ABI ไฟล์เหล่านี้เป็นไฟล์ข้อความธรรมดาที่แสดงสัญลักษณ์เคอร์เนล ABI ที่เกี่ยวข้อง ตัวอย่างเช่นไฟล์รายการสัญลักษณ์ที่มีเนื้อหาดังต่อไปนี้ข้อ จำกัด การวิเคราะห์ ABI สัญลักษณ์เอลฟ์ที่มีชื่อ symbol1 และ symbol2 :

[abi_symbol_list]
   symbol1
   symbol2

ไม่พิจารณาการเปลี่ยนแปลงสัญลักษณ์เอลฟ์อื่นๆ แฟ้มรายการสัญลักษณ์สามารถระบุได้ในที่สอดคล้อง build.config แฟ้มการกำหนดค่ากับ KMI_SYMBOL_LIST= เป็นไฟล์ที่สัมพันธ์กับไดเรกทอรีมาเคอร์เนล ( $KERNEL_DIR ) เพื่อให้ระดับขององค์กรคุณสามารถระบุไฟล์รายการสัญลักษณ์เพิ่มเติมโดยใช้ ADDITIONAL_KMI_SYMBOL_LISTS= ใน build.config ไฟล์ นี้ระบุเพิ่มเติมไฟล์รายการสัญลักษณ์เทียบกับ $KERNEL_DIR ; แยกชื่อไฟล์หลายๆ ชื่อด้วยช่องว่าง

การสร้างรายการสัญลักษณ์เริ่มต้นหรือการปรับปรุงการหนึ่งที่มีอยู่คุณต้องใช้ build_abi.sh สคริปต์กับ --update-symbol-list พารามิเตอร์

เมื่อสคริปต์ที่จะดำเนินการกับการกำหนดค่าที่เหมาะสมมันสร้างเคอร์เนลและสารสกัดจากสัญลักษณ์ที่มีการส่งออกจาก vmlinux และ GKI โมดูลและที่จำเป็นโดยโมดูลอื่น ๆ ในต้นไม้

พิจารณา vmlinux ส่งออกสัญลักษณ์ต่อไป (โดยปกติทำผ่าน EXPORT_SYMBOL* มาโคร):

  func1
  func2
  func3

นอกจากนี้ยังมีจินตนาการมีสองโมดูลผู้ขาย modA.ko และ modB.ko ซึ่งต้องใช้สัญลักษณ์ต่อไปนี้ (ในคำอื่น ๆ ที่พวกเขารายการ undefined รายการสัญลักษณ์ในตารางสัญลักษณ์ของพวกเขา):

 modA.ko:    func1 func2
 modB.ko:    func2

จากจุดเสถียรภาพ ABI ในมุมมองของ func1 และ func2 จะต้องเก็บไว้มีเสถียรภาพขณะที่พวกเขากำลังใช้โดยโมดูลภายนอก ในทางตรงกันข้ามในขณะที่ func3 มีการส่งออกก็ไม่ได้ใช้งาน (ในคำอื่น ๆ ก็ไม่จำเป็น) โดยโมดูลใด ๆ ดังนั้นรายการสัญลักษณ์มี func1 และ func2 เท่านั้น

การสร้างหรือปรับปรุงรายการสัญลักษณ์ที่มีอยู่ build_abi.sh ต้องทำงานดังต่อไปนี้:

BUILD_CONFIG=path/to/build.config.device build/build_abi.sh --update-symbol-list

ในตัวอย่างนี้ build.config.device จะต้องมีการตั้งค่าตัวเลือกหลาย

  • vmlinux จะต้องอยู่ใน FILES รายการ
  • KMI_SYMBOL_LIST ต้องเป็นชุดและชี้ไปที่รายการสัญลักษณ์ KMI การอัปเดต
  • GKI_MODULES_LIST ต้องเป็นชุดและชี้ไปที่รายชื่อของโมดูล GKI เส้นทางนี้มักจะเป็น android/gki_aarch64_modules

การทำงานกับเครื่องมือ ABI ระดับล่าง

ผู้ใช้ส่วนใหญ่จะต้องใช้ build_abi.sh ในบางกรณี อาจจำเป็นต้องทำงานโดยตรงกับเครื่องมือ ABI ระดับล่าง ทั้งสองคำสั่งที่ใช้โดย build_abi.sh , dump_abi และ diff_abi มีไว้เพื่อสารสกัดและเปรียบเทียบไฟล์ ABI ดูส่วนต่อไปนี้สำหรับการใช้งาน

การสร้างการแทนค่า ABI จากต้นไม้เคอร์เนล

ให้ต้นไม้ลินุกซ์เคอร์เนลที่มีในตัว vmlinux และเคอร์เนลโมดูลเครื่องมือ dump_abi สร้างตัวแทน ABI ABI ใช้เครื่องมือที่เลือก ตัวอย่างการเรียกใช้มีลักษณะดังนี้:

dump_abi --linux-tree path/to/out --out-file /path/to/abi.xml

แฟ้ม abi.xml มีตัวแทน ABI ต้นฉบับเดิมของรวมกันสังเกต ABI ของ vmlinux และโมดูลเคอร์เนลในไดเรกทอรีที่กำหนด ไฟล์นี้อาจใช้สำหรับการตรวจสอบด้วยตนเอง การวิเคราะห์เพิ่มเติม หรือเป็นไฟล์อ้างอิงเพื่อบังคับใช้ความเสถียรของ ABI

การเปรียบเทียบการแทนค่า ABI

การแสดงที่สร้างขึ้นโดย ABI dump_abi สามารถเทียบกับ diff_abi ใช้เหมือนกัน ABI เครื่องมือสำหรับทั้ง dump_abi และ diff_abi ตัวอย่างการเรียกใช้มีลักษณะดังนี้:

diff_abi --baseline abi1.xml --new abi2.xml --report report.out

รายการรายงานที่สร้างขึ้นตรวจพบการเปลี่ยนแปลง ABI ที่ส่งผลต่อ KMI ไฟล์ที่ระบุเป็น baseline และ new เป็นตัวแทน ABI ที่ถูกเก็บรวบรวมด้วย dump_abi diff_abi แพร่กระจายรหัสทางออกของเครื่องมือพื้นฐานและดังนั้นจึงส่งกลับไม่ใช่ศูนย์ค่าเมื่อ ABIs เมื่อเทียบกันไม่ได้

การใช้รายการสัญลักษณ์ KMI

เพื่อแสดงตัวกรองที่สร้างขึ้นด้วย dump_abi สัญลักษณ์หรือตัวกรองเทียบกับ diff_abi ใช้พารามิเตอร์ --kmi-symbol-list ที่ต้องใช้เส้นทางไปยังแฟ้ม KMI รายการสัญลักษณ์:

dump_abi --linux-tree path/to/out --out-file /path/to/abi.xml --kmi-symbol-list /path/to/symbol_list_file

การเปรียบเทียบไบนารีเคอร์เนลกับการอ้างอิง GKI KMI

ขณะที่คุณกำลังทำงานในการปฏิบัติตาม GKI Kernel ก็มีประโยชน์ในการเปรียบเทียบเป็นประจำสร้างเคอร์เนลท้องถิ่นเพื่อการอ้างอิง GKI KMI การแสดงได้โดยไม่ต้องใช้ build_abi.sh เครื่องมือ gki_check เป็นเครื่องมือที่มีน้ำหนักเบาเพื่อทำตรงนั้น กำหนดโครงสร้างลินุกซ์เคอร์เนลในเครื่อง ตัวอย่างการเรียกใช้เพื่อเปรียบเทียบการแทนค่าไบนารีในเครื่องกับตัวอย่าง นี่คือวิธีเปรียบเทียบกับการแทนค่า 5.4:

build/abi/gki_check --linux-tree path/to/out/ --kernel-version 5.4

gki_check ใช้ชื่อพารามิเตอร์สอดคล้องกับ dump_abi และ diff_abi ดังนั้น --kmi-symbol-list path/to/kmi_symbol_list_file สามารถนำมาใช้เพื่อ จำกัด การเปรียบเทียบกับสัญลักษณ์ที่ได้รับอนุญาตว่าโดยผ่านรายการสัญลักษณ์ KMI

การจัดการกับการแตกหักของ ABI

ตัวอย่างเช่น แพตช์ต่อไปนี้แนะนำการแตกของ ABI ที่ชัดเจนมาก:

 diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
  index 5ed8f6292a53..f2ecb34c7645 100644
  --- a/include/linux/mm_types.h
  +++ b/include/linux/mm_types.h
  @@ -339,6 +339,7 @@ struct core_state {
   struct kioctx_table;
   struct mm_struct {
      struct {
  +       int dummy;
          struct vm_area_struct *mmap;            /* list of VMAs */
          struct rb_root mm_rb;
          u64 vmacache_seqnum;                   /* per-thread vmacache */

เมื่อคุณเรียก build_abi.sh อีกครั้งกับแพทช์นี้ใช้ออกจากแม่พิมพ์ที่มีไม่ใช่ศูนย์รหัสข้อผิดพลาดและรายงานความแตกต่าง ABI ที่คล้ายกันนี้:

 Leaf changes summary: 1 artifact changed
  Changed leaf types summary: 1 leaf type changed
  Removed/Changed/Added functions summary: 0 Removed, 0 Changed, 0 Added function
  Removed/Changed/Added variables summary: 0 Removed, 0 Changed, 0 Added variable

  'struct mm_struct at mm_types.h:372:1' changed:
    type size changed from 6848 to 6912 (in bits)
    there are data member changes:
  [...]

แก้ไข ABI ที่เสียหายบน Android Gerrit

หากคุณไม่ได้ตั้งใจทำลาย ABI ของเคอร์เนล คุณต้องตรวจสอบโดยใช้คำแนะนำจากเครื่องมือตรวจสอบ ABI สาเหตุที่พบบ่อยที่สุดของการแตกหักคือการเพิ่มหรือลบฟังก์ชัน การเปลี่ยนแปลงโครงสร้างข้อมูล หรือการเปลี่ยนแปลง ABI ที่เกิดจากการเพิ่มตัวเลือกการกำหนดค่าที่นำไปสู่สิ่งที่กล่าวมาข้างต้น เริ่มต้นด้วยการแก้ไขปัญหาที่พบโดยเครื่องมือ

คุณสามารถทำซ้ำการทดสอบ ABI ในประเทศโดยใช้คำสั่งดังต่อไปนี้มีข้อโต้แย้งเดียวกับที่คุณจะได้ใช้สำหรับการทำงาน build/build.sh :

นี่คือตัวอย่างคำสั่งสำหรับเมล็ด GKI:

BUILD_CONFIG=common/build.config.gki.aarch64 build/build_abi.sh

กำลังอัปเดตเคอร์เนล ABI

หากคุณจำเป็นต้องปรับปรุงเคอร์เนลตัวแทน ABI แล้วคุณจะต้องอัปเดตที่สอดคล้องกัน abi.xml ไฟล์ในต้นไม้มาเคอร์เนล วิธีที่สะดวกที่สุดในการทำเช่นนี้คือการใช้ build/build_abi.sh ชอบโดย:

build/build_abi.sh --update --print-report

ใช้อาร์กิวเมนต์เดียวกับที่คุณจะได้ใช้ในการทำงาน build/build.sh นี้การปรับปรุงที่ถูกต้อง abi.xml ในต้นไม้แหล่งที่มาและพิมพ์ที่ตรวจพบความแตกต่าง ในทางปฏิบัติ ให้รวมรายงานที่พิมพ์ออกมา (แบบสั้น) ไว้ในข้อความยืนยัน (อย่างน้อยบางส่วน)

สาขาเคอร์เนลของ Android พร้อม ABI . ที่กำหนดไว้ล่วงหน้า

สาขาเคอร์เนลบางสาขามาพร้อมกับการแสดง ABI ที่กำหนดไว้ล่วงหน้าสำหรับ Android ซึ่งเป็นส่วนหนึ่งของการกระจายแหล่งที่มา เหล่านี้เป็นตัวแทน ABI มีวัตถุประสงค์เพื่อความถูกต้องและเพื่อสะท้อนให้เห็นถึงผลของ build_abi.sh เช่นถ้าคุณจะดำเนินการได้ด้วยตัวคุณเอง ในฐานะที่เป็น ABI ได้รับอิทธิพลอย่างมากจากตัวเลือกการตั้งค่า kernel ต่างๆเหล่านี้ .xml ไฟล์มักจะอยู่ในการกำหนดค่าบางอย่าง ยกตัวอย่างเช่นการ common-android12-5.10 สาขามี abi_gki_aarch64.xml ที่สอดคล้องกับการสร้างผลเมื่อใช้ build.config.gki.aarch64 โดยเฉพาะอย่างยิ่ง build.config.gki.aarch64 ยังหมายถึงไฟล์นี้ผ่าน ABI_DEFINITION

ดังกล่าวกำหนดไว้ล่วงหน้าเป็นตัวแทน ABI จะถูกใช้เป็นคำนิยามพื้นฐานเมื่อเทียบกับ diff_abi ยกตัวอย่างเช่นในการตรวจสอบแพทช์เคอร์เนลเกี่ยวกับการเปลี่ยนแปลงใด ๆ ที่จะ ABI สร้างตัวแทน ABI กับแพทช์ที่ใช้และการใช้ diff_abi เพื่อเปรียบเทียบกับ ABI คาดว่าแหล่งต้นไม้ที่เฉพาะเจาะจงหรือการกำหนดค่า หาก ABI_DEFINITION มีการตั้งค่าการทำงาน build_abi.sh ตามจะทำ

การบังคับใช้ KMI โดยใช้การกำหนดเวอร์ชันโมดูล

GKI เมล็ดใช้ โมดูลเวอร์ชัน ( CONFIG_MODVERSIONS ) เพื่อบังคับตาม KMI ที่รันไทม์ เวอร์ชันโมดูลจะทำให้เกิดความล้มเหลวของซีอาร์ซีไม่ตรงกันในโมดูลโหลดเวลาถ้าคาดว่า KMI ของโมดูลไม่ตรงกับ vmlinux KMI ยกตัวอย่างเช่นที่นี่คือความล้มเหลวทั่วไปที่เกิดขึ้นในโมดูลโหลดเวลาที่กำหนดจะไม่ตรงกัน 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 ''

การกำหนดเวอร์ชันโมดูลใช้

การกำหนดเวอร์ชันโมดูลมีประโยชน์ด้วยเหตุผลหลายประการ:

  1. ตรวจจับการเปลี่ยนแปลงในการมองเห็นโครงสร้างข้อมูล หากโมดูลสามารถเปลี่ยนโครงสร้างข้อมูลทึบแสงได้ เช่น โครงสร้างข้อมูลที่ไม่ได้เป็นส่วนหนึ่งของ KMI โมดูลจะพังหลังจากการเปลี่ยนแปลงโครงสร้างในอนาคต
  2. เพิ่มการตรวจสอบรันไทม์เพื่อหลีกเลี่ยงการโหลดโมดูลที่ไม่รองรับ KMI กับเคอร์เนลโดยไม่ได้ตั้งใจ (เช่นเมื่อโหลดโมดูลปัจจุบันในภายหลังโดยเคอร์เนลใหม่ที่เข้ากันไม่ได้) วิธีนี้ดีกว่าที่จะมีปัญหารันไทม์ที่ยากต่อการแก้ไขในภายหลังหรือเคอร์เนลขัดข้อง
  3. abidiff มีข้อ จำกัด ในการระบุความแตกต่าง ABI ในกรณีที่ซับซ้อนบางอย่างที่ CONFIG_MODVERSIONS สามารถจับ

เป็นตัวอย่างสำหรับ (1) การพิจารณา fwnode เขตข้อมูลใน struct device ข้อมูลที่จะต้องทึบแสงโมดูลเพื่อให้พวกเขาไม่สามารถทำให้การเปลี่ยนแปลงด้านการ device->fw_node หรือสมมติฐานที่ทำเกี่ยวกับขนาดของมัน

แต่ถ้าโมดูลรวมถึง <linux/fwnode.h> (โดยตรงหรือโดยอ้อม) แล้ว fwnode ข้อมูลใน struct device หยุดเป็นทึบแสงกับมัน โมดูลแล้วสามารถทำการเปลี่ยนแปลง device->fwnode->dev หรือ device->fwnode->ops นั่นเป็นปัญหาด้วยเหตุผลหลายประการ:

  1. มันสามารถทำลายสมมติฐานที่รหัสเคอร์เนลหลักทำเกี่ยวกับโครงสร้างข้อมูลภายใน

  2. ถ้ามีการปรับปรุงในอนาคตเคอร์เนลเปลี่ยนแปลง struct fwnode_handle (ชนิดข้อมูลของ fwnode ) แล้วโมดูลจะไม่ทำงานกับเคอร์เนลใหม่ นอกจากนี้ abidiff จะไม่แสดงความแตกต่างใด ๆ เพราะโมดูลทำลาย KMI โดยตรงโดยการจัดการข้อมูลภายในโครงสร้างในรูปแบบที่ไม่สามารถจับภาพโดยเฉพาะการตรวจสอบฐานเป็นตัวแทน

การเปิดใช้งานการกำหนดเวอร์ชันของโมดูลจะป้องกันปัญหาเหล่านี้ทั้งหมด

กำลังตรวจหา CRC ไม่ตรงกันโดยไม่ต้องบูตอุปกรณ์

ใด ๆ ที่สร้างเคอร์เนลกับ CONFIG_MODVERSIONS เปิดการใช้งานไม่สร้าง Module.symvers ไฟล์เป็นส่วนหนึ่งของการสร้างกระบวนการ ไฟล์ที่มีหนึ่งเส้นสำหรับสัญลักษณ์ส่งออกโดยทุก vmlinux และโมดูล แต่ละบรรทัดประกอบด้วยค่า CRC ชื่อสัญลักษณ์สัญลักษณ์ namespace, vmlinux หรือชื่อโมดูลที่ส่งออกสัญลักษณ์และประเภทการส่งออก (ตัวอย่างเช่น EXPORT_SYMBOL กับ EXPORT_SYMBOL_GPL )

คุณสามารถเปรียบเทียบ Module.symvers ไฟล์ระหว่างการสร้าง GKI และสร้างคุณเพื่อตรวจสอบความแตกต่างใด ๆ ในซีอาร์ซีสัญลักษณ์ส่งออกโดย 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=1 ในด้านหน้าของคำสั่งที่คุณใช้ในการสร้างเคอร์เนล หมายเหตุเมื่อใช้ build_abi.sh, นี้ถูกตั้งค่าโดยปริยายแล้ว นี้จะสร้าง .symtypes ไฟล์สำหรับแต่ละ .o ไฟล์ ตัวอย่างเช่น:

    KBUILD_SYMTYPES=1 BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
    
  2. ค้นหา .c ไฟล์ที่สัญลักษณ์กับซีอาร์ซีไม่ตรงกันจะถูกส่งออก ตัวอย่างเช่น:

    cd common && git grep EXPORT_SYMBOL.*module_layout
    kernel/module.c:EXPORT_SYMBOL(module_layout);
    
  3. นั่น .c ไฟล์มีสอดคล้อง .symtypes แฟ้มใน GKI และอุปกรณ์เคอร์เนลสร้างสิ่งประดิษฐ์ของคุณ

    cd out/$BRANCH/common && ls -1 kernel/module.*
    kernel/module.o
    kernel/module.o.symversions
    kernel/module.symtypes
    

    NS. รูปแบบของไฟล์นี้คือหนึ่งบรรทัด (อาจยาวมาก) ต่อสัญลักษณ์

    NS. [s|u|e|etc]# ในช่วงเริ่มต้นของเส้นหมายถึงสัญลักษณ์เป็นประเภทข้อมูล [struct|union|enum|etc] ] ยกตัวอย่างเช่น t#bool typedef _Bool bool

    ค. หายไป # คำนำหน้าในการเริ่มต้นของเส้นที่บ่งชี้สัญลักษณ์คือฟังก์ชั่น ตัวอย่างเช่น,
    find_module s#module * find_module ( const char * )

  4. เปรียบเทียบทั้งสองไฟล์และแก้ไขความแตกต่างทั้งหมด

กรณีที่ 1: ความแตกต่างเนื่องจากการมองเห็นประเภทข้อมูล

หากหนึ่งในเคอร์เนลช่วยให้เป็นสัญลักษณ์หรือชนิดข้อมูลทึบโมดูลและเคอร์เนลอื่น ๆ ไม่ได้แล้วจะแสดงขึ้นเป็นความแตกต่างระหว่าง .symtypes ไฟล์ของทั้งสองเมล็ด .symtypes ไฟล์จากหนึ่งในเมล็ดมี UNKNOWN สำหรับสัญลักษณ์และ .symtypes ไฟล์จากเคอร์เนลอื่น ๆ ที่มีมุมมองที่ขยายตัวของสัญลักษณ์หรือข้อมูลประเภท

ตัวอย่างเช่นสมมติว่าคุณเพิ่มบรรทัดนี้ include/linux/device.h ในเคอร์เนลของคุณ:

 #include <linux/fwnode.h>

ที่ทำให้เกิดความไม่ตรงกัน CRC กับหนึ่งในพวกเขาสำหรับ module_layout() ถ้าคุณเปรียบเทียบ 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 ทั่วไปใน kernel ของคุณเพื่อให้คุณใช้ฐาน GKI เคอร์เนลล่าสุด

เกือบตลอดเวลาที่ GKI เคอร์เนลมีมันเป็น UNKNOWN แต่เคอร์เนลของคุณมีรายละเอียดภายในของสัญลักษณ์นั้นเนื่องจากการเปลี่ยนแปลงที่เกิดขึ้นกับเคอร์เนลของคุณ เพราะนี่คือหนึ่งในไฟล์ในเคอร์เนลของคุณเพิ่ม #include ที่ไม่ได้อยู่ในเคอร์เนล GKI

เพื่อระบุ #include ที่ทำให้เกิดความแตกต่างให้ทำตามขั้นตอนเหล่านี้:

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

    #ifdef CRC_CATCH
    #error "Included from here"
    #endif
    
  3. จากนั้นในโมดูล .c ไฟล์ (หนึ่งที่มีไม่ตรงกันซีอาร์ซี), เพิ่มความต่อไปนี้เป็นบรรทัดแรกก่อนที่ใด ๆ ของ #include สาย

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

    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"
    
  5. หนึ่งของการเชื่อมโยงในห่วงโซ่ของนี้ #include เกิดจากการเปลี่ยนแปลงทำในเคอร์เนลของคุณที่ขาดหายไปในเคอร์เนล GKI

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

กรณีที่ 2: ความแตกต่างเนื่องจากการเปลี่ยนแปลงประเภทข้อมูล

หาก CRC ไม่ตรงกันสำหรับสัญลักษณ์หรือประเภทข้อมูลไม่ได้เกิดจากความแตกต่างในการมองเห็น นั่นเป็นเพราะการเปลี่ยนแปลงที่แท้จริง (การเพิ่ม การลบ หรือการเปลี่ยนแปลง) ในประเภทข้อมูลเอง โดยปกติ abidiff จับนี้ แต่ถ้าพลาดใด ๆ เนื่องจากเป็นที่รู้จักในช่องว่างการตรวจสอบที่ MODVERSIONS กลไกสามารถจับพวกเขา

ตัวอย่างเช่น สมมติว่าคุณทำการเปลี่ยนแปลงต่อไปนี้ในเคอร์เนลของคุณ:

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);

ที่จะทำให้เกิดจำนวนมากที่ไม่ตรงกันซีอาร์ซี (เหมือนสัญลักษณ์จำนวนมากได้รับผลกระทบทางอ้อมโดยแบ่งตามชนิดของการเปลี่ยนแปลงนี้) และหนึ่งในนั้นจะเป็นสำหรับ 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 ไฟล์)
  2. หากมีความแตกต่างที่เห็นได้ชัดระหว่างสัญลักษณ์เคอร์เนลและเคอร์เนล GKI ของคุณทำ git blame เพื่อหาสิ่งที่กระทำ
  3. บางครั้งสัญลักษณ์จะถูกลบในทรีหนึ่ง และคุณต้องการลบในทรีอื่น หากต้องการค้นหาการเปลี่ยนแปลงที่ลบบรรทัด ให้รันคำสั่งนี้บนทรีที่บรรทัดถูกลบ:

    NS. git log -S "copy paste of deleted line/word" -- <file where it was deleted>

    NS. คุณจะได้รับรายการคอมมิตที่สั้นลง คนแรกน่าจะเป็นคนที่คุณกำลังมองหา หากไม่เป็นเช่นนั้น ให้ไปที่รายการจนกว่าคุณจะพบการคอมมิต

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