مراقبة ABI لنظام تشغيل Android

يمكنك استخدام أدوات مراقبة واجهة التطبيق الثنائية (ABI)، المتوفّرة في Android 11 والإصدارات الأحدث، لتحسين استقرار واجهة ABI المضمّنة في نواة نظام التشغيل Android. تجمع الأدوات تمثيلات ABI من ملفات ثنائية النواة الحالية (vmlinux+ وحدات GKI) وتقارن بينها. تمثيلات ABI هذه هي ملفات .stg وقوائم الرموز. تُعرف الواجهة التي يعرض عليها التمثيل عرضًا باسم واجهة وحدة النواة (KMI). يمكنك استخدام الأدوات لتتبُّع التغييرات التي تطرأ على مؤشر KMI والحدّ منها.

أداة مراقبة واجهة التطبيق الثنائية (ABI) تم تطويرها في شركة AOSP ويستخدم STG (أو libabigail في Android 13 والإصدارات الأقدم) لإجراء عمليات الإنشاء والمقارنة والتمثيليات.

توضّح هذه الصفحة الأدوات وعملية جمع تمثيلات ABI وتحليلها واستخدام هذه التمثيلات لتوفير ثبات لـ ABI داخل النواة. تقدّم هذه الصفحة أيضًا معلومات عن المساهمة في إدخال تغييرات على نواة Android.

معالجة

يتطلّب تحليل ABI للنواة خطوات متعدّدة، يمكن تنفيذ معظمها تلقائيًا:

  1. إنشاء النواة وتمثيل واجهة التطبيق الثنائية (ABI)
  2. تحليل الاختلافات في ABI بين الإصدار والمرجع
  3. عدِّل تمثيل ABI (إذا لزم الأمر).
  4. استخدام قوائم الرموز

يمكن استخدام التعليمات التالية لأي التي يمكنك إنشاؤها باستخدام سلسلة أدوات متوافقة (مثل سلسلة أدوات Clang المصممة مسبقًا). تتوفر repo manifests لجميع فروع نواة Android الشائعة ولعدة ملفّات نواة تتعلّق بالأجهزة، وهي تضمن استخدام مجموعة الأدوات الصحيحة عند إنشاء إصدار نواة للتحليل.

قوائم الرموز

لا يتضمن مؤشر 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 الحالية. أنها مستقرة. ومع ذلك، يمكن للمورّدين إضافة رموز إلى KMI في أي وقت. طالما أنّ الإضافات لا تؤثر في استقرار واجهة التطبيق الثنائية (ABI) الحالية. تمت إضافتها مؤخرًا يتم الاحتفاظ بالرموز كثابتة بمجرد اقتباسها من خلال قائمة رموز KMI. يجب عدم إزالة الرموز من قائمة نواة ما ما لم يكن من الممكن تأكيد أنّه لم يتم شحن أي جهاز يتضمّن رمزًا يعتمد على هذا الرمز.

يمكنك إنشاء قائمة رموز KMI لجهاز باستخدام التعليمات الواردة في مقالة كيفية التعامل مع قوائم الرموز. يرسل العديد من الشركاء قائمة رموز واحدة لكلّ رسالة تأكيد، ولكنّ هذا ليس شرطًا صارمًا. وإذا كان ذلك يساعد في الصيانة، يمكنك إرسال قوائم رموز متعددة.

تمديد مطاردات KMI

