美文网首页
Peeking Stack Frame

Peeking Stack Frame

作者: vedon_fu | 来源:发表于2017-05-07 15:31 被阅读180次

    为了更加清晰的了解stack frame 是什么,下面写了测试的代码并附上debug信息 。可能比较冗长,耐心查看必有收获!

    - (void)viewDidLoad {
        [super viewDidLoad];
        [self launchUCBrowser];
    }
    - (void)didReceiveMemoryWarning {
        [super didReceiveMemoryWarning];
        // Dispose of any resources that can be recreated.
    }
    - (void)launchUCBrowser
    {
        [self clickSearchBar];
    }
    - (void)clickSearchBar
    {
        [self typing];
    }
    - (void)typing
    {
        NSLog(@"Hello world");
    }
    

    首先查看一下当前程序加载到内存的起始地址:

    (lldb) image list
    [  0] B8DF937E-E66C-3633-AEE8-87CFE8EA18DE 0x00000001000d8000 /Users/vedon/Library/Developer/Xcode/DerivedData/StackFrame-blkbdwapfezplhfqiynvukzdvxgc/Build/Products/Debug-iphoneos/StackFrame.app/StackFrame 
    

    分别在每个函数执行的位置打上断点,并把当前的寄存器的值打印出来。以下去除了部分数据的寄存器数据:

    (lldb) register read
    General Purpose Registers:
            x0 = 0x0000000109d0aab0
            x1 = 0x000000018b331732  "viewDidLoad"
            x2 = 0x0000000000000001
            fp = 0x000000016fd25f70
            lr = 0x00000001000de710  StackFrame`-[ViewController viewDidLoad] + 68 at ViewController.m:17
            sp = 0x000000016fd25f50
            pc = 0x00000001000de718  StackFrame`-[ViewController viewDidLoad] + 76 at ViewController.m:18
    (lldb) register read
    General Purpose Registers:
            x0 = 0x0000000109d0aab0
            x1 = 0x00000001000dec90  "launchUCBrowser"
            x2 = 0x0000000000000001
            fp = 0x000000016fd25f40
            lr = 0x00000001000de728  StackFrame`-[ViewController viewDidLoad] + 92 at ViewController.m:19
            sp = 0x000000016fd25f30
            pc = 0x00000001000de7a0  StackFrame`-[ViewController launchUCBrowser] + 28 at ViewController.m:31
    (lldb) register read
    General Purpose Registers:
            x0 = 0x0000000109d0aab0
            x1 = 0x00000001000decb8  "clickSearchBar"
            x2 = 0x0000000000000001
            fp = 0x000000016fd25f20
            lr = 0x00000001000de7ac  StackFrame`-[ViewController launchUCBrowser] + 40 at ViewController.m:32
            sp = 0x000000016fd25f10
            pc = 0x00000001000de7d4  StackFrame`-[ViewController clickSearchBar] + 28 at ViewController.m:36
    (lldb) register read
    General Purpose Registers:
            x0 = 0x0000000109d0aab0
            x1 = 0x00000001000decc7  "typing"
            x2 = 0x0000000000000001
            fp = 0x000000016fd25f00
            lr = 0x00000001000de7e0  StackFrame`-[ViewController clickSearchBar] + 40 at ViewController.m:37
            sp = 0x000000016fd25ef0
            pc = 0x00000001000de800  StackFrame`-[ViewController typing] + 20 at ViewController.m:41
    

    可以看到:

    • x0 的地址都一样,代表的self
    • x1 就是函数的selector
    • lr 保存了方法调用完后,返回的地址。
    • pc 就是当前执行的指令地址。

    fp 与 sp 是一个frame 的界限。如下表:

    function name fp sp desc
    viewDidLoad 0x000000016fd25f70 0x000000016fd25f50 此fp 和sp 构成了上caller的frame
    launchUCBrowser 0x000000016fd25f40 0x000000016fd25f30 此fp 和sp 构成了上viewDidLoad的frame
    clickSearchBar 0x000000016fd25f20 0x000000016fd25f10 此fp 和sp 构成了上launchUCBrowser的frame
    typing 0x000000016fd25f00 0x000000016fd25ef0 此fp 和sp 构成了上clickSearchBar的frame

    下面是demo 在hopper 内容:

    ; ================ B E G I N N I N G   O F   P R O C E D U R E ================
    
    
                         -[ViewController viewDidLoad]:
    00000001000066cc         sub        sp, sp, #0x30                               ; Objective C Implementation defined at 0x1000080f8 (instance method), DATA XREF=0x1000080f8
    // sp = sp - 0x30
    00000001000066d0         stp        x29, x30, [sp, #0x20]
    //将x29(fp) ,x30(lr) 存入 sp + 0x20。sp = sp + 0x20 (指针下移),注意这里是上一次方法调用存进来的,用于方法调用完之后恢复上一次调用。
    00000001000066d4         add        x29, sp, #0x20
    //设置当前函数的fp = sp + 0x20
    00000001000066d8         mov        x8, sp
    //将sp 的值保存到x8
    00000001000066dc         adrp       x9, #0x100008000
    //把当前pc的值后12bit 设为0,并把0x100008000 << 12,然后把两者相加存入x9.
    00000001000066e0         add        x9, x9, #0xcd0                              ; @selector(viewDidLoad)
    //x9 = x9 + 0xcd0 定位到viewDidLoad
    00000001000066e4         adrp       x10, #0x100008000
    //同上
    00000001000066e8         add        x10, x10, #0xd08                            ; 0x100008d08
    00000001000066ec         stur       x0, [x29, #-0x8]
    //把x0 存入 fp - 0x8 
    00000001000066f0         str        x1, [sp, #0x10]
    // 把x1 存入 sp + 0x10
    00000001000066f4         ldur       x0, [x29, #-0x8]
    //把存在 fp - 0x8 的内容存入x0 ,这里可以看到和上面的操作是相对的,刚被存到sp + 0x10 ,又马上拿出来。如果编译器允许基本的编译优化,这些多余的指令会被删除。
    00000001000066f8         str        x0, sp
    //把 x0 存入 sp
    00000001000066fc         ldr        x10, x10
    0000000100006700         str        x10, [sp, #0x8]
    // 把x10 存入 sp + 0x8
    0000000100006704         ldr        x1, x9
    // 把x9 存入x1 ,x9 就是viewDidload 的地址
    0000000100006708         mov        x0, x8
    // 把 x8 存入 x0 
    000000010000670c         bl         imp___stubs__objc_msgSendSuper2
    // 执行 [super viewDidLoad]
    0000000100006710         adrp       x8, #0x100008000
    // 把当前pc的值后12bit 设为0,并把0x100008000 << 12,然后把两者相加存入x8.
    0000000100006714         add        x8, x8, #0xcd8                              ; @selector(launchUCBrowser)
    //  x8 = x8 +  0xcd8 ,定位到launchUCBrowser
    0000000100006718         ldur       x9, [x29, #-0x8]
    // 把x29 - 0x8 存入x9
    000000010000671c         ldr        x1, x8
    //把x8 存入 x1
    0000000100006720         mov        x0, x9
    // 把 x9 存入x0 
    0000000100006724         bl         imp___stubs__objc_msgSend
    0000000100006728         ldp        x29, x30, [sp, #0x20]
    //从sp + 0x20 取数据,存入 fp 和sp . (ldp/stp 从栈取/存数据)
    000000010000672c         add        sp, sp, #0x30
    //sp = sp + 0x30
    0000000100006730         ret
    
                         -[ViewController launchUCBrowser]:
    0000000100006784         sub        sp, sp, #0x20                               ; Objective C Implementation defined at 0x100008128 (instance method), DATA XREF=0x100008128
    // sp = sp - 0x20 
    0000000100006788         stp        x29, x30, [sp, #0x10]
    // 把 fp 和 sp 存入 sp + 0x10 ,并且 sp = sp + 0x10
    000000010000678c         add        x29, sp, #0x10
    // fp = sp + 0x10
    0000000100006790         adrp       x8, #0x100008000
    //把当前pc的值后12bit 设为0,并把0x100008000 << 12,然后把两者相加存入x8
    0000000100006794         add        x8, x8, #0xce8                              ; @selector(clickSearchBar)
    //x8 = x8 +  0xcd8 ,定位到launchUCBrowser
    0000000100006798         str        x0, [sp, #0x8]
    // 把x0 存入 sp + 0x8
    000000010000679c         str        x1, sp
    // 把 x1 存入 sp
    00000001000067a0         ldr        x0, [sp, #0x8]
    // 把 sp + 0x8 存入 x0
    00000001000067a4         ldr        x1, x8
    // 把 x8 存入 x1
    00000001000067a8         bl         imp___stubs__objc_msgSend
    00000001000067ac         ldp        x29, x30, [sp, #0x10]
    //从sp + 0x20 取数据,存入 fp 和sp . (ldp/stp 从栈取/存数据)
    00000001000067b0         add        sp, sp, #0x20
    00000001000067b4         ret
                            ; endp
            ; ================ B E G I N N I N G   O F   P R O C E D U R E ================
    
    
                         -[ViewController clickSearchBar]:
    00000001000067b8         sub        sp, sp, #0x20                               ; Objective C Implementation defined at 0x100008140 (instance method), DATA XREF=0x100008140
    00000001000067bc         stp        x29, x30, [sp, #0x10]
    00000001000067c0         add        x29, sp, #0x10
    00000001000067c4         adrp       x8, #0x100008000
    00000001000067c8         add        x8, x8, #0xcf0                              ; @selector(typing)
    00000001000067cc         str        x0, [sp, #0x8]
    00000001000067d0         str        x1, sp
    00000001000067d4         ldr        x0, [sp, #0x8]
    00000001000067d8         ldr        x1, x8
    00000001000067dc         bl         imp___stubs__objc_msgSend
    00000001000067e0         ldp        x29, x30, [sp, #0x10]
    00000001000067e4         add        sp, sp, #0x20
    00000001000067e8         ret
                            ; endp
    
    
            ; ================ B E G I N N I N G   O F   P R O C E D U R E ================
    
    
                         -[ViewController typing]:
    00000001000067ec         sub        sp, sp, #0x20                               ; Objective C Implementation defined at 0x100008158 (instance method), DATA XREF=0x100008158
    00000001000067f0         stp        x29, x30, [sp, #0x10]
    00000001000067f4         add        x29, sp, #0x10
    00000001000067f8         str        x0, [sp, #0x8]
    00000001000067fc         str        x1, sp
    0000000100006800         adrp       x0, #0x100008000                            ; argument #1 for method imp___stubs__NSLog
    0000000100006804         add        x0, x0, #0x60                               ; @"Hello world"
    0000000100006808         bl         imp___stubs__NSLog
    000000010000680c         ldp        x29, x30, [sp, #0x10]
    0000000100006810         add        sp, sp, #0x20
    0000000100006814         ret
    

    不知道大家有没有发现,

    function name assembly range
    viewDidLoad x29, x30, [sp, #0x20] 0x000000016fd25f70 - 0x000000016fd25f50 = 0x20
    launchUCBrowser x29, x30, [sp, #0x10] 0x000000016fd25f40 - 0x000000016fd25f30 = 0x10

    压栈的范围是有调用函数的frame 决定的。通过fp 和sp ,就可以得出所有函数的调用顺序。

    To be continue

    相关文章

      网友评论

          本文标题:Peeking Stack Frame

          本文链接:https://www.haomeiwen.com/subject/uewutxtx.html