swift结构体和类的汇编指令分析

275 阅读7分钟

1.结构体在栈内分析

func test() {
    struct Point {
        var x: Int
        var y: Int
    }

    var p1 = Point(x: 10, y: 20)
    p1.x = 11
    p1.y = 22
}

test()



Swift测试`test():
    0x100002940 <+0>:  pushq  %rbp
    0x100002941 <+1>:  movq   %rsp, %rbp
    0x100002944 <+4>:  subq   $0x10, %rsp
    0x100002948 <+8>:  xorps  %xmm0, %xmm0
    0x10000294b <+11>: movaps %xmm0, -0x10(%rbp)
    0x10000294f <+15>: movl   $0xa, %edi  //把10赋值给edi
    0x100002954 <+20>: movl   $0x14, %esi //把20赋值给esi
->  0x100002959 <+25>: callq  0x100002980               ; init(x: Swift.Int, y: Swift.Int) -> Point #1 in Swift测试.test() -> () in Point #1 in Swift测试.test() -> () at main.swift:15//先看init方法(rax:10,rdx:20)
    0x10000295e <+30>: movq   %rax, -0x10(%rbp) //rax给到rbp-0x10
    0x100002962 <+34>: movq   %rdx, -0x8(%rbp) //rdx给到rbp-0x8
    0x100002966 <+38>: movq   $0xb, -0x10(%rbp) //11赋值到rbp-0x10
    0x10000296e <+46>: movq   $0x16, -0x8(%rbp)//22赋值到rbp-0x8
    0x100002976 <+54>: addq   $0x10, %rsp
    0x10000297a <+58>: popq   %rbp
    0x10000297b <+59>: retq   



Swift测试`init(x:y:) in Point #1 in test():
->  0x100002980 <+0>:  pushq  %rbp
    0x100002981 <+1>:  movq   %rsp, %rbp
    0x100002984 <+4>:  xorps  %xmm0, %xmm0
    0x100002987 <+7>:  movaps %xmm0, -0x10(%rbp)
    0x10000298b <+11>: movq   $0x0, -0x18(%rbp)
    0x100002993 <+19>: movq   $0x0, -0x20(%rbp)
    0x10000299b <+27>: movq   %rdi, -0x18(%rbp)
    0x10000299f <+31>: movq   %rsi, -0x20(%rbp)
    0x1000029a3 <+35>: movq   %rdi, -0x10(%rbp)
    0x1000029a7 <+39>: movq   %rsi, -0x8(%rbp)
    0x1000029ab <+43>: movq   %rdi, %rax //edi,即rdi ,10给到rax
    0x1000029ae <+46>: movq   %rsi, %rdx //esi,即rsi ,20给到rdx
    0x1000029b1 <+49>: popq   %rbp
    0x1000029b2 <+50>: retq   

分析:

1.把10赋值给edi,把20赋值给esi。

2.调用init方法,10给到rax,20给到rdx

3.rax给到rbp-0x10就是10,rdx给到rbp-0x8就是20,我们可以使用lldb :register read rbp,拿到rbp的内存地址,然后减0x10和0x8的值,去View Memory填写刚的值可以查看到数据。

4.栈是高位到低位

5.类似-0x10(%rbp),这种结构都是局部变量,在栈空间。

6.结构体在函数体内,是存放在栈空间当中。

2.类在栈区分析

func test() {
    class Point {
        var x: Int
        var y: Int
        
        init(x: Int, y: Int) {
            self.x = x
            self.y = y
        }
    }

    var p1 = Point(x: 10, y: 20)
    p1.x = 11
    p1.y = 22
}

test()




