Android कर्नेल एबीआई मॉनिटरिंग

Android 11 और इसके बाद के वर्शन में उपलब्ध, ऐप्लिकेशन बाइनरी इंटरफ़ेस (एबीआई) मॉनिटरिंग टूल का इस्तेमाल करके, Android के कोर में मौजूद एबीआई को स्थिर किया जा सकता है. टूल, मौजूदा कर्नेल बाइनरी (vmlinux+ GKI मॉड्यूल) से एबीआई के रिप्रज़ेंटेशन इकट्ठा करता है और उनकी तुलना करता है. ये एबीआई को, .stg फ़ाइलों और सिंबल की सूचियों को दिखाया जाता है. जिस इंटरफ़ेस पर डेटा दिखाया जाता है उसे कर्नल मॉड्यूल इंटरफ़ेस (KMI) कहा जाता है. टूल का इस्तेमाल करके, केएमआई में होने वाले बदलावों को ट्रैक किया जा सकता है और उन्हें कम किया जा सकता है.

एबीआई मॉनिटरिंग टूल को AOSP में डेवलप किया गया है. साथ ही, यह STG (या Android 13 और उससे पहले के वर्शन में libabigail) का इस्तेमाल करके, रिप्रज़ेंटेशन जनरेट करता है और उनकी तुलना करता है.

इस पेज पर, टूल, एबीआई के रिप्रज़ेंटेशन को इकट्ठा करने और उनका विश्लेषण करने की प्रोसेस, और इन-कर्नल एबीआई को स्थिरता देने के लिए, ऐसे रिप्रज़ेंटेशन के इस्तेमाल के बारे में बताया गया है. इस पेज पर, Android के कर्नेल में बदलाव करने के बारे में भी जानकारी दी गई है.

प्रोसेस

कर्नेल के एबीआई का विश्लेषण करने के लिए कई चरण पूरे करने होते हैं. इनमें से ज़्यादातर चरण अपने-आप पूरे हो सकते हैं:

  1. कर्नल और उसके एबीआई का प्रतिनिधित्व बनाएं.
  2. बिल्ड और रेफ़रंस के बीच एबीआई (एबिट्रेशन बिडिंग इंटरफ़ेस) के अंतर का विश्लेषण करें.
  3. अगर ज़रूरी हो, तो एबीआई का प्रतिनिधित्व अपडेट करें.
  4. सिंबल वाली सूचियों के साथ काम करें.

नीचे दिए गए निर्देश ऐसे किसी भी कर्नेल के लिए काम करते हैं जिसे आप इस्तेमाल किए जा सकने वाले टूलचेन (जैसे, पहले से बने Clang टूलचेन) का इस्तेमाल करके बना सकते हैं. repo manifests सभी Android सामान्य कर्नेल शाखाओं और कई डिवाइस-खास कर्नेल के लिए उपलब्ध हैं. इससे यह पक्का होता है कि विश्लेषण के लिए कर्नेल डिस्ट्रिब्यूशन बनाने के दौरान, सही टूलचेन का इस्तेमाल किया जाए.

सिंबल की सूचियां

KMI में, कर्नेल के सभी सिंबल या एक्सपोर्ट किए गए 30,000 से ज़्यादा सिंबल शामिल नहीं होते. इसके बजाय, वेंडर मॉड्यूल के ज़रिए इस्तेमाल किए जा सकने वाले सिंबल, सिंबल की सूची वाली फ़ाइलों के सेट में साफ़ तौर पर शामिल किए जाते हैं. इन फ़ाइलों को कर्नेल ट्री के रूट में सार्वजनिक तौर पर रखा जाता है. सभी सिंबल की सूची वाली फ़ाइलों में मौजूद सभी सिंबल का यूनियन, स्थिर के तौर पर बनाए गए केएमआई सिंबल के सेट को तय करता है. सिंबल की सूची वाली फ़ाइल का उदाहरण, abi_gki_aarch64_db845c है. इसमें DragonBoard 845c के लिए ज़रूरी सिंबल के बारे में बताया गया है.

सिर्फ़ सिंबल की सूची में शामिल सिंबल और उनसे जुड़े स्ट्रक्चर और परिभाषाओं को केएमआई का हिस्सा माना जाता है. अगर आपको ज़रूरी सिंबल नहीं मिल रहे हैं, तो सिंबल की सूचियों में बदलाव किए जा सकते हैं. नए इंटरफ़ेस, सिंबल की सूची में शामिल होने और केएमआई के ब्यौरे का हिस्सा बनने के बाद, उन्हें स्थिर के तौर पर बनाए रखा जाता है. साथ ही, शाखा को फ़्रीज़ करने के बाद, उन्हें सिंबल की सूची से नहीं हटाया जाना चाहिए या उनमें बदलाव नहीं किया जाना चाहिए.

Android Common Kernel (ACK) KMI kernel की हर शाखा में, सिंबल सूचियों का अपना सेट होता है. अलग-अलग KMI कर्नेल ब्रैंच के बीच एबीआई को स्थिर रखने की कोशिश नहीं की जाती. उदाहरण के लिए, android12-5.10 के लिए केएमआई, android13-5.10 के लिए केएमआई से पूरी तरह से अलग होता है.

ABI टूल, KMI सिंबल की सूचियों का इस्तेमाल करके यह तय करते हैं कि किन इंटरफ़ेस को स्थिरता के लिए मॉनिटर किया जाना चाहिए. मुख्य सिंबल की सूची में वे सिंबल होते हैं जो GKI के kernel मॉड्यूल के लिए ज़रूरी होते हैं. वेंडर को अन्य सिंबल की सूचियां सबमिट और अपडेट करनी होंगी, ताकि यह पक्का किया जा सके कि वे इंटरफ़ेस जिन पर वे भरोसा करते हैं वे एबीआई के साथ काम करते रहें. उदाहरण के लिए, android13-5.15 के लिए सिंबल की सूची देखने के लिए, https://android.googlesource.com/kernel/common/+/refs/heads/android13-5.15/android देखें

सिंबल की सूची में वे सिंबल शामिल होते हैं जिनकी किसी खास वेंडर या डिवाइस के लिए ज़रूरत होती है. टूल में इस्तेमाल की गई पूरी सूची, केएमआई की निशान वाली सूची की सभी फ़ाइलों को मिलाकर बनाई गई है. एबीआई टूल, हर सिंबल की जानकारी तय करते हैं. इसमें फ़ंक्शन हस्ताक्षर और नेस्ट किए गए डेटा स्ट्रक्चर भी शामिल हैं.

KMI फ़्रीज़ होने पर, मौजूदा KMI इंटरफ़ेस में कोई बदलाव नहीं किया जा सकता. ये इंटरफ़ेस स्थिर होते हैं. हालांकि, वेंडर के पास किसी भी समय केएमआई में सिंबल जोड़ने का विकल्प होता है. ऐसा तब तक किया जा सकता है, जब तक कि इससे मौजूदा एबीआई की स्थिरता पर असर न पड़े. जोड़े गए नए चिह्नों को, KMI के चिह्न की सूची में शामिल करने के बाद, स्थिर माना जाता है. किसी कर्नेल की सूची से सिंबल तब तक नहीं हटाए जाने चाहिए, जब तक यह पुष्टि न हो जाए कि किसी भी डिवाइस को कभी भी उस सिंबल पर निर्भर करके शिप नहीं किया गया है.

सिंबल की सूचियों के साथ काम करने का तरीका में दिए गए निर्देशों का इस्तेमाल करके, किसी डिवाइस के लिए केएमआई सिंबल की सूची जनरेट की जा सकती है. कई पार्टनर हर ACK के लिए एक सिंबल सूची सबमिट करते हैं. हालांकि, ऐसा करना ज़रूरी नहीं है. अगर इससे रखरखाव में मदद मिलती है, तो एक से ज़्यादा सिंबल की सूचियां सबमिट की जा सकती हैं.

केएमआई को बढ़ाना

KMI सिंबल और उससे जुड़े स्ट्रक्चर को स्थिर रखा जाता है. इसका मतलब है कि फ़्रीज़ किए गए KMI वाले कर्नेल में, ऐसे बदलाव स्वीकार नहीं किए जा सकते जो स्थिर इंटरफ़ेस को तोड़ते हैं. हालांकि, GKI कर्नेल में एक्सटेंशन जोड़े जा सकते हैं, ताकि साल के आखिर में शिप होने वाले डिवाइसों को KMI के फ़्रीज़ होने से पहले, अपनी सभी डिपेंडेंसी तय करने की ज़रूरत न पड़े. KMI को बड़ा करने के लिए, नए या मौजूदा एक्सपोर्ट किए गए कर्नेल फ़ंक्शन के लिए, KMI में नए सिंबल जोड़े जा सकते हैं. भले ही, KMI फ़्रीज़ हो. अगर नए कर्नेल पैच, केएमआई का उल्लंघन नहीं करते हैं, तो उन्हें भी स्वीकार किया जा सकता है.

KMI के काम न करने के बारे में जानकारी

किसी कर्नेल में सोर्स होते हैं और बाइनरी उन सोर्स से बनाई जाती हैं. एबीआई की निगरानी वाली कर्नेल शाखाओं में, मौजूदा GKI एबीआई (.stg फ़ाइल के तौर पर) का एबीआई दिखाया जाता है. बाइनरी (vmlinux, Image, और किसी भी GKI मॉड्यूल) बनने के बाद, बाइनरी से एबीआई का प्रतिनिधित्व निकाला जा सकता है. किसी कर्नेल सोर्स फ़ाइल में किए गए किसी भी बदलाव से, बाइनरी पर असर पड़ सकता है. साथ ही, इससे निकाले गए .stg पर भी असर पड़ सकता है. AbiAnalyzer विश्लेषक, कमिट की गई .stg फ़ाइल की तुलना, बिल्ड आर्टफ़ैक्ट से निकाली गई फ़ाइल से करता है. अगर उसे सेमेटिक फ़र्क़ मिलता है, तो वह Gerrit में किए गए बदलाव पर Lint-1 लेबल सेट करता है.

एबीआई (ऐप्लिकेशन बाइनरी इंटरफ़ेस) से जुड़ी गड़बड़ियों को मैनेज करना

उदाहरण के लिए, नीचे दिए गए पैच में एबीआई (ऑब्जेक्ट फ़ाइल फ़ॉर्मैट) में बहुत साफ़ तौर पर बदलाव किया गया है:

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.

इस पैच को लागू करके एबीआई बिल्ड करने पर, टूल एक नॉन-ज़ीरो गड़बड़ी कोड के साथ बाहर निकल जाता है और इससे मिलता-जुलता एबीआई अंतर रिपोर्ट करता है:

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

बिल्ड के समय एबीआई में अंतर का पता चलना

गड़बड़ियों की सबसे आम वजह यह होती है कि कोई ड्राइवर, कर्नेल से किसी ऐसे नए सिंबल का इस्तेमाल करता है जो किसी भी सिंबल की सूची में नहीं है.

अगर सिंबल, सिंबल की सूची (android/abi_gki_aarch64) में शामिल नहीं है, तो आपको पहले पुष्टि करनी होगी कि इसे EXPORT_SYMBOL_GPL(symbol_name) के साथ एक्सपोर्ट किया गया है. इसके बाद, एबीआई एक्सएमएल के तौर पर दिखाए जाने वाले डेटा और सिंबल की सूची को अपडेट करें. उदाहरण के लिए, इन बदलावों से android-12-5.10 शाखा में इंक्रीमेंटल एफ़एस की नई सुविधा जुड़ती है. इसमें, सिंबल की सूची और एबीआई एक्सएमएल का अपडेट शामिल है.

  • सुविधा में हुए बदलाव का उदाहरण, aosp/1345659 में दिया गया है.
  • सिंबल की सूची का उदाहरण aosp/1346742 में दिया गया है.
  • एबीआई एक्सएमएल में बदलाव का उदाहरण, 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

इसे ठीक करने के लिए, अपने कर्नेल और ACK, दोनों में KMI सिंबल की सूची अपडेट करें (एबीआई के रिप्रज़ेंटेशन को अपडेट करना देखें). ACK में एबीआई एक्सएमएल और सिंबल की सूची अपडेट करने के उदाहरण के लिए, aosp/1367601 देखें.

कर्नेल ABI ब्रेकेज का समाधान करें

कोड को फिर से लिखकर, एबीआई में बदलाव न करने या एबीआई के रिप्रज़ेंटेशन को अपडेट करके, कर्नेल एबीआई के गड़बड़ियों को ठीक किया जा सकता है. अपनी स्थिति के हिसाब से सबसे सही तरीका तय करने के लिए, यहां दिए गए चार्ट का इस्तेमाल करें.

ABI ब्रेकेज फ़्लो चार्ट

