Android Kernel ABI Monitoring

می‌توانید از ابزار نظارت بر رابط باینری برنامه (ABI) که در اندروید 11 و بالاتر موجود است، برای تثبیت ABI درون هسته هسته‌های اندروید استفاده کنید. این ابزار نمایش های ABI را از باینری های هسته موجود (ماژول های vmlinux + GKI) جمع آوری و مقایسه می کند. این نمایش های ABI فایل های .stg و لیست های نماد هستند. رابطی که نمایش روی آن یک نما می دهد، رابط ماژول هسته (KMI) نامیده می شود. می توانید از ابزار برای ردیابی و کاهش تغییرات KMI استفاده کنید.

ابزار نظارت ABI در AOSP توسعه یافته است و از STG (یا libabigail در اندروید 13 و پایین تر) برای تولید و مقایسه بازنمایی ها استفاده می کند.

این صفحه ابزار، فرآیند جمع‌آوری و تجزیه و تحلیل نمایش‌های ABI، و استفاده از چنین نمایش‌هایی را برای ایجاد ثبات در ABI درون هسته توضیح می‌دهد. این صفحه همچنین اطلاعاتی را برای کمک به تغییرات در هسته اندروید ارائه می دهد.

روند

تجزیه و تحلیل ABI هسته چندین مرحله را انجام می دهد که بیشتر آنها می توانند خودکار شوند:

  1. هسته و نمایندگی ABI آن را بسازید .
  2. تفاوت های ABI بین ساخت و یک مرجع را تجزیه و تحلیل کنید .
  3. نمایندگی ABI (در صورت نیاز) را به روز کنید .
  4. با لیست نمادها کار کنید .

دستورالعمل های زیر برای هر هسته ای که می توانید با استفاده از زنجیره ابزار پشتیبانی شده (مانند زنجیره ابزار Clang از پیش ساخته شده) بسازید، کار می کند. repo manifests برای همه شاخه‌های هسته مشترک اندروید و برای چندین هسته خاص دستگاه، در دسترس هستند، آنها اطمینان می‌دهند که وقتی یک توزیع هسته برای تجزیه و تحلیل می‌سازید، از زنجیره ابزار صحیح استفاده می‌شود.

لیست نمادها

KMI همه نمادها را در هسته یا حتی همه نمادهای 30000+ صادر شده را شامل نمی شود. درعوض، نمادهایی که می‌توانند توسط ماژول‌های فروشنده استفاده شوند، به صراحت در مجموعه‌ای از فایل‌های فهرست نمادها که به صورت عمومی در ریشه درخت هسته نگهداری می‌شوند، فهرست می‌شوند. اتحاد همه نمادها در همه فایل های لیست نمادها مجموعه ای از نمادهای KMI را به عنوان پایدار تعریف می کند. نمونه فایل لیست نمادها abi_gki_aarch64_db845c است که نمادهای مورد نیاز برای DragonBoard 845c را اعلام می کند.

فقط نمادهای فهرست شده در فهرست نمادها و ساختارها و تعاریف مرتبط با آنها بخشی از KMI محسوب می شوند. اگر نمادهای مورد نیاز شما موجود نباشد، می توانید تغییراتی را در لیست نمادهای خود ارسال کنید. پس از اینکه اینترفیس‌های جدید در لیست نماد قرار گرفتند و بخشی از توضیحات KMI هستند، به‌عنوان پایدار نگه داشته می‌شوند و نباید از فهرست نمادها حذف شوند یا پس از مسدود شدن شاخه اصلاح شوند.