Swift测试`test():
    0x100002660 <+0>:   pushq  %rbp
    0x100002661 <+1>:   movq   %rsp, %rbp
    0x100002664 <+4>:   pushq  %r13
    0x100002666 <+6>:   subq   $0x78, %rsp
    0x10000266a <+10>:  movq   $0x0, -0x10(%rbp)
    0x100002672 <+18>:  xorl   %eax, %eax
    0x100002674 <+20>:  movl   %eax, %ecx
->  0x100002676 <+22>:  movq   %rcx, %rdi
    0x100002679 <+25>:  movq   %rcx, -0x48(%rbp)
    0x10000267d <+29>:  callq  0x100002760               ; type metadata accessor for Point #1 in Swift测试.test() -> () at <compiler-generated>
    0x100002682 <+34>:  movl   $0xa, %edi  // 10赋值给edi
    0x100002687 <+39>:  movl   $0x14, %esi  //20赋值给esi
    0x10000268c <+44>:  movq   %rax, %r13
    0x10000268f <+47>:  movq   %rdx, -0x50(%rbp)
    0x100002693 <+51>:  callq  0x100002780               ; __allocating_init(x: Swift.Int, y: Swift.Int) -> Point #1 in Swift测试.test() -> () in Point #1 in Swift测试.test() -> () at main.swift:15
    0x100002698 <+56>:  movq   %rax, %rdi //rax就是p1指向的内存地址
    0x10000269b <+59>:  movq   %rax, -0x58(%rbp)
    0x10000269f <+63>:  callq  0x100003bda               ; symbol stub for: swift_retain
    0x1000026a4 <+68>:  movq   -0x58(%rbp), %rdi
    0x1000026a8 <+72>:  movq   %rax, -0x60(%rbp)
    0x1000026ac <+76>:  callq  0x100003bda               ; symbol stub for: swift_retain
    0x1000026e8 <+136>: movq   -0x58(%rbp), %rax
    0x1000026ec <+140>: movq   $0xb, 0x10(%rax)  //11给到rax+0x10
    0x1000026f4 <+148>: movq   -0x70(%rbp), %rdi
    0x1000026f8 <+152>: callq  0x100003bce               ; symbol stub for: swift_endAccess
    0x1000026fd <+157>: movq   -0x58(%rbp), %rdi
    0x100002701 <+161>: callq  0x100003bd4               ; symbol stub for: swift_release
    0x100002706 <+166>: movq   -0x58(%rbp), %rax
    0x10000270a <+170>: addq   $0x18, %rax
    0x10000270e <+174>: leaq   -0x40(%rbp), %rcx
    0x100002712 <+178>: movq   %rax, %rdi
    0x100002715 <+181>: movq   %rcx, %rsi
    0x100002718 <+184>: movq   -0x68(%rbp), %rdx
    0x10000271c <+188>: movq   -0x48(%rbp), %rax
    0x100002720 <+192>: movq   %rcx, -0x80(%rbp)
    0x100002724 <+196>: movq   %rax, %rcx
    0x100002727 <+199>: callq  0x100003bc2               ; symbol stub for: swift_beginAccess
    0x10000272c <+204>: movq   -0x58(%rbp), %rax
    0x100002730 <+208>: movq   $0x16, 0x18(%rax) //22给到rax+0x18
    0x100002738 <+216>: movq   -0x80(%rbp), %rdi
    0x10000273c <+220>: callq  0x100003bce               ; symbol stub for: swift_endAccess
    0x100002741 <+225>: movq   -0x58(%rbp), %rdi
    0x100002745 <+229>: callq  0x100003bd4               ; symbol stub for: swift_release
    0x10000274a <+234>: movq   -0x10(%rbp), %rdi
    0x10000274e <+238>: callq  0x100003bd4               ; symbol stub for: swift_release
    0x100002753 <+243>: addq   $0x78, %rsp
    0x100002757 <+247>: popq   %r13
    0x100002759 <+249>: popq   %rbp
    0x10000275a <+250>: retq   




Swift测试`__allocating_init(x:y:) in Point #1 in test()://分配内存空间
->  0x100002780 <+0>:  pushq  %rbp
    0x100002781 <+1>:  movq   %rsp, %rbp
    0x100002784 <+4>:  pushq  %r13
    0x100002786 <+6>:  subq   $0x18, %rsp
    0x10000278a <+10>: movl   $0x20, %eax
    0x10000278f <+15>: movl   $0x7, %edx
    0x100002794 <+20>: movq   %rdi, -0x10(%rbp)//edi,即rdi给到rbp-0x10
    0x100002798 <+24>: movq   %r13, %rdi
    0x10000279b <+27>: movq   %rsi, -0x18(%rbp) //esi即rsi给到rbp-0x18
    0x10000279f <+31>: movq   %rax, %rsi
    0x1000027a2 <+34>: callq  0x100003bbc               ; symbol stub for: swift_allocObject
    0x1000027a7 <+39>: movq   -0x10(%rbp), %rdi //rbp-0x10给到rdi
    0x1000027ab <+43>: movq   -0x18(%rbp), %rsi //rbp-0x18给到rsi
    0x1000027af <+47>: movq   %rax, %r13    //进一步不再分析
    0x1000027b2 <+50>: callq  0x1000029e0               ; init(x: Swift.Int, y: Swift.Int) -> Point #1 in Swift测试.test() -> () in Point #1 in Swift测试.test() -> () at main.swift:15
    0x1000027b7 <+55>: addq   $0x18, %rsp
    0x1000027bb <+59>: popq   %r13
    0x1000027bd <+61>: popq   %rbp
    0x1000027be <+62>: retq   

分析:

1.把10赋值给edi,把20赋值给esi。

2.调用init方法,10给到rdi,20给到rsi,在具体就不再分析,返回rax。

3.回调方法后,我们可以使用lldb :register read rbp,拿到rax的内存地址去View Memory查看到数据。前8位是系统信息,然后8位是引用计数。后面才是数据。

4.11给到rax+0x10,22给到rax+0x18,相当于把11更改rax的16位,22更改为rax的24位。

4.堆是低位到高位。

5.类似0x10(%rax),这种结构都是在堆空间。

6.类在函数体内,对象的指针变量是存放在栈空间,指向的是堆的内存地址。

3.结构体在全局区分析

import Foundation
struct Point {
    var x: Int
    var y: Int
}

var p1 = Point(x: 10, y: 20)
p1.x = 11
p1.y = 22




