2013-05-30 11 views
13

टी एल काम को समझने; डॉप्रसंस्करण ELF relocations - relocs, प्रतीक, खंड डेटा, और कैसे वे एक साथ

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

यहां मेरे विशिष्ट मुद्दे हैं। अधिक जानकारी के लिए नीचे पढ़ें।

  • मैं स्थानांतरण डेटा प्रविष्टियों को संसाधित करने और अनुभाग डेटा में अनसुलझे प्रतीकों को अपडेट करने के बारे में मार्गदर्शन की तलाश में हूं। मैं बस समझ नहीं पा रहा हूं कि मैंने स्थानांतरित करने और अनुभागों, आदि से खींची गई सभी जानकारी के साथ क्या करना है।
  • मैं यह समझने की भी उम्मीद कर रहा हूं कि लिंकर को स्थानांतरित होने पर क्या हो रहा है। स्थानान्तरण समीकरणों को सही ढंग से कार्यान्वित करने और सही तरीके से सभी सही मानों का उपयोग करने का प्रयास करना अविश्वसनीय रूप से चुनौतीपूर्ण है।
  • जब मुझे ओप कोड और पते और प्रतीकों का सामना करना पड़ता है, तो मुझे समझना होगा कि उनके साथ क्या करना है। मुझे लगता है कि मुझे कुछ कदम याद आ रहे हैं।
  • मुझे लगता है कि मुझे इस बात की अच्छी समझ नहीं है कि प्रतीक तालिका प्रविष्टियां स्थानान्तरण के साथ कैसे सहभागिता करती हैं। मुझे प्रतीक की बाध्यकारी, दृश्यता, मूल्य और आकार की जानकारी का उपयोग कैसे करना चाहिए?
  • आखिरकार, जब मैं निष्पादित डेटा द्वारा निष्पादित डेटा और नई स्थानांतरण प्रविष्टियों के साथ अपनी फ़ाइल आउटपुट करता हूं, तो डेटा सभी गलत है। मुझे यकीन नहीं है कि सभी स्थानान्तरणों का पालन कैसे करें और आवश्यक सभी जानकारी प्रदान करें। मेरे द्वारा अपेक्षित निष्पादन योग्य क्या है?

मेरे दृष्टिकोण अब तक

मैं एक विशिष्ट [गैर-दस्तावेजी] मालिकाना प्रारूप है कि भारी ELF पर आधारित है में एक स्थान परिवर्तन फ़ाइल बनाने के लिए कोशिश कर रहा हूँ। मैंने एक ऐसा टूल लिखा है जो ईएलएफ फ़ाइल और आंशिक रूप से जुड़ी फ़ाइल (पीएलएफ) लेता है और पूरी तरह से हल की गई रिलाय फ़ाइल को आउटपुट करने के लिए प्रक्रिया करता है। यह रिला फ़ाइल स्मृति को बचाने के लिए आवश्यकतानुसार डेटा लोड/अनलोड करने के लिए प्रयोग की जाती है। मंच 32 बिट पीपीसी है। एक शिकन यह है कि उपकरण सी # में विंडोज के लिए लिखा गया है, लेकिन डेटा पीपीसी के लिए है, इसलिए मजेदार एंडियन मुद्दे हैं और इसके लिए देखना पसंद है।

मैं यह समझने की कोशिश कर रहा हूं कि अनसुलझे प्रतीकों को हल करने के लिए उपयोग किए जाने पर स्थानांतरण कैसे किया जाता है। मैंने अब तक जो किया है वह संबंधित अनुभागों को पीएलएफ से कॉपी करना है और फिर प्रत्येक संबंधित .rela सेक्शन के लिए, मैं प्रविष्टियों का विश्लेषण करता हूं और अनुभाग डेटा को ठीक करने का प्रयास करता हूं और आवश्यकतानुसार नई स्थानांतरण प्रविष्टियां उत्पन्न करता हूं। लेकिन यह वह जगह है जहां मेरी कठिनाई है। मैं यहां अपने तत्व से बाहर हूं और इस तरह की चीज आमतौर पर लिंकर्स और लोडर्स द्वारा की जाती है, इसलिए आकर्षित करने के लिए बहुत अच्छे उदाहरण नहीं हैं। लेकिन मुझे कुछ ऐसे मिल गए हैं जो THIS ONE सहित मददगार हैं।

