上一篇向大家介绍了__block变量的反汇编和它的伪代码,本篇函数块block,通常定义成原型(^){},它在反汇编中是什么东西。

我们先定义将要反汇编的例子,为减少篇幅例子采用non-arc环境。

NSString* a = @"abc";
NSString* b = [NSString stringWithUTF8String:"abc"];
NSString* c = [@"ab" stringByAppendingString:@"c"];
__block NSInteger bi = ;
__block NSString* bc = c;
int j = ;
block = ^{
id x = a;
id y = b;
id z = c;
id o = bc;
NSInteger i = bi;
[o hash];
int k = j;
};
bc = [bc stringByAppendingString:@"d"];
block();

下面将按代码切分来分段贴汇编代码:

首先,这个代码在前面的篇章中,已经举例了无数次。

NSString* a = @"abc";

NSString* b = [NSString stringWithUTF8String:"abc"];

NSString* c = [@"ab" stringByAppendingString:@"c"];

    0x1055a4650 <+>:   pushq  %rbp
0x1055a4651 <+>: movq %rsp, %rbp
0x1055a4654 <+>: subq $0xf0, %rsp
0x1055a465b <+>: movq %rdi, -0x8(%rbp)
0x1055a465f <+>: movq %rsi, -0x10(%rbp)
0x1055a4663 <+>: leaq 0x13c66(%rip), %rsi ; @"abc"
0x1055a466a <+>: movq %rsi, -0x18(%rbp)
0x1055a466e <+>: movq 0x14b33(%rip), %rdi ; (void *)0x00000001058d1b20: NSString
0x1055a4675 <+>: movq 0x14ab4(%rip), %rsi ; "stringWithUTF8String:"
0x1055a467c <+>: leaq 0x1353c(%rip), %rdx ; "abc"
0x1055a4683 <+>: movq 0x1399e(%rip), %rax ; (void *)0x0000000105ad0800: objc_msgSend
0x1055a468a <+>: movq %rax, -0xe0(%rbp)
0x1055a4691 <+>: callq *%rax
0x1055a4693 <+>: movq %rax, -0x20(%rbp)
0x1055a4697 <+>: movq 0x14a9a(%rip), %rsi ; "stringByAppendingString:"
0x1055a469e <+>: leaq 0x13c4b(%rip), %rdi ; @"ab"
0x1055a46a5 <+>: leaq 0x13c64(%rip), %rdx ; @"'c'"
0x1055a46ac <+>: movq -0xe0(%rbp), %rax
0x1055a46b3 <+>: callq *%rax
0x1055a46b5 <+>: movq %rax, -0x28(%rbp)

接着是__block变量的定义和初始化,上一篇已经介绍过。

__block NSInteger bi = 0;

__block NSString* bc = c;

int j = 1;

->  0x1055a46b9 <+>: movq   $0x0, -0x48(%rbp)
0x1055a46c1 <+>: leaq -0x48(%rbp), %rax
0x1055a46c5 <+>: movq %rax, -0x40(%rbp)
0x1055a46c9 <+>: movl $0x20000000, -0x38(%rbp)
0x1055a46d0 <+>: movl $0x20, -0x34(%rbp)
0x1055a46d7 <+>: movq $0x0, -0x30(%rbp)
0x1055a46df <+>: movq $0x0, -0x78(%rbp)
0x1055a46e7 <+>: leaq -0x78(%rbp), %rdx
0x1055a46eb <+>: movq %rdx, -0x70(%rbp)
0x1055a46ef <+>: movl $0x52000000, -0x68(%rbp)
0x1055a46f6 <+>: movl $0x30, -0x64(%rbp)
0x1055a46fd <+>: leaq -0x204(%rip), %rsi ; __Block_byref_object_copy_ at ViewController.mm:74
0x1055a4704 <+>: movq %rsi, -0x60(%rbp)
0x1055a4708 <+>: leaq -0x1cf(%rip), %rsi ; __Block_byref_object_dispose_ at ViewController.mm:74
0x1055a470f <+>: movq %rsi, -0x58(%rbp)
0x1055a4713 <+>: movq -0x28(%rbp), %rsi
0x1055a4717 <+>: movq %rsi, -0x50(%rbp)
0x1055a471b <+>: movl $0x1, -0x7c(%rbp)

本篇的主角出场了,函数块block。

