runtime & 消息发送流程(重磅)

2022-07-17  本文已影响0人  f8d1cf28626a

runtime & 消息发送流程 (重磅)

类的走位链

对象 的 isa 指向 类(也可称为类对象)
类 的 isa 指向 元类
元类 的 isa 指向 根元类,即NSObject
根元类 的 isa 指向 它自己

首先拓展一下类的结构

objc_class、objc_object、isa、object、NSObject等的整体的关系,如下图所示

bits 的结构

class_ro_t 与 class_rw_t 的关系

通过以上class_rw_t注解中提到的文章,我们对两者有个大体的概念,两者都定义了方法列表,协议列表,属性列表等, 我们来看一下为什么要这么做,这么做有什么意义.

struct class_rw_t {             // 重点

    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;       // 重点

    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;

    Class firstSubclass;
    Class nextSiblingClass;

    char *demangledName;

#if SUPPORT_INDEXED_ISA
    uint32_t index;
#endif

    void setFlags(uint32_t set) 
    {
        OSAtomicOr32Barrier(set, &flags);
    }

    void clearFlags(uint32_t clear) 
    {
        OSAtomicXor32Barrier(clear, &flags);
    }

    // set and clear must not overlap
    void changeFlags(uint32_t set, uint32_t clear) 
    {
        assert((set & clear) == 0);

        uint32_t oldf, newf;
        do {
            oldf = flags;
            newf = (oldf | set) & ~clear;
        } while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
    }
};

class_ro_t 结构

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    const uint8_t * ivarLayout;
    
    const char * name;
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;

    // This field exists only when RO_HAS_SWIFT_INITIALIZER is set.
    _objc_swiftMetadataInitializer __ptrauth_objc_method_list_imp _swiftMetadataInitializer_NEVER_USE[0];

    _objc_swiftMetadataInitializer swiftMetadataInitializer() const {
        if (flags & RO_HAS_SWIFT_INITIALIZER) {
            return _swiftMetadataInitializer_NEVER_USE[0];
        } else {
            return nil;
        }
    }

    method_list_t *baseMethods() const {
        return baseMethodList;
    }

    class_ro_t *duplicate() const {
        if (flags & RO_HAS_SWIFT_INITIALIZER) {
            size_t size = sizeof(*this) + sizeof(_swiftMetadataInitializer_NEVER_USE[0]);
            class_ro_t *ro = (class_ro_t *)memdup(this, size);
            ro->_swiftMetadataInitializer_NEVER_USE[0] = this->_swiftMetadataInitializer_NEVER_USE[0];
            return ro;
        } else {
            size_t size = sizeof(*this);
            class_ro_t *ro = (class_ro_t *)memdup(this, size);
            return ro;
        }
    }
};

对比class_rw_t我们发现, class_ro_t中多了 const uint8_t * ivarLayout; const char * name; const ivar_list_t * ivars; const uint8_t * weakIvarLayout;

static Class realizeClassWithoutSwift(Class cls)
{
    runtimeLock.assertLocked();

    const class_ro_t *ro;
    class_rw_t *rw;
    Class supercls;
    Class metacls;
    bool isMeta;

    if (!cls) return nil;
    if (cls->isRealized()) return cls;
    assert(cls == remapClass(cls));

    // fixme verify class is not in an un-dlopened part of the shared cache?

    ro = (const class_ro_t *)cls->data();
    if (ro->flags & RO_FUTURE) {
        // This was a future class. rw data is already allocated.
        rw = cls->data();
        ro = cls->data()->ro;
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {
        // Normal class. Allocate writeable class data.
        rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
        rw->ro = ro;
        rw->flags = RW_REALIZED|RW_REALIZING;
        cls->setData(rw);
    }

    isMeta = ro->flags & RO_META;

    rw->version = isMeta ? 7 : 0;  // old runtime went up to 6


    // Choose an index for this class.
    // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
    cls->chooseClassArrayIndex();

    if (PrintConnecting) {
        _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
                     cls->nameForLogging(), isMeta ? " (meta)" : "", 
                     (void*)cls, ro, cls->classArrayIndex(),
                     cls->isSwiftStable() ? "(swift)" : "",
                     cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
    }

    // Realize superclass and metaclass, if they aren't already.
    // This needs to be done after RW_REALIZED is set above, for root classes.
    // This needs to be done after class index is chosen, for root metaclasses.
    // This assumes that none of those classes have Swift contents,
    //   or that Swift's initializers have already been called.
    //   fixme that assumption will be wrong if we add support
    //   for ObjC subclasses of Swift classes.
    supercls = realizeClassWithoutSwift(remapClass(cls->superclass));
    metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));

#if SUPPORT_NONPOINTER_ISA
    // Disable non-pointer isa for some classes and/or platforms.
    // Set instancesRequireRawIsa.
    bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
    bool rawIsaIsInherited = false;
    static bool hackedDispatch = false;

    if (DisableNonpointerIsa) {
        // Non-pointer isa disabled by environment or app SDK version
        instancesRequireRawIsa = true;
    }
    else if (!hackedDispatch  &&  !(ro->flags & RO_META)  &&  
             0 == strcmp(ro->name, "OS_object")) 
    {
        // hack for libdispatch et al - isa also acts as vtable pointer
        hackedDispatch = true;
        instancesRequireRawIsa = true;
    }
    else if (supercls  &&  supercls->superclass  &&  
             supercls->instancesRequireRawIsa()) 
    {
        // This is also propagated by addSubclass() 
        // but nonpointer isa setup needs it earlier.
        // Special case: instancesRequireRawIsa does not propagate 
        // from root class to root metaclass
        instancesRequireRawIsa = true;
        rawIsaIsInherited = true;
    }
    
    if (instancesRequireRawIsa) {
        cls->setInstancesRequireRawIsa(rawIsaIsInherited);
    }
// SUPPORT_NONPOINTER_ISA
#endif

    // Update superclass and metaclass in case of remapping
    cls->superclass = supercls;
    cls->initClassIsa(metacls);

    // Reconcile instance variable offsets / layout.
    // This may reallocate class_ro_t, updating our ro variable.
    if (supercls  &&  !isMeta) reconcileInstanceVariables(cls, supercls, ro);

    // Set fastInstanceSize if it wasn't set already.
    cls->setInstanceSize(ro->instanceSize);

    // Copy some flags from ro to rw
    if (ro->flags & RO_HAS_CXX_STRUCTORS) {
        cls->setHasCxxDtor();
        if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
            cls->setHasCxxCtor();
        }
    }
    
    // Propagate the associated objects forbidden flag from ro or from
    // the superclass.
    if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
        (supercls && supercls->forbidsAssociatedObjects()))
    {
        rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
    }

    // Connect this class to its superclass's subclass lists
    if (supercls) {
        addSubclass(supercls, cls);
    } else {
        addRootClass(cls);
    }

    // Attach categories
    methodizeClass(cls);

    return cls;
}

抽取其中部分

    ro = (const class_ro_t *)cls->data();
    if (ro->flags & RO_FUTURE) {
        // This was a future class. rw data is already allocated.
        rw = cls->data();
        ro = cls->data()->ro;
        cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
    } else {
        // Normal class. Allocate writeable class data.
        rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
        rw->ro = ro;
        rw->flags = RW_REALIZED|RW_REALIZING;
        cls->setData(rw);
    }

那么。。问题来了 rwe 呢??

rwe的一切都来自于extAllocIfNeeded()的创建,

ro属于cleanmemory,在编译即确定的内存空间,只读,可以从内存中移除,需要时再次从文件中加载(多么完美的设计啊)
rw属于dirtymemory,rw是运行时结构可读可写,可以向类中添加属性、方法等,在运行时会改变的内存;
rwe相当于类的额外信息(动态拓展的时候生成),在实际使用过程中,只有很少的类会真正的改变他们的内容,所rwe的存在是为了避免资源的消耗

进入主题

对象调用方法其实是发送消息的过程,接受者用于定位当前的类,消息主体的sel用于查找对应的方法名,在找到imp

我们在调用一个方法的时候,下层的实现实际上是objc_msgSend(self,_cmd);

