مراقبة Android Kernel ABI

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

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

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

عملية

يستغرق تحليل واجهة برمجة التطبيقات (ABI) للنواة عدة خطوات، يمكن أتمتة معظمها:

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

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

قوائم الرموز

لا يتضمن KMI جميع الرموز الموجودة في النواة أو حتى جميع الرموز المصدرة التي يزيد عددها عن 30000+. بدلاً من ذلك، يتم إدراج الرموز التي يمكن استخدامها بواسطة وحدات البائع بوضوح في مجموعة من ملفات قائمة الرموز التي يتم الاحتفاظ بها بشكل عام في جذر شجرة kernel. يحدد اتحاد كافة الرموز الموجودة في كافة ملفات قائمة الرموز مجموعة رموز KMI التي يتم الاحتفاظ بها على أنها مستقرة. مثال على ملف قائمة الرموز هو abi_gki_aarch64_db845c ، الذي يعلن عن الرموز المطلوبة لـ DragonBoard 845c .

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

يحتوي كل فرع من فروع KMI لنظام Android Common Kernel (ACK) على مجموعة خاصة به من قوائم الرموز. لم تتم أي محاولة لتوفير استقرار ABI بين فروع kernel KMI المختلفة. على سبيل المثال، يعتبر KMI لنظام android12-5.10 مستقلاً تمامًا عن KMI لنظام android13-5.10 .

تستخدم أدوات ABI قوائم رموز KMI لتحديد الواجهات التي يجب مراقبتها لتحقيق الاستقرار. تحتوي قائمة الرموز الرئيسية على الرموز المطلوبة بواسطة وحدات kernel 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 لجهاز ما باستخدام الإرشادات الواردة في كيفية العمل مع قوائم الرموز . يقدم العديد من الشركاء قائمة رموز واحدة لكل ACK، ولكن هذا ليس مطلبًا صعبًا. إذا كان ذلك يساعد في الصيانة، فيمكنك إرسال قوائم رموز متعددة.

تمديد KMI

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

حول أعطال KMI

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

السبب الأكثر شيوعًا للأخطاء هو عندما يستخدم برنامج التشغيل رمزًا جديدًا من kernel غير موجود في أي من قوائم الرموز.

إذا لم يتم تضمين الرمز في قائمة الرموز ( android/abi_gki_aarch64 )، فأنت بحاجة أولاً إلى التحقق من أنه تم تصديره باستخدام EXPORT_SYMBOL_GPL( symbol_name ) ثم تحديث تمثيل ABI XML وقائمة الرموز. على سبيل المثال، تضيف التغييرات التالية ميزة Incremental FS الجديدة إلى فرع 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 في كل من kernel وACK (راجع تحديث تمثيل ABI ). للحصول على مثال لتحديث ABI XML وقائمة الرموز في ACK، راجع aosp/1367601 .

حل أعطال kernel ABI

يمكنك التعامل مع انقطاعات ABI لـ kernel عن طريق إعادة بناء التعليمات البرمجية بحيث لا يتم تغيير ABI أو تحديث تمثيل ABI . استخدم الرسم البياني التالي لتحديد أفضل نهج لموقفك.

مخطط تدفق كسر ABI

الشكل 1. قرار الكسر ABI

رمز Refactor لتجنب تغييرات ABI

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

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

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

إصلاح واجهة ABI المعطلة على Android Gerrit

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

يمكنك إعادة إنتاج نتائج ABI محليًا، راجع إنشاء النواة وتمثيل ABI الخاص بها .

حول ملصقات Lint-1

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

إذا عثر AbiAnalyzer على تقرير غير فارغ، فإنه يقوم بتعيين تسمية Lint-1 ويتم حظر إرسال التغيير حتى يتم حله؛ حتى تتلقى مجموعة التصحيح تسمية Lint+1.

