preface

Objc_msgSend is about getting a class through ISA, so what do you do next? Let’s explore this part of the source code.

Tips: I am using version 818.2 of the source code.

CacheLookup

Last time we saw GetClassFromIsa_p16, where we got the class, LGetIsaDone.

LGetIsaDone:
	// calls imp or objc_msgSend_uncached
	CacheLookup NORMAL, _objc_msgSend, __objc_msgSend_uncached
Copy the code

So you can see that there’s just one line of code here, and I’m calling CacheLookup, so let’s look at what’s in this code, and let’s do a global search to find where it’s defined. But see the code, on the spot meng force, a total of more than 200 lines of code, let people look cowardly. Adjust the mentality, one by one to see how the implementation of the inside, a careful look will find that there are a lot of judgment, then we put the criteria for our exploration of the screening out (according to the real machine 64 bits).

.macro CacheLookup Mode, Function, MissLabelDynamic, MissLabelConstant
    mov	x15, x16
LLookupStart\Function:
    // p1 = SEL, p16 = isa
    ldr	p11, [x16, #CACHE]			// p11 = mask|buckets
    and	p10, p11, #0x0000fffffffffffe	// p10 = buckets
    tbnz	p11, #0, LLookupPreopt\Function
    eor	p12, p1, p1, LSR #7
    and	p12, p12, p11, LSR #48		// x12 = (_cmd ^ (_cmd >> 7)) & mask
    
    add	p13, p10, p12, LSL #(1+PTRSHIFT) // p13 = buckets + ((_cmd & mask) << (1+PTRSHIFT))

						// do {
1:  ldp	p17, p9, [x13], #-BUCKET_SIZE	// {imp, sel} = *bucket--
    cmp	p9, p1				// if (sel ! = _cmd) {
    b.ne	3f				// scan more
						// } else {
2:  CacheHit \Mode				// hit: call or return imp
						/ /}
3:  cbz	p9, \MissLabelDynamic		// if (sel == 0) goto Miss;
    cmp	p13, p10			// } while (bucket >= buckets)
    b.hs	1b
    
    add	p13, p10, p11, LSR #(48 - (1+PTRSHIFT))
						// p13 = buckets + (mask << 1+PTRSHIFT)
						// see comment about maskZeroBits
    add	p12, p10, p12, LSL #(1+PTRSHIFT)
						// p12 = first probed bucket

						// do {
4:  ldp	p17, p9, [x13], #-BUCKET_SIZE	// {imp, sel} = *bucket--
    cmp	p9, p1				// if (sel == _cmd)
    b.eq	2b				// goto hit
    cmp	p9, #0				// } while (sel ! = 0 &&
    ccmp	p13, p12, #0, ne		// bucket > first_probed)
    b.hi	4b
    
.macro CacheHit
.if $0 == NORMAL
    TailCallCachedImp x17, x10, x1, x16	// authenticate and call imp
.elseif $0 == GETIMP
    mov	p0, p17
    cbz	p0, 9f			// don't ptrauth a nil imp
    AuthAndResignAsIMP x0, x10, x1, x16	// authenticate imp and re-sign as IMP
9:	ret				// return IMP
.elseif $0 == LOOKUP
    // No nil check for ptrauth: the caller would crash anyway when they
    // jump to a nil IMP. We don't care if that jump also fails ptrauth.
    AuthAndResignAsIMP x17, x10, x1, x16	// authenticate imp and re-sign as IMP
    cmp	x16, x15
    cinc	x16, x16, ne			// x16 += 1 when x15 ! = x16 (for instrumentation ; fallback to the parent class)
    ret				// return imp via x17
.else
.abort oops
.endif
.endmacro

.macro TailCallCachedImp
    // $0 = cached imp, $1 = address of cached imp, $2 = SEL, $3 = isa
    eor	$0, $0, $3
    br	$0
.endmacro
        
Copy the code

Finally, we get this code, and this is the main code, and the rest is not very useful for us to learn. Assembly language, look at each one.

So this gives you an idea of what’s going on, not necessarily exactly, but it gives you an idea of what’s going on here. The general result is either to find the corresponding IMP to execute, or not to execute the MissLabelDynamic instruction.

Tips: There are a lot of CONFIG_USE_PREOPT_CACHES in objc_msgSend, you need to find the definition yourself. There are also a lot of __has_feature(ptrauth_calls) judgments, which is basically a way to determine if the phone is A12 or above, which is the iPhone X after the iPhone X.

conclusion

The reason objc_msgSend uses assembly language is to find the method to call more efficiently and quickly. There may be thousands of methods in a project, so it is necessary to improve the search speed and performance. This is essentially a quick lookup of a method, and if a quick lookup of a method does not find it, then a slow lookup will enter.