पहली इमेज. एबीआई के उल्लंघन को ठीक करना

एबीआई में बदलावों से बचने के लिए, कोड को रीफ़ैक्टर करें

मौजूदा एबीआई में बदलाव करने से बचने की पूरी कोशिश करें. कई मामलों में, एबीआई पर असर डालने वाले बदलावों को हटाने के लिए, अपने कोड को फिर से तैयार किया जा सकता है.

  • स्ट्रक्चर फ़ील्ड में किए गए बदलावों को रीफ़ैक्टर करना. अगर किसी बदलाव से डीबग करने की सुविधा के लिए एबीआई में बदलाव होता है, तो फ़ील्ड के आस-पास (स्ट्रक्चर और सोर्स रेफ़रंस में) #ifdef जोड़ें. साथ ही, पक्का करें कि #ifdef के लिए इस्तेमाल किया गया CONFIG, प्रोडक्शन defconfig और gki_defconfig के लिए बंद हो. एबीआई को बदले बिना, किसी स्ट्रक्चर में डीबग कॉन्फ़िगरेशन जोड़ने का उदाहरण देखने के लिए, इस पैच सेट को देखें.

  • कोर कर्नेल में बदलाव न करने के लिए, सुविधाओं को फिर से तैयार करना. अगर पार्टनर मॉड्यूल के साथ काम करने के लिए, ACK में नई सुविधाएं जोड़नी हैं, तो बदलाव के एबीआई हिस्से को फिर से फ़ैक्टर करने की कोशिश करें, ताकि कर्नेल एबीआई में बदलाव न करना पड़े. मौजूदा कर्नेल एबीआई का इस्तेमाल करके, कर्नेल एबीआई में बदलाव किए बिना अतिरिक्त सुविधाएं जोड़ने का उदाहरण देखने के लिए, aosp/1312213 देखें.

Android Gerrit पर काम न करने वाले एबीआई को ठीक करना

अगर आपने जान-बूझकर कर्नेल एबीआई को नहीं तोड़ा है, तो आपको एबीआई मॉनिटरिंग टूल से मिले दिशा-निर्देशों का इस्तेमाल करके, इसकी जांच करनी होगी. डेटा स्ट्रक्चर में बदलाव होने और उससे जुड़े सिंबल सीआरसी में बदलाव होने की सबसे सामान्य वजहें हैं. इसके अलावा, कॉन्फ़िगरेशन के विकल्पों में बदलाव की वजह से भी इनमें से कोई भी बदलाव हो सकता है. टूल से मिली समस्याओं को ठीक करके शुरुआत करें.

एबीआई से जुड़ी समस्याओं को स्थानीय तौर पर दोहराया जा सकता है. इसके लिए, कर्नल और उसके एबीआई का प्रतिनिधित्व बनाएं लेख पढ़ें.

Lint-1 लेबल के बारे में जानकारी

अगर किसी ऐसी शाखा में बदलाव अपलोड किए जाते हैं जिसमें फ़्रीज़ किया गया या फ़ाइनल किया गया केएमआई शामिल है, तो बदलावों को AbiAnalyzer से पास करना होगा. इससे यह पक्का किया जा सकेगा कि बदलावों का, काम करने वाले एबीआई पर गलत तरीके से असर न पड़े. इस प्रोसेस के दौरान, AbiAnalyzer उस एबीआई रिपोर्ट को खोजता है जो बिल्ड के दौरान बनाई गई है. यह एक एक्सटेंडेड बिल्ड है, जो सामान्य बिल्ड के बाद, एबीआई निकालने और तुलना करने के कुछ चरणों को पूरा करता है.

अगर AbiAnalyzer को कोई रिपोर्ट मिलती है, तो वह Lint-1 लेबल सेट करता है. साथ ही, जब तक समस्या हल नहीं हो जाती, तब तक बदलाव को सबमिट करने से रोक दिया जाता है. ऐसा तब तक किया जाता है, जब तक पैचसेट को Lint+1 लेबल नहीं मिल जाता.

kernel ABI को अपडेट करना

