This is the 8th day of my participation in the August More text Challenge. For details, see: August More Text Challenge
Follow the code analysis from the previous section
Index ((_cmd ^ (_cmd >> 7)) & mask)
P10 is the first address of the buckets. This is where you want to look for p10 in the first place
// Then store it in p12.
add p12, p10, p12, LSL #(1+PTRSHIFT)
// p12 = first probed bucket
// do {
// Equivalent to the previous bucket-- loop through the current lookup
4: ldp p17, p9, [x13], #-BUCKET_SIZE // {imp, sel} = *bucket--
cmp p9, p1 // if (sel == _cmd)
// Go ahead to cachehit
b.eq 2b // goto hit
// p9 does not equal 0
cmp p9, #0 // } while (sel ! = 0 &&
/// If p13 is greater than p12, make sure that you do not search for the bucket that was previously checked
ccmp p13, p12, #0, ne // bucket > first_probed)
b.hi 4b
LLookupEnd\Function:
LLookupRecover\Function:
b \MissLabelDynamic
Copy the code
Look for objc_msgSend on the real machine
Recreate an app project and run the code on the real machine to explore objc_msgSend.
Open always show disassembly and run it.
Hold down Control and click Step into to go to objc_msgSend.
Next, read registers X0,x1.
Here you can see that X1 is SEL, x0 is receiver, then hold down control and click Step into to go to libobjc.a.dylib ‘objc_msgSend. In line 2, x0 is compared with 0x0 to determine whether x0 exists, and in line 5, isa is obtained by putting the address of the receiver on the upper mask.
The output proves that you do get ISA.
_objc_msgSend_uncached
We said that if you find a method you will cachecached, and if you don’t, you will go to _objc_msgSend_uncached, so search the source for _objc_msgSend_uncached, and you will find entries for you.
So we see here that we’re going to go into MethodTableLookup, so let’s search for MethodTableLookup.
Look at TailCallFunctionPointer again. It looks like it just returns. Indicates that important information is in MethodTableLookup.
If we look at the tablelookUp, we see that we have x0 below, which is the first address in the register, where the return value is stored. So x0 means imp is returned. We need to find IMP, so we need to look at _lookUpImpOrForward.
Search for _lookUpImpOrForward and find objc-msg-arm64.s only there.
Next, look at the c++ code and search for lookUpImpOrForward, go back to objc-runtimenew and find the implementation of lookUpImpOrForward.
Why is the cache deliberately written in assembly instead of c++
- Assembly flow is closer to machine language,
Fast execution and optimized search time
. security
The unknown parameters
, c language cannot satisfy
If you don’t find a method in assembly, you go through a slow lookup process, which is going through the methodList.
In lookUpImpOrForward, our target is IMP, and we need to focus on the return value IMP.
Now let’s find out.
And you see there’s a checkIsKnownClass, so the name means check to see if it’s a known class.
Click in to see the method implementation.
Looking at the isKnownClass implementation, we can see that allocatedClasses are a set that holds all the classes that have been loaded.
Going back to lookUpImpOrForward comes down here, which is mainly the implementation of the process class.
Looking at here
Click on it
The point here
So you see the familiar class_rw_t, supercls, Metaclas, and this method does a little bit of work with ro, rw
When you look at this, you see that the superclass and the metaclass are also implemented.
If you look down, you see that the CLS superclass and the metaclass are assigned. If you go here, you can see that the class and its parent class and the metaclass -> root root metaclass are initialized. This is so that if a method cannot be found in the current class, it can be found in the parent class or metaclass.
Go back to the lookUpImpOrForward method
When I get into an infinite loop, I’m going to look in the shared cache first, so that when I get there, I don’t get in the method cache before
If there is no cache go to getMethodNoSuper_nolock.
Click in to see the implementation of getMethodNoSuper_nolock. CLS ->data()->methods() gets the list of methods. It then retrieves the first and last method in the list of methods and iterates to find them.
Next, it goes to search_method_list_inline, so click on that.
Look at findMethodInSortedMethodList. In this case, it’s always going to be else, and in the case of m1 it’s going to be isSmallList.
It opens at findMethodInSortedMethodList view. Binary search is used here. If the count is 8, which is the binary 1000, and the probe to be searched is 7,1000 >> 1 = 0100 = 4, then the first time into the loop, the probe is 4, and probeValue is the SEL obtained by getName, if it matches the SEL to be searched, the probe is returned. Probe — is to call the method of the class first if there is a class in the return, which I won’t expand here. If 7 is greater than 4, then base = probe + 1 = 5, count — that’s 7. Count >>= 1, 7 >>= 1, 0011 = 3. Base = base + (count >> 1) = 5+1 = 6, base = probe +1 = 7, count — = 2. In a loop. Count >>=1 = 0, probe = base + (count >>1) = 0, keyValue = probeValue
After going back to lookUpImpOrForward, you see that if an IMP is found, then goto done;
Let’s see what done is, and I see that log_and_fill_cache is filling it.
Log_and_fill_cache: insert cache.insert.
At this point, msgSend has formed a closed loop. If the first loopUpCache search is not found, it will enter the slow search, if there is no rw, ro, an error will be reported. If so, it inserts, and next time it comes in, it does a quick loopUpCache lookup.
Go back to lookUpImpOrForward. We’ve replaced curClass with the superclass. If all the parent classes are found, imp will set to forward_IMP, exit the loop, and the process will be analyzed later.
,
If you don’t find it you’re going to go to cache_getImp.
I clicked on cache_getImp and found no implementation found.
Go look for it in assembly, find the implementation, again familiar GetClassFromIsa_p16 and CacheLookup, indicating that this is a fast lookup process. If you can’t find it here, you’re going to return an empty.
Imp = cache_getImp(curClass, sel); Imp will be empty. Then the loop begins as you go back to lookUpImpOrForward, where curClass is the parent class. The loop continues until the method is found, or the superclass is empty and no method is found.
LookUpImpOrForward Process summary:
- Checks if the class is registered and reports an error if it is not
- Check if classes and metaclasses on the superclass chain are implemented/initialized. If not, implement them. If not found in this class, look for them in the superclass.
- Enter the for loop
- Check to see if there’s a shared cache, so that when you get here, the method cache is in
- FindMethodInSortedMethodList binary search in the current class of sel corresponding Method, find out of circulation, inserted into the cache.
- If not found, the parent class is detected, and if the parent class is nil, the loop exits, imp = forward_IMP
- If it’s not found and the superclass is not nil, the slow search process for the superclass goes through.
- If imp is found in the parent class, exit the loop and jump to goto Done