LOOK_RESOLVER 动态方法决议(仅来一次,不会每个月都来一次)

if (slowpath(behavior & LOOK_RESOLVER)){

   behavior ^= LOOK_RESOLVER; // 仅走一次

   return resolveMethod_locked(inst,sel,cls, behavior);
}

resolveMethod_locked 它做了什么??

static NEVER_INLINE IMP resolveMethod_locked(id inst,SEL sel,Class cls,int behavior){
    rutimeLock.assertLocked();
    ASSERT(cls->isRealized());
    runtimeLock.unlock();
    
    if (!cls -> isMetaClass()){
        resolveInstanceMethod(inst,sel,cls);
    }
    else{
        resolveInstanceMethod(inst,sel,cls);
        if (!lookUpImpOrNil(inst,sel,cls)){
            resolveInstanceMethod(inst,sel,cls);
        }
    }
    
    // 再次进入慢速查找
    return lookUpImpOrForward(inst,sel,cls,behavior | LOOKUP_CACHE);
}

static void resolveInstanceMethod(id inst,SEL oldSel,Class cls){
    runtimeLock.assertUnlocked();
    ASSERT(cls->isRealized());
    
    // 这里的意思是可以在当前类重写resolveInstanceMethod方法(动态添加一次方法的机会)
    SEL resolve_sel = @selector(resolveInstanceMethod:);
    
    if (!lookUpImpOrNil(inst,resolve_sel,cls->ISA())){
        // Resolver not implemented.
        return;
    }
    
    BOOL (*msg)(class,SEL,SEL) = (typeof(msg))objc_msgSend;
    bool resolved = msg(cls,resolve_sel,oldSel);
    
    // 如果决议一次之后没有找到,继续lookUpImpOrForward(二分遍历当前类和所有父类),最后执行消息转发
    IMP imp = lookUpImpOrNil(inst,oldSel,cls);
    
    if (resolved && PrintResolveing){
        if (imp){
            _objc_inform("RESOLVE: method %c[%s %s]""bynamically resolveed to %p",
                         cls->isMetaClass()?'+':'-',
                         cls->nameForLogging(),
                         sel_getName(oldSel),
                         imp);
        }
        else {
            _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
                         ",but no new implementation of %c[%s %s] was found",
                         cls->nameForLogging(),sel_getName(oldSel),
                         cls->isMetaClass()?'+':'-',
                         cls->nameForLogging(),sel_getName(oldSel));
        }
    }
    
}

resolveMethod_locked 有什么用??
(可以在当前类重写resolveInstanceMethod方法(动态添加一次方法的机会))

+(BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(cxxxx)){
        // 添加方法
        IMP imp = class_getMethodImplementation(self,@selector(kxxxx));
        Method met = class_getInstanceMethod(self,@selector(kxxxx));
        const char *type = method_getTypeEncoding(mit);
        BOOL hasWhy = class_addMethod(self,sel,type);
    }
    
    return [super resolveInstanceMethod:sel];
}

重点 NSObject中的决议(不建议这么处理)

#import "NSObject+RC.h"
#import <objc/message.h>

@implementation NSObject (RC)
// 实例方法 动态决议
+(BOOL)resolveInstanceMethod:(SEL)sel{
    
    const char *name = object_getClassName(self);
    NSString *hasName = [NSString stringWithUTF8String:name];
    // 理解面向切面
    if (![hasName hasPrefix:@"RC"])return NO; // 只处理自己定义的方法
    
    // 对象方法处理
    const char * cx = "cxxxx";

    if (sel_getName(sel) == cx){
        // 添加方法
        IMP imp = class_getMethodImplementation(self,@selector(kxxxx));
        Method met = class_getInstanceMethod(self,@selector(kxxxx));
        const char *type = method_getTypeEncoding(met);
        BOOL hasWhy = class_addMethod(self, sel, imp, type);
        return NO;
    }

    // 类方法处理
    const char * sx = "sxxxx";
    
    if (sel_getName(sel) == sx){
        // 添加方法
        Class currenrClass = objc_getMetaClass("RCPerson");
        IMP imp = class_getMethodImplementation(currenrClass,@selector(zxxxx));
        Method met = class_getInstanceMethod(currenrClass,@selector(zxxxx));
        const char *type = method_getTypeEncoding(met);
        BOOL hasWhy = class_addMethod(currenrClass, sel, imp, type);
        return  NO;
    }
    
    return NO;
}