هر شاخه هسته هسته مشترک Android (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 موجود مجاز نیست. آنها پایدار هستند با این حال، تا زمانی که اضافه‌ها بر پایداری ABI موجود تأثیری نگذارند، فروشندگان می‌توانند در هر زمان نمادهایی را به KMI اضافه کنند. نمادهای جدید اضافه شده به محض اینکه توسط لیست نمادهای KMI ذکر شوند، ثابت نگه داشته می شوند. نمادها نباید از لیست یک هسته حذف شوند، مگر اینکه بتوان تأیید کرد که هیچ دستگاهی تا به حال با وابستگی به آن نماد ارسال نشده است.

می‌توانید با استفاده از دستورالعمل‌های نحوه کار با لیست‌های نماد، فهرست نمادهای KMI را برای دستگاه ایجاد کنید. بسیاری از شرکا یک لیست نماد در هر ACK ارسال می کنند، اما این یک نیاز سخت نیست. اگر به نگهداری کمک می کند، می توانید چندین لیست نماد را ارسال کنید.

KMI را گسترش دهید

در حالی که نمادهای KMI و ساختارهای مرتبط به‌عنوان پایدار حفظ می‌شوند (به این معنی که تغییراتی که رابط‌های پایدار را در یک هسته با KMI منجمد می‌شکنند، قابل قبول نیستند)، هسته GKI برای برنامه‌های افزودنی باز می‌ماند تا دستگاه‌هایی که در اواخر سال ارسال می‌شوند نیازی به تعریف همه نداشته باشند. وابستگی آنها قبل از اینکه KMI منجمد شود. برای گسترش KMI، می توانید نمادهای جدیدی را برای عملکردهای هسته صادر شده جدید یا موجود به KMI اضافه کنید، حتی اگر KMI ثابت باشد. وصله‌های هسته جدید نیز ممکن است پذیرفته شوند اگر KMI را خراب نکنند.

درباره خرابی های KMI

یک هسته دارای منابع است و باینری ها از آن منابع ساخته می شوند. شاخه های هسته تحت نظارت ABI شامل یک نمایش ABI از GKI ABI فعلی (در قالب یک فایل .stg ) است. پس از ساخت باینری ها ( vmlinux ، Image و هر ماژول GKI)، می توان یک نمایش ABI از باینری ها استخراج کرد. هر تغییری که در فایل منبع هسته ایجاد شود ممکن است بر باینری ها تأثیر بگذارد و به نوبه خود بر روی .stg استخراج شده نیز تأثیر بگذارد. تحلیلگر AbiAnalyzer فایل .stg متعهد را با فایل استخراج شده از مصنوعات ساخت مقایسه می کند و اگر تفاوت معنایی پیدا کند، روی تغییر در Gerrit یک برچسب Lint-1 قرار می دهد.

شکستگی های 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 در زمان ساخت شناسایی شد

رایج ترین دلیل برای خطاها زمانی است که یک راننده از نماد جدیدی از هسته استفاده می کند که در هیچ یک از لیست نمادها وجود ندارد.

اگر نماد در لیست نمادها ( android/abi_gki_aarch64 ) گنجانده نشده است، ابتدا باید تأیید کنید که با EXPORT_SYMBOL_GPL( symbol_name ) صادر شده است و سپس نمایش ABI XML و لیست نمادها را به روز کنید. به عنوان مثال، تغییرات زیر ویژگی جدید Incremental FS را به شاخه android-12-5.10 اضافه می کند که شامل به روز رسانی لیست نمادها و نمایش ABI XML است.

اگر نماد صادر شده باشد (چه توسط شما یا قبلاً صادر شده است) اما هیچ راننده دیگری از آن استفاده نمی کند، ممکن است با خطای ساخت مشابه زیر مواجه شوید.

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 مراجعه کنید.

شکستگی های ABI هسته را حل کنید

شما می توانید با تغییر دادن کد به منظور عدم تغییر ABI یا به روز رسانی نمایش ABI، شکستگی های هسته ABI را مدیریت کنید. از نمودار زیر برای تعیین بهترین رویکرد برای موقعیت خود استفاده کنید.

نمودار جریان شکست ABI

شکل 1. وضوح شکست ABI

کد Refactor برای جلوگیری از تغییرات ABI

تمام تلاش خود را برای جلوگیری از اصلاح ABI موجود انجام دهید. در بسیاری از موارد، می‌توانید کد خود را تغییر دهید تا تغییراتی که بر ABI تأثیر می‌گذارند حذف کنید.

  • Refactoring تغییرات میدان ساختار. اگر تغییری ABI را برای یک ویژگی اشکال‌زدایی تغییر داد، یک #ifdef در اطراف فیلدها (در ساختارها و منابع منبع) اضافه کنید و مطمئن شوید که CONFIG مورد استفاده برای #ifdef برای defconfig تولید و gki_defconfig غیرفعال است. برای مثالی از اینکه چگونه می‌توان یک پیکربندی اشکال زدایی را بدون شکستن ABI به یک ساختار اضافه کرد، به این پچ‌ست مراجعه کنید.

  • Refactoring ویژگی ها برای تغییر نکردن هسته اصلی. اگر برای پشتیبانی از ماژول‌های شریک نیاز است ویژگی‌های جدیدی به 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 مناسب است:

  • اگر تغییر ABI ویژگی جدیدی را معرفی کرد که نیازی به گنجاندن آن در ACK نیست، می‌توانید همانطور که در بخش زیر توضیح داده شده است با استفاده از یک خرد نمادها را به ACK معرفی کنید.

از خرد برای ACK استفاده کنید

خرده‌ها باید فقط برای تغییرات هسته اصلی که به نفع ACK نیستند، مانند تغییرات عملکرد و قدرت، ضروری باشند. فهرست زیر نمونه‌هایی از خرده‌ها و گیلاس‌های جزئی در ACK برای GKI را شرح می‌دهد.

  • خرد ویژگی Core-Isolate ( aosp/1284493 ). عملکرد در ACK ضروری نیست، اما نمادها باید در ACK وجود داشته باشند تا ماژول های شما از این نمادها استفاده کنند.

  • نماد جای جای ماژول فروشنده ( aosp/1288860 ).

  • ویژگی ردیابی رویداد در mm فرآیند فقط برای انتخاب گیلاس ABI ( aosp/1288454 ). وصله اصلی به ACK انتخاب شد و سپس برش داده شد تا فقط تغییرات لازم برای حل تفاوت ABI برای task_struct و mm_event_count را شامل شود. این پچ همچنین mm_event_type enum را به‌روزرسانی می‌کند تا اعضای نهایی را در بر بگیرد.

  • تغییرات جزئی ساختار حرارتی 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 را با استفاده از نسخه‌سازی ماژول اجرا کنید

هسته‌های تصویر هسته عمومی (GKI) از نسخه‌سازی ماژول ( CONFIG_MODVERSIONS ) به‌عنوان یک اقدام اضافی برای اعمال انطباق KMI در زمان اجرا استفاده می‌کنند. اگر KMI مورد انتظار یک ماژول با vmlinux KMI مطابقت نداشته باشد، نسخه‌سازی ماژول می‌تواند باعث خرابی عدم تطابق بررسی افزونگی چرخه‌ای (CRC) در زمان بارگذاری ماژول شود. به عنوان مثال، موارد زیر یک شکست معمولی است که در زمان بارگذاری ماژول به دلیل عدم تطابق 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 را به عنوان بخشی از فرآیند ساخت عادی تولید می‌کند. این فایل دارای یک خط برای هر نماد صادر شده توسط هسته ( 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
    

    این دستور برای هر فایل .o یک فایل .symtypes ایجاد می کند. برای جزئیات به KBUILD_SYMTYPES در Kleaf مراجعه کنید.

    برای اندروید 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: تفاوت های ناشی از قابلیت مشاهده نوع داده

اگر یک هسته یک نماد یا نوع داده را برای ماژول ها مات نگه می دارد و هسته دیگر این کار را نمی کند، این تفاوت بین فایل های .symtypes دو هسته ظاهر می شود. فایل .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 را در هسته خود ادغام کنید تا از آخرین پایه هسته 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 برای 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، commit را با اجرای دستور زیر پیدا کنید:
    git blame
    
    • برای نمادهای حذف شده (جایی که یک نماد در یک درخت حذف می شود و شما همچنین می خواهید آن را در درخت دیگر حذف کنید)، باید تغییری را پیدا کنید که باعث حذف خط شده است. از دستور زیر در درختی که خط حذف شده است استفاده کنید:
    git log -S "copy paste of deleted line/word" -- <file where it was deleted>
    
  2. برای تعیین محل تغییر یا حذف، فهرست بازگشتی از commit ها را مرور کنید. اولین commit احتمالا همانی است که شما به دنبال آن هستید. اگر اینطور نیست، لیست را مرور کنید تا commit را پیدا کنید.

  3. پس از شناسایی تغییر، یا آن را در هسته خود برگردانید یا آن را در ACK آپلود کنید و آن را ادغام کنید .