6

पृष्ठभूमि

हाल ही में, मेरा एक सहयोगी एक समस्या है, जहां एक पुस्तकालय के लिए एक हेडर फाइल के एक पुराने संस्करण इस्तेमाल किया गया था में भाग गया। नतीजा यह था कि वर्चुअल फ़ंक्शन को कॉल करने के लिए जेनरेट किया गया कोड C++ में वर्चुअल फ़ंक्शन लुकअप टेबल में गलत ऑफ़सेट को संदर्भित करता है (vtable)।संकलन समय स्थिर प्रकार की जांच ++

दुर्भाग्यवश, यह त्रुटि संकलन के दौरान पकड़ी नहीं गई थी।

प्रश्न

सभी साधारण कार्यों उनके घायल नाम जो सही समारोह (सही अधिभार संस्करण सहित) को सुनिश्चित करती जुड़े हुए हैं लिंकर द्वारा चुना जाता है। इसी तरह, कोई कल्पना कर सकता है कि एक ऑब्जेक्ट फ़ाइल या लाइब्रेरी में C12+ वर्ग के लिए vtable में फ़ंक्शंस के बारे में प्रतीकात्मक जानकारी हो सकती है।

क्या सी ++ कंपाइलर (g++ या विजुअल स्टूडियो कहें) को लिंक करने के दौरान वर्चुअल फ़ंक्शन पर चेक कॉल करने का कोई तरीका है?

उदाहरण

यहां एक साधारण परीक्षण उदाहरण है।

Base.hpp:

#ifndef BASE_HPP 
#define BASE_HPP 

namespace Test 
{ 
    class Base 
    { 
    public: 
    virtual int f() const = 0; 
    virtual int g() const = 0; 
    virtual int h() const = 0; 
    }; 

    class BaseFactory 
    { 
    public: 
    static const Base* createBase(); 
    }; 
} 

#endif 

Derived.cpp: इस सरल हेडर फाइल और संबद्ध कार्यान्वयन कल्पना कीजिए

#include "Base.hpp" 

#include <iostream> 

using namespace std; 

namespace Test 
{ 
    class Derived : public Base 
    { 
    public: 
    virtual int f() const 
    { 
     cout << "Derived::f()" << endl; 
     return 1; 
    } 

    virtual int g() const 
    { 
     cout << "Derived::g()" << endl; 
     return 2; 
    } 

    virtual int h() const 
    { 
     cout << "Derived::h()" << endl; 
     return 3; 
    } 
    }; 

    const Base* BaseFactory::createBase() 
    { 
    return new Derived(); 
    } 

} 

अब, कल्पना एक कार्यक्रम एक गलत उपयोग करता है/हेडर फ़ाइल का पुराना संस्करण जहां मध्य में वर्चुअल फ़ंक्शन गुम है:

BaseWrong.hpp

#ifndef BASEWRONG_HPP 
#define BASEWRONG_HPP 

namespace Test 
{ 
    class Base 
    { 
    public: 
    virtual int f() const = 0; 
    // Missing: virtual int g() const = 0; 
    virtual int h() const = 0; 
    }; 

    class BaseFactory 
    { 
    public: 
    static const Base* createBase(); 
    }; 
} 

#endif 

तो यहां हम मुख्य कार्यक्रम है:

main.cpp

// Including the _wrong_ version of the header! 
#include "BaseWrong.hpp" 

#include <iostream> 

using namespace std; 

int main() 
{ 
    const Test::Base* base = Test::BaseFactory::createBase(); 
    const int fres = base->f(); 
    cout << "f() returned: " << fres << endl; 
    const int hres = base->h(); 
    cout << "h() returned: " << hres << endl; 
    return 0; 
} 

जब हम "पुस्तकालय" सही हेडर का उपयोग कर संकलन और उसके बाद गलत शीर्षलेख का उपयोग कर मुख्य प्रोग्राम संकलित और लिंक करें ...

$ g++ -c Derived.cpp 
$ g++ Main.cpp Derived.o -o Main 

... तो h() करने के लिए आभासी कॉल vtable में गलत सूचकांक तो कॉल वास्तव में g() को जाता है का उपयोग करता है:

$ ./Main 
Derived::f() 
f() returned: 1 
Derived::g() 
h() returned: 2 

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

+2

ओडीआर उल्लंघन आम तौर पर निदान नहीं हैं। यही कारण है कि मानक को उनके लिए निदान की आवश्यकता नहीं होती है, और यही कारण है कि वे इतनी भयानक विफलता मोड हैं। –

+1

आपके शामिल गार्ड अवैध हैं। http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-a-c-identifier –

+0

@BaummitAugen फिक्स्ड। – mgd

उत्तर

0

अपने प्रश्न का संक्षिप्त उत्तर है नहीं। मौलिक समस्या यह है कि ऑफसेट को फ़ंक्शन कहा जाता है, संकलन-समय पर गणना की जाती है; इसलिए अगर आपके फोन करने वाले कोड (गलत) हेडर फाइल है कि शामिल "आभासी पूर्णांक ग्राम() स्थिरांक" के साथ संकलित किया गया है, तो अपने main.o ज() जी की उपस्थिति की भरपाई के सभी संदर्भ होगा()। लेकिन अपने पुस्तकालय सही हेडर फाइल के साथ संकलित किया गया है, इसलिए कोई समारोह ग्राम(), इस प्रकार Derived.o में ज() की भरपाई main.o में से अलग होगी

यह typechecking की बात नहीं है आभासी कार्यों को कॉल करता है - यह इस तथ्य के आधार पर एक "सीमा" है कि सी ++ कंपाइलर संकलन-समय कार्य ऑफ़सेट गणना करता है, और रनटाइम नहीं करता है।

आप इस समस्या को हल करने के बजाय स्थिर यह जोड़ने की, प्रत्यक्ष फ़ंक्शन कॉल के बजाय dl_open का उपयोग कर और गतिशील रूप से अपने पुस्तकालय को जोड़ने के द्वारा प्राप्त करने में सक्षम हो सकता है।

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