github.com/jxskiss/gopkg@v0.17.3/forceexport/function.go (about) 1 package forceexport 2 3 import ( 4 "fmt" 5 "reflect" 6 "runtime" 7 "unsafe" 8 9 "github.com/jxskiss/gopkg/internal/linkname" 10 "github.com/jxskiss/gopkg/reflectx" 11 ) 12 13 // GetFunc gets the function defined by the given fully-qualified name. 14 // The outFuncPtr parameter should be a pointer to a function with the 15 // appropriate type (e.g. the address of a local variable), and is set to 16 // a new function value that calls the specified function. 17 // If the function does not exist, or is inlined, or inactive (haven't been 18 // compiled into the binary), it panics. 19 func GetFunc(outFuncPtr interface{}, name string) { 20 codePtr := FindFuncWithName(name) 21 CreateFuncForCodePtr(outFuncPtr, codePtr) 22 } 23 24 // Func is a convenience struct for modifying the underlying code pointer 25 // of a function value. The actual struct has other values, but always 26 // starts with a code pointer. 27 type Func struct { 28 codePtr uintptr 29 } 30 31 // CreateFuncForCodePtr accepts a code pointer and creates a function 32 // value that uses the pointer. The outFuncPtr argument should be a pointer 33 // to a function of the proper type (e.g. the address of a local variable), 34 // and will be set to the result function value. 35 func CreateFuncForCodePtr(outFuncPtr interface{}, codePtr uintptr) { 36 outFuncVal := reflect.ValueOf(outFuncPtr).Elem() 37 // Use reflect.MakeFunc to create a well-formed function value that's 38 // guaranteed to be of the right type and guaranteed to be on the heap 39 // (so that we can modify it). 40 newFuncVal := reflect.MakeFunc(outFuncVal.Type(), nil) 41 42 // Use reflection on the reflect.Value (yep!) to grab the underlying 43 // function value pointer. Trying to call newFuncVal.Pointer() wouldn't 44 // work because it gives the code pointer rather than the function value 45 // pointer. The function value is a struct that starts with its code 46 // pointer, so we can swap out the code pointer with our desired value. 47 funcValuePtr := reflect.ValueOf(newFuncVal).FieldByName("ptr").Pointer() 48 funcPtr := (*Func)(unsafe.Pointer(funcValuePtr)) 49 funcPtr.codePtr = codePtr 50 51 outFuncVal.Set(newFuncVal) 52 } 53 54 // FindFuncWithName searches through the moduledata table created by the 55 // linker and returns the function's code pointer. 56 // If the function does not exist, or is inlined, or inactive (haven't been 57 // compiled into the binary), it panics. 58 func FindFuncWithName(name string) uintptr { 59 for _, moduleData := range activeModules() { 60 pclntable := moduleData.pclntable() 61 for _, ftab := range moduleData.ftab() { 62 f := (*runtime.Func)(unsafe.Pointer(&pclntable[ftab.funcoff])) 63 if getName(f) == name { 64 return f.Entry() 65 } 66 } 67 } 68 panic(fmt.Sprintf("forceexport: cannot find function %s, maybe inlined or inactive", name)) 69 } 70 71 func getName(f *runtime.Func) string { 72 defer func() { 73 recover() 74 }() 75 return f.Name() 76 } 77 78 func activeModules() []moduledata { 79 mdptrs := linkname.Runtime_activeModules() 80 out := make([]moduledata, len(mdptrs)) 81 for i, ptr := range mdptrs { 82 out[i] = moduledata{ptr} 83 } 84 return out 85 } 86 87 type moduledata struct { 88 p unsafe.Pointer 89 } 90 91 func (p *moduledata) pclntable() []byte { 92 return *(*[]byte)(unsafe.Pointer(uintptr(p.p) + moduledata_pclntableOffset)) 93 } 94 95 func (p *moduledata) ftab() []functab { 96 return *(*[]functab)(unsafe.Pointer(uintptr(p.p) + moduledata_ftabOffset)) 97 } 98 99 var ( 100 moduledata_pclntableOffset uintptr 101 moduledata_ftabOffset uintptr 102 ) 103 104 func init() { 105 rtmdtype := GetType("runtime.moduledata") 106 moduledata_pclntableOffset = getOffset(rtmdtype, "pclntable", "forceexport: moduledata.pclntable not found") 107 moduledata_ftabOffset = getOffset(rtmdtype, "ftab", "foceexport: moduledata.ftab not found") 108 } 109 110 func getOffset(t *reflectx.RType, fieldname string, msg string) uintptr { 111 f, ok := t.FieldByName(fieldname) 112 if !ok { 113 panic(msg) 114 } 115 return f.Offset 116 }