तो क्या हो रहा है:

  1. पीएलएफ से कॉपी अनुभाग डेटा rel फ़ाइल के लिए प्रयोग की जाने वाली। मुझे केवल .init (कोई डेटा नहीं), .text, .ctors, .dtors, .rodata, .data, .bss (कोई डेटा नहीं), और एक अन्य कस्टम अनुभाग में हम रुचि रखते हैं।
  2. पीएलएफ में .rela अनुभागों पर Iterate और Elf32_Rela प्रविष्टियों में पढ़ें।
  3. प्रत्येक प्रविष्टि के लिए, मैं r_offset, r_info, और r_addend फ़ील्ड खींचता हूं और r_info (प्रतीक और रिलाक प्रकार) से प्रासंगिक जानकारी निकालता हूं।
  4. पीएलएफ की प्रतीक तालिका से, मैं प्रतीक ऑफसेट, प्रतीक पहचान, और प्रतीक वैल्यू प्राप्त कर सकता हूं।
  5. ईएलएफ से, मुझे प्रतीकसेक्शन का लोड पता मिलता है।
  6. मैं int localAddress = (.relaSection.Offset + r_offset) की गणना करता हूं।
  7. मुझे r_offset पर प्रतीक सेक्शन की सामग्री से uint relocValue मिलता है।
  8. अब मेरे पास आवश्यक सभी जानकारी है इसलिए मैं रिलाक प्रकार पर स्विच करता हूं और डेटा को संसाधित करता हूं। इन प्रकार मैं समर्थन कर रहे हैं:
    R_PPC_NONE
    R_PPC_ADDR32
    R_PPC_ADDR24
    R_PPC_ADDR16
    R_PPC_ADDR16_LO
    R_PPC_ADDR16_HI
    R_PPC_ADDR16_HA
    R_PPC_ADDR14
    R_PPC_ADDR14_BRTAKEN
    R_PPC_ADDR14_BRNTAKEN
    R_PPC_REL24
    R_PPC_REL14
    आर_PPC_REL14_BRTAKEN
    आर_PPC_REL14_BRNTAKEN
  9. अब क्या ?? मुझे सेक्शन डेटा अपडेट करना और साथी स्थानांतरण प्रविष्टियां बनाना है। लेकिन मुझे समझ में नहीं आता कि क्या करना आवश्यक है और इसे कैसे किया जाए।

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

तो कस्टम अनुभागों का समर्थन करने वाले हमारे स्वयं के उपकरण को आदर्श बनाने के दौरान आदर्श है, यदि इस लक्ष्य को प्राप्त करने के किसी अन्य तरीके के लिए कोई उज्ज्वल विचार हैं, तो मैं सभी कान हूं! हमने अपने डेटा के लिए .dtor सेक्शन का उपयोग करने के विचार के चारों ओर तैर लिया है क्योंकि यह वैसे भी लगभग खाली है। लेकिन यह गन्दा है और अगर यह एक साफ शट डाउन रोकता है तो वैसे भी काम नहीं कर सकता है।


Relocations प्लस उदाहरण कोड

जब मैं relocations की प्रक्रिया, मैं समीकरण और जानकारी HERE (खंड 4.13, पेज 80ish के आसपास) ABI डॉक्स में पाया पर कार्य करते है और साथ ही कर रहा हूँ मैंने कई अन्य कोड उदाहरण और ब्लॉग पोस्ट खोले हैं जिन्हें मैंने खोला है। लेकिन यह सब इतना भ्रमित है और वास्तव में वर्तनी नहीं है और मैंने जो भी कोड पाया है वह चीजों को थोड़ा अलग करता है।

उदाहरण के लिए,

  • R_PPC_ADDR16_LO -> half16: #lo (एस + A)
  • R_PPC_ADDR14_BRTAKEN -> low14 *: (एस + A) >> 2
  • आदि

