מעקב באמצעות ממשק ABI בליבה (kernel) של Android

אפשר להשתמש בכלי למעקב אחרי ממשק בינארי של אפליקציה (ABI), שזמינים ב- Android מגרסה 11 ואילך, כדי לייצב את הליבה (kernel) ממשק ABI של הליבה של Android. הכלי אוסף ייצוגי ABI ומשווה ביניהם מקובצי ליבה בינאריים קיימים (vmlinux+ מודולים של GKI). ה-ABI הזה הם הקבצים .stg ורשימות הסמלים. הממשק ב- שהייצוג מעניק לתצוגה נקרא ממשק מודול ליבה (Kernel) (KMI). בעזרת הכלים האלה תוכלו לעקוב אחרי שינויים ב-KMI ולצמצם אותם.

הכלים למעקב ABI מפתח ב-AOSP ומשתמשת STG (או libabigail ב- Android מגרסה 13 ומטה) כדי ליצור ולהשוות. של משפטים יחידים,

בדף הזה נתאר את הכלים, תהליך האיסוף והניתוח של ממשק ABI ושימוש בייצוגים כאלה כדי לספק יציבות של ה-ABI המובנה. הדף הזה כולל גם מידע לגבי השינויים לליבות (kernel) של Android.

תהליך

ניתוח ה-ABI של הליבה כולל מספר שלבים, שרובם יכולים להיות אוטומטיים:

  1. פיתוח הליבה וייצוג ה-ABI שלה
  2. ניתוח ההבדלים ב-ABI בין ה-build לבין קובץ עזר.
  3. מעדכנים את ייצוג ה-ABI (אם נדרש).
  4. עבודה עם רשימות סמלים.

ההוראות הבאות מיועדות לכל הליבה (kernel) שאפשר ליצור באמצעות (למשל, ה-toolchain מובנה מראש). repo manifests זמינים לכל הסתעפויות הליבה (kernel) הנפוצות של Android, בליבות ספציפיות למכשיר, הן מבטיחות שנעשה שימוש בצרור הכלים הנכון לפתח התפלגות ליבה (kernel) לניתוח.

רשימות סמלים

ה-KMI לא כולל את כל הסמלים בליבה (kernel) או אפילו לא את כל סמלים שיוצאו. במקום זאת, הסמלים שבהם אפשר להשתמש במודולים של ספקים רשום במפורש בקבוצה של קובצי רשימת סמלים שמנוהלים באופן ציבורי ברמה הבסיסית (root) של עץ הליבה. איחוד של כל הסמלים בכל הקבצים של רשימת הסמלים מגדיר את קבוצת סמלי ה-KMI שנשמרת כיציבה. קובץ לדוגמה של רשימת סמלים תואם לערך abi_gki_aarch64_db845c, שמצהיר על הסמלים הנדרשים DragonBoard 845c.

רק הסמלים המפורטים ברשימת סמלים והמבנים הקשורים אליהם ההגדרות נחשבות לחלק מה-KMI. אפשר לפרסם שינויים ב אם הסמלים הדרושים לך לא נמצאים. אחרי השימוש בממשקים חדשים רשימת סמלים, והם חלק מתיאור ה-KMI, נשארות יציבות ואין להסיר אותו מרשימת הסמלים או לשנות אותו לאחר שההסתעפות קפוא.

לכל הסתעפות ליבה (KMI) של Android Common KMI יש קבוצת סמלים משלה. רשימות. לא נעשה ניסיון לספק יציבות ABI בין ליבה (kernel) שונה של 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. אין להסיר סמלים מרשימה של ליבה (kernel) אלא אם ניתן לאמת אותם שאף מכשיר לא שלח אף מכשיר שתלוי בסמל הזה.

אפשר ליצור רשימת סמלי KMI עבור מכשיר מסוים בעזרת ההוראות הבאות: איך לעבוד עם רשימות סמלים שותפים רבים שולחים רשימת סמלים אחת לכל ACK, אבל זו לא דרישה קשה. אם הדבר עוזר בתחזוקה, אפשר לשלוח כמה רשימות של סמלים.

הרחבת KMI

