हम असेंबली से मशीन कोड (कोड जनरेशन) में कैसे जाते हैं


16

मशीन कोड के कोड कोडिंग के बीच के चरण की कल्पना करने का एक आसान तरीका है?

उदाहरण के लिए यदि आप नोटपैड में एक बाइनरी फ़ाइल के बारे में खोलते हैं तो आपको मशीन कोड का एक पाठ स्वरूपित प्रतिनिधित्व दिखाई देता है। मुझे लगता है कि प्रत्येक बाइट (प्रतीक) जो आप देखते हैं, यह बाइनरी मान के लिए संबंधित एएससीआई चरित्र है?

लेकिन हम असेंबली से बाइनरी तक कैसे जाते हैं, पर्दे के पीछे क्या चल रहा है ??

जवाबों:


28

निर्देश सेट दस्तावेज़ देखें, और आपको प्रत्येक निर्देश के लिए एक तस्वीर माइक्रोकंट्रोलर से इस तरह की प्रविष्टियाँ मिलेंगी :

उदाहरण addlw निर्देश

"एन्कोडिंग" लाइन बताती है कि वह निर्देश बाइनरी में कैसा दिखता है। इस मामले में, यह हमेशा 5 लोगों के साथ शुरू होता है, फिर एक केयर बिट (जो या तो एक या शून्य हो सकता है), फिर आप जो शाब्दिक जोड़ रहे हैं उसके लिए "k" का स्टैंड।

पहले कुछ बिट्स को "ओपकोड" कहा जाता है, प्रत्येक निर्देश के लिए अद्वितीय हैं। सीपीयू मूल रूप से ओपकोड को देखता है कि यह क्या निर्देश है, फिर यह "k" s को एक संख्या के रूप में जोड़ना जानता है।

यह थकाऊ है, लेकिन सांकेतिक शब्दों में बदलना और डिकोड करना मुश्किल नहीं है। मेरे पास एक अंडरग्रेजुएट क्लास थी जहाँ हमें एग्जाम में हाथ बँटाना पड़ता था।

वास्तव में एक पूर्ण निष्पादन योग्य फ़ाइल बनाने के लिए, आपको अपने ऑपरेटिंग सिस्टम के आधार पर, मेमोरी को आवंटित करने, शाखा ऑफ़सेट की गणना करने और ईएलएफ जैसे प्रारूप में रखने जैसे काम भी करने होंगे।


10

असेंबली ऑपकोड में अधिकांश भाग के लिए अंतर्निहित मशीन निर्देशों के साथ एक-से-एक पत्राचार होता है। तो आपको बस इतना करना है कि असेंबली लैंग्वेज में प्रत्येक ओपकोड की पहचान करें, उसे संबंधित मशीन इंस्ट्रक्शन में मैप करें, और मशीन इंस्ट्रक्शन को फाइल के साथ-साथ उसके संबंधित मापदंडों (यदि कोई हो) के साथ लिखें। तब आप स्रोत फ़ाइल में प्रत्येक अतिरिक्त opcode के लिए प्रक्रिया को दोहराते हैं।

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


7

पहली चीज जो आपको चाहिए वह है कुछ इस फाइल की तरह । यह x86 प्रोसेसर के लिए निर्देश डेटाबेस है जैसा कि NASM असेंबलर द्वारा उपयोग किया जाता है (जो मैंने लिखने में मदद की, हालांकि वे भाग जो वास्तव में निर्देशों का अनुवाद नहीं करते हैं)। डेटाबेस से एक मनमानी लाइन लेने दें:

ADD   rm32,imm8    [mi:    hle o32 83 /0 ib,s]      386,LOCK

इसका मतलब यह है कि यह निर्देश का वर्णन करता है ADD। इस निर्देश के कई प्रकार हैं, और जो विशिष्ट यहां वर्णित किया जा रहा है, वह संस्करण है जो या तो 32-बिट रजिस्टर या मेमोरी एड्रेस लेता है और तत्काल 8-बिट मान जोड़ता है (अर्थात एक अनुदेश में लगातार शामिल)। एक उदाहरण विधानसभा निर्देश जो इस संस्करण का उपयोग करेगा, वह यह है:

add eax, 42

अब, आपको अपने पाठ इनपुट को लेने और इसे व्यक्तिगत निर्देशों और ऑपरेंड में पार्स करने की आवश्यकता है। ऊपर दिए गए निर्देश के लिए, यह संभवतः एक संरचना में परिणाम होगा जिसमें अनुदेश ADD, और एक सरणी ऑफ़ेंड (रजिस्टर EAXऔर मूल्य का एक संदर्भ 42) है। एक बार जब आपके पास यह संरचना होती है, तो आप निर्देश डेटाबेस के माध्यम से चलाते हैं और निर्देश नाम और ऑपरेंड्स के प्रकारों से मेल खाने वाली रेखा को ढूंढते हैं। यदि आपको कोई मेल नहीं मिलता है, तो यह एक त्रुटि है जिसे उपयोगकर्ता को प्रस्तुत करना होगा ("ओपकोड और ऑपरेंड्स का अवैध संयोजन" या समान सामान्य पाठ है)।

डेटाबेस से लाइन मिल जाने के बाद, हम तीसरे कॉलम को देखते हैं, जो इस निर्देश के लिए है:

[mi:    hle o32 83 /0 ib,s] 

यह निर्देशों का एक सेट है जो यह बताता है कि मशीन कोड निर्देश को उत्पन्न करने के लिए कैसे आवश्यक है:

  • miएक: ऑपरेंड के एक descriptiuon है modr/m(रजिस्टर या स्मृति) संकार्य (जिसका अर्थ है हम एक संलग्न करने की आवश्यकता होगी modr/mअनुदेश, जो हम बाद में करने के लिए आया हूँ के अंत तक बाइट) और एक एक तत्काल अनुदेश (जो होगा निर्देश के विवरण में इस्तेमाल किया जा सकता है)।
  • अगला है hle। यह पहचानता है कि हम "लॉक" उपसर्ग को कैसे संभालते हैं। हमने "लॉक" का उपयोग नहीं किया है, इसलिए हम इसे अनदेखा करते हैं।
  • अगला है o32। यह हमें बताता है कि अगर हम 16-बिट आउटपुट फॉर्मेट के लिए कोड असेंबल कर रहे हैं, तो इंस्ट्रक्शन को ऑपरेंड-साइज ओवरराइड प्रीफिक्स की जरूरत है। यदि हम 16-बिट आउटपुट का उत्पादन कर रहे थे, तो हम अब उपसर्ग का उत्पादन 0x66करेंगे ( ), लेकिन मुझे लगता है कि हम नहीं हैं और इसे आगे बढ़ाएंगे।
  • अगला है 83। यह हेक्साडेसिमल में एक शाब्दिक बाइट है। हम इसका उत्पादन करते हैं।
  • अगला है /0। यह कुछ अतिरिक्त बिट्स को निर्दिष्ट करता है जिनकी हमें modr / m bytem में आवश्यकता होगी, और हमें इसे उत्पन्न करने का कारण बनता है। modr/mबाइट एनकोड रजिस्टर या अप्रत्यक्ष स्मृति संदर्भ किया जाता है। हमारे पास एक ऐसा ऑपरेंड, एक रजिस्टर है। रजिस्टर में एक नंबर है, जो किसी अन्य डेटा फ़ाइल में निर्दिष्ट है :

    eax     REG_EAX         reg32           0
  • हम जाँचते हैं कि reg32मूल डेटाबेस से निर्देश के आवश्यक आकार से सहमत हैं (यह करता है)। 0रजिस्टर के नंबर है। एक modr/mबाइट प्रोसेसर द्वारा निर्दिष्ट एक डेटा संरचना है, जो इस तरह दिखता है:

     (most significant bit)
     2 bits       mod    - 00 => indirect, e.g. [eax]
                           01 => indirect plus byte offset
                           10 => indirect plus word offset
                           11 => register
     3 bits       reg    - identifies register
     3 bits       rm     - identifies second register or additional data
     (least significant bit)
  • क्योंकि हम एक रजिस्टर के साथ काम कर रहे हैं, modफील्ड है 0b11

  • regक्षेत्र, रजिस्टर हम प्रयोग कर रहे की संख्या है0b000
  • क्योंकि इस निर्देश में केवल एक रजिस्टर है, हमें किसी rmचीज़ के साथ फ़ील्ड भरने की आवश्यकता है। क्या अतिरिक्त डेटा में निर्दिष्ट है कि के /0तो हम में है कि शब्दों में कहें, के लिए था rm, क्षेत्र 0b000
  • modr/mबाइट इसलिए है 0b11000000या 0xC0। हम इसका उत्पादन करते हैं।
  • अगला है ib,s। यह एक हस्ताक्षरित तत्काल बाइट निर्दिष्ट करता है। हम ऑपरेंड को देखते हैं और ध्यान दें कि हमारे पास तत्काल मूल्य उपलब्ध है। हम इसे एक हस्ताक्षरित बाइट में परिवर्तित करते हैं और इसे ( 42=> 0x2A) आउटपुट करते हैं ।

