GCC -fPIC विकल्प


437

मैंने कोड जनरेशन कन्वेंशनों के लिए जीसीसी के विकल्पों के बारे में पढ़ा है , लेकिन समझ नहीं सका कि "स्थिति-स्वतंत्र कोड (पीआईसीआर) क्या करता है"। कृपया मुझे यह समझाने के लिए एक उदाहरण दें कि इसका क्या मतलब है।


25
क्लैंग भी -fPIC का उपयोग करता है।
चला गया

जवाबों:


526

पोजीशन इंडिपेंडेंट कोड का अर्थ है कि उत्पन्न मशीन कोड काम करने के लिए किसी विशिष्ट पते पर स्थित होने पर निर्भर नहीं है।

एग जंप्स को पूर्ण के बजाय सापेक्ष के रूप में उत्पन्न किया जाएगा।

छद्म विधानसभा:

तस्वीर: यह काम करेगा कि क्या कोड पता 100 या 1000 पर था

100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL CURRENT+10
...
111: NOP

नॉन-पीआईसी: यह तभी काम करेगा जब कोड 100 पते पर होगा

100: COMPARE REG1, REG2
101: JUMP_IF_EQUAL 111
...
111: NOP

EDIT: टिप्पणी के जवाब में।

यदि आपका कोड -fPIC के साथ संकलित है, तो यह एक पुस्तकालय में शामिल करने के लिए उपयुक्त है - पुस्तकालय को अपने पसंदीदा स्थान से दूसरे पते पर मेमोरी में स्थानांतरित करने में सक्षम होना चाहिए, आपके पुस्तकालय के पते पर पहले से ही भरी हुई लाइब्रेरी हो सकती है।


36
यह उदाहरण स्पष्ट है, लेकिन एक उपयोगकर्ता के रूप में अगर मैं बिना विकल्प के एक साझा लैब्ररी (.so) फ़ाइल बनाता हूं तो क्या अंतर होगा? क्या कुछ ऐसे मामले हैं, जिनके बिना-मेरा दायित्व अवैध होगा?
नारेक

16
हाँ, साझा लाइब्रेरी का निर्माण जो PIC नहीं है, एक त्रुटि हो सकती है।
जॉन Zwinck

92
अधिक विशिष्ट होने के लिए, साझा लाइब्रेरी को प्रक्रियाओं के बीच साझा किया जाना चाहिए, लेकिन दोनों में एक ही पते पर लाइब्रेरी को लोड करना हमेशा संभव नहीं हो सकता है। यदि कोड स्वतंत्र नहीं था, तो प्रत्येक प्रक्रिया को अपनी प्रति की आवश्यकता होगी।
साइमन रिक्टर

19
@ नोट: त्रुटि तब होती है जब एक प्रक्रिया एक ही वर्चुअल पते पर एक से अधिक साझा लाइब्रेरी लोड करना चाहती है। चूंकि पुस्तकालय यह अनुमान नहीं लगा सकते हैं कि अन्य पुस्तकालयों को क्या लोड किया जा सकता है, यह समस्या पारंपरिक साझा पुस्तकालय अवधारणा के साथ अपरिहार्य है। वर्चुअल एड्रेस स्पेस यहां मदद नहीं करता है।
फिलिप

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

61

मैं समझाने की कोशिश करूँगा कि पहले से ही सरल तरीके से क्या कहा गया है।

जब भी एक साझा लिबर लोड किया जाता है, तो लोडर (ओएस पर कोड जो आपके द्वारा चलाए जा रहे किसी भी प्रोग्राम को लोड करता है) उस कोड के कुछ पते को बदल देता है जहां ऑब्जेक्ट को लोड किया गया था।

उपरोक्त उदाहरण में, गैर-PIC कोड में "111" को लोडर द्वारा पहली बार लोड किया गया था।

साझा नहीं की गई वस्तुओं के लिए, आप यह चाहते हैं कि ऐसा हो क्योंकि संकलक उस कोड पर कुछ अनुकूलन कर सकते हैं।

साझा किए गए ऑब्जेक्ट के लिए, यदि कोई अन्य प्रक्रिया उस कोड को "लिंक" करना चाहेगी तो उसे उसी वर्चुअल पते पर पढ़ना होगा या "111" से कोई मतलब नहीं होगा। लेकिन वह वर्चुअल-स्पेस पहले से ही दूसरी प्रक्रिया में उपयोग में हो सकता है।