block = ^{

id x = a;

id y = b;

id z = c;

id o = bc;

NSInteger i = bi;

[o hash];

int k = j;

};

    0x1055a4722 <+>: movq   0x138e7(%rip), %rsi       ; (void *)0x000000010875e050: _NSConcreteStackBlock
0x1055a4729 <+>: movq %rsi, -0xc8(%rbp)
0x1055a4730 <+>: movl $0xc2000000, -0xc0(%rbp)
0x1055a473a <+>: movl $0x0, -0xbc(%rbp)
0x1055a4744 <+>: leaq 0x115(%rip), %rsi ; __31-[ViewController testNSString3]_block_invoke at ViewController.mm:135
0x1055a474b <+>: movq %rsi, -0xb8(%rbp)
0x1055a4752 <+>: leaq 0x13b47(%rip), %rsi ; __block_descriptor_tmp
0x1055a4759 <+>: movq %rsi, -0xb0(%rbp)
0x1055a4760 <+>: movq -0x18(%rbp), %rsi
0x1055a4764 <+>: movq %rsi, -0xa8(%rbp)
0x1055a476b <+>: movq -0x20(%rbp), %rsi
0x1055a476f <+>: movq %rsi, -0xa0(%rbp)
0x1055a4776 <+>: movq -0x28(%rbp), %rsi
0x1055a477a <+>: movq %rsi, -0x98(%rbp)
0x1055a4781 <+>: movq %rdx, -0x90(%rbp)
0x1055a4788 <+>: movq %rax, -0x88(%rbp)
0x1055a478f <+>: movl -0x7c(%rbp), %ecx
0x1055a4792 <+>: movl %ecx, -0x80(%rbp)
0x1055a4795 <+>: leaq -0xc8(%rbp), %max
0x1055a479c <+>: movq %rax, 0x14bdd(%rip) ; block

可以看到这是一个关于实例_NSConcreteStackBlock的构造初始化,并且将引用到函数块定义范围之外的变量一一送入它的私有栈,__block NSInteger和__block NSString*分别是以__block<NSInteger>*和__block<NSString*>*送入了私有栈,template<ty> __block模板的伪代码请参考我的上一篇《反汇编分析__block》。第二点是有两个地址_block_invoke和__block_descriptor_tmp。_block_invoke地址在rip下方不远处,也就是函数地的执行代码。另一个地址类型不详,通过查看内存可逻辑推断出它的作用。

&__block_descriptor_tmp
0x1086652a0: 0x0000000000000000
0x1086652a8: 0x000000000000004c
0x1086652b0: 0x00000001086518e0 // __copy_helper_block_
0x1086652b8: 0x00000001086519a0 // __destroy_helper_block_

现在我们清楚看到,在函数块block定义所在的函数体代码附近,生成了三个函数块block使用到的函数,分别是_block_invoke, __copy_helper_block_和__destroy_helper_block_。现在就可以写出反c++伪代码:

大家可以很容易看出,这不就是一个带context的callback。但是大家要注意,这个block实例是一个_NSConcreteStackBlock实例,也就是说stack-based,它还不能用作作用范围外的回调或调度,本篇不作深入介绍,留在后面的章节中。

回到我们的例子中,接着是对__block NSString* bc赋值,结合上一篇的定义,大家现在清楚明白为什么__block变量可以被修改值了,但大家也要注意这个__block变量也是一个stack-based的封装体,送入block块的私有栈的指针是指向这个stack-based的storage。

bc = [bc stringByAppendingString:@"d"];

    0x1055a47a3 <+>: movq   -0x70(%rbp), %rax
0x1055a47a7 <+>: movq 0x28(%rax), %rdi
0x1055a47ab <+>: movq 0x14986(%rip), %rsi ; "stringByAppendingString:"
0x1055a47b2 <+>: leaq 0x13bb7(%rip), %rdx ; @"'d'"
0x1055a47b9 <+>: movq -0xe0(%rbp), %rax
0x1055a47c0 <+>: callq *%rax
0x1055a47c2 <+>: movq %rax, -0xe8(%rbp)
0x1055a47c9 <+>: jmp 0x1055a47ce ; <+382> at ViewController.mm:145
0x1055a47ce <+>: movq -0x70(%rbp), %rax
0x1055a47d2 <+>: movq -0xe8(%rbp), %rcx
0x1055a47d9 <+>: movq %rcx, 0x28(%rax)

函数块被调用了,结合上面伪代码就知道是调用block->_block_invoke(block):

    0x1055a47dd <+>: movq   0x14b9c(%rip), %rax       ; block
