github.com/volts-dev/volts@v0.0.0-20240120094013-5e9c65924106/internal/test/router/force_export.go (about) 1 package router 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 // 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 := &Firstmoduledata; moduleData != nil; moduleData = moduleData.next { 60 for _, ftab := range moduleData.ftab { 61 f := (*runtime.Func)(unsafe.Pointer(&moduleData.pclntable[ftab.funcoff])) 62 if f.Name() == name { 63 return f.Entry(), nil 64 } 65 } 66 } 67 return 0, fmt.Errorf("Invalid function name: %s", name) 68 } 69 70 // Everything below is taken from the runtime package, and must stay in sync 71 // with it. 72 73 ///go:linkname Firstmoduledata runtime.firstmoduledata 74 var Firstmoduledata Moduledata 75 76 // pcHeader holds data used by the pclntab lookups. 77 type pcHeader struct { 78 magic uint32 // 0xFFFFFFF0 79 pad1, pad2 uint8 // 0,0 80 minLC uint8 // min instruction size 81 ptrSize uint8 // size of a ptr in bytes 82 nfunc int // number of functions in the module 83 nfiles uint // number of entries in the file tab 84 textStart uintptr // base for function entry PC offsets in this module, equal to moduledata.text 85 funcnameOffset uintptr // offset to the funcnametab variable from pcHeader 86 cuOffset uintptr // offset to the cutab variable from pcHeader 87 filetabOffset uintptr // offset to the filetab variable from pcHeader 88 pctabOffset uintptr // offset to the pctab variable from pcHeader 89 pclnOffset uintptr // offset to the pclntab variable from pcHeader 90 } 91 type Moduledata struct { 92 pcHeader *pcHeader 93 funcnametab []byte 94 cutab []uint32 95 filetab []byte 96 pctab []byte 97 pclntable []byte 98 ftab []Functab 99 findfunctab uintptr 100 minpc, maxpc uintptr 101 102 text, etext uintptr 103 noptrdata, enoptrdata uintptr 104 data, edata uintptr 105 bss, ebss uintptr 106 noptrbss, enoptrbss uintptr 107 end, gcdata, gcbss uintptr 108 types, etypes uintptr 109 rodata uintptr 110 gofunc uintptr // go.func.* 111 112 // Original type was []*_type 113 typelinks []interface{} 114 115 modulename string 116 // Original type was []modulehash 117 modulehashes []interface{} 118 119 gcdatamask, gcbssmask Bitvector 120 121 next *Moduledata 122 } 123 124 type Functab struct { 125 entry uintptr 126 funcoff uintptr 127 } 128 129 type Bitvector struct { 130 n int32 // # of bits 131 bytedata *uint8 132 }