update: 动态库、静态库的编译链接细节

This commit is contained in:
FantasticLBP
2025-06-23 01:18:55 +08:00
parent aca020701b
commit 1142064d28
129 changed files with 10932 additions and 2615 deletions

View File

@@ -43,9 +43,9 @@ print("全局变量", Mems.ptr(ofVal: &p))
print("堆空间", Mems.ptr(ofRef: p))
```
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClassMemoryLayoutExcludeFunction.png" style="zoom:25%">
<img src="./../assets/ClassMemoryLayoutExcludeFunction.png" style="zoom:25%">
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClassMemoryLayoutExcludeFunction2.png" style="zoom:25%">
<img src="./../assets/ClassMemoryLayoutExcludeFunction2.png" style="zoom:25%">
代码段Person.sayHi 0x1000034d0
@@ -183,7 +183,7 @@ print(fn(2)) // 2
print(fn(3)) // 3
```
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClosureCaptureVariableDemo1.png" style="zoom:25%">
<img src="./../assets/ClosureCaptureVariableDemo1.png" style="zoom:25%">
@@ -207,7 +207,7 @@ print(fn(3)) // 6
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClosureCaptureVariableDemo2.png" style="zoom:25%">
<img src="./../assets/ClosureCaptureVariableDemo2.png" style="zoom:25%">
可以看到上面有 `allocObject` 方法调用,说明产生了堆空间对象,用于存放 `num` 。是由 `var fn = getFn()` 造成的调用1次 `getFn` 则产生1次堆空间分配用于保存 num。
@@ -235,31 +235,31 @@ print(fn3(3)) // 4
我们在汇编 `swift_allocObject` 下面下个断点
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClosureCaptureVariableDemo3.png" style="zoom:25%">
<img src="./../assets/ClosureCaptureVariableDemo3.png" style="zoom:25%">
第一次:可以看到 `alloc` 在堆空间申请后的内存被存放到寄存器 `rax` 中,此时只是申请内存,没有赋值的。利用 Debug- Debug Workflow - View Memory 查看内存信息`0x0000600000210000`。此时还没值。
第一次:可以看到 `alloc` 在堆空间申请后的内存被存放到寄存器 `rax` 中,此时只是申请内存,没有赋值的。利用 `Debug -> Debug Workflow -> View Memory` 查看内存信息`0x0000600000210000`。此时还没值。
敏感点查看到字面量1给汇编代码15行加断点执行完15行继续查看内存信息
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClosureCaptureVariableDemo4.png" style="zoom:25%">
<img src="./../assets/ClosureCaptureVariableDemo4.png" style="zoom:25%">
可以看到内存数据发生了改变。绿色框内有了值1。
第二次:可以看到 `alloc` 在堆空间申请后的内存被存放到寄存器 `rax` 中,此时只是申请内存,没有赋值的。利用 Debug- Debug Workflow - View Memory 查看内存信息`0x00006000002042c0`。此时还没值。
第二次:可以看到 `alloc` 在堆空间申请后的内存被存放到寄存器 `rax` 中,此时只是申请内存,没有赋值的。利用 `Debug -> Debug Workflow -> View Memory` 查看内存信息`0x00006000002042c0`。此时还没值。
敏感点查看到字面量1给汇编代码15行加断点执行完15行继续查看内存信息
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClosureCaptureVariableDemo5.png" style="zoom:25%">
<img src="./../assets/ClosureCaptureVariableDemo5.png" style="zoom:25%">
第三次:可以看到 `alloc` 在堆空间申请后的内存被存放到寄存器 `rax` 中,此时只是申请内存,没有赋值的。利用 Debug- Debug Workflow - View Memory 查看内存信息`0x000060000020d460`。此时还没值。
第三次:可以看到 `alloc` 在堆空间申请后的内存被存放到寄存器 `rax` 中,此时只是申请内存,没有赋值的。利用 `Debug -> Debug Workflow -> View Memory` 查看内存信息`0x000060000020d460`。此时还没值。
敏感点查看到字面量1给汇编代码15行加断点执行完15行继续查看内存信息
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClosureCaptureVariableDemo6.png" style="zoom:25%">
<img src="./../assets/ClosureCaptureVariableDemo6.png" style="zoom:25%">
打印结果也说明了问题因为调用3次 `getFn()` 所以会在堆上 alloc 3块内存用于保存捕获的变量。所以调用 fn1 得到 2调用 fn2 得到 3调用 fn3 得到 4。
@@ -279,7 +279,7 @@ print(fn(1, 2)) // 3
在 `var fn = sum` 处下断点,可以看到下面汇编
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClouserMemoryLayoutExploreDemo1.png" style="zoom:25%">
<img src="./../assets/ClouserMemoryLayoutExploreDemo1.png" style="zoom:25%">
我们知道 `leaq` 是从 `%rip + 0x10f` 算出来的地址,赋值给 `%rax`。所以大概可以推测 `%rip + 0x10f` 就是 `sum` 函数地址。LLDM p 打印 `sum` 地址为 `0x0000000100003a30`。
@@ -297,15 +297,15 @@ print(fn(1, 2)) // 3
直奔主题,研究闭包内存
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClouseExploreNotCaptureVariableDemo1.png" style="zoom:25%">
<img src="./../assets/ClouseExploreNotCaptureVariableDemo1.png" style="zoom:25%">
可以看到在调用完第六行的函数后将寄存器 `rax`、`rdx` 里的值取出来使用了。进入函数内部看看发生了什么LLDB 输入 `si`
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClouseExploreNotCaptureVariableDemo2.png" style="zoom:25%">
<img src="./../assets/ClouseExploreNotCaptureVariableDemo2.png" style="zoom:25%">
可以看到 `rax` 里面存放了 `plus` 函数地址。第7行汇编是异或运算2个 `ecx` 异或结果为0写入到 `ecx` 里。然后第8行汇编将 `ecx` 里的0写入到 `edx``edx` 也就是 `rdx`。走完第6行的汇编继续看第7、8行
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClouseExploreNotCaptureVariableDemo1.png" style="zoom:25%">
<img src="./../assets/ClouseExploreNotCaptureVariableDemo1.png" style="zoom:25%">
将第6行函数的返回结果 `rax` 里的函数地址赋值给 `rip +0x89e7 = 0x100003819 + 0x89e7 = 0x10000C200 ` 第7行的`rdx` 的值赋值个给 `rip +0x89e8 = 0x100003820 + 0x89e8 = 0x10000C208`。
@@ -315,7 +315,7 @@ print(fn(1, 2)) // 3
继续对比实验,查看闭包放了什么东西(注意和上面的实验不同,下面存在闭包)
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClouseExploreNotCaptureVariableDemo3.png" style="zoom:25%">
<img src="./../assets/ClouseExploreNotCaptureVariableDemo3.png" style="zoom:25%">
基本可以断定函数会返回一个长度为16 Byte 的内存。分别保存在 `rax` 和 `rdx`上。所以针对性的研究 `rdx` 和 `rax`
@@ -323,7 +323,7 @@ print(fn(1, 2)) // 3
20行的 `rax` 保存了一个看似是 `plus` 的函数地址。具体是:`rip + 0x167 = 0x100003969 + 0x167= 0x100003C37 `
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClouseExploreNotCaptureVariableDemo3.png" style="zoom:25%">
<img src="./../assets/ClouseExploreNotCaptureVariableDemo3.png" style="zoom:25%">
继续走,走到真正的 `plus` 方法内,可以看到函数地址是 `0x100003970` 。所以返回的 `rax` 里面可能是间接调用真正的 `plus` 函数的。
@@ -340,13 +340,13 @@ Tips由于地址是动态生成的所以真正去调用 plus 的时候一
顺着思路,分析下汇编:
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClouseExploreCaptureVariableDemo1.png" style="zoom:25%">
<img src="./../assets/ClouseExploreCaptureVariableDemo1.png" style="zoom:25%">
我们看到25行 `callq *%rax` 存在动态调用,所以需要找到 `%rax` 里的值是哪里来的。然后顺着向上找找到23行 `movq -0x30(%rbp), %rax`。然后继续向上看到16行 `movq %rax, -0x30(%rbp)`。继续向上看到15行 `movq 0x88db(%rip), %rax`。相当于就是在 `rip + 0x88db ` 处取出8个字节出来当作函数地址调用汇编代码的右边写了`fn1` ),地址为:`0x88db(%rip) = rip + 0x88db = 0x10000392d + 0x88db = 0x10000C208` 。
断点继续放开在汇编25行处加断点 `callq *%rax`
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClouseExploreCaptureVariableDemo2.png" style="zoom:25%">
<img src="./../assets/ClouseExploreCaptureVariableDemo2.png" style="zoom:25%">
可以看到在方法内部第6行汇编处直接调用一个代码段的函数地址 `jmp 0x1000039f0 `
@@ -357,13 +357,13 @@ Tips由于地址是动态生成的所以真正去调用 plus 的时候一
LLDB 输入 `si`,可以看到是 `plus` 函数真正的地址
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClouseExploreCaptureVariableDemo3.png" style="zoom:25%">
<img src="./../assets/ClouseExploreCaptureVariableDemo3.png" style="zoom:25%">
`fn1` 函数调用的时候,参数如何传递?
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClouseExploreCaptureVariableDemo4.png" style="zoom:25%">
<img src="./../assets/ClouseExploreCaptureVariableDemo4.png" style="zoom:25%">
汇编17行 `movq 0x88d8(%rip), %r13 ; SwiftDemo.fn1 : (Swift.Int) -> Swift.Int + 8`可以看到将返回的 fn1 本身16字节的后8个字节也就是堆地址空间值保存到寄存器 `edi` 也就是寄存器 `rdi` 上了。
@@ -371,7 +371,7 @@ LLDB 输入 `si`,可以看到是 `plus` 函数真正的地址
然后 LLDB 输入 `si` 去分析 callq 内部
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClouseExploreCaptureVariableDemo5.png" style="zoom:25%">
<img src="./../assets/ClouseExploreCaptureVariableDemo5.png" style="zoom:25%">
@@ -379,7 +379,7 @@ LLDB 输入 `si`,可以看到是 `plus` 函数真正的地址
继续输入 `si`
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClouseExploreCaptureVariableDemo6.png" style="zoom:25%">
<img src="./../assets/ClouseExploreCaptureVariableDemo6.png" style="zoom:25%">
可以看到汇编的第5行 `movq %rsi, -0x50(%rbp)` 将 `rsi` 里的1保存到 `rbp - 0x50` 处。第6行汇编 `movq %rdi, -0x58(%rbp)` 将 `rdi` 堆地址值保存到 `rbp - 0x58` 处。
@@ -387,16 +387,16 @@ LLDB 输入 `si`,可以看到是 `plus` 函数真正的地址
然后真正做 `plus` 加法运算的就是28行的 `addq 0x10(%rsi), %rdi` 从 `rsi` 堆地址值的第16个字节的地方取出8个字节的值也就是捕获的外部变量 `num` 再和参数1相加。
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/ClouseExploreCaptureVariableDemo7.png" style="zoom:25%">
<img src="./../assets/ClouseExploreCaptureVariableDemo7.png" style="zoom:25%">
可以看到第6行堆地址空间的值写入到 `rbp -0x58` 第26行又将 `rbp -0x58` 写入到 `rdi`29行将 `rdi` 的值,写入到 `rbp - 0x48`34行将 `rbp - 0x48` 写入到 `rcx`35行将 `rcx` 的地址值,写入到 `rax` 对应的存储空间。也就是堆上的 `num` 值已经修改,被覆盖了。
总结:当 `getFn` 内部没有发生闭包的时候fn1 的地址就是16 Byte前8 Byte就是 `plus` 的函数地址。当发生函数闭包的时候,`fn1` 的16 Byte前8 Byte 存储间接调用 `plus` 函数的中转函数后8 Byte 存储着捕获的且在堆上分配内存的地址值。真正调用 `plus` 的时候会通过寄存器传递2个参数1个是 fn1 的参数1个是堆空间的地址值。
总结:当 `getFn` 内部没有发生闭包的时候fn1 的地址就是16 Byte前8 Byte就是 `plus` 的函数地址。当发生函数闭包的时候,`fn1` 的16 Byte前8 Byte 存储间接调用 `plus` 函数的中转函数后8 Byte 存储着捕获的且在堆上分配内存的地址值。真正调用 `plus` 的时候会通过寄存器传递2个参数1个是 fn1 函数的参数1个是堆空间的地址值。
```swift
var fn1 = getFn()
var fn1 = getFn()
fn1(1) // 2
fn1(3) //4
```
@@ -414,11 +414,32 @@ fn2(4) // 5
因为调用了2次 `getFn` 所以堆内存分配了2个被捕获的变量地址调用过1次 fn1 后,堆地址所指向的内存上的数被修改了。当第二次调用的时候操作的还是同一块堆内存地址,也就是同一个被捕获的堆地址所指向的变量。当调用 fn2 的时候操作的是被捕获的新的一个堆地址空间所指向的变量。
且 fn1 所占用的16个字节fn2 所占用16个字节。2者的前8字节内容相同都是包装了 `plus` 函数的一个地址。
### “闭包就是对象”
捕获了外部变量的闭包类似于一个类,里面存在存储属性和方法
```swift
class {
var num: Int
func fn(_ i: Int) -> Int {
return i + num
}
}
```
## 自动闭包
自动闭包是一种自动创建的,用来把作为实际参数传递给函数的表达式打包的闭包。它不接受任何实际参数,并且当它被调用时,它会返回内部打包的表达式的值
**自动闭包是一种自动创建的,用来把作为实际参数传递给函数的表达式打包的闭包**。它不接受任何实际参数,并且当它被调用时,它会返回内部打包的表达式的值
这个语法的好处在于通过写普通表达式代替显示闭包,而使你省略包围函数的形式参数的括号.
@@ -481,7 +502,7 @@ serve(customer: group.remove(at: 0))
但如果函数参数没有加 `@autoclosure`,在调用函数的时候,传参没有加 `{}` 编译器是会报错的
<img src="https://github.com/FantasticLBP/knowledge-kit/raw/master/assets/SwiftAutoClosureError.png" style="zoom:25%">
<img src="./../assets/SwiftAutoClosureError.png" style="zoom:25%">
正确的做法是:要么加 `@autoclosure` 要么在函数调用的时候加 `{}`
@@ -507,4 +528,63 @@ func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> S
}
collectCustomerProviders(group.remove(at: 0))
```
## 闭包和闭包表达式的区别
### 闭包
定义:闭包是自包含的功能代码块,可以在代码中被传递和使用。它能捕获并存储其所在上下文的常量和变量(即“闭合”环境)
种类:
- 全局函数(有名称,不捕获任何值)
- 嵌套函数(有名称,可捕获外曾函数的变量)
- 闭包表达式(匿名,轻量语法,可以捕获上下文变量)
闭包强调的是:捕获上下文的能力。不管是函数还是闭包表达式
### 闭包表达式
定义:是 Swift 中一种简洁的语法格式,用于编写匿名闭包。是闭包的一种实现方式之一(函数和闭包表达式都可以实现闭包)
特点:
- 没有函数名
- 语法清凉,支持参数类型推断、隐式返回、简写参数名(如 $0、$1等特性
- 通常用于作为函数的参数传递
### 总结
- **闭包**是广义概念,包含所有能捕获上下文的代码块(如函数、闭包表达式)。
- **闭包表达式**是闭包的一种具体实现方式,专指用轻量语法编写的匿名闭包。