警告一下!以下代码均不是常规操作,且存在各种潜在不可控的风险。在项目中应用有可能被同事打死,慎用!!!
1.调用其他包中公有结构的私有成员变量
如果需要引用某个包中公有结构体的私有变量,而这个变量又没有提供对应的访问方法。那么如何绕过“小写不公开”这个限制呢?简单介绍一种方法直接通过变量地址访问变量:
package other1
import "fmt"
type TestPointer struct {
A int
b int // 私有变量
}
func (T *TestPointer) OouPut() {
fmt.Println("TestPointer OouPut:", T.A, T.b)
}
package main
import (
"fmt"
"test/test4/other1"
"unsafe"
)
func main() {
T := other1.TestPointer{A:1}
pb := (*int)(unsafe.Pointer(uintptr(unsafe.Pointer(&T)) + 8))
/*
Tmp := uintptr(unsafe.Pointer(&T)) + 8)
pb := (*int)(unsafe.Pointer(Tmp)
千万不能出现这种用临时变量中转一下的情况。因为GC可能因为优化内存碎片的原因移动了这个对象。只保留了指针的地址是没有意义的。
*/
*pb = 2
T.OouPut() //1 2
}
用unsafe包中的unsafe.Pointer获取到结构体对象的首地址,然后加上想访问的私有变量的偏移地址就是私有变量的地址。关于成员变量偏移量的问题请参阅 内存对齐
2.调用其他包的私有func
go提供了一个编译指令,绕过编译器检查。直接访问func的实现
//go:linkname
package other1
import "fmt"
func privateFunc() {
fmt.Println("this is private func")
}
package main
import (
_ "test/test4/other1"
_ "unsafe"
)
// call private func
//go:linkname private_func test/test4/other1.privateFunc
func private_func()
func main() {
private_func() // this is private func
}
关于编译指令可以参阅 编译指令 或 Command compile
上面代码需要在调用者(这里是main.go)同目录添加一个.s汇编文件,骗过编译器。让编译器认为是实现是在.s汇编文件中,从而跳过检查
3. 调用其他包的公有结构的私有方法
package other1
import "fmt"
type PublicStruct struct {
I int
b int
}
func (p *PublicStruct) f(a int) {
println("PublicStruct f()", p.I, p.b, a)
}
package main
import (
"test/test4/other1"
_ "unsafe"
)
// 调用其他包的公有结构的私有func
//go:linkname public_struc_private_func test/test4/other1.(*PublicStruct).f
func public_struc_private_func(p *other1.PublicStruct, a int)
func main() {
// 先构造一个other1.PublicStruct
p := &other1.PublicStruct{I:1}
public_struc_private_func(p, 100) // PublicStruct f() 1 0 100
}
和上面的类似用linkname指令骗过编译器。这里声明了一个指针接收者的func public_struc_private_func
第一个参数是对应对象的指针,第二个参数开始是对应func需要的参数。
其实这就是指针接收者func原本的实现方式(即 本质上是一个普通的函数,只是隐式传递了对象的指针)
4. 调用其他包的私有全局变量
package other1
var private_m = map[int]string {
1:"a",
}
import (
"fmt"
_ "test/test4/other1"
_ "unsafe"
) // 调用其他包的私有全局变量
//go:linkname private_member test/test4/other1.private_m
var private_member map[int]string
func main() {
fmt.Println(private_member[1]) // a
private_member[2] = "b"
for k, v := range private_member {
fmt.Println(k, v) // 1 a; 2 b
}
}
和上面的linkname类似,骗过编译器。直接访问变量