2011-11-14 18 views
10

Why is the ELF execution entry point virtual address of the form 0x80xxxxx and not zero 0x0? और Why do virtual memory addresses for linux binaries start at 0x8048000? के बाद, मैं ldld -e के साथ डिफ़ॉल्ट से भिन्न प्रविष्टि बिंदु का उपयोग क्यों नहीं कर सकता?ईएलएफ प्रविष्टि बिंदु 0x8048000 "ld -e" विकल्प के साथ क्यों नहीं बदला जा सकता है?

यदि ऐसा है, तो मुझे डिफ़ॉल्ट कोड बिंदु के करीब पते के लिए भी segmentation fault रिटर्न कोड 13 9 के साथ मिलता है। क्यूं कर?

संपादित करें:

मैं प्रश्न अधिक विशिष्ट कर देगा: यदि मैं as program.s -o program.o के साथ इस संकलन

 .text 
     .globl _start  
_start: 
     movl $0x4,%eax  # eax = code for 'write' system call 
     movl $1,%ebx   # ebx = file descriptor to standard output 
     movl $message,%ecx # ecx = pointer to the message 
     movl $13,%edx   # edx = length of the message 
     int $0x80   # make the system call 
     movl $0x0,%ebx  # the status returned by 'exit' 
     movl $0x1,%eax  # eax = code for 'exit' system call 
     int $0x80   # make the system call 
     .data 
     .globl message 
message:   
     .string "Hello world\n" # The message as data 

