1) संकलित बाइनरी को प्रॉम / फ्लैश हां के लिए लिखा गया है। USB, सीरियल, i2c, jtag, आदि डिवाइस पर निर्भर करता है, जो कि डिवाइस द्वारा समर्थित है, बूट प्रक्रिया को समझने के लिए irrelevent।
2) यह आमतौर पर एक माइक्रोकंट्रोलर के लिए सच नहीं है, प्राथमिक उपयोग के मामले में रोम / फ्लैश और रैम में डेटा के निर्देश हैं। चाहे आर्किटेक्चर कोई भी हो। एक गैर-माइक्रोकंट्रोलर के लिए, आपका पीसी, आपका लैपटॉप, आपका सर्वर, प्रोग्राम को गैर-वाष्पशील (डिस्क) से कॉपी किया जाता है, फिर राम वहां से चला जाता है। कुछ माइक्रोकंट्रोलर आपको राम के साथ-साथ, यहां तक कि कटाई का भी दावा करते हैं, भले ही यह परिभाषा का उल्लंघन करता दिखाई दे। हार्वर्ड के बारे में कुछ भी नहीं है जो आपको निर्देश के पक्ष में राम की मैपिंग करने से रोकता है, आपको बस शक्ति होने के बाद निर्देश प्राप्त करने के लिए एक तंत्र की आवश्यकता होती है (जो परिभाषा का उल्लंघन करता है, लेकिन हार्वर्ड सिस्टम को ऐसा करना होगा जो अन्य उपयोगी हो माइक्रोकंट्रोलर की तुलना में)।
3) की तरह।
प्रत्येक सीपीयू एक नियतात्मक में "बूट" करता है, जैसा कि डिजाइन किया गया है। सबसे आम तरीका एक वेक्टर टेबल है जहां पॉवर अप करने के बाद चलने वाले पहले निर्देशों के लिए पता रीसेट वेक्टर में हैं, एक पता जिसे हार्डवेयर पढ़ता है फिर उस पते का उपयोग चालू करना शुरू करता है। अन्य सामान्य तरीका यह है कि प्रोसेसर को बिना किसी जाने-पहचाने पते पर वेक्टर तालिका के बिना निष्पादित करना शुरू करना है। कभी-कभी चिप में "पट्टियाँ" होंगी, कुछ पिन जिन्हें आप रीसेट जारी करने से पहले उच्च या निम्न टाई कर सकते हैं, जो तर्क कई तरीकों से बूट करने के लिए उपयोग करता है। आपको सिस्टम के बाकी हिस्सों से खुद को सीपीयू, प्रोसेसर कोर को अलग करना होगा। यह समझें कि सीपीयू कैसे संचालित होता है, और फिर समझें कि चिप / सिस्टम डिजाइनरों के पास सीपीयू के बाहर के आसपास सेटअप एड्रेस डिकोडर है ताकि cpus एड्रेस स्पेस का कुछ हिस्सा एक फ्लैश के साथ संचार करता है। और कुछ राम के साथ और कुछ बाह्य उपकरणों (uart, i2c, spi, gpio, आदि) के साथ। आप चाहें तो वही सीपीयू कोर ले सकते हैं, और इसे अलग तरह से लपेट सकते हैं। यह वह चीज है जो आपको तब मिलती है जब आप कुछ हथियार खरीदते हैं या आधारित होते हैं। हाथ और कूल्हे सीपीयू कोर बनाते हैं, जो लोग चिप खरीदते हैं और अपने सामान को इधर-उधर लपेटते हैं, विभिन्न कारणों से वे उस सामान को ब्रांड से ब्रांड तक नहीं बनाते हैं। यही कारण है कि जब यह कोर के बाहर कुछ भी आता है तो शायद ही कभी एक सामान्य बांह का सवाल पूछ सकता है।
एक माइक्रोकंट्रोलर एक चिप पर एक सिस्टम बनने का प्रयास करता है, इसलिए इसकी गैर-वाष्पशील मेमोरी (फ्लैश / रोम), वाष्पशील (श्रम), और सीपीयू सभी एक ही चिप पर परिधीयों के मिश्रण के साथ होते हैं। लेकिन चिप को आंतरिक रूप से इस तरह से डिज़ाइन किया गया है कि फ्लैश को उस सीपीयू के एड्रेस स्पेस में मैप किया जाता है जो उस सीपीयू की बूट विशेषताओं से मेल खाता है। यदि उदाहरण के लिए cpu में 0xFFFC पते पर एक रीसेट वेक्टर है, तो उस पते पर प्रतिक्रिया करने के लिए फ़्लैश / रोम होना चाहिए जो कि हम 1 के माध्यम से प्रोग्राम कर सकते हैं), उपयोगी कार्यक्रमों के लिए पता स्थान में पर्याप्त फ़्लैश / रोम के साथ। एक चिप डिजाइनर उन आवश्यकताओं को पूरा करने के लिए 0xF000 पर शुरू होने वाले 0x1000 बाइट्स का फ्लैश चुन सकता है। और शायद उन्होंने राम की कुछ मात्रा को कम पते पर या शायद 0x0000, और परिधीयों को कहीं बीच में रख दिया।
सीपीयू की एक और वास्तुकला पता शून्य पर निष्पादित करना शुरू कर सकती है, इसलिए उन्हें विपरीत करने की आवश्यकता होगी, फ्लैश को जगह दें ताकि यह शून्य के आसपास एक पता सीमा का जवाब दे। उदाहरण के लिए 0x0000 से 0x0FFF कहें। और फिर कहीं और राम रख दिया।
चिप डिजाइनरों को पता है कि कैसे सीपीयू बूट करते हैं और उन्होंने वहां गैर-वाष्पशील भंडारण रखा है (फ्लैश / रोम)। इसके बाद सॉफ्टवेयर लोगों पर निर्भर है कि वह उस सीपीयू के जाने-माने व्यवहार से मेल खाने के लिए बूट कोड लिखें। आपको रीसेट वेक्टर में रीसेट वेक्टर पता और आपके बूट कोड को रीसेट वेक्टर में निर्धारित पते पर रखना होगा। टूलचिन यहां आपकी बहुत मदद कर सकता है। कभी-कभी, बिंदु के साथ जासूसी और आईडी या अन्य सैंडबॉक्स के साथ वे आपके लिए सबसे अधिक काम कर सकते हैं जो आप करते हैं वह उच्च स्तरीय भाषा (सी) में कॉलिस है।
लेकिन, हालांकि यह किया जाता है कि प्रोग्राम को फ्लैश / रोम में लोड किया गया है और इसे सीपीयू के हार्डवेयर्ड बूट व्यवहार से मेल खाना है। आपके कार्यक्रम के मुख्य भाग से पहले सी () और यदि आप अपने प्रवेश बिंदु के रूप में मुख्य का उपयोग करते हैं, तो कुछ चीजें करनी होंगी। एसी प्रोग्रामर मानता है कि जब एक प्रारंभिक मूल्य के साथ एक चर घोषित करते हैं, तो वे उम्मीद करते हैं कि वास्तव में काम करना है। खैर, चर, कांस्टेबल के अलावा, राम में हैं, लेकिन अगर आपके पास एक प्रारंभिक मूल्य है जो प्रारंभिक मूल्य गैर-वाष्पशील राम में होना है। तो यह .data सेगमेंट है और C बूटस्ट्रैप को .data सामान को फ़्लैश से राम तक कॉपी करने की आवश्यकता है (जहां आमतौर पर टूलकिन द्वारा आपके लिए निर्धारित किया जाता है)। आपके प्रारंभिक कार्यक्रम के बिना घोषित किए जाने वाले वैश्विक चरों को आपके कार्यक्रम के शुरू होने से पहले शून्य माना जाता है, हालांकि आपको वास्तव में यह नहीं मानना चाहिए और शुक्र है कि कुछ संकलक अनधिकृत चर के बारे में चेतावनी देने लगे हैं। यह .bs सेगमेंट है, और C बूटस्ट्रैप शून्य, जो राम में है, सामग्री, शून्य, को गैर-वाष्पशील मेमोरी में संग्रहीत नहीं किया जाना है, लेकिन शुरुआती पता और कितना करता है। फिर से टूलचिन आपको यहां बहुत मदद करता है। और अंत में नंगे न्यूनतम आपको एक स्टैक पॉइंटर को सेटअप करने की आवश्यकता है क्योंकि सी प्रोग्राम स्थानीय चर और अन्य कार्यों को कॉल करने में सक्षम होने की उम्मीद करते हैं। फिर शायद कुछ अन्य चिप विशिष्ट सामान किया जाता है, या हम बाकी चिप विशिष्ट सामान को सी में करते हैं। गैर-वाष्पशील मेमोरी में संग्रहीत नहीं किया जाना चाहिए, लेकिन शुरुआती पता और कितना करता है। फिर से टूलचिन आपको यहां बहुत मदद करता है। और अंत में नंगे न्यूनतम आपको एक स्टैक पॉइंटर को सेटअप करने की आवश्यकता है क्योंकि सी प्रोग्राम स्थानीय चर और अन्य कार्यों को कॉल करने में सक्षम होने की उम्मीद करते हैं। फिर शायद कुछ अन्य चिप विशिष्ट सामान किया जाता है, या हम बाकी चिप विशिष्ट सामान को सी में करते हैं। गैर-वाष्पशील मेमोरी में संग्रहीत नहीं किया जाना चाहिए, लेकिन शुरुआती पता और कितना करता है। फिर से टूलचिन आपको यहां बहुत मदद करता है। और अंत में नंगे न्यूनतम आपको एक स्टैक पॉइंटर को सेटअप करने की आवश्यकता है क्योंकि सी प्रोग्राम स्थानीय चर और अन्य कार्यों को कॉल करने में सक्षम होने की उम्मीद करते हैं। फिर शायद कुछ अन्य चिप विशिष्ट सामान किया जाता है, या हम बाकी चिप विशिष्ट सामान को सी में करते हैं।
हाथ से कॉर्टेक्स-एम सीरीज़ कोर आपके लिए यह कुछ करेगा, स्टैक पॉइंटर वेक्टर टेबल में है, रीसेट के बाद चलाने के लिए कोड पर इंगित करने के लिए एक रीसेट वेक्टर है, ताकि आपको जो करना है उसके अलावा अन्य वेक्टर टेबल जेनरेट करने के लिए (जिसका उपयोग आप आमतौर पर किसी भी तरह से करते हैं) आप बिना एस के शुद्ध सी जा सकते हैं। अब आप अपने .data को न तो कॉपी करें और न ही अपने .bs को शून्य करें ताकि आपको स्वयं ऐसा करना पड़े यदि आप कुछ कॉर्टेक्स-मी पर आधारित एएसएम के बिना जाने की कोशिश करना चाहते हैं। बड़ी सुविधा रीसेट वेक्टर नहीं बल्कि इंटरप्ट वैक्टर है जहां हार्डवेयर हथियारों की सिफारिश करते हुए सी कॉलिंग कन्वेंशन की सिफारिश करता है और आपके लिए रजिस्टरों को संरक्षित करता है, और उस वेक्टर के लिए सही रिटर्न का उपयोग करता है, ताकि आपको प्रत्येक हैंडलर के चारों ओर सही एएसएम लपेटना न पड़े ( या आपके लक्ष्य के लिए टूलचिन के विशिष्ट निर्देश हैं जो आपके लिए टूलचिन को इसे लपेटें)।
उदाहरण के लिए चिप विशिष्ट सामान हो सकता है, माइक्रोकंट्रोलर का उपयोग अक्सर बैटरी आधारित प्रणालियों में किया जाता है, इसलिए कम शक्ति होती है, इसलिए अधिकांश में से अधिकांश बाह्य उपकरणों के साथ रीसेट से बाहर आ जाते हैं, और आपको इनमें से प्रत्येक उप सिस्टम को चालू करना होगा ताकि आप उनका उपयोग कर सकें । Uarts, gpios, आदि अक्सर एक कम-ईश घड़ी की गति का उपयोग किया जाता है, सीधे एक क्रिस्टल या आंतरिक थरथरानवाला से। और आपका सिस्टम डिज़ाइन यह दिखा सकता है कि आपको एक तेज़ घड़ी की आवश्यकता है, इसलिए आप इसे आरंभ करते हैं। आपकी घड़ी फ़्लैश या रैम के लिए बहुत तेज़ हो सकती है इसलिए आपको घड़ी को ऊपर करने से पहले प्रतीक्षा की स्थिति बदलने की आवश्यकता हो सकती है। Uart, या usb या अन्य इंटरफेस को सेटअप करने की आवश्यकता हो सकती है। तब आपका आवेदन अपनी बात कर सकता है।
एक कंप्यूटर डेस्कटॉप, लैपटॉप, सर्वर और एक माइक्रोकंट्रोलर अलग नहीं हैं कि वे बूट / काम कैसे करते हैं। सिवाय इसके कि वे ज्यादातर एक चिप पर नहीं हैं। बायोस प्रोग्राम अक्सर सीपीयू से एक अलग चिप फ्लैश / रोम पर होता है। हालांकि हाल ही में x86 cpus अधिक से अधिक खींच रहा है जो एक ही पैकेज (pcie नियंत्रकों, आदि) में चिप्स का समर्थन करते थे, लेकिन आपके पास अभी भी आपके अधिकांश रैम और रोम ऑफ चिप हैं, लेकिन यह अभी भी एक प्रणाली है और यह अभी भी ठीक से काम करता है उच्च स्तर पर समान। सीपीयू बूट प्रक्रिया को अच्छी तरह से जाना जाता है, बोर्ड डिजाइनर फ़्लैश / रोम को पता स्थान में रखते हैं जहां सीपीयू बूट होता है। वह प्रोग्राम (एक x86 पीसी पर BIOS का हिस्सा) उपरोक्त सभी चीजों को करता है, यह विभिन्न बाह्य उपकरणों को शुरू करता है, यह नाटक को आरम्भ करता है, पीसीआई बसों की गणना करता है, और इसी तरह। अक्सर उपयोगकर्ता द्वारा बायोस सेटिंग्स या जिसे हम cmos सेटिंग्स कहते थे, के आधार पर काफी विन्यास योग्य है, क्योंकि उस समय जो तकनीक का उपयोग किया गया था। कोई बात नहीं, उपयोगकर्ता सेटिंग्स हैं जो आप जा सकते हैं और बायोस बूट कोड को बताने के लिए बदल सकते हैं कि यह कैसे बदलता है।
विभिन्न लोग विभिन्न शब्दावली का उपयोग करेंगे। एक चिप बूट, यह पहला कोड है जो चलता है। जिसे कभी-कभी बूटस्ट्रैप भी कहा जाता है। लोडर शब्द के साथ एक बूटलोडर का अर्थ अक्सर यह होता है कि यदि आप हस्तक्षेप करने के लिए कुछ नहीं करते हैं तो यह एक बूटस्ट्रैप है जो आपको सामान्य बूटिंग से कुछ बड़े, आपके आवेदन या ऑपरेटिंग सिस्टम में ले जाता है। लेकिन लोडर भाग का अर्थ है कि आप बूट प्रक्रिया को बाधित कर सकते हैं और फिर अन्य परीक्षण कार्यक्रमों को लोड कर सकते हैं। यदि आपने कभी भी एक अंतर्निहित लिनक्स सिस्टम पर उदाहरण के लिए uboot का उपयोग किया है, तो आप एक कुंजी को हिट कर सकते हैं और सामान्य बूट को रोक सकते हैं, फिर आप रैम में एक टेस्ट कर्नेल डाउनलोड कर सकते हैं और इसे फ्लैश के बजाय बूट कर सकते हैं, या आप अपना डाउनलोड कर सकते हैं खुद के कार्यक्रम, या आप नए कर्नेल को डाउनलोड कर सकते हैं, फिर बूटलोडर को इसे फ्लैश करने के लिए लिखें ताकि अगली बार जब आप बूट करें तो यह नया सामान चलाए।
जहाँ तक स्वयं सीपीयू, कोर प्रोसेसर, जो बाह्य उपकरणों से फ्लैश से रैम को नहीं जानता है। बूटलोडर, ऑपरेटिंग सिस्टम, एप्लिकेशन की कोई धारणा नहीं है। यह केवल निर्देशों का एक अनुक्रम है जिसे cpu में खिलाया जाता है। विभिन्न प्रोग्रामिंग कार्यों को एक दूसरे से अलग करने के लिए ये सॉफ्टवेयर शब्द हैं। एक दूसरे से सॉफ्टवेयर अवधारणाओं।
कुछ माइक्रोकंट्रोलर में एक अलग फ्लैश या फ्लैश के अलग क्षेत्र में चिप विक्रेता द्वारा प्रदान किया गया एक अलग बूट लोडर होता है जिसे आप संशोधित करने में सक्षम नहीं हो सकते हैं। इस मामले में अक्सर पिन या पिन का सेट होता है (मैं उन्हें पट्टियाँ कहता हूं) कि यदि आप रीसेट जारी होने से पहले उन्हें उच्च या निम्न टाई करते हैं तो आप तर्क और / या उस बूटलोडर को बता रहे हैं कि क्या करना है, उदाहरण के लिए एक पट्टा संयोजन हो सकता है उस बूटलोडर को चलाने के लिए चिप को बताएं और डेटा को फ्लैश में प्रोग्राम किए जाने के लिए uart पर प्रतीक्षा करें। पट्टियों को दूसरे तरीके से सेट करें और आपके प्रोग्राम बूट चिप विक्रेता बूटलोडर को नहीं, चिप की फील्ड प्रोग्रामिंग या आपके प्रोग्राम के दुर्घटनाग्रस्त होने से उबरने की अनुमति देते हैं। कभी-कभी यह सिर्फ शुद्ध तर्क है जो आपको फ्लैश को प्रोग्राम करने की अनुमति देता है। यह इन दिनों काफी आम है,
अधिकांश माइक्रोकंट्रोलर्स के पास राम की तुलना में अधिक फ्लैश है, इसका कारण यह है कि प्राथमिक उपयोग का मामला प्रोग्राम को सीधे फ्लैश से चलाने के लिए है, और केवल स्टैक और चर को कवर करने के लिए पर्याप्त रैम है। यद्यपि कुछ मामलों में आप रैम से प्रोग्राम चला सकते हैं, जिसे आपको राइट संकलित करना होगा और फ्लैश में स्टोर करना होगा, फिर कॉल करने से पहले कॉपी करें।
संपादित करें
flash.s
.cpu cortex-m0
.thumb
.thumb_func
.global _start
_start:
stacktop: .word 0x20001000
.word reset
.word hang
.word hang
.word hang
.thumb_func
reset:
bl notmain
b hang
.thumb_func
hang: b .
notmain.c
int notmain ( void )
{
unsigned int x=1;
unsigned int y;
y = x + 1;
return(0);
}
flash.ld
MEMORY
{
bob : ORIGIN = 0x00000000, LENGTH = 0x1000
ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
.text : { *(.text*) } > bob
.rodata : { *(.rodata*) } > bob
.bss : { *(.bss*) } > ted
.data : { *(.bss*) } > ted AT > bob
}
तो यह एक कोर्टेक्स- m0 के लिए एक उदाहरण है, कोर्टेक्स-एमएस सभी एक ही काम करते हैं जहां तक यह उदाहरण जाता है। इस उदाहरण के लिए, विशेष चिप में, हाथ के पते के स्थान में 0x00000000 पर पता फ्लैश है और 0x20000000 पर रैम है।
जिस तरह से 0x0000 पर एड्रेस में 32 बिट का कॉर्टेक्स-एम बूट है वह स्टैक पॉइंटर को इनिशियलाइज़ करने के लिए एड्रेस है। मुझे इस उदाहरण के लिए बहुत अधिक स्टैक की आवश्यकता नहीं है, इसलिए 0x20001000 पर्याप्त होगा, जाहिर है कि उस पते के नीचे राम होना चाहिए (जिस तरह से हाथ को धक्का दिया जाता है, क्या यह पहले घटाया जाता है, फिर धक्का देता है यदि आप 0x20001000 सेट करते हैं तो स्टैक पर पहला आइटम पता 0x2000FFFC पर है आपको 0x2000FFFC का उपयोग नहीं करना है)। 0x0004 पते पर 32 बिट शब्द रीसेट हैंडलर का पता है, मूल रूप से पहला कोड जो रीसेट के बाद चलता है। फिर अधिक कॉन्ट्रैक्ट और ईवेंट हैंडलर हैं जो कि कॉर्टेक्स एम कोर और चिप के लिए विशिष्ट हैं, संभवतः 128 या 256 के रूप में, यदि आप उनका उपयोग नहीं करते हैं, तो आपको उनके लिए तालिका सेटअप करने की आवश्यकता नहीं है, मैं प्रदर्शन के लिए कुछ में फेंक दिया प्रयोजनों।
मुझे इस उदाहरण में .data और न ही .bs से निपटने की आवश्यकता नहीं है क्योंकि मुझे पता है कि कोड को देखकर उन खंडों में कुछ भी नहीं है। अगर वहाँ मैं इसके साथ सौदा होगा, और एक दूसरे में होगा।
तो स्टैक सेटअप है, चेक, .data ने ध्यान रखा, चेक, .bs, चेक, इसलिए सी बूटस्ट्रैप सामान किया जाता है, सी के लिए प्रवेश समारोह के लिए शाखा कर सकता है क्योंकि कुछ कंपाइलर अतिरिक्त जंक जोड़ देंगे यदि वे फ़ंक्शन देखते हैं। main () और main के रास्ते पर, मैं उस सटीक नाम का उपयोग नहीं करता, मैंने अपने C एंट्री पॉइंट के रूप में यहाँ notmain () का उपयोग किया। तो रिसेट हैंडलर notmain () को कॉल करता है, अगर if / notmain () रिटर्न देता है तो यह हैंग हो जाता है जो कि एक अनंत लूप है, संभवतः खराब नाम वाला।
मैं दृढ़ता से उपकरणों में महारत हासिल करने में विश्वास करता हूं, बहुत से लोग नहीं करते हैं, लेकिन आप जो पाएंगे वह यह है कि प्रत्येक नंगे धातु डेवलपर अपने काम खुद करता है, क्योंकि पूर्ण स्वतंत्रता के निकट, दूर से संकुचित नहीं है क्योंकि आप एप्लिकेशन या वेब पेज बना रहे होंगे। । वे फिर से अपना काम करते हैं। मैं अपना बूटस्ट्रैप कोड और लिंकर स्क्रिप्ट रखना पसंद करता हूं। अन्य लोग टूलचेन पर भरोसा करते हैं, या विक्रेताओं के सैंडबॉक्स में खेलते हैं जहां अधिकांश काम किसी और द्वारा किया जाता है (और अगर कुछ टूटता है तो आप चोट की दुनिया में हैं, और नंगे धातु की चीजों के साथ अक्सर और नाटकीय तरीके से टूटते हैं)।
इसलिए मुझे मिलने वाले गन टूल के साथ संयोजन, संकलन और लिंकिंग:
00000000 <_start>:
0: 20001000 andcs r1, r0, r0
4: 00000015 andeq r0, r0, r5, lsl r0
8: 0000001b andeq r0, r0, fp, lsl r0
c: 0000001b andeq r0, r0, fp, lsl r0
10: 0000001b andeq r0, r0, fp, lsl r0
00000014 <reset>:
14: f000 f802 bl 1c <notmain>
18: e7ff b.n 1a <hang>
0000001a <hang>:
1a: e7fe b.n 1a <hang>
0000001c <notmain>:
1c: 2000 movs r0, #0
1e: 4770 bx lr
तो बूटलोडर कैसे जानता है कि सामान कहाँ है। क्योंकि कंपाइलर ने काम किया। पहले मामले में कोडांतरक ने फ़्लैश.s के लिए कोड उत्पन्न किया, और ऐसा करने से पता चलता है कि लेबल कहां हैं (लेबल केवल फ़ंक्शन नाम या परिवर्तनशील नाम, आदि जैसे पते हैं) इसलिए मुझे बाइट गिनना नहीं है और वेक्टर में भरना है तालिका मैन्युअल रूप से, मैंने एक लेबल नाम का उपयोग किया और कोडांतरक ने मेरे लिए किया। अब आप पूछते हैं कि यदि रीसेट 0x14 पता है तो कोडांतरक ने वेक्टर तालिका में 0x15 क्यों डाला। वैसे यह एक कोर्टेक्स-एम है और यह बूट होता है और केवल अंगूठे मोड में चलता है। एआरएम के साथ जब आप एक पते पर शाखा करते हैं यदि शाखा मोड में अंगूठे को सेट करने की आवश्यकता होती है, तो आर्म मोड रीसेट हो जाता है। इसलिए आपको हमेशा उस बिट सेट की आवश्यकता होती है। मुझे उपकरण पता है और एक लेबल से पहले .thumb_func डालकर, अगर उस लेबल का उपयोग किया जाता है जैसा कि वह वेक्टर टेबल में या ब्रांचिंग के लिए या जो भी हो। टूलकिन को लैबिट सेट करना जानता है। तो यह यहाँ 0x14 | 1 = 0x15 है। इसी तरह फांसी के लिए भी। अब disassembler नॉटमैन () को कॉल करने के लिए 0x1D शो नहीं करता है, लेकिन चिंता न करें कि टूल ने निर्देश को सही तरीके से बनाया है।
अब उस कोड में नोटमैन, जो स्थानीय चर हैं, उपयोग नहीं किए गए हैं, वे मृत कोड हैं। कंपाइलर उस तथ्य पर भी टिप्पणी करता है जिसमें y सेट किया गया है लेकिन उपयोग नहीं किया गया है।
एड्रेस स्पेस पर ध्यान दें, ये सभी चीजें एड्रेस 0x0000 से शुरू होती हैं और वहां से जाती हैं, इसलिए वेक्टर टेबल को ठीक से रखा गया है, इनटेक्स या प्रोग्राम स्पेस को भी ठीक से रखा गया है, मुझे कैसे पता चला कि मैं notmain.c के कोड के सामने फ़्लैश हूं। उपकरणों को जानना, एक सामान्य गलती यह है कि सही और दुर्घटनाग्रस्त न हो और कड़ी मेहनत से जलाएं। IMO आपको यह सुनिश्चित करने के लिए अलग करना होगा कि पहली बार बूट करने से पहले चीजें सही रखी गई हैं, एक बार जब आपके पास सही जगह की चीजें हैं, तो आपको हर बार जांचना जरूरी नहीं है। बस नई परियोजनाओं के लिए या यदि वे लटकाते हैं।
अब कुछ लोग जो कुछ लोगों को आश्चर्यचकित करते हैं, वह यह है कि किसी भी दो संकलक से एक ही इनपुट से एक ही आउटपुट का निर्माण करने की अपेक्षा करने का कोई कारण नहीं है। या विभिन्न सेटिंग्स के साथ भी एक ही संकलक। क्लैंग का उपयोग करते हुए, llvm संकलक मैं इन दो आउटपुट को अनुकूलन के साथ और बिना प्राप्त करता हूं
llvm / clang अनुकूलित
00000000 <_start>:
0: 20001000 andcs r1, r0, r0
4: 00000015 andeq r0, r0, r5, lsl r0
8: 0000001b andeq r0, r0, fp, lsl r0
c: 0000001b andeq r0, r0, fp, lsl r0
10: 0000001b andeq r0, r0, fp, lsl r0
00000014 <reset>:
14: f000 f802 bl 1c <notmain>
18: e7ff b.n 1a <hang>
0000001a <hang>:
1a: e7fe b.n 1a <hang>
0000001c <notmain>:
1c: 2000 movs r0, #0
1e: 4770 bx lr
अनुकूलित नहीं है
00000000 <_start>:
0: 20001000 andcs r1, r0, r0
4: 00000015 andeq r0, r0, r5, lsl r0
8: 0000001b andeq r0, r0, fp, lsl r0
c: 0000001b andeq r0, r0, fp, lsl r0
10: 0000001b andeq r0, r0, fp, lsl r0
00000014 <reset>:
14: f000 f802 bl 1c <notmain>
18: e7ff b.n 1a <hang>
0000001a <hang>:
1a: e7fe b.n 1a <hang>
0000001c <notmain>:
1c: b082 sub sp, #8
1e: 2001 movs r0, #1
20: 9001 str r0, [sp, #4]
22: 2002 movs r0, #2
24: 9000 str r0, [sp, #0]
26: 2000 movs r0, #0
28: b002 add sp, #8
2a: 4770 bx lr
ऐसा इसलिए है कि एक कंपाइलर ने इसके अतिरिक्त अनुकूलन किया, लेकिन इसने चर के लिए स्टैक पर दो आइटम आवंटित किए, क्योंकि ये स्थानीय चर हैं जो वे राम में हैं लेकिन स्टैक पर निश्चित पते पर नहीं, ग्लोबल्स के साथ देखेंगे। परिवर्तन। लेकिन संकलक को एहसास हुआ कि यह संकलन समय पर y की गणना कर सकता है और इसे चलाने के समय में गणना करने का कोई कारण नहीं था, इसलिए इसने x के लिए आवंटित स्टैक स्पेस में 1 और y के लिए आवंटित स्टैक स्पेस के लिए 2 रखा। संकलक आंतरिक तालिकाओं के साथ इस स्थान को "आवंटित" करता है जिसे मैं वेरिएबल y के लिए स्टैक प्लस 0 और वेरिएबल x के लिए स्टैक प्लस 4 घोषित करता हूं। कंपाइलर जो कुछ भी करना चाहता है वह कर सकता है जब तक कि यह कोड लागू होता है वह सी प्रोग्रामर के सी मानक या एक्सपेक्टेशन के अनुरूप होता है। फ़ंक्शन की अवधि के लिए कंपाइलर को स्टैक + 4 पर छोड़ने का कोई कारण नहीं है,
अगर मैं असेंबलर में एक फ़ंक्शन डमी जोड़ता हूं
.thumb_func
.globl dummy
dummy:
bx lr
और फिर इसे कॉल करें
void dummy ( unsigned int );
int notmain ( void )
{
unsigned int x=1;
unsigned int y;
y = x + 1;
dummy(y);
return(0);
}
आउटपुट बदलता है
00000000 <_start>:
0: 20001000 andcs r1, r0, r0
4: 00000015 andeq r0, r0, r5, lsl r0
8: 0000001b andeq r0, r0, fp, lsl r0
c: 0000001b andeq r0, r0, fp, lsl r0
10: 0000001b andeq r0, r0, fp, lsl r0
00000014 <reset>:
14: f000 f804 bl 20 <notmain>
18: e7ff b.n 1a <hang>
0000001a <hang>:
1a: e7fe b.n 1a <hang>
0000001c <dummy>:
1c: 4770 bx lr
...
00000020 <notmain>:
20: b510 push {r4, lr}
22: 2002 movs r0, #2
24: f7ff fffa bl 1c <dummy>
28: 2000 movs r0, #0
2a: bc10 pop {r4}
2c: bc02 pop {r1}
2e: 4708 bx r1
अब जब हमारे पास नेस्टेड फ़ंक्शन हैं, तो नोटमैन फ़ंक्शन को अपना रिटर्न पता संरक्षित करने की आवश्यकता है, ताकि यह नेस्टेड कॉल के लिए रिटर्न एड्रेस को क्लोब कर सके। इसका कारण यह है कि भुजा रिटर्न के लिए एक रजिस्टर का उपयोग करता है, अगर यह स्टैक का उपयोग करता है जैसे कि x86 या कुछ अन्य अच्छी तरह से कहें ... यह अभी भी स्टैक का उपयोग करेगा लेकिन अलग तरह से। अब आप पूछते हैं कि इसने r4 को धक्का क्यों दिया? ठीक है, बहुत पहले नहीं बुलाए गए सम्मेलन ने 32 बिट के बजाय 64 बिट (दो शब्द) सीमाओं पर संरेखित रखने के लिए बदल दिया, एक सीमाएं। इसलिए उन्हें स्टैक को संरेखित रखने के लिए कुछ धक्का देने की आवश्यकता है, इसलिए कंपाइलर ने मनमाने ढंग से किसी कारण से r4 को चुना, इससे कोई फर्क नहीं पड़ता। इस लक्ष्य के लिए बुलाए गए सम्मेलन के अनुसार, r4 में एक बग बग होगा, हम फंक्शन कॉल पर clobber r4 नहीं करते, हम r3 के माध्यम से r0 को clobber कर सकते हैं। r0 रिटर्न वैल्यू है। ऐसा लगता है कि शायद यह एक पूंछ अनुकूलन कर रहा है,
लेकिन हम देखते हैं कि x और y गणित डमी फ़ंक्शन में पारित होने के 2 के हार्डकोडेड मान के अनुकूल है (डमी को विशेष रूप से एक अलग फाइल में कोडित किया गया था, इस मामले में एएसएम, ताकि कंपाइलर फ़ंक्शन कॉल को पूरी तरह से ऑप्टिमाइज़ नहीं कर सके। अगर मेरे पास एक डमी फंक्शन होता है, जो केवल notmain.c में C में वापस आता है, तो ऑप्टिमाइज़र ने x, y और डमी फंक्शन कॉल को हटा दिया होगा क्योंकि वे सभी डेड / बेकार कोड हैं)।
यह भी ध्यान दें कि क्योंकि फ्लैश.एस कोड को बड़ा नोट मिल गया है, वह कोई और नहीं है और टूलचिन ने हमारे लिए सभी पते को पैच करने का ध्यान रखा है इसलिए हमें मैन्युअल रूप से ऐसा नहीं करना है।
संदर्भ के लिए अडॉप्टेड क्लैंग
00000020 <notmain>:
20: b580 push {r7, lr}
22: af00 add r7, sp, #0
24: b082 sub sp, #8
26: 2001 movs r0, #1
28: 9001 str r0, [sp, #4]
2a: 2002 movs r0, #2
2c: 9000 str r0, [sp, #0]
2e: f7ff fff5 bl 1c <dummy>
32: 2000 movs r0, #0
34: b002 add sp, #8
36: bd80 pop {r7, pc}
अनुकूलित क्लैंग
00000020 <notmain>:
20: b580 push {r7, lr}
22: af00 add r7, sp, #0
24: 2002 movs r0, #2
26: f7ff fff9 bl 1c <dummy>
2a: 2000 movs r0, #0
2c: bd80 pop {r7, pc}
उस संकलक लेखक ने स्टैक को संरेखित करने के लिए डमी वैरिएबल के रूप में r7 का उपयोग करने के लिए चुना, यह भी r7 का उपयोग करके एक फ्रेम पॉइंटर बना रहा है, भले ही इसमें स्टैक फ्रेम में कुछ भी न हो। मूल रूप से निर्देश को अनुकूलित किया जा सकता था। लेकिन इसने तीन निर्देशों को वापस करने के लिए पॉप का उपयोग किया, जो शायद मुझ पर था मुझे यकीन था कि मैं इसे सही कमांड लाइन विकल्पों (प्रोसेसर को निर्दिष्ट करने) के साथ कर सकता हूं।
यह ज्यादातर आपके सवालों के जवाब देना चाहिए
void dummy ( unsigned int );
unsigned int x=1;
unsigned int y;
int notmain ( void )
{
y = x + 1;
dummy(y);
return(0);
}
मेरे पास अब ग्लोबल्स हैं। इसलिए वे या तो .data या .bs में जाते हैं अगर वे बाहर अनुकूलित नहीं मिलते हैं।
इससे पहले कि हम अंतिम आउटपुट को देखें इटर्माडिएट ऑब्जेक्ट को देखें
00000000 <notmain>:
0: b510 push {r4, lr}
2: 4b05 ldr r3, [pc, #20] ; (18 <notmain+0x18>)
4: 6818 ldr r0, [r3, #0]
6: 4b05 ldr r3, [pc, #20] ; (1c <notmain+0x1c>)
8: 3001 adds r0, #1
a: 6018 str r0, [r3, #0]
c: f7ff fffe bl 0 <dummy>
10: 2000 movs r0, #0
12: bc10 pop {r4}
14: bc02 pop {r1}
16: 4708 bx r1
...
Disassembly of section .data:
00000000 <x>:
0: 00000001 andeq r0, r0, r1
अब इसमें से जानकारी गायब है, लेकिन यह इस बात का अंदाजा लगाता है कि क्या चल रहा है, लिंकर वह है जो वस्तुओं को ले जाता है और उन्हें सूचना के साथ एक साथ जोड़ता है (बशर्ते कि इस मामले में Flash.ld) यह बताता है कि .text और। डेटा और ऐसे जाता है। संकलक ऐसी चीजों को नहीं जानता है, यह केवल उस कोड पर ध्यान केंद्रित कर सकता है जो इसे प्रस्तुत किया गया है, किसी भी बाहरी इसे लिंकर को कनेक्शन में भरने के लिए एक छेद छोड़ना होगा। किसी भी डेटा को उन चीजों को एक साथ जोड़ने का एक तरीका छोड़ना पड़ता है, इसलिए सब कुछ के लिए पते यहां शून्य आधारित हैं, क्योंकि संकलक और इस असंतुष्ट व्यक्ति को नहीं पता है। अन्य जानकारी यहाँ नहीं दिखाई गई है कि लिंकर चीजों को रखने के लिए उपयोग करता है। यहां कोड पर्याप्त स्वतंत्र स्थिति है, ताकि लिंकर अपना काम कर सके।
फिर हम कम से कम लिंक किए गए आउटपुट के डिस्सैस को देखते हैं
00000020 <notmain>:
20: b510 push {r4, lr}
22: 4b05 ldr r3, [pc, #20] ; (38 <notmain+0x18>)
24: 6818 ldr r0, [r3, #0]
26: 4b05 ldr r3, [pc, #20] ; (3c <notmain+0x1c>)
28: 3001 adds r0, #1
2a: 6018 str r0, [r3, #0]
2c: f7ff fff6 bl 1c <dummy>
30: 2000 movs r0, #0
32: bc10 pop {r4}
34: bc02 pop {r1}
36: 4708 bx r1
38: 20000004 andcs r0, r0, r4
3c: 20000000 andcs r0, r0, r0
Disassembly of section .bss:
20000000 <y>:
20000000: 00000000 andeq r0, r0, r0
Disassembly of section .data:
20000004 <x>:
20000004: 00000001 andeq r0, r0, r1
संकलक ने मूल रूप से रैम में दो 32 बिट चर के लिए कहा है। एक .bs में है क्योंकि मैंने इसे शुरू नहीं किया है इसलिए इसे शून्य के रूप में माना जाता है। दूसरा है .डेटा क्योंकि मैंने घोषणा पर इसे इनिशियलाइज़ किया था।
अब क्योंकि ये वैश्विक चर हैं इसलिए यह माना जाता है कि अन्य कार्य इन्हें संशोधित कर सकते हैं। संकलक कोई धारणा नहीं बनाता है कि कब नोटिन कहा जा सकता है इसलिए यह जो इसे देख सकता है उसके साथ अनुकूलन नहीं कर सकता है, y = x + 1 गणित, इसलिए इसे उस रनटाइम को करना होगा। यह राम से पढ़ना है दो चर उन्हें जोड़ते हैं और वापस बचाते हैं।
अब स्पष्ट रूप से यह कोड काम नहीं करेगा। क्यों? क्योंकि मेरा बूटस्ट्रैप जैसा कि यहां दिखाया गया है, नोटमैन को बुलाने से पहले राम को तैयार नहीं करता है, इसलिए जो भी कचरा 0x20000000 और 0x20000004 में था, जब चिप जगाया जाता है, तो इसका उपयोग y और x के लिए किया जाएगा।
यहां नहीं दिखाने जा रहे हैं। आप मेरे लंबे समय तक घुमावदार डेडटा और .bss पर पढ़ सकते हैं और मुझे अपने नंगे धातु कोड में कभी भी उनकी आवश्यकता क्यों नहीं है, लेकिन अगर आपको लगता है कि आपको किसी और की उम्मीद करने के बजाय उपकरण को मास्टर करना है और इसे सही करना है .. ।
https://github.com/dwelch67/raspberrypi/tree/master/bssdata
लिंकर स्क्रिप्ट, और बूटस्ट्रैप कुछ विशिष्ट संकलक हैं, इसलिए जब आप एक संकलक के एक संस्करण के बारे में सीखते हैं, तो अगले संस्करण पर या किसी अन्य संकलक के साथ फेंक दिया जा सकता है, फिर भी एक और कारण है कि मैं एक टन प्रयास में नहीं डाल सकता हूँ .data और .bs तैयारी। बस इस आलसी होने के लिए:
unsigned int x=1;
मैं ऐसा नहीं करूंगा
unsigned int x;
...
x = 1;
और कंपाइलर ने इसे मेरे लिए .text में डाल दिया। कभी-कभी यह फ्लैश को बचाता है जिस तरह से कभी-कभी यह अधिक जलता है। यह टूलचैन संस्करण या एक संकलक से दूसरे में प्रोग्राम और पोर्ट के लिए सबसे निश्चित रूप से बहुत आसान है। बहुत अधिक विश्वसनीय, कम त्रुटि प्रवण। हां, सी मानक के अनुरूप नहीं है।
अब क्या होगा अगर हम इन स्थैतिक ग्लोबल्स को बनाते हैं?
void dummy ( unsigned int );
static unsigned int x=1;
static unsigned int y;
int notmain ( void )
{
y = x + 1;
dummy(y);
return(0);
}
कुंआ
00000020 <notmain>:
20: b510 push {r4, lr}
22: 2002 movs r0, #2
24: f7ff fffa bl 1c <dummy>
28: 2000 movs r0, #0
2a: bc10 pop {r4}
2c: bc02 pop {r1}
2e: 4708 bx r1
स्पष्ट रूप से उन चर को अन्य कोड द्वारा संशोधित नहीं किया जा सकता है, इसलिए संकलक अब संकलित समय पर मृत कोड का अनुकूलन कर सकता है, जैसे कि यह पहले किया था।
unoptimized
00000020 <notmain>:
20: b580 push {r7, lr}
22: af00 add r7, sp, #0
24: 4804 ldr r0, [pc, #16] ; (38 <notmain+0x18>)
26: 6800 ldr r0, [r0, #0]
28: 1c40 adds r0, r0, #1
2a: 4904 ldr r1, [pc, #16] ; (3c <notmain+0x1c>)
2c: 6008 str r0, [r1, #0]
2e: f7ff fff5 bl 1c <dummy>
32: 2000 movs r0, #0
34: bd80 pop {r7, pc}
36: 46c0 nop ; (mov r8, r8)
38: 20000004 andcs r0, r0, r4
3c: 20000000 andcs r0, r0, r0
यह संकलक जो स्थानीय लोगों के लिए स्टैक का उपयोग करता था, अब ग्लोबल्स के लिए रैम का उपयोग करता है और लिखित रूप में यह कोड टूट गया है क्योंकि मैंने नहीं किया है।
और एक आखिरी चीज जो हम कठबोली में देख सकते हैं।
:1000000000100020150000001B0000001B00000075
:100010001B00000000F004F8FFE7FEE77047000057
:1000200080B500AF04480068401C04490860FFF731
:10003000F5FF002080BDC046040000200000002025
:08004000E0FFFF7F010000005A
:0400480078563412A0
:00000001FF
मैंने x को 0x12345678 के साथ प्री-इनिट किया। मेरी लिंकर स्क्रिप्ट (यह gnu ld के लिए है) में bob चीज़ पर यह टेड है। लिंक करने वाले को बताता है कि मैं चाहता हूं कि अंतिम स्थान टेड एड्रेस स्पेस में हो, लेकिन इसे टेड एड्रेस स्पेस में बाइनरी में स्टोर करें और कोई इसे आपके लिए स्थानांतरित कर देगा। और हम देख सकते हैं कि हुआ। यह इंटेल हेक्स प्रारूप है। और हम 0x12345678 देख सकते हैं
:0400480078563412A0
बाइनरी के फ्लैश एड्रेस स्पेस में है।
यह भी पता चलता है
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
EXIDX 0x010040 0x00000040 0x00000040 0x00008 0x00008 R 0x4
LOAD 0x010000 0x00000000 0x00000000 0x00048 0x00048 R E 0x10000
LOAD 0x020004 0x20000004 0x00000048 0x00004 0x00004 RW 0x10000
LOAD 0x030000 0x20000000 0x20000000 0x00000 0x00004 RW 0x10000
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10
LOAD लाइन जहां आभासी पता 0x20000004 है और भौतिक 0x48 है