github.com/lsg2020/go-forceexport@v1.0.5/go116/forceexport.go (about)

     1  // for go1.16
     2  
     3  package forceexport
     4  
     5  import (
     6  	"fmt"
     7  	"reflect"
     8  	"runtime"
     9  	"unsafe"
    10  )
    11  
    12  // GetFunc gets the function defined by the given fully-qualified name. The
    13  // outFuncPtr parameter should be a pointer to a function with the appropriate
    14  // type (e.g. the address of a local variable), and is set to a new function
    15  // value that calls the specified function. If the specified function does not
    16  // exist, outFuncPtr is not set and an error is returned.
    17  func GetFunc(outFuncPtr interface{}, name string) error {
    18  	codePtr, err := FindFuncWithName(name)
    19  	if err != nil {
    20  		return err
    21  	}
    22  	CreateFuncForCodePtr(outFuncPtr, codePtr)
    23  	return nil
    24  }
    25  
    26  // Convenience struct for modifying the underlying code pointer of a function
    27  // value. The actual struct has other values, but always starts with a code
    28  // pointer.
    29  type Func struct {
    30  	codePtr uintptr
    31  }
    32  
    33  // CreateFuncForCodePtr is given a code pointer and creates a function value
    34  // that uses that pointer. The outFun argument should be a pointer to a function
    35  // of the proper type (e.g. the address of a local variable), and will be set to
    36  // the result function value.
    37  func CreateFuncForCodePtr(outFuncPtr interface{}, codePtr uintptr) {
    38  	outFuncVal := reflect.ValueOf(outFuncPtr).Elem()
    39  	// Use reflect.MakeFunc to create a well-formed function value that's
    40  	// guaranteed to be of the right type and guaranteed to be on the heap
    41  	// (so that we can modify it). We give a nil delegate function because
    42  	// it will never actually be called.
    43  	newFuncVal := reflect.MakeFunc(outFuncVal.Type(), nil)
    44  	// Use reflection on the reflect.Value (yep!) to grab the underling
    45  	// function value pointer. Trying to call newFuncVal.Pointer() wouldn't
    46  	// work because it gives the code pointer rather than the function value
    47  	// pointer. The function value is a struct that starts with its code
    48  	// pointer, so we can swap out the code pointer with our desired value.
    49  	funcValuePtr := reflect.ValueOf(newFuncVal).FieldByName("ptr").Pointer()
    50  	funcPtr := (*Func)(unsafe.Pointer(funcValuePtr))
    51  	funcPtr.codePtr = codePtr
    52  	outFuncVal.Set(newFuncVal)
    53  }
    54  
    55  // FindFuncWithName searches through the moduledata table created by the linker
    56  // and returns the function's code pointer. If the function was not found, it
    57  // returns an error. Since the data structures here are not exported, we copy
    58  // them below (and they need to stay in sync or else things will fail
    59  // catastrophically).
    60  func FindFuncWithName(name string) (uintptr, error) {
    61  	for moduleData := &Firstmoduledata; moduleData != nil; moduleData = moduleData.next {
    62  		for _, ftab := range moduleData.ftab {
    63  			if int(ftab.funcoff) >= len(moduleData.pclntable) {
    64  				continue
    65  			}
    66  			f := (*runtime.Func)(unsafe.Pointer(&moduleData.pclntable[ftab.funcoff]))
    67  			funcName, err := getFuncName(f)
    68  			if err == nil && funcName == name {
    69  				return f.Entry(), nil
    70  			}
    71  		}
    72  	}
    73  	return 0, fmt.Errorf("Invalid function name: %s", name)
    74  }
    75  
    76  func GetAllFuncName() (names []string) {
    77  	for moduleData := &Firstmoduledata; moduleData != nil; moduleData = moduleData.next {
    78  		for _, ftab := range moduleData.ftab {
    79  			if int(ftab.funcoff) >= len(moduleData.pclntable) {
    80  				continue
    81  			}
    82  			f := (*runtime.Func)(unsafe.Pointer(&moduleData.pclntable[ftab.funcoff]))
    83  			funcName, err := getFuncName(f)
    84  			if err == nil {
    85  				names = append(names, funcName)
    86  			}
    87  		}
    88  	}
    89  	return
    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
    97  
    98  type Moduledata struct {
    99  	pcHeader     *pcHeader
   100  	funcnametab  []byte
   101  	cutab        []uint32
   102  	filetab      []byte
   103  	pctab        []byte
   104  	pclntable    []byte
   105  	ftab         []functab
   106  	findfunctab  uintptr
   107  	minpc, maxpc uintptr
   108  
   109  	text, etext           uintptr
   110  	noptrdata, enoptrdata uintptr
   111  	data, edata           uintptr
   112  	bss, ebss             uintptr
   113  	noptrbss, enoptrbss   uintptr
   114  	end, gcdata, gcbss    uintptr
   115  	types, etypes         uintptr
   116  
   117  	textsectmap []textsect
   118  	typelinks   []int32 // offsets from types
   119  	itablinks   []*itab
   120  
   121  	ptab []ptabEntry
   122  
   123  	pluginpath string
   124  	pkghashes  []modulehash
   125  
   126  	modulename   string
   127  	modulehashes []modulehash
   128  
   129  	hasmain uint8 // 1 if module contains the main function, 0 otherwise
   130  
   131  	gcdatamask, gcbssmask bitvector
   132  
   133  	typemap map[typeOff]*_type // offset to *_rtype in previous module
   134  
   135  	bad bool // module failed to load and should be ignored
   136  
   137  	next *Moduledata
   138  }
   139  
   140  // pcHeader holds data used by the pclntab lookups.
   141  type pcHeader struct {
   142  	magic          uint32  // 0xFFFFFFFA
   143  	pad1, pad2     uint8   // 0,0
   144  	minLC          uint8   // min instruction size
   145  	ptrSize        uint8   // size of a ptr in bytes
   146  	nfunc          int     // number of functions in the module
   147  	nfiles         uint    // number of entries in the file tab.
   148  	funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
   149  	cuOffset       uintptr // offset to the cutab variable from pcHeader
   150  	filetabOffset  uintptr // offset to the filetab variable from pcHeader
   151  	pctabOffset    uintptr // offset to the pctab varible from pcHeader
   152  	pclnOffset     uintptr // offset to the pclntab variable from pcHeader
   153  }
   154  
   155  type functab struct {
   156  	entry   uintptr
   157  	funcoff uintptr
   158  }
   159  
   160  type textsect struct {
   161  	vaddr    uintptr // prelinked section vaddr
   162  	length   uintptr // section length
   163  	baseaddr uintptr // relocated section address
   164  }
   165  
   166  type itab struct {
   167  	inter *interfacetype
   168  	_type *_type
   169  	hash  uint32 // copy of _type.hash. Used for type switches.
   170  	_     [4]byte
   171  	fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
   172  }
   173  
   174  type interfacetype struct {
   175  	typ     _type
   176  	pkgpath name
   177  	mhdr    []imethod
   178  }
   179  
   180  type _type struct {
   181  	size       uintptr
   182  	ptrdata    uintptr // size of memory prefix holding all pointers
   183  	hash       uint32
   184  	tflag      tflag
   185  	align      uint8
   186  	fieldAlign uint8
   187  	kind       uint8
   188  	// function for comparing objects of this type
   189  	// (ptr to object A, ptr to object B) -> ==?
   190  	equal func(unsafe.Pointer, unsafe.Pointer) bool
   191  	// gcdata stores the GC type data for the garbage collector.
   192  	// If the KindGCProg bit is set in kind, gcdata is a GC program.
   193  	// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
   194  	gcdata    *byte
   195  	str       nameOff
   196  	ptrToThis typeOff
   197  }
   198  
   199  type name struct {
   200  	bytes *byte
   201  }
   202  
   203  type imethod struct {
   204  	name nameOff
   205  	ityp typeOff
   206  }
   207  
   208  type tflag uint8
   209  type nameOff int32
   210  type typeOff int32
   211  
   212  type ptabEntry struct {
   213  	name nameOff
   214  	typ  typeOff
   215  }
   216  
   217  type modulehash struct {
   218  	modulename   string
   219  	linktimehash string
   220  	runtimehash  *string
   221  }
   222  
   223  type bitvector struct {
   224  	n        int32 // # of bits
   225  	bytedata *uint8
   226  }
   227  
   228  func getFuncName(f *runtime.Func) (funcName string, err error) {
   229  	// f.Name() may panic because runtime.findmoduledatap may return nil
   230  	defer func() {
   231  		if r := recover(); r != nil {
   232  			switch e := r.(type) {
   233  			case error:
   234  				err = e
   235  			default:
   236  				panic("unexpect")
   237  			}
   238  		}
   239  	}()
   240  
   241  	// (*Func).Name() assumes that the *Func was created by some exported
   242  	// method that would have returned a nil *Func pointer IF the
   243  	// desired function's datap resolves to nil.
   244  	// (a.k.a. if findmoduledatap(pc) returns nil)
   245  	// Since the last element of the moduleData.ftab has a datap of nil
   246  	// (from experimentation), .Name() Seg Faults on the last element.
   247  	//
   248  	// If we instead ask the external function FuncForPc to fetch
   249  	// our *Func object, it will check the datap first and give us
   250  	// a proper nil *Func, that .Name() understands.
   251  	// The down side of doing this is that internally, the
   252  	// findmoduledatap(pc) function is called twice for every element
   253  	// we loop over.
   254  	f = runtime.FuncForPC(f.Entry())
   255  
   256  	funcName = f.Name()
   257  	return funcName, err
   258  }