Whenever a shared lib is loaded, the loader changes some addresses in the code depending on where the object was loaded to.मुझे लगता है कि यह सही नहीं है अगर -fpic के साथ संकलित किया गया है और कारण -fpic मौजूद है अर्थात प्रदर्शन कारणों से या क्योंकि आपके पास एक लोडर है जो स्थानांतरित करने में सक्षम नहीं है या क्योंकि आपको विभिन्न स्थानों में या कई और कारणों से कई प्रतियों की आवश्यकता है।
robsn

हमेशा -fpic का उपयोग क्यों न करें?
Jay

1
@ जय - क्योंकि यह प्रत्येक फ़ंक्शन कॉल के लिए एक और गणना (फ़ंक्शन पता) की आवश्यकता होगी। इसलिए प्रदर्शन के लिहाज से, अगर जरूरत नहीं है तो बेहतर है कि इसका इस्तेमाल न करें।
Roee Gavirel

45

साझा पुस्तकालयों में बनाया गया कोड आम तौर पर स्थिति-स्वतंत्र कोड होना चाहिए, ताकि साझा पुस्तकालय को मेमोरी में किसी भी पते पर आसानी से लोड किया जा सके। -fPICविकल्प सुनिश्चित करता है कि जीसीसी इस तरह के कोड पैदा करता है।


एक साझा लाइब्रेरी को -fPICध्वज पर ध्वज के बिना किसी भी पते पर लोड क्यों नहीं किया जाएगा ? क्या यह कार्यक्रम से जुड़ा नहीं है? जब प्रोग्राम चल रहा होता है, ऑपरेटिंग सिस्टम इसे मेमोरी में अपलोड करता है। क्या मैं कुछ भूल रहा हूँ?
टोनी टैनस

1
क्या -fPICयह सुनिश्चित करने के लिए कि इस लिंक को जोड़ने वाली प्रक्रिया में किसी भी आभासी पते पर लोड किया जा सकता है, यह सुनिश्चित करने के लिए ध्वज का उपयोग किया गया है? 5 मिनट की दोहरी टिप्पणियों के लिए खेद है कि पिछले एक को संपादित नहीं किया जा सकता है।
टोनी टैनस

1
साझा पुस्तकालय (बनाने libwotnot.so) और इसके साथ जोड़ने ( -lwotnot) के बीच भेद । लिंक करते समय, आपको उपद्रव करने की आवश्यकता नहीं है -fPIC। यह मामला हुआ करता था कि साझा पुस्तकालय का निर्माण करते समय, आपको यह सुनिश्चित करने की आवश्यकता थी कि -fPICसभी ऑब्जेक्ट फ़ाइलों को साझा पुस्तकालय में बनाया जाए। इन दिनों नियमों में बदलाव हो सकता है क्योंकि इन दिनों डिफ़ॉल्ट रूप से PIC कोड के साथ कंपाइलर का निर्माण होता है। तो, 20 साल पहले क्या महत्वपूर्ण था, और 7 साल पहले महत्वपूर्ण हो सकता है, इन दिनों कम महत्वपूर्ण है, मुझे विश्वास है। ओ / एस कर्नेल के बाहर के पते 'हमेशा' वर्चुअल पते होते हैं।
जोनाथन लेफलर

तो पहले आपको जोड़ना था -fPIC। इस ध्वज को पास किए बिना, .so का निर्माण करते समय उत्पन्न कोड विशिष्ट वर्चुअल पते पर लोड किए जाने की आवश्यकता है जो उपयोग में हो सकते हैं?
टोनी टैनस

1
हां, क्योंकि यदि आपने PIC ध्वज का उपयोग नहीं किया है, तो कोड मज़बूती से भरोसेमंद नहीं था। ASLR (एड्रेस स्पेस लेआउट रैंडमाइजेशन) जैसी चीजें संभव नहीं हैं यदि कोड PIC नहीं है (या, कम से कम, यह हासिल करना इतना कठिन है कि वे प्रभावी रूप से असंभव हैं)।
जोनाथन लेफ्लर

21

आगे जोड़ना ...

हर प्रक्रिया में एक ही वर्चुअल एड्रेस स्पेस होता है (यदि linux OS में फ्लैग का उपयोग करके वर्चुअल एड्रेस का रेंडमाइजेशन रोक दिया जाता है) (अधिक जानकारी के लिए एड्रेस स्पेस लेआउट को फिर से इनेबल और री-इनेबल करें अपने लिए )

