-
Notifications
You must be signed in to change notification settings - Fork 36
/
forceexport.go
110 lines (95 loc) · 3.53 KB
/
forceexport.go
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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package forceexport
import (
"fmt"
"reflect"
"runtime"
"unsafe"
)
// GetFunc gets the function defined by the given fully-qualified name. The
// outFuncPtr parameter should be a pointer to a function with the appropriate
// type (e.g. the address of a local variable), and is set to a new function
// value that calls the specified function. If the specified function does not
// exist, outFuncPtr is not set and an error is returned.
func GetFunc(outFuncPtr interface{}, name string) error {
codePtr, err := FindFuncWithName(name)
if err != nil {
return err
}
CreateFuncForCodePtr(outFuncPtr, codePtr)
return nil
}
// Convenience struct for modifying the underlying code pointer of a function
// value. The actual struct has other values, but always starts with a code
// pointer.
type Func struct {
codePtr uintptr
}
// CreateFuncForCodePtr is given a code pointer and creates a function value
// that uses that pointer. The outFun argument should be a pointer to a function
// of the proper type (e.g. the address of a local variable), and will be set to
// the result function value.
func CreateFuncForCodePtr(outFuncPtr interface{}, codePtr uintptr) {
outFuncVal := reflect.ValueOf(outFuncPtr).Elem()
// Use reflect.MakeFunc to create a well-formed function value that's
// guaranteed to be of the right type and guaranteed to be on the heap
// (so that we can modify it). We give a nil delegate function because
// it will never actually be called.
newFuncVal := reflect.MakeFunc(outFuncVal.Type(), nil)
// Use reflection on the reflect.Value (yep!) to grab the underling
// function value pointer. Trying to call newFuncVal.Pointer() wouldn't
// work because it gives the code pointer rather than the function value
// pointer. The function value is a struct that starts with its code
// pointer, so we can swap out the code pointer with our desired value.
funcValuePtr := reflect.ValueOf(newFuncVal).FieldByName("ptr").Pointer()
funcPtr := (*Func)(unsafe.Pointer(funcValuePtr))
funcPtr.codePtr = codePtr
outFuncVal.Set(newFuncVal)
}
// FindFuncWithName searches through the moduledata table created by the linker
// and returns the function's code pointer. If the function was not found, it
// returns an error. Since the data structures here are not exported, we copy
// them below (and they need to stay in sync or else things will fail
// catastrophically).
func FindFuncWithName(name string) (uintptr, error) {
for moduleData := &Firstmoduledata; moduleData != nil; moduleData = moduleData.next {
for _, ftab := range moduleData.ftab {
f := (*runtime.Func)(unsafe.Pointer(&moduleData.pclntable[ftab.funcoff]))
if f.Name() == name {
return f.Entry(), nil
}
}
}
return 0, fmt.Errorf("Invalid function name: %s", name)
}
// Everything below is taken from the runtime package, and must stay in sync
// with it.
//go:linkname Firstmoduledata runtime.firstmoduledata
var Firstmoduledata Moduledata
type Moduledata struct {
pclntable []byte
ftab []Functab
filetab []uint32
findfunctab uintptr
minpc, maxpc uintptr
text, etext uintptr
noptrdata, enoptrdata uintptr
data, edata uintptr
bss, ebss uintptr
noptrbss, enoptrbss uintptr
end, gcdata, gcbss uintptr
// Original type was []*_type
typelinks []interface{}
modulename string
// Original type was []modulehash
modulehashes []interface{}
gcdatamask, gcbssmask Bitvector
next *Moduledata
}
type Functab struct {
entry uintptr
funcoff uintptr
}
type Bitvector struct {
n int32 // # of bits
bytedata *uint8
}