بينما يتم الحفاظ على رموز KMI والبُنى ذات الصلة (أي لا يمكن تحويل التغييرات التي تؤدي إلى تعطيل الواجهات الثابتة في النواة ذات خوارزمية KMI مجمّدة. (مقبولة) تظل نواة GKI مفتوحة للإضافات، بحيث تشحن الأجهزة في وقت لاحق من العام، لا يحتاجون إلى تحديد جميع تبعياتهم قبل أن يتم مجمّد لتوسيع نطاق KMI، يمكنك إضافة رموز جديدة إلى KMI لدوالّ kernel الجديدة أو الحالية التي تم تصديرها، حتى إذا تم تجميد KMI. نواة جديدة قد تُقبل التصحيحات أيضًا إذا لم تكسر KMI.

لمحة عن حالات تعطُّل واجهة برمجة التطبيقات KMI

تحتوي النواة على مصادر ويتم إنشاء البرامج الثنائية من هذه المصادر. تتضمّن فروع النواة التي يتم تتبُّعها من خلال ABI تمثيلًا لـ ABI لـ GKI ABI الحالي (بتنسيق ملف .stg). بعد إنشاء الملفات الثنائية (vmlinux وImage و أي وحدات GKI)، يمكن استخراج تمثيل ABI من الملفات الثنائية. يمكن أن يؤثر أي تغيير يتم إجراؤه على ملف مصدر نواة النظام في الملفات الثنائية، وبالتالي في .stg المستخرَج. يقارن محلّل AbiAnalyzerملف .stg الذي تمّコミتمنته بالملف المستخرَج من عناصر التصميم، ويضع تصنيفًا 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.

عند تشغيل إنشاء واجهة التطبيق الثنائية (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 على سبيل المثال، تضيف التغييرات التالية الخاصة بالميزة الإضافية التزايدية الجديدة في فرع android-12-5.10، والتي تعديل قائمة الرموز وتمثيل ABI XML.

  • مثال على تغيير الميزة موجود في aosp/1345659.
  • يمكنك العثور على مثال لقائمة الرموز فيملف aosp/1346742.
  • يمكنك العثور على مثال على تغيير ملف ABI XML في aosp/1349377.

إذا تم تصدير الرمز (إما بواسطتك أو سبق أن تم تصديره) ولكن لا يستخدمه برنامج تشغيل آخر، فقد يظهر لك خطأ إصدار مشابه لما يلي.

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 للنواة من خلال إعادة صياغة الرمز البرمجي لتجنُّب تغيير IDE أو تعديل تمثيل ABI. استخدِم الرسم البياني التالي لتحديد أفضل نهج لحالتك.

رسم بياني انسيابي لتعطّل واجهة التطبيق الثنائية (ABI)

الشكل 1: حلّ مشكلة عدم توافق واجهة ABI

إعادة ضبط الإعدادات لتجنُّب التغييرات في واجهة التطبيق الثنائية (ABI)

ابذل قصارى جهدك لتجنُّب تعديل واجهة التطبيق الثنائية (ABI) الحالية. في كثير من الحالات، يمكنك عليك إعادة ضبط إعدادات الرمز البرمجي لإزالة التغييرات التي تؤثر في واجهة التطبيق الثنائية (ABI).

  • إعادة صياغة تغييرات حقل البنية إذا كان التغيير يُعدِّل ABI ميزة debugging ، أضِف #ifdef حول الحقول (في البنى ومراجع المصدر) وتأكَّد من أنّ CONFIG المستخدَم في #ifdef غير مفعَّل لملف gki_defconfig وملف defconfig في وضع الإنتاج. للاطّلاع على مثال حول كيفية تصحيح الأخطاء يمكن إضافة الإعدادات إلى أي بنية بدون خرق واجهة ABI، راجِع هذه حزمة التصحيح.

  • إعادة ضبط الميزات بدون تغيير النواة الأساسية: إذا احتاجت ميزات جديدة إضافته إلى ACK لدعم وحدات الشركاء، فحاول إعادة ضبط واجهة التطبيق الثنائية (ABI) جزءًا من التغيير لتجنب تعديل واجهة التطبيق الثنائية (ABI) للنواة. للحصول على مثال على استخدام واجهة تطبيقات ثنائية (ABI) الحالية للنواة لإضافة إمكانات إضافية بدون تغيير واجهة تطبيقات ثنائية (ABI) للنواة، يُرجى الرجوع إلى aosp/1312213.

إصلاح واجهة برمجة تطبيقات معطّلة على Android Gerrit

إذا لم تكن قد اخترقت ABI للنواة عن قصد، عليك التحقيق في الأمر، باستخدام الإرشادات المقدَّمة من خلال أدوات مراقبة ABI. إنّ الأسباب الأكثر شيوعًا للحدوث الأعطال هي تغييرات في بنية البيانات والرموز المرتبطة بها المتعلّقة بـ CRC ، أو بسبب تغييرات في خيارات الضبط تؤدي إلى أيّ من المشاكل المذكورة أعلاه. ابدأ بمعالجة المشاكل التي عثرت عليها الأداة.

يمكنك إعادة إنتاج نتائج ABI محليًا، اطّلِع على إنشاء النواة وتمثيل ABI.

لمحة عن تصنيفات Lint-1

في حال تحميل تغييرات إلى فرع يحتوي على رمز KMI مجمّد أو نهائي، يجب أن تجتاز التغييرات AbiAnalyzer لضمان عدم تأثير التغييرات في العنصر الثابت واجهة التطبيق الثنائية (ABI) بطريقة غير متوافقة. خلال هذه العملية، يبحث AbiAnalyzer عن تقرير ABI الذي تم إنشاؤه أثناء عملية الإنشاء (عملية إنشاء موسّعة تُجري عملية الإنشاء العادية ثم بعض خطوات استخراج ABI ومقارنتها).

إذا عثرت أداة AbiAnalyzer على تقرير غير فارغ، يتم ضبط التصنيف Lint-1 ويتم حظر إرسال المحتوى المتغيّر إلى أن يتم حلّ المشكلة، إلى أن تتلقّى مجموعة التصحيحات تصنيف Lint+1.

تعديل واجهة برمجة التطبيقات للنواة

إذا كان تعديل واجهة التطبيق الثنائية (ABI) أمرًا لا مفر منه، عليك تطبيق التغييرات على الرمز البرمجي وتمثيل ABI وقائمة الرموز إلى ACK. لإزالة القيمة -1 من خلال Lint وعدم إيقاف توافق GKI، اتّبِع الخطوات التالية:

  1. حمِّل التغييرات في الرمز إلى الإشعار بتلقّي الرسالة.

  2. انتظِر ريثما تتلقّى رمز التصحيح 2+ المراجعة.

  3. تعديل تمثيل ABI المرجعي

  4. دمج تغييرات الرمز البرمجي وتغيير تحديث ABI

تحميل التغييرات في رمز ABI إلى ACK

يعتمد تعديل ABI لتطبيق ACK على نوع التغيير الذي يتم إجراؤه.

  • إذا كان تغيير ABI مرتبطًا بميزة تؤثّر في اختبارات CTS أو VTS، يمكن عادةً اختيار التغيير لتأكيده كما هو. على سبيل المثال:

    • aosp/1289677 مطلوبة ليعمل الصوت.
    • يجب تثبيت الإصدار aosp/1295945 لكي يعمل منفذ USB.
  • إذا كان تغيير ABI ناتجًا عن ميزة يمكن مشاركتها مع ACK، يمكن اختيار هذا التغيير وإضافته إلى ACK كما هو. على سبيل المثال، التغييرات التالية غير مطلوبة لاختبار CTS أو VTS، ولكن يمكن مشاركتها مع ACK:

  • إذا أدى تغيير واجهة التطبيق الثنائية (ABI) إلى تقديم ميزة جديدة لا يلزم تضمينها في يمكنك إدخال الرموز إلى ACK باستخدام رمز بديل كما هو موضح في القسم التالي.

استخدام العناصر النائبة لتأكيد الاستلام

يجب أن تكون العناصر الأساسية ضرورية فقط لتغييرات نواة النظام الأساسية التي لا تعود بالفائدة على ACK، مثل التغييرات في الأداء واستهلاك الطاقة. توضِّح القائمة التالية أمثلة على العناصر النائبة وعمليات الاختيار الجزئي في ACK لـ GKI.

  • مقتطف خاصية بالعزل الأساسي (aosp/1284493). إنّ الإمكانات في ACK ليست ضرورية، ولكن يجب أن تكون الرموز متوفّرة في ACK لكي تستخدم وحداتك هذه الرموز.

  • رمز عنصر نائب لوحدة المورّد (aosp/1288860).

  • مجموعة مختارة من واجهة التطبيق الثنائية (ABI) فقط لميزة تتبُّع الأحداث في mm لكل عملية (aosp/1288454). تم اختيار التصحيح الأصلي للموافقة عليه، ثم تم اقتصاصه ليشمل فقط التغييرات اللازمة لحلّ مشكلة اختلاف ABI في task_struct و mm_event_count. يؤدي رمز التصحيح هذا أيضًا إلى تعديل التعداد "mm_event_type" ليتضمن أيضًا. الأعضاء النهائيين.

  • اختيار جزئي لتغييرات ABI في البنية الحرارية التي تتطلّب أكثر من إضافة حقول ABI الجديدة

    • لاصقة aosp/1255544 تم حل الاختلافات في واجهة التطبيق الثنائية (ABI) بين نواة الشريك وACK.

    • تم حلّ المشاكل الوظيفية التي تم رصدها أثناء اختبار GKI للّصقة السابقة من خلال الإصدار aosp/1291018. وتضمّن الإصلاح إعداد هيكل معلَمة أداة الاستشعار للتسجيل مناطق حرارية متعددة في جهاز استشعار واحد

  • 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 على وقت التشغيل. يمكن أن يؤدي إنشاء إصدارات من الوحدة النمطية إلى عدم تطابق فحص التكرار الدوري (CRC) في وقت تحميل الوحدة إذا لم يتطابق KMI المتوقع في الوحدة مع vmlinux KMI على سبيل المثال، فيما يلي فشل نموذجي يحدث في وقت تحميل الوحدة بسبب عدم تطابق CRC مع الرمز module_layout():

init: Loading module /lib/modules/kernel/.../XXX.ko with args ""
XXX: disagrees about version of symbol module_layout
init: Failed to insmod '/lib/modules/kernel/.../XXX.ko' with args ''

استخدامات ترقيم إصدارات الوحدات

يكون ترقيم الإصدارات للمكوّنات مفيدًا للأسباب التالية:

  • ترصد إصدارات الوحدة التغييرات التي تطرأ على رؤية بنية البيانات. إذا كانت الوحدات تغير هياكل البيانات المبهمة، أي هياكل البيانات التي ليست جزءًا من KMI، فإنها تتعطل بعد إجراء تغييرات مستقبلية على الهيكل.

    على سبيل المثال، فكِّر في حقل fwnode في struct device. يجب أن يكون هذا الحقل معتمًا للوحدات بحيث لا يمكنها إجراء تغييرات على الحقول من device->fw_node أو وضع افتراضات حول حجمها.

    ومع ذلك، إذا كانت إحدى الوحدات تتضمّن <linux/fwnode.h> (بشكل مباشر أو غير مباشر)، لن يعود حقل fwnode في struct device غير شفاف بالنسبة إليها. يمكن للوحدة عندئذٍ إجراء تغييرات على device->fwnode->dev أو device->fwnode->ops. يمثل هذا السيناريو إشكالية لعدة أسباب، مذكورة على النحو التالي:

    • ويمكنه كسر الافتراضات التي يطرحها رمز النواة الأساسي حول بنيته وهياكل البيانات.

    • إذا غيّر تحديث مستقبلي للنواة struct fwnode_handle (نوع data 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 kernel وkernel، يمكنك مقارنة قيم 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 للحصول على التفاصيل.

    إنشاء نواة GKI في نظام التشغيل Android 13 والإصدارات الأقدم ونواة الجهاز من خلال إضافة "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 ومواد عرض إنشاء ملف ‎ kernel للجهاز. حدِّد موقع الملف .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 في نواة النظام إلى حدوث تعارضات في تسلسل التحقّق من صحة البيانات (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 لديها العرض الموسّع هذا الرمز (مستبعد جدًا)، ثم دمج أحدث Kernel الأساسي في Android في للنواة لديك كي تستخدم أحدث قاعدة نواة GKI.

في معظم الحالات، تكون قيمة kernel (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. اجمع الوحدة. يعرض الخطأ الناتج عن وقت الإنشاء سلسلة ملف العنوان #include التي أدّت إلى عدم تطابق رمز التحقّق من القيمة التكرارية. مثلاً:

    In file included from .../drivers/clk/XXX.c:16:`
    In file included from .../include/linux/of_device.h:5:
    In file included from .../include/linux/cpu.h:17:
    In file included from .../include/linux/node.h:18:
    .../include/linux/device.h:16:2: error: "Included from here"
    #error "Included from here"
    

    يرجع سبب أحد الروابط في سلسلة #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، ابحث عن عملية الإضافة من خلال تنفيذ الأمر التالي:
    git blame
    • بالنسبة إلى الرموز المحذوفة (حيث يتم حذف رمز في شجرة ويجب أيضًا حذفه في الشجرة الأخرى)، عليك العثور على التغيير الذي أدى إلى حذف السطر. استخدِم الأمر التالي على الشجرة التي تم فيها حذف السطر :
    git log -S "copy paste of deleted line/word" -- <file where it was deleted>
  2. راجِع قائمة عمليات الإلغاء المعروضة للعثور على التغيير أو الحذف. تشير رسالة الأشكال البيانية الالتزام الأول على الأرجح هو الخيار الذي تبحث عنه. إذا لم يكن الأمر كذلك، انتقِل إلى أسفل القائمة للعثور على عملية الربط.

  3. بعد تحديد التغيير، يمكنك التراجع عنه في النواة (kernel) أو حمِّل الملف إلى ACK واحصل عليه دمجها.