匯編角度探索Objctive-C一些常用點(diǎn)的實(shí)現(xiàn)

對(duì)于 Objective-C 的一些實(shí)現(xiàn),我們可以在 Apple 開源網(wǎng)站上下載 objc4 等源碼一探究竟,之前也寫了一篇如何 debug objc4 源碼的文章。這篇文章是從匯編角度簡(jiǎn)單的去窺探一下 Objective-C 的一些實(shí)現(xiàn),個(gè)人記錄下。如 Class Metada、屬性、對(duì)成員變量的訪問、調(diào)用類方法、調(diào)用實(shí)例方法、block 這幾個(gè)基礎(chǔ)常用點(diǎn)。

前言

Objective-C 源文件(.m) 的編譯器是 Clang + LLVM,Swift 源文件的編譯器是 swift + LLVM。借助 clang 命令,可以查看 .m 文件的匯編結(jié)果。

xcrun --sdk iphoneos clang -arch arm64 -S XXXX.m

-arch <arm|arm64|x86_64|i386>: 生成的代碼的架構(gòu)體系

-S:源代碼文件。-o:輸出文件。

生成匯編代碼后,可以比照 objc4 中的源碼對(duì)應(yīng)去理解。

Class Metadata

  1. @interface
#import "THNormalClass.h"

@interface THNormalClass()

@end
 .section    __TEXT,__text,regular,pure_instructions
 .build_version ios, 15, 2   sdk_version 15, 2
 .section    __DATA,__objc_imageinfo,regular,no_dead_strip
L_OBJC_IMAGE_INFO:
 .long   0
 .long   64

.subsections_via_symbol

編譯器對(duì) @interface 并未產(chǎn)生啥匯編代碼。

  1. @implementation

    #import "THNormalClass.h"
    
    @interface THNormalClass()
    
    @end
    
    @implementation THNormalClass
    
    @end
    
     .section    __TEXT,__text,regular,pure_instructions
     .build_version ios, 15, 2   sdk_version 15, 2
     .section    __TEXT,__objc_classname,cstring_literals
    l_OBJC_CLASS_NAME_:                     ; @OBJC_CLASS_NAME_
     .asciz  "THNormalClass"
    
     .section    __DATA,__objc_const
     .p2align    3                               ; @"_OBJC_METACLASS_RO_$_THNormalClass"
    __OBJC_METACLASS_RO_$_THNormalClass:
     .long   1                               ; 0x1
     .long   40                              ; 0x28
     .long   40                              ; 0x28
     .space  4
     .quad   0
     .quad   l_OBJC_CLASS_NAME_
     .quad   0
     .quad   0
     .quad   0
     .quad   0
     .quad   0
    
     .section    __DATA,__objc_data
     .globl  _OBJC_METACLASS_$_THNormalClass ; @"OBJC_METACLASS_$_THNormalClass"
     .p2align    3
    _OBJC_METACLASS_$_THNormalClass:
     .quad   _OBJC_METACLASS_$_NSObject
     .quad   _OBJC_METACLASS_$_NSObject
     .quad   __objc_empty_cache
     .quad   0
     .quad   __OBJC_METACLASS_RO_$_THNormalClass
    
     .section    __DATA,__objc_const
     .p2align    3                               ; @"_OBJC_CLASS_RO_$_THNormalClass"
    __OBJC_CLASS_RO_$_THNormalClass:
     .long   0                               ; 0x0
     .long   8                               ; 0x8
     .long   8                               ; 0x8
     .space  4
     .quad   0
     .quad   l_OBJC_CLASS_NAME_
     .quad   0
     .quad   0
     .quad   0
     .quad   0
     .quad   0
    
     .section    __DATA,__objc_data
     .globl  _OBJC_CLASS_$_THNormalClass     ; @"OBJC_CLASS_$_THNormalClass"
     .p2align    3
    _OBJC_CLASS_$_THNormalClass:
     .quad   _OBJC_METACLASS_$_THNormalClass
     .quad   _OBJC_CLASS_$_NSObject
     .quad   __objc_empty_cache
     .quad   0
     .quad   __OBJC_CLASS_RO_$_THNormalClass
    
     .section    __DATA,__objc_classlist,regular,no_dead_strip
     .p2align    3                               ; @"OBJC_LABEL_CLASS_$"
    l_OBJC_LABEL_CLASS_$:
     .quad   _OBJC_CLASS_$_THNormalClass
    
     .section    __DATA,__objc_imageinfo,regular,no_dead_strip
    L_OBJC_IMAGE_INFO:
     .long   0
     .long   64
    
    .subsections_via_symbols
    
    • __OBJC_CLASS_RO_$_THNormalClass__OBJC_METACLASS_RO_$_THNormalClass 對(duì)應(yīng) objc-runtime-new.hstruct class_ro_t

      struct class_ro_t {
              uint32_t flags;
              uint32_t instanceStart;
              uint32_t instanceSize;                    // instance 對(duì)象占用的內(nèi)存控件
              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;
              method_list_t *baseMethods() const {
                  return baseMethodList;
              }
          };
      
    • _OBJC_CLASS_$_THNormalClass_OBJC_METACLASS_$_THNormalClass 對(duì)應(yīng) objc-runtime-new.hstruct objc_class

      struct objc_class : objc_object {
              // Class ISA;
              Class superclass;
              cache_t cache;          // formerly cache pointer and vtable  方法緩存
              class_data_bits_t bits; // 用于獲取具體的類信息
       
              class_rw_t *data() {
                  return bits.data(); //  class_rw_t* data() { return (class_rw_t *)(bits & FAST_DATA_MASK); }
              }
              .... // 其他的都是方法
          }
      

