مانیتورینگ ABI هسته اندروید

می توانید از ابزار نظارت بر رابط باینری برنامه (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+ صادر شده را شامل نمی شود. در عوض، نمادهایی که می‌توانند توسط ماژول‌های فروشنده استفاده شوند به صراحت در مجموعه‌ای از فایل‌های فهرست نمادها که به صورت عمومی در درخت هسته نگهداری می‌شوند فهرست می‌شوند ( gki/{ARCH}/symbols/* یا android/abi_gki_{ARCH}_* در Android 15 و پایین‌تر). اتحاد همه نمادها در همه فایل های لیست نمادها مجموعه ای از نمادهای KMI را به عنوان پایدار تعریف می کند. نمونه فایل لیست نمادها gki/aarch64/symbols/db845c است که نمادهای مورد نیاز برای DragonBoard 845c را اعلام می‌کند.

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

هر شاخه هسته هسته مشترک Android (ACK) مجموعه ای از لیست نمادها دارد. هیچ تلاشی برای ایجاد ثبات ABI بین شاخه های مختلف هسته KMI انجام نشده است. به عنوان مثال، KMI برای android12-5.10 کاملاً مستقل از KMI برای android13-5.10 است.

ابزارهای ABI از لیست نمادهای KMI برای محدود کردن اینترفیس هایی که باید برای پایداری نظارت شوند، استفاده می کنند. از فروشندگان انتظار می رود که لیست نمادهای خود را ارسال و به روز کنند تا اطمینان حاصل کنند که رابط هایی که به آنها متکی هستند سازگاری ABI را حفظ می کنند. به عنوان مثال، برای مشاهده لیستی از لیست نمادها برای هسته android16-6.12 ، به https://android.googlesource.com/kernel/common/+/refs/heads/android16-6.12/gki/aarch64/symbols مراجعه کنید.

لیست نمادها شامل نمادهایی است که برای فروشنده یا دستگاه خاص مورد نیاز گزارش شده است. فهرست کاملی که ابزارها استفاده می‌کنند، اتحاد همه فایل‌های فهرست نماد 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 در زمان ساخت شناسایی شد

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

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

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

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 در 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 همانطور که هست انتخاب شود. به عنوان مثال:

  • اگر یک تغییر ABI برای یک ویژگی باشد که می تواند با ACK به اشتراک گذاشته شود، آن تغییر را می توان به ACK همانطور که هست انتخاب کرد. به عنوان مثال، تغییرات زیر برای تست CTS یا VTS مورد نیاز نیست، اما برای به اشتراک گذاشتن با ACK مناسب است:

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

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

Stubها باید فقط برای تغییرات هسته اصلی که به نفع 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 در آن صادر شده است:

    git -C common grep EXPORT_SYMBOL.*module_layout
    kernel/module/version.c:EXPORT_SYMBOL(module_layout);
  3. فایل .c دارای یک فایل .symtypes مربوطه در GKI است و آرتیفکت های ساخت هسته دستگاه شما. با استفاده از دستورات زیر فایل .symtypes را پیدا کنید:

    cd bazel-bin/common/kernel_aarch64/symtypes
    ls -1 kernel/module/version.symtypes

    در Android 13 و پایین‌تر، با استفاده از اسکریپت‌های ساخت قدیمی، مکان احتمالاً یا out/$BRANCH/common یا out_abi/$BRANCH/common باشد.

    هر فایل .symtypes یک فایل متنی ساده است که از توضیحات نوع و نماد تشکیل شده است:

    • هر خط از key description فرم است که در آن توضیحات می تواند به کلیدهای دیگر در همان فایل اشاره کند.

    • کلیدهایی مانند [s|u|e|t]#foo به [struct|union|enum|typedef] foo اشاره دارد. به عنوان مثال:

      t#bool typedef _Bool bool
      
    • کلیدهای بدون پیشوند x# فقط نام نماد هستند. به عنوان مثال:

      find_module s#module * find_module ( const char * )
      
  4. دو فایل را مقایسه کنید و تمام تفاوت ها را برطرف کنید.

بهتر است symtypes با یک ساخت درست قبل از تغییر مشکل‌ساز و سپس در تغییر مشکل‌ساز تولید کنید. ذخیره همه فایل ها به این معنی است که می توان آنها را به صورت انبوه مقایسه کرد.

به عنوان مثال،

for f in $(find good bad -name '*.symtypes' | sed -r 's;^(good|bad)/;;' | LANG=C sort -u); do
  diff -N -U0 --label good/"$f" --label bad/"$f" <(LANG=C sort good/"$f") <(LANG=C sort bad/"$f")
done

در غیر این صورت، فقط فایل های خاص مورد علاقه را مقایسه کنید.

مورد 1: تفاوت های ناشی از قابلیت مشاهده نوع داده

یک #include جدید می‌تواند یک تعریف نوع جدید (مثلا struct foo ) را در یک فایل منبع بکشد، در این موارد، توضیحات آن در فایل .symtypes مربوطه از یک structure_type foo { } خالی به یک تعریف کامل تغییر می‌کند.

این روی همه CRCهای همه نمادها در فایل .symtypes که توضیحات آنها به طور مستقیم یا غیرمستقیم به تعریف نوع بستگی دارد، تأثیر می گذارد.

به عنوان مثال، افزودن خط زیر به فایل include/linux/device.h در هسته خود باعث عدم تطابق CRC می شود که یکی از آنها برای module_layout() است:

 #include <linux/fwnode.h>

مقایسه module/version.symtypes برای آن نماد، تفاوت های زیر را آشکار می کند:

 $ diff -u <GKI>/kernel/module/version.symtypes <your kernel>/kernel/module/version.symtypes
  --- <GKI>/kernel/module/version.symtypes
  +++ <your kernel>/kernel/module/version.symtypes
  @@ -334,12 +334,15 @@
  ...
  -s#fwnode_handle structure_type fwnode_handle { }
  +s#fwnode_reference_args structure_type fwnode_reference_args { s#fwnode_handle * fwnode ; unsigned int nargs ; t#u64 args [ 8 ] ; }
  ...

اگر هسته GKI دارای تعریف کامل نوع است، اما هسته شما آن را ندارد (بعید است)، پس آخرین هسته مشترک Android را در هسته خود ادغام کنید تا از آخرین پایه هسته GKI استفاده کنید.

در بیشتر موارد، هسته GKI فاقد تعریف نوع کامل در .symtypes است، اما هسته شما به دلیل دستورات #include اضافی آن را دارد.

وضوح برای اندروید 16 و بالاتر

مطمئن شوید که فایل منبع آسیب‌دیده شامل هدر تثبیت‌کننده Android KABI باشد:

#include <linux/android_kabi.h>

برای هر نوع آسیب‌دیده، ANDROID_KABI_DECLONLY(name); در دامنه جهانی به فایل منبع آسیب دیده.

به عنوان مثال، اگر تفاوت symtypes این بود:

--- good/drivers/android/vendor_hooks.symtypes
+++ bad/drivers/android/vendor_hooks.symtypes
@@ -1051 +1051,2 @@
-s#ubuf_info structure_type ubuf_info { }
+s#ubuf_info structure_type ubuf_info { member pointer_type { const_type { s#ubuf_info_ops } } ops data_member_location(0) , member t#refcount_t refcnt data_member_location(8) , member t#u8 flags data_member_location(12) } byte_size(16)
+s#ubuf_info_ops structure_type ubuf_info_ops { member pointer_type { subroutine_type ( formal_parameter pointer_type { s#sk_buff } , formal_parameter pointer_type { s#ubuf_info } , formal_parameter t#bool ) -> base_type void } complete data_member_location(0) , member pointer_type { subroutine_type ( formal_parameter pointer_type { s#sk_buff } , formal_parameter pointer_type { s#ubuf_info } ) -> base_type int byte_size(4) encoding(5) } link_skb data_member_location(8) } byte_size(16)

سپس مشکل این است که struct ubuf_info اکنون یک تعریف کامل در symtypes دارد. راه حل این است که یک خط به drivers/android/vendor_hooks.c اضافه کنید:

ANDROID_KABI_DECLONLY(ubuf_info);

این دستور به gendwarfksyms می‌دهد که نوع نام‌گذاری شده را در فایل تعریف نشده در نظر بگیرد.

یک احتمال پیچیده تر این است که #include جدید خودش در یک فایل هدر باشد. در این مورد ممکن است لازم باشد مجموعه‌های مختلفی از فراخوان‌های کلان ANDROID_KABI_DECLONLY را در بین فایل‌های منبع توزیع کنید که به طور غیرمستقیم تعاریف نوع اضافی را وارد می‌کنند، زیرا برخی از آنها ممکن است قبلاً برخی از تعاریف نوع را داشته باشند.

برای خوانایی، چنین فراخوانی های ماکرو را نزدیک ابتدای فایل منبع قرار دهید.

وضوح برای اندروید 15 و پایین تر

اغلب، راه حل فقط پنهان کردن #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 وجود ندارد.

مورد 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 structure_type 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 structure_type 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. پس از شناسایی commit، یا آن را در هسته خود برگردانید یا آن را به روز کنید تا تغییر CRC متوقف شود و آن را در ACK آپلود کنید و آن را ادغام کنید . هر شکست ABI باقیمانده باید از نظر ایمنی بررسی شود و در صورت لزوم می توان یک شکست مجاز را ثبت کرد.

ترجیحاً بالشتک موجود را مصرف کنید

برخی از ساختارها در GKI دارای بالشتک هستند تا امکان گسترش آنها بدون شکستن ماژول های فروشنده موجود فراهم شود. اگر یک commit بالادستی (به عنوان مثال) عضوی را به چنین ساختاری اضافه کند، ممکن است بتوان آن را تغییر داد تا بخشی از padding را مصرف کند. سپس این تغییر از محاسبه CRC پنهان می شود.

ماکرو استاندارد شده و مستندسازی شده ANDROID_KABI_RESERVE فضایی به ارزش u64 (تراز شده) را برای خود محفوظ می دارد. به جای یک اعلامیه عضو استفاده می شود.

به عنوان مثال:

struct data {
        u64 handle;
        ANDROID_KABI_RESERVE(1);
        ANDROID_KABI_RESERVE(2);
};

بالشتک را می‌توان بدون تأثیر بر CRC نمادها، با ANDROID_KABI_USE (یا ANDROID_KABI_USE2 یا انواع دیگری که ممکن است تعریف شوند) مصرف کرد.

عضو sekret به گونه‌ای در دسترس است که گویی مستقیماً اعلام شده است، اما ماکرو در واقع به یک عضو اتحادیه ناشناس که حاوی sekret و همچنین مواردی است که توسط gendwarfksyms برای حفظ ثبات symtype استفاده می‌شود، گسترش می‌یابد.

struct data {
        u64 handle;
        ANDROID_KABI_USE(1, void *sekret);
        ANDROID_KABI_RESERVE(2);
};
وضوح برای اندروید 16 و بالاتر

CRC ها توسط gendwarfksyms محاسبه می شوند که از اطلاعات اشکال زدایی DWARF استفاده می کند، بنابراین از هر دو نوع C و Rust پشتیبانی می کند. وضوح بسته به نوع تغییر نوع متفاوت است. در اینجا چند نمونه آورده شده است.

شمارشگرهای جدید یا اصلاح شده

گاهی اوقات شمارشگرهای جدید اضافه می شوند و گاهی اوقات مقدار MAX یا مشابه آن نیز تحت تأثیر قرار می گیرد. این تغییرات در صورتی ایمن هستند که از GKI «فرار» نکنند یا مطمئن باشیم که ماژول‌های فروشنده نمی‌توانند به مقادیر خود اهمیت دهند.

به عنوان مثال:

 enum outcome {
       SUCCESS,
       FAILURE,
       RETRY,
+      TRY_HARDER,
       OUTCOME_LIMIT
 };

افزودن TRY_HARDER و تغییر به OUTCOME_LIMIT می‌تواند از محاسبه CRC با فراخوان‌های کلان در دامنه جهانی پنهان شود:

ANDROID_KABI_ENUMERATOR_IGNORE(outcome, TRY_HARDER);
ANDROID_KABI_ENUMERATOR_VALUE(outcome, OUTCOME_LIMIT, 3);

برای خوانایی، اینها را درست بعد از تعریف enum قرار دهید.

یک عضو ساختار جدید که یک سوراخ موجود را اشغال می کند

به دلیل تراز، بایت های بلااستفاده بین urgent و scratch وجود خواهد داشت.

        void *data;
        bool urgent;
+       bool retry;
        void *scratch;

هیچ افست عضو موجود یا اندازه ساختار تحت تأثیر افزودن retry نمی‌شود. با این حال، ممکن است بر نمادهای CRC یا نمایش ABI یا هر دو تأثیر بگذارد.

این آن را از محاسبه CRC پنهان می کند:

        void *data;
        bool urgent;
+       ANDROID_KABI_IGNORE(1, bool retry);
        void *scratch_space;

retry عضو به گونه‌ای در دسترس است که گویی مستقیماً اعلام شده است، اما ماکرو در واقع به عضو اتحادیه ناشناس شامل retry و همچنین مواردی که توسط gendwarfksyms برای حفظ ثبات symtype استفاده می‌شود، گسترش می‌یابد.

گسترش یک ساختار با اعضای جدید

گاهی اوقات اعضا به انتهای یک ساختار اضافه می شوند. این بر افست اعضای موجود تأثیر نمی گذارد یا بر کاربران موجود ساختار که فقط با اشاره گر به آن دسترسی دارند تأثیر نمی گذارد. اندازه ساختار بر روی CRC آن تأثیر می گذارد و تغییرات در آن را می توان با یک فراخوانی کلان اضافی در محدوده جهانی، به شرح زیر سرکوب کرد:

struct data {
        u64 handle;
        u64 counter;
        ANDROID_KABI_IGNORE(1, void *sekret);
};

ANDROID_KABI_BYTE_SIZE(data, 16);

برای خوانایی، این را درست بعد از تعریف struct قرار دهید.

همه تغییرات دیگر به یک نوع یا نوع نماد

گاهی اوقات، ممکن است تغییراتی وجود داشته باشد که در یکی از دسته‌های قبلی قرار نگیرند، که منجر به تغییرات CRC می‌شود که با استفاده از ماکروهای قبلی نمی‌توان آنها را سرکوب کرد.

در این موارد، توصیف symtypes اصلی یک نوع یا نماد را می توان با فراخوانی ANDROID_KABI_TYPE_STRING در دامنه جهانی ارائه کرد.

struct data {
        /* extensive changes */
};

ANDROID_KABI_TYPE_STRING("s#data", "original s#data symtypes definition");

برای خوانایی، این را درست بعد از تعریف نوع یا نماد قرار دهید.

وضوح برای اندروید 15 و پایین تر

تغییرات نوع و نماد باید از genksyms پنهان شود. این را می توان با کنترل پیش پردازش با __GENKSYMS__ انجام داد.

تبدیل کد دلخواه را می توان به این شکل بیان کرد.

به عنوان مثال، برای پنهان کردن یک عضو جدید که سوراخی را در یک ساختار موجود اشغال کرده است:

struct parcel {
        void *data;
        bool urgent;
#ifndef __GENKSYMS__
        bool retry;
#endif
        void *scratch_space;
};