c++ - संकलन/लिंकिंग प्रक्रिया कैसे काम करती है?




compiler-construction linker (4)

CProgramming.com पर इस विषय पर चर्चा की गई है:
https://www.cprogramming.com/compilingandlinking.html

लेखक ने वहां लिखा है:

संकलन एक निष्पादन योग्य फ़ाइल बनाने के समान नहीं है! इसके बजाए, निष्पादन योग्य बनाना एक बहुस्तरीय प्रक्रिया है जो दो घटकों में विभाजित है: संकलन और लिंकिंग। हकीकत में, भले ही कोई प्रोग्राम "ठीक से संकलित" हो, फिर भी यह लिंकिंग चरण के दौरान त्रुटियों के कारण वास्तव में काम नहीं कर सकता है। स्रोत कोड फ़ाइलों से निष्पादन योग्य होने की कुल प्रक्रिया को बेहतर रूप से निर्माण के रूप में संदर्भित किया जा सकता है।

संकलन

संकलन स्रोत कोड फ़ाइलों (.c, .cc, या .cpp) की प्रसंस्करण और 'ऑब्जेक्ट' फ़ाइल के निर्माण को संदर्भित करता है। यह चरण उपयोगकर्ता को वास्तव में चलाने वाले कुछ भी नहीं बनाता है। इसके बजाए, कंपाइलर केवल मशीन भाषा निर्देशों का उत्पादन करता है जो संकलित किए गए स्रोत कोड फ़ाइल से मेल खाते हैं। उदाहरण के लिए, यदि आप तीन अलग-अलग फ़ाइलों को संकलित (लेकिन लिंक नहीं करते हैं), तो आपके पास आउटपुट के रूप में बनाई गई तीन ऑब्जेक्ट फ़ाइलें होंगी, प्रत्येक नाम .o या .obj (एक्सटेंशन आपके कंपाइलर पर निर्भर करेगा)। इनमें से प्रत्येक फाइल में आपकी सोर्स कोड फ़ाइल का एक मशीन भाषा फ़ाइल में अनुवाद होता है - लेकिन आप उन्हें अभी तक नहीं चला सकते हैं! आपको उन्हें अपने ऑपरेटिंग सिस्टम का उपयोग कर निष्पादन योग्य में बदलना होगा। यही वह जगह है जहां लिंकर आता है।

लिंक करना

लिंकिंग एकाधिक ऑब्जेक्ट फ़ाइलों से एक निष्पादन योग्य फ़ाइल के निर्माण को संदर्भित करता है। इस चरण में, यह आम बात है कि लिंकर अपरिभाषित कार्यों (आमतौर पर, मुख्य रूप से) के बारे में शिकायत करेगा। संकलन के दौरान, यदि संकलक किसी विशेष फ़ंक्शन की परिभाषा नहीं ढूंढ सका, तो यह केवल यह मान लेगा कि फ़ंक्शन को किसी अन्य फ़ाइल में परिभाषित किया गया था। यदि ऐसा नहीं है, तो कंपाइलर को कोई रास्ता नहीं पता होगा - यह एक समय में एक से अधिक फाइलों की सामग्री को नहीं देखता है। दूसरी तरफ, लिंकर कई फाइलों को देख सकता है और उन कार्यों के संदर्भों को खोजने का प्रयास कर सकता है जिनका उल्लेख नहीं किया गया था।

आप पूछ सकते हैं कि क्यों अलग संकलन और लिंकिंग कदम हैं। सबसे पहले, इस तरह से चीजों को लागू करना शायद आसान है। कंपाइलर इसकी बात करता है, और लिंकर इसकी बात करता है - कार्यों को अलग रखकर, कार्यक्रम की जटिलता कम हो जाती है। एक और (अधिक स्पष्ट) लाभ यह है कि यह हर बार फ़ाइल बदलने के बाद संकलन चरण को फिर से किए बिना बड़े कार्यक्रमों के निर्माण की अनुमति देता है। इसके बजाए, तथाकथित "सशर्त संकलन" का उपयोग करके, केवल उन स्रोत फ़ाइलों को संकलित करना जरूरी है जो बदल गए हैं; बाकी के लिए, ऑब्जेक्ट फ़ाइलें लिंकर के लिए पर्याप्त इनपुट हैं। अंत में, यह पूर्व-संकलित कोड के पुस्तकालयों को कार्यान्वित करना आसान बनाता है: केवल ऑब्जेक्ट फाइलें बनाएं और उन्हें किसी अन्य ऑब्जेक्ट फ़ाइल की तरह लिंक करें। (तथ्य यह है कि प्रत्येक फाइल को अन्य फाइलों में निहित जानकारी से अलग से संकलित किया गया है, संयोग से, इसे "अलग संकलन मॉडल" कहा जाता है।)