屬性

@interface THNormalClass()

@property(nonatomic, copy) NSString *str1;

@property(nonatomic, copy) NSString *str2;

@end

@implementation THNormalClass

@end
    .section    __TEXT,__text,regular,pure_instructions
    .build_version ios, 15, 2   sdk_version 15, 2
    .p2align    2                               ; -- Begin function -[THNormalClass str1]
"-[THNormalClass str1]":                ; @"\01-[THNormalClass str1]"
...
"-[THNormalClass setStr1:]":            ; @"\01-[THNormalClass setStr1:]"
...
"-[THNormalClass str2]":                ; @"\01-[THNormalClass str2]"
...
"-[THNormalClass setStr2:]":            ; @"\01-[THNormalClass setStr2:]"
...
l_OBJC_CLASS_NAME_:                     ; @OBJC_CLASS_NAME_
...
__OBJC_METACLASS_RO_$_THNormalClass:
...
_OBJC_METACLASS_$_THNormalClass:
...
l_OBJC_METH_VAR_NAME_:                  ; @OBJC_METH_VAR_NAME_
    .asciz  "str1"

    .section    __TEXT,__objc_methtype,cstring_literals
l_OBJC_METH_VAR_TYPE_:                  ; @OBJC_METH_VAR_TYPE_
    .asciz  "@16@0:8"

    .section    __TEXT,__objc_methname,cstring_literals
l_OBJC_METH_VAR_NAME_.1:                ; @OBJC_METH_VAR_NAME_.1
    .asciz  "setStr1:"

...
__OBJC_$_INSTANCE_METHODS_THNormalClass:
    .long   24                              ; 0x18
    .long   4                               ; 0x4
    .quad   l_OBJC_METH_VAR_NAME_
    .quad   l_OBJC_METH_VAR_TYPE_
    .quad   "-[THNormalClass str1]"
    .quad   l_OBJC_METH_VAR_NAME_.1
    .quad   l_OBJC_METH_VAR_TYPE_.2
    .quad   "-[THNormalClass setStr1:]"
    .quad   l_OBJC_METH_VAR_NAME_.3
    .quad   l_OBJC_METH_VAR_TYPE_
    .quad   "-[THNormalClass str2]"
    .quad   l_OBJC_METH_VAR_NAME_.4
    .quad   l_OBJC_METH_VAR_TYPE_.2
    .quad   "-[THNormalClass setStr2:]"

    .private_extern _OBJC_IVAR_$_THNormalClass._str1 ; @"OBJC_IVAR_$_THNormalClass._str1"
    .section    __DATA,__objc_ivar
    .globl  _OBJC_IVAR_$_THNormalClass._str1
    .p2align    2
