156 lines
3.4 KiB
Markdown
156 lines
3.4 KiB
Markdown
|
# 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/)
|