2015-11-17 3 views
8

निम्नलिखित C++ स्रोत कोड पर विचार करें की ओर जाता है। 4 और क्लैंग-3.6.0 उबंटू 14.04 के तहत। अजीब व्यवहार यह है कि एक स्थिर आवंटित सरणी _end के अंत में, _end अंक इसकी शुरुआत में नहीं है। यदि हम _end को end_ के साथ प्रतिस्थापित करते हैं, तो सब कुछ ठीक काम करता है।ग्राम में ++ प्रतीक '_end' का उपयोग करते हुए एक विभाजन गलती

$ g++ main.cpp -o main.s -O0 -S 
$ g++ main2.cpp -o main2.s -O0 -S 
$ diff main.s main2.s 
1,2c1,2 
< .file "main.cpp" 
< .globl _end 
--- 
> .file "main2.cpp" 
> .globl end_ 
5,7c5,7 
< .type _end, @object 
< .size _end, 4200 
< _end: 
--- 
> .type end_, @object 
> .size end_, 4200 
> end_: 
25c25 
< movl $0, _end(,%rax,4) 
--- 
> movl $0, end_(,%rax,4) 
:

इसके अलावा, अगर हम एस कमांड लाइन तर्क प्रदान करके एक विधानसभा कोड उत्पादन के लिए जीसीसी से पूछते हैं, वहाँ "_end" और किसी भी अन्य सरणी नाम के साथ संस्करण के साथ संस्करण के बीच कोई महत्वपूर्ण अंतर हो जाएगा

$ g++ main.cpp -o main -O0 
$ g++ main2.cpp -o main2 -O0 
$ objdump -d main >main.dump 
$ objdump -d main2 > main2.dump 
$ diff main.dump main2.dump 
2c2 
< main:  формат файла elf64-x86-64 // "File format" in Russian 
--- 
> main2:  формат файла elf64-x86-64 
123c123 
< 4004ff: c7 04 85 c8 20 60 00 movl $0x0,0x6020c8(,%rax,4) 
--- 
> 4004ff: c7 04 85 60 10 60 00 movl $0x0,0x601060(,%rax,4) 

जहां तक:

लेकिन अगर हम objdump का उपयोग निष्पादनयोग्य डंप और उनके खिलाफ diff चलाने के लिए, हम _end संस्करण का उपयोग किया पते में देखेंगे की जरूरत की तुलना में 4200 = 4 * 1050 बाइट्स आगे है मुझे पता है, जीसीसी कंपाइलर variab का इलाज कर सकते हैं लेस अंडरस्कोर से शुरू होता है जैसा कि यह चाहता है, i। ई। यह आपके कोड में ऐसे प्रतीकों का उपयोग करने का एक बुरा अभ्यास है। लेकिन मेरा सवाल है: वास्तव में यहां क्या होता है? क्यों _end आवंटित सरणी के अंत के पते के साथ प्रतिस्थापित किया गया है? यदि हम "-S" कमांड लाइन तर्क का उपयोग करते हैं तो कोई फर्क नहीं पड़ता है, लेकिन वास्तव में निर्मित बाइनरी में कोई अंतर है? इस मामले में जीसीसी और क्लैंग समान रूप से व्यवहार नहीं करते हैं, यह मेरे लिए भी अजीब है।

उत्तर

2

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

नोट: _etext, _edata, और _end: कुछ सिस्टम पर इन प्रतीकों के नाम अंडरस्कोर, इस प्रकार से पहले कर रहे हैं।

स्रोत: http://man7.org/linux/man-pages/man3/end.3.html

+0

बिल्कुल मुझे जो चाहिए, धन्यवाद! लेकिन क्यों "-S" कमांड लाइन तर्क इस कोड को संकलित करते समय संदिग्ध कुछ भी नहीं दिखाता है? –

+0

@MaximAkhmedov शायद ऐसा इसलिए है क्योंकि '_end' किसी भी अन्य पॉइंटर्स के रूप में एक सूचक है, और जब आप अपनी सरणी को असाइन करते हैं तो पॉइंटर अंकगणित किया जाता है। – vsoftco

0

C99 N1256 standard draft 7.1.3 "सुरक्षित पहचानकर्ता" कहते हैं:

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

तो हम है कि पता होना चाहिए:

  • फ़ाइल गुंजाइश वैश्विक के लिए (अन्य समारोह और गुंजाइश को ब्लॉक कर रहे हैं)
  • साधारण नाम अंतरिक्ष चर

तो C99 के अनुसार शामिल आप पहचानकर्ता _end का उपयोग नहीं कर सकते हैं।

आप कार्यान्वयन

अब देखने के लिए कारण है कि यह वास्तव में अपने कार्यान्वयन पर विफल रहता है, उपयोग करें:

g++ -Wl,--verbose main.c 

इस्तेमाल किया लिंकर स्क्रिप्ट को देखने के लिए।

उबंटू 15.10 पर, यह डेटा अनुभाग के अंत में प्रतीक _end परिभाषित करता है:

_end = .; PROVIDE (end = .); 
. = DATA_SEGMENT_END (.); 

तो यह कोई आश्चर्य नहीं है कि स्मृति का उपयोग जिस तरह से यह से आगे SEGFAULT सकता है।

संबंधित मुद्दे