पता स्थानांतरण न्यूनतम उदाहरण
पता स्थानांतरण लिंकिंग के महत्वपूर्ण कार्यों में से एक है।
तो आइए इस पर एक नज़र डालें कि यह न्यूनतम उदाहरण के साथ कैसे काम करता है।
०) परिचय
सारांश: स्थानांतरण .text
अनुवाद करने के लिए ऑब्जेक्ट फ़ाइलों के अनुभाग को संपादित करता है :
- ऑब्जेक्ट फ़ाइल पता
- निष्पादन योग्य के अंतिम पते में
यह लिंकर द्वारा किया जाना चाहिए क्योंकि कंपाइलर एक समय में केवल एक इनपुट फ़ाइल देखता है, लेकिन हमें यह जानने के लिए कि एक ही बार में सभी ऑब्जेक्ट फ़ाइलों के बारे में पता होना चाहिए:
- घोषित अपरिभाषित कार्यों की तरह अपरिभाषित प्रतीकों को हल करें
- कई टकराव नहीं
.text
है और .data
कई वस्तु फ़ाइलों के वर्गों
आवश्यक शर्तें: न्यूनतम समझ:
- x86-64 या IA-32 विधानसभा
- एक ELF फ़ाइल की वैश्विक संरचना। मैंने उसके लिए एक ट्यूटोरियल बनाया है
लिंकिंग का विशेष रूप से C या C ++ से कोई लेना-देना नहीं है: कंपाइलर केवल ऑब्जेक्ट फ़ाइलों को उत्पन्न करते हैं। लिंकर फिर उन्हें इनपुट के रूप में लेता है, बिना यह जाने कि उन्हें किस भाषा ने संकलित किया है। यह फोरट्रान भी हो सकता है।
तो क्रस्ट को कम करने के लिए, आइए एक NASM x86-64 ELF लिनक्स हेलो दुनिया का अध्ययन करें:
section .data
hello_world db "Hello world!", 10
section .text
global _start
_start:
; sys_write
mov rax, 1
mov rdi, 1
mov rsi, hello_world
mov rdx, 13
syscall
; sys_exit
mov rax, 60
mov rdi, 0
syscall
संकलित और इसके साथ इकट्ठा:
nasm -o hello_world.o hello_world.asm
ld -o hello_world.out hello_world.o
NASM 2.10.09 के साथ।
१) .टेक्स्ट ऑफ़ .ओ
सबसे पहले हम .text
ऑब्जेक्ट फाइल के सेक्शन को डिकंपाइल करते हैं :
objdump -d hello_world.o
जो देता है:
0000000000000000 <_start>:
0: b8 01 00 00 00 mov $0x1,%eax
5: bf 01 00 00 00 mov $0x1,%edi
a: 48 be 00 00 00 00 00 movabs $0x0,%rsi
11: 00 00 00
14: ba 0d 00 00 00 mov $0xd,%edx
19: 0f 05 syscall
1b: b8 3c 00 00 00 mov $0x3c,%eax
20: bf 00 00 00 00 mov $0x0,%edi
25: 0f 05 syscall
महत्वपूर्ण रेखाएँ हैं:
a: 48 be 00 00 00 00 00 movabs $0x0,%rsi
11: 00 00 00
जो rsi
रजिस्टर में हैलो वर्ल्ड स्ट्रिंग के पते को स्थानांतरित करना चाहिए , जो कि लेखन सिस्टम कॉल को पास किया गया है।
लेकिन रुकें! कंपाइलर संभवतः यह जान सकता है कि "Hello world!"
प्रोग्राम लोड होने पर मेमोरी में कहां समाप्त होगा?
खैर, यह नहीं हो सकता है, विशेष रूप से जब हम .o
कई .data
खंडों के साथ फ़ाइलों का एक गुच्छा लिंक करते हैं।
केवल लिंकर ही ऐसा कर सकता है क्योंकि केवल उसके पास ही उन सभी ऑब्जेक्ट फ़ाइलों में होगा।
तो संकलक सिर्फ:
0x0
संकलित आउटपुट पर एक प्लेसहोल्डर मान डालता है
- अच्छे पते के साथ संकलित कोड को संशोधित करने के तरीके के लिंक के लिए कुछ अतिरिक्त जानकारी देता है
यह "अतिरिक्त जानकारी" .rela.text
ऑब्जेक्ट फ़ाइल के अनुभाग में निहित है
2) .rela.text
.rela.text
".text अनुभाग के स्थानांतरण" के लिए खड़ा है।
स्थानांतरण शब्द का उपयोग किया जाता है क्योंकि लिंक करने वाले को ऑब्जेक्ट से निष्पादन योग्य में पता स्थानांतरित करना होगा।
हम इस .rela.text
अनुभाग को अलग कर सकते हैं :
readelf -r hello_world.o
जिसमें है;
Relocation section '.rela.text' at offset 0x340 contains 1 entries:
Offset Info Type Sym. Value Sym. Name + Addend
00000000000c 000200000001 R_X86_64_64 0000000000000000 .data + 0
इस सेक्शन का प्रारूप तय किया गया है: http://www.sco.com/developers/gabi/2003-12-17/ch4.relp.html
प्रत्येक प्रविष्टि लिंकर को एक पते के बारे में बताती है जिसे स्थानांतरित करने की आवश्यकता होती है, यहां हमारे पास स्ट्रिंग के लिए केवल एक है।
थोड़ा सा सरलीकरण, इस विशेष पंक्ति के लिए हमारे पास निम्नलिखित जानकारी है:
Offset = C
: .text
इस प्रविष्टि के पहले बाइट में क्या बदलाव होता है।
यदि हम विघटित पाठ को वापस देखते हैं, तो यह वास्तव में महत्वपूर्ण के अंदर है movabs $0x0,%rsi
, और जो x86-64 निर्देश एन्कोडिंग को जानते हैं वे ध्यान देंगे कि यह निर्देश का 64-बिट पता भाग को एन्कोड करता है।
Name = .data
: पता .data
अनुभाग को इंगित करता है
Type = R_X86_64_64
, जो निर्दिष्ट करता है कि पते का अनुवाद करने के लिए वास्तव में क्या गणना की जानी है।
यह क्षेत्र वास्तव में प्रोसेसर पर निर्भर है, और इस प्रकार AMD64 सिस्टम V ABI एक्सटेंशन पर प्रलेखित है खंड 4.4 "पुनर्वास" ।
यह दस्तावेज़ कहता है कि R_X86_64_64
यह करता है:
Field = word64
: 8 बाइट्स, इस प्रकार 00 00 00 00 00 00 00 00
पते पर0xC
Calculation = S + A
S
है मूल्य पते पर दूसरी जगह किया जा रहा है, इस प्रकार00 00 00 00 00 00 00 00
A
जो 0
यहाँ है परिशिष्ट है । यह स्थानांतरण प्रविष्टि का एक क्षेत्र है।
तो S + A == 0
और हम .data
खंड के पहले पते पर स्थानांतरित हो जाएंगे ।
3) .Txt के बाहर
अब ld
हमारे लिए तैयार निष्पादन योग्य के पाठ क्षेत्र को देखें :
objdump -d hello_world.out
देता है:
00000000004000b0 <_start>:
4000b0: b8 01 00 00 00 mov $0x1,%eax
4000b5: bf 01 00 00 00 mov $0x1,%edi
4000ba: 48 be d8 00 60 00 00 movabs $0x6000d8,%rsi
4000c1: 00 00 00
4000c4: ba 0d 00 00 00 mov $0xd,%edx
4000c9: 0f 05 syscall
4000cb: b8 3c 00 00 00 mov $0x3c,%eax
4000d0: bf 00 00 00 00 mov $0x0,%edi
4000d5: 0f 05 syscall
तो केवल एक चीज जो ऑब्जेक्ट फ़ाइल से बदल जाती है, वह है महत्वपूर्ण लाइनें:
4000ba: 48 be d8 00 60 00 00 movabs $0x6000d8,%rsi
4000c1: 00 00 00
जो अब पते 0x6000d8
( d8 00 60 00 00 00 00 00
छोटे-से-अंत में) के बजाय इंगित करता है0x0
।
क्या यह सही स्थान है hello_world
स्ट्रिंग के ?
यह तय करने के लिए हमें प्रोग्राम हेडर की जांच करनी होगी, जो लिनक्स को बताएगा कि प्रत्येक सेक्शन को कहां लोड करना है।
हम उन्हें अलग करते हैं:
readelf -l hello_world.out
जो देता है:
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000000d7 0x00000000000000d7 R E 200000
LOAD 0x00000000000000d8 0x00000000006000d8 0x00000000006000d8
0x000000000000000d 0x000000000000000d RW 200000
Section to Segment mapping:
Segment Sections...
00 .text
01 .data
यह हमें बताता है कि .data
अनुभाग, जो दूसरा है, VirtAddr
= पर शुरू होता है0x06000d8
।
और डेटा अनुभाग पर केवल एक चीज हमारी हैलो वर्ल्ड स्ट्रिंग है।
बोनस स्तर