يمكنك استخدام أدوات مراقبة واجهة التطبيق الثنائية (ABI) المتوفّرة في الإصدار 11 من نظام التشغيل Android والإصدارات الأحدث لتحقيق الاستقرار في واجهة التطبيق الثنائية (ABI) داخل النواة لنواة Android. تجمع الأداة تمثيلات ABI وتقارنها
من ملفات ثنائية حالية للنواة (vmlinux ووحدات GKI). تمثيلات ABI هذه هي ملفات .stg وقوائم الرموز. تُسمى الواجهة التي يوفّر التمثيل من خلالها عرضًا واجهة وحدة النواة (KMI). يمكنك استخدام الأدوات لتتبُّع التغييرات التي تطرأ على مؤشر KMI والحدّ من تأثيرها.
تم تطوير أدوات مراقبة واجهة التطبيق الثنائية (ABI) في مشروع Android مفتوح المصدر (AOSP)، وتستخدم STG (أو libabigail في الإصدار 13 من نظام التشغيل Android والإصدارات الأقدم) لإنشاء تمثيلات ومقارنتها.
توضّح هذه الصفحة الأدوات وعملية جمع وتحليل تمثيلات واجهة التطبيق الثنائية (ABI) واستخدام هذه التمثيلات لتوفير ثبات واجهة التطبيق الثنائية (ABI) في النواة. تقدّم هذه الصفحة أيضًا معلومات حول المساهمة في إجراء تغييرات على نواة Android.
معالجة
يتطلّب تحليل واجهة ABI للنواة عدة خطوات، يمكن تنفيذ معظمها تلقائيًا:
- إنشاء النواة وتمثيل واجهة التطبيق الثنائية (ABI)
- تحليل الاختلافات في واجهة التطبيق الثنائية (ABI) بين الإصدار ومرجع
- تعديل تمثيل واجهة التطبيق الثنائية (إذا لزم الأمر)
- التعامل مع قوائم الرموز
تعمل التعليمات التالية مع أي نواة يمكنك إنشاؤها باستخدام مجموعة أدوات متوافقة (مثل مجموعة أدوات Clang المُنشأة مسبقًا). تتوفّر repo manifests لجميع فروع نواة Android الشائعة ولعدة نِوى خاصة بالأجهزة، وتتحقّق من استخدام سلسلة الأدوات الصحيحة عند إنشاء توزيعة نواة للتحليل.
قوائم الرموز
لا تتضمّن واجهة KMI جميع الرموز في النواة أو حتى جميع الرموز التي يزيد عددها عن 30,000 رمز تم تصديرها. بدلاً من ذلك، يتم إدراج الرموز التي يمكن أن تستخدمها وحدات المورّد بشكل صريح في مجموعة من ملفات قوائم الرموز التي يتم صيانتها بشكل علني في شجرة النواة (gki/{ARCH}/symbols/* أو android/abi_gki_{ARCH}_* في Android 15 والإصدارات الأقدم). إنّ اتحاد جميع الرموز في جميع ملفات قائمة الرموز يحدّد مجموعة رموز KMI التي يتم الحفاظ عليها على أنّها ثابتة. مثال على ملف قائمة الرموز
هو
gki/aarch64/symbols/db845c،
الذي يحدّد الرموز المطلوبة
لجهاز DragonBoard 845c.
لا تُعتبر سوى الرموز المدرَجة في قائمة الرموز والبِنى والتعريفات ذات الصلة جزءًا من KMI. يمكنك نشر تغييرات على قوائم الرموز إذا لم تكن الرموز التي تحتاج إليها متوفرة. بعد إضافة واجهات جديدة إلى قائمة الرموز، وإدراجها ضمن وصف 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 الحالية، بل تظل ثابتة. ومع ذلك، يحق للمورّدين إضافة رموز إلى واجهة KMI في أي وقت طالما أنّ الإضافات لا تؤثّر في ثبات واجهة ABI الحالية. يتم الحفاظ على استقرار الرموز المضافة حديثًا بمجرد الإشارة إليها من خلال قائمة رموز KMI. يجب عدم إزالة الرموز من قائمة خاصة بنواة إلا إذا كان من الممكن التأكّد من أنّ أي جهاز لم يتم شحنه مطلقًا مع اعتمادية على هذا الرمز.
يمكنك إنشاء قائمة رموز KMI لجهاز باتّباع التعليمات الواردة في مقالة كيفية استخدام قوائم الرموز. يقدّم العديد من الشركاء قائمة رموز واحدة لكلّ إقرار استلام، ولكنّ هذا ليس شرطًا أساسيًا. إذا كان ذلك يساعد في الصيانة، يمكنك إرسال قوائم رموز متعددة.
تمديد KMI
مع أنّ رموز KMI والبِنى ذات الصلة تظل ثابتة (ما يعني أنّه لا يمكن قبول التغييرات التي تؤدي إلى تعطيل الواجهات الثابتة في النواة التي تم تجميد KMI فيها)، تظل نواة GKI مفتوحة للإضافات حتى لا تحتاج الأجهزة التي سيتم شحنها في وقت لاحق من العام إلى تحديد جميع التبعيات قبل تجميد KMI. لتوسيع KMI، يمكنك إضافة رموز جديدة إلى KMI للدوال الجديدة أو الحالية التي تم تصديرها من النواة، حتى إذا كان KMI مجمدًا. قد يتم أيضًا قبول تصحيحات جديدة لنواة النظام إذا لم تؤدِّ إلى إيقاف توافق واجهة KMI.
لمحة عن حالات التعطّل في KMI
يحتوي النواة على مصادر ويتم إنشاء الملفات الثنائية من هذه المصادر.
تتضمّن فروع النواة التي تراقبها واجهة ABI تمثيلاً لواجهة ABI الحالية في GKI (في شكل ملف .stg). بعد إنشاء الملفات الثنائية (vmlinux وImage وأي وحدات GKI)، يمكن استخراج تمثيل لواجهة التطبيق الثنائية من الملفات الثنائية. يمكن أن يؤثر أي تغيير يتم إجراؤه على ملف مصدر kernel في الملفات الثنائية، وبالتالي يؤثر أيضًا في .stg الذي تم استخراجه. تقارن عملية تحليل توافق واجهة التطبيق الثنائية (ABI) ملف .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) في مدّة التصميم
السبب الأكثر شيوعًا لحدوث الأخطاء هو عندما يستخدم برنامج التشغيل رمزًا جديدًا من النواة غير مدرَج في أي من قوائم الرموز.
إذا لم يكن الرمز مضمّنًا في قائمة الرموز، عليك أولاً التأكّد من أنّه تم تصديره باستخدام EXPORT_SYMBOL_GPL(symbol_name)، ثم تعديل قائمة الرموز وتمثيل ABI. على سبيل المثال، تضيف التغييرات التالية ميزة
Incremental FS الجديدة إلى فرع android-12-5.10، الذي يتضمّن
تعديل قائمة الرموز وتمثيل ABI.
- يمكن الاطّلاع على مثال على تغيير الميزة في aosp/1345659.
- يمكنك الاطّلاع على مثال لقائمة الرموز في aosp/1346742.
- يمكنك الاطّلاع على مثال لتغيير تمثيل واجهة التطبيق الثنائية (ABI) في 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 في ACK، يُرجى الرجوع إلى aosp/1367601.
حلّ المشاكل في توافق واجهة التطبيق الثنائية للنواة
يمكنك التعامل مع حالات عدم توافق واجهة التطبيق الثنائية (ABI) لنواة النظام من خلال إعادة تصميم الرمز البرمجي بدون تغيير واجهة التطبيق الثنائية أو تعديل تمثيل واجهة التطبيق الثنائية. استخدِم المخطّط التالي لتحديد الطريقة الأنسب لحالتك.
الشكل 1: حلّ مشاكل عدم توافق واجهة التطبيق الثنائية (ABI)
إعادة هيكلة الرمز البرمجي لتجنُّب تغييرات واجهة التطبيق الثنائية (ABI)
يجب بذل كل جهد ممكن لتجنُّب تعديل واجهة ABI الحالية. في كثير من الحالات، يمكنك إعادة هيكلة الرمز البرمجي لإزالة التغييرات التي تؤثر في واجهة التطبيق الثنائية (ABI).
إعادة تصميم تغييرات حقل البنية إذا كان التغيير يعدّل واجهة التطبيق الثنائية (ABI) لإحدى ميزات تصحيح الأخطاء، أضِف
#ifdefحول الحقول (في البُنى والمراجع المصدرية) وتأكَّد من إيقافCONFIGالمستخدَم في#ifdefلإعدادات defconfig الخاصة بالإنتاج وgki_defconfig. للاطّلاع على مثال حول كيفية إضافة إعداد تصحيح الأخطاء إلى بنية بدون إيقاف توافق التطبيق الثنائي (ABI)، يُرجى الرجوع إلى مجموعة التصحيحات هذه.إعادة تصميم الميزات بدون تغيير النواة الأساسية إذا كان يجب إضافة ميزات جديدة إلى ACK لتتوافق مع وحدات الشريك، حاوِل إعادة هيكلة جزء واجهة التطبيق الثنائية (ABI) من التغيير لتجنُّب تعديل واجهة التطبيق الثنائية (ABI) للنواة. للاطّلاع على مثال حول استخدام واجهة ABI الحالية للنواة من أجل إضافة إمكانات إضافية بدون تغيير واجهة ABI للنواة، يُرجى الرجوع إلى aosp/1312213.
إصلاح واجهة ABI معطَّلة على Android Gerrit
إذا لم تتعطّل واجهة ABI لنواة النظام عن غير قصد، عليك التحقيق في المشكلة باستخدام الإرشادات المقدَّمة من أدوات مراقبة واجهة ABI. تشمل الأسباب الأكثر شيوعًا لحدوث أخطاء تغييرات في بنى البيانات وتغييرات في رمز CRC المرتبط، أو تغييرات في خيارات الإعدادات تؤدي إلى أي من الأسباب المذكورة أعلاه. ابدأ بمعالجة المشاكل التي عثرت عليها الأداة.
يمكنك إعادة إنتاج نتائج ABI محليًا، راجِع إنشاء النواة وتمثيل ABI الخاص بها.
لمحة عن تصنيفات Lint-1
في حال تحميل تغييرات إلى فرع يحتوي على واجهة KMI مجمدة أو نهائية، يجب أن تجتاز التغييرات تحليلات التوافق مع واجهة ABI لضمان أنّ التغييرات في تمثيل واجهة ABI تعكس واجهة ABI الفعلية ولا تحتوي على أي حالات عدم توافق (إزالة الرموز أو تغيير الأنواع).
قد يؤدي كل تحليل من تحليلات واجهة التطبيق الثنائية إلى ضبط التصنيف Lint-1 وحظر إرسال التغييرات إلى أن يتم حل جميع المشاكل أو إلغاء التصنيف.
تعديل واجهة ABI لنواة النظام
إذا كان تعديل واجهة ABI أمرًا لا يمكن تجنُّبه، عليك تطبيق تغييرات الرمز وتمثيل واجهة ABI وقائمة الرموز على حزمة ACK. لإزالة القيمة -1 من خلال أداة Lint بدون التأثير في توافق GKI، اتّبِع الخطوات التالية:
انتظِر إلى أن تتلقّى Code-Review +2 لمجموعة التصحيحات.
ادمج تغييرات الرمز وتغيير تحديث ABI.
تحميل تغييرات رمز واجهة ABI إلى ACK
يعتمد تعديل ACK ABI على نوع التغيير الذي يتم إجراؤه.
إذا كان تغيير واجهة ABI مرتبطًا بميزة تؤثر في اختبارات CTS أو VTS، يمكن عادةً اختيار التغيير وإضافته إلى ACK كما هو. على سبيل المثال:
- يجب توفّر aosp/1289677 لكي يعمل الصوت.
- يجب توفير aosp/1295945 لكي يعمل منفذ USB.
إذا كان تغيير واجهة التطبيق الثنائية (ABI) مخصّصًا لميزة يمكن مشاركتها مع حزمة ACK، يمكن اختيار هذا التغيير ونقله إلى حزمة ACK كما هو. على سبيل المثال، لا حاجة إلى إجراء التغييرات التالية لاختبار CTS أو VTS، ولكن يمكن مشاركتها مع حزمة ACK:
- aosp/1250412 هو تغيير في إحدى ميزات إدارة درجة الحرارة.
- aosp/1288857
هو تغيير
EXPORT_SYMBOL_GPL.
إذا كان تغيير واجهة التطبيق الثنائية (ABI) يقدّم ميزة جديدة لا يلزم تضمينها في ACK، يمكنك تقديم الرموز إلى ACK باستخدام رمز تجريبي كما هو موضّح في القسم التالي.
استخدام رموز ACK
يجب أن تكون الرموز الصورية ضرورية فقط لإجراء تغييرات أساسية في النواة لا تستفيد منها ACK، مثل تغييرات الأداء والطاقة. توضّح القائمة التالية أمثلة على التعليمات البرمجية الصورية وعمليات اختيار جزئية في ACK لنواة GKI.
Core-isolate feature stub (aosp/1284493). ليست الإمكانات في ACK ضرورية، ولكن يجب أن تكون الرموز متوفرة في ACK لكي تستخدم الوحدات هذه الرموز.
رمز العنصر النائب لوحدة المورّد (aosp/1288860).
الاختيار الدقيق لميزة تتبُّع أحداث
mmعلى مستوى كل عملية فقط في واجهة التطبيق الثنائية (ABI) (aosp/1288454). تم اختيار التصحيح الأصلي لدمجه في ACK ثم تم تقليصه ليشمل فقط التغييرات اللازمة لحل اختلاف ABI فيtask_structوmm_event_count. يعدّل هذا التصحيح أيضًا التعدادmm_event_typeليحتوي على الأعضاء النهائيين.الاختيار الدقيق الجزئي للتغييرات في واجهة التطبيق الثنائية (ABI) الخاصة بالبنية الحرارية التي تطلّبت أكثر من مجرد إضافة حقول واجهة التطبيق الثنائية الجديدة
تم حلّ الاختلافات في واجهة التطبيق الثنائية (ABI) بين نواة الشريك ونواة ACK في التصحيح aosp/1255544.
أصلحت حزمة التصحيح 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 في وقت التشغيل. يمكن أن يؤدي تحديد إصدارات الوحدات إلى حدوث أخطاء في عدم تطابق فحص التكرار الدوري (CRC) عند مدّة التحميل إذا لم يتطابق KMI المتوقّع لوحدة مع vmlinux KMI. على سبيل المثال، في ما يلي خطأ نموذجي يحدث أثناء تحميل الوحدة النمطية بسبب عدم تطابق فحص التكرار الدوري للرمز 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) عند تحميل وحدة:
أنشئ نواة GKI ونواة جهازك باستخدام الخيار
--kbuild_symtypesكما هو موضّح في الأمر التالي:tools/bazel run --kbuild_symtypes //common:kernel_aarch64_distينشئ هذا الأمر ملف
.symtypesلكل ملف.o. يمكنك الاطّلاع علىKBUILD_SYMTYPESفي Kleaf لمزيد من التفاصيل.بالنسبة إلى الإصدار 13 من نظام التشغيل Android والإصدارات الأقدم، أنشئ نواة GKI ونواة جهازك عن طريق إضافة
KBUILD_SYMTYPES=1إلى الأمر الذي تستخدمه لإنشاء النواة، كما هو موضّح في الأمر التالي:KBUILD_SYMTYPES=1 BUILD_CONFIG=common/build.config.gki.aarch64 build/build.shعند استخدام
build_abi.sh,، يتم ضبط العلامةKBUILD_SYMTYPES=1ضمنيًا.ابحث عن الملف
.cالذي تم تصدير الرمز الذي لا يتطابق مع CRC فيه، وذلك باستخدام الأمر التالي:git -C common grep EXPORT_SYMBOL.*module_layout kernel/module/version.c:EXPORT_SYMBOL(module_layout);يحتوي ملف
.cعلى ملف.symtypesمطابق في GKI، بالإضافة إلى عناصر إصدار نواة الجهاز. حدِّد موقع الملف.symtypesباستخدام الأوامر التالية:cd bazel-bin/common/kernel_aarch64/symtypes ls -1 kernel/module/version.symtypesفي الإصدار 13 من نظام التشغيل Android والإصدارات الأقدم، عند استخدام نصوص البرامج القديمة، من المرجّح أن يكون الموقع الجغرافي إما
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 * )
قارِن بين الملفَين وأصلِح جميع الاختلافات.
من الأفضل إنشاء symtypes باستخدام إصدار قبل التغيير الذي يتضمّن المشكلة مباشرةً، ثم إنشاء إصدار آخر عند التغيير الذي يتضمّن المشكلة. يعني حفظ جميع الملفات أنّه يمكن مقارنتها بشكل مجمّع.
على سبيل المثال:
diff -r -N -U0 good badبخلاف ذلك، ما عليك سوى مقارنة الملفات المحدّدة التي تهمّك.
الحالة 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 والإصدارات الأحدث
تأكَّد من أنّ ملف المصدر المتأثّر يتضمّن عنوان 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 من نظام التشغيل Android والإصدارات الأقدم
في كثير من الأحيان، يكون الحلّ هو إخفاء #include الجديد عن genksyms.
#ifndef __GENKSYMS__
#include <linux/fwnode.h>
#endif
في ما عدا ذلك، لتحديد #include الذي يتسبّب في الاختلاف، اتّبِع الخطوات التالية:
افتح ملف العنوان الذي يحدّد الرمز أو نوع البيانات الذي يتضمّن هذا الاختلاف. على سبيل المثال، عدِّل
include/linux/fwnode.hstruct fwnode_handle.أضِف الرمز التالي في أعلى ملف العنوان:
#ifdef CRC_CATCH #error "Included from here" #endifفي ملف
.cالخاص بالوحدة الذي يتضمّن خطأ في تطابق CRC، أضِف ما يلي كسطر أول قبل أي من أسطر#include.#define CRC_CATCH 1جمِّع الوحدة. يعرض خطأ وقت الإنشاء الناتج سلسلة ملفات العناوين
#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 * ) ; ...
لتحديد النوع الذي تم تغييره، اتّبِع الخطوات التالية:
ابحث عن تعريف الرمز في رمز المصدر (عادةً في ملفات
.h).- للاطّلاع على الاختلافات في الرموز بين نواة الجهاز ونواة GKI، ابحث عن عملية الإيداع من خلال تنفيذ الأمر التالي:
git blame- بالنسبة إلى الرموز المحذوفة (حيث يتم حذف رمز في شجرة وتريد أيضًا حذفه في الشجرة الأخرى)، عليك العثور على التغيير الذي حذف السطر. استخدِم الأمر التالي على الشجرة التي تم حذف السطر منها:
git log -S "copy paste of deleted line/word" -- <file where it was deleted>راجِع قائمة عمليات الإيداع التي تم إرجاعها للعثور على التغيير أو الحذف. من المرجّح أنّ يكون أول عملية دمج هي التي تبحث عنها. إذا لم يكن كذلك، انتقِل إلى أسفل القائمة حتى تعثر على عملية الإيداع.
بعد تحديد عملية الدمج، يمكنك إما التراجع عنها في النواة أو تعديلها لإيقاف تغيير CRC وتحميلها إلى ACK ودمجها. يجب مراجعة كل تغيير في واجهة ABI المتبقية للتأكّد من أمانه، ويمكن تسجيل تغيير مسموح به إذا لزم الأمر.
تفضيل استهلاك المساحة المتروكة الحالية
يتم توسيع بعض البُنى في GKI للسماح بتوسيعها بدون تعطيل وحدات المورّد الحالية. إذا أضافت عملية دمج في المصدر (على سبيل المثال) عنصرًا إلى هذا النوع من البنية، قد يكون من الممكن تغييرها لاستخدام بعض المساحة المتروكة بدلاً من ذلك. بعد ذلك، يتم إخفاء هذا التغيير من عملية احتساب معدّل التحويل.
يحتفظ الماكرو الموحّد الذي يقدّم وصفًا ذاتيًا 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 من نظام التشغيل Android والإصدارات الأحدث
يتم احتساب رموز 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 من نظام التشغيل Android والإصدارات الأقدم
يجب إخفاء التغييرات في النوع ونوع الرمز من genksyms. ويمكن إجراء ذلك من خلال التحكّم في المعالجة المسبقة باستخدام __GENKSYMS__.
يمكن التعبير عن عمليات تحويل الرموز العشوائية بهذه الطريقة.
على سبيل المثال، لإخفاء عضو جديد يشغل مكانًا في بنية حالية:
struct parcel {
void *data;
bool urgent;
#ifndef __GENKSYMS__
bool retry;
#endif
void *scratch_space;
};