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会重复使用上一个命令。