github.com/henrylee2cn/go-forceexport@v0.0.0-20200725083357-a18b02135c5b/forceexport.go (about) 1 package forceexport 2 3 import ( 4 "fmt" 5 "reflect" 6 "runtime" 7 "unsafe" 8 ) 9 10 // GetFunc gets the function defined by the given fully-qualified name. The 11 // outFuncPtr parameter should be a pointer to a function with the appropriate 12 // type (e.g. the address of a local variable), and is set to a new function 13 // value that calls the specified function. If the specified function does not 14 // exist, outFuncPtr is not set and an error is returned. 15 func GetFunc(outFuncPtr interface{}, name string) error { 16 codePtr, err := FindFuncWithName(name) 17 if err != nil { 18 return err 19 } 20 CreateFuncForCodePtr(outFuncPtr, codePtr) 21 return nil 22 } 23 24 // Func convenience struct for modifying the underlying code pointer of a function 25 // value. The actual struct has other values, but always starts with a code 26 // pointer. 27 type Func struct { 28 codePtr uintptr 29 } 30 31 // CreateFuncForCodePtr is given a code pointer and creates a function value 32 // that uses that pointer. The outFun argument should be a pointer to a function 33 // of the proper type (e.g. the address of a local variable), and will be set to 34 // 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). We give a nil delegate function because 40 // it will never actually be called. 41 newFuncVal := reflect.MakeFunc(outFuncVal.Type(), nil) 42 // Use reflection on the reflect.Value (yep!) to grab the underling 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 outFuncVal.Set(newFuncVal) 51 } 52 53 // FindFuncWithName searches through the moduledata table created by the linker 54 // and returns the function's code pointer. If the function was not found, it 55 // returns an error. Since the data structures here are not exported, we copy 56 // them below (and they need to stay in sync or else things will fail 57 // catastrophically). 58 func FindFuncWithName(name string) (uintptr, error) { 59 for _, moduleData := range activeModules() { 60 for _, ftab := range moduleData.ftab { 61 f := (*runtime.Func)(unsafe.Pointer(&moduleData.pclntable[ftab.funcoff])) 62 if getName(f) == name { 63 return f.Entry(), nil 64 } 65 } 66 } 67 return 0, fmt.Errorf("invalid function name: %s", name) 68 } 69 70 func getName(f *runtime.Func) string { 71 defer func() { 72 recover() 73 }() 74 return f.Name() 75 } 76 77 // SymtabNamesOfActiveFunc return the symtab name list of active functions. 78 func SymtabNamesOfActiveFunc() []string { 79 slice := activeModules() 80 names := make([]string, 0, len(slice)) 81 for _, moduleData := range slice { 82 for _, ftab := range moduleData.ftab { 83 f := (*runtime.Func)(unsafe.Pointer(&moduleData.pclntable[ftab.funcoff])) 84 names = append(names, getName(f)) 85 } 86 } 87 return names 88 } 89 90 //go:linkname activeModules runtime.activeModules 91 func activeModules() []*moduledata 92 93 type moduledata struct { 94 // NOTE: Just need to ensure that the following fields and their indexes are consistent with runtime.moduledata 95 pclntable []byte 96 ftab []functab 97 } 98 99 type functab struct { 100 entry uintptr 101 funcoff uintptr 102 }