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