funcdata_latest.go raw

   1  // go:build go1.18 && !go1.25
   2  // +build go1.18,!go1.25
   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/loader/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 segments
 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      // special 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