अगर एबीआई में बदलाव करना ज़रूरी है, तो आपको कोड में किए गए बदलावों, एबीआई के रेप्रज़ेंटेशन, और सिंबल की सूची को ACK में लागू करना होगा. Lint को -1 हटाने और GKI के साथ काम करने की सुविधा को बनाए रखने के लिए, यह तरीका अपनाएं:

  1. ACK में कोड में हुए बदलाव अपलोड करें.

  2. पैचसेट के लिए, कोड की समीक्षा +2 स्टेटस मिलने का इंतज़ार करें.

  3. रेफ़रंस एबीआई का प्रतिनिधित्व अपडेट करें.

  4. अपने कोड और एबीआई में हुए बदलावों को मर्ज करें.

एबीआई कोड में किए गए बदलावों को ACK पर अपलोड करें

ACK ABI को अपडेट करने का तरीका, किए जा रहे बदलाव के टाइप पर निर्भर करता है.

  • अगर एबीआई में किया गया बदलाव, किसी ऐसी सुविधा से जुड़ा है जिसका असर सीटीएस या वीटीएस टेस्ट पर पड़ता है, तो आम तौर पर बदलाव को स्वीकार करने के लिए, उसे चुनकर स्वीकार किया जा सकता है. उदाहरण के लिए:

    • ऑडियो की सुविधा काम करने के लिए, aosp/1289677 की ज़रूरत होती है.
    • यूएसबी के काम करने के लिए, aosp/1295945 की ज़रूरत होती है.
  • अगर एबीआई में कोई बदलाव किसी ऐसी सुविधा के लिए है जिसे ACK के साथ शेयर किया जा सकता है, तो उस बदलाव को ACK पर लागू किया जा सकता है. उदाहरण के लिए, ये बदलाव CTS या VTS के लिए ज़रूरी नहीं हैं, लेकिन इन्हें ACK के साथ शेयर किया जा सकता है:

    • aosp/1250412 एक थर्मल फ़ीचर बदलाव है.
    • aosp/1288857 EXPORT_SYMBOL_GPL बदलाव है.
  • अगर एबीआई में किए गए बदलाव से कोई नई सुविधा मिलती है, जिसे ACK में शामिल करने की ज़रूरत नहीं है, तो स्टब का इस्तेमाल करके ACK में सिंबल जोड़े जा सकते हैं. इसके बारे में अगले सेक्शन में बताया गया है.

ACK के लिए स्टब का इस्तेमाल करना

स्टब सिर्फ़ मुख्य कर्नेल में किए गए उन बदलावों के लिए ज़रूरी हैं जिनसे ACK को फ़ायदा नहीं मिलता. जैसे, परफ़ॉर्मेंस और पावर में बदलाव. नीचे दी गई सूची में, GKI के लिए ACK में स्टब और कुछ हिस्सों को चुनकर शामिल करने के उदाहरण दिए गए हैं.

  • कोर-आइसोलेट फ़ीचर स्टब (aosp/1284493). ACK में ये सुविधाएं होना ज़रूरी नहीं है. हालांकि, आपके मॉड्यूल में इन सिंबल का इस्तेमाल करने के लिए, ACK में ये सिंबल मौजूद होने चाहिए.

  • वेंडर मॉड्यूल के लिए प्लेसहोल्डर सिंबल (aosp/1288860).

  • हर प्रोसेस के लिए mm इवेंट ट्रैकिंग की सुविधा को सिर्फ़ एबीआई के लिए चुना गया है (aosp/1288454). ओरिजनल पैच को स्वीकार करने के लिए चुना गया था. इसके बाद, task_struct और mm_event_count के लिए एबीआई के अंतर को हल करने के लिए, सिर्फ़ ज़रूरी बदलावों को शामिल करने के लिए पैच को छोटा किया गया था. इस पैच से mm_event_type enum को भी अपडेट किया जाता है, ताकि उसमें आखिरी सदस्य शामिल किए जा सकें.

  • थर्मल स्ट्रक्चर एबीआई में किए गए कुछ बदलावों को चुना गया है. इन बदलावों के लिए, एबीआई के नए फ़ील्ड जोड़ने के अलावा और भी काम करने पड़े.

    • पैच aosp/1255544 ने पार्टनर कर्नेल और ACK के बीच एबीआई के अंतर को ठीक किया.

    • पैच aosp/1291018 ने, पिछले पैच की GKI जांच के दौरान मिली फ़ंक्शनल समस्याओं को ठीक किया है. इस समस्या में सेंसर पैरामीटर के निर्देश को शुरू करना शामिल है, ताकि एक से ज़्यादा थर्मल ज़ोन को एक सेंसर पर रजिस्टर किया जा सके.

  • CONFIG_NL80211_TESTMODE एबीआई में बदलाव (aosp/1344321). इस पैच ने एबीआई के लिए ज़रूरी स्ट्रक्चर में बदलाव किए हैं. साथ ही, यह पक्का किया है कि अतिरिक्त फ़ील्ड की वजह से काम करने के तरीके में अंतर न आए. साथ ही, पार्टनर को अपने प्रोडक्शन कर्नेल में CONFIG_NL80211_TESTMODE को शामिल करने और जीकेआई का पालन करने में मदद मिले.

रनटाइम के दौरान KMI लागू करें

GKI केर्नेल, TRIM_UNUSED_KSYMS=y और UNUSED_KSYMS_WHITELIST=<union of all symbol lists> कॉन्फ़िगरेशन विकल्पों का इस्तेमाल करते हैं. इनकी मदद से, एक्सपोर्ट किए गए सिंबल (जैसे, EXPORT_SYMBOL_GPL() का इस्तेमाल करके एक्सपोर्ट किए गए सिंबल) को सिंबल की सूची में शामिल सिंबल तक सीमित किया जा सकता है. इसके अलावा, अन्य सभी सिंबल को एक्सपोर्ट नहीं किया जाता है. साथ ही, ऐसा मॉड्यूल लोड होने की अनुमति नहीं है जिसे एक्सपोर्ट नहीं किया गया हो. यह पाबंदी, बिल्ड के समय लागू होती है और मौजूद न होने वाली एंट्री को फ़्लैग किया जाता है.

डेवलपमेंट के लिए, GKI के ऐसे कर्नेल बिल्ड का इस्तेमाल किया जा सकता है जिसमें सिंबल ट्रिमिंग शामिल न हो. इसका मतलब है कि आम तौर पर एक्सपोर्ट किए गए सभी सिंबल का इस्तेमाल किया जा सकता है. इन बिल्ड को ढूंढने के लिए, ci.android.com पर kernel_debug_aarch64 बिल्ड खोजें.

मॉड्यूल वर्शन का इस्तेमाल करके, केएमआई लागू करें

जनरेटिक कर्नेल इमेज (जीकेआई) के कर्नेल, रनटाइम पर केएमआई के अनुपालन को लागू करने के लिए, मॉड्यूल वर्शनिंग (CONFIG_MODVERSIONS) का इस्तेमाल एक अतिरिक्त उपाय के तौर पर करते हैं. मॉड्यूल के वर्शन की वजह से, मॉड्यूल लोड होने के समय साइक्लिक रेडंडेंसी जांच (सीआरसी) में गड़बड़ी हो सकती है. ऐसा तब होता है, जब किसी मॉड्यूल का अनुमानित केएमआई, vmlinux केएमआई से मेल न खाता हो. उदाहरण के लिए, 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 ''

मॉड्यूल के वर्शन का इस्तेमाल

