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  }