सी

2009-03-06 13 views
58

में फ़ंक्शन कॉल को ओवरराइड करें, मैं कॉल को लॉग इन करने के लिए विभिन्न एपीआई को कुछ फ़ंक्शन कॉल ओवरराइड करना चाहता हूं, लेकिन मैं वास्तविक फ़ंक्शन पर भेजने से पहले डेटा को हेरफेर करना चाहता हूं।सी

उदाहरण के लिए, मैं अपने स्रोत कोड में getObjectName हजारों बार नामक फ़ंक्शन का उपयोग करता हूं। मैं कभी-कभी इस फ़ंक्शन को अस्थायी रूप से ओवरराइड करना चाहता हूं क्योंकि मैं अलग-अलग परिणाम देखने के लिए इस फ़ंक्शन के व्यवहार को बदलना चाहता हूं।

मैं इस तरह एक नया स्रोत फ़ाइल बनाने:

#include <apiheader.h>  

const char *getObjectName (object *anObject) 
{ 
    if (anObject == NULL) 
     return "(null)"; 
    else 
     return "name should be here"; 
} 

मैं के रूप में मैं आमतौर पर करते हैं मेरे सभी अन्य स्रोत संकलन है, लेकिन मैं पहले एपीआई पुस्तकालय के साथ जोड़ने से पहले इस समारोह के खिलाफ लिंक करें। यह ठीक काम करता है सिवाय इसके कि मैं स्पष्ट रूप से अपने ओवरराइडिंग फ़ंक्शन के अंदर वास्तविक फ़ंक्शन को कॉल नहीं कर सकता।

क्या त्रुटियों/चेतावनियों को जोड़ने/संकलित किए बिना फ़ंक्शन को "ओवरराइड" करने का कोई आसान तरीका है? आदर्श रूप से मैं लिंकिंग विकल्पों के साथ चारों ओर झुकाव के बजाय या मेरे प्रोग्राम के वास्तविक स्रोत कोड को बदलने के बजाय बस एक अतिरिक्त फ़ाइल या दो को संकलित और लिंक करके फ़ंक्शन को ओवरराइड करने में सक्षम होना चाहता हूं। में

#ifdef INTERCEPT 
    #define getObjectName(x) myGetObectName(x) 
#endif 

और इस प्रकार समारोह को लागू (:

+0

@dreamlax, अब हम विशिष्ट (जीसीसी/लिनक्स) समाधान करने के लिए सामान्य (सी) से आगे बढ़ रहे है - यह एक अच्छा विचार होगा कि तुम क्या इतनी के रूप में सही उत्तर को लक्षित करने के चला रहे हैं स्पष्ट करने के लिए । – paxdiablo

+1

वैसे मैं लिनक्स पर विकास कर रहा हूं लेकिन लक्ष्य मैक ओएस, लिनक्स और विंडोज हैं। असल में मैं उन कारणों में से एक को ओवरराइड करना चाहता हूं क्योंकि मुझे संदेह है कि वे विभिन्न ऑपरेटिंग सिस्टम पर अलग-अलग व्यवहार करते हैं। – dreamlax

उत्तर

61

यदि यह अपने स्रोत है कि आप पर कब्जा/कॉल संशोधित करना चाहते हैं केवल है, सबसे आसान समाधान एक साथ के साथ एक हेडर फाइल (intercept.h) डाल करने के लिए है intercept.c जो करता intercept.h शामिल नहीं):

जहां कॉल है रोकना चाहते
const char *myGetObjectName (object *anObject) { 
    if (anObject == NULL) 
     return "(null)"; 
    else 
     return getObjectName(anObject); 
} 

तो सुनिश्चित करें कि प्रत्येक स्रोत फ़ाइल बनाने:

#include "intercept.h" 

शीर्ष पर।

फिर, जब आप "-DINTERCEPT" के साथ संकलित करते हैं, तो सभी फ़ाइलें वास्तविक कार्य की बजाय आपके फ़ंक्शन को कॉल करेंगी और आपका फ़ंक्शन अभी भी असली कॉल कर सकता है।

"-DINTERCEPT" के बिना संकलन अवरोध को होने से रोक देगा।

