From 9469ebe4b9aadc44b4139b854cdb1547d2774844 Mon Sep 17 00:00:00 2001 From: "chen.yang" Date: Sat, 13 Nov 2021 15:44:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20Golang=20=E8=B0=83?= =?UTF-8?q?=E7=94=A8=E5=85=B6=E5=AE=83=E5=8C=85=E4=B8=AD=E7=9A=84=E7=A7=81?= =?UTF-8?q?=E6=9C=89=E5=87=BD=E6=95=B0=E3=80=81=E5=85=A8=E5=B1=80=E5=8F=98?= =?UTF-8?q?=E9=87=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: chen.yang --- .../Golang_调用其它包中的私有函数_全局变量.md | 155 ++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 Software/Development/Language/Go/Basic/Golang_调用其它包中的私有函数_全局变量.md diff --git a/Software/Development/Language/Go/Basic/Golang_调用其它包中的私有函数_全局变量.md b/Software/Development/Language/Go/Basic/Golang_调用其它包中的私有函数_全局变量.md new file mode 100644 index 0000000..9e03ce4 --- /dev/null +++ b/Software/Development/Language/Go/Basic/Golang_调用其它包中的私有函数_全局变量.md @@ -0,0 +1,155 @@ +# Golang 调用其它包中的私有函数、全局变量 + +在使用 Golang 的某些情况下(当然,应该是在万不得已的情况下),你可能需要调用引入的某个包中的某些私有全局变量或函数(包括方法)。事实上,Golang 是有一些未在官方文档中公布的相关隐藏技能的,这些技能在 Golang 开源的标准库代码中出现,终究被挖了出来。 + +## 调用私有函数 + +这里有一个被引入的包 somewhere.com/someone/another + +```go +package another + +func inc(i int) int { + return i + 1 +} +``` + +在自己的包中调用上面的私有函数 inc + +```go +package main + +import ( + _ "unsafe" + + _ "somewhere.com/someone/another" +) + +//go:linkname inc somewhere.com/someone/another.inc +func inc(i int) int + +func main() { + println(inc(2)) +} +``` + +首先,需要在自己的包中声明要调用的私有函数。在声明的上一行虽然是一行注释,不过和 //export 开头的注释一样,这样的注释是有自己特殊语义的。//go:linkname 会在编译期将本包符号链接到目标符号。因为在编译产物的符号表中已不存在 Golang 语法中的所谓公开与私有,可谓“想跳就跳”。 + +//go:linkname 后面跟上本地函数名和目标函数,目标函数需要指定包含完整路径的包名,点号后面加上函数名。 + +同时,需要引入 unsafe 包,并且在自己的包中需要加入一个空白的 *.s 汇编文件,来绕过编译检查。 + +## 调用公开结构的私有方法 + +这里有一个公开结构 Cls 和其私有方法 m + +```go +package another + +type Cls struct { + I int +} + +func (c *Cls) m() { + println(c.I) +} +``` + +```go +package main + +import ( + _ "unsafe" + + "somewhere.com/someone/another" +) + +//go:linkname m somewhere.com/someone/another.(*Cls).m +func m(c *another.Cls) + +func main() { + c := &another.Cls{2} + m(c) +} +``` + +Golang 不能在一个包内定义其它包中类型的方法,因此需要定义为本包内的函数,并把方法的 this 指针语义显式地表达出来。同时,对于目标方法的指定,需要加上形如 (*S) 的类型指定。 + +## 调用私有结构的私有方法 + +结构的定义不像函数那样是在符号表进行链接,所以私有结构的定义需要在本包重复进行一次。也因为本包已有此结构,所以可直接在结构上声明方法,无须显式表达 this 指针。 + +```go +package another + +type cls struct { + I int +} + +func (c *cls) m() { + println(c.I) +} +``` + +```go +package main + +import ( + _ "unsafe" + + _ "somewhere.com/someone/another" +) + +type cls struct { + I int +} + +//go:linkname (*cls).m somewhere.com/someone/another.(*cls).m +func (c *cls) m() + +func main() { + c := &cls{2} + c.m() +} +``` + +到这里,对于私有结构的公开方法的调用,相信你已心里有数了吧。 + +## 调用私有全局变量 + +和函数一样,全局变量也存在于符号表中,因此也可以有这种操作。 + +```go +package another + +var m = map[int]string{ + 1: "a", +} + +func M(i int) string { + return m[i] +} +``` + +```go +package main + +import ( + _ "unsafe" + + "somewhere.com/someone/another" +) + +//go:linkname m somewhere.com/someone/another.m +var m map[int]string + +func main() { + println(m[1]) + m[2] = "b" + println(another.M(2)) +} +``` + +## 外部参考资料 + +* [How to call private functions (bind to hidden symbols) in GoLang](https://sitano.github.io/2016/04/28/golang-private/)