如果没有使用resolveInstanceMethod动态决议呢??(此时imp等于forawrd_imp)

来到 消息转发流程

forward_imp 的定义:const forward_imp = (IMP)_objc_msgForward_impcache;

_objc_msgForward_impcache (没有开源,所以只能全局搜索看汇编)找到STATIC_ENTRY __objc_msg_Forward_impcache

STATIC_ENTRY __objc_msg_Forward_impcache

b __objc_msgForward

END_ENTRY __objc_msg_Forward_impcache

ENTRY __objc_msgForward

adrp x17, __objc_forward_handler@PAGE

ldr p17, [x17, __objc_forward_handler@PAGEOFF]

TailCallFunctionPointer x17

ENTRY __objc_msgForward

汇编描述 :主要执行在p17证明是指向x17,即搜索 _objc_forward_handler

查找结果:void * _objc_forward_handler = (void*)objc_defaultForwardHandler;

objc_defaultForwardHandler 是一个Cxx函数 (这是我们经常见到而且无比熟悉的)

__attribute__((noreturn,cold))void objc_defaultForwardHandler(id self,SEL sel){

    _objc_fatal("%c[%s %s]:unrecognized selector sent to instance %p"
                "(no message forward handler is installed)",
                class_isMetaClass(object_getClass(self)) ? '+' : '-',
                object_getClassName(self),
                sel_getName(sel),
                self);
}

引入 instrumentObjcMessageSnds 打印消息发送流程(添加到你想要的位置)


extern void instrumentObjcMessageSnds(BOOL flag);
// 这种写法是cxx,mian.m 改成 main.mm

struct RCTest{
    void *met;
    RCTest(){
        instrumentObjcMessageSnds(YES);
    }
    ~RCTest(){
        instrumentObjcMessageSnds(NO);
    }
};

int main(int argc, char * argv[]) {
    NSString * appDelegateClassName;
    @autoreleasepool {
        
        RCTest();
        NSLog(@"main 函数\n");
        
        appDelegateClassName = NSStringFromClass([AppDelegate class]);
    }
    return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

消息机制建议在这里处理

#import "RPerson.h"
#import "RCxxxxx.h"
#import <objc/message.h>

@implementation RPerson

-(id)forwardingTargetForSelector:(SEL)aSelector{
    
    const char *sayBye = "sayBye";
    if (sel_getName(aSelector) == sayBye){
        // 背锅侠
        return [RCxxxxx alloc];
        
        // 也可以在这里动态添加方法xxxxx
    }
    
    return [super forwardingTargetForSelector:aSelector];
}

@end
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{
    // 1.
    NSMethodSignature *ms = [NSMethodSignature methodSignatureForSelector:@selector(hookHit)];
    return ms;
}

注意:在methodSignatureForSelector 和 forwardInvocation方法之间还有一次动态方法决议,即苹果再次给的一个机会

- (void)forwardInvocation:(NSInvocation *)anInvocation{
    
    // 继续找背锅侠
    anInvocation.target = (id)[[RCxxxxx alloc] init];
    [anInvocation invoke];
}

- (void)hookHit{
    
}

总结1 消息流程 CacheLookUp --> _lookUpImpOrForward(并且cache_fill()) --> LOOK_RESOLVER --> _lookUpImpOrForward --> 消息转发流程(imp = forward_imp)-->快速转发(forwardingTargetForSelector) 或 慢速转发(methodSignatureForSelector & forwardInvocation)

总结2 实践中主要利用到 消息的处理机制动态方法决议重写消息转发

技术实践:没有开元的文件,就使反汇编(需要获取本地库文件->断住断点(image list)),然后找到一个黑不溜秋的东西,拖入反汇编的APP。

这边文章掌握透彻将披荆斩棘,美女无数我只取一瓢。。。

上一篇 下一篇

猜你喜欢

热点阅读