funcdata_latest.go 12 KB

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