Due to the word limit of the previous article, the remaining few functions can only be opened in this new article.
objc_msgSend
Finally, we come to our core objc_msgSend function.
/* * * this function declares that two functions have the same input arguments, one is to execute the function and return the id return value. * One is to find SEL IMP in self. * id objc_msgSend(id self, SEL _cmd, ...) ; * IMP objc_msgLookup(id self, SEL _cmd, ...) ; * * objc_msgLookup ABI: IMP returned in X17 IMP returned in X17 is stored in x17 register * x16 reserved for our use but not used * */
#if SUPPORT_TAGGED_POINTERS
.data // Data content
.align 3 // 2^3 = 8 bytes aligned
// Define a global tag _objC_debug_taggedPOinter_classes
.globl _objc_debug_taggedpointer_classes
_objc_debug_taggedpointer_classes:
//. Fill repeat, size, value
// Size and value are optional. The default values are 1 and 0 respectively
// Fill all with 0
.fill 16.8.0
/ / same as above
// Define a global tag _objC_debug_taggedPOinter_ext_classes
.globl _objc_debug_taggedpointer_ext_classes
_objc_debug_taggedpointer_ext_classes:
//. Fill repeat, size, value
// Size and value are optional. The default values are 1 and 0 respectively
// Fill all with 0
.fill 256.8.0
#endif
// Here we go
// Here is ENTRY
// Review our previous ENTRY definition
// .macro ENTRY /* name */
// .text
// .align 5
// .globl $0
/ / $0:
// .endmacro
// $0 means _objc_msgSend
// Then the overall meaning is:
// _objc_msgSend is a code snippet followed by 2^5 = 32 bytes aligned
// the function is global. (Something like that)
ENTRY _objc_msgSend
// UNWIND UNWIND information is generated, no window
UNWIND _objc_msgSend, NoFrame
// p0 and null, that is, to determine whether the receiver exists,
// p0 is the first parameter of objc_msgSend,
// Message receiver
cmp p0, #0 // nil check and tagged pointer check
#if SUPPORT_TAGGED_POINTERS
// If the value is smaller than the specified value, tagged Pointer is supported
// Jump to LNilOrTagged and execute a Taggend Pointer
b.le LNilOrTagged // (MSB tagged pointer looks negative)
#else
// if p0 equals 0, jump to LReturnZero
// Set 0 to return nil and terminate _objc_msgSend directly
b.eq LReturnZero
#endif
// p0 is the flow that must exist in the receiver. The actual specification is that p0-p7 is the register of the receiver function parameters
// Select isa from x0 and store isa in p13
ldr p13, [x0] // p13 = isa
// GetClassFromIsa_p16: Gets the class pointer from ISA and puts it in the general purpose register p16
// and p16, $0, #ISA_MASK
// Under __LP64__ p16 = isa(p13) & ISA_MASK, get the shiftcls information, get the class information
GetClassFromIsa_p16 p13 // p16 = class
// Local tags (indicating that the isa is completed)
LGetIsaDone:
// calls imp or objc_msgSend_uncached
// CacheLookup has been parsed in detail
// if there is isa, go to the CacheLookup process (sel-imp quick lookup process),
/ / NORMAL and _objc_msgSend
CacheLookup NORMAL, _objc_msgSend
#if SUPPORT_TAGGED_POINTERS
LNilOrTagged:
// Check for nil, if nil also jumps to LReturnZero tag
// Set 0 to return nil and terminate _objc_msgSend
b.eq LReturnZero // nil check
// tagged
// Read the base address of the page where _objc_debug_taggedPOinter_classes is located into the X10 register
adrp x10, _objc_debug_taggedpointer_classes@PAGE
// x10 = x10 + _objc_debug_taggedPOinter_classes (offset in page)
// the x10 base address is memory offset based on the offset
add x10, x10, _objc_debug_taggedpointer_classes@PAGEOFF
// Unsigned bit-field extract instruction
// UBFX Wd, Wn, #lsb, #width ; 32-bit
// UBFX Xd, Xn, #lsb, #width ; 64-bit
// Select * from Wn register LSB bit; select * from Wd register width bit
// start at bit 60 of x0,
// Fetch 4 bits to register X11, and fill the other bits with zeros
ubfx x11, x0, #60#,4
// Read the class that Taggedn Pointer belongs to and save it in x16
ldr x16, [x10, x11, LSL #3]
adrp x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGE
add x10, x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGEOFF
cmp x10, x16
// Jump to the LGetIsaDone tag
b.ne LGetIsaDone
// ext tagged
adrp x10, _objc_debug_taggedpointer_ext_classes@PAGE
add x10, x10, _objc_debug_taggedpointer_ext_classes@PAGEOFF
ubfx x11, x0, #52#,8
ldr x16, [x10, x11, LSL #3]
// Jump to the LGetIsaDone tag
b LGetIsaDone
// SUPPORT_TAGGED_POINTERS
#endif
// The nil object passed in is:
LReturnZero:
// x0 is already zero
/ / s
mov x1, #0
movi d0, #0
movi d1, #0
movi d2, #0
movi d3, #0
// return Completes the execution
ret
// LExit ends the _objc_msgSend function
END_ENTRY _objc_msgSend
Copy the code
_objc_msgLookup
Find the IMP.
// _objc_msgLookup function implementation part
ENTRY _objc_msgLookup
UNWIND _objc_msgLookup, NoFrame
// Same as above for nil detection
cmp p0, #0 // nil check and tagged pointer check
#if SUPPORT_TAGGED_POINTERS
// Find IMP in tagged Pointer
b.le LLookup_NilOrTagged // (MSB tagged pointer looks negative)
#else
// Return nil if no corresponding IMP is found
b.eq LLookup_Nil
#endif
// the first argument to x0 (isa) is stored in register P13
ldr p13, [x0] // p13 = isa
// Retrieve the corresponding class according to p13 and save it in p16
GetClassFromIsa_p16 p13 // p16 = class
LLookup_GetIsaDone:
// returns imp
// Find imp and return
CacheLookup LOOKUP, _objc_msgLookup
// Tagged Pointer
#if SUPPORT_TAGGED_POINTERS
LLookup_NilOrTagged:
b.eq LLookup_Nil // nil check
// tagged
adrp x10, _objc_debug_taggedpointer_classes@PAGE
add x10, x10, _objc_debug_taggedpointer_classes@PAGEOFF
ubfx x11, x0, #60#,4
ldr x16, [x10, x11, LSL #3]
adrp x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGE
add x10, x10, _OBJC_CLASS_$___NSUnrecognizedTaggedPointer@PAGEOFF
cmp x10, x16
b.ne LLookup_GetIsaDone
LLookup_ExtTag:
adrp x10, _objc_debug_taggedpointer_ext_classes@PAGE
add x10, x10, _objc_debug_taggedpointer_ext_classes@PAGEOFF
ubfx x11, x0, #52#,8
ldr x16, [x10, x11, LSL #3]
b LLookup_GetIsaDone
// SUPPORT_TAGGED_POINTERS
#endif
// IMP is not found
LLookup_Nil:
// Read the base address of the page __objc_msgNil is on into register X17
adrp x17, __objc_msgNil@PAGE
// x17 = x17 + __objc_msgNil(page offset)
// The x17 base addresses are memory offset based on the offset
add x17, x17, __objc_msgNil@PAGEOFF
// return Completes the execution
ret
// _objc_msgLookup Content ends
END_ENTRY _objc_msgLookup
Copy the code
__objc_msgNil
End work when IMP is not found.
// Private static function
STATIC_ENTRY __objc_msgNil
// x0 is already zero
// x0 is already zero
/ / s
mov x1, #0
movi d0, #0
movi d1, #0
movi d2, #0
movi d3, #0
// return terminates the function
ret
// End the __objc_msgNil function
END_ENTRY __objc_msgNil
Copy the code
_objc_msgSendSuper/_objc_msgSendSuper2/_objc_msgLookupSuper2
The [super XXXXX] function call we use everyday, whose first argument receiver is also self and not self’s parent class, The only difference between _objc_msgSend and _objc_msgSendSuper is that _objc_msgSendSuper goes directly to the parent class.
/ / _objc_msgSendSuper function
ENTRY _objc_msgSendSuper
UNWIND _objc_msgSendSuper, NoFrame
[x0] [p0] [x16] [p0] [x16
ldp p0, p16, [x0] // p0 = real receiver, p16 = class
// calls imp or objc_msgSend_uncached
// Execute imp with _objc_msgSend
CacheLookup NORMAL, _objc_msgSendSuper
END_ENTRY _objc_msgSendSuper
// no _objc_msgLookupSuper
ENTRY _objc_msgSendSuper2
UNWIND _objc_msgSendSuper2, NoFrame
ldp p0, p16, [x0] // p0 = real receiver, p16 = class
// p16 is now superclass (the second member of objc_class is superclass, and the first isa refers to the metaclass)
ldr p16, [x16, #SUPERCLASS] // p16 = class->superclass
/ / execution imp
CacheLookup NORMAL, _objc_msgSendSuper2
END_ENTRY _objc_msgSendSuper2
// _objc_msgLookupSuper2 looks in the parent class
ENTRY _objc_msgLookupSuper2
UNWIND _objc_msgLookupSuper2, NoFrame
ldp p0, p16, [x0] // p0 = real receiver, p16 = class
/ / find the superclass
ldr p16, [x16, #SUPERCLASS] // p16 = class->superclass
/ / to find
CacheLookup LOOKUP, _objc_msgLookupSuper2
END_ENTRY _objc_msgLookupSuper2
Copy the code
MethodTableLookup
.macro MethodTableLookup
// push frame
SignLR
stp fp, lr, [sp, #- 16]!
mov fp, sp
// save parameter registers: x0.. x8, q0.. q7
// Save method parameters to register
sub sp, sp, #(10*8 + 8*16)
stp q0, q1, [sp, #(0*16)]
stp q2, q3, [sp, #(2*16)]
stp q4, q5, [sp, #(4*16)]
stp q6, q7, [sp, #(6*16)]
stp x0, x1, [sp, #(8*16+0*8)]
stp x2, x3, [sp, #(8*16+2*8)]
stp x4, x5, [sp, #(8*16+4*8)]
stp x6, x7, [sp, #(8*16+6*8)]
str x8, [sp, #(8*16+8*8)]
// lookUpImpOrForward(obj, sel, cls, LOOKUP_INITIALIZE | LOOKUP_RESOLVER)
// receiver and selector already in x0 and x1
// Receiver and selector are already in x0 and X1
mov x2, x16
mov x3, #3
// If not found in the cache, go to _lookUpImpOrForward (c function) to look for the function in the list of methods.
// Objc-Runtimenew.mm lookUpImpOrForward is too simple to be expanded here.
bl _lookUpImpOrForward
// IMP in x0
mov x17, x0
// restore registers and return
// Restore the register and return
ldp q0, q1, [sp, #(0*16)]
ldp q2, q3, [sp, #(2*16)]
ldp q4, q5, [sp, #(4*16)]
ldp q6, q7, [sp, #(6*16)]
ldp x0, x1, [sp, #(8*16+0*8)]
ldp x2, x3, [sp, #(8*16+2*8)]
ldp x4, x5, [sp, #(8*16+4*8)]
ldp x6, x7, [sp, #(8*16+6*8)]
ldr x8, [sp, #(8*16+8*8)]
// Handle the top and bottom of the stack
mov sp, fp
ldp fp, lr, [sp], #16
AuthenticateLR
.endmacro
// __objc_msgSend_uncached
STATIC_ENTRY __objc_msgSend_uncached
UNWIND __objc_msgSend_uncached, FrameWithNoSaves
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band p16 is the class to search
MethodTableLookup
TailCallFunctionPointer x17
END_ENTRY __objc_msgSend_uncached
STATIC_ENTRY __objc_msgLookup_uncached
UNWIND __objc_msgLookup_uncached, FrameWithNoSaves
// THIS IS NOT A CALLABLE C FUNCTION
// Out-of-band p16 is the class to search
MethodTableLookup
ret
END_ENTRY __objc_msgLookup_uncached
// _cache_getImp
STATIC_ENTRY _cache_getImp
// Save class in p16
GetClassFromIsa_p16 p0
CacheLookup GETIMP, _cache_getImp
LGetImpMiss:
/ / p0 0
mov p0, #0
// return terminates the function
ret
// _cache_getImp ends
END_ENTRY _cache_getImp
Copy the code
_objc_msgForward
/* * * id _objc_msgForward(id self, SEL _cmd,...) ; * * _objc_msgForward is the externally-callable function returned by things like method_getImplementation(). * _objc_msgForward is an external calling function returned by something like method_getImplementation(). * * _objc_msgForward_impcache is the function pointer * actually stored in method caches. * _objc_msgForward_impcache Is the function pointer that is actually stored in the method cache. * * /
STATIC_ENTRY __objc_msgForward_impcache
// No stret specialization.
b __objc_msgForward
END_ENTRY __objc_msgForward_impcache
/ / __objc_msgForward function
ENTRY __objc_msgForward
// Read the base address of the page __objc_forward_handler into the X10 register
adrp x17, __objc_forward_handler@PAGE
ldr p17, [x17, __objc_forward_handler@PAGEOFF]
// $0 = function pointer value
// br $0
// Jump to x17
TailCallFunctionPointer x17
END_ENTRY __objc_msgForward
// Different jump implementations
ENTRY _objc_msgSend_noarg
// _objc_msgSend
b _objc_msgSend
END_ENTRY _objc_msgSend_noarg
ENTRY _objc_msgSend_debug
// _objc_msgSend
b _objc_msgSend
END_ENTRY _objc_msgSend_debug
ENTRY _objc_msgSendSuper2_debug
// _objc_msgSendSuper2
b _objc_msgSendSuper2
END_ENTRY _objc_msgSendSuper2_debug
ENTRY _method_invoke
// x1 is method triplet instead of SEL
// x1 is the ancestor of the method instead of SEL
add p16, p1, #METHOD_IMP
ldr p17, [x16]
ldr p1, [x1, #METHOD_NAME]
// Method jump
TailCallMethodListImp x17, x16
END_ENTRY _method_invoke
#endif
Copy the code
Refer to the link
Reference link :🔗
- Method to find the flow objC_MSG_arm64.s
- OC Low-level Exploration 09, objc_msgSend process 1- Cache lookup
- Assembly instruction interpretation
- Objc-msg-arm64 source code in-depth analysis
- Assembly language learning notes
- IOS assembly tutorial: Understand ARM
- Assembler jump instruction B, BL, BX, BLX and BXJ difference
- Introduction to ARM64 assembler for iOS developers
- C language stack area (based on ARM) and the function of THE ARM SP, FP registers
- .align 5 specifies how many bytes are aligned
- Interpret objc_msgSend
- ARM assembly instruction
- Translation – Why does objc_msgSend have to be implemented in assembly
- IOS Runtime overview, internal principles, and application scenarios
- Ios-runtime Class, message mechanism, super keyword
- Go deep into the assembly language of iOS
- Operating system memory management (Mind map details)
- ARM Instruction analysis 2 (ADRP, B)
- Arm64 assembler: UBFX instruction
- Part 9 – Linux ARM assembly syntax
- Instructions for CBZ and CBNZ RealView Compiler
- Linux kernel OOPS (1)
- BRAA, BRAAZ, BRAB, BRABZ