तो जब मैं इस तरह का कोड देखता हूं, तो मैं इसे कैसे समझूं?

यहाँ (this source से) एक उदाहरण

case ELF::R_PPC64_ADDR14 : { 
    assert(((Value + Addend) & 3) == 0); 
    // Preserve the AA/LK bits in the branch instruction 
    uint8_t aalk = *(LocalAddress+3); 
    writeInt16BE(LocalAddress + 2, (aalk & 3) | ((Value + Addend) & 0xfffc)); 
} break; 

case ELF::R_PPC64_REL24 : { 
    uint64_t FinalAddress = (Section.LoadAddress + Offset); 
    int32_t delta = static_cast<int32_t>(Value - FinalAddress + Addend); 
    if (SignExtend32<24>(delta) != delta) 
     llvm_unreachable("Relocation R_PPC64_REL24 overflow"); 
    // Generates a 'bl <address>' instruction 
    writeInt32BE(LocalAddress, 0x48000001 | (delta & 0x03FFFFFC)); 
} break; 

यहाँ एक और उदाहरण से कुछ (here)

case R_PPC_ADDR32: /* word32 S + A */ 
    addr = elf_lookup(lf, symidx, 1); 
    if (addr == 0) 
     return -1; 
    addr += addend; 
    *where = addr; 
    break; 

case R_PPC_ADDR16_LO: /* #lo(S) */ 
    if (addend != 0) { 
     addr = relocbase + addend; 
    } else { 
     addr = elf_lookup(lf, symidx, 1); 
     if (addr == 0) 
      return -1; 
    } 
    *hwhere = addr & 0xffff; 
    break; 

case R_PPC_ADDR16_HA: /* #ha(S) */ 
    if (addend != 0) { 
     addr = relocbase + addend; 
    } else { 
     addr = elf_lookup(lf, symidx, 1); 
     if (addr == 0) 
      return -1; 
    } 
    *hwhere = ((addr >> 16) + ((addr & 0x8000) ? 1 : 0)) & 0xffff; 
    break; 

और एक दूसरी उदाहरण (from here)

case R_PPC_ADDR16_HA: 
    write_be16 (dso, rela->r_offset, (value + 0x8000) >> 16); 
    break; 
case R_PPC_ADDR24: 
    write_be32 (dso, rela->r_offset, (value & 0x03fffffc) | (read_ube32 (dso, rela->r_offset) & 0xfc000003)); 
    break; 
case R_PPC_ADDR14: 
    write_be32 (dso, rela->r_offset, (value & 0xfffc) | (read_ube32 (dso, rela->r_offset) & 0xffff0003)); 
    break; 
case R_PPC_ADDR14_BRTAKEN: 
case R_PPC_ADDR14_BRNTAKEN: 
    write_be32 (dso, rela->r_offset, (value & 0xfffc) 
            | (read_ube32 (dso, rela->r_offset) & 0xffdf0003) 
            | ((((GELF_R_TYPE (rela->r_info) == R_PPC_ADDR14_BRTAKEN) << 21) 
            ^(value >> 10)) & 0x00200000)); 
    break; 
case R_PPC_REL24: 
    write_be32 (dso, rela->r_offset, ((value - rela->r_offset) & 0x03fffffc) | (read_ube32 (dso, rela->r_offset) & 0xfc000003)); 
    break; 
case R_PPC_REL32: 
    write_be32 (dso, rela->r_offset, value - rela->r_offset); 
    break; 

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


प्रतीकों/डेटा/relocations के बाद , आदि

जब मैं एक hexeditor में डेटा को देखो, मैं "48 00 00 01" सब कुछ खत्म हो के एक झुंड को देखते हैं। मुझे पता चला है कि यह एक ऑपोड है और इसे स्थानान्तरण जानकारी के साथ अद्यतन करने की आवश्यकता है (यह विशेष रूप से 'ब्ल' शाखा और लिंक के लिए है), फिर भी मेरा उपकरण उनमें से अधिकांश पर काम नहीं करता है और जो मैं करता हूं अद्यतन में उनमें गलत मान हैं (एक अप्रचलित उपकरण द्वारा किए गए उदाहरण की तुलना में)। स्पष्ट रूप से मैं प्रक्रिया का कुछ हिस्सा याद कर रहा हूँ।

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