यदि आप सभी कॉल (केवल आपके स्रोत से नहीं) को अवरुद्ध करना चाहते हैं तो यह थोड़ा सा ट्रिकियर है - यह आमतौर पर वास्तविक फ़ंक्शन के गतिशील लोडिंग और रिज़ॉल्यूशन के साथ किया जा सकता है (dlload- और dlsym- टाइप कॉल के साथ) लेकिन मैं ' टी लगता है कि यह आपके मामले में जरूरी है।

+0

परिभाषा का उपयोग करना एक वास्तविक बहुलक उत्तर नहीं है, लेकिन मैं सरलता के उपयोग के लिए सहमत हूं: पी – Suroot

+0

धन्यवाद, यह वास्तव में एक अच्छा विचार है, लेकिन जैसा कि मैंने कहा था कि मैं कोशिश करना चाहता हूं और स्रोत कोड को संशोधित करने से बचाना चाहता हूं। अगर मुझे कोई और रास्ता नहीं मिल रहा है तो मुझे ऐसा लगता है कि मुझे ऐसा लगता है। इसे अवरोध को बंद करना आसान होना चाहिए। – dreamlax

+1

अवरोध को नियंत्रित करने के लिए संकलन-समय ध्वज का उपयोग करें (अद्यतन उत्तर देखें)। दोबारा, यह रनटाइम पर भी किया जा सकता है, आपको बस इसे myGetObjectName() में पहचानने की आवश्यकता है और रनटाइम ध्वज सेट होने पर हमेशा getObjectName पर कॉल करें (यानी, अभी भी अवरोध करें लेकिन व्यवहार बदलें)। – paxdiablo

3

दो स्टब पुस्तकालयों से जुड़े लिंकर में ऐसा करने का एक मुश्किल तरीका भी है।

लाइब्रेरी # 1 होस्ट लाइब्रेरी के खिलाफ जुड़ा हुआ है और दूसरे नाम के तहत प्रतीक को फिर से परिभाषित किया गया है।

लाइब्रेरी # 2 लाइब्रेरी # 1 के खिलाफ जुड़ा हुआ है, कॉल को पूर्ण करने और लाइब्रेरी # 1 में पुनर्नवीनीकरण संस्करण को कॉल करने के लिए जुड़ा हुआ है।

यहां लिंक ऑर्डर के साथ बहुत सावधान रहें या यह काम नहीं करेगा।

+0

मुश्किल लगता है, लेकिन यह स्रोत को संशोधित करने से बचता है। बहुत अच्छा सुझाव। – dreamlax

+0

मुझे नहीं लगता कि आप getObjectName को dlopen/dlsym trickery के बिना किसी विशिष्ट लाइब्रेरी पर जाने के लिए मजबूर कर सकते हैं। – paxdiablo

+0

होस्ट लाइब्रेरी में ड्रैग किए गए किसी भी लिंक-टाइम ऑपरेशन के परिणामस्वरूप एक गुणा-परिभाषित प्रतीक होगा। – paxdiablo

7

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

आप कॉल करने के स्रोत कोड संशोधित करने में सक्षम होने की आवश्यकता होगी, लेकिन नहीं कॉल प्राप्त करने वाला (तो यह जब तीसरे पक्ष के पुस्तकालयों बुला काम करेगा)।

foo.h:

typedef const char* (*GetObjectNameFuncPtr)(object *anObject); 
extern GetObjectNameFuncPtr GetObjectName; 

foo.cpp:

const char* GetObjectName_real(object *anObject) 
{ 
    return "object name"; 
} 

const char* GetObjectName_logging(object *anObject) 
{ 
    if (anObject == null) 
     return "(null)"; 
    else 
     return GetObjectName_real(anObject); 
} 

GetObjectNameFuncPtr GetObjectName = GetObjectName_real; 

void main() 
{ 
    GetObjectName(NULL); // calls GetObjectName_real(); 

    if (isLoggingEnabled) 
     GetObjectName = GetObjectName_logging; 

    GetObjectName(NULL); // calls GetObjectName_logging(); 
} 
+0

मैंने इस विधि को माना है लेकिन इसे स्रोत कोड को संशोधित करने की आवश्यकता है, कुछ ऐसा जो मैं वास्तव में करना नहीं चाहता हूं जब तक मुझे करना नहीं है। हालांकि इसमें रन-टाइम के दौरान स्विचिंग का अतिरिक्त लाभ है। – dreamlax

21

आप जीसीसी का उपयोग करते हैं, तो आप अपने समारोह weak बना सकते हैं। गैर कमजोर कार्यों द्वारा उन can be overridden:

test.c:

#include <stdio.h> 

__attribute__((weak)) void test(void) { 
    printf("not overridden!\n"); 
} 

int main() { 
    test(); 
} 

यह क्या करता है?

$ gcc test.c 
$ ./a.out 
not overridden! 

test1.c:

#include <stdio.h> 

void test(void) { 
    printf("overridden!\n"); 
} 

यह क्या करता है?

$ gcc test1.c test.c 
$ ./a.out 
overridden! 

अफसोस की बात है कि यह अन्य कंपाइलरों के लिए काम नहीं करेगा। लेकिन तुम कमजोर घोषणाओं है कि उनके अपने फ़ाइल में overridable कार्य शामिल हो सकते हैं, रखकर सिर्फ एक API कार्यान्वयन फ़ाइलों में शामिल करता है, तो आप जीसीसी का उपयोग कर संकलन कर रहे हैं:

weakdecls.h:

__attribute__((weak)) void test(void); 
... other weak function declarations ... 

functions.c:

/* for GCC, these will become weak definitions */ 
#ifdef __GNUC__ 
#include "weakdecls.h" 
#endif 

void test(void) { 
    ... 
} 

... other functions ... 

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

+1

कि एपीआई को संशोधित करने की आवश्यकता होगी, है ना? – dreamlax

+0

आपका फ़ंक्शन नाम वही होगा। एबीआई या एपीआई को किसी भी तरह से नहीं बदलेगा। लिंक करते समय ओवरराइडिंग फ़ाइल को शामिल करें, और गैर-कमजोर फ़ंक्शन पर कॉल किए जाएंगे। libc/pthread कि चाल कार्य करें: जब pthread में जुड़ा हुआ है, इसकी threadsafe कार्यों libc के लिए एक कड़ी कमजोर एक –

+0

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

67
जीसीसी के साथ

, लिनक्स के तहत आप इस तरह --wrap लिंकर ध्वज का उपयोग कर सकते हैं:।

gcc program.c -Wl,-wrap,getObjectName -o program 

और अपने समारोह को परिभाषित के रूप में:

const char *__wrap_getObjectName (object *anObject) 
{ 
    if (anObject == NULL) 
     return "(null)"; 
    else 
     return __real_getObjectName(anObject); // call the real function 
} 

यह सुनिश्चित होगा कि getObjectName() के लिए सभी कॉल (लिंक समय में) अपने आवरण समारोह पर भेज रहे हैं। मैक ओएस एक्स

extern "C" के साथ रैपर फ़ंक्शन घोषित करना याद रखें यदि आप g ++ के साथ संकलित कर रहे हैं, तो यह बहुत उपयोगी ध्वज जीसीसी में अनुपस्थित है।

+3

यह एक अच्छा तरीका है। उस बारे में नहीं पता था। लेकिन अगर मैं मैनपेज सही पढ़ रहा हूं, तो यह "__real_getObjectName (anObject) होना चाहिए;" जिसे लिंकर द्वारा ऑब्जेक्टनाम प्राप्त करने के लिए भेजा जाता है। अन्यथा आप फिर से __wrap_getObjectName को कॉल करेंगे। या क्या मैं कुछ न कुछ भूल रहा हूं? –

+0

आपको सही है कि इसे __real_getObjectName होना चाहिए, धन्यवाद। मुझे मैन पेज में दो बार चेक करना चाहिए :) – codelogic

+1

मुझे निराश है कि मैक ओएस एक्स पर एलडी '- वाष्प' ध्वज का समर्थन नहीं करता है। मैक ओएस एक्स पर –

0

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

मैंने अतीत में एक समान काम किया है (जो हासिल करने की कोशिश कर रहे हैं उसे प्राप्त करने के लिए नहीं, लेकिन मूल आधार समान है) और यह अच्छी तरह से काम करता है। क्योंकि मैं संदिग्ध ये अलग-अलग अलग ऑपरेटिंग सिस्टम पर व्यवहार करते हैं

[संपादित करें ओपी टिप्पणी के आधार पर]

वास्तव में कारणों मैं ओवरराइड कार्यों करना चाहते हैं में से एक है।

उस से निपटने के दो सामान्य तरीके (जिन्हें मैं जानता हूं) साझा किया गया है, साझा lib/dll रास्ता या विभिन्न कार्यान्वयन लिखना जो आप लिंक करते हैं।