हालत संकलन के पूर्ण लाभ प्राप्त करने के लिए, प्रोग्राम को प्राप्त करने के लिए संभवतः यह संभव है कि आप पिछली बार संकलित होने के बाद से कौन सी फाइलें बदल चुके हैं, याद रखने और याद रखने में मदद करें। (निश्चित रूप से, आप बस उस फाइल को दोबारा संकलित कर सकते हैं जिसमें संबंधित ऑब्जेक्ट फ़ाइल के टाइमस्टैम्प से अधिक टाइमस्टैम्प हो।) यदि आप एक एकीकृत विकास वातावरण (आईडीई) के साथ काम कर रहे हैं तो यह पहले से ही आपके लिए इसका ख्याल रख सकता है। यदि आप कमांड लाइन टूल्स का उपयोग कर रहे हैं, तो निफ्टी यूटिलिटी है जिसे अधिकांश * निक्स डिस्ट्रीब्यूशन के साथ आता है। सशर्त संकलन के साथ, इसमें प्रोग्रामिंग के लिए कई अन्य अच्छी सुविधाएं हैं, जैसे कि आपके प्रोग्राम के विभिन्न संकलन की अनुमति - उदाहरण के लिए, यदि आपके पास डिबगिंग के लिए वर्बोज़ आउटपुट का संस्करण है।

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

संकलन और लिंकिंग प्रक्रिया कैसे काम करती है?

(नोट: यह स्टैक ओवरफ्लो के सी ++ एफएक्यू में प्रवेश करने के लिए है । यदि आप इस फॉर्म में एक एफएक्यू प्रदान करने के विचार की आलोचना करना चाहते हैं, तो मेटा पर पोस्ट करना जो यह सब शुरू कर देगा, ऐसा करने का स्थान होगा। उस प्रश्न की निगरानी सी ++ चैटरूम में की जाती है, जहां एफएक्यू विचार पहली जगह शुरू हुआ था, इसलिए आपके उत्तर को उन लोगों द्वारा पढ़ा जाने की संभावना है जो इस विचार के साथ आए थे।)


पतला यह है कि एक सीपीयू मेमोरी पतों से डेटा लोड करता है, डेटा को स्मृति पते पर संग्रहीत करता है, और संसाधित निर्देशों के अनुक्रम में कुछ सशर्त कूद के साथ क्रमशः स्मृति पते से निर्देशों को निष्पादित करता है। निर्देशों की इन तीन श्रेणियों में से प्रत्येक में मशीन निर्देश में उपयोग किए जाने वाले मेमोरी सेल को एक पता कंप्यूटिंग करना शामिल है। चूंकि मशीन निर्देश एक विशेष लंबाई के आधार पर एक परिवर्तनीय लंबाई के होते हैं, और क्योंकि हम अपने मशीन कोड का निर्माण करते समय एक चर की लंबाई को एक साथ जोड़ते हैं, इसलिए किसी भी पते की गणना और निर्माण में शामिल दो चरण प्रक्रिया होती है।

सबसे पहले हम स्मृति के आवंटन को जितना संभव कर सकते हैं उतना ही पहले हम जानते हैं कि प्रत्येक सेल में वास्तव में क्या होता है। हम बाइट्स, या शब्दों, या जो भी निर्देश और अक्षर और किसी भी डेटा बनाते हैं, को समझते हैं। हम बस स्मृति आवंटित करना शुरू करते हैं और उन मानों का निर्माण करते हैं जो प्रोग्राम बनाते हैं, जैसे ही हम जाते हैं, और किसी भी जगह पर ध्यान दें कि हमें वापस जाने और पते को ठीक करने की आवश्यकता है। उस स्थान पर हम स्थान को पैड करने के लिए एक डमी डालते हैं ताकि हम स्मृति आकार की गणना जारी रख सकें। उदाहरण के लिए हमारा पहला मशीन कोड एक सेल ले सकता है। अगला मशीन कोड 3 कक्ष ले सकता है, जिसमें एक मशीन कोड सेल और दो पता कक्ष शामिल होते हैं। अब हमारा पता सूचक 4 है। हम जानते हैं कि मशीन सेल में क्या होता है, जो ओप कोड है, लेकिन हमें पता लगाने के लिए इंतजार करना है कि पता कोशिकाओं में क्या होता है जब तक हम नहीं जानते कि वह डेटा कहां स्थित होगा, यानी क्या होगा उस डेटा का मशीन पता।

