github.com/lsg2020/go-forceexport@v1.0.5/go116/forceexport.go (about) 1 // for go1.16 2 3 package forceexport 4 5 import ( 6 "fmt" 7 "reflect" 8 "runtime" 9 "unsafe" 10 ) 11 12 // GetFunc gets the function defined by the given fully-qualified name. The 13 // outFuncPtr parameter should be a pointer to a function with the appropriate 14 // type (e.g. the address of a local variable), and is set to a new function 15 // value that calls the specified function. If the specified function does not 16 // exist, outFuncPtr is not set and an error is returned. 17 func GetFunc(outFuncPtr interface{}, name string) error { 18 codePtr, err := FindFuncWithName(name) 19 if err != nil { 20 return err 21 } 22 CreateFuncForCodePtr(outFuncPtr, codePtr) 23 return nil 24 } 25 26 // Convenience struct for modifying the underlying code pointer of a function 27 // value. The actual struct has other values, but always starts with a code 28 // pointer. 29 type Func struct { 30 codePtr uintptr 31 } 32 33 // CreateFuncForCodePtr is given a code pointer and creates a function value 34 // that uses that pointer. The outFun argument should be a pointer to a function 35 // of the proper type (e.g. the address of a local variable), and will be set to 36 // the result function value. 37 func CreateFuncForCodePtr(outFuncPtr interface{}, codePtr uintptr) { 38 outFuncVal := reflect.ValueOf(outFuncPtr).Elem() 39 // Use reflect.MakeFunc to create a well-formed function value that's 40 // guaranteed to be of the right type and guaranteed to be on the heap 41 // (so that we can modify it). We give a nil delegate function because 42 // it will never actually be called. 43 newFuncVal := reflect.MakeFunc(outFuncVal.Type(), nil) 44 // Use reflection on the reflect.Value (yep!) to grab the underling 45 // function value pointer. Trying to call newFuncVal.Pointer() wouldn't 46 // work because it gives the code pointer rather than the function value 47 // pointer. The function value is a struct that starts with its code 48 // pointer, so we can swap out the code pointer with our desired value. 49 funcValuePtr := reflect.ValueOf(newFuncVal).FieldByName("ptr").Pointer() 50 funcPtr := (*Func)(unsafe.Pointer(funcValuePtr)) 51 funcPtr.codePtr = codePtr 52 outFuncVal.Set(newFuncVal) 53 } 54 55 // FindFuncWithName searches through the moduledata table created by the linker 56 // and returns the function's code pointer. If the function was not found, it 57 // returns an error. Since the data structures here are not exported, we copy 58 // them below (and they need to stay in sync or else things will fail 59 // catastrophically). 60 func FindFuncWithName(name string) (uintptr, error) { 61 for moduleData := &Firstmoduledata; moduleData != nil; moduleData = moduleData.next { 62 for _, ftab := range moduleData.ftab { 63 if int(ftab.funcoff) >= len(moduleData.pclntable) { 64 continue 65 } 66 f := (*runtime.Func)(unsafe.Pointer(&moduleData.pclntable[ftab.funcoff])) 67 funcName, err := getFuncName(f) 68 if err == nil && funcName == name { 69 return f.Entry(), nil 70 } 71 } 72 } 73 return 0, fmt.Errorf("Invalid function name: %s", name) 74 } 75 76 func GetAllFuncName() (names []string) { 77 for moduleData := &Firstmoduledata; moduleData != nil; moduleData = moduleData.next { 78 for _, ftab := range moduleData.ftab { 79 if int(ftab.funcoff) >= len(moduleData.pclntable) { 80 continue 81 } 82 f := (*runtime.Func)(unsafe.Pointer(&moduleData.pclntable[ftab.funcoff])) 83 funcName, err := getFuncName(f) 84 if err == nil { 85 names = append(names, funcName) 86 } 87 } 88 } 89 return 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 97 98 type Moduledata struct { 99 pcHeader *pcHeader 100 funcnametab []byte 101 cutab []uint32 102 filetab []byte 103 pctab []byte 104 pclntable []byte 105 ftab []functab 106 findfunctab uintptr 107 minpc, maxpc uintptr 108 109 text, etext uintptr 110 noptrdata, enoptrdata uintptr 111 data, edata uintptr 112 bss, ebss uintptr 113 noptrbss, enoptrbss uintptr 114 end, gcdata, gcbss uintptr 115 types, etypes uintptr 116 117 textsectmap []textsect 118 typelinks []int32 // offsets from types 119 itablinks []*itab 120 121 ptab []ptabEntry 122 123 pluginpath string 124 pkghashes []modulehash 125 126 modulename string 127 modulehashes []modulehash 128 129 hasmain uint8 // 1 if module contains the main function, 0 otherwise 130 131 gcdatamask, gcbssmask bitvector 132 133 typemap map[typeOff]*_type // offset to *_rtype in previous module 134 135 bad bool // module failed to load and should be ignored 136 137 next *Moduledata 138 } 139 140 // pcHeader holds data used by the pclntab lookups. 141 type pcHeader struct { 142 magic uint32 // 0xFFFFFFFA 143 pad1, pad2 uint8 // 0,0 144 minLC uint8 // min instruction size 145 ptrSize uint8 // size of a ptr in bytes 146 nfunc int // number of functions in the module 147 nfiles uint // number of entries in the file tab. 148 funcnameOffset uintptr // offset to the funcnametab variable from pcHeader 149 cuOffset uintptr // offset to the cutab variable from pcHeader 150 filetabOffset uintptr // offset to the filetab variable from pcHeader 151 pctabOffset uintptr // offset to the pctab varible from pcHeader 152 pclnOffset uintptr // offset to the pclntab variable from pcHeader 153 } 154 155 type functab struct { 156 entry uintptr 157 funcoff uintptr 158 } 159 160 type textsect struct { 161 vaddr uintptr // prelinked section vaddr 162 length uintptr // section length 163 baseaddr uintptr // relocated section address 164 } 165 166 type itab struct { 167 inter *interfacetype 168 _type *_type 169 hash uint32 // copy of _type.hash. Used for type switches. 170 _ [4]byte 171 fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter. 172 } 173 174 type interfacetype struct { 175 typ _type 176 pkgpath name 177 mhdr []imethod 178 } 179 180 type _type struct { 181 size uintptr 182 ptrdata uintptr // size of memory prefix holding all pointers 183 hash uint32 184 tflag tflag 185 align uint8 186 fieldAlign uint8 187 kind uint8 188 // function for comparing objects of this type 189 // (ptr to object A, ptr to object B) -> ==? 190 equal func(unsafe.Pointer, unsafe.Pointer) bool 191 // gcdata stores the GC type data for the garbage collector. 192 // If the KindGCProg bit is set in kind, gcdata is a GC program. 193 // Otherwise it is a ptrmask bitmap. See mbitmap.go for details. 194 gcdata *byte 195 str nameOff 196 ptrToThis typeOff 197 } 198 199 type name struct { 200 bytes *byte 201 } 202 203 type imethod struct { 204 name nameOff 205 ityp typeOff 206 } 207 208 type tflag uint8 209 type nameOff int32 210 type typeOff int32 211 212 type ptabEntry struct { 213 name nameOff 214 typ typeOff 215 } 216 217 type modulehash struct { 218 modulename string 219 linktimehash string 220 runtimehash *string 221 } 222 223 type bitvector struct { 224 n int32 // # of bits 225 bytedata *uint8 226 } 227 228 func getFuncName(f *runtime.Func) (funcName string, err error) { 229 // f.Name() may panic because runtime.findmoduledatap may return nil 230 defer func() { 231 if r := recover(); r != nil { 232 switch e := r.(type) { 233 case error: 234 err = e 235 default: 236 panic("unexpect") 237 } 238 } 239 }() 240 241 // (*Func).Name() assumes that the *Func was created by some exported 242 // method that would have returned a nil *Func pointer IF the 243 // desired function's datap resolves to nil. 244 // (a.k.a. if findmoduledatap(pc) returns nil) 245 // Since the last element of the moduleData.ftab has a datap of nil 246 // (from experimentation), .Name() Seg Faults on the last element. 247 // 248 // If we instead ask the external function FuncForPc to fetch 249 // our *Func object, it will check the datap first and give us 250 // a proper nil *Func, that .Name() understands. 251 // The down side of doing this is that internally, the 252 // findmoduledatap(pc) function is called twice for every element 253 // we loop over. 254 f = runtime.FuncForPC(f.Entry()) 255 256 funcName = f.Name() 257 return funcName, err 258 }