2010-05-06 24 views
7

मैंने एक साधारण हैलो वर्ल्ड प्रोग्राम लिखा है।निष्पादन योग्य और स्थानांतरित करने योग्य ऑब्जेक्ट फ़ाइल में संदेह

#include <stdio.h> 
    int main() { 
    printf("Hello World"); 
    return 0; 
    } 

मैं पुनर्निधारणीय वस्तु फ़ाइल और निष्पादन योग्य फ़ाइल की तरह लग रही है कि कैसे को समझने के लिए करना चाहता था। वस्तु फ़ाइल मुख्य कार्य करने के लिए इसी

0000000000000000 <main>: 
    0: 55      push %rbp 
    1: 48 89 e5    mov %rsp,%rbp 
    4: bf 00 00 00 00   mov $0x0,%edi 
    9: b8 00 00 00 00   mov $0x0,%eax 
    e: e8 00 00 00 00   callq 13 <main+0x13> 
    13: b8 00 00 00 00   mov $0x0,%eax 
    18: c9      leaveq 
    19: c3      retq 

यहाँ है printf के लिए समारोह कॉल callq 13. है एक बात मुझे समझ नहीं आता क्यों है यह 13. है इसका मतलब है कि सही adresss 13 में फ़ंक्शन को कॉल करें, ??। 13 में अगला निर्देश है, है ना ?? कृपया मुझे बताएं इसका क्या अर्थ है ??

निष्पादन योग्य कोड मुख्य करने के लिए इसी

00000000004004cc <main>: 
    4004cc:  55      push %rbp 
    4004cd:  48 89 e5    mov %rsp,%rbp 
    4004d0:  bf dc 05 40 00   mov $0x4005dc,%edi 
    4004d5:  b8 00 00 00 00   mov $0x0,%eax 
    4004da:  e8 e1 fe ff ff   callq 4003c0 <[email protected]> 
    4004df:  b8 00 00 00 00   mov $0x0,%eax 
    4004e4:  c9      leaveq 
    4004e5:  c3      retq 

यहाँ है callq 4003c0 है। लेकिन द्विआधारी निर्देश e8 e1 fe ff ff है। 4003 सी 0 के अनुरूप कुछ भी नहीं है। क्या है कि मैं गलत हो रहा हूँ?

धन्यवाद। बाला

उत्तर

7

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

पहले, वस्तु फ़ाइल के disassembly:

00000000 <_main>: 
    0: 8d 4c 24 04    lea 0x4(%esp),%ecx 
    4: 83 e4 f0    and $0xfffffff0,%esp 
    7: ff 71 fc    pushl -0x4(%ecx) 
    a: 55      push %ebp 
    b: 89 e5     mov %esp,%ebp 
    d: 51      push %ecx 
    e: 83 ec 04    sub $0x4,%esp 
    11: e8 00 00 00 00   call 16 <_main+0x16> 
    16: c7 04 24 00 00 00 00 movl $0x0,(%esp) 
    1d: e8 00 00 00 00   call 22 <_main+0x22> 
    22: b8 00 00 00 00   mov $0x0,%eax 
    27: 83 c4 04    add $0x4,%esp 
    2a: 59      pop %ecx 
    2b: 5d      pop %ebp 
    2c: 8d 61 fc    lea -0x4(%ecx),%esp 
    2f: c3      ret  

फिर relocations:

main.o:  file format pe-i386 

RELOCATION RECORDS FOR [.text]: 
OFFSET TYPE    VALUE 
00000012 DISP32   ___main 
00000019 dir32    .rdata 
0000001e DISP32   _puts 

आप एक स्थान परिवर्तन _puts के लिए वहाँ नहीं है, जो क्या printf करने के लिए कॉल कर दिया है देख सकते हैं में। उस स्थान पर लिंक समय पर ध्यान दिया जाएगा और तय किया जाएगा। डायनामिक लाइब्रेरी लिंकिंग के मामले में, प्रोग्राम चलने तक स्थानांतरित होने और फ़िक्सअप पूरी तरह से हल नहीं हो सकते हैं, लेकिन आपको उम्मीद है कि आपको इस उदाहरण से विचार मिलेगा।

