74

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

 5 years ago
source link: https://www.jianshu.com/p/29d0272a97ff?
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

0.3272019.06.23 22:16:33字数 522阅读 482
webp
反汇编分析消息转发

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

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

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

webp
汇编部分__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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK