iOS-OC的汇编

OC方法

通过前面几篇文章,我们已经了解了汇编的基础指令,今天来看一下OC的汇编是怎么实现的。

我们都知道OC方法在底层是消息发送,而且默认带有两个参数,调用的是objc_msgSend(id self, SEL _cmd),那么我们就看一个简单的OC方法的汇编实现:

ED08F7E2-2841-4AE8-A6AB-E82ECB1C4B2E.png
这段main函数的汇编代码如下:

ZZZ`main:
    0x102e798b0 <+0>:   sub    sp, sp, #0x30             ; =0x30 
    0x102e798b4 <+4>:   stp    x29, x30, [sp, #0x20]
    0x102e798b8 <+8>:   add    x29, sp, #0x20            ; =0x20 
    0x102e798bc <+12>:  stur   wzr, [x29, #-0x4]
    0x102e798c0 <+16>:  stur   w0, [x29, #-0x8]
    0x102e798c4 <+20>:  str    x1, [sp, #0x10]
    0x102e798c8 <+24>:  adrp   x8, 4
    0x102e798cc <+28>:  add    x8, x8, #0xc40            ; =0xc40 
->  0x102e798d0 <+32>:  ldr    x0, [x8]
    0x102e798d4 <+36>:  adrp   x8, 4
    0x102e798d8 <+40>:  add    x8, x8, #0xc00            ; =0xc00 
    0x102e798dc <+44>:  ldr    x1, [x8]
    0x102e798e0 <+48>:  bl     0x102e79c84               ; symbol stub for: objc_msgSend
    0x102e798e4 <+52>:  mov    x29, x29
    0x102e798e8 <+56>:  bl     0x102e79cc0               ; symbol stub for: objc_retainAutoreleasedReturnValue
    0x102e798ec <+60>:  add    x8, sp, #0x8              ; =0x8 
    0x102e798f0 <+64>:  str    x0, [sp, #0x8]
    0x102e798f4 <+68>:  stur   wzr, [x29, #-0x4]
    0x102e798f8 <+72>:  mov    x0, x8
    0x102e798fc <+76>:  mov    x8, #0x0
    0x102e79900 <+80>:  mov    x1, x8
    0x102e79904 <+84>:  bl     0x102e79cd8               ; symbol stub for: objc_storeStrong
    0x102e79908 <+88>:  ldur   w0, [x29, #-0x4]
    0x102e7990c <+92>:  ldp    x29, x30, [sp, #0x20]
    0x102e79910 <+96>:  add    sp, sp, #0x30             ; =0x30 
    0x102e79914 <+100>: ret  
复制代码

在这段代码里,我们看到了objc_msgSend,在此处打一个断点,读出此时的x0和x1,看看是什么

49742B2A-8223-42B5-A5D3-39B5C2386743.png
通过lldb调试,我们看到此时的x0是Person这个类,x1是instance这个SEL,验证了OC方法在底层就是objc_msgSend。

当我们看到一段汇编代码,找到objc_msgSend,读出此时到x0和x1就能知道正在调用的是哪个对象或者类的什么方法。

objc_storeStrong

在main函数代码的倒数第5行,我们看到调用了objc_storeStrong方法。这个方法应该是被strong修饰的对象才会调用,因为我们定义的p是局部变量,默认也是strong修饰的

image.png

151AE0FD-1A96-4C6D-B410-E15B252190AF.png

在上图中,通过lldb读取出了x0和x1中的值,x0中保存着p对象的指针地址,x1=nil。

在OC的源码中,找到objc_storeStrong,把x0和x1带入到函数中

objc_storeStrong(id *location, id obj)
{
    id prev = *location;   //prev = *location = p对象 , obj = nil
    if (obj == prev) {
        return;
    }
    objc_retain(obj);
    *location = obj;      //p = nil
    objc_release(prev);   //objc_release(p)
}
复制代码

通过分析,这里其实是把p指向nil,p对象所在的堆空间释放。因为p是一个局部变量,函数执行完成就会被释放。

Block

Block的底层也是一个结构体,也是一个对象。我们在使用block的时候,大多数就是作为参数,来实现结果回调,那么我们主要的研究对象就是block中的内容,也就是invoke。

struct Block_layout {
    void *isa;
    volatile int32_t flags; // contains ref count
    int32_t reserved;
    BlockInvokeFunction invoke;
    struct Block_descriptor_1 *descriptor;
    // imported variables
};
复制代码

下面我们看一个简单的block:

Snipaste_2021-04-24_15-49-19.png

Snipaste_2021-04-24_21-12-06.png
可以看到汇编代码的第10行objc_retainBlock,此时我们读x0寄存器,可以看到这个一个globalBlock,invoke在0x1005c58c0这里。继续向下执行,我们可以看到17行有一个跳转指令,读出跳转的地址,Ei!! 正是invoke的地址,并且告诉我们是main函数74行那个block的invoke,点击进去,就看到了invoke的汇编实现。

Snipaste_2021-04-24_21-27-02.png

ZZZ`__main_block_invoke:
->  0x1005c58c0 <+0>:  sub    sp, sp, #0x20             ; =0x20 
    0x1005c58c4 <+4>:  stp    x29, x30, [sp, #0x10]
    0x1005c58c8 <+8>:  add    x29, sp, #0x10            ; =0x10 
    0x1005c58cc <+12>: str    x0, [sp, #0x8]
    0x1005c58d0 <+16>: str    x0, [sp]
    0x1005c58d4 <+20>: adrp   x0, 3
    0x1005c58d8 <+24>: add    x0, x0, #0x2f8            ; =0x2f8 
    0x1005c58dc <+28>: bl     0x1005c5bf8               ; symbol stub for: NSLog
    0x1005c58e0 <+32>: ldp    x29, x30, [sp, #0x10]
    0x1005c58e4 <+36>: add    sp, sp, #0x20             ; =0x20 
    0x1005c58e8 <+40>: ret    
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享