दोनों समाधानों (साझा libs या अलग-अलग लिंकिंग) के लिए आपके पास foo_linux.c, foo_osx.c, foo_win32.c होगा (या बेहतर तरीका linux/foo.c, osx/foo.c और win32/foo है। सी) और फिर उचित एक के साथ संकलित और लिंक।

यदि आप विभिन्न प्लेटफार्मों और डीबग-वीएस रिलीज के लिए अलग-अलग कोड दोनों की तलाश में हैं, तो शायद मैं साझा lib/DLL समाधान के साथ जाने के इच्छुक हूं क्योंकि यह सबसे लचीला है।

34

आप LD_PRELOAD चाल का उपयोग कर फ़ंक्शन को ओवरराइड कर सकते हैं - man ld.so देखें। आप अपने फ़ंक्शन के साथ साझा lib को संकलित करते हैं और बाइनरी शुरू करते हैं (आपको बाइनरी को संशोधित करने की आवश्यकता नहीं है!) जैसे LD_PRELOAD=mylib.so myprog

अपने समारोह (साझा lib में) के शरीर में आप इस प्रकार लिख:

const char *getObjectName (object *anObject) { 
    static char * (*func)(); 

    if(!func) 
    func = (char *(*)()) dlsym(RTLD_NEXT, "getObjectName"); 
    printf("Overridden!\n");  
    return(func(anObject)); // call original function 
} 

आप किसी भी समारोह साझा लाइब्रेरी से, stdlib से भी, संशोधित किए बिना/कार्यक्रम फिर कंपाइल है, तो आप कर सकते थे ओवरराइड कर सकते हैं उन कार्यक्रमों पर चाल करें जिनके लिए आपके पास स्रोत नहीं है। क्या यह अच्छा नहीं है?

+2

नहीं, आप केवल प्रदान की एक समारोह ओवरराइड कर सकते हैं एक ** साझा लाइब्रेरी ** इस तरह से। –

+2

@ChrisStratton आप सही, syscall इस तरह से ओवरराइड नहीं किया जा सकता कर रहे हैं द्वारा, मैं साझा करने के लिए क्या यह पीडीएफ हो सकता है मेरा उत्तर। – qrdl

9

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

यह एक महान पीडीएफ है जिसमें यह ओएस एक्स, लिनक्स और विंडोज पर किया गया था।

इसमें कोई अद्भुत चाल नहीं है जिसे यहां दस्तावेज नहीं किया गया है (यह बीटीडब्लू प्रतिक्रियाओं का एक अद्भुत सेट है) ... लेकिन यह एक अच्छा पढ़ा है।

http://wwwold.cs.umd.edu/Library/TRs/CS-TR-4585/CS-TR-4585.pdf

+0

देखभाल संपादित किया है? – HanClinto

+0

lattice.umiacs.umd.edu/ फाइलें/functions_tr.pdf - लिंक जोड़ा गया –

+0

लिंक मर चुका है, – paxdiablo

1

कोड आपकी नहीं है के लिए उपयुक्त एक समाधान के साथ @Johannes Schaub के जवाब पर बिल्डिंग।

उपनाम जो आप कमजोर परिभाषित फ़ंक्शन को ओवरराइड करना चाहते हैं, और उसके बाद इसे स्वयं कार्यान्वित करें।

override.h

#define foo(x) __attribute__((weak))foo(x) 

foo.c

function foo() { return 1234; } 

override.c

function foo() { return 5678; } 

उपयोगआपके मेकफ़ाइल मेंकंपाइलर ध्वज -include override.h जोड़ने के लिए।

%foo.o: ALL_CFLAGS += -include override.h 

एक तरफ: शायद आप भी -D 'foo(x) __attribute__((weak))foo(x)' का उपयोग अपने मैक्रो निर्धारित करने का हो सकता है।

फ़ाइल को अपने पुनर्मूल्यांकन (override.c) से संकलित और लिंक करें।

  • इससे आपको कोड को संशोधित किए बिना किसी भी स्रोत फ़ाइल से एक ही फ़ंक्शन को ओवरराइड करने की अनुमति मिलती है।

  • नकारात्मकता यह है कि आपको प्रत्येक फ़ाइल के लिए एक अलग हेडर फ़ाइल का उपयोग करना होगा जिसे आप ओवरराइड करना चाहते हैं।