github.com/szmcdull/go-forceexport@v0.0.0-20230908151957-3dac42f564da/forceexport.go (about)

     1  package forceexport
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     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  	if strings.HasPrefix(name, `go.`) && !strings.Contains(name, `/`) {
    17  		name = strings.Replace(name, `go.`, `go%2e`, 1)
    18  	}
    19  	codePtr, err := FindFuncWithName(name)
    20  	if err != nil {
    21  		return err
    22  	}
    23  	CreateFuncForCodePtr(outFuncPtr, codePtr)
    24  	return nil
    25  }
    26  
    27  // Convenience struct for modifying the underlying code pointer of a function
    28  // value. The actual struct has other values, but always starts with a code
    29  // pointer.
    30  type Func struct {
    31  	codePtr uintptr
    32  }
    33  
    34  // CreateFuncForCodePtr is given a code pointer and creates a function value
    35  // that uses that pointer. The outFun argument should be a pointer to a function
    36  // of the proper type (e.g. the address of a local variable), and will be set to
    37  // the result function value.
    38  func CreateFuncForCodePtr(outFuncPtr interface{}, codePtr uintptr) {
    39  	outFuncVal := reflect.ValueOf(outFuncPtr).Elem()
    40  	// Use reflect.MakeFunc to create a well-formed function value that's
    41  	// guaranteed to be of the right type and guaranteed to be on the heap
    42  	// (so that we can modify it). We give a nil delegate function because
    43  	// it will never actually be called.
    44  	newFuncVal := reflect.MakeFunc(outFuncVal.Type(), nil)
    45  	// Use reflection on the reflect.Value (yep!) to grab the underling
    46  	// function value pointer. Trying to call newFuncVal.Pointer() wouldn't
    47  	// work because it gives the code pointer rather than the function value
    48  	// pointer. The function value is a struct that starts with its code
    49  	// pointer, so we can swap out the code pointer with our desired value.
    50  	funcPtr := (*Func)(unsafe.Pointer(reflect.ValueOf(newFuncVal).FieldByName("ptr").Pointer()))
    51  	funcPtr.codePtr = codePtr
    52  	outFuncVal.Set(newFuncVal)
    53  }
    54  
    55  type ()
    56  
    57  // FindFuncWithName searches through the moduledata table created by the linker
    58  // and returns the function's code pointer. If the function was not found, it
    59  // returns an error. Since the data structures here are not exported, we copy
    60  // them below (and they need to stay in sync or else things will fail
    61  // catastrophically).
    62  func FindFuncWithName(name string) (uintptr, error) {
    63  	module := getModuleWrapper()
    64  
    65  	for {
    66  		ftabs := module.GetFtab()
    67  		l := len(ftabs)
    68  		for i, ftab := range ftabs {
    69  			if i == l-1 {
    70  				break
    71  			}
    72  			f := module.GetFunc(ftab)
    73  			n := f.Name()
    74  			//println(n)
    75  			// if n == `main.init.0` || n == `main.main` {
    76  			// 	time.Now()
    77  			// }
    78  			if n == name {
    79  				return f.Entry(), nil
    80  			}
    81  		}
    82  		module = module.GetNext()
    83  		//println(module)
    84  		if module == nil {
    85  			break
    86  		}
    87  	}
    88  
    89  	return 0, fmt.Errorf("Invalid function name: %s", name)
    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