पूर्ण इकट्ठे निर्देश इसलिए है 0x83 0xC0 0x2A:। इसे अपने आउटपुट मॉड्यूल पर भेजें, साथ ही ध्यान दें कि कोई भी बाइट मेमोरी रेफरेंस का गठन नहीं करता है (आउटपुट मॉड्यूल को यह जानने की आवश्यकता हो सकती है कि यह क्या है)।

हर निर्देश के लिए दोहराएँ। लेबल का ध्यान रखें ताकि आपको पता हो कि जब वे संदर्भित हों तो क्या डालें। मैक्रो और निर्देशों के लिए सुविधाएं जोड़ें जो आपके ऑब्जेक्ट फ़ाइल आउटपुट मॉड्यूल में पास हो जाते हैं। और यह मूल रूप से एक कोडांतरक कैसे काम करता है।


1
धन्यवाद। शानदार व्याख्या लेकिन यह "0x83 0xC0 0x2A" के बजाय "0x83 0xC0 0x2A" नहीं होना चाहिए क्योंकि 0b11000000 = 0xC0
कामरान

@ कामरान - $ cat > test.asm bits 32 add eax,42 $ nasm -f bin test.asm -o test.bin $ od -t x1 test.bin 0000000 83 c0 2a 0000003... हाँ, आप काफी सही कह रहे हैं। :)
जूल्स

2

व्यवहार में, एक असेंबलर आमतौर पर सीधे कुछ बाइनरी निष्पादन योग्य उत्पादन नहीं करता है , लेकिन कुछ ऑब्जेक्ट फ़ाइल (बाद में लिंकर को खिलाया जा सकता है )। हालांकि, कुछ अपवाद हैं (आप सीधे कुछ बाइनरी निष्पादन योग्य उत्पादन करने के लिए कुछ कोडांतरक का उपयोग कर सकते हैं; वे असामान्य हैं)।

सबसे पहले, ध्यान दें कि कई कोडर आज मुफ्त सॉफ्टवेयर प्रोग्राम हैं। अपने कंप्यूटर पर डाउनलोड और संकलन के स्रोत कोड तो के रूप में जीएनयू (का एक हिस्सा binutils ) और के एनएएसएम । फिर उनके सोर्स कोड का अध्ययन करें। BTW, मैं उस उद्देश्य के लिए लिनक्स का उपयोग करने की सलाह देता हूं (यह एक बहुत ही डेवलपर-अनुकूल और मुफ्त-सॉफ्टवेयर अनुकूल ओएस है)।

एक कोडांतरक द्वारा निर्मित ऑब्जेक्ट फ़ाइल में विशेष रूप से एक कोड खंड और स्थानांतरण निर्देश शामिल हैं। यह एक अच्छी तरह से प्रलेखित फ़ाइल प्रारूप में आयोजित किया जाता है, जो ऑपरेटिंग सिस्टम पर निर्भर करता है। लिनक्स पर, वह प्रारूप (ऑब्जेक्ट फ़ाइलों, साझा पुस्तकालयों, कोर डंप और निष्पादन योग्य के लिए उपयोग किया जाता है) ईएलएफ है । वह ऑब्जेक्ट फ़ाइल बाद में लिंकर के लिए इनपुट है (जो अंततः एक निष्पादन योग्य बनाता है)। Relocations ABI (जैसे x86-64 ABI ) द्वारा निर्दिष्ट किए गए हैं । अधिक के लिए लेविन की पुस्तक लिंकर्स और लोडर पढ़ें ।

ऐसी ऑब्जेक्ट फ़ाइल में कोड सेगमेंट में छेद के साथ मशीन कोड होता है (भरा जाना है, स्थानांतरण जानकारी की मदद से, लिंकर द्वारा)। एक कोडांतरक द्वारा उत्पन्न (रीकोसेबल) मशीन कोड स्पष्ट रूप से एक निर्देश सेट आर्किटेक्चर के लिए विशिष्ट है । 86 या x86-64 (सबसे लैपटॉप या डेस्कटॉप प्रोसेसर में प्रयुक्त) ISAs उनके विवरण में बहुत जटिल हैं। लेकिन शिक्षण उद्देश्यों के लिए एक सरल उपसमूह, जिसे y86 या y86-64 कहा जाता है, का आविष्कार किया गया है। आगे की स्लाइड्स पर पढ़ें इस सवाल के अन्य जवाब भी उस के बारे में थोड़ा समझाते हैं। आप कंप्यूटर आर्किटेक्चर पर एक अच्छी किताब पढ़ना चाह सकते हैं ।

अधिकांश असेंबलर दो पास में काम कर रहे हैं , दूसरा एक उत्सर्जन स्थानांतरण है या पहली पास के कुछ आउटपुट को सही करता है। वे अब सामान्य रूप से पार्सिंग तकनीकों का उपयोग करते हैं (इसलिए शायद ड्रैगन बुक पढ़ें )।

ओएस कर्नेल द्वारा एक निष्पादन योग्य कैसे शुरू किया जाता है (जैसे कि execveसिस्टम कॉल लिनक्स पर कैसे काम करता है) एक अलग (और जटिल) प्रश्न है। यह आमतौर पर कुछ वर्चुअल एड्रेस स्पेस सेट करता है (इस प्रक्रिया को करने में जो (2) निष्पादित करता है ...) फिर प्रक्रिया को आंतरिक स्थिति ( उपयोगकर्ता-मोड रजिस्टर सहित) को फिर से संगठित करता है । एक गतिशील लिंकर - लिनक्स के रूप में ld-linux.so (8) लिनक्स पर- रनटाइम में शामिल हो सकता है। एक अच्छी किताब पढ़ें, जैसे ऑपरेटिंग सिस्टम: तीन आसान टुकड़ेOSDEV विकि भी उपयोगी जानकारी दे रहा है।

पुनश्च। आपका प्रश्न इतना व्यापक है कि आपको इसके बारे में कई पुस्तकें पढ़ने की आवश्यकता है। मैंने कुछ (बहुत अधूरे) संदर्भ दिए हैं। आपको उनमें से अधिक का पता लगाना चाहिए।


1
ऑब्जेक्ट फ़ाइल स्वरूपों के बारे में, एक शुरुआत के लिए मैं NASM द्वारा निर्मित RDOFF प्रारूप को देखने की सलाह दूंगा। यह जानबूझकर वास्तविक रूप से यथासंभव सरल और अभी भी विभिन्न स्थितियों में काम करने के लिए डिज़ाइन किया गया था। NASM स्रोत में प्रारूप के लिए एक लिंकर और एक लोडर शामिल है। (पूर्ण प्रकटीकरण - मैंने इन सभी को डिज़ाइन और लिखा है)
जूल्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.