मॉड्यूल वर्शनिंग इन वजहों से फ़ायदेमंद है:

  • मॉड्यूल के वर्शन से, डेटा स्ट्रक्चर की दिखने की सेटिंग में हुए बदलावों का पता चलता है. अगर मॉड्यूल, ऐसे डेटा स्ट्रक्चर में बदलाव करते हैं जो पारदर्शी नहीं हैं, यानी ऐसे डेटा स्ट्रक्चर जो केएमआई का हिस्सा नहीं हैं, तो स्ट्रक्चर में आने वाले बदलावों के बाद वे काम नहीं करते.

    उदाहरण के लिए, struct device में मौजूद fwnode फ़ील्ड को देखें. यह ज़रूरी है कि मॉड्यूल के लिए यह फ़ील्ड ऐपैरेंट न हो, ताकि वे device->fw_node के फ़ील्ड में बदलाव न कर सकें या उसके साइज़ के बारे में अनुमान न लगा सकें.

    हालांकि, अगर किसी मॉड्यूल में <linux/fwnode.h> (सीधे या किसी दूसरे तरीके से) शामिल है, तो struct device में मौजूद fwnode फ़ील्ड अब उसके लिए पारदर्शी नहीं होगा. इसके बाद, मॉड्यूल device->fwnode->dev या device->fwnode->ops में बदलाव कर सकता है. इस स्थिति में कई समस्याएं आ सकती हैं. इनके बारे में यहां बताया गया है:

    • इससे, कोर कर्नेल कोड के अपने इंटरनल डेटा स्ट्रक्चर के बारे में की गई अनुमानित गलतियां ठीक हो सकती हैं.

    • अगर आने वाले समय में किसी कर्नेल के अपडेट में struct fwnode_handle (fwnode का डेटा टाइप) बदल जाता है, तो मॉड्यूल नए कर्नेल के साथ काम नहीं करता. इसके अलावा, stgdiff में कोई अंतर नहीं दिखेगा, क्योंकि मॉड्यूल सीधे तौर पर इंटरनल डेटा स्ट्रक्चर में बदलाव करके, केएमआई को तोड़ रहा है. इस बदलाव को सिर्फ़ बाइनरी रिप्रज़ेंटेशन की जांच करके कैप्चर नहीं किया जा सकता.

  • किसी मौजूदा मॉड्यूल को तब KMI के साथ काम न करने वाला माना जाता है, जब उसे बाद में किसी ऐसे नए कर्नेल से लोड किया जाता है जो काम नहीं करता. मॉड्यूल के वर्शन में, रन-टाइम की जांच जोड़ी जाती है, ताकि गलती से ऐसा मॉड्यूल लोड न हो जो कर्नेल के साथ KMI के साथ काम न करता हो. इस जांच से, रनटाइम की उन समस्याओं और कर्नेल क्रैश से बचा जा सकता है जिन्हें डीबग करना मुश्किल होता है. ये समस्याएं, KMI में मौजूद किसी ऐसी समस्या की वजह से हो सकती हैं जिसका पता नहीं चल पाया है.

मॉड्यूल वर्शनिंग की सुविधा चालू करने से, इन सभी समस्याओं से बचा जा सकता है.

डिवाइस को चालू किए बिना, सीआरसी मेल नहीं खाने की जांच करें

stgdiff, एबीआई के अन्य अंतरों के साथ-साथ, कर्नेल के बीच सीआरसी मैच न होने की तुलना करता है और उसकी शिकायत करता है.

इसके अलावा, CONFIG_MODVERSIONS चालू होने पर पूरा कर्नेल बिल्ड करने पर, सामान्य बिल्ड प्रोसेस के हिस्से के तौर पर Module.symvers फ़ाइल जनरेट होती है. इस फ़ाइल में, कर्नेल (vmlinux) और मॉड्यूल से एक्सपोर्ट किए गए हर सिंबल के लिए एक लाइन होती है. हर लाइन में सीआरसी वैल्यू, सिंबल का नाम, सिंबल नेमस्पेस, सिंबल को एक्सपोर्ट करने वाले vmlinux या मॉड्यूल का नाम, और एक्सपोर्ट टाइप (उदाहरण के लिए, EXPORT_SYMBOL बनाम EXPORT_SYMBOL_GPL) शामिल होता है.

GKI बिल्ड और अपने बिल्ड के बीच Module.symvers फ़ाइलों की तुलना की जा सकती है, ताकि vmlinux से एक्सपोर्ट किए गए सिंबल में सीआरसी के अंतर की जांच की जा सके. अगर vmlinux से एक्सपोर्ट किए गए किसी सिंबल में सीआरसी वैल्यू का अंतर है और उस सिंबल का इस्तेमाल आपके डिवाइस में लोड किए गए किसी मॉड्यूल में किया जाता है, तो मॉड्यूल लोड नहीं होता.

अगर आपके पास सभी बिल्ड आर्टफ़ैक्ट नहीं हैं, लेकिन जीकेआई कर्नेल और कर्नेल की vmlinux फ़ाइलें हैं, तो किसी खास सिंबल के लिए सीआरसी वैल्यू की तुलना करने के लिए, दोनों कर्नेल पर नीचे दिया गया कमांड चलाएं.

nm <path to vmlinux>/vmlinux | grep __crc_<symbol name>

उदाहरण के लिए, यह निर्देश module_layout सिंबल के लिए सीआरसी वैल्यू की जांच करता है:

nm vmlinux | grep __crc_module_layout
0000000008663742 A __crc_module_layout

सीआरसी मैच न होने की समस्या हल करना