और फिर इसे स्थिर लिंक ld -N program.o -o program, readelf -l program शो 0x0000000000400078 के साथ पाठ का VirtAddr के रूप में सेगमेंट और 0x400078 प्रवेश बिंदु के रूप में। जब चलाने के लिए, `नमस्ते दुनिया" छपा है।

हालांकि, जब मैं (4 बाइट्स द्वारा पाठ खंड और प्रवेश बिंदु से आगे बढ़), कार्यक्रम killed हो जाएगा। readelf -l के साथ निरीक्षण अब दो अलग हेडर से पता चलता ld -N -e0x400082 -Ttext=0x400082 program.o -o program साथ लिंक करने का प्रयास प्रकार LOAD, 0x0000000000400082 से कम एक और 0x00000000004000b0 में एक के।

जब मैं 0x400086 कोशिश, यह सब काम करता है, और वहाँ केवल एक LOAD खंड है।

  1. यहाँ क्या हो रहा है?
  2. मैं कौन से मेमोरी पते चुन सकता हूं, जिन्हें मैं नहीं चुन सकता और क्यों?

धन्यवाद।

+0

मैं भी लिंकर स्क्रिप्ट के साथ प्रवेश बिंदु को संशोधित करने में सक्षम है: http://stackoverflow.com/a/30536800/895245 –

उत्तर

24

कारण है कि मैं ld ld -e

तुम्हें यकीन कर सकते हैं के साथ डिफ़ॉल्ट से एक अलग प्रवेश बिंदु का उपयोग नहीं कर सकता। यह:

int foo(int argc, char *argv[]) { return 0; } 

gcc main.c -Wl,-e,foo 

काम नहीं करेगा, क्योंकि निष्पादन मुख्य पर शुरू नहीं होता है। यह _start से शुरू होता है, जो crt0.o (glibc का हिस्सा) से जुड़ा हुआ है और ठीक से शुरू करने के लिए गतिशील लिंकिंग इत्यादि जैसी चीजों की व्यवस्था करता है। _start से foo पर रीडायरेक्ट करके, आपने उन सभी आवश्यक ग्लिब प्रारंभिकताओं को छोड़ दिया है, और इसलिए चीजें काम नहीं करती हैं।

लेकिन यदि आपको गतिशील लिंकिंग की आवश्यकता नहीं है, और जो भी ग्लिब सामान्य रूप से आपके लिए करता है, तो आप जो कुछ भी चाहते हैं उसे प्रविष्टि बिंदु का नाम दे सकते हैं। उदाहरण:

#include <syscall.h> 

int foo() 
{ 
    syscall(SYS_write, 1, "Hello, world\n", 13); 
    syscall(SYS_exit, 0); 
} 

gcc t.c -static -nostartfiles -Wl,-e,foo && ./a.out 
Hello, world 

ओह, और इस प्रश्न का आपका शीर्षक आपके वास्तविक प्रश्न (खराब विचार (टीएम) से मेल नहीं खाता है)।

शीर्षक में प्रश्न का उत्तर देने के लिए, आपको यकीन है कि आपके निष्पादन योग्य पते को संबोधित कर सकता है। डिफ़ॉल्ट रूप से, आपको 0x8048000 लोड पता मिलता है (केवल 32-बिट्स में; 64-बिट डिफ़ॉल्ट 0x400000 है)।

आप इसे आसानी से बदल सकते हैं उदा।लिंक लाइन पर -Wl,-Ttext-segment=0x80000 जोड़ कर 0x80000

अद्यतन:

हालांकि, जब मैं (4 बाइट्स द्वारा पाठ खंड और प्रवेश बिंदु से आगे बढ़) ld एन -e0x400082 -Ttext = 0x400082 program.o -ओ कार्यक्रम के साथ लिंक करने के लिए प्रयास करते हैं, कार्यक्रम होगा मारे जाना।

खैर, यह .text खंड संरेखण बाधा उल्लंघन करने के बिना 0x400082 को Ttext आवंटित करने के लिए असंभव है (जो 4 है)। आपको कम से कम 4-बाइट सीमा पर संरेखित .text पता रखना होगा (या .text के आवश्यक संरेखण को बदलें)।

जब मैं प्रारंभ पता 0x400078, 0x40007c, 0x400080, 0x400084, ..., 0x400084, ..., 0x400098 और जीएनयू-एलडी 2.20.1 का उपयोग करता हूं, प्रोग्राम काम करता है।

हालांकि, जब मैं binutils की वर्तमान सीवीएस स्नैपशॉट का उपयोग करें, कार्यक्रम 0x400078, 0x40007c, 0x400088, 0x40008c के लिए काम करता है, और 0x400080, 0x400084, 0x400090, 0x400094, 0x400098 के लिए मार डाला जाता है। यह लिंकर में एक बग हो सकता है, या मैं कुछ अन्य बाधा का उल्लंघन कर रहा हूं (मुझे यह नहीं दिख रहा है)।

इस बिंदु पर

, यदि आप वास्तव में रुचि रखते हैं, मैं, binutils स्रोतों को डाउनलोड करने ld निर्माण, और पता लगाना बिल्कुल का कारण बनता है क्या यह एक के बजाय दो PT_LOAD सेगमेंट बनाने के लिए सुझाव देते हैं।

अद्यतन 2:

फोर्स ओवरलैपिंग LMAs साथ वर्गों के लिए नया खंड।

आह! इसका मतलब है कि आपको रास्ते से .data स्थानांतरित करने की आवश्यकता है। यह एक काम कर निष्पादन योग्य बनाता है:

ld -N -o t t.o -e0x400080 -Ttext=0x400080 -Tdata=0x400180 
+0

मैं मेरे सवाल का एक बेहतर उदाहरण बनाने के लिए अद्यतन अपेक्षित के रूप में काम नहीं करता है। – nh2

+0

धन्यवाद, महान जवाब, मैंने संरेखण पर विचार नहीं किया। – nh2

+0

मैंने बिनटिल्स 2.20 और 2.21 के बीच बदलने के लिए गिट बिसेक्ट का उपयोग किया जो आपके द्वारा वर्णित परिवर्तन को प्रस्तुत करता है। इसे "elf.c (_bfd_elf_map_sections_to_segments) कहा जाता है: एलएमए ओवरलैपिंग वाले अनुभागों के लिए नया सेगमेंट फोर्स करें।" (Http://repo.or.cz/w/binutils.git/commit/278c98e2ff1c95c8ad9579755abda467ea2bc1b4) – nh2

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