反汇编 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 *queuex1即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 *signatureBlock函数签名的字符串的
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的地址为_NSConcreteGlobalBlockflags字段被设置为 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)};