मॉड्यूल लोड करते समय सीआरसी मैच न होने की समस्या को हल करने के लिए, यह तरीका अपनाएं:

  1. इस कमांड में दिखाए गए तरीके का इस्तेमाल करके, --kbuild_symtypes विकल्प का इस्तेमाल करके GKI कर्नेल और डिवाइस कर्नेल बनाएं:

    tools/bazel run --kbuild_symtypes //common:kernel_aarch64_dist
    

    यह कमांड, हर .o फ़ाइल के लिए एक .symtypes फ़ाइल जनरेट करता है. ज़्यादा जानकारी के लिए, Kleaf में KBUILD_SYMTYPES देखें.

    Android 13 और उससे पहले के वर्शन के लिए, GKI kernel और अपने डिवाइस के kernel को इकट्ठा करें. इसके लिए, kernel को इकट्ठा करने के लिए इस्तेमाल किए जाने वाले कमांड के आगे KBUILD_SYMTYPES=1 जोड़ें, जैसा कि इस कमांड में दिखाया गया है:

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

    build_abi.sh, का इस्तेमाल करते समय, KBUILD_SYMTYPES=1 फ़्लैग पहले से ही सेट होता है.

  2. नीचे दिए गए कमांड का इस्तेमाल करके, वह .c फ़ाइल ढूंढें जिसमें सीआरसी मैच न करने वाला सिंबल एक्सपोर्ट किया गया है:

    cd common && git grep EXPORT_SYMBOL.*module_layout
    kernel/module.c:EXPORT_SYMBOL(module_layout);
    
  3. .c फ़ाइल के लिए, जीकेआई में .symtypes फ़ाइल और आपके डिवाइस के कर्नेल के बिल्ड आर्टफ़ैक्ट मौजूद होते हैं. नीचे दिए गए निर्देशों का इस्तेमाल करके, .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. दोनों फ़ाइलों की तुलना करें और उनमें मौजूद अंतरों को ठीक करें.

पहला उदाहरण: डेटा टाइप दिखने की सेटिंग की वजह से अंतर

अगर एक कर्नेल, मॉड्यूल के लिए किसी सिंबल या डेटा टाइप को ओपैक रखता है और दूसरा कर्नेल ऐसा नहीं करता है, तो दोनों कर्नेल की .symtypes फ़ाइलों के बीच यह अंतर दिखता है. एक केरल में मौजूद .symtypes फ़ाइल में, किसी सिंबल के लिए UNKNOWN है और दूसरे केरल में मौजूद .symtypes फ़ाइल में, सिंबल या डेटा टाइप का बड़ा व्यू है.

उदाहरण के लिए, अपनी कर्नेल में include/linux/device.h फ़ाइल में यह लाइन जोड़ने से सीआरसी मेल नहीं खाता, जिसमें से एक 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 कर्नेल में मौजूद नहीं है.

अक्सर, genksyms से नया #include छिपाकर ही समस्या को ठीक किया जा सकता है.

#ifndef __GENKSYMS__
#include <linux/fwnode.h>
#endif

इसके अलावा, अंतर की वजह बनने वाले #include की पहचान करने के लिए, यह तरीका अपनाएं:

  1. वह हेडर फ़ाइल खोलें जिसमें इस अंतर वाले सिंबल या डेटा टाइप के बारे में बताया गया है. उदाहरण के लिए, struct fwnode_handle के लिए include/linux/fwnode.h में बदलाव करें.

  2. हेडर फ़ाइल के सबसे ऊपर यह कोड जोड़ें:

    #ifdef CRC_CATCH
    #error "Included from here"
    #endif
    
  3. जिस मॉड्यूल की .c फ़ाइल में सीआरसी मैच नहीं हो रहा है उसमें, #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 पर अपलोड करें और मर्ज कराएं.

दूसरा मामला: डेटा टाइप में बदलाव की वजह से अंतर

अगर किसी सिंबल या डेटा टाइप के लिए सीआरसी मैच नहीं होता है, तो इसका मतलब है कि डेटा टाइप में कोई बदलाव हुआ है. जैसे, डेटा टाइप में कुछ जोड़ा गया है, कुछ हटाया गया है या उसमें बदलाव किया गया है.

उदाहरण के लिए, अपने कर्नेल में यह बदलाव करने से कई सीआरसी मैच नहीं होते, क्योंकि इस तरह के बदलाव से कई सिंबल पर अप्रत्यक्ष रूप से असर पड़ता है:

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);

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 पर अपलोड करें और मर्ज करें.