github.com/henrylee2cn/go-forceexport@v0.0.0-20200725083357-a18b02135c5b/forceexport.go (about)

     1  package forceexport
     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  // Func 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 := range activeModules() {
    60  		for _, ftab := range moduleData.ftab {
    61  			f := (*runtime.Func)(unsafe.Pointer(&moduleData.pclntable[ftab.funcoff]))
    62  			if getName(f) == name {
    63  				return f.Entry(), nil
    64  			}
    65  		}
    66  	}
    67  	return 0, fmt.Errorf("invalid function name: %s", name)
    68  }
    69  
    70  func getName(f *runtime.Func) string {
    71  	defer func() {
    72  		recover()
    73  	}()
    74  	return f.Name()
    75  }
    76  
    77  // SymtabNamesOfActiveFunc return the symtab name list of active functions.
    78  func SymtabNamesOfActiveFunc() []string {
    79  	slice := activeModules()
    80  	names := make([]string, 0, len(slice))
    81  	for _, moduleData := range slice {
    82  		for _, ftab := range moduleData.ftab {
    83  			f := (*runtime.Func)(unsafe.Pointer(&moduleData.pclntable[ftab.funcoff]))
    84  			names = append(names, getName(f))
    85  		}
    86  	}
    87  	return names
    88  }
    89  
    90  //go:linkname activeModules runtime.activeModules
    91  func activeModules() []*moduledata
    92  
    93  type moduledata struct {
    94  	// NOTE: Just need to ensure that the following fields and their indexes are consistent with runtime.moduledata
    95  	pclntable []byte
    96  	ftab      []functab
    97  }
    98  
    99  type functab struct {
   100  	entry   uintptr
   101  	funcoff uintptr
   102  }