מעקב אחר ABI של ליבה של Android

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

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

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

תהליך

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

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

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

רשימות סמלים

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

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

הרחבת KMI

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

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

לליבה יש מקורות וקבצים בינאריים נוצרים מהמקורות האלה. ההסתעפויות של הליבה שנמצאות במעקב אחרי ABI כוללות ייצוג ABI של ABI הנוכחי של GKI (בצורת קובץ .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.

  • דוגמה לשינוי תכונה נמצאת aosp/1345659.
  • הדוגמה לרשימת הסמלים נמצאת aosp/1346742.
  • דוגמה לשינוי ב-XML של ABI מופיעה ב-aosp/1349377.

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

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

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

תרשים זרימה של הפרעה ב-ABI

איור 1. פתרון של שגיאות ABI

שינוי הקוד כדי למנוע שינויים ב-ABI

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

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

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

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

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

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

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

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

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

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

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

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

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

  3. עדכון הייצוג של ABI של הקובץ העזר

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

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

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

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

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

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

שימוש ב-stubs ל-ACK

צריך להשתמש ב-stubs רק בשינויים בליבה של הליבה שלא תורמים ל-ACK, כמו שינויים בביצועים ובצריכת החשמל. ברשימה הבאה מפורטות דוגמאות לקטעי קוד חלולים ולבחירות חלקיות ב-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). התיקון הזה הוסיף את השינויים הנדרשים במבנה ל-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 מלא של הליבה עם הפעלת CONFIG_MODVERSIONS יוצר קובץ Module.symvers כחלק מתהליך ה-build הרגיל. הקובץ הזה מכיל שורה אחת לכל סמל שיוצאו על ידי הליבה (vmlinux) והמודולים. כל אחד השורה מורכבת מערך CRC, שם הסמל, מרחב השמות של הסמלים, vmlinux או שם המודול שמייצא את הסמל, ואת סוג הייצוא (לדוגמה, EXPORT_SYMBOL מול EXPORT_SYMBOL_GPL).

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

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

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

לעיתים קרובות, התיקון רק מסתיר את #include החדש מ-genksyms.

#ifndef __GENKSYMS__
#include <linux/fwnode.h>
#endif

אחרת, כדי לזהות את הערך של #include שגורם להבדל, פועלים לפי השלבים הבאים:

  1. פותחים את קובץ הכותרת שמגדיר את הסמל או את סוג הנתונים הבדל. לדוגמה, אפשר לערוך את include/linux/fwnode.h של struct fwnode_handle.

  2. מוסיפים את הקוד הבא בחלק העליון של קובץ ה-header:

    #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. מזהים את השינוי, מבטלים אותו בליבה או מעלים אותו ל-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 היא עבור 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 ולבצע מיזוג.