当我们调试Block
时,有时会招致不知道这个Block
是在哪里被调用,找起来相当麻烦,但是通过LLDB
可能方便很多。
0x01 准备条件
self.myBlock = ^{
// 此处会造成循环引用,导致此对象无法被释放
self.name = @"测试";
NSLog(@"测试block");
};
0x02. 调试视图
a. 我们在Debug Memory Graph
可以看到以下视图

b. 操作以下区域

c. 查询调用block
的函数信息

步骤解释:
- 找到
Block
的调用地址addr
后,找到block
对象内部函数指针的地址:addr+16
- 反汇编函数指针地址信息,得到调用
block
的函数位置
0x03 问题分析
LLDB命令解释:
# dis s
dis -s <address-expression> ( --start-address <address-expression> )
Address at which to start disassembling.
开始反汇编的起始地址
0x01 为什么block
入口地址要加16
才是函数指针地址
- 写一段简单代码
void (^myBlock)(void) = ^{
NSLog(@"this is a test file");
};
myBlock();
- 将其转换为汇编后为:
使用命令:clang -rewrite-objc main.m -o main.mm
/*
__main_block_impl_0: block类型
__main_block_func_0: 函数指针
__main_block_desc_0_DATA: 参数
*/
void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
/*
调用:通过block找到函数指针,再入参调用
(myBlock->FuncPtr)(myBlock);
*/
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
block
类型摘要:
struct __main_block_impl_0 { // 首地址假设为addr
// struct __block_impl impl; 这个类型就是下面的结构体,可以直接替换展开出来
struct __block_impl {
void *isa; // 对象的标识 首地址:addr
int Flags; // 标志信息 首地址:addr + 8
int Reserved; // 保留字段 首地址:addr + 8 + 4
void *FuncPtr;// 函数指针 首地址:addr + 8 + 4 + 4
};
....
};
由上面示例代码可以看到block
首地址偏移16
字节后就是函数指针首地址
0x02 反汇编函数指针内容的两种方式
- 找到内容直接反汇编
# x/g addr + 16
(lldb) x/g 0x6000017feca0+16
0x6000017fecb0: 0x000000010e4d80b0
(lldb) dis -s 0x000000010e4d80b0
CrashCollectDemo`__32-[TmpViewController viewDidLoad]_block_invoke:
0x10e4d80b0 <+0>: pushq %rbp
0x10e4d80b1 <+1>: movq %rsp, %rbp
0x10e4d80b4 <+4>: subq $0x10, %rsp
0x10e4d80b8 <+8>: leaq 0x4159(%rip), %rax ; @
0x10e4d80bf <+15>: movq %rdi, -0x8(%rbp)
0x10e4d80c3 <+19>: movq %rdi, %rcx
0x10e4d80c6 <+22>: movq %rcx, -0x10(%rbp)
0x10e4d80ca <+26>: movq 0x20(%rdi), %rcx
- 直接强转获取指针内容
dis -s *(void**)(addr+16)
# 由于addr+16 是函数指针的地址,而我们需要的是函数指针中的内容
# 通过*(void**)(addr+16) 可以获取到指针中的内容, 注意void**没有空格
网友评论