למרות שסמלי KMI ומבנים קשורים נשמרים כיציבים (כלומר אי אפשר לגרום לשינויים של ממשקים יציבים בליבה (kernel) עם KMI קפוא הליבה של GKI תישאר פתוחה לתוספים, כדי שמשלוח המכשירים בהמשך השנה לא צריכים להגדיר את כל יחסי התלות לפני שה-KMI קפוא. כדי להרחיב את KMI אפשר להוסיף ל-KMI סמלים חדשים עבור פונקציות ליבה (kernel) קיימות שיוצאו, גם אם ה-KMI קפוא. ליבה חדשה ייתכן שיתקבלו גם תיקונים אם הם לא ישברו את ה-KMI.

מידע על תקלות ב-KMI

לליבה יש מקורות וקבצים בינאריים נוצרים מהמקורות האלה. הסתעפויות ליבה (kernel) עם מעקב ABI כוללות ייצוג ABI של ה-GKI הנוכחי ABI (בקובץ .stg). אחרי הקבצים הבינאריים (vmlinux, Image וגם מודולים של GKI), ניתן לחלץ ייצוג ABI בינאריים. כל שינוי שמתבצע בקובץ מקור ליבה יכול להשפיע על הקבצים הבינאריים הפנייה תשפיע גם על .stg שחולץ. המנתח של AbiAnalyzer משווה את את קובץ .stg שהוקצה באמצעות הקובץ שנשלף מפריטי ה-build ומגדיר תווית איתור שגיאות בקוד (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.

כשמריצים build ABI עם התיקון הזה, הכלי יוצא עם קוד שגיאה שאינו אפס ומדווח על הבדל ב-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 שזוהו בזמן ה-build

הסיבה השכיחה ביותר לשגיאות היא כשנהג משתמש בסמל חדש ליבה (kernel) שלא מופיעה באף אחת מרשימות הסמלים.

אם הסמל לא כלול ברשימת הסמלים (android/abi_gki_aarch64), אז צריך קודם לוודא שהייצוא מיוצא עם EXPORT_SYMBOL_GPL(symbol_name) ולאחר מכן מעדכנים את ייצוג XML של ABI ורשימת סמלים. לדוגמה, השינויים הבאים מוסיפים את התכונה החדשה של המרות מצטברות להסתעפות android-12-5.10, כולל עדכון של רשימת הסימנים וייצוג XML של ABI.

אם הסמל מיוצא (אתם או שהוא ייצא בעבר), אבל לא נהג אחר משתמש בו, יכול להיות שתתקבל שגיאת build שדומה לזו.

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 גם בליבה (kernel) וגם ב-ACK (מידע נוסף זמין בקטע לעדכן את ייצוג ה-ABI). לדוגמה לעדכון ה-ABI XML ורשימת הסמלים ב-ACK, aosp/1367601.

פתרון תקלות ABI בליבה (kernel)

אפשר להתמודד עם תקלות ABI בליבה (kernel) על ידי ארגון מחדש של הקוד כך שלא ישנה את ממשק ABI או עדכון של ייצוג ABI. צריך להשתמש בהגדרות הבאות כדי לקבוע את הגישה המתאימה ביותר למצב שלכם.

תרשים זרימה של תקלות ABI

איור 1. רזולוציה של שבירת ABI

ארגון קוד מחדש כדי למנוע שינויים ב-ABI

עושים כל מאמץ כדי לא לשנות את ה-ABI הקיים. במקרים רבים, אפשר לארגן מחדש את הקוד כדי להסיר שינויים שמשפיעים על ה-ABI.

  • ארגון מחדש של שינויים בשדה struct. אם שינוי משנה את ה-ABI לניפוי באגים צריך להוסיף #ifdef מסביב לשדות (במבנים ובמקור הפניות) ולוודא שהשדה CONFIG שמשמש את #ifdef מושבת עבור הגדרת הייצור ו-gki_defconfig. דוגמה לאופן שבו ניפוי באגים אפשר להוסיף config ל-build בלי לשבור את ה-ABI, כאן מוסבר איך עושים את זה. patchset.

  • ארגון מחדש של התכונות כך שלא ישנו את הליבה (kernel) של הליבה. אם יש צורך בתכונות חדשות יתווסף ל-ACK כדי לתמוך במודולים של שותפים, נסו לשנות את ארגון ה-ABI חלק מהשינוי כדי להימנע משינוי ה-ABI של הליבה. לדוגמה לשימוש את ה-ABI הקיים של הליבה כדי להוסיף יכולות נוספות בלי לשנות את מתייחס ל-ABI של ליבה (kernel) aosp/1312213.

תיקון ממשק ABI לא תקין ב-Android Gerrit

אם לא שברתם באופן מכוון את ה-ABI של הליבה, תצטרכו לבדוק את הנושא בהתאם להנחיות של הכלים למעקב אחרי ממשק ABI. הבעיה הנפוצה ביותר הסיבות לשיבושים הם מבני נתונים משתנים והסמל המשויך ל-CRC או בגלל שינויים באפשרויות התצורה שמובילים לאחד מהערכים שצוינו למעלה. כדי להתחיל, צריך לטפל בבעיות שהכלי מצא.

אפשר לשחזר את ממצאי ה-ABI באופן מקומי, פיתוח הליבה וייצוג ה-ABI שלה

מידע על תוויות Linux-1

אם מעלים שינויים להסתעפות שמכילה KMI שהוקפא או סופי, כדי לוודא שהשינויים לא ישפיעו על היציבות, השינויים חייבים לעבור את AbiAnalyzer את ה-ABI בצורה לא תואמת. במהלך התהליך, AbiAnalyzer מחפש את דוח ABI שנוצר במהלך ה-build (build מורחב שמבצע את build רגיל ואז כמה שלבי חילוץ ABI והשוואה.

אם השדה AbiAnalyzer מוצא דוח שאינו ריק, מוגדרת התוויתlint-1 ופונקציית ייחסם שליחה של שינוי עד שיטופל; עד שה-patchset מקבל התווית Linux+1.

עדכון ה-ABI של הליבה

אם אי אפשר להימנע משינוי ה-ABI, צריך להחיל את השינויים בקוד את ייצוג ה-ABI, ואת רשימת הסמלים ל-ACK. כדי להגיע אל Linux להסיר את -1 ולא לשבור את התאימות של GKI, צריך לבצע את השלבים הבאים:

  1. מעלים שינויים בקוד ל-ACK.

  2. צריך להמתין לקבלת בדיקת קוד 2+ עבור ערכת התיקונים.

  3. מעדכנים את ייצוג ה-ABI של ההפניה.

  4. מיזוג השינויים בקוד והשינוי בעדכון ה-ABI.

העלאת שינויים בקוד ABI ל-ACK

העדכון של ה-ACK ABI תלוי בסוג השינוי שמבוצע.

  • אם שינוי ABI קשור לתכונה שמשפיעה על בדיקות CTS או VTS, בדרך כלל ניתן לבחור ב-ACK בדיוק כפי שהוא. מוצרים לדוגמה:

  • אם שינוי ב-ABI מיועד לתכונה שאפשר לשתף עם ה-ACK, ניתן לבחור ב-ACK בדיוק כפי שהוא. לדוגמה, השינויים הבאים לא נחוצים לבדיקת CTS או VTS, אבל אפשר לשתף אותם עם ACK:

  • אם בעקבות שינוי ב-ABI מופיעה תכונה חדשה שלא צריך לכלול את ACK, ניתן להציג את הסמלים של ACK באמצעות stub כפי שמתואר בסעיף הבא.

שימוש ב-stubs עבור ACK

ה-Stubs חייבים להיות נחוצים רק לביצוע שינויים בליבה (kernel) שלא משפרים את ACK, כמו שינויים בביצועים ובעוצמה. דוגמאות לפרטי הרשימה של stubs ובחירת זריזות חלקיות ב-ACK עבור GKI.

  • stub של פיצ'ר ליבה (aosp/1284493). היכולות ב-ACK לא נחוצות, אבל הסמלים צריכים להיות ב-ACK כדי שהמודולים ישתמשו בסמלים האלה.

  • סמל placeholder למודול ספק (aosp/1288860).

  • בחירת ממשק ABI בלבד: תכונת מעקב אחרי אירועים mm לפי תהליך (aosp/1288454). התיקון המקורי נבחר ב-ACK ואז נחתך כך שיכלול רק את השינויים הנדרשים כדי לפתור את הבדלי ה-ABI עבור task_struct mm_event_count. בתיקון הזה גם מתבצע עדכון של ה-enum mm_event_type כך שיכלול חברי המועדון הסופיים.

  • בדיקה חלקית של שינויים ב-ABI של מבנה תרמי, שדרשו יותר מאשר ולהוסיף את שדות ה-ABI החדשים.

    • מדבקה aosp/1255544 פתרנו הבדלים ב-ABI בין ליבה (kernel) של השותף לבין ACK.

    • מדבקה aosp/1291018 תוקנו הבעיות הפונקציונליות שנמצאו במהלך בדיקת ה-GKI של התיקון הקודם. התיקון כלל אתחול של מבנה הפרמטרים של החיישן כדי לרשום כמה אזורים תרמיים בחיישן אחד.

  • CONFIG_NL80211_TESTMODE שינויים בממשק ABI (aosp/1344321). התיקון הזה הוסיף את שינויי ה-build הנדרשים ל-ABI ווידאת שהתיקון שדות נוספים לא גרמו להבדלים בפונקציונליות, ולכן השותפים אפשרו לכלול את CONFIG_NL80211_TESTMODE בליבות הייצור, ועדיין לשמור על תאימות ל-GKI.

אכיפת ה-KMI בזמן הריצה

הליבה של GKI משתמשת באפשרויות ההגדרה TRIM_UNUSED_KSYMS=y ו-UNUSED_KSYMS_WHITELIST=<union of all symbol lists>, שמגבילות את הסמלים המיוצאים (למשל סמלים שיוצאו באמצעות EXPORT_SYMBOL_GPL()) לאלה שרשומים של רשימת הסימנים. לא מתבצע ייצוא של כל שאר הסמלים, ומתבצעת טעינה של מודול שמחייב הסמל שלא יוצא הייצוא נדחה. ההגבלה הזו נאכפת בזמן ה-build רשומות חסרות מסומנות.

למטרות פיתוח, אפשר להשתמש ב-build של ליבה (kernel) של GKI שלא כולל חיתוך סמלים (כלומר, ניתן להשתמש בכל הסמלים שמיוצאים בדרך כלל). כדי לאתר את גרסאות ה-build האלה, צריך לחפש את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.

נוסף על כך, build של ליבה (kernel) מלאה שהופעל בו CONFIG_MODVERSIONS יוצר Module.symvers כחלק מתהליך ה-build הרגיל. בקובץ הזה יש לכל סמל שיוצאו על ידי הליבה (vmlinux) והמודולים. כל אחד השורה מורכבת מערך CRC, שם הסמל, מרחב השמות של הסמלים, vmlinux או שם המודול שמייצא את הסמל, ואת סוג הייצוא (לדוגמה, EXPORT_SYMBOL מול EXPORT_SYMBOL_GPL).

אפשר להשוות בין קובצי Module.symvers של גרסת ה-build של GKI לבין ה-build שלך כדי לבדוק אם יש הבדלים ב-CRC לסמלים שיוצאו על ידי vmlinux. אם יש הוא הבדל בערך CRC בכל סמל שמיוצא על ידי vmlinux וגם משמש את אחד מהמודולים שטוענים במכשיר, המודול לא טעינה.

אם אין לך את כל פריטי המידע שנוצרו בתהליך הפיתוח (Artifact) של ה-build, אבל יש לך את vmlinux הקבצים של את הליבה של GKI והליבה, תוכלו להשוות את ערכי ה-CRC על ידי הרצת הפקודה הבאה על הליבה (kernel) והשוואת פלט:

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 הוא שורה אחת (אולי ארוכה מאוד) לכל סמל.

    • [s|u|e|etc]# בתחילת השורה פירושו שהסמל הוא מסוג נתונים [struct|union|enum|etc]. לדוגמה:

      t#bool typedef _Bool bool
      
    • אם חסרה קידומת # בתחילת השורה, סימן שהסמל הוא פונקציה. לדוגמה:

      find_module s#module * find_module ( const char * )
      
  4. משווים בין שני הקבצים ומתקנים את כל ההבדלים.

מקרה 1: הבדלים בגלל הרשאות הגישה של סוגי הנתונים

אם ליבה אחת משאירה סמל או סוג נתונים אטומים למודולים והשני אין ליבה (kernel), ההבדל הזה מופיע בין קובצי .symtypes של שתי הליבות. בקובץ .symtypes מאחת מהליבה יש UNKNOWN עבור סמל, ולקובץ .symtypes מהליבה השנייה יש תצוגה מורחבת של הסמל או סוג הנתונים.

לדוגמה, הוספת השורה הבאה הקובץ include/linux/device.h בליבה (kernel) גורם לחוסר התאמה ב-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 ] ; }
  ...

אם ערך הליבה (kernel) שלך הוא UNKNOWN וליבה של GKI יש תצוגה מורחבת מהסמל (סביר מאוד שלא סביר), ואז למזג את הגרסה העדכנית ביותר של Android Common Kernel את הליבה שלכם כדי להשתמש בבסיס הליבה העדכני של GKI.

ברוב המקרים, בליבה של GKI יש ערך של UNKNOWN, אבל הליבה כוללת את פרטים פנימיים של הסמל בגלל שינויים שבוצעו בליבה (kernel) שלך. הדבר כי אחד מהקבצים בליבה (kernel) הוסיף #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. עורכים את המודול. שגיאת זמן ה-build שמתקבלת מציגה את שרשרת קובץ הכותרת #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 נובע משינוי שבוצע ב הליבה חסרה בליבה (kernel) של GKI.

  5. מזהים את השינוי, מבטלים אותו בליבה (kernel) או להעלות אותו אל ACK ולמזג אותו.

מקרה 2: הבדלים בגלל שינויים בסוג הנתונים

אם חוסר ההתאמה של ה-CRC בסמל או בסוג הנתונים לא נובע מהבדלים חשיפה, אז הדבר נובע משינויים שבוצעו בפועל (הוספות, הסרות או שינויים) סוג הנתונים עצמו.

לדוגמה, ביצוע השינוי הבא בליבה (kernel) גורם לכמה 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 היא עבור 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. אחרי זיהוי השינוי, מחזירים אותו למצב הליבה (kernel) או להעלות אותו ל-ACK ולקבל אותו ממוזג.