Ubuntu 15.10, कर्नेल 4.2.0, x86-64, GCC 5.2.1 उदाहरण
पर्याप्त मानकों, चलो एक कार्यान्वयन को देखो :-)
स्थानीय चर
मानक: अपरिभाषित व्यवहार।
कार्यान्वयन: कार्यक्रम स्टैक स्थान आवंटित करता है, और कभी भी उस पते पर कुछ भी नहीं ले जाता है, इसलिए जो कुछ भी पहले था उसका उपयोग किया जाता है।
#include <stdio.h>
int main() {
int i;
printf("%d\n", i);
}
संकलन:
gcc -O0 -std=c99 a.c
आउटपुट:
0
और साथ विघटित:
objdump -dr a.out
सेवा:
0000000000400536 <main>:
400536: 55 push %rbp
400537: 48 89 e5 mov %rsp,%rbp
40053a: 48 83 ec 10 sub $0x10,%rsp
40053e: 8b 45 fc mov -0x4(%rbp),%eax
400541: 89 c6 mov %eax,%esi
400543: bf e4 05 40 00 mov $0x4005e4,%edi
400548: b8 00 00 00 00 mov $0x0,%eax
40054d: e8 be fe ff ff callq 400410 <printf@plt>
400552: b8 00 00 00 00 mov $0x0,%eax
400557: c9 leaveq
400558: c3 retq
X86-64 कॉलिंग कन्वेंशन के हमारे ज्ञान से:
%rdi
पहला प्रिंटफ तर्क है, इस प्रकार "%d\n"
पते पर स्ट्रिंग0x4005e4
%rsi
इस प्रकार दूसरा प्रिंटफ तर्क है i
।
यह आता है -0x4(%rbp)
, जो पहले 4-बाइट स्थानीय चर है।
इस बिंदु पर, rbp
स्टैक के पहले पृष्ठ में कर्नेल द्वारा आवंटित किया गया है, इसलिए उस मूल्य को समझने के लिए हमें कर्नेल कोड को देखना होगा और यह पता लगाना होगा कि यह किसके लिए सेट है।
TODO क्या कर्नेल उस मेमोरी को किसी अन्य प्रक्रिया के लिए पुन: उपयोग करने से पहले सेट करता है जब कोई प्रक्रिया मर जाती है? यदि नहीं, तो नई प्रक्रिया डेटा को लीक करते हुए अन्य तैयार कार्यक्रमों की मेमोरी को पढ़ने में सक्षम होगी। देखें: क्या अनैतिक रूप से मूल्य कभी सुरक्षा जोखिम हैं?
फिर हम अपने स्वयं के स्टैक संशोधनों के साथ भी खेल सकते हैं और मजेदार चीजें लिख सकते हैं जैसे:
#include <assert.h>
int f() {
int i = 13;
return i;
}
int g() {
int i;
return i;
}
int main() {
f();
assert(g() == 13);
}
में स्थानीय चर -O3
पर कार्यान्वयन विश्लेषण: क्या करता है <मूल्य बाहर अनुकूलित> जीडीबी में मतलब है?
सार्वत्रिक चर
मानक: 0
कार्यान्वयन: .bss
अनुभाग।
#include <stdio.h>
int i;
int main() {
printf("%d\n", i);
}
gcc -00 -std=c99 a.c
के लिए संकलन:
0000000000400536 <main>:
400536: 55 push %rbp
400537: 48 89 e5 mov %rsp,%rbp
40053a: 8b 05 04 0b 20 00 mov 0x200b04(%rip),%eax # 601044 <i>
400540: 89 c6 mov %eax,%esi
400542: bf e4 05 40 00 mov $0x4005e4,%edi
400547: b8 00 00 00 00 mov $0x0,%eax
40054c: e8 bf fe ff ff callq 400410 <printf@plt>
400551: b8 00 00 00 00 mov $0x0,%eax
400556: 5d pop %rbp
400557: c3 retq
400558: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
40055f: 00
# 601044 <i>
कहते हैं कि i
पते पर है 0x601044
और:
readelf -SW a.out
शामिल हैं:
[25] .bss NOBITS 0000000000601040 001040 000008 00 WA 0 0 4
जो कहता 0x601044
है कि .bss
अनुभाग के मध्य में सही है , जो शुरू होता है 0x601040
और 8 बाइट लंबा होता है।
ELF मानक तो गारंटी देता है कि नामित अनुभाग .bss
पूरी तरह से शून्य की से भर जाता है:
.bss
यह खंड अनइंस्टाल्यूटेड डेटा रखता है जो प्रोग्राम की मेमोरी इमेज में योगदान देता है। परिभाषा के अनुसार, प्रोग्राम शुरू होने पर सिस्टम शून्य के साथ डेटा को इनिशियलाइज़ करता है। अनुभाग, के रूप में अनुभाग इस प्रकार से संकेत दिया, कचौड़ी occu- कोई फ़ाइल अंतरिक्ष SHT_NOBITS
।
इसके अलावा, प्रकार SHT_NOBITS
कुशल है और निष्पादन योग्य फ़ाइल पर कोई स्थान नहीं लेता है:
sh_size
यह सदस्य बाइट में अनुभाग का आकार देता है। जब तक sec- tion टाइप नहीं होता है SHT_NOBITS
, तब तक sh_size
फाइल में सेक्शन बाइट्स पर रहता है। प्रकार के एक खंड में SHT_NOBITS
एक गैर-शून्य आकार हो सकता है, लेकिन यह फ़ाइल में कोई स्थान नहीं रखता है।
फिर यह लिनक्स कर्नेल पर निर्भर है कि प्रोग्राम को लोड करने पर उस मेमोरी क्षेत्र को शून्य कर दिया जाए जब यह शुरू हो जाता है।