قم بتحديث Kernel 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

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

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

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

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

  • انتقاء جزئي لتغييرات البنية الحرارية لواجهة برمجة التطبيقات (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 الذي لا يتضمن قطع الرموز (بمعنى أنه يمكن استخدام جميع الرموز التي يتم تصديرها عادةً). لتحديد موقع هذه الإصدارات، ابحث عن إصدارات kernel_debug_aarch64 على ci.android.com .

فرض KMI باستخدام إصدار الوحدة النمطية

تستخدم نواة صورة Kernel العامة (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 . ويشكل هذا السيناريو إشكالية لعدة أسباب، نلخصها فيما يلي:

    • يمكنه كسر الافتراضات التي يضعها كود kernel الأساسي حول هياكل البيانات الداخلية الخاصة به.

    • إذا أدى تحديث kernel مستقبلي إلى تغيير struct fwnode_handle (نوع بيانات fwnode )، فإن الوحدة لم تعد تعمل مع kernel الجديد. علاوة على ذلك، لن يُظهر stgdiff أي اختلافات لأن الوحدة النمطية تكسر KMI عن طريق معالجة هياكل البيانات الداخلية بشكل مباشر بطرق لا يمكن التقاطها عن طريق فحص التمثيل الثنائي فقط.

  • تعتبر الوحدة الحالية غير متوافقة مع KMI عندما يتم تحميلها في وقت لاحق بواسطة نواة جديدة غير متوافقة. يضيف إصدار الوحدة النمطية فحصًا لوقت التشغيل لتجنب التحميل غير المقصود لوحدة غير متوافقة مع KMI مع kernel. يمنع هذا الفحص مشكلات وقت التشغيل التي يصعب تصحيحها وتعطل kernel الذي قد ينتج عن عدم توافق غير مكتشف في KMI.

يؤدي تمكين إصدار الوحدة النمطية إلى منع كل هذه المشكلات.

تحقق من عدم تطابق CRC دون تشغيل الجهاز

يقوم stgdiff بمقارنة حالات عدم تطابق CRC بين النوى والإبلاغ عنها بالإضافة إلى اختلافات ABI الأخرى.

بالإضافة إلى ذلك، يؤدي إنشاء kernel الكامل مع تمكين 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
    

    يقوم هذا الأمر بإنشاء ملف .symtypes لكل ملف .o . راجع KBUILD_SYMTYPES في Kleaf للحصول على التفاصيل.

    بالنسبة لنظام التشغيل Android 13 والإصدارات الأقدم، قم ببناء نواة GKI ونواة جهازك عن طريق إضافة KBUILD_SYMTYPES=1 مسبقًا إلى الأمر الذي تستخدمه لبناء النواة، كما هو موضح في الأمر التالي:

    KBUILD_SYMTYPES=1 BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh
    

    عند استخدام build_abi.sh, يتم تعيين العلامة KBUILD_SYMTYPES=1 ضمنيًا بالفعل.

  2. ابحث عن ملف .c الذي تم تصدير الرمز الذي يحتوي على عدم تطابق CRC، باستخدام الأمر التالي:

    cd common && git grep EXPORT_SYMBOL.*module_layout
    kernel/module.c:EXPORT_SYMBOL(module_layout);
    
  3. يحتوي الملف .c على ملف .symtypes مطابق في GKI، كما يقوم ببناء عناصر نواة جهازك. حدد موقع الملف .c باستخدام الأوامر التالية:

    cd out/$BRANCH/common && ls -1 kernel/module.*
    kernel/module.o
    kernel/module.o.symversions
    kernel/module.symtypes
    

    فيما يلي خصائص الملف .c :

    • تنسيق ملف .c هو سطر واحد (يحتمل أن يكون طويلًا جدًا) لكل رمز.

    • [s|u|e|etc]# في بداية السطر يعني أن الرمز من نوع البيانات [struct|union|enum|etc] . على سبيل المثال:

      t#bool typedef _Bool bool
      
    • تشير البادئة # المفقودة في بداية السطر إلى أن الرمز عبارة عن دالة. على سبيل المثال:

      find_module s#module * find_module ( const char * )
      
  4. قارن الملفين وأصلح جميع الاختلافات.

الحالة 1: الاختلافات بسبب رؤية نوع البيانات

إذا احتفظت إحدى النواة برمز أو نوع بيانات غير شفاف للوحدات النمطية ولم تفعل النواة الأخرى ذلك، فسيظهر هذا الاختلاف بين ملفات .symtypes للنواتين. يحتوي ملف .symtypes من إحدى النوى على رمز UNKNOWN ، بينما يحتوي ملف .symtypes من kernel الآخر على عرض موسع للرمز أو نوع البيانات.

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

  3. بعد تحديد التغيير، قم إما بإعادته إلى النواة الخاصة بك أو تحميله إلى ACK ودمجه .