github.com/szmcdull/go-forceexport@v0.0.0-20230908151957-3dac42f564da/forceexport.go (about) 1 package forceexport 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 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 if strings.HasPrefix(name, `go.`) && !strings.Contains(name, `/`) { 17 name = strings.Replace(name, `go.`, `go%2e`, 1) 18 } 19 codePtr, err := FindFuncWithName(name) 20 if err != nil { 21 return err 22 } 23 CreateFuncForCodePtr(outFuncPtr, codePtr) 24 return nil 25 } 26 27 // Convenience struct for modifying the underlying code pointer of a function 28 // value. The actual struct has other values, but always starts with a code 29 // pointer. 30 type Func struct { 31 codePtr uintptr 32 } 33 34 // CreateFuncForCodePtr is given a code pointer and creates a function value 35 // that uses that pointer. The outFun argument should be a pointer to a function 36 // of the proper type (e.g. the address of a local variable), and will be set to 37 // the result function value. 38 func CreateFuncForCodePtr(outFuncPtr interface{}, codePtr uintptr) { 39 outFuncVal := reflect.ValueOf(outFuncPtr).Elem() 40 // Use reflect.MakeFunc to create a well-formed function value that's 41 // guaranteed to be of the right type and guaranteed to be on the heap 42 // (so that we can modify it). We give a nil delegate function because 43 // it will never actually be called. 44 newFuncVal := reflect.MakeFunc(outFuncVal.Type(), nil) 45 // Use reflection on the reflect.Value (yep!) to grab the underling 46 // function value pointer. Trying to call newFuncVal.Pointer() wouldn't 47 // work because it gives the code pointer rather than the function value 48 // pointer. The function value is a struct that starts with its code 49 // pointer, so we can swap out the code pointer with our desired value. 50 funcPtr := (*Func)(unsafe.Pointer(reflect.ValueOf(newFuncVal).FieldByName("ptr").Pointer())) 51 funcPtr.codePtr = codePtr 52 outFuncVal.Set(newFuncVal) 53 } 54 55 type () 56 57 // FindFuncWithName searches through the moduledata table created by the linker 58 // and returns the function's code pointer. If the function was not found, it 59 // returns an error. Since the data structures here are not exported, we copy 60 // them below (and they need to stay in sync or else things will fail 61 // catastrophically). 62 func FindFuncWithName(name string) (uintptr, error) { 63 module := getModuleWrapper() 64 65 for { 66 ftabs := module.GetFtab() 67 l := len(ftabs) 68 for i, ftab := range ftabs { 69 if i == l-1 { 70 break 71 } 72 f := module.GetFunc(ftab) 73 n := f.Name() 74 //println(n) 75 // if n == `main.init.0` || n == `main.main` { 76 // time.Now() 77 // } 78 if n == name { 79 return f.Entry(), nil 80 } 81 } 82 module = module.GetNext() 83 //println(module) 84 if module == nil { 85 break 86 } 87 } 88 89 return 0, fmt.Errorf("Invalid function name: %s", name) 90 } 91 92 // Everything below is taken from the runtime package, and must stay in sync 93 // with it. 94 95 // //go:linkname Firstmoduledata runtime.firstmoduledata 96 // var Firstmoduledata Moduledata