यदि आप RuntimeDyldELF::processRelocationRef पर this file के अंत के करीब देखते हैं, तो आपको कुछ रिलायंस प्रविष्टियां बनाई जा रही हैं। वे स्टब फ़ंक्शन भी बनाते हैं। मुझे संदेह है कि यह मेरे लिए लापता लिंक है, लेकिन यह मिट्टी के रूप में स्पष्ट है और मैं इसे थोड़ा सा भी नहीं कर रहा हूं।

जब मैं प्रत्येक स्थानांतरण प्रविष्टि में प्रतीकों को आउटपुट करता हूं, तो उनमें से प्रत्येक के पास बाध्यकारी/दृश्यता [ग्लोबल/कमजोर/स्थानीय] [फ़ंक्शन/ऑब्जेक्ट] और एक मान, आकार और एक अनुभाग होता है। मुझे पता है कि अनुभाग वह स्थान है जहां प्रतीक स्थित है, और मान उस खंड में प्रतीक के लिए ऑफसेट है (या यह आभासी पता है?)। आकार प्रतीक का आकार है, लेकिन यह महत्वपूर्ण है? शायद वैश्विक/कमजोर/स्थानीय यह निर्धारित करने के लिए उपयोगी है कि यह आंतरिक या बाहरी स्थानांतरण है या नहीं?

शायद यह स्थानांतरण तालिका मैं बनाने के बारे में बात कर रहा हूं वास्तव में मेरी रिला फाइल के लिए प्रतीक तालिका है? हो सकता है कि यह तालिका प्रतीक खंड को एक सेक्शन ऑफ़सेट होने के लिए वर्चुअल एड्रेस होने से अपडेट करे (क्योंकि यह वही है जो मूल्य स्थानांतरित करने योग्य फ़ाइलों में है और पीएलएफ में प्रतीक तालिका मूल रूप से निष्पादन योग्य है)?


