5

मैं एक "कोड इंजेक्टर कक्षा" को लागू करने रहा हूँ, उस विधि swizzling के माध्यम से आप इस तरह कुछ करने के लिए संभावना दे सकते हैं:चर तर्क के साथ एक विधि Swizzling और संदेश को अग्रेषित - बुरा पहुँच

FLCodeInjector *injector = [FLCodeInjector injectorForClass:[self class]]; 
[injector injectCodeBeforeSelector:@selector(aSelector:) code:^{ 
    NSLog(@"This code should be injected"); 
}]; 

aSelector तर्कों की परिवर्तनीय संख्या के साथ एक विधि हो सकती है, और परिवर्तनीय वापसी प्रकार। तर्क/और वापसी प्रकार वस्तुओं या आदिम प्रकार हो सकता है।

पहले, मैं तुम्हें समझ में मैं क्या कर रहा हूँ जाने के लिए injectCodeBeforeSelector: का कोड देते हैं (मैं कोड के दिलचस्प भागों नहीं निकाला गया):

- (void)injectCodeBeforeSelector:(SEL)method code:(void (^)())completionBlock 
{ 

    NSString *selector = NSStringFromSelector(method); 

    [self.dictionaryOfBlocks setObject:completionBlock forKey:selector]; 

    NSString *swizzleSelector = [NSString stringWithFormat:@"SWZ%@", selector]; 

    // add a new method to the swizzled class 
    Method origMethod = class_getInstanceMethod(self.mainClass, NSSelectorFromString(selector)); 
    const char *encoding = method_getTypeEncoding(origMethod); 

    [self addSelector:NSSelectorFromString(swizzleSelector) toClass:self.mainClass methodTypeEncoding:encoding]; 
    SwizzleMe(self.mainClass, NSSelectorFromString(selector), NSSelectorFromString(swizzleSelector)); 

} 

-(void)addSelector:(SEL)selector toClass:(Class)aClass methodTypeEncoding:(const char *)encoding 
{ 
    class_addMethod(aClass, 
        selector, 
        (IMP)genericFunction, encoding); 
} 

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

id genericFunction(id self, SEL cmd, ...) { 
    // call the block to inject 
    ... 
    // now forward the message to the right method, since method are swizzled 
    // I need to forward to the "fake" selector SWZxxx 

    NSString *actualSelector = NSStringFromSelector(cmd); 
    NSString *newSelector = [NSString stringWithFormat:@"SWZ%@", actualSelector]; 
    SEL finalSelector = NSSelectorFromString(newSelector); 

    // forward the argument list 
    va_list arguments; 
    va_start (arguments, cmd); 

    return objc_msgSend(self, finalSelector, arguments); 
} 

अब समस्या: मैं अंतिम पंक्ति पर एक EXC_BAD_INSTRUCTION (objc_msgSend_corrupt_cache_error()) है। समस्या तब होती है जब मैं नकली चयनकर्ता पर va_list तर्क को अग्रेषित करता हूं। अगर मैं

return objc_msgSend(self, cmd, arguments); 

के अंतिम पंक्ति कोई त्रुटि है, लेकिन स्पष्ट रूप से एक अनंत प्रत्यावर्तन शुरू होता है बदल जाते हैं।

मैं करने के लिए कोशिश की है:

  • उपयोग va_copy
  • संदेश

लेकिन कोई परिणाम नहीं भेजने से पहले एक प्रकार का शराबी को हटा दें। मुझे लगता है कि समस्या इस तथ्य से संबंधित है: va_list एक साधारण सूचक नहीं है, यह विधि के स्टैक पते के सापेक्ष ऑफसेट के समान कुछ हो सकता है। इसलिए, मैं किसी अन्य फ़ंक्शन (गैर-स्विस वाले) की तर्क सूची के साथ एक फ़ंक्शन (swizzled फ़ंक्शन) के objc_msgsend को कॉल नहीं कर सकता।

मैंने दृष्टिकोण बदलने और एनएसआईएनवोकेशन में सभी तर्कों की प्रतिलिपि बनाने की कोशिश की, लेकिन मुझे आमंत्रण के वापसी मूल्य को प्रबंधित करने में अन्य समस्याएं थीं, और यहां तक ​​कि एक-एक करके तर्कों की प्रतिलिपि बनाना (सभी अलग-अलग प्रकारों के प्रबंधन) के लिए बहुत सारे कोड की आवश्यकता होती है, इसलिए मैं इस दृष्टिकोण पर लौटना पसंद करता हूं, जो मुझे क्लीनर लगता है (imho)

क्या आपके पास कोई सुझाव है? धन्यवाद

उत्तर

