...

Source file src/github.com/bytedance/sonic/loader/funcdata_go116.go

Documentation: github.com/bytedance/sonic/loader

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

View as plain text