github.com/alaxlee/go-forceexport@v0.0.0-20200629082744-bf87b4f3ffd6/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  // 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 := &Firstmoduledata; moduleData != nil; moduleData = moduleData.next {
    60  		for _, ftab := range moduleData.ftab {
    61  			f := (*runtime.Func)(unsafe.Pointer(&moduleData.pclntable[ftab.funcoff]))
    62  			if f.Name() == name {
    63  				return f.Entry(), nil
    64  			}
    65  		}
    66  	}
    67  	return 0, fmt.Errorf("Invalid function name: %s", name)
    68  }
    69  
    70  // Everything below is taken from the runtime package, and must stay in sync
    71  // with it.
    72  
    73  //go:linkname Firstmoduledata runtime.firstmoduledata
    74  var Firstmoduledata Moduledata
    75  
    76  type Moduledata struct {
    77  	pclntable    []byte
    78  	ftab         []Functab
    79  	filetab      []uint32
    80  	findfunctab  uintptr
    81  	minpc, maxpc uintptr
    82  
    83  	text, etext           uintptr
    84  	noptrdata, enoptrdata uintptr
    85  	data, edata           uintptr
    86  	bss, ebss             uintptr
    87  	noptrbss, enoptrbss   uintptr
    88  	end, gcdata, gcbss    uintptr
    89  
    90  	// Original type was []*_type
    91  	typelinks []interface{}
    92  
    93  	modulename string
    94  	// Original type was []modulehash
    95  	modulehashes []interface{}
    96  
    97  	gcdatamask, gcbssmask Bitvector
    98  
    99  	next *Moduledata
   100  }
   101  
   102  type Functab struct {
   103  	entry   uintptr
   104  	funcoff uintptr
   105  }
   106  
   107  type Bitvector struct {
   108  	n        int32 // # of bits
   109  	bytedata *uint8
   110  }