• objc反汇编分析,block函数块为何物?


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

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

    NSString* a = @"abc";
        NSString* b = [NSString stringWithUTF8String:"abc"];
        NSString* c = [@"ab" stringByAppendingString:@"c"];
        __block NSInteger bi = 0;
        __block NSString* bc = c;
        int j = 1;
        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 <+0>:   pushq  %rbp
        0x1055a4651 <+1>:   movq   %rsp, %rbp
        0x1055a4654 <+4>:   subq   $0xf0, %rsp
        0x1055a465b <+11>:  movq   %rdi, -0x8(%rbp)
        0x1055a465f <+15>:  movq   %rsi, -0x10(%rbp)
        0x1055a4663 <+19>:  leaq   0x13c66(%rip), %rsi       ; @"abc"
        0x1055a466a <+26>:  movq   %rsi, -0x18(%rbp)
        0x1055a466e <+30>:  movq   0x14b33(%rip), %rdi       ; (void *)0x00000001058d1b20: NSString
        0x1055a4675 <+37>:  movq   0x14ab4(%rip), %rsi       ; "stringWithUTF8String:"
        0x1055a467c <+44>:  leaq   0x1353c(%rip), %rdx       ; "abc"
        0x1055a4683 <+51>:  movq   0x1399e(%rip), %rax       ; (void *)0x0000000105ad0800: objc_msgSend
        0x1055a468a <+58>:  movq   %rax, -0xe0(%rbp)
        0x1055a4691 <+65>:  callq  *%rax
        0x1055a4693 <+67>:  movq   %rax, -0x20(%rbp)
        0x1055a4697 <+71>:  movq   0x14a9a(%rip), %rsi       ; "stringByAppendingString:"
        0x1055a469e <+78>:  leaq   0x13c4b(%rip), %rdi       ; @"ab"
        0x1055a46a5 <+85>:  leaq   0x13c64(%rip), %rdx       ; @"'c'"
        0x1055a46ac <+92>:  movq   -0xe0(%rbp), %rax
        0x1055a46b3 <+99>:  callq  *%rax
        0x1055a46b5 <+101>: movq   %rax, -0x28(%rbp)

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

    __block NSInteger bi = 0;

    __block NSString* bc = c;

    int j = 1;

    ->  0x1055a46b9 <+105>: movq   $0x0, -0x48(%rbp)
        0x1055a46c1 <+113>: leaq   -0x48(%rbp), %rax
        0x1055a46c5 <+117>: movq   %rax, -0x40(%rbp)
        0x1055a46c9 <+121>: movl   $0x20000000, -0x38(%rbp)
        0x1055a46d0 <+128>: movl   $0x20, -0x34(%rbp)
        0x1055a46d7 <+135>: movq   $0x0, -0x30(%rbp)
        0x1055a46df <+143>: movq   $0x0, -0x78(%rbp)
        0x1055a46e7 <+151>: leaq   -0x78(%rbp), %rdx
        0x1055a46eb <+155>: movq   %rdx, -0x70(%rbp)
        0x1055a46ef <+159>: movl   $0x52000000, -0x68(%rbp)
        0x1055a46f6 <+166>: movl   $0x30, -0x64(%rbp)
        0x1055a46fd <+173>: leaq   -0x204(%rip), %rsi        ; __Block_byref_object_copy_ at ViewController.mm:74
        0x1055a4704 <+180>: movq   %rsi, -0x60(%rbp)
        0x1055a4708 <+184>: leaq   -0x1cf(%rip), %rsi        ; __Block_byref_object_dispose_ at ViewController.mm:74
        0x1055a470f <+191>: movq   %rsi, -0x58(%rbp)
        0x1055a4713 <+195>: movq   -0x28(%rbp), %rsi
        0x1055a4717 <+199>: movq   %rsi, -0x50(%rbp)
        0x1055a471b <+203>: 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 <+210>: movq   0x138e7(%rip), %rsi       ; (void *)0x000000010875e050: _NSConcreteStackBlock
        0x1055a4729 <+217>: movq   %rsi, -0xc8(%rbp)
        0x1055a4730 <+224>: movl   $0xc2000000, -0xc0(%rbp)
        0x1055a473a <+234>: movl   $0x0, -0xbc(%rbp)
        0x1055a4744 <+244>: leaq   0x115(%rip), %rsi         ; __31-[ViewController testNSString3]_block_invoke at ViewController.mm:135
        0x1055a474b <+251>: movq   %rsi, -0xb8(%rbp)
        0x1055a4752 <+258>: leaq   0x13b47(%rip), %rsi       ; __block_descriptor_tmp
        0x1055a4759 <+265>: movq   %rsi, -0xb0(%rbp)
        0x1055a4760 <+272>: movq   -0x18(%rbp), %rsi
        0x1055a4764 <+276>: movq   %rsi, -0xa8(%rbp)
        0x1055a476b <+283>: movq   -0x20(%rbp), %rsi
        0x1055a476f <+287>: movq   %rsi, -0xa0(%rbp)
        0x1055a4776 <+294>: movq   -0x28(%rbp), %rsi
        0x1055a477a <+298>: movq   %rsi, -0x98(%rbp)
        0x1055a4781 <+305>: movq   %rdx, -0x90(%rbp)
        0x1055a4788 <+312>: movq   %rax, -0x88(%rbp)
        0x1055a478f <+319>: movl   -0x7c(%rbp), %ecx
        0x1055a4792 <+322>: movl   %ecx, -0x80(%rbp)
        0x1055a4795 <+325>: leaq   -0xc8(%rbp), %max
        0x1055a479c <+332>: 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 <+339>: movq   -0x70(%rbp), %rax
        0x1055a47a7 <+343>: movq   0x28(%rax), %rdi
        0x1055a47ab <+347>: movq   0x14986(%rip), %rsi       ; "stringByAppendingString:"
        0x1055a47b2 <+354>: leaq   0x13bb7(%rip), %rdx       ; @"'d'"
        0x1055a47b9 <+361>: movq   -0xe0(%rbp), %rax
        0x1055a47c0 <+368>: callq  *%rax
        0x1055a47c2 <+370>: movq   %rax, -0xe8(%rbp)
        0x1055a47c9 <+377>: jmp    0x1055a47ce               ; <+382> at ViewController.mm:145
        0x1055a47ce <+382>: movq   -0x70(%rbp), %rax
        0x1055a47d2 <+386>: movq   -0xe8(%rbp), %rcx
        0x1055a47d9 <+393>: movq   %rcx, 0x28(%rax)

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

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

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

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

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

    _block_invoke:
        0x1055a4860 <+0>:   pushq  %rbp
        0x1055a4861 <+1>:   movq   %rsp, %rbp
        0x1055a4864 <+4>:   subq   $0x50, %rsp
        0x1055a4868 <+8>:   movq   %rdi, -0x8(%rbp)
    ->  0x1055a486c <+12>:  movq   %rdi, %rax
        0x1055a486f <+15>:  movq   %rax, -0x10(%rbp)
        0x1055a4873 <+19>:  movq   0x20(%rdi), %rax
        0x1055a4877 <+23>:  movq   %rax, -0x18(%rbp)
        0x1055a487b <+27>:  movq   0x28(%rdi), %rax
        0x1055a487f <+31>:  movq   %rax, -0x20(%rbp)
        0x1055a4883 <+35>:  movq   0x30(%rdi), %rax
        0x1055a4887 <+39>:  movq   %rax, -0x28(%rbp)
        0x1055a488b <+43>:  movq   0x38(%rdi), %rax
        0x1055a488f <+47>:  movq   0x8(%rax), %rax
        0x1055a4893 <+51>:  movq   0x28(%rax), %rax
        0x1055a4897 <+55>:  movq   %rax, -0x30(%rbp)
        0x1055a489b <+59>:  movq   0x40(%rdi), %rax
        0x1055a489f <+63>:  movq   0x8(%rax), %rax
        0x1055a48a3 <+67>:  movq   0x18(%rax), %rax
        0x1055a48a7 <+71>:  movq   %rax, -0x38(%rbp)
        0x1055a48ab <+75>:  movq   -0x30(%rbp), %rax
        0x1055a48af <+79>:  movq   0x148a2(%rip), %rsi       ; "hash"
        0x1055a48b6 <+86>:  movq   %rdi, -0x48(%rbp)
        0x1055a48ba <+90>:  movq   %rax, %rdi
        0x1055a48bd <+93>:  callq  0x1055b62e2               ; symbol stub for: objc_msgSend
        0x1055a48c2 <+98>:  movq   -0x48(%rbp), %rsi
        0x1055a48c6 <+102>: movl   0x48(%rsi), %ecx
        0x1055a48c9 <+105>: movl   %ecx, -0x3c(%rbp)
        0x1055a48cc <+108>: movq   %rax, -0x50(%rbp)
        0x1055a48d0 <+112>: addq   $0x50, %rsp
        0x1055a48d4 <+116>: popq   %rbp
        0x1055a48d5 <+117>: retq   

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

  • 相关阅读:
    创建一个 mac 的后台进程(daemon)
    Centos 7创建一个服务
    MAC配置VIM环境
    Spark源码剖析(九):TaskScheduler原理与源码剖析
    Spark源码剖析(八):stage划分原理与源码剖析
    教你如何写递归(数学归纳法,干货强推!)
    Spark源码剖析(七):Job触发流程原理与源码剖析
    剑指offer:变态跳台阶
    Spark源码剖析(六):Worker原理与源码剖析
    Spark源码剖析(五):Master原理与源码剖析(下)
  • 原文地址:https://www.cnblogs.com/bbqzsl/p/5134885.html
Copyright © 2020-2023  润新知