0x1055a47e4 <+>: movq 0x10(%rax), %rdx
0x1055a47e8 <+>: movq %rax, %rdi
0x1055a47eb <+>: callq *%rdx

__block变量,block函数块定义所在的函数体结束了,析构。

destructures of __block variables.
0x1055a47ed <+>: jmp 0x1055a47f2 ; <+418> at ViewController.mm:146
0x1055a47f2 <+>: movl $0x8, %esi
0x1055a47f7 <+>: leaq -0x78(%rbp), %rax
0x1055a47fb <+>: movq %rax, %rdi
0x1055a47fe <+>: callq 0x1055b62fa ; symbol stub for: _Block_object_dispose
0x1055a4803 <+>: movl $0x8, %esi
0x1055a4808 <+>: leaq -0x48(%rbp), %rax
0x1055a480c <+>: movq %rax, %rdi
0x1055a480f <+>: callq 0x1055b62fa ; symbol stub for: _Block_object_dispose
0x1055a4814 <+>: addq $0xf0, %rsp
0x1055a481b <+>: popq %rbp
0x1055a481c <+>: retq

最后贴出block的反汇编代码,block访问了它的私有栈(,或者说context)。请大家想一想,在私有栈里被送入的是__block变量封装体在上面结束了函数体的栈帧中的地址,如果在上面的函数体外调用会有什么结果?

_block_invoke:
0x1055a4860 <+>: pushq %rbp
0x1055a4861 <+>: movq %rsp, %rbp
0x1055a4864 <+>: subq $0x50, %rsp
0x1055a4868 <+>: movq %rdi, -0x8(%rbp)
-> 0x1055a486c <+>: movq %rdi, %rax
0x1055a486f <+>: movq %rax, -0x10(%rbp)
0x1055a4873 <+>: movq 0x20(%rdi), %rax
0x1055a4877 <+>: movq %rax, -0x18(%rbp)
0x1055a487b <+>: movq 0x28(%rdi), %rax
0x1055a487f <+>: movq %rax, -0x20(%rbp)
0x1055a4883 <+>: movq 0x30(%rdi), %rax
0x1055a4887 <+>: movq %rax, -0x28(%rbp)
0x1055a488b <+>: movq 0x38(%rdi), %rax
0x1055a488f <+>: movq 0x8(%rax), %rax
0x1055a4893 <+>: movq 0x28(%rax), %rax
0x1055a4897 <+>: movq %rax, -0x30(%rbp)
0x1055a489b <+>: movq 0x40(%rdi), %rax
0x1055a489f <+>: movq 0x8(%rax), %rax
0x1055a48a3 <+>: movq 0x18(%rax), %rax
0x1055a48a7 <+>: movq %rax, -0x38(%rbp)
0x1055a48ab <+>: movq -0x30(%rbp), %rax
0x1055a48af <+>: movq 0x148a2(%rip), %rsi ; "hash"
0x1055a48b6 <+>: movq %rdi, -0x48(%rbp)
0x1055a48ba <+>: movq %rax, %rdi
0x1055a48bd <+>: callq 0x1055b62e2 ; symbol stub for: objc_msgSend
0x1055a48c2 <+>: movq -0x48(%rbp), %rsi
0x1055a48c6 <+>: movl 0x48(%rsi), %ecx
0x1055a48c9 <+>: movl %ecx, -0x3c(%rbp)
0x1055a48cc <+>: movq %rax, -0x50(%rbp)
0x1055a48d0 <+>: addq $0x50, %rsp
0x1055a48d4 <+>: popq %rbp
0x1055a48d5 <+>: retq

最后谢谢路过的你观看,下一篇将反汇编介绍__block变量和函数块block如何从stack-based中蜕变的。

