反汇编 Block 的内部逻辑实现
一下是汇编中 block
在汇编中的体现。
__text:00000001002BF6EC BL _objc_release
__text:00000001002BF6F0 ADRP X0, #aComKujiangLoad@PAGE ; "com.kujiang.loadQueue"
__text:00000001002BF6F4 ADD X0, X0, #aComKujiangLoad@PAGEOFF ; "com.kujiang.loadQueue"
__text:00000001002BF6F8 MOV X1, #0 ; attr
__text:00000001002BF6FC BL _dispatch_queue_create
__text:00000001002BF700 MOV X19, X0
__text:00000001002BF704 ADRP X8, #__NSConcreteStackBlock_ptr@PAGE
__text:00000001002BF708 LDR X8, [X8,#__NSConcreteStackBlock_ptr@PAGEOFF]
__text:00000001002BF70C STUR X8, [X29,#var_80]
__text:00000001002BF710 ADRP X8, #qword_100BE3268@PAGE
__text:00000001002BF714 LDR D0, [X8,#qword_100BE3268@PAGEOFF]
__text:00000001002BF718 STUR D0, [X29,#var_78]
__text:00000001002BF71C ADR X8, sub_1002BF908
__text:00000001002BF720 NOP
__text:00000001002BF724 STUR X8, [X29,#var_70]
__text:00000001002BF728 ADRP X8, #unk_100DC0718@PAGE
__text:00000001002BF72C ADD X8, X8, #unk_100DC0718@PAGEOFF
__text:00000001002BF730 STUR X8, [X29,#var_68]
__text:00000001002BF734 MOV X0, X20
__text:00000001002BF738 BL _objc_retain
__text:00000001002BF73C MOV X21, X0
__text:00000001002BF740 STUR X21, [X29,#var_60]
__text:00000001002BF744 MOV X0, X28
__text:00000001002BF748 BL _objc_retain
__text:00000001002BF74C STUR X0, [X29,#var_58]
__text:00000001002BF750 SUB X1, X29, #-var_80
__text:00000001002BF754 MOV X0, X19
__text:00000001002BF758 BL _dispatch_async
从以上汇编代码可以看出
dispatch_asynce(x0,x1)
;
x0
即x19
也就是上面的返回值,即dispatch_queue_t *queue
x1
即x29
即block
所以重点分析在block
,其中 sub_1002BF908
跳转到 block
的内部实现。
struct block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables.*/变量入口,第一个参数入口
};
下面让我进入 sub_1002BF908
看看汇编实现:
__text:00000001002BF908 STP X22, X21, [SP,#-0x10+var_20]!
__text:00000001002BF90C STP X20, X19, [SP,#0x20+var_10]
__text:00000001002BF910 STP X29, X30, [SP,#0x20+var_s0]
__text:00000001002BF914 ADD X29, SP, #0x20
__text:00000001002BF918 MOV X19, X0
__text:00000001002BF91C ADRP X8, #classRef_FastCoder@PAGE
__text:00000001002BF920 LDR X0, [X8,#classRef_FastCoder@PAGEOFF] ; void *
__text:00000001002BF924 LDR X2, [X19,#0x20]
__text:00000001002BF928 ADRP X8, #selRef_dataWithRootObject_@PAGE
__text:00000001002BF92C LDR X1, [X8,#selRef_dataWithRootObject_@PAGEOFF] ; char *
__text:00000001002BF930 BL _objc_msgSend
__text:00000001002BF934 MOV X29, X29
__text:00000001002BF938 BL _objc_retainAutoreleasedReturnValue
__text:00000001002BF93C MOV X20, X0
__text:00000001002BF940 ADRP X8, #selRef_gzippedData@PAGE
__text:00000001002BF944 LDR X1, [X8,#selRef_gzippedData@PAGEOFF] ; char *
__text:00000001002BF948 BL _objc_msgSend
__text:00000001002BF94C MOV X29, X29
__text:00000001002BF950 BL _objc_retainAutoreleasedReturnValue
__text:00000001002BF954 MOV X21, X0
__text:00000001002BF958 LDR X2, [X19,#0x28]
__text:00000001002BF95C ADRP X8, #selRef_writeToFile_atomically_@PAGE
__text:00000001002BF960 LDR X1, [X8,#selRef_writeToFile_atomically_@PAGEOFF] ; char *
__text:00000001002BF964 MOV W3, #1
__text:00000001002BF968 BL _objc_msgSend
__text:00000001002BF96C MOV X0, X21
__text:00000001002BF970 BL _objc_release
__text:00000001002BF974 MOV X0, X20
__text:00000001002BF978 LDP X29, X30, [SP,#0x20+var_s0]
__text:00000001002BF97C LDP X20, X19, [SP,#0x20+var_10]
__text:00000001002BF980 LDP X22, X21, [SP+0x20+var_20],#0x30
__text:00000001002BF984 B _objc_release
上面的汇编是 block
的内部实现逻辑,以下分析以上汇编实现:
// Block 引用的第一个外部参数,具体参数要看调用 Block 处的上下文
id x2 = [X19,#0x20];
id data = [FastCoder dataWithRootObject:x2];
id gzippedData = [data gzippedData];
// Block 引用的第二个外部参数
NSString *filePath = [X19,#0x28];
[gzippedData writeToFile:filePath atomically:YES];
分析:
x19
来自 x0
,方法入口时候的 x0
指的是调用这个函数的第一个参数,这里是 block
结构体地址,两次取 x19
,[X19,#0x20]
和 [X19,#0x28]
,分别是取引用 block
引用的第一个,第二个外部参数。
下面是往 block
内部赋值的汇编和分析:
_text:00000001002BF704 ADRP X8, #__NSConcreteStackBlock_ptr@PAGE
__text:00000001002BF708 LDR X8, [X8,#__NSConcreteStackBlock_ptr@PAGEOFF]
__text:00000001002BF70C STUR X8, [X29,#var_80]
__text:00000001002BF710 ADRP X8, #qword_100BE3268@PAGE
__text:00000001002BF714 LDR D0, [X8,#qword_100BE3268@PAGEOFF]
__text:00000001002BF718 STUR D0, [X29,#var_78]
__text:00000001002BF71C ADR X8, sub_1002BF908
__text:00000001002BF720 NOP
__text:00000001002BF724 STUR X8, [X29,#var_70]
__text:00000001002BF728 ADRP X8, #unk_100DC0718@PAGE
__text:00000001002BF72C ADD X8, X8, #unk_100DC0718@PAGEOFF
__text:00000001002BF730 STUR X8, [X29,#var_68]
__text:00000001002BF734 MOV X0, X20
__text:00000001002BF738 BL _objc_retain
__text:00000001002BF73C MOV X21, X0
__text:00000001002BF740 STUR X21, [X29,#var_60]
__text:00000001002BF744 MOV X0, X28
__text:00000001002BF748 BL _objc_retain
__text:00000001002BF74C STUR X0, [X29,#var_58]
__text:00000001002BF750 SUB X1, X29, #-var_80
__text:00000001002BF754 MOV X0, X19
__text:00000001002BF758 BL _dispatch_async
这里是构造 block
,block
首地址是 var_80
,所以加 0x20
就是 var_60
,所以只要看往 var_60
怎么赋值,就能找到第一个引用参数,以此类推。
_text:00000001002BF704 ADRP X8, #__NSConcreteStackBlock_ptr@PAGE
__text:00000001002BF708 LDR X8, [X8,#__NSConcreteStackBlock_ptr@PAGEOFF]
__text:00000001002BF70C STUR X8, [X29,#var_80]
var_60
的值来自 x21
,x21
来自 x0
,x0
来自 x20
,所以 x20
就是第一个外部参数.然后网上找,找到赋值处。
__text:00000001002BF734 MOV X0, X20
__text:00000001002BF738 BL _objc_retain
__text:00000001002BF73C MOV X21, X0
__text:00000001002BF740 STUR X21, [X29,#var_60]
block
的首地址是 var_80
,对应的地址是 __text:00000001002BF70C
,也就是 x19
的地址,所以 [X19,#0x28]
对应的地址应该是 var_58
,然后 分析 x0
,x0
来自 x28
,也就是 [X19,#0x28]
中的 0x28
,然后选中 x28
,看下 mov
赋值:
__text:00000001002BF744 MOV X0, X28
__text:00000001002BF748 BL _objc_retain
__text:00000001002BF74C STUR X0, [X29,#var_58]
这两处,就是第1、2个参数,点中这里的 X20
, X28
寄存器, 看看上面是哪里给这两个寄存器赋值的;
======================================================
Block 结构
struct Block_literal_1 {
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor_1 {
unsigned long int reserved; // NULL
unsigned long int size; // sizeof(struct Block_literal_1)
// optional helper functions
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
void (*dispose_helper)(void *src); // IFF (1<<25)
// required ABI.2010.3.16
const char *signature; // IFF (1<<30)
} *descriptor;
// imported variables
};
名词解释:
isa
: 指针,代表Block
的类型,也说明了Block
是一个对象flags
: 标志位,表示Block
的附加信息,实现Block
的相关代码会用到reserved
: 保留的一个变量void (*invoke)(void *, ...)
: 函数指针,指向的是block
具体的实现地址!Block_descriptor_1
: 表示block
的一些描述信息:如copy
、dispose
方法,Block
函数签名的字符串reserved
: 保留字段size
:block
的大小copy_helper
和dispose_helper
对__block
修饰外部变量使用,基本上配合使用signature
:Block
函数签名的字符串
imported variables
: 这里就是捕获过来的外部变量,Block
内能够访问外部的变量,就是因为这个参数的存在,也是变量的入口
并不是每一个
Block
都有const char *signature
Block
函数签名的字符串的
flags
enum {
// Set to true on blocks that have captures (and thus are not true
// global blocks) but are known not to escape for various other
// reasons. For backward compatiblity with old runtimes, whenever
// BLOCK_IS_NOESCAPE is set, BLOCK_IS_GLOBAL is set too. Copying a
// non-escaping block returns the original block and releasing such a
// block is a no-op, which is exactly how global blocks are handled.
BLOCK_IS_NOESCAPE = (1 << 23),
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code
BLOCK_IS_GLOBAL = (1 << 28),
BLOCK_HAS_STRET = (1 << 29), // IFF BLOCK_HAS_SIGNATURE
BLOCK_HAS_SIGNATURE = (1 << 30),
};
在10.6.ABI 中,(1 « 29) 通常被设置并且总是在运行的时候被忽略的,它只是一个过渡的标记,在转换(过渡)之后没有被删除。此位现在于(1 « 30)配对使用,并表示为对(3«30),用于以下有效位设置组合及其含义:
switch (flags & (3<<29)) {
case (0<<29): 10.6.ABI, no signature field available
case (1<<29): 10.6.ABI, no signature field available
case (2<<29): ABI.2010.3.16, regular calling convention, presence of signature field
case (3<<29): ABI.2010.3.16, stret calling convention, presence of signature field,
}
10.6.ABI 的讨论
Block
可能出现在本地堆栈内存中创建结构的函数中,可以作为全局或者静态局部变量的 Block
变量在初始化表达式中出现。
当分析 Block
表达式的时候,Block
的堆栈结构初始化如下:
static
修饰的Block descriptor
的声明和初始化如下:invoke
函数指针设置成一个函数,该函数将作为Block
的第一个参数,将其余参数(如果有)作为 block 的其他参数,并一起执行.<有异议,并没有看到这个,可能我的机器不是32位
>size
字段设置为Block
结构的大小(size
)- 如果
Block
需要copy_helper
和dispose_helper
函数指针,可以将他们设置为各种的辅助函数.
- 创建一个堆栈(全局)
Block
数据结构,并初始化如下:isa
字段设置为_NSConcreteStackBlock
的地址,那么它是在libSystem
中没有初始化的Block
,如果是一个静态或者文件级别的Block
,那么isa
的地址为_NSConcreteGlobalBlock
flags
字段被设置为 0,除非有变量被导入到Block
,这些变量需要帮助程序进行Block_copy()
和Block_release()
操作,在这种情况下,设置(1<<25)
标志位(flags
).
例子:
^ { printf("hello world\n"); }
64 位机器转换如下
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("hello world\n"); }
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};