github.com/volts-dev/volts@v0.0.0-20240120094013-5e9c65924106/internal/test/router/force_export.go (about)

     1  package router
     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  // pcHeader holds data used by the pclntab lookups.
    77  type pcHeader struct {
    78  	magic          uint32  // 0xFFFFFFF0
    79  	pad1, pad2     uint8   // 0,0
    80  	minLC          uint8   // min instruction size
    81  	ptrSize        uint8   // size of a ptr in bytes
    82  	nfunc          int     // number of functions in the module
    83  	nfiles         uint    // number of entries in the file tab
    84  	textStart      uintptr // base for function entry PC offsets in this module, equal to moduledata.text
    85  	funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
    86  	cuOffset       uintptr // offset to the cutab variable from pcHeader
    87  	filetabOffset  uintptr // offset to the filetab variable from pcHeader
    88  	pctabOffset    uintptr // offset to the pctab variable from pcHeader
    89  	pclnOffset     uintptr // offset to the pclntab variable from pcHeader
    90  }
    91  type Moduledata struct {
    92  	pcHeader     *pcHeader
    93  	funcnametab  []byte
    94  	cutab        []uint32
    95  	filetab      []byte
    96  	pctab        []byte
    97  	pclntable    []byte
    98  	ftab         []Functab
    99  	findfunctab  uintptr
   100  	minpc, maxpc uintptr
   101  
   102  	text, etext           uintptr
   103  	noptrdata, enoptrdata uintptr
   104  	data, edata           uintptr
   105  	bss, ebss             uintptr
   106  	noptrbss, enoptrbss   uintptr
   107  	end, gcdata, gcbss    uintptr
   108  	types, etypes         uintptr
   109  	rodata                uintptr
   110  	gofunc                uintptr // go.func.*
   111  
   112  	// Original type was []*_type
   113  	typelinks []interface{}
   114  
   115  	modulename string
   116  	// Original type was []modulehash
   117  	modulehashes []interface{}
   118  
   119  	gcdatamask, gcbssmask Bitvector
   120  
   121  	next *Moduledata
   122  }
   123  
   124  type Functab struct {
   125  	entry   uintptr
   126  	funcoff uintptr
   127  }
   128  
   129  type Bitvector struct {
   130  	n        int32 // # of bits
   131  	bytedata *uint8
   132  }