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