इसलिए अगर इसका कोई साझा लिंकिंग (हाइपोटेटिकल परिदृश्य) है, तो हम बिना किसी नुकसान के एक ही समान निर्देश के लिए एक ही वर्चुअल एड्रेस दे सकते हैं।

लेकिन जब हम साझा किए गए ऑब्जेक्ट को exe से लिंक करना चाहते हैं, तो हम साझा किए गए ऑब्जेक्ट को दिए गए प्रारंभ पते के बारे में निश्चित नहीं हैं क्योंकि यह साझा ऑब्जेक्ट से जुड़े हुए आदेश पर निर्भर करेगा। कहा जा रहा है, अंदर का निर्देश हमेशा रहेगा। इसके लिंकिंग की प्रक्रिया के आधार पर अलग-अलग वर्चुअल एड्रेस।

तो एक प्रक्रिया अपने स्वयं के वर्चुअल स्पेस में 0x45678910 के रूप में .so को एड्रेस एड्रेस दे सकती है और उसी समय अन्य प्रक्रिया 0x12131415 का स्टार्ट एड्रेस दे सकती है और यदि वे सापेक्ष एड्रेसिंग का उपयोग नहीं करते हैं, तो .so बिल्कुल भी काम नहीं करेगा।

इसलिए उन्हें हमेशा सापेक्ष एड्रेसिंग मोड और इसलिए fpic विकल्प का उपयोग करना पड़ता है।


1
आभासी Addr स्पष्टीकरण के लिए धन्यवाद।
हॉट। पीएक्सएल

2
क्या कोई समझा सकता है कि यह एक स्थैतिक पुस्तकालय के साथ कोई समस्या नहीं है, आपको स्थैतिक पुस्तकालय पर -fPIC का उपयोग करने की आवश्यकता क्यों नहीं है? मैं समझता हूं कि लिंकिंग संकलन समय में किया जाता है (या वास्तव में सही होने के बाद), लेकिन यदि आपके पास स्थिति निर्भर कोड के साथ 2 स्थिर पुस्तकालय हैं, तो वे कैसे लिंक होने जा रहे हैं?
माइकल पी।

3
@MichaelP ऑब्जेक्ट फ़ाइल में स्थिति परिलक्षित लेबल की एक तालिका होती है और जब विशेष ओब्जेक्ट फ़ाइल को लिंक किया जाता है तो सभी लेबल तदनुसार अपडेट किए जाते हैं। यह साझा पुस्तकालय के लिए नहीं किया जा सकता है।
स्लाव

16

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


इस समस्या से निपटने के लिए आमतौर पर इस्तेमाल की जाने वाली दो विधियाँ हैं:

1.Relocation। कोड में सभी संकेत और पते को संशोधित किया जाता है, यदि आवश्यक हो, तो वास्तविक लोड पते को फिट करने के लिए। स्थानांतरण लिंकर और लोडर द्वारा किया जाता है।

2.Position- स्वतंत्र कोड। कोड के सभी पते वर्तमान स्थिति के सापेक्ष हैं। यूनिक्स जैसे सिस्टम में साझा की गई वस्तुएं डिफ़ॉल्ट रूप से स्थिति-स्वतंत्र कोड का उपयोग करती हैं। यह स्थानांतरण से कम कुशल है यदि प्रोग्राम लंबे समय तक चलता है, खासकर 32-बिट मोड में।


नाम " स्थिति-स्वतंत्र कोड " वास्तव में निम्नलिखित का अर्थ है:

  • कोड अनुभाग में कोई पूर्ण पते नहीं होते हैं, जिन्हें स्थानांतरण की आवश्यकता होती है, लेकिन केवल स्वयं सापेक्ष पते। इसलिए, कोड अनुभाग एक अनियंत्रित मेमोरी पते पर लोड किया जा सकता है और कई प्रक्रियाओं के बीच साझा किया जा सकता है।

  • डेटा अनुभाग कई प्रक्रियाओं के बीच साझा नहीं किया जाता है क्योंकि इसमें अक्सर लेखन योग्य डेटा होता है। इसलिए, डेटा सेक्शन में ऐसे पॉइंटर्स या एड्रेस हो सकते हैं, जिन्हें रिलोकेशन की ज़रूरत होती है।

  • सभी सार्वजनिक कार्यों और सार्वजनिक डेटा को लिनक्स में ओवरराइड किया जा सकता है। यदि मुख्य निष्पादन योग्य किसी फ़ंक्शन का साझा ऑब्जेक्ट में फ़ंक्शन के समान नाम है, तो मुख्य में संस्करण पूर्ववर्तीता लेगा, न केवल जब मुख्य से बुलाया जाता है, बल्कि साझा ऑब्जेक्ट से भी कॉल किया जाता है। इसी तरह, जब मुख्य में एक वैश्विक चर साझा वस्तु में एक वैश्विक चर के रूप में एक ही नाम है, तो मुख्य में उदाहरण का उपयोग किया जाएगा, यहां तक ​​कि जब साझा वस्तु से एक्सेस किया जाएगा।


