美文网首页iOS开发进阶
Runtime底层原理总结--反汇编分析消息转发

Runtime底层原理总结--反汇编分析消息转发

作者: 我叫Vincent | 来源:发表于2019-06-23 22:16 被阅读289次
    反汇编分析消息转发

    消息转发:发送一个消息,也就是sel查找imp,当没有找到imp,接下来进入动态方法解析,如果开发者并没有处理,会进入消息转发。

    消息转发

    前几篇文章介绍了Runtime底层原理动态方法解析总结

    ,我们知道如果前面的动态方法解析也没有解决问题的话,那么就会进入消息转发_objc_msgForward_impcache方法,会有快速消息转发和慢速消息转发。
    _objc_msgForward_impcache方法会从C转换到汇编部分__objc_msgForward_impcache进行快速消息转发,执行闭源__objc_msgForward

    汇编部分__objc_msgForward

    如果我们的方法没有查找到会报错_forwarding_prep_0

    崩溃信息

    但是我们在源代码中找不到该方法,除了前面文章--Runtime底层原理--动态方法解析、消息转发源码分析提到的方法外,我们可以用反汇编分析消息转发。

    首先进入/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/CoreSimulator/Profiles/Runtimes/iOS.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/CoreFoundation.framework中,找到可执行文件CoreFoundation,将该文件拖入hopper中,找到CFInitialze可以看到了___forwarding_prep_0___

    forwarding_prep_0

    进入___forwarding_prep_0___内部,

    ____forwarding___

    从崩溃堆栈信息中看到有执行forwarding,进入forwarding内部,里面判断了_objc_msgSend_stret、_objc_msgSend、taggedpointer之后有个forwardingTargetForSelector:,判断forwardingTargetForSelector:没有实现,没有实现跳转到loc_126fc7,

    forwardingTargetForSelector

    进入loc_126fc7,判断僵尸对象之后执行方法签名methodSignatureForSelector:,如果有值,获取Name、是否有效的位移等,之后会响应_forwardStackInvocation:

    _forwardStackInvocation

    如果没有响应_forwardStackInvocation:,则会响应forwardInvocation:,给rdi发送rax消息,rdi就是NSInvocation,rax就是selforwardInvocation:这就是消息转发的流程

    selforwardInvocation

    _objc_msgForward部分进行反汇编成OC代码:

    
    int __forwarding__(void *frameStackPointer, int isStret) {
        id receiver = *(id *)frameStackPointer;
        SEL sel = *(SEL *)(frameStackPointer + 8);
        const char *selName = sel_getName(sel);
        Class receiverClass = object_getClass(receiver);
        
        // 调用 forwardingTargetForSelector:
        if (class_respondsToSelector(receiverClass, @selector(forwardingTargetForSelector:))) {
            id forwardingTarget = [receiver forwardingTargetForSelector:sel];
            if (forwardingTarget && forwarding != receiver) {
                if (isStret == 1) {
                    int ret;
                    objc_msgSend_stret(&ret,forwardingTarget, sel, ...);
                    return ret;
                }
                return objc_msgSend(forwardingTarget, sel, ...);
            }
        }
        
        // 僵尸对象
        const char *className = class_getName(receiverClass);
        const char *zombiePrefix = "_NSZombie_";
        size_t prefixLen = strlen(zombiePrefix); // 0xa
        if (strncmp(className, zombiePrefix, prefixLen) == 0) {
            CFLog(kCFLogLevelError,
                  @"*** -[%s %s]: message sent to deallocated instance %p",
                  className + prefixLen,
                  selName,
                  receiver);
            <breakpoint-interrupt>
        }
        
        // 调用 methodSignatureForSelector 获取方法签名后再调用 forwardInvocation
        if (class_respondsToSelector(receiverClass, @selector(methodSignatureForSelector:))) {
            NSMethodSignature *methodSignature = [receiver methodSignatureForSelector:sel];
            if (methodSignature) {
                BOOL signatureIsStret = [methodSignature _frameDescriptor]->returnArgInfo.flags.isStruct;
                if (signatureIsStret != isStret) {
                    CFLog(kCFLogLevelWarning ,
                          @"*** NSForwarding: warning: method signature and compiler disagree on struct-return-edness of '%s'.  Signature thinks it does%s return a struct, and compiler thinks it does%s.",
                          selName,
                          signatureIsStret ? "" : not,
                          isStret ? "" : not);
                }
                if (class_respondsToSelector(receiverClass, @selector(forwardInvocation:))) {
                    NSInvocation *invocation = [NSInvocation _invocationWithMethodSignature:methodSignature frame:frameStackPointer];
                    
                    [receiver forwardInvocation:invocation];
                    
                    void *returnValue = NULL;
                    [invocation getReturnValue:&value];
                    return returnValue;
                } else {
                    CFLog(kCFLogLevelWarning ,
                          @"*** NSForwarding: warning: object %p of class '%s' does not implement forwardInvocation: -- dropping message",
                          receiver,
                          className);
                    return 0;
                }
            }
        }
        
        SEL *registeredSel = sel_getUid(selName);
        
        // selector 是否已经在 Runtime 注册过
        if (sel != registeredSel) {
            CFLog(kCFLogLevelWarning ,
                  @"*** NSForwarding: warning: selector (%p) for message '%s' does not match selector known to Objective C runtime (%p)-- abort",
                  sel,
                  selName,
                  registeredSel);
        } // doesNotRecognizeSelector
        else if (class_respondsToSelector(receiverClass,@selector(doesNotRecognizeSelector:))) {
            [receiver doesNotRecognizeSelector:sel];
        }
        else {
            CFLog(kCFLogLevelWarning ,
                  @"*** NSForwarding: warning: object %p of class '%s' does not implement doesNotRecognizeSelector: -- abort",
                  receiver,
                  className);
        }
        
        // The point of no return.
        kill(getpid(), 9);
    }
    
    

    该文章为记录本人的学习路程,希望能够帮助大家,也欢迎大家点赞留言交流!!!文章地址:https://www.jianshu.com/p/29d0272a97ff

    相关文章

      网友评论

        本文标题:Runtime底层原理总结--反汇编分析消息转发

        本文链接:https://www.haomeiwen.com/subject/nqjwqctx.html