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