_OBJC_IVAR_$_THNormalClass._str1:
    .long   8                               ; 0x8

    .section    __TEXT,__objc_methname,cstring_literals
l_OBJC_METH_VAR_NAME_.5:                ; @OBJC_METH_VAR_NAME_.5
    .asciz  "_str1"

    .section    __TEXT,__objc_methtype,cstring_literals
l_OBJC_METH_VAR_TYPE_.6:                ; @OBJC_METH_VAR_TYPE_.6
    .asciz  "@\"NSString\""

    .private_extern _OBJC_IVAR_$_THNormalClass._str2 ; @"OBJC_IVAR_$_THNormalClass._str2"
    .section    __DATA,__objc_ivar
    .globl  _OBJC_IVAR_$_THNormalClass._str2
    .p2align    2
_OBJC_IVAR_$_THNormalClass._str2:
    .long   16                              ; 0x10

    .section    __TEXT,__objc_methname,cstring_literals
l_OBJC_METH_VAR_NAME_.7:                ; @OBJC_METH_VAR_NAME_.7
    .asciz  "_str2"

    .section    __DATA,__objc_const
    .p2align    3                               ; @"_OBJC_$_INSTANCE_VARIABLES_THNormalClass"
__OBJC_$_INSTANCE_VARIABLES_THNormalClass:
    .long   32                              ; 0x20
    .long   2                               ; 0x2
    .quad   _OBJC_IVAR_$_THNormalClass._str1
    .quad   l_OBJC_METH_VAR_NAME_.5
    .quad   l_OBJC_METH_VAR_TYPE_.6
    .long   3                               ; 0x3
    .long   8                               ; 0x8
    .quad   _OBJC_IVAR_$_THNormalClass._str2
    .quad   l_OBJC_METH_VAR_NAME_.7
    .quad   l_OBJC_METH_VAR_TYPE_.6
    .long   3                               ; 0x3
    .long   8                               ; 0x8

    .section    __TEXT,__objc_methname,cstring_literals
l_OBJC_PROP_NAME_ATTR_:                 ; @OBJC_PROP_NAME_ATTR_
    .asciz  "str1"

l_OBJC_PROP_NAME_ATTR_.8:               ; @OBJC_PROP_NAME_ATTR_.8
    .asciz  "T@\"NSString\",C,N,V_str1"

l_OBJC_PROP_NAME_ATTR_.9:               ; @OBJC_PROP_NAME_ATTR_.9
    .asciz  "str2"

l_OBJC_PROP_NAME_ATTR_.10:              ; @OBJC_PROP_NAME_ATTR_.10
    .asciz  "T@\"NSString\",C,N,V_str2"

    .section    __DATA,__objc_const
    .p2align    3                               ; @"_OBJC_$_PROP_LIST_THNormalClass"
__OBJC_$_PROP_LIST_THNormalClass:
    .long   16                              ; 0x10
    .long   2                               ; 0x2
    .quad   l_OBJC_PROP_NAME_ATTR_
    .quad   l_OBJC_PROP_NAME_ATTR_.8
    .quad   l_OBJC_PROP_NAME_ATTR_.9
    .quad   l_OBJC_PROP_NAME_ATTR_.10

    .p2align    3                               ; @"_OBJC_CLASS_RO_$_THNormalClass"