3

मुख्य समस्या यहाँ कैसे चर रहा है तर्क समारोह में पास कर दिए जाते हैं।

आमतौर पर, वे ढेर पर पारित होते हैं, लेकिन जहां तक ​​मुझे पता है, यह एआरएम एबीआई के मामले में नहीं है, जहां कम से कम संभव हो, रजिस्टरों का उपयोग किया जाता है।

तो आपके पास यहां दो समस्याएं हैं।
सबसे पहले, संकलक आपकी रजिस्ट्रार को गड़बड़ कर सकता है, जबकि आपकी अपनी विधि के कोड को निष्पादित करता है।
मुझे इस बारे में निश्चित नहीं है, क्योंकि मुझे एआरएम एबीआई के बारे में ज्यादा जानकारी नहीं है, इसलिए आपको संदर्भ में जांच करनी चाहिए।

दूसरा मुद्दा, अधिक महत्वपूर्ण, आप वास्तव में obj_msgSend (va_list) पर एक एकल चर तर्क दे रहे हैं। तो लक्ष्य विधि स्पष्ट रूप से प्राप्त होने वाली चीज़ों को प्राप्त नहीं करेगी।

कल्पना कीजिए निम्नलिखित:

void bar(int x, ...) 
{} 

void foo(void) 
{ 
    bar(1, 2, 3, 4); 
} 

हाथ पर, इसका मतलब यह होगा, foo समारोह के लिए:

movs r0, #1 
movt r0, #0 
movs r1, #2 
movt r1, #0 
movs r2, #3 
movt r2, #0 
movs r3, #4 
movt r3, #0 
bl  _bar 

चर तर्क R1, R2 और R3, और में int तर्क में पारित कर रहे हैं R0

तो आपके मामले में, के रूप में objc_msdSend के लिए एक कॉल अपने विधि कॉल करने के लिए उपयोग किया गया, R0 लक्ष्य वस्तु की सूचक, R1 चयनकर्ता का सूचक होना चाहिए, और चर तर्क R2 पर शुरू करना चाहिए।

और objc_msdSend पर अपनी कॉल जारी करते समय, आप अपने va_list के साथ R2 की सामग्री को कम से कम ओवरराइट कर रहे हैं।

आपको चरणीय तर्कों की परवाह न करने का प्रयास करना चाहिए। थोड़ी किस्मत के साथ, यदि objc_msgSend कॉल (जहां आपको अंतिम चयनकर्ता मिलता है) से पहले कोड उन रजिस्टरों को गड़बड़ नहीं करता है, तो सही मान अभी भी वहां होना चाहिए, जिससे उन्हें लक्ष्य विधि के लिए उपलब्ध कराया जा सके।

यह निश्चित रूप से वास्तविक डिवाइस पर काम करेगा, सिम्युलेटर में नहीं (सिम्युलेटर x86 है, इसलिए वैरिएड पैरामीटर स्टैक पर पास किए गए हैं)।

+1

दोनों धन्यवाद, दोनों उत्तर बहुत उपयोगी हैं, मैं इसे एक सही के रूप में चिह्नित करूंगा क्योंकि एक स्पष्टीकरण प्लस एक दिलचस्प उदाहरण है। इन उत्तरों के कारण, मैंने एनएसआईएनवोकेशन के साथ पुनः प्रयास करने का फैसला किया, मैं इसके बारे में एक और सवाल पोस्ट करूंगा। धन्यवाद फिर से – LombaX

+1

यदि कोई दिलचस्पी लेता है, तो यहां गिथब पर कक्षा के पहले संस्करण के लिए एक लिंक है: https://github.com/lombax85/FLCodeInjector – LombaX

1

दुर्भाग्य से, नहीं। यही कारण है कि परिवर्तनीय तर्क लेने वाले कार्यों को समान कार्यान्वयन प्रदान करना चाहिए जो va_list एस लेते हैं। एपीआई जो इस कार्यक्षमता प्रदान करता था (जैसे objc_msgSendv) 'अनुपलब्ध "के बाद से ObjC 2. शायद यह भी पता लगाने की अच्छा होगा क्यों इस कार्यक्षमता हटा दिया गया था चिह्नित किया गया है।

Variadic Macros अक्सर अपने मामले में इस मुद्दे को हल करेंगे, लेकिन नहीं क्योंकि आप एक समारोह सूचक की जरूरत है एक प्रकार का शराबी है।

तो मुझे लगता है आप अपने लक्षित कार्यान्वयन पर वीए सूची के यांत्रिकी समझने के लिए आपके सिस्टम कार्यान्वयन के लिए देखने के लिए की आवश्यकता होगी।

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