Swift测试`main:
    0x100002870 <+0>:   pushq  %rbp
    0x100002871 <+1>:   movq   %rsp, %rbp
    0x100002874 <+4>:   subq   $0x50, %rsp
->  0x100002878 <+8>:   movl   $0xa, %eax //10给到eax
    0x10000287d <+13>:  movl   %edi, -0x34(%rbp)
    0x100002880 <+16>:  movq   %rax, %rdi //rax即eax,给到rdi,即10
    0x100002883 <+19>:  movl   $0x14, %eax //20给到eax
    0x100002888 <+24>:  movq   %rsi, -0x40(%rbp)
    0x10000288c <+28>:  movq   %rax, %rsi //rax即eax给到rsi,即20
    0x10000288f <+31>:  callq  0x1000029b0               ; Swift测试.Point.init(x: Swift.Int, y: Swift.Int) -> Swift测试.Point at main.swift:9
    0x100002894 <+36>:  leaq   0x58cd(%rip), %rcx        ; Swift测试.p1 : Swift测试.Point
    0x10000289b <+43>:  xorl   %r8d, %r8d
    0x10000289e <+46>:  movl   %r8d, %esi
    0x1000028a1 <+49>:  movq   %rax, 0x58c0(%rip)        ; Swift测试.p1 : Swift测试.Point//rax给到rip+ 0x58c0,即10
    0x1000028a8 <+56>:  movq   %rdx, 0x58c1(%rip)        ; Swift测试.p1 : Swift测试.Point + 8//rdx给到rip+ 0x58c1,即20    0x1000028af <+63>:  movq   %rcx, %rdi
    0x1000028b2 <+66>:  leaq   -0x18(%rbp), %rax
    0x1000028b6 <+70>:  movq   %rsi, -0x48(%rbp)
    0x1000028ba <+74>:  movq   %rax, %rsi
    0x1000028bd <+77>:  movl   $0x21, %edx
    0x1000028c2 <+82>:  movq   -0x48(%rbp), %rcx
    0x1000028c6 <+86>:  callq  0x100003c96               ; symbol stub for: swift_beginAccess
    0x1000028cb <+91>:  movq   $0xb, 0x5892(%rip) //11给到rip+ 0x5892
    0x1000028d6 <+102>: leaq   -0x18(%rbp), %rdi
    0x1000028da <+106>: callq  0x100003c9c               ; symbol stub for: swift_endAccess
    0x1000028df <+111>: leaq   0x5882(%rip), %rax        ; Swift测试.p1 : Swift测试.Point
    0x1000028e6 <+118>: xorl   %r8d, %r8d
    0x1000028e9 <+121>: movl   %r8d, %ecx
    0x1000028ec <+124>: movq   %rax, %rdi
    0x1000028ef <+127>: leaq   -0x30(%rbp), %rsi
    0x1000028f3 <+131>: movl   $0x21, %edx
    0x1000028f8 <+136>: callq  0x100003c96               ; symbol stub for: swift_beginAccess
    0x1000028fd <+141>: movq   $0x16, 0x5868(%rip)       ; Swift测试.p1 : Swift测试.Point + 4//22给到rip+ 0x5868
    0x100002908 <+152>: leaq   -0x30(%rbp), %rdi
    0x10000290c <+156>: callq  0x100003c9c               ; symbol stub for: swift_endAccess
    0x100002911 <+161>: xorl   %eax, %eax
    0x100002913 <+163>: addq   $0x50, %rsp
    0x100002917 <+167>: popq   %rbp
    0x100002918 <+168>: retq   




Swift测试`Point.init(x:y:):
->  0x1000029b0 <+0>:  pushq  %rbp
    0x1000029b1 <+1>:  movq   %rsp, %rbp
    0x1000029b4 <+4>:  movq   %rdi, %rax // rdi给到rax,即rax为10
    0x1000029b7 <+7>:  movq   %rsi, %rdx // rsi给到rdx,即rdx为20
    0x1000029ba <+10>: popq   %rbp
    0x1000029bb <+11>: retq   


分析:

1.把10赋值给rax,把20赋值给rsi。

2.调用init方法,10给到rax,20给到rdx。

3.回调方法后,rax给到rip+0x58c0,rdx给到rip+0x58c1,我们可以使用计算机0x1000028a8 + 0x58c0拿到x内存地址,0x1000028af + 0x58c1拿到y的内存地址,这里的rip就是下一行的前面的内存地址,也可以直接register read rip,拿到内存地址去View Memory查看到数据。

4.11给到rip+ 0x5892,22给到rip+0x5868,使用0x1000028d6+0x5892可以发现和x的内存地址是一样 。同理y也一样。

5.类似0x58c0(%rip),这种都是在全局变量,存放在全局区(数据段)中。

4.总结

汇编是感觉有点难懂,现在也只是窥探一下小知识点,如有问题,欢迎指出。

在上面的汇编中,也可以发现结构体和类的区别。

关于LLDB的一些命令:

si:遇到函数也直接下一步

ni:遇到函数直接跳过

finish:遇到函数有return,可以finish返回。

直接按enter会重复使用上一个命令。