__OBJC_CLASS_RO_$_THNormalClass:
    .long   0                               ; 0x0
    .long   8                               ; 0x8
    .long   24                              ; 0x18
    .space  4
    .quad   0
    .quad   l_OBJC_CLASS_NAME_
    .quad   __OBJC_$_INSTANCE_METHODS_THNormalClass
    .quad   0
    .quad   __OBJC_$_INSTANCE_VARIABLES_THNormalClass
    .quad   0
    .quad   __OBJC_$_PROP_LIST_THNormalClass

    .section    __DATA,__objc_data
    .globl  _OBJC_CLASS_$_THNormalClass     ; @"OBJC_CLASS_$_THNormalClass"
    .p2align    3
_OBJC_CLASS_$_THNormalClass:
    .quad   _OBJC_METACLASS_$_THNormalClass
    .quad   _OBJC_CLASS_$_NSObject
    .quad   __objc_empty_cache
    .quad   0
    .quad   __OBJC_CLASS_RO_$_THNormalClass

    .section    __DATA,__objc_classlist,regular,no_dead_strip
    .p2align    3                               ; @"OBJC_LABEL_CLASS_$"
l_OBJC_LABEL_CLASS_$:
    .quad   _OBJC_CLASS_$_THNormalClass

    .section    __DATA,__objc_imageinfo,regular,no_dead_strip
L_OBJC_IMAGE_INFO:
    .long   0
    .long   64

.subsections_via_symbols

__OBJC_$_INSTANCE_VARIABLES_THNormalClass 對(duì)應(yīng) objc-runtime-new.h 中的 struct ivar_list_t 加上 2 個(gè).long(4 bytes)的開銷。

__OBJC_$_INSTANCE_VARIABLES_THNormalClass:
    .long   32                              ; 0x20
    .long   2                               ; 0x2
    .quad   _OBJC_IVAR_$_THNormalClass._str1
    .quad   l_OBJC_METH_VAR_NAME_.5
    .quad   l_OBJC_METH_VAR_TYPE_.6
    .long   3                               ; 0x3
    .long   8                               ; 0x8
    .quad   _OBJC_IVAR_$_THNormalClass._str2
    .quad   l_OBJC_METH_VAR_NAME_.7
    .quad   l_OBJC_METH_VAR_TYPE_.6
    .long   3                               ; 0x3
    .long   8                               ; 0x8

對(duì)應(yīng) objc-runtime-new.h 中的 struct ivar_t。最后一段表明 struct class_ro_t 中的 ivars 指向上面提到的 ivar_list_t。

注意:這里保存 ivars 的是 class_ro_t 而不是 struct objc_class。

  1. 編譯器首先會(huì)為 property 自動(dòng)生成 getter 和 setter ,并放到 method_list_t 中。

    __OBJC_$_INSTANCE_METHODS_THNormalClass:
     .long   24                              ; 0x18
     .long   4                               ; 0x4
     .quad   l_OBJC_METH_VAR_NAME_
     .quad   l_OBJC_METH_VAR_TYPE_
     .quad   "-[THNormalClass str1]"
     .quad   l_OBJC_METH_VAR_NAME_.1
     .quad   l_OBJC_METH_VAR_TYPE_.2
     .quad   "-[THNormalClass setStr1:]"
     .quad   l_OBJC_METH_VAR_NAME_.3
     .quad   l_OBJC_METH_VAR_TYPE_
     .quad   "-[THNormalClass str2]"
     .quad   l_OBJC_METH_VAR_NAME_.4
     .quad   l_OBJC_METH_VAR_TYPE_.2
     .quad   "-[THNormalClass setStr2:]"
    
  2. 由于 property 是成員變量的封裝, ivar_list_t 中會(huì)對(duì)其保存對(duì)應(yīng)的成員變量。

    __OBJC_$_INSTANCE_VARIABLES_THNormalClass:
     .long   32                              ; 0x20
     .long   2                               ; 0x2
     .quad   _OBJC_IVAR_$_THNormalClass._str1
     .quad   l_OBJC_METH_VAR_NAME_.5
     .quad   l_OBJC_METH_VAR_TYPE_.6
     .long   3                               ; 0x3
     .long   8                               ; 0x8
     .quad   _OBJC_IVAR_$_THNormalClass._str2
     .quad   l_OBJC_METH_VAR_NAME_.7
     .quad   l_OBJC_METH_VAR_TYPE_.6
     .long   3                               ; 0x3
     .long   8                               ; 0x8
    
  3. Objective-C 的每個(gè)類有一個(gè) property_list_t ,被保存在 struct class_ro_t 中。

    __OBJC_$_PROP_LIST_THNormalClass:
     .long   16                              ; 0x10
     .long   2                               ; 0x2
     .quad   l_OBJC_PROP_NAME_ATTR_
     .quad   l_OBJC_PROP_NAME_ATTR_.8
     .quad   l_OBJC_PROP_NAME_ATTR_.9
     .quad   l_OBJC_PROP_NAME_ATTR_.10
    
  4. 每個(gè) property_list_t 保存的是一個(gè) struct property_t 的對(duì)象。

    struct property_t {
        const char *name;
        const char *attributes;
    };
    
    l_OBJC_PROP_NAME_ATTR_:                 ; @OBJC_PROP_NAME_ATTR_
     .asciz  "str1"
    
    l_OBJC_PROP_NAME_ATTR_.8:               ; @OBJC_PROP_NAME_ATTR_.8
     .asciz  "T@\"NSString\",C,N,V_str1"
    

