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