...

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

Documentation: github.com/bytedance/sonic/loader

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

View as plain text