github.com/dearplain/goloader@v0.0.0-20190107071432-2b1e47d74273/module.go (about)

     1  package goloader
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"strings"
     7  	"unsafe"
     8  )
     9  
    10  //go:linkname firstmoduledata runtime.firstmoduledata
    11  var firstmoduledata moduledata
    12  
    13  const PtrSize = 4 << (^uintptr(0) >> 63)
    14  const _funcSize = int(unsafe.Sizeof(_func{}))
    15  
    16  type functab struct {
    17  	entry   uintptr
    18  	funcoff uintptr
    19  }
    20  
    21  // findfunctab is an array of these structures.
    22  // Each bucket represents 4096 bytes of the text segment.
    23  // Each subbucket represents 256 bytes of the text segment.
    24  // To find a function given a pc, locate the bucket and subbucket for
    25  // that pc. Add together the idx and subbucket value to obtain a
    26  // function index. Then scan the functab array starting at that
    27  // index to find the target function.
    28  // This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead.
    29  type findfuncbucket struct {
    30  	idx        uint32
    31  	subbuckets [16]byte
    32  }
    33  
    34  // Mapping information for secondary text sections
    35  type textsect struct {
    36  	vaddr    uintptr // prelinked section vaddr
    37  	length   uintptr // section length
    38  	baseaddr uintptr // relocated section address
    39  }
    40  
    41  type itab struct {
    42  	inter  uintptr
    43  	_type  uintptr
    44  	link   uintptr
    45  	hash   uint32 // copy of _type.hash. Used for type switches.
    46  	bad    bool   // type does not implement interface
    47  	inhash bool   // has this itab been added to hash?
    48  	unused [2]byte
    49  	fun    [1]uintptr // variable sized
    50  }
    51  
    52  type nameOff int32
    53  type typeOff int32
    54  type textOff int32
    55  
    56  // A ptabEntry is generated by the compiler for each exported function
    57  // and global variable in the main package of a plugin. It is used to
    58  // initialize the plugin module's symbol map.
    59  type ptabEntry struct {
    60  	name nameOff
    61  	typ  typeOff
    62  }
    63  
    64  type modulehash struct {
    65  	modulename   string
    66  	linktimehash string
    67  	runtimehash  *string
    68  }
    69  
    70  type bitvector struct {
    71  	n        int32 // # of bits
    72  	bytedata *uint8
    73  }
    74  
    75  type moduledata struct {
    76  	pclntable    []byte
    77  	ftab         []functab
    78  	filetab      []uint32
    79  	findfunctab  uintptr
    80  	minpc, maxpc uintptr
    81  
    82  	text, etext           uintptr
    83  	noptrdata, enoptrdata uintptr
    84  	data, edata           uintptr
    85  	bss, ebss             uintptr
    86  	noptrbss, enoptrbss   uintptr
    87  	end, gcdata, gcbss    uintptr
    88  	types, etypes         uintptr
    89  
    90  	textsectmap []textsect
    91  	typelinks   []int32 // offsets from types
    92  	itablinks   []*itab
    93  
    94  	ptab []ptabEntry
    95  
    96  	pluginpath string
    97  	pkghashes  []modulehash
    98  
    99  	modulename   string
   100  	modulehashes []modulehash
   101  
   102  	gcdatamask, gcbssmask bitvector
   103  
   104  	typemap map[typeOff]uintptr // offset to *_rtype in previous module
   105  
   106  	next *moduledata
   107  }
   108  
   109  type _func struct {
   110  	entry   uintptr // start pc
   111  	nameoff int32   // function name
   112  
   113  	args int32 // in/out args size
   114  	_    int32 // previously legacy frame size; kept for layout compatibility
   115  
   116  	pcsp      int32
   117  	pcfile    int32
   118  	pcln      int32
   119  	npcdata   int32
   120  	nfuncdata int32
   121  }
   122  
   123  type funcInfoData struct {
   124  	_func
   125  	pcdata   []uint32
   126  	funcdata []uintptr
   127  }
   128  
   129  type stackmap struct {
   130  	n        int32   // number of bitmaps
   131  	nbit     int32   // number of bits in each bitmap
   132  	bytedata [1]byte // bitmaps, each starting on a byte boundary
   133  }
   134  
   135  type Module struct {
   136  	pclntable []byte
   137  	pcfunc    []findfuncbucket
   138  	funcinfo  []funcInfoData
   139  	ftab      []functab // entry need reloc
   140  	filetab   []uint32
   141  	stkmaps   [][]byte
   142  }
   143  
   144  const minfunc = 16                 // minimum function size
   145  const pcbucketsize = 256 * minfunc // size of bucket in the pc->func lookup table
   146  const nsub = len(findfuncbucket{}.subbuckets)
   147  
   148  func readFuncData(module *Module, curSymFile symFile,
   149  	allSyms map[string]symFile, gcObjs map[string]uintptr,
   150  	fileTabOffsetMap map[string]int, curSymOffset, curCodeLen int) {
   151  
   152  	fs := readAtSeeker{ReadSeeker: curSymFile.file}
   153  	curSym := curSymFile.sym
   154  
   155  	{
   156  		x := curCodeLen
   157  		b := x / pcbucketsize
   158  		i := x % pcbucketsize / (pcbucketsize / nsub)
   159  		for lb := b - len(module.pcfunc); lb >= 0; lb-- {
   160  			module.pcfunc = append(module.pcfunc, findfuncbucket{
   161  				idx: uint32(256 * len(module.pcfunc))})
   162  		}
   163  		bucket := &module.pcfunc[b]
   164  		if len(module.ftab) <= 0 {
   165  			module.ftab = append(module.ftab, functab{})
   166  		}
   167  		bucket.subbuckets[i] = byte(len(module.ftab) - int(bucket.idx))
   168  	}
   169  
   170  	var fileTabOffset = len(module.filetab)
   171  	var fileOffsets []uint32
   172  	var fullFile string
   173  	for _, fileName := range curSym.Func.File {
   174  		fileOffsets = append(fileOffsets, uint32(len(fullFile)+len(module.pclntable)))
   175  		fileName = strings.TrimLeft(curSym.Func.File[0], "gofile..")
   176  		fullFile += fileName + "\x00"
   177  	}
   178  	if tabOffset, ok := fileTabOffsetMap[fullFile]; !ok {
   179  		module.pclntable = append(module.pclntable, []byte(fullFile)...)
   180  		fileTabOffsetMap[fullFile] = fileTabOffset
   181  		module.filetab = append(module.filetab, fileOffsets...)
   182  	} else {
   183  		fileTabOffset = tabOffset
   184  	}
   185  	var pcFileHead [2]byte
   186  	if fileTabOffset > 128 {
   187  		fmt.Println("filetab overflow!")
   188  	}
   189  	pcFileHead[0] = byte(fileTabOffset << 1)
   190  
   191  	nameOff := len(module.pclntable)
   192  	nameByte := make([]byte, len(curSym.Name)+1)
   193  	copy(nameByte, []byte(curSym.Name))
   194  	module.pclntable = append(module.pclntable, nameByte...)
   195  
   196  	spOff := len(module.pclntable)
   197  	var fb = make([]byte, curSym.Func.PCSP.Size)
   198  	fs.ReadAt(fb, curSym.Func.PCSP.Offset)
   199  	// fmt.Println("sp val:", fb)
   200  	module.pclntable = append(module.pclntable, fb...)
   201  
   202  	pcfileOff := len(module.pclntable)
   203  	fb = make([]byte, curSym.Func.PCFile.Size)
   204  	fs.ReadAt(fb, curSym.Func.PCFile.Offset)
   205  	// dumpPCData(fb, "pcfile")
   206  	module.pclntable = append(module.pclntable, pcFileHead[:]...)
   207  	module.pclntable = append(module.pclntable, fb...)
   208  
   209  	pclnOff := len(module.pclntable)
   210  	fb = make([]byte, curSym.Func.PCLine.Size)
   211  	fs.ReadAt(fb, curSym.Func.PCLine.Offset)
   212  	module.pclntable = append(module.pclntable, fb...)
   213  
   214  	fdata := _func{
   215  		entry:     uintptr(curSymOffset),
   216  		nameoff:   int32(nameOff),
   217  		args:      int32(curSym.Func.Args),
   218  		pcsp:      int32(spOff),
   219  		pcfile:    int32(pcfileOff),
   220  		pcln:      int32(pclnOff),
   221  		npcdata:   int32(len(curSym.Func.PCData)),
   222  		nfuncdata: int32(len(curSym.Func.FuncData)),
   223  	}
   224  	var fInfo funcInfoData
   225  	fInfo._func = fdata
   226  	for _, data := range curSym.Func.PCData {
   227  		fInfo.pcdata = append(fInfo.pcdata, uint32(len(module.pclntable)))
   228  
   229  		var b = make([]byte, data.Size)
   230  		fs.ReadAt(b, data.Offset)
   231  		// dumpPCData(b)
   232  		module.pclntable = append(module.pclntable, b...)
   233  	}
   234  	for _, data := range curSym.Func.FuncData {
   235  		var offset uintptr
   236  		if off, ok := gcObjs[data.Sym.Name]; !ok {
   237  			if gcobj, ok := allSyms[data.Sym.Name]; ok {
   238  				var b = make([]byte, gcobj.sym.Data.Size)
   239  				cfs := readAtSeeker{ReadSeeker: gcobj.file}
   240  				cfs.ReadAt(b, gcobj.sym.Data.Offset)
   241  				offset = uintptr(len(module.stkmaps))
   242  				module.stkmaps = append(module.stkmaps, b)
   243  				gcObjs[data.Sym.Name] = offset
   244  			} else {
   245  				fmt.Println("unknown gcobj:", data.Sym.Name)
   246  			}
   247  		} else {
   248  			offset = off
   249  		}
   250  
   251  		fInfo.funcdata = append(fInfo.funcdata, offset)
   252  	}
   253  
   254  	module.ftab = append(module.ftab, functab{
   255  		entry: uintptr(curSymOffset),
   256  	})
   257  
   258  	module.funcinfo = append(module.funcinfo, fInfo)
   259  }
   260  
   261  func dumpPCData(b []byte, prefix string) {
   262  	fmt.Println(prefix, b)
   263  	var pc uintptr
   264  	val := int32(-1)
   265  	var ok bool
   266  	b, ok = step(b, &pc, &val, true)
   267  	for {
   268  		if !ok || len(b) <= 0 {
   269  			fmt.Println(prefix, "step end")
   270  			break
   271  		}
   272  		fmt.Println(prefix, "pc:", pc, "val:", val)
   273  		b, ok = step(b, &pc, &val, false)
   274  	}
   275  }
   276  
   277  //go:linkname step runtime.step
   278  func step(p []byte, pc *uintptr, val *int32, first bool) (newp []byte, ok bool)
   279  
   280  //go:linkname findfunc runtime.findfunc
   281  func findfunc(pc uintptr) funcInfo
   282  
   283  //go:linkname funcdata runtime.funcdata
   284  func funcdata(f funcInfo, i int32) unsafe.Pointer
   285  
   286  //go:linkname funcname runtime.funcname
   287  func funcname(f funcInfo) string
   288  
   289  type funcInfo struct {
   290  	*_func
   291  	datap *moduledata
   292  }
   293  
   294  const (
   295  	_PCDATA_StackMapIndex       = 0
   296  	_PCDATA_InlTreeIndex        = 1
   297  	_FUNCDATA_ArgsPointerMaps   = 0
   298  	_FUNCDATA_LocalsPointerMaps = 1
   299  	_FUNCDATA_InlTree           = 2
   300  	_ArgsSizeUnknown            = -0x80000000
   301  )
   302  
   303  func dumpStackMap(f interface{}) {
   304  	finfo := findfunc(getFuncPtr(f))
   305  	fmt.Println(funcname(finfo))
   306  	stkmap := (*stackmap)(funcdata(finfo, _FUNCDATA_LocalsPointerMaps))
   307  	fmt.Printf("%v %p\n", stkmap, stkmap)
   308  }
   309  
   310  type moduledata110 struct {
   311  	pclntable    []byte
   312  	ftab         []functab
   313  	filetab      []uint32
   314  	findfunctab  uintptr
   315  	minpc, maxpc uintptr
   316  
   317  	text, etext           uintptr
   318  	noptrdata, enoptrdata uintptr
   319  	data, edata           uintptr
   320  	bss, ebss             uintptr
   321  	noptrbss, enoptrbss   uintptr
   322  	end, gcdata, gcbss    uintptr
   323  	types, etypes         uintptr
   324  
   325  	textsectmap []textsect
   326  	typelinks   []int32 // offsets from types
   327  	itablinks   []*itab
   328  
   329  	ptab []ptabEntry
   330  
   331  	pluginpath string
   332  	pkghashes  []modulehash
   333  
   334  	modulename   string
   335  	modulehashes []modulehash
   336  
   337  	hasmain uint8 // 1 if module contains the main function, 0 otherwise
   338  
   339  	gcdatamask, gcbssmask bitvector
   340  
   341  	typemap map[typeOff]uintptr // offset to *_rtype in previous module
   342  
   343  	bad bool // module failed to load and should be ignored
   344  
   345  	next *moduledata110
   346  }
   347  
   348  func moduledataTo110(m110 *moduledata110, m *moduledata) {
   349  	m110.pclntable = m.pclntable
   350  	m110.ftab = m.ftab
   351  	m110.filetab = m.filetab
   352  	m110.filetab = m.filetab
   353  	m110.findfunctab = m.findfunctab
   354  	m110.minpc = m.minpc
   355  	m110.maxpc = m.maxpc
   356  	m110.text = m.text
   357  	m110.etext = m.etext
   358  	m110.typemap = m.typemap
   359  	m110.types = m.types
   360  	m110.etypes = m.etypes
   361  }
   362  
   363  func linkModule(first uintptr, offset uintptr, newModule uintptr) {
   364  	for datap := first; ; {
   365  		p := (*uintptr)(unsafe.Pointer(datap + offset))
   366  		nextdatap := *p
   367  		if nextdatap == 0 {
   368  			*p = newModule
   369  			break
   370  		}
   371  		datap = nextdatap
   372  	}
   373  }
   374  
   375  func unlinkModule(first uintptr, offset uintptr, module uintptr) {
   376  	prevp := first
   377  	for datap := first; datap != 0; {
   378  		p := (*uintptr)(unsafe.Pointer(datap + offset))
   379  		nextdatap := *p
   380  		if datap == module {
   381  			pp := (*uintptr)(unsafe.Pointer(prevp + offset))
   382  			*pp = nextdatap
   383  		}
   384  		prevp = datap
   385  		datap = nextdatap
   386  	}
   387  }
   388  
   389  func addModule(codeModule *CodeModule, m *moduledata, goVer string) {
   390  	switch goVer[:5] {
   391  	case "go1.8", "go1.9":
   392  		tmpModule = m
   393  		modules[tmpModule] = true
   394  		offset := uintptr(unsafe.Pointer(&m.next)) - uintptr(unsafe.Pointer(m))
   395  		linkModule(uintptr(unsafe.Pointer(&firstmoduledata)),
   396  			offset, reflect.ValueOf(tmpModule).Pointer())
   397  	case "go1.1":
   398  		var m110 moduledata110
   399  		moduledataTo110(&m110, m)
   400  		tmpModule = &m110
   401  		modules[tmpModule] = true
   402  		offset := uintptr(unsafe.Pointer(&m110.next)) - uintptr(unsafe.Pointer(&m110))
   403  		linkModule(uintptr(unsafe.Pointer(&firstmoduledata)),
   404  			offset, reflect.ValueOf(tmpModule).Pointer())
   405  	default:
   406  		panic("unsupported go version: " + goVer)
   407  	}
   408  	codeModule.Module = tmpModule
   409  }
   410  
   411  func removeModule(module interface{}, goVer string) {
   412  	var offset uintptr
   413  	switch goVer[:5] {
   414  	case "go1.8", "go1.9":
   415  		var m moduledata
   416  		offset = uintptr(unsafe.Pointer(&m.next)) - uintptr(unsafe.Pointer(&m))
   417  	case "go1.1":
   418  		var m110 moduledata110
   419  		offset = uintptr(unsafe.Pointer(&m110.next)) - uintptr(unsafe.Pointer(&m110))
   420  	default:
   421  		panic("unsupported go version: " + goVer)
   422  	}
   423  	unlinkModule(uintptr(unsafe.Pointer(&firstmoduledata)), offset,
   424  		reflect.ValueOf(module).Pointer())
   425  	delete(modules, module)
   426  }