अगर वहां केवल एक स्रोत फ़ाइल थी तो एक कंपाइलर सैद्धांतिक रूप से एक लिंकर के बिना पूरी तरह निष्पादन योग्य मशीन कोड का उत्पादन कर सकता था। दो पास प्रक्रिया में यह किसी भी मशीन लोड या स्टोर निर्देशों द्वारा संदर्भित सभी डेटा कक्षों के सभी वास्तविक पतों की गणना कर सकता है। और यह किसी भी पूर्ण कूद निर्देशों द्वारा संदर्भित सभी पूर्ण पते की गणना कर सकता है। यह कितना सरल कंपाइलर है, फोर्ट वर्क में से एक की तरह, कोई लिंकर नहीं है।

एक लिंकर ऐसा कुछ है जो कोड के ब्लॉक को अलग से संकलित करने की अनुमति देता है। यह बिल्डिंग कोड की समग्र प्रक्रिया को तेज कर सकता है, और ब्लॉकों को बाद में उपयोग किए जाने के साथ कुछ लचीलापन की अनुमति देता है, दूसरे शब्दों में उन्हें स्मृति में स्थानांतरित किया जा सकता है, उदाहरण के लिए 1000 पता कक्षों द्वारा ब्लॉक को स्कूटर करने के लिए प्रत्येक पते में 1000 जोड़ना।

तो कंपाइलर आउटपुट एक मोटा मशीन कोड है जो अभी तक पूरी तरह से निर्मित नहीं है, लेकिन इसे बाहर रखा गया है, इसलिए हम सब कुछ के आकार को जानते हैं, दूसरे शब्दों में, ताकि हम गणना कर सकें कि सभी पूर्ण पते कहां स्थित होंगे। कंपाइलर प्रतीकों की एक सूची भी आउटपुट करता है जो नाम / पता जोड़े हैं। प्रतीकों को मॉड्यूल में मशीन कोड में एक मेमोरी ऑफसेट से संबंधित नाम से संबंधित है। ऑफ़सेट मॉड्यूल में प्रतीक के स्मृति स्थान की पूर्ण दूरी है।

यही वह जगह है जहां हम लिंकर जाते हैं। लिंकर पहले मशीन कोड के इन सभी ब्लॉक को एक साथ समाप्त कर देता है और अंत में नोट करता है कि प्रत्येक व्यक्ति कहां से शुरू होता है। फिर यह मॉड्यूल के भीतर रिश्तेदार ऑफसेट और बड़े लेआउट में मॉड्यूल की पूर्ण स्थिति को जोड़कर तय किए जाने वाले पते की गणना करता है।

जाहिर है, मैंने इसे अधिक बढ़ा दिया है ताकि आप इसे समझने की कोशिश कर सकें, और मैंने जानबूझकर ऑब्जेक्ट फाइलों, प्रतीक तालिकाओं आदि के शब्दकोष का उपयोग नहीं किया है जो मेरे लिए भ्रम का हिस्सा है।



सी ++ कार्यक्रम के संकलन में तीन कदम शामिल हैं:

  1. प्रीप्रोकैसिंग: प्रीप्रोसेसर एक सी ++ स्रोत कोड फ़ाइल लेता है और #include , #define और अन्य प्रीप्रोसेसर निर्देशों से संबंधित है। इस चरण का आउटपुट पूर्व-प्रोसेसर निर्देशों के बिना "शुद्ध" सी ++ फ़ाइल है।

  2. संकलन: संकलक प्री-प्रोसेसर का आउटपुट लेता है और इससे ऑब्जेक्ट फ़ाइल उत्पन्न करता है।

  3. लिंकिंग: लिंकर कंपाइलर द्वारा उत्पादित ऑब्जेक्ट फ़ाइलों को लेता है और या तो लाइब्रेरी या निष्पादन योग्य फ़ाइल बनाता है।

preprocessing

प्रीप्रोसेसर प्रीप्रोसेसर निर्देशों को संभालता है, जैसे #include और #define । यह सी ++ के सिंटैक्स का अज्ञेयवादी है, यही कारण है कि इसे देखभाल के साथ इस्तेमाल किया जाना चाहिए।