對(duì)成員變量的訪問

@interface THNormalClass() {
    int a;
    int b;
}

@end

@implementation THNormalClass

- (int)calAdd {
    return a + b;
}

@end
FunctionalProgrammingLearn`-[THNormalClass calAdd]:
    0x100b9a4e4 <+0>:  sub    sp, sp, #0x10             ; =0x10 
    0x100b9a4e8 <+4>:  str    x0, [sp, #0x8]
    0x100b9a4ec <+8>:  str    x1, [sp]
    0x100b9a4f0 <+12>: ldr    x8, [sp, #0x8]
->  0x100b9a4f4 <+16>: ldr    w8, [x8, #0x8]
    0x100b9a4f8 <+20>: ldr    x9, [sp, #0x8]
    0x100b9a4fc <+24>: ldr    w9, [x9, #0xc]
    0x100b9a500 <+28>: add    w0, w8, w9
    0x100b9a504 <+32>: add    sp, sp, #0x10             ; =0x10 
    0x100b9a508 <+36>: ret  

x8 保存的是 THNormalClass * 的地址。[x8, #0x8] offset 8 字節(jié)找到 a 在內(nèi)存中的位置。ldr 在這里是內(nèi)存訪問指令。

調(diào)用方法

@interface THNormalClass() {
    int a;
    int b;
}

@end

@implementation THNormalClass

- (int)calAdd {
    return [self calMethod];
}

- (int)calMethod {
    a = 1;
    b = 8;
    return a + b;
}

@end
"-[THNormalClass calAdd]":              ; @"\01-[THNormalClass calAdd]"
    .cfi_startproc
; %bb.0:
    sub sp, sp, #32                     ; =32
    stp x29, x30, [sp, #16]             ; 16-byte Folded Spill
    add x29, sp, #16                    ; =16
    .cfi_def_cfa w29, 16
    .cfi_offset w30, -8
    .cfi_offset w29, -16
    str x0, [sp, #8]
    str x1, [sp]
    ldr x0, [sp, #8]
    adrp    x8, _OBJC_SELECTOR_REFERENCES_@PAGE
    ldr x1, [x8, _OBJC_SELECTOR_REFERENCES_@PAGEOFF]
    bl  _objc_msgSend
    ldp x29, x30, [sp, #16]             ; 16-byte Folded Reload
    add sp, sp, #32                     ; =32
    ret

adr:小范圍的地址讀取指令。將基于 PC 相對(duì)偏移的地址值讀取到寄存器中。

adrp:以 page 為單位大范圍的地址讀取指令。

bl:跳轉(zhuǎn),還將 bl 的下一條指令的地址保存到寄存器中。

x1 保存了 selector 的地址。

調(diào)用類方法

@implementation THNormalClass

- (void)calMethod {
    return [THNormalClass calClassMethod];
}

+ (void)calClassMethod {
     
}

@end
"-[THNormalClass calMethod]":           ; @"\01-[THNormalClass calMethod]"
    .cfi_startproc
; %bb.0:
    sub sp, sp, #32                     ; =32
    stp x29, x30, [sp, #16]             ; 16-byte Folded Spill
    add x29, sp, #16                    ; =16
    .cfi_def_cfa w29, 16
    .cfi_offset w30, -8
    .cfi_offset w29, -16
    str x0, [sp, #8]
    str x1, [sp]
    adrp    x8, _OBJC_CLASSLIST_REFERENCES_$_@PAGE
    ldr x0, [x8, _OBJC_CLASSLIST_REFERENCES_$_@PAGEOFF]
    adrp    x8, _OBJC_SELECTOR_REFERENCES_@PAGE
    ldr x1, [x8, _OBJC_SELECTOR_REFERENCES_@PAGEOFF]
    bl  _objc_msgSend
    ldp x29, x30, [sp, #16]             ; 16-byte Folded Reload
    add sp, sp, #32                     ; =32
    ret

在上邊的匯編代碼中,可以看到類方法的調(diào)用與成員方法差不多,都是 _objc_msgSendadrp x8, _OBJC_CLASSLIST_REFERENCES_$_@PAGE ldr x0, [x8, _OBJC_CLASSLIST_REFERENCES_$_@PAGEOFF],類方法調(diào)用多了一步獲取全局類指針。

adrp x8, 6

假定當(dāng)前的 PC 寄存器為 0x10410e324
1.先將 6 的值左移 12 位二進(jìn)制位則為 0x6000
2.將 PC 寄存器的低12位清零,即 0x10410e324 => 0x10410e000
3.最后將 0x10410e000 和 0x6000 相加給 x8 寄存器。x8 為 0x104114000

block

@implementation THNormalClass

void(^block)(void) = ^(void) {
    NSLog(@"this is a block");
};

- (void)calMethod {
    block();
}

@end
image-20220225163123030
Screen Shot 2022-02-25 at 4.34.30 PM.png
  1. 可以看到這是一個(gè) __NSGlobalBlock__。

  2. struct Block_layout {
        void * __ptrauth_objc_isa_pointer isa;
        volatile int32_t flags; // contains ref count
        int32_t reserved;
        BlockInvokeFunction invoke;
        struct Block_descriptor_1 *descriptor;
        // imported variables
    };
    

    invoke 在內(nèi)存處于 17-24 字節(jié)處。po 0x00000001028ac090 得知 invoke :0x1028aa304。通過 dis -s 來(lái)反匯編地址。如下圖所示找到了源碼中寫的 NSLog。

    (lldb) dis -s 0x00000001028aa304
    FunctionalProgrammingLearn`block_block_invoke:
        0x1028aa304 <+0>:  sub    sp, sp, #0x20             ; =0x20 
        0x1028aa308 <+4>:  stp    x29, x30, [sp, #0x10]
        0x1028aa30c <+8>:  add    x29, sp, #0x10            ; =0x10 
        0x1028aa310 <+12>: str    x0, [sp, #0x8]
        0x1028aa314 <+16>: str    x0, [sp]
        0x1028aa318 <+20>: adrp   x0, 2
        0x1028aa31c <+24>: add    x0, x0, #0xb0             ; =0xb0 
        0x1028aa320 <+28>: bl     0x1028aa668               ; symbol stub for: NSLog
    (lldb) 
    
  3. signature :"v8@?0"

    Type Encodings

后記

很簡(jiǎn)單的在匯編角度對(duì)于 OC 一些常用點(diǎn),比照著 objc4 和 libclosure 的源碼探索了下。覺得有必要自己整理個(gè) arm 匯編學(xué)習(xí)筆記,不求完全記住理解,后邊自查也好。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容