+0

डाउनवॉटर से कोई टिप्पणी? –

5

कॉल x86 में संबंधित हैं, आईआईआरसी यदि आपके पास ई 8 है, तो कॉल स्थान addr + 5 है।

e1 fe ff ff एक छोटा एंडियन एन्कोडेड रिश्तेदार कूद है। यह वास्तव में fffffee1 का मतलब है।

अब कॉल निर्देश का पता करने के लिए इस जोड़ने + 5: (0xfffffee1 + 0x4004da + 5) % 2**32 = 0x4003c0

+1

+5 ऐसा इसलिए है क्योंकि यह कॉल के बाद * अगली * निर्देश के सापेक्ष है, और कॉल 5 बाइट लंबा है। – caf

+0

x86 पर कॉल या तो रिश्तेदार या पूर्ण हो सकते हैं। यह सिर्फ इतना है कि 'E8' एक सापेक्ष कॉल है। – AnT

+0

हाँ मैं भूल गया कि पूर्ण गंतव्य भी हैं, लेकिन वे या तो सेगमेंट द्वारा निर्दिष्ट हैं: चयनकर्ता, या किसी पते पर एक सूचक को कूदने के लिए। –

7

E8 अनुदेश में कॉल (call) के रूप में रिश्तेदार निर्दिष्ट किया जाता है का लक्ष्य ऑफसेट वर्तमान अनुदेश सूचक से (आईपी) मूल्य।

आपके पहले कोड नमूने में ऑफसेट स्पष्ट रूप से 0x00000000 है। यह मूल रूप से कहते हैं

call +0 

printf की वास्तविक पता, अभी तक ज्ञात नहीं है तो संकलक सिर्फ वहाँ 32-बिट मूल्य 0x00000000 डाल एक प्लेसहोल्डर के रूप।

शून्य ऑफ़सेट के साथ इस अपूर्ण कॉल को स्वाभाविक रूप से वर्तमान आईपी मूल्य पर कॉल के रूप में व्याख्या किया जाएगा। आपके प्लेटफ़ॉर्म पर, आईपी पूर्व-वृद्धि हुई है, जिसका अर्थ है कि जब कुछ निर्देश निष्पादित होते हैं, तो आईपी में अगले निर्देश का पता होता है। अर्थात। जब पते पर निर्देश 0xE निष्पादित किया गया है तो आईपी में 0x13 मान है। और call +0 स्वाभाविक रूप से 0x13 निर्देश के लिए कॉल के रूप में व्याख्या किया गया है। यही कारण है कि आप अधूरे कोड के disassembly में 0x13 देखते हैं।

कोड पूरा होने के बाद, प्लेसहोल्डर 0x00000000 ऑफसेट को कोड में printf फ़ंक्शन के वास्तविक ऑफसेट के साथ प्रतिस्थापित किया गया है। ऑफसेट सकारात्मक (आगे) या नकारात्मक (पिछड़ा) हो सकता है। आपके मामले में कॉल के पल में आईपी 0x4004DF है, जबकि printf फ़ंक्शन का पता 0x4003C0 है। इस कारण से, मशीन निर्देश में 32-बिट ऑफ़सेट मान 0x4003C0 - 0x4004DF के बराबर होगा, जो नकारात्मक मान -287 है। तो क्या आप कोड में देख बाइनरी में वास्तव में

call -287 

-287 है 0xFFFFFEE1 है। यह वही है जो आप अपने मशीन कोड में देखते हैं। यह सिर्फ इतना है कि आप जिस उपकरण का उपयोग कर रहे हैं वह इसे पीछे की तरफ दिखाता है।

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