github.com/goshafaq/sonic@v0.0.0-20231026082336-871835fb94c6/loader/funcdata_latest.go (about)

     1  // go:build go1.18 && !go1.22
     2  //go:build go1.18 && !go1.22
     3  // +build go1.18,!go1.22
     4  
     5  /*
     6   * Copyright 2021 ByteDance Inc.
     7   *
     8   * Licensed under the Apache License, Version 2.0 (the "License");
     9   * you may not use this file except in compliance with the License.
    10   * You may obtain a copy of the License at
    11   *
    12   *     http://www.apache.org/licenses/LICENSE-2.0
    13   *
    14   * Unless required by applicable law or agreed to in writing, software
    15   * distributed under the License is distributed on an "AS IS" BASIS,
    16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    17   * See the License for the specific language governing permissions and
    18   * limitations under the License.
    19   */
    20  
    21  package loader
    22  
    23  import (
    24  	"os"
    25  	"sort"
    26  	"unsafe"
    27  
    28  	"github.com/goshafaq/sonic/internal/rt"
    29  )
    30  
    31  type funcTab struct {
    32  	entry   uint32
    33  	funcoff uint32
    34  }
    35  
    36  type pcHeader struct {
    37  	magic          uint32  // 0xFFFFFFF0
    38  	pad1, pad2     uint8   // 0,0
    39  	minLC          uint8   // min instruction size
    40  	ptrSize        uint8   // size of a ptr in bytes
    41  	nfunc          int     // number of functions in the module
    42  	nfiles         uint    // number of entries in the file tab
    43  	textStart      uintptr // base for function entry PC offsets in this module, equal to moduledata.text
    44  	funcnameOffset uintptr // offset to the funcnametab variable from pcHeader
    45  	cuOffset       uintptr // offset to the cutab variable from pcHeader
    46  	filetabOffset  uintptr // offset to the filetab variable from pcHeader
    47  	pctabOffset    uintptr // offset to the pctab variable from pcHeader
    48  	pclnOffset     uintptr // offset to the pclntab variable from pcHeader
    49  }
    50  
    51  type bitVector struct {
    52  	n        int32 // # of bits
    53  	bytedata *uint8
    54  }
    55  
    56  type ptabEntry struct {
    57  	name int32
    58  	typ  int32
    59  }
    60  
    61  type textSection struct {
    62  	vaddr    uintptr // prelinked section vaddr
    63  	end      uintptr // vaddr + section length
    64  	baseaddr uintptr // relocated section address
    65  }
    66  
    67  type modulehash struct {
    68  	modulename   string
    69  	linktimehash string
    70  	runtimehash  *string
    71  }
    72  
    73  // findfuncbucket is an array of these structures.
    74  // Each bucket represents 4096 bytes of the text segment.
    75  // Each subbucket represents 256 bytes of the text segment.
    76  // To find a function given a pc, locate the bucket and subbucket for
    77  // that pc. Add together the idx and subbucket value to obtain a
    78  // function index. Then scan the functab array starting at that
    79  // index to find the target function.
    80  // This table uses 20 bytes for every 4096 bytes of code, or ~0.5% overhead.
    81  type findfuncbucket struct {
    82  	idx         uint32
    83  	_SUBBUCKETS [16]byte
    84  }
    85  
    86  type compilationUnit struct {
    87  	fileNames []string
    88  }
    89  
    90  func makeFtab(funcs []_func, maxpc uint32) (ftab []funcTab, pclntabSize int64, startLocations []uint32) {
    91  	// Allocate space for the pc->func table. This structure consists of a pc offset
    92  	// and an offset to the func structure. After that, we have a single pc
    93  	// value that marks the end of the last function in the binary.
    94  	pclntabSize = int64(len(funcs)*2*int(_PtrSize) + int(_PtrSize))
    95  	startLocations = make([]uint32, len(funcs))
    96  	for i, f := range funcs {
    97  		pclntabSize = rnd(pclntabSize, int64(_PtrSize))
    98  		//writePCToFunc
    99  		startLocations[i] = uint32(pclntabSize)
   100  		pclntabSize += int64(uint8(_FUNC_SIZE) + f.nfuncdata*4 + uint8(f.npcdata)*4)
   101  	}
   102  
   103  	ftab = make([]funcTab, 0, len(funcs)+1)
   104  
   105  	// write a map of pc->func info offsets
   106  	for i, f := range funcs {
   107  		ftab = append(ftab, funcTab{uint32(f.entryOff), uint32(startLocations[i])})
   108  	}
   109  
   110  	// Final entry of table is just end pc offset.
   111  	ftab = append(ftab, funcTab{maxpc, 0})
   112  	return
   113  }
   114  
   115  // Pcln table format: [...]funcTab + [...]_Func
   116  func makePclntable(size int64, startLocations []uint32, funcs []_func, maxpc uint32, pcdataOffs [][]uint32, funcdataOffs [][]uint32) (pclntab []byte) {
   117  	// Allocate space for the pc->func table. This structure consists of a pc offset
   118  	// and an offset to the func structure. After that, we have a single pc
   119  	// value that marks the end of the last function in the binary.
   120  	pclntab = make([]byte, size, size)
   121  
   122  	// write a map of pc->func info offsets
   123  	offs := 0
   124  	for i, f := range funcs {
   125  		byteOrder.PutUint32(pclntab[offs:offs+4], uint32(f.entryOff))
   126  		byteOrder.PutUint32(pclntab[offs+4:offs+8], uint32(startLocations[i]))
   127  		offs += 8
   128  	}
   129  	// Final entry of table is just end pc offset.
   130  	byteOrder.PutUint32(pclntab[offs:offs+4], maxpc)
   131  
   132  	// write func info table
   133  	for i := range funcs {
   134  		off := startLocations[i]
   135  
   136  		// write _func structure to pclntab
   137  		fb := rt.BytesFrom(unsafe.Pointer(&funcs[i]), int(_FUNC_SIZE), int(_FUNC_SIZE))
   138  		copy(pclntab[off:off+uint32(_FUNC_SIZE)], fb)
   139  		off += uint32(_FUNC_SIZE)
   140  
   141  		// NOTICE: _func.pcdata always starts from PcUnsafePoint, which is index 3
   142  		for j := 3; j < len(pcdataOffs[i]); j++ {
   143  			byteOrder.PutUint32(pclntab[off:off+4], uint32(pcdataOffs[i][j]))
   144  			off += 4
   145  		}
   146  
   147  		// funcdata refs as offsets from gofunc
   148  		for _, funcdata := range funcdataOffs[i] {
   149  			byteOrder.PutUint32(pclntab[off:off+4], uint32(funcdata))
   150  			off += 4
   151  		}
   152  
   153  	}
   154  
   155  	return
   156  }
   157  
   158  // findfunc table used to map pc to belonging func,
   159  // returns the index in the func table.
   160  //
   161  // All text section are divided into buckets sized _BUCKETSIZE(4K):
   162  //
   163  //	every bucket is divided into _SUBBUCKETS sized _SUB_BUCKETSIZE(64),
   164  //	and it has a base idx to plus the offset stored in jth subbucket.
   165  //
   166  // see findfunc() in runtime/symtab.go
   167  func writeFindfunctab(out *[]byte, ftab []funcTab) (start int) {
   168  	start = len(*out)
   169  
   170  	max := ftab[len(ftab)-1].entry
   171  	min := ftab[0].entry
   172  	nbuckets := (max - min + _BUCKETSIZE - 1) / _BUCKETSIZE
   173  	n := (max - min + _SUB_BUCKETSIZE - 1) / _SUB_BUCKETSIZE
   174  
   175  	tab := make([]findfuncbucket, 0, nbuckets)
   176  	var s, e = 0, 0
   177  	for i := 0; i < int(nbuckets); i++ {
   178  		// store the start s-th func of the bucket
   179  		var fb = findfuncbucket{idx: uint32(s)}
   180  
   181  		// find the last e-th func of the bucket
   182  		var pc = min + uint32((i+1)*_BUCKETSIZE)
   183  		for ; e < len(ftab)-1 && ftab[e+1].entry <= pc; e++ {
   184  		}
   185  
   186  		for j := 0; j < _SUBBUCKETS && (i*_SUBBUCKETS+j) < int(n); j++ {
   187  			// store the start func of the subbucket
   188  			fb._SUBBUCKETS[j] = byte(uint32(s) - fb.idx)
   189  
   190  			// find the s-th end func of the subbucket
   191  			pc = min + uint32(i*_BUCKETSIZE) + uint32((j+1)*_SUB_BUCKETSIZE)
   192  			for ; s < len(ftab)-1 && ftab[s+1].entry <= pc; s++ {
   193  			}
   194  		}
   195  
   196  		s = e
   197  		tab = append(tab, fb)
   198  	}
   199  
   200  	// write findfuncbucket
   201  	if len(tab) > 0 {
   202  		size := int(unsafe.Sizeof(findfuncbucket{})) * len(tab)
   203  		*out = append(*out, rt.BytesFrom(unsafe.Pointer(&tab[0]), size, size)...)
   204  	}
   205  	return
   206  }
   207  
   208  func makeModuledata(name string, filenames []string, funcsp *[]Func, text []byte) (mod *moduledata) {
   209  	mod = new(moduledata)
   210  	mod.modulename = name
   211  
   212  	// sort funcs by entry
   213  	funcs := *funcsp
   214  	sort.Slice(funcs, func(i, j int) bool {
   215  		return funcs[i].EntryOff < funcs[j].EntryOff
   216  	})
   217  	*funcsp = funcs
   218  
   219  	// make filename table
   220  	cu := make([]string, 0, len(filenames))
   221  	cu = append(cu, filenames...)
   222  	cutab, filetab, cuOffs := makeFilenametab([]compilationUnit{{cu}})
   223  	mod.cutab = cutab
   224  	mod.filetab = filetab
   225  
   226  	// make funcname table
   227  	funcnametab, nameOffs := makeFuncnameTab(funcs)
   228  	mod.funcnametab = funcnametab
   229  
   230  	// mmap() text and funcdata segements
   231  	p := os.Getpagesize()
   232  	size := int(rnd(int64(len(text)), int64(p)))
   233  	addr := mmap(size)
   234  	// copy the machine code
   235  	s := rt.BytesFrom(unsafe.Pointer(addr), len(text), size)
   236  	copy(s, text)
   237  	// make it executable
   238  	mprotect(addr, size)
   239  
   240  	// assign addresses
   241  	mod.text = addr
   242  	mod.etext = addr + uintptr(size)
   243  	mod.minpc = addr
   244  	mod.maxpc = addr + uintptr(len(text))
   245  
   246  	// make pcdata table
   247  	// NOTICE: _func only use offset to index pcdata, thus no need mmap() pcdata
   248  	cuOff := cuOffs[0]
   249  	pctab, pcdataOffs, _funcs := makePctab(funcs, cuOff, nameOffs)
   250  	mod.pctab = pctab
   251  
   252  	// write func data
   253  	// NOTICE: _func use mod.gofunc+offset to directly point funcdata, thus need cache funcdata
   254  	// TODO: estimate accurate capacity
   255  	cache := make([]byte, 0, len(funcs)*int(_PtrSize))
   256  	fstart, funcdataOffs := writeFuncdata(&cache, funcs)
   257  
   258  	// make pc->func (binary search) func table
   259  	ftab, pclntSize, startLocations := makeFtab(_funcs, uint32(len(text)))
   260  	mod.ftab = ftab
   261  
   262  	// write pc->func (modmap) findfunc table
   263  	ffstart := writeFindfunctab(&cache, ftab)
   264  
   265  	// cache funcdata and findfuncbucket
   266  	moduleCache.Lock()
   267  	moduleCache.m[mod] = cache
   268  	moduleCache.Unlock()
   269  	mod.gofunc = uintptr(unsafe.Pointer(&cache[fstart]))
   270  	mod.findfunctab = uintptr(unsafe.Pointer(&cache[ffstart]))
   271  
   272  	// make pclnt table
   273  	pclntab := makePclntable(pclntSize, startLocations, _funcs, uint32(len(text)), pcdataOffs, funcdataOffs)
   274  	mod.pclntable = pclntab
   275  
   276  	// make pc header
   277  	mod.pcHeader = &pcHeader{
   278  		magic:          _Magic,
   279  		minLC:          _MinLC,
   280  		ptrSize:        _PtrSize,
   281  		nfunc:          len(funcs),
   282  		nfiles:         uint(len(cu)),
   283  		textStart:      mod.text,
   284  		funcnameOffset: getOffsetOf(moduledata{}, "funcnametab"),
   285  		cuOffset:       getOffsetOf(moduledata{}, "cutab"),
   286  		filetabOffset:  getOffsetOf(moduledata{}, "filetab"),
   287  		pctabOffset:    getOffsetOf(moduledata{}, "pctab"),
   288  		pclnOffset:     getOffsetOf(moduledata{}, "pclntable"),
   289  	}
   290  
   291  	// sepecial case: gcdata and gcbss must by non-empty
   292  	mod.gcdata = uintptr(unsafe.Pointer(&emptyByte))
   293  	mod.gcbss = uintptr(unsafe.Pointer(&emptyByte))
   294  
   295  	return
   296  }
   297  
   298  // makePctab generates pcdelta->valuedelta tables for functions,
   299  // and returns the table and the entry offset of every kind pcdata in the table.
   300  func makePctab(funcs []Func, cuOffset uint32, nameOffset []int32) (pctab []byte, pcdataOffs [][]uint32, _funcs []_func) {
   301  	_funcs = make([]_func, len(funcs))
   302  
   303  	// Pctab offsets of 0 are considered invalid in the runtime. We respect
   304  	// that by just padding a single byte at the beginning of runtime.pctab,
   305  	// that way no real offsets can be zero.
   306  	pctab = make([]byte, 1, 12*len(funcs)+1)
   307  	pcdataOffs = make([][]uint32, len(funcs))
   308  
   309  	for i, f := range funcs {
   310  		_f := &_funcs[i]
   311  
   312  		var writer = func(pc *Pcdata) {
   313  			var ab []byte
   314  			var err error
   315  			if pc != nil {
   316  				ab, err = pc.MarshalBinary()
   317  				if err != nil {
   318  					panic(err)
   319  				}
   320  				pcdataOffs[i] = append(pcdataOffs[i], uint32(len(pctab)))
   321  			} else {
   322  				ab = []byte{0}
   323  				pcdataOffs[i] = append(pcdataOffs[i], _PCDATA_INVALID_OFFSET)
   324  			}
   325  			pctab = append(pctab, ab...)
   326  		}
   327  
   328  		if f.Pcsp != nil {
   329  			_f.pcsp = uint32(len(pctab))
   330  		}
   331  		writer(f.Pcsp)
   332  		if f.Pcfile != nil {
   333  			_f.pcfile = uint32(len(pctab))
   334  		}
   335  		writer(f.Pcfile)
   336  		if f.Pcline != nil {
   337  			_f.pcln = uint32(len(pctab))
   338  		}
   339  		writer(f.Pcline)
   340  		writer(f.PcUnsafePoint)
   341  		writer(f.PcStackMapIndex)
   342  		writer(f.PcInlTreeIndex)
   343  		writer(f.PcArgLiveIndex)
   344  
   345  		_f.entryOff = f.EntryOff
   346  		_f.nameOff = nameOffset[i]
   347  		_f.args = f.ArgsSize
   348  		_f.deferreturn = f.DeferReturn
   349  		// NOTICE: _func.pcdata is always as [PCDATA_UnsafePoint(0) : PCDATA_ArgLiveIndex(3)]
   350  		_f.npcdata = uint32(_N_PCDATA)
   351  		_f.cuOffset = cuOffset
   352  		_f.funcID = f.ID
   353  		_f.flag = f.Flag
   354  		_f.nfuncdata = uint8(_N_FUNCDATA)
   355  	}
   356  
   357  	return
   358  }
   359  
   360  func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argptrs uintptr, localptrs uintptr) {
   361  }