यह तथाकथित प्रतीक अंतर्क्रिया स्थैतिक पुस्तकालयों के व्यवहार की नकल करना है।

एक साझा ऑब्जेक्ट में अपने फ़ंक्शन के लिए पॉइंटर्स की एक तालिका होती है, जिसे प्रक्रिया लिंक टेबल (PLT) कहा जाता है और इस "ओवरराइड" सुविधा को लागू करने के लिए ग्लोबल ऑफ़सेट टेबल (GOT) नामक इसके चर के लिए पॉइंटर्स की एक तालिका। फ़ंक्शंस और सार्वजनिक चर तक सभी पहुंच इस तालिका के माध्यम से जाती हैं।

ps जहाँ डायनेमिक लिंकिंग से बचा नहीं जा सकता है, वहाँ स्थिति-स्वतंत्र कोड की टाइमकोन्सुमिंग सुविधाओं से बचने के विभिन्न तरीके हैं।

आप इस लेख से और अधिक पढ़ सकते हैं: http://www.agner.org/optimize/optimizing_cpp.pdf


9

पहले से ही पोस्ट किए गए उत्तरों के लिए एक मामूली जोड़: ऑब्जेक्ट फ़ाइलें जो स्वतंत्र होने के लिए संकलित नहीं हैं, वे स्थानांतरित करने योग्य हैं; उनमें स्थानांतरण तालिका प्रविष्टियाँ हैं।

ये प्रविष्टियाँ लोडर (उस कोड को थोड़ा मेमोरी में लोड करती है) को वर्चुअल एड्रेस स्पेस में वास्तविक लोड पते के लिए समायोजित करने के लिए पूर्ण पते को फिर से लिखने की अनुमति देती है।

एक ऑपरेटिंग सिस्टम सभी साझा कार्यक्रमों के साथ मेमोरी में भरी हुई "साझा ऑब्जेक्ट लाइब्रेरी" की एक प्रतिलिपि को उसी साझा लाइब्रेरी लाइब्रेरी से जोड़ने का प्रयास करेगा।

चूंकि कोड एड्रेस स्पेस (डेटा स्पेस के वर्गों के विपरीत) को सन्निहित होने की आवश्यकता नहीं है, और क्योंकि अधिकांश प्रोग्राम जो किसी विशिष्ट लाइब्रेरी से लिंक होते हैं, उनमें काफी हद तक निश्चित लाइब्रेरी डिपेंडेंसी ट्री होता है, यह ज्यादातर समय सफल होता है। उन दुर्लभ मामलों में जहां विसंगति है, हां, एक साझा ऑब्जेक्ट लाइब्रेरी की दो या अधिक प्रतियां स्मृति में होना आवश्यक है।

जाहिर है, कार्यक्रमों और / या कार्यक्रम के उदाहरणों के बीच एक पुस्तकालय के लोड पते को यादृच्छिक करने के लिए कोई भी प्रयास (ताकि एक शोषक पैटर्न बनाने की संभावना को कम करने के लिए) ऐसे मामलों को सामान्य बना देगा, दुर्लभ नहीं, इसलिए जहां एक प्रणाली ने इस क्षमता को सक्षम किया है, सभी को सभी साझा वस्तु पुस्तकालयों को स्वतंत्र करने के लिए संकलित करने का हरसंभव प्रयास करना चाहिए।

चूंकि मुख्य कार्यक्रम के शरीर से इन पुस्तकालयों में कॉल को भी स्थानांतरित किया जाएगा, इससे यह बहुत कम संभावना है कि एक साझा पुस्तकालय को कॉपी करना होगा।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.