导出 golang 中的私有 (unexported) 项的方法
导出 golang 中的私有 (unexported) 项的方法
本文将以 go 标准库中的 sync/pool 为例探讨导出 unexported 项的方法
导出私有全局变量
通过 go:linkname 来导出私有全局变量
1 2 3 4 5 6 7 8 9 10 11 12
| package main
import _ "unsafe"
var poolRaceHash [128]uint64
func main() { var _ = poolRaceHash }
|
导出私有 struct field
可通过裸指针直接操作内存来导出私有 field
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| package main
import ( "sync" "unsafe" )
type Pool struct { noCopy noCopy
local unsafe.Pointer localSize uintptr
New func() interface{} }
type noCopy struct{}
func main() { var externPool = sync.Pool{ New: func() interface{} { return nil }, } var pool = (*Pool)(unsafe.Pointer(&externPool)) _ = pool.local }
|
导出私有 function 或 method
方法 1: 通过 go:linkname
第一步
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| package main
import ( "sync" "unsafe" )
type Pool struct { noCopy noCopy
local unsafe.Pointer localSize uintptr
New func() interface{} }
type noCopy struct{}
type poolLocal struct { private interface{} shared []interface{} sync.Mutex pad [128]byte }
func pinSlow(p *sync.Pool) *poolLocal
func (p *Pool) pinSlow() *poolLocal
func poolRaceAddr(x interface{}) unsafe.Pointer
func main() { var externPool = sync.Pool{ New: func() interface{} { return nil }, } var pool = (*Pool)(unsafe.Pointer(&externPool))
var _ = pinSlow(&externPool)
var _ = (*Pool).pinSlow(pool)
var _ = poolRaceAddr(nil) }
|
第二步
在项目中的新建一个空白的、任意名称的汇编代码文件(比如 asm.s)以帮助编译器完成函数完整性检查
1 2 3
| go_export ├── asm.s └── main.go
|
第三步
方法 2: 直接获取运行时中的函数指针
go-forceexport 提供了这种便利
注意事项
- 当使用 go:linkname 导出非标准库中的私有项时,须在当前包中显式地 import 来源包
参考资料:https://sitano.github.io/2016/04/28/golang-private/