最近在 hack viewcore,需要了解闭包的实现机制,来完成逆向的类型推导,所以搞了几个小例子,分析了闭包的实现机制,简单记录一下的。
运行时表示
在运行时,闭包是一个 GC 对象,可以用下面的结构来表示。
1 | type closure struct { |
pc 比较好理解,就是对应函数的入口地址。
arg1, arg2, … 则是闭包函数引用的上层局部变量。
如下示例:
1 | func genClosure(a int, b int) func(int) bool { |
生成的汇编如下,核心就是构造一个新的 GC 对象:
1 | LEAQ runtime.rodata+58080(SB), AX ## 0x1099b00 <_type.*+0xe2e0> |
此时生成的闭包则等效于这样的 struct 对象:
1 | type closure struct { |
调用过程
调用闭包,跟普通的函数调用基本类似,只是把闭包对象放到 DX
寄存器。
例如下面的示例:
1 | f := genClosure(10, 100) |
生成如下汇编:
1 | MOVL $0xa, AX |
我们可以看到:
- 先从闭包对象中取出函数入口地址,写入 CX 寄存器
- 将闭包对象写入 DX 寄存器
- CALL CX,调用闭包函数
总结
简单的来说,闭包的实现也比较简单,通过一个 GC 对象,将函数入口地址,以及引用的局部变量,都装进来,就是一个闭包对象了。
调用的时候,将闭包对象,作为函数的第四个参数,也就是使用 DX
寄存器传参。