github.com/jxskiss/gopkg@v0.17.3/forceexport/function.go (about)

     1  package forceexport
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"runtime"
     7  	"unsafe"
     8  
     9  	"github.com/jxskiss/gopkg/internal/linkname"
    10  	"github.com/jxskiss/gopkg/reflectx"
    11  )
    12  
    13  // GetFunc gets the function defined by the given fully-qualified name.
    14  // The outFuncPtr parameter should be a pointer to a function with the
    15  // appropriate type (e.g. the address of a local variable), and is set to
    16  // a new function value that calls the specified function.
    17  // If the function does not exist, or is inlined, or inactive (haven't been
    18  // compiled into the binary), it panics.
    19  func GetFunc(outFuncPtr interface{}, name string) {
    20  	codePtr := FindFuncWithName(name)
    21  	CreateFuncForCodePtr(outFuncPtr, codePtr)
    22  }
    23  
    24  // Func is a convenience struct for modifying the underlying code pointer
    25  // of a function value. The actual struct has other values, but always
    26  // starts with a code pointer.
    27  type Func struct {
    28  	codePtr uintptr
    29  }
    30  
    31  // CreateFuncForCodePtr accepts a code pointer and creates a function
    32  // value that uses the pointer. The outFuncPtr argument should be a pointer
    33  // to a function of the proper type (e.g. the address of a local variable),
    34  // and will be set to 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).
    40  	newFuncVal := reflect.MakeFunc(outFuncVal.Type(), nil)
    41  
    42  	// Use reflection on the reflect.Value (yep!) to grab the underlying
    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  
    51  	outFuncVal.Set(newFuncVal)
    52  }
    53  
    54  // FindFuncWithName searches through the moduledata table created by the
    55  // linker and returns the function's code pointer.
    56  // If the function does not exist, or is inlined, or inactive (haven't been
    57  // compiled into the binary), it panics.
    58  func FindFuncWithName(name string) uintptr {
    59  	for _, moduleData := range activeModules() {
    60  		pclntable := moduleData.pclntable()
    61  		for _, ftab := range moduleData.ftab() {
    62  			f := (*runtime.Func)(unsafe.Pointer(&pclntable[ftab.funcoff]))
    63  			if getName(f) == name {
    64  				return f.Entry()
    65  			}
    66  		}
    67  	}
    68  	panic(fmt.Sprintf("forceexport: cannot find function %s, maybe inlined or inactive", name))
    69  }
    70  
    71  func getName(f *runtime.Func) string {
    72  	defer func() {
    73  		recover()
    74  	}()
    75  	return f.Name()
    76  }
    77  
    78  func activeModules() []moduledata {
    79  	mdptrs := linkname.Runtime_activeModules()
    80  	out := make([]moduledata, len(mdptrs))
    81  	for i, ptr := range mdptrs {
    82  		out[i] = moduledata{ptr}
    83  	}
    84  	return out
    85  }
    86  
    87  type moduledata struct {
    88  	p unsafe.Pointer
    89  }
    90  
    91  func (p *moduledata) pclntable() []byte {
    92  	return *(*[]byte)(unsafe.Pointer(uintptr(p.p) + moduledata_pclntableOffset))
    93  }
    94  
    95  func (p *moduledata) ftab() []functab {
    96  	return *(*[]functab)(unsafe.Pointer(uintptr(p.p) + moduledata_ftabOffset))
    97  }
    98  
    99  var (
   100  	moduledata_pclntableOffset uintptr
   101  	moduledata_ftabOffset      uintptr
   102  )
   103  
   104  func init() {
   105  	rtmdtype := GetType("runtime.moduledata")
   106  	moduledata_pclntableOffset = getOffset(rtmdtype, "pclntable", "forceexport: moduledata.pclntable not found")
   107  	moduledata_ftabOffset = getOffset(rtmdtype, "ftab", "foceexport: moduledata.ftab not found")
   108  }
   109  
   110  func getOffset(t *reflectx.RType, fieldname string, msg string) uintptr {
   111  	f, ok := t.FieldByName(fieldname)
   112  	if !ok {
   113  		panic(msg)
   114  	}
   115  	return f.Offset
   116  }