कुछ संसाधन:

  1. relocations पर ब्लॉग: http://wiki.netbsd.org/examples/elf_executables_for_powerpc/
  2. मेरी संबंधित अनुत्तरित प्रश्न: ELF Relocation reverse engineering

  • अंत में opcodes का उल्लेख है

    Whew! यह एक प्रश्न का एक जानवर है। बधाई हो अगर आपने इसे अभी तक बनाया है। :) किसी भी मदद के लिए अग्रिम धन्यवाद जो आप मुझे दे सकते हैं।

  • +0

    [स्थानांतरण की अवधारणा] के संभावित डुप्लिकेट (http://stackoverflow.com/questions/16385826/concept-of-relocation) विस्तृत न्यूनतम उदाहरण: http://stackoverflow.com/a/30507725/895245 –

    उत्तर

    14

    मैंने इस प्रश्न पर ठोकर खाई और सोचा कि यह एक उत्तर के लायक है।

    elf.h आसान है। आप इसे इंटरनेट पर पा सकते हैं।

    प्रत्येक आरईएलए अनुभाग में आप जानते हैं कि Elf32_Rela प्रविष्टियों की एक सरणी है, लेकिन यह एक निश्चित अन्य खंड से भी जुड़ा हुआ है। r_offset उस दूसरे खंड में ऑफसेट है (इस मामले में - it works differently for shared libraries)। आप पाएंगे कि सेक्शन हेडर के पास sh_info नामक सदस्य है। यह आपको बताता है कि कौन सा अनुभाग है। (यह अनुभाग हेडर तालिका में एक इंडेक्स है जैसा आप उम्मीद करेंगे।)

    'प्रतीक', जिसे आप r_info से प्राप्त करते हैं वास्तव में एक अन्य खंड में रहने वाली प्रतीक तालिका में एक सूचकांक है। अपने आरईएलए अनुभाग के शीर्षलेख में सदस्य sh_link की तलाश करें।

    प्रतीक तालिका आपको उस प्रतीक का नाम बताती है जिसे आप ढूंढ रहे हैं, Elf32_Sym के st_name सदस्य के रूप में। st_name एक स्ट्रिंग सेक्शन में ऑफ़सेट है। कौन सा अनुभाग है, आप अपने प्रतीक तालिका के अनुभाग शीर्षलेख के sh_link सदस्य से प्राप्त करते हैं। क्षमा करें अगर यह भ्रमित हो जाता है।

    Elf32_Shdr *sh_table = elf_image + ((Elf32_Ehdr *)elf_image)->e_shoff; 
    Elf32_Rela *relocs = elf_image + sh_table[relocation_section_index]->sh_offset; 
    
    unsigned section_to_modify_index = sh_table[relocation_section_index].sh_info; 
    char *to_modify = elf_image + sh_table[section_to_modify_index].sh_offset; 
    
    unsigned symbol_table_index = sh_table[relocation_section_index].sh_link; 
    Elf32_Sym *symbol_table = elf_image + sh_table[symbol_table_index].sh_offset; 
    
    unsigned string_table_index = sh_table[symbol_table].sh_link; 
    char *string_table = elf_image + sh_table[string_table_index].sh_offset; 
    

    मान लें कि हम स्थानान्तरण संख्या i के साथ काम कर रहे हैं।

    Elf32_Rela *rel = &relocs[i]; 
    Elf32_Sym *sym = &symbol_table[ELF32_R_SYM(rel->r_info)]; 
    char *symbol_name = string_table + sym->st_name; 
    

    उस प्रतीक का पता लगाएं (मान लें कि symbol_name == "printf")। अंतिम मूल्य (to_modify + rel-> r_offset) में जाएगा।

    आपके द्वारा लिंक किए गए पीडीएफ के पेज 79-83 पर तालिका के लिए, यह हमें बताता है कि उस पते पर क्या रखा जाए, और कितने बाइट लिखने के लिए। जाहिर है कि हमें जो पता मिला है (इस मामले में printf का) उनमें से ज्यादातर का हिस्सा है। यह अभिव्यक्तियों में एस के अनुरूप है।

    r_addend केवल ए है। कभी-कभी संकलक को मुझे लगता है कि एक रिलायंस के लिए एक स्थिर स्थिरता जोड़ने की जरूरत है।

    बी साझा ऑब्जेक्ट का मूल पता है, या निष्पादन योग्य प्रोग्राम के लिए 0 है क्योंकि वे स्थानांतरित नहीं होते हैं।

    तो अगर ELF32_R_TYPE (rel-> r_info) == R_PPC_ADDR32 हम एस + एक है, और शब्द आकार word32 है तो हम मिलेगा:

    *(uint32_t *)(to_modify + rel->r_offset) = address_of_printf + rel->r_addend; 
    

    ... और हम सफलतापूर्वक एक स्थान परिवर्तन प्रदर्शन किया है ।

    #lo, #hi, आदि और कम 14 जैसे शब्द आकारों की बात करते समय मैं आपकी सहायता नहीं कर सकता। मुझे पीपीसी के बारे में कुछ नहीं पता लेकिन लिंक किया गया पीडीएफ काफी उचित लगता है।

    मुझे स्टब कार्यों के बारे में भी पता नहीं है। लिंक करने पर आमतौर पर उन लोगों के बारे में जानने की आवश्यकता नहीं है (गतिशील रूप से कम से कम)।

    मुझे यकीन नहीं है कि मैंने आपके सभी सवालों का जवाब दिया है लेकिन आपको यह देखने में सक्षम होना चाहिए कि आपका उदाहरण कोड कम से कम क्या करता है।

    1

    ईएलएफ विशिष्टता में लॉक करने का प्रयास करें। इसमें लगभग 60 पृष्ठ लगते हैं, और चीजों को बहुत स्पष्ट करते हैं। विशेष रूप से भाग 2, जोड़ने के बारे में एक।

    +0

    या , बेहतर अभी तक, सिस्टम वी एबीआई (दोनों जेनेरिक (जीएबीआई) और आर्किटेक्चर-विशिष्ट पूरक। मुझे उपयोगी मिला: https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI (x86/x86-64) और http://www.sco.com/developers/gabi/ –

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