funcdata_compat.go 15 KB

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