github.com/tencent/goom@v1.0.1/internal/unexports/unexports.go (about) 1 // Package unexports 实现了对未导出函数的获取 2 // 基于 github.com/alangpierce/go-forceexport 进行了修改和扩展。 3 package unexports 4 5 import ( 6 "errors" 7 "fmt" 8 "reflect" 9 "runtime" 10 "unsafe" 11 12 "github.com/tencent/goom/erro" 13 "github.com/tencent/goom/internal/hack" 14 "github.com/tencent/goom/internal/logger" 15 ) 16 17 const ptrMax uintptr = (1<<31 - 1) * 100000 18 19 // FindFuncByName searches through the 'moduledata' table created by the linker 20 // and returns the function's code pointer. If the function was not found, it 21 // returns an error. Since the data structures here are not exported, we copy 22 // them below (and they need to stay in sync or else things will fail 23 // catastrophically). 24 func FindFuncByName(name string) (uintptr, error) { 25 if len(name) == 0 { 26 return 0, errors.New("FindFuncByName error: func name is empty") 27 } 28 29 suggester := newSuggester(name) 30 for moduleData := &hack.Firstmoduledata; moduleData != nil; moduleData = moduleData.Next { 31 for _, ftab := range moduleData.Ftab { 32 if checkOverflow(ftab, moduleData) { 33 break 34 } 35 f := (*runtime.Func)(unsafe.Pointer(&moduleData.Pclntable[ftab.Funcoff])) 36 if f == nil { 37 continue 38 } 39 40 if f.Entry() > ptrMax { 41 fmt.Println(f.Entry(), ptrMax) 42 continue 43 } 44 45 fName := funcName(f) 46 if fName == name { 47 return f.Entry(), nil 48 } 49 50 suggester.AddItem(fName) 51 } 52 } 53 logger.Debugf("FindFuncByName not found %s", name) 54 return 0, erro.NewFuncNotFoundErrorWithSuggestion(name, suggester.Suggestions()) 55 } 56 57 // funcName 获取函数名字 58 func funcName(f *runtime.Func) string { 59 defer func() { 60 if err := recover(); err != nil { 61 var buf = make([]byte, 1024) 62 63 runtime.Stack(buf, true) 64 logger.Errorf("get funcName error:[%+v]\n%s", err, buf) 65 } 66 }() 67 return f.Name() 68 } 69 70 // FindFuncByPtr 根据地址找到函数信息 71 // 返回: *runtime.Func 函数信息结构体 72 // string 函数名 73 func FindFuncByPtr(ptr uintptr) (*runtime.Func, string, error) { 74 for moduleData := &hack.Firstmoduledata; moduleData != nil; moduleData = moduleData.Next { 75 for _, ftab := range moduleData.Ftab { 76 if checkOverflow(ftab, moduleData) { 77 break 78 } 79 f := (*runtime.Func)(unsafe.Pointer(&moduleData.Pclntable[ftab.Funcoff])) 80 if f == nil { 81 continue 82 } 83 84 if f.Entry() > ptrMax { 85 continue 86 } 87 88 fName := funcName(f) 89 90 if f.Entry() == ptr { 91 return f, fName, nil 92 } 93 } 94 } 95 return nil, "", fmt.Errorf("invalid function ptr: %d", ptr) 96 } 97 98 // CreateFuncForCodePtr is given a code pointer and creates a function value 99 // that uses that pointer. The outFun argument should be a pointer to a function 100 // of the proper type (e.g. the address of a local variable), and will be set to 101 // the result function value. 102 func CreateFuncForCodePtr(outFuncPtr interface{}, codePtr uintptr) (*hack.Func, error) { 103 outFunc := reflect.ValueOf(outFuncPtr) 104 if outFunc.Kind() != reflect.Ptr { 105 return nil, errors.New("func param must be ptr") 106 } 107 108 outFuncVal := outFunc.Elem() 109 // Use reflect.MakeGlobalFunc to create a well-formed function value that's 110 // guaranteed to be of the right type and guaranteed to be on the heap 111 // (so that we can modify it). We give a nil delegate function because 112 // it will never actually be called. 113 newFuncVal := reflect.MakeFunc(outFuncVal.Type(), nil) 114 // Use reflection on the reflect.Value (yep!) to grab the underling 115 // function value pointer. Trying to call newFuncVal.Pointer() wouldn't 116 // work because it gives the code pointer rather than the function value 117 // pointer. The function value is a struct that starts with its code 118 // pointer, so we can swap out the code pointer with our desired value. 119 funcValuePtr := reflect.ValueOf(newFuncVal).FieldByName("ptr").Pointer() 120 // nolint hack 用法 121 funcPtr := (*hack.Func)(unsafe.Pointer(funcValuePtr)) 122 funcPtr.CodePtr = codePtr 123 124 outFuncVal.Set(newFuncVal) 125 return funcPtr, nil 126 } 127 128 // NewFuncWithCodePtr 根据类型和函数地址进行构造 reflect.Value 129 func NewFuncWithCodePtr(typ reflect.Type, codePtr uintptr) reflect.Value { 130 pointer := unsafe.Pointer(&codePtr) 131 funcVal := reflect.NewAt(typ, pointer).Elem() 132 (*hack.Value)(unsafe.Pointer(&funcVal)).Flag = uintptr(reflect.Func) 133 return funcVal 134 }