Defs:
形如 //go:linkname localname importpath.name 的注释叫做 //go:linkname 指令,可理解为名为 localname 的私有函数|变量为 importpath 包下的 name 函数签名提供了实现|变量赋值,从而突破包的访问限制。
PartⅠ:源码实例
time/sleep.go 中存在如下:
func startTimer(*runtimeTimer)
func stopTimer(*runtimeTimer) bool
其实现在 runtime/time.go
// startTimer adds t to the timer heap.
//go:linkname startTimer time.startTimer
func startTimer(t *timer) {
if raceenabled {
racerelease(unsafe.Pointer(t))
}
addtimer(t)
}
PartⅡ:实现
目录结构:
main.go
package main
import (
"demo/usePrivate"
)
func main() {
usePrivate.Call()
}
private.go
package private
import (
"fmt"
_ "unsafe" //私有函数文件要 import _ "unsafe"
)
//go:linkname privateStr demo/usePrivate.usePrivateStr
var privateStr = "this is privateStr"
//go:linkname privateFunc demo/usePrivate.usePrivateFunc
func privateFunc() {
fmt.Print("this is privateFunc\n")
}
usePrivate.go:调用私有函数|变量
package usePrivate
import (
_ "demo/private" // 函数签名文件要 import 私有函数所在包
"fmt"
)
func usePrivateFunc()
var usePrivateStr string
func Call() {
usePrivateFunc()
fmt.Printf("private str = %s", usePrivateStr)
}
Notes:
1. //go:linkname 不能通过 main 直接访问(试了不行,不清楚为什么)
2. //go:linkname 所在文件要 import _ "unsafe"
3. 函数签名文件要 import 私有函数所在包
4. 函数签名文件夹要建立后缀为 .s 的空文件,原因在于Go在编译的时候会启用 -complete 编译器flag,它要求所有的函数必需包含函数体,创建一个空的汇编语言文件绕过这个限制。
参考:https://colobu.com/2017/05/12/call-private-functions-in-other-packages/