objc反汇编分析,block函数块为何物?的更多相关文章

  1. objc反汇编分析,手工逆向libsystem_blocks.dylib

    上一篇<block函数块为何物?>介绍了在函数中定义的block函数块的反汇编实现,我在文中再三指出__block变量和block函数块自始还都是stack-based的,还不完全适合在离 ...

  2. objc反汇编分析__strong和__weak

    如题所说反汇编看__strong和__weak的真实样子,代码列举自然多,篇幅长不利于阅读,我就先搬出结论,后面是分析. 在NON-ARC环境,__strong和__weak不起作用.相反在ARC环境 ...

  3. 反汇编分析objc函数枢纽objc_msgSend

    在分析objc_msgSend之前,先来搞清楚另一个问题. 函数是什么?可能会答 void foo(void) {} 像这样就是一个函数.或者函数包括函数原型和函数定义,是一段执行某样功能的机器代码. ...

  4. arm汇编进入C函数分析,C函数压栈,出栈,传参,返回值

    环境及代码介绍 环境和源码 由于有时候要透彻的理解C里面的一些细节问题,所有有必要看看汇编,首先这一切的开始就是从汇编代码进入C的main函数过程.这里不使用编译器自动生成的这部分汇编代码,因为编译器 ...

  5. block(代码块)的介绍以及使用方法和变量之间的关系

    http://blog.csdn.net/menxu_work/article/details/8762848 block(代码块)的介绍以及使用方法和变量之间的关系 block(代码块)的介绍以及使 ...

  6. block代码块介绍

    关于block的简单介绍 什么是block? Block是C语言的一个语法特性,同时也是C语言的运行时特性,它很像C中的函数指针,因为你可以像使用函数指针一样的去使用block对象:它也很像C++中的 ...

  7. ios开发 block语句块

    ios开发 block语句块 1.block 理解为匿名函数 2.block变量的定义 //定义block变量,^表示定义block //技巧:函数名左右加括号,在函数名前面在加^ void (^bl ...

  8. Linux下简单C语言小程序的反汇编分析

    韩洋原创作品转载请注明出处<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 写在开始,本文为因为参加MOO ...

  9. 可以在函数中间打点了,以分析bpf_prog_load函数为例

    可以在函数中间打点了, sudo stap -L 'process("./test").statement("func@test.c:10")' //12.10 ...

随机推荐

  1. Class constructor FileManager cannot be invoked without 'new'

    bug:今天项目重新安装依赖打包的时候出现错误:Class constructor FileManager cannot be invoked without 'new' 解决:尝试了很多种解决方案, ...

  2. Redis(八)理解内存

    Redis所有的数据都存在内存中,当前内存虽然越来越便宜,但跟廉价的硬盘相比成本还是比较昂贵,因此如何高效利用Redis内存变得非常重要. 高效利用Redis内存首先需要理解Redis内存消耗在哪里, ...

  3. Java 用单向循环链表实现 约瑟夫问题

    public class lianbiao2 { class Node{ Node next; int number; public Node getNext() { return next; } p ...

  4. PyQt图形化布局

    安装PyQt第三方库 pip install PyQt5 安装Qt Designer(Qt的布局工具) pip install PyQt5-tools PyChram设置Qt工具 配置Qt Desig ...

  5. JC的小苹果 逆矩阵

    这题主要有两种做法:1种是用逆矩阵,转移时无须高斯消元.2是将常数项回代.这里主要介绍第一种. 首先题里少个条件:点权非负.设f [ i ][ j ]表示hp为i时,到达j点的期望次数. 那么若点权为 ...

  6. NOIP模拟 10

    (果然题目描述越人畜无害,题目难度越丧心病狂) (感觉T2大大锻炼了我的码力) T1 辣鸡 看见自己作为题目标题出现在模拟赛中,我内心无比激动 看完题面,一个N^2暴力思路已经成形 然后开始拼命想正解 ...

  7. [AspNetCore 3.0 ] Blazor 服务端组件 Render, RenderFragment ,RenderTreeBuilder, CascadingValue/CascadingParameter 等等

    一.组件 支撑Blazor的是微软的两大成熟技术,Razor模板和SignalR,两者的交汇点就是组件.通常,我们从ComponentBase派生的类型,或者创建的.razor 文件,就可以称作组件. ...

  8. 『题解』洛谷P3384 【模板】树链剖分

    Problem Portal Portal1: Luogu Description 如题,已知一棵包含\(N\)个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作\(1\): ...

  9. php PDO getlastsql写法

    php PDO getlastsql写法有些时候 运行失败需要查看 sql语句 原型有没有语法错误 这个时候就用 下面的函数就是把问号替换成 值 就可以看到原型了<pre>function ...

  10. Linux下mysql 多实例安装配置

    首先我们要清楚什么是多实例?所谓多实例就是用多个配置文件来启动多个不同端口的进程,以不同的端口的形式为外提供服务.明白了多实例 我们下面的操作和配置就一目了然了首先我们要安装一套基础的应用程序,也就是 ...