यह मैक्रोज़ ( #define ) के प्रतिस्थापन, और #if आधार पर टेक्स्ट के विभिन्न हिस्सों का चयन करते हुए, संबंधित फाइलों (जो आमतौर पर केवल घोषणाएं) की सामग्री के साथ #include अंतर्निहित निर्देशों को प्रतिस्थापित करके एक सी ++ स्रोत फ़ाइल पर काम करता है, #ifndef और #ifndef निर्देश।

प्रीप्रोसेसर प्रीप्रोकैसिंग टोकन की धारा पर काम करता है। मैक्रो प्रतिस्थापन को टोकन को अन्य टोकन के साथ बदलने के रूप में परिभाषित किया जाता है (ऑपरेटर ## समझ में आता है तो दो टोकन विलय करने में सक्षम बनाता है)।

इसके बाद, प्रीप्रोसेसर एक एकल आउटपुट उत्पन्न करता है जो उपरोक्त वर्णित परिवर्तनों के परिणामस्वरूप टोकन की एक धारा है। यह कुछ विशेष मार्कर भी जोड़ता है जो संकलक को बताते हैं कि प्रत्येक पंक्ति कहाँ से आई है ताकि यह समझदार त्रुटि संदेशों को उत्पन्न करने के लिए उपयोग कर सके।

#if और #error निर्देशों के चालाक उपयोग के साथ इस चरण में कुछ त्रुटियां उत्पन्न की जा सकती हैं।

संकलन

प्रीप्रोसेसर के प्रत्येक आउटपुट पर संकलन चरण किया जाता है। कंपाइलर शुद्ध सी ++ स्रोत कोड (अब बिना किसी प्रीप्रोसेसर निर्देशों के) का विश्लेषण करता है और इसे असेंबली कोड में परिवर्तित करता है। फिर अंतर्निहित बैक-एंड (टूलचैन में असेंबलर) का आह्वान करता है जो उस कोड को मशीन कोड में कुछ प्रारूप (ईएलएफ, सीओएफएफ, ए.ओउट, ...) में वास्तविक बाइनरी फ़ाइल का उत्पादन करता है। इस ऑब्जेक्ट फ़ाइल में इनपुट में परिभाषित प्रतीकों के संकलित कोड (बाइनरी रूप में) शामिल है। ऑब्जेक्ट फ़ाइलों में प्रतीकों को नाम से संदर्भित किया जाता है।

ऑब्जेक्ट फ़ाइलें उन प्रतीकों को संदर्भित कर सकती हैं जिन्हें परिभाषित नहीं किया गया है। यह तब होता है जब आप घोषणा का उपयोग करते हैं, और इसके लिए परिभाषा प्रदान नहीं करते हैं। कंपाइलर इस पर ध्यान नहीं देता है, और जब तक स्रोत कोड अच्छी तरह से गठित होता है तब तक खुशी से ऑब्जेक्ट फ़ाइल का उत्पादन करेगा।

कंपाइलर्स आमतौर पर आपको इस बिंदु पर संकलन को रोकने देते हैं। यह बहुत उपयोगी है क्योंकि इसके साथ आप प्रत्येक स्रोत कोड फ़ाइल को अलग से संकलित कर सकते हैं। यह लाभ यह है कि यदि आप केवल एक फ़ाइल को बदलते हैं तो आपको सबकुछ फिर से सम्मिलित करने की आवश्यकता नहीं है।

उत्पादित ऑब्जेक्ट फ़ाइलों को बाद में आसानी से पुन: उपयोग करने के लिए स्थिर पुस्तकालय नामक विशेष अभिलेखागार में रखा जा सकता है।

यह इस चरण में है कि "नियमित" कंपाइलर त्रुटियां, जैसे सिंटैक्स त्रुटियों या विफल ओवरलोड रिज़ॉल्यूशन त्रुटियों की सूचना दी जाती है।

लिंक करना

लिंकर उत्पादक फ़ाइलों द्वारा उत्पादित अंतिम संकलन आउटपुट का उत्पादन करता है। यह आउटपुट या तो साझा (या गतिशील) लाइब्रेरी हो सकता है (और जब नाम समान है, तो उन्हें पहले वर्णित स्थैतिक पुस्तकालयों के साथ बहुत आम नहीं मिला है) या निष्पादन योग्य।

यह सही ऑब्जेक्ट्स के साथ अनिर्धारित प्रतीकों के संदर्भों को प्रतिस्थापित करके सभी ऑब्जेक्ट फ़ाइलों को लिंक करता है। इन प्रतीकों में से प्रत्येक को अन्य ऑब्जेक्ट फ़ाइलों या पुस्तकालयों में परिभाषित किया जा सकता है। यदि उन्हें मानक पुस्तकालय के अलावा पुस्तकालयों में परिभाषित किया गया है, तो आपको उनके बारे में लिंकर बताना होगा।

इस स्तर पर सबसे आम त्रुटियों में परिभाषाएं या डुप्लिकेट परिभाषाएं अनुपलब्ध हैं। पूर्व का मतलब है कि या तो परिभाषाएं मौजूद नहीं हैं (यानी वे लिखी नहीं हैं), या ऑब्जेक्ट फाइल या पुस्तकालय जहां वे रहते हैं वे लिंकर को नहीं दिए गए थे। उत्तरार्द्ध स्पष्ट है: एक ही प्रतीक को दो अलग-अलग ऑब्जेक्ट फ़ाइलों या पुस्तकालयों में परिभाषित किया गया था।





c++-faq