2011-02-23 10 views
7

पृष्ठभूमि:यह गतिशील लाइब्रेरी लोडिंग कोड जीसीसी के साथ क्यों काम करता है?

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

समस्या:

गतिशील dlsym() या GetProcAddress के साथ एक सी ++ संकलक द्वारा उत्पन्न प्रतीकों की खोज करने के लिए() वे एक निर्वासन 'सी' लिंकेज ब्लॉक का उपयोग करके unmangled किया जाना चाहिए। उदाहरण के लिए:

#include <list> 
#include <string> 

using std::list; 
using std::string; 

extern "C" { 

    list<string> get_list() 
    { 
     list<string> myList; 
     myList.push_back("list object"); 
     return myList; 
    } 

} 

इस कोड को पूरी तरह से वैध सी ++ और संकलित है और दोनों लिनक्स और विंडोज पर कई compilers पर चलता है। हालांकि, यह एमएसवीसी के साथ संकलित नहीं है क्योंकि "वापसी का प्रकार वैध सी नहीं है"। वैकल्पिक हल हम लेकर आए हैं समारोह को बदलने के लिए सूची वस्तु के स्थान पर सूची में एक सूचक वापस जाने के लिए है:

#include <list> 
#include <string> 

using std::list; 
using std::string; 

extern "C" { 

    list<string>* get_list() 
    { 
     list<string>* myList = new list<string>(); 
     myList->push_back("ptr to list"); 
     return myList; 
    } 

} 

मैं जीएनयू/लिनक्स लोडर के लिए सर्वोत्कृष्ट समाधान खोजने की कोशिश कर रहा है कि या तो दोनों नए कार्यों और पुराने विरासत समारोह प्रोटोटाइप के साथ काम करेंगे या कम से कम पता लगाएंगे जब बहिष्कृत कार्य का सामना करना पड़ता है और चेतावनी जारी होती है। यह हमारे उपयोगकर्ताओं के लिए अनजाने में होगा यदि कोड पुराने पुस्तकालय का उपयोग करने की कोशिश करते समय बस segfaulted। मेरा मूल विचार है कि get_list में कॉल के दौरान एक SIGSEGV सिग्नल हैंडलर सेट करना था (मुझे पता है कि यह icky है - मैं बेहतर विचारों के लिए खुला हूं)। तो बस यह पुष्टि करने के लिए कि पुरानी लाइब्रेरी को लोड करना होगा, जहां मैंने सोचा था कि यह नए लोडिंग कोड (जो सूची में पॉइंटर की अपेक्षा करता है) के माध्यम से पुरानी फ़ंक्शन प्रोटोटाइप (एक सूची ऑब्जेक्ट लौटा रहा है) का उपयोग करके लाइब्रेरी चलाता है और मुझे आश्चर्य होता है बस काम किया मेरे पास सवाल है क्यों?

नीचे लोडिंग कोड ऊपर सूचीबद्ध फ़ंक्शन प्रोटोटाइप दोनों के साथ काम करता है। मैंने पुष्टि की है कि यह जीएसी संस्करण 4.1.2 और 4.4.4 का उपयोग कर फेडोरा 12, रेडहाट 5.5, और रेडहाक 5.1 पर काम करता है। G ++ का उपयोग करके पुस्तकालयों को संकलित करें और शेर और -पीपीआईसी के साथ निष्पादन योग्य आवश्यकताओं को डीएल (-ldl) के विरुद्ध जोड़ा जाना चाहिए।

#include <dlfcn.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <list> 
#include <string> 

using std::list; 
using std::string; 

int main(int argc, char **argv) 
{ 
    void *handle; 
    list<string>* (*getList)(void); 
    char *error; 

    handle = dlopen("library path", RTLD_LAZY); 
    if (!handle) 
    { 
     fprintf(stderr, "%s\n", dlerror()); 
     exit(EXIT_FAILURE); 
    } 

    dlerror(); 

    *(void **) (&getList) = dlsym(handle, "get_list"); 

    if ((error = dlerror()) != NULL) 
    { 
     printf("%s\n", error); 
     exit(EXIT_FAILURE); 
    } 

    list<string>* libList = (*getList)(); 

    for(list<string>::iterator iter = libList->begin(); 
      iter != libList->end(); iter++) 
    { 
     printf("\t%s\n", iter->c_str()); 
    } 

    dlclose(handle); 

    exit(EXIT_SUCCESS); 
} 
+3

क्योंकि आप भाग्यशाली हो गए हैं। मुझे संदेह है कि क्या आपने इस तरह की चीज को एक और जटिल प्रोग्राम के साथ करने की कोशिश की है, तो आप एक धराशायी स्टैक या इसी तरह के प्रभावों को देखना शुरू कर देंगे। – aschepler

+0

मेरे द्वारा पोस्ट किया गया कोड सरलीकृत है। वास्तविक आवेदन कोड की लगभग 100 किलोग्राम रेखाएं है और मैंने कुछ बहुत व्यापक परीक्षण मामलों को चलाया है जो सभी काम करने लगते हैं। हालांकि मैं सहमत हूं, इस काम में जीसीसी के साथ कुछ कर्कश होने तक यह काम नहीं करना चाहिए। – bckohan

+1

मुझे यकीन नहीं है कि 'उन्हें अनमोल किया जाना चाहिए' सच है। यदि आप mangled नाम के लिए dlsym() से पूछते हैं तो यह सही तरीके से नहीं मिलेगा। –

उत्तर

4

जैसा कि एशप्लर कहता है, क्योंकि यह भाग्यशाली है।

जैसा कि यह पता चला है, एबीआई x86 और x64 दोनों के लिए जीसीसी (और अधिकांश अन्य कंपाइलर्स) के लिए उपयोग किया जाता है, जो 'अतिरिक्त' structs (एक रजिस्टर में फिट होने के लिए बहुत बड़ा) देता है, एक अतिरिक्त 'छुपा' पॉइंटर तर्क पास करके फ़ंक्शन, जो उस पॉइंटर को रिटर्न वैल्यू को स्टोर करने के लिए स्पेस के रूप में उपयोग करता है, और उसके बाद पॉइंटर को वापस देता है। तो यह पता चला है कि प्रपत्र

struct foo func(...) 

के एक समारोह लगभग

struct foo *func(..., struct foo *) 

को equivlant है जहां फोन करने वाले एक 'foo' (शायद ढेर पर) के लिए जगह आवंटित करने के लिए और में पारित होने की उम्मीद है इसके लिए एक सूचक।

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

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