github.com/goshafaq/sonic@v0.0.0-20231026082336-871835fb94c6/loader/funcdata_go116.go (about) 1 //go:build go1.16 && !go1.18 2 // +build go1.16,!go1.18 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 195 return 196 } 197 198 // Pcln table format: [...]funcTab + [...]_Func 199 func makePclntable(size int64, startLocations []uint32, funcs []_func, maxpc uintptr, pcdataOffs [][]uint32, funcdataAddr uintptr, funcdataOffs [][]uint32) (pclntab []byte) { 200 pclntab = make([]byte, size, size) 201 202 // write a map of pc->func info offsets 203 offs := 0 204 for i, f := range funcs { 205 byteOrder.PutUint64(pclntab[offs:offs+8], uint64(f.entry)) 206 byteOrder.PutUint64(pclntab[offs+8:offs+16], uint64(startLocations[i])) 207 offs += 16 208 } 209 // Final entry of table is just end pc offset. 210 byteOrder.PutUint64(pclntab[offs:offs+8], uint64(maxpc)) 211 offs += 8 212 213 // write func info table 214 for i, f := range funcs { 215 off := startLocations[i] 216 217 // write _func structure to pclntab 218 byteOrder.PutUint64(pclntab[off:off+8], uint64(f.entry)) 219 off += 8 220 byteOrder.PutUint32(pclntab[off:off+4], uint32(f.nameOff)) 221 off += 4 222 byteOrder.PutUint32(pclntab[off:off+4], uint32(f.args)) 223 off += 4 224 byteOrder.PutUint32(pclntab[off:off+4], uint32(f.deferreturn)) 225 off += 4 226 byteOrder.PutUint32(pclntab[off:off+4], uint32(f.pcsp)) 227 off += 4 228 byteOrder.PutUint32(pclntab[off:off+4], uint32(f.pcfile)) 229 off += 4 230 byteOrder.PutUint32(pclntab[off:off+4], uint32(f.pcln)) 231 off += 4 232 byteOrder.PutUint32(pclntab[off:off+4], uint32(f.npcdata)) 233 off += 4 234 byteOrder.PutUint32(pclntab[off:off+4], uint32(f.cuOffset)) 235 off += 4 236 pclntab[off] = f.funcID 237 // NOTICE: _[2]byte alignment 238 off += 3 239 pclntab[off] = f.nfuncdata 240 off += 1 241 242 // NOTICE: _func.pcdata always starts from PcUnsafePoint, which is index 3 243 for j := 3; j < len(pcdataOffs[i]); j++ { 244 byteOrder.PutUint32(pclntab[off:off+4], uint32(pcdataOffs[i][j])) 245 off += 4 246 } 247 248 off = uint32(rnd(int64(off), int64(_PtrSize))) 249 250 // funcdata refs as offsets from gofunc 251 for _, funcdata := range funcdataOffs[i] { 252 if funcdata == _INVALID_FUNCDATA_OFFSET { 253 byteOrder.PutUint64(pclntab[off:off+8], 0) 254 } else { 255 byteOrder.PutUint64(pclntab[off:off+8], uint64(funcdataAddr)+uint64(funcdata)) 256 } 257 off += 8 258 } 259 } 260 261 return 262 } 263 264 // findfunc table used to map pc to belonging func, 265 // returns the index in the func table. 266 // 267 // All text section are divided into buckets sized _BUCKETSIZE(4K): 268 // 269 // every bucket is divided into _SUBBUCKETS sized _SUB_BUCKETSIZE(64), 270 // and it has a base idx to plus the offset stored in jth subbucket. 271 // 272 // see findfunc() in runtime/symtab.go 273 func writeFindfunctab(out *[]byte, ftab []funcTab) (start int) { 274 start = len(*out) 275 276 max := ftab[len(ftab)-1].entry 277 min := ftab[0].entry 278 nbuckets := (max - min + _BUCKETSIZE - 1) / _BUCKETSIZE 279 n := (max - min + _SUB_BUCKETSIZE - 1) / _SUB_BUCKETSIZE 280 281 tab := make([]findfuncbucket, 0, nbuckets) 282 var s, e = 0, 0 283 for i := 0; i < int(nbuckets); i++ { 284 // store the start func of the bucket 285 var fb = findfuncbucket{idx: uint32(s)} 286 287 // find the last e-th func of the bucket 288 var pc = min + uintptr((i+1)*_BUCKETSIZE) 289 for ; e < len(ftab)-1 && ftab[e+1].entry <= pc; e++ { 290 } 291 292 for j := 0; j < _SUBBUCKETS && (i*_SUBBUCKETS+j) < int(n); j++ { 293 // store the start func of the subbucket 294 fb._SUBBUCKETS[j] = byte(uint32(s) - fb.idx) 295 296 // find the s-th end func of the subbucket 297 pc = min + uintptr(i*_BUCKETSIZE) + uintptr((j+1)*_SUB_BUCKETSIZE) 298 for ; s < len(ftab)-1 && ftab[s+1].entry <= pc; s++ { 299 } 300 } 301 302 s = e 303 tab = append(tab, fb) 304 } 305 306 // write findfuncbucket 307 if len(tab) > 0 { 308 size := int(unsafe.Sizeof(findfuncbucket{})) * len(tab) 309 *out = append(*out, rt.BytesFrom(unsafe.Pointer(&tab[0]), size, size)...) 310 } 311 return 312 } 313 314 func makeModuledata(name string, filenames []string, funcsp *[]Func, text []byte) (mod *moduledata) { 315 mod = new(moduledata) 316 mod.modulename = name 317 318 // sort funcs by entry 319 funcs := *funcsp 320 sort.Slice(funcs, func(i, j int) bool { 321 return funcs[i].EntryOff < funcs[j].EntryOff 322 }) 323 *funcsp = funcs 324 325 // make filename table 326 cu := make([]string, 0, len(filenames)) 327 cu = append(cu, filenames...) 328 cutab, filetab, cuOffs := makeFilenametab([]compilationUnit{{cu}}) 329 mod.cutab = cutab 330 mod.filetab = filetab 331 332 // make funcname table 333 funcnametab, nameOffs := makeFuncnameTab(funcs) 334 mod.funcnametab = funcnametab 335 336 // mmap() text and funcdata segements 337 p := os.Getpagesize() 338 size := int(rnd(int64(len(text)), int64(p))) 339 addr := mmap(size) 340 // copy the machine code 341 s := rt.BytesFrom(unsafe.Pointer(addr), len(text), size) 342 copy(s, text) 343 // make it executable 344 mprotect(addr, size) 345 346 // assign addresses 347 mod.text = addr 348 mod.etext = addr + uintptr(size) 349 mod.minpc = addr 350 mod.maxpc = addr + uintptr(len(text)) 351 352 // make pcdata table 353 // NOTICE: _func only use offset to index pcdata, thus no need mmap() pcdata 354 cuOff := cuOffs[0] 355 pctab, pcdataOffs, _funcs := makePctab(funcs, addr, cuOff, nameOffs) 356 mod.pctab = pctab 357 358 // write func data 359 // NOTICE: _func use mod.gofunc+offset to directly point funcdata, thus need cache funcdata 360 // TODO: estimate accurate capacity 361 cache := make([]byte, 0, len(funcs)*int(_PtrSize)) 362 fstart, funcdataOffs := writeFuncdata(&cache, funcs) 363 364 // make pc->func (binary search) func table 365 ftab, pclntSize, startLocations := makeFtab(_funcs, mod.maxpc) 366 mod.ftab = ftab 367 368 // write pc->func (modmap) findfunc table 369 ffstart := writeFindfunctab(&cache, ftab) 370 371 // cache funcdata and findfuncbucket 372 moduleCache.Lock() 373 moduleCache.m[mod] = cache 374 moduleCache.Unlock() 375 mod.findfunctab = uintptr(rt.IndexByte(cache, ffstart)) 376 funcdataAddr := uintptr(rt.IndexByte(cache, fstart)) 377 378 // make pclnt table 379 pclntab := makePclntable(pclntSize, startLocations, _funcs, mod.maxpc, pcdataOffs, funcdataAddr, funcdataOffs) 380 mod.pclntable = pclntab 381 382 // make pc header 383 mod.pcHeader = &pcHeader{ 384 magic: _Magic, 385 minLC: _MinLC, 386 ptrSize: _PtrSize, 387 nfunc: len(funcs), 388 nfiles: uint(len(cu)), 389 funcnameOffset: getOffsetOf(moduledata{}, "funcnametab"), 390 cuOffset: getOffsetOf(moduledata{}, "cutab"), 391 filetabOffset: getOffsetOf(moduledata{}, "filetab"), 392 pctabOffset: getOffsetOf(moduledata{}, "pctab"), 393 pclnOffset: getOffsetOf(moduledata{}, "pclntable"), 394 } 395 396 // sepecial case: gcdata and gcbss must by non-empty 397 mod.gcdata = uintptr(unsafe.Pointer(&emptyByte)) 398 mod.gcbss = uintptr(unsafe.Pointer(&emptyByte)) 399 400 return 401 } 402 403 // makePctab generates pcdelta->valuedelta tables for functions, 404 // and returns the table and the entry offset of every kind pcdata in the table. 405 func makePctab(funcs []Func, addr uintptr, cuOffset uint32, nameOffset []int32) (pctab []byte, pcdataOffs [][]uint32, _funcs []_func) { 406 _funcs = make([]_func, len(funcs)) 407 408 // Pctab offsets of 0 are considered invalid in the runtime. We respect 409 // that by just padding a single byte at the beginning of runtime.pctab, 410 // that way no real offsets can be zero. 411 pctab = make([]byte, 1, 12*len(funcs)+1) 412 pcdataOffs = make([][]uint32, len(funcs)) 413 414 for i, f := range funcs { 415 _f := &_funcs[i] 416 417 var writer = func(pc *Pcdata) { 418 var ab []byte 419 var err error 420 if pc != nil { 421 ab, err = pc.MarshalBinary() 422 if err != nil { 423 panic(err) 424 } 425 pcdataOffs[i] = append(pcdataOffs[i], uint32(len(pctab))) 426 } else { 427 ab = []byte{0} 428 pcdataOffs[i] = append(pcdataOffs[i], _PCDATA_INVALID_OFFSET) 429 } 430 pctab = append(pctab, ab...) 431 } 432 433 if f.Pcsp != nil { 434 _f.pcsp = uint32(len(pctab)) 435 } 436 writer(f.Pcsp) 437 if f.Pcfile != nil { 438 _f.pcfile = uint32(len(pctab)) 439 } 440 writer(f.Pcfile) 441 if f.Pcline != nil { 442 _f.pcln = uint32(len(pctab)) 443 } 444 writer(f.Pcline) 445 writer(f.PcUnsafePoint) 446 writer(f.PcStackMapIndex) 447 writer(f.PcInlTreeIndex) 448 writer(f.PcArgLiveIndex) 449 450 _f.entry = addr + uintptr(f.EntryOff) 451 _f.nameOff = nameOffset[i] 452 _f.args = f.ArgsSize 453 _f.deferreturn = f.DeferReturn 454 // NOTICE: _func.pcdata is always as [PCDATA_UnsafePoint(0) : PCDATA_ArgLiveIndex(3)] 455 _f.npcdata = uint32(_N_PCDATA) 456 _f.cuOffset = cuOffset 457 _f.funcID = f.ID 458 _f.nfuncdata = uint8(_N_FUNCDATA) 459 } 460 461 return 462 } 463 464 func registerFunction(name string, pc uintptr, textSize uintptr, fp int, args int, size uintptr, argptrs uintptr, localptrs uintptr) { 465 }