bar
के बाद से काफी बड़ी संकलक स्टैक पर बजाय स्थिर आवंटन स्वत: आवंटन उत्पन्न करता है। स्टेटिक सरणी .comm
असेंबली निर्देश के साथ बनाई गई हैं जो तथाकथित COMMON अनुभाग में आवंटन करती है। उस खंड से प्रतीकों को इकट्ठा किया जाता है, समान नाम वाले प्रतीकों को विलय कर दिया जाता है (अनुरोध किए गए सबसे बड़े आकार के बराबर आकार के साथ एक प्रतीक अनुरोध में कम किया जाता है) और फिर अधिकांश निष्पादन योग्य प्रारूपों में बीएसएस (अनियंत्रित डेटा) अनुभाग में शेष को मैप किया जाता है। ईएलएफ निष्पादन योग्य के साथ .bss
अनुभाग डेटा सेगमेंट में स्थित है, केवल ढेर के डेटा सेगमेंट हिस्से से पहले (अज्ञात मेमोरी मैपिंग्स द्वारा प्रबंधित एक और ढेर हिस्सा है जो डेटा सेगमेंट में नहीं रहता है)।
small
मेमोरी मॉडल 32-बिट एड्रेसिंग निर्देशों के साथ x86_64 पर प्रतीकों को संबोधित करने के लिए उपयोग किया जाता है। यह कोड को छोटा और तेज़ बनाता है। कुछ विधानसभा उत्पादन जब small
स्मृति मॉडल का उपयोग:
movl $bar.1535, %ebx <---- Instruction length saving
...
movl %eax, baz_+4(%rip) <---- Problem!!
...
.local bar.1535
.comm bar.1535,2575411200,32
...
.comm baz_,12,16
इस में (यह मान प्रतीक स्थान के पते के बराबर) bar.1535
प्रतीक का मूल्य डाल करने के लिए एक 32-बिट कदम अनुदेश (5 बाइट्स लंबे) का उपयोग करता है RBX
रजिस्टर के निचले 32 बिट्स (ऊपरी 32 बिट्स शून्य हो जाते हैं)। bar.1535
प्रतीक स्वयं को .comm
निर्देश का उपयोग करके आवंटित किया गया है। baz
के लिए मेमोरी बाद में COMMON ब्लॉक आवंटित किया गया है। क्योंकि bar.1535
बहुत बड़ा है, baz_
.bss
सेक्शन की शुरुआत से 2 जीबी से अधिक समाप्त होता है। यह RIP
से गैर-32 बिट (हस्ताक्षरित) ऑफ़सेट के बाद b
वैरिएबल को संबोधित करने के लिए उपयोग किया जाना चाहिए, जहां EAX
के मान को स्थानांतरित किया जाना चाहिए, दूसरे movl
निर्देश में समस्या उत्पन्न हुई है। यह केवल लिंक समय के दौरान पता चला है। असेंबलर को उचित ऑफसेट नहीं पता है क्योंकि यह नहीं जानता कि निर्देश सूचक (RIP
) का मूल्य क्या होगा (यह पूर्ण वर्चुअल एड्रेस पर निर्भर करता है जहां कोड लोड होता है और यह लिंकर द्वारा निर्धारित किया जाता है), इसलिए यह बस 0
का ऑफ़सेट रखता है और फिर R_X86_64_PC32
टाइप का स्थानांतरण स्थान बनाता है। यह वास्तविक ऑफसेट मान के साथ 0
के मान को पैच करने के लिए लिंकर को निर्देश देता है। लेकिन ऐसा नहीं हो सकता है क्योंकि ऑफसेट मान एक हस्ताक्षरित 32-बिट पूर्णांक के अंदर फिट नहीं होगा और इसलिए बाहर निकलता है।
जगह बातों में medium
स्मृति मॉडल के साथ इस तरह दिखेगा:
movabsq $bar.1535, %r10
...
movl %eax, baz_+4(%rip)
...
.local bar.1535
.largecomm bar.1535,2575411200,32
...
.comm baz_,12,16
पहले एक 64-बिट तत्काल कदम अनुदेश (10 बाइट्स लंबे) 64-बिट मूल्य जिनमें से पते का प्रतिनिधित्व डाल करने के लिए प्रयोग किया जाता है bar.1535
रजिस्टर R10
में। bar.1535
प्रतीक के लिए मेमोरी .largecomm
निर्देश का उपयोग करके आवंटित की जाती है और इस प्रकार यह ईएलएफ निष्कर्ष के .lbss
अनुभाग में समाप्त होता है। .lbss
दुकान प्रतीकों छोटे बातें .bss
करने के लिए जाना है, जबकि जो पहले 2 GiB में फिट हो सकता है नहीं (और इसलिए 32-बिट निर्देश या आरआईपी-रिश्तेदार को संबोधित का उपयोग कर संबोधित नहीं किया जाना चाहिए), करने के लिए प्रयोग किया जाता है (baz_
अभी भी .comm
और नहीं .largecomm
का उपयोग कर आवंटित किया जाता है)। चूंकि .lbss
अनुभाग ईएलएफ लिंकर स्क्रिप्ट में .bss
अनुभाग के बाद रखा गया है, baz_
32-बिट आरआईपी-संबंधित पते का उपयोग करके पहुंच से बाहर नहीं होगा।
सभी संबोधित मोड System V ABI: AMD64 Architecture Processor Supplement में वर्णित हैं। यह एक भारी तकनीकी पठन है लेकिन किसी को भी पढ़ना चाहिए जो वास्तव में समझना चाहता है कि 64-बिट कोड अधिकांश x86_64 यूनिक्स पर कैसे काम करता है।
movl $2575411200, %edi
...
call malloc
movq %rax, %rdi
यह मूलतः RDI = malloc(2575411200)
है:
जब एक ALLOCATABLE
सरणी के बजाय प्रयोग किया जाता है, gfortran
ढेर स्मृति (सबसे अधिक संभावना एक गुमनाम स्मृति नक्शा आवंटन के बड़े आकार को देखते हुए के रूप में लागू) आवंटित करता है। तो bar
के तत्वों पर से RDI
में संग्रहीत मूल्य से सकारात्मक ऑफसेट का उपयोग करके पहुंचा जा सकता है:
movl 51190040(%rdi), %eax
movl %eax, baz_+4(%rip)
स्थानों कि bar
की शुरुआत से अधिक से अधिक 2 GiB हैं, उनके लिए एक अधिक विस्तृत विधि का इस्तेमाल किया जाता है। जैसे कुछ भी नहीं के बाद से पता नहीं है जहां गतिशील आवंटन किया जाएगा के बारे में माना जाता है
; Some computations that leave the offset in RAX
movl (%rdi,%rax), %eax
movl %eax, baz_+4(%rip)
इस कोड को स्मृति मॉडल से प्रभावित नहीं है: b = bar(12,144*144*450)
gfortran
का उत्सर्जन करता है लागू करने के लिए। इसके अलावा, चूंकि सरणी पारित नहीं होती है, इसलिए कोई वर्णनकर्ता नहीं बनाया जा रहा है। यदि आप एक और फ़ंक्शन जोड़ते हैं जो मानी गई आकार वाली सरणी लेता है और bar
पास करता है, तो bar
के लिए एक वर्णक स्वचालित चर के रूप में बनाया गया है (यानी foo
के ढेर पर)। (
movl $bar.1580, %edi
...
; RAX still holds the address of the allocated memory as returned by malloc
; Computations, computations
movl -232(%rax,%rdx,4), %eax
movl %eax, baz_+4(%rip)
पहला कदम एक समारोह कॉल के तर्क को तैयार करता है मेरी नमूना मामले call boo(bar)
जहां boo
एक अंतरफलक है कि वाणी है में: सरणी SAVE
विशेषता के साथ स्थिर किया जाता है, वर्णनकर्ता .bss
खंड में रखा गया है यह एक अनुमानित आकार सरणी लेने के रूप में)। यह bar
के सरणी वर्णनकर्ता का पता EDI
में ले जाता है। यह एक 32-बिट तत्काल कदम है ताकि वर्णनकर्ता पहले 2 जीबीबी में होने की उम्मीद है। वास्तव में, यह दोनों small
और medium
स्मृति मॉडल इस तरह में .bss
में आवंटित किया जाता है:
.local bar.1580
.comm bar.1580,72,32
यह एक बहुत अच्छा स्पष्टीकरण है। धन्यवाद। यह मुझे इस सामान के एक समूह में बहुत गहराई से देखने के लिए एक अच्छी शुरुआत देता है (जो मैं चाहता था)। – mgilson
@mgilson, सिर्फ उत्तर की पूर्णता के लिए, मैंने यह भी बताया है कि क्या होता है जब वर्णनकर्ता द्वारा अन्य उप-सूटिन में 'बार' पास किया जाता है। –