Android 11 और इसके बाद के वर्शन में उपलब्ध, ऐप्लिकेशन बाइनरी इंटरफ़ेस (एबीआई) मॉनिटरिंग टूल का इस्तेमाल करके, Android के कोर में मौजूद एबीआई को स्थिर किया जा सकता है. टूल, मौजूदा कर्नेल बाइनरी (vmlinux
+ GKI मॉड्यूल) से एबीआई के रिप्रज़ेंटेशन इकट्ठा करता है और उनकी तुलना करता है. एबीआई के ये प्रतिनिधित्व, .stg
फ़ाइलें और सिंबल की सूचियां हैं. जिस इंटरफ़ेस पर प्रज़ेंटेशन व्यू देता है उसे केर्नेल मॉड्यूल इंटरफ़ेस
(केएमआई) कहा जाता है. टूल का इस्तेमाल करके, केएमआई में होने वाले बदलावों को ट्रैक किया जा सकता है और उन्हें कम किया जा सकता है.
एबीआई मॉनिटरिंग टूल को AOSP में डेवलप किया गया है. साथ ही, यह STG (या Android 13 और उससे पहले के वर्शन में libabigail
) का इस्तेमाल करके, रिप्रज़ेंटेशन जनरेट करता है और उनकी तुलना करता है.
इस पेज में टूलिंग, एबीआई के प्रतिनिधित्व को इकट्ठा करने और उनका विश्लेषण करने की प्रक्रिया के बारे में बताया गया है. साथ ही, कर्नेल एबीआई को स्थिरता देने के लिए ऐसे तरीकों के इस्तेमाल के बारे में भी बताया गया है. इस पेज पर, Android के कर्नेल में बदलाव करने के बारे में भी जानकारी दी गई है.
प्रोसेस
कर्नेल के एबीआई का विश्लेषण कई चरणों में होता है, जिनमें से ज़्यादातर चरण अपने-आप होते हैं:
- कर्नल और उसके एबीआई का प्रतिनिधित्व बनाएं.
- बिल्ड और रेफ़रंस के बीच एबीआई (एबिट्रेशन बिडिंग इंटरफ़ेस) के अंतर का विश्लेषण करें.
- एबीआई प्रतिनिधि की जानकारी अपडेट करें (अगर ज़रूरी हो).
- सिंबल की सूचियों के साथ काम करना.
नीचे दिए गए निर्देश, काम करने वाले टूलचेन का इस्तेमाल करके, बिल्ड किए जा सकने वाले किसी भी कर्नेल के लिए काम करते हैं. जैसे, पहले से बने 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 फ़्रीज़ हो. अगर नए कर्नेल पैच केएमआई को तोड़ नहीं पाते, तो उन्हें भी स्वीकार किया जा सकता है.
केएमआई की गड़बड़ी के बारे में जानकारी
किसी कर्नेल में सोर्स होते हैं और बाइनरी उन सोर्स से बनाई जाती हैं.
एबीआई की निगरानी वाली कर्नेल शाखाओं में, मौजूदा 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 में ABI एक्सएमएल और सिंबल की सूची को अपडेट करने का उदाहरण पाने के लिए, aosp/1367601 देखें.
कर्नेल एबीआई के गड़बड़ियों को ठीक करना
एबीआई में बदलाव न करने के लिए कोड को रीफ़ैक्ट करके या एबीआई रिप्रज़ेंटेशन को अपडेट करके, कर्नेल एबीआई के ब्रेकेज को मैनेज किया जा सकता है. अपनी स्थिति के हिसाब से सबसे सही तरीका तय करने के लिए, नीचे दिए गए चार्ट का इस्तेमाल करें.
पहली इमेज. एबीआई के उल्लंघन को ठीक करना
एबीआई में बदलावों से बचने के लिए, कोड को फिर से लिखना
मौजूदा एबीआई में बदलाव करने से बचने की पूरी कोशिश करें. कई मामलों में, एबीआई पर असर डालने वाले बदलावों को हटाने के लिए, अपने कोड को फिर से तैयार किया जा सकता है.
स्ट्रक्चर फ़ील्ड में बदलावों को फिर से तैयार करना. अगर किसी बदलाव से डीबग की सुविधा के लिए एबीआई में बदलाव होता है, तो फ़ील्ड के आस-पास (स्ट्रक्चर और सोर्स रेफ़रंस में)
#ifdef
जोड़ें. साथ ही, पक्का करें कि#ifdef
के लिए इस्तेमाल किया गयाCONFIG
, प्रोडक्शन defconfig औरgki_defconfig
के लिए बंद हो. एबीआई को बदले बिना, किसी स्ट्रक्चर में डीबग कॉन्फ़िगरेशन जोड़ने का उदाहरण देखने के लिए, इस पैच सेट को देखें.कोर कर्नेल में बदलाव न करने के लिए, सुविधाओं को फिर से तैयार करना. अगर पार्टनर मॉड्यूल के साथ काम करने के लिए, ACK में नई सुविधाएं जोड़नी हैं, तो बदलाव के एबीआई हिस्से को फिर से फ़ैक्टर करने की कोशिश करें, ताकि कर्नेल एबीआई में बदलाव न करना पड़े. मौजूदा कर्नेल एबीआई का इस्तेमाल करके, कर्नेल एबीआई में बदलाव किए बिना अतिरिक्त सुविधाएं जोड़ने का उदाहरण देखने के लिए, aosp/1312213 देखें.
Android Gerrit पर, काम न करने वाले एबीआई को ठीक करना
अगर आपने जान-बूझकर कर्नेल एबीआई को नहीं तोड़ा है, तो आपको एबीआई मॉनिटरिंग टूल से मिले दिशा-निर्देशों का इस्तेमाल करके, इसकी जांच करनी होगी. आम तौर पर, डेटा स्ट्रक्चर और उससे जुड़े सिंबल सीआरसी में बदलाव होने की वजह से, डेटा फ़ाइलें क्रैक हो जाती हैं. इसके अलावा, कॉन्फ़िगरेशन के विकल्प में बदलाव होने की वजह से भी ऐसा हो सकता है. टूल से मिली समस्याओं को हल करके शुरुआत करें.
एबीआई से जुड़ी समस्याओं को स्थानीय तौर पर दोहराया जा सकता है. इसके लिए, कर्नल और उसके एबीआई का प्रतिनिधित्व बनाएं लेख पढ़ें.
लिंट-1 लेबल के बारे में जानकारी
अगर किसी ऐसी शाखा में बदलाव अपलोड किए जाते हैं जिसमें फ़्रीज़ किया गया या फ़ाइनल किया गया केएमआई शामिल है, तो बदलावों को AbiAnalyzer
से पास करना होगा. इससे यह पक्का किया जा सकेगा कि बदलावों का, काम करने वाले एबीआई पर गलत तरीके से असर न पड़े. इस प्रोसेस के दौरान, AbiAnalyzer
उस एबीआई रिपोर्ट को खोजता है जो बिल्ड के दौरान बनाई गई है. यह एक एक्सटेंडेड बिल्ड है, जो सामान्य बिल्ड के बाद, एबीआई निकालने और तुलना करने के कुछ चरणों को पूरा करता है.
अगर AbiAnalyzer
को कोई रिपोर्ट मिलती है, तो वह Lint-1 लेबल सेट करता है. साथ ही, जब तक समस्या हल नहीं हो जाती, तब तक बदलाव को सबमिट करने से रोक दिया जाता है. ऐसा तब तक किया जाता है, जब तक पैचसेट को Lint+1 लेबल नहीं मिल जाता.
kernel ABI को अपडेट करना
अगर एबीआई में बदलाव नहीं किया जा सकता, तो आपको कोड में किए गए बदलाव, एबीआई में बदलाव, और सिंबल की सूची को ACK पर लागू करना होगा. Lint को -1 हटाने और GKI के साथ काम करने की सुविधा को बनाए रखने के लिए, यह तरीका अपनाएं:
पैचसेट के लिए कोड-समीक्षा +2 मिलने की इंतज़ार करें.
अपने कोड में किए गए बदलावों और एबीआई अपडेट में किए गए बदलावों को मर्ज करें.
ACK में ABI कोड में किए गए बदलाव अपलोड करना
ACK ABI को अपडेट किए जाने के तरीके पर निर्भर करता है.
अगर एबीआई में किया गया बदलाव, किसी ऐसी सुविधा से जुड़ा है जिसका असर सीटीएस या वीटीएस टेस्ट पर पड़ता है, तो आम तौर पर बदलाव को स्वीकार करने के लिए, उसे चुनकर स्वीकार किया जा सकता है. उदाहरण के लिए:
- ऑडियो की सुविधा काम करने के लिए, aosp/1289677 की ज़रूरत होती है.
- यूएसबी के काम करने के लिए, aosp/1295945 की ज़रूरत होती है.
अगर एबीआई में किया गया बदलाव किसी ऐसी सुविधा के लिए है जिसे ACK के साथ शेयर किया जा सकता है, तो उस बदलाव को ACK के साथ शेयर किया जा सकता है. उदाहरण के लिए, नीचे दिए गए बदलावों की ज़रूरत CTS या VTS टेस्ट के लिए नहीं है, लेकिन इन्हें ACK के साथ शेयर किया जा सकता है:
- aosp/1250412, डिवाइस के तापमान से जुड़ी सुविधा में बदलाव है.
- aosp/1288857
EXPORT_SYMBOL_GPL
बदलाव है.
अगर एबीआई में किए गए बदलाव से कोई नई सुविधा मिलती है, जिसे ACK में शामिल करने की ज़रूरत नहीं है, तो स्टब का इस्तेमाल करके ACK में सिंबल जोड़े जा सकते हैं. इसके बारे में अगले सेक्शन में बताया गया है.
ACK के लिए स्टब का इस्तेमाल करना
स्टब सिर्फ़ मुख्य कर्नेल में किए गए उन बदलावों के लिए ज़रूरी हैं जिनसे ACK को फ़ायदा नहीं मिलता. जैसे, परफ़ॉर्मेंस और पावर में बदलाव. नीचे दी गई सूची में, GKI के लिए ACK में स्टब और कुछ हिस्सों को चुनकर शामिल करने के उदाहरण दिए गए हैं.
Core-isolate सुविधा का स्टब (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 में मौजूद किसी ऐसी समस्या की वजह से हो सकती हैं जिसका पता नहीं चल पाया है.
मॉड्यूल वर्शनिंग की सुविधा चालू करने से, इन सभी समस्याओं से बचा जा सकता है.
डिवाइस को बूट किए बिना सीआरसी मेल न खाने की जांच करना
stgdiff
, एबीआई के अन्य अंतरों के साथ-साथ, कर्नेल के बीच सीआरसी मैच न होने की तुलना करता है और उसकी शिकायत करता है.
इसके अलावा, CONFIG_MODVERSIONS
चालू होने पर पूरा कर्नेल बिल्ड करने पर, सामान्य बिल्ड प्रोसेस के हिस्से के तौर पर Module.symvers
फ़ाइल जनरेट होती है. इस फ़ाइल में, कर्नेल (vmlinux
) और मॉड्यूल से एक्सपोर्ट किए गए हर सिंबल के लिए एक लाइन होती है. हर लाइन में सीआरसी वैल्यू, सिंबल का नाम, सिंबल नेमस्पेस, सिंबल को एक्सपोर्ट करने वाले vmlinux
या मॉड्यूल का नाम, और एक्सपोर्ट टाइप (उदाहरण के लिए, EXPORT_SYMBOL
बनाम EXPORT_SYMBOL_GPL
) शामिल होता है.
GKI बिल्ड और अपने बिल्ड के बीच Module.symvers
फ़ाइलों की तुलना की जा सकती है, ताकि vmlinux
से एक्सपोर्ट किए गए सिंबल में सीआरसी के अंतर की जांच की जा सके. अगर vmlinux
से एक्सपोर्ट किए गए किसी सिंबल में सीआरसी वैल्यू का अंतर है और उस सिंबल का इस्तेमाल आपके डिवाइस में लोड किए गए किसी मॉड्यूल में किया जाता है, तो मॉड्यूल लोड नहीं होता.
अगर आपके पास सभी बिल्ड आर्टफ़ैक्ट नहीं हैं, लेकिन आपके पास GKI कर्नेल और आपके कर्नेल की vmlinux
फ़ाइलें हैं, तो किसी खास सिंबल के लिए सीआरसी वैल्यू की तुलना की जा सकती है. इसके लिए, दोनों कर्नेल पर यह कमांड चलाएं और आउटपुट की तुलना करें:
nm <path to vmlinux>/vmlinux | grep __crc_<symbol name>
उदाहरण के लिए, यह निर्देश module_layout
सिंबल के लिए सीआरसी वैल्यू की जांच करता है:
nm vmlinux | grep __crc_module_layout
0000000008663742 A __crc_module_layout
सीआरसी मैच न होने की समस्या हल करना
मॉड्यूल लोड करते समय, सीआरसी के मेल न खाने की समस्या को हल करने के लिए, नीचे दिया गया तरीका अपनाएं:
--kbuild_symtypes
विकल्प का इस्तेमाल करके, GKI kernel और अपने डिवाइस का kernel बनाएं. इसके लिए, नीचे दिए गए कमांड का इस्तेमाल करें: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
फ़्लैग पहले से ही सेट होता है.नीचे दिए गए कमांड का इस्तेमाल करके, वह
.c
फ़ाइल ढूंढें जिसमें सीआरसी मैच न करने वाला सिंबल एक्सपोर्ट किया गया है:cd common && git grep EXPORT_SYMBOL.*module_layout kernel/module.c:EXPORT_SYMBOL(module_layout);
.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 * )
दोनों फ़ाइलों की तुलना करें और सभी अंतर ठीक करें.
पहला उदाहरण: डेटा टाइप दिखने की सेटिंग की वजह से अंतर
अगर एक कर्नेल, मॉड्यूल के लिए किसी सिंबल या डेटा टाइप को ओपैक रखता है और दूसरा कर्नेल ऐसा नहीं करता है, तो दोनों कर्नेल की .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
की पहचान करने के लिए, यह तरीका अपनाएं:
वह हेडर फ़ाइल खोलें जिसमें इस अंतर वाले सिंबल या डेटा टाइप के बारे में बताया गया है. उदाहरण के लिए,
struct fwnode_handle
के लिएinclude/linux/fwnode.h
में बदलाव करें.हेडर फ़ाइल के सबसे ऊपर यह कोड जोड़ें:
#ifdef CRC_CATCH #error "Included from here" #endif
जिस मॉड्यूल की
.c
फ़ाइल में सीआरसी मैच नहीं हो रहा है उसमें,#include
लाइनों से पहले पहली लाइन के तौर पर यह जोड़ें.#define CRC_CATCH 1
अपना मॉड्यूल कंपाइल करें. बिल्ड के समय मिलने वाली गड़बड़ी से, हेडर फ़ाइल
#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 कोर में मौजूद नहीं है.बदलाव की पहचान करें और उसे अपने कर्नेल में पहले जैसा करें या उसे 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 * ) ; ...
बदले गए टाइप की पहचान करने के लिए, यह तरीका अपनाएं:
सोर्स कोड में सिंबल की परिभाषा ढूंढें (आम तौर पर,
.h
फ़ाइलों में).- अपने कर्नेल और GKI कर्नेल के बीच सिंबल के अंतर के लिए, कमिट ढूंढने के लिए यह कमांड चलाएं:
git blame
- मिटाए गए सिंबल के लिए (जहां किसी सिंबल को एक ट्री में मिटाया गया है और आपको उसे दूसरे ट्री में भी मिटाना है), आपको उस बदलाव को ढूंढना होगा जिसकी वजह से लाइन मिट गई है. जहां से लाइन मिटाई गई थी उस ट्री पर यहां दिए गए कमांड का इस्तेमाल करें:
git log -S "copy paste of deleted line/word" -- <file where it was deleted>
बदलाव या मिटाए गए डेटा का पता लगाने के लिए, कमिट की गई फ़ाइलों की सूची देखें. सबसे पहला वादा, शायद वह हो जिसे आप खोज रहे हैं. अगर ऐसा नहीं है, तो इस सूची को तब तक देखें, जब तक आपको तय की गई नीति नहीं मिल जाती.
बदलाव की पहचान करने के बाद, उसे अपने कर्नेल में वापस लाएं या उसे ACK पर अपलोड करें और उसे मर्ज कराएं.