github.com/primecitizens/pcz/std@v0.2.1/core/abi/moduledata.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright 2023 The Prime Citizens 3 4 package abi 5 6 import ( 7 "unsafe" 8 _ "unsafe" // for go:linkname 9 10 stdstring "github.com/primecitizens/pcz/std/builtin/string" 11 "github.com/primecitizens/pcz/std/core/arch" 12 "github.com/primecitizens/pcz/std/core/assert" 13 "github.com/primecitizens/pcz/std/core/mark" 14 ) 15 16 var ( 17 //go:linkname firstmoduledata runtime.firstmoduledata 18 firstmoduledata ModuleData // linker symbol 19 20 //go:linkname lastmoduledatap runtime.lastmoduledatap 21 lastmoduledatap *ModuleData // linker symbol 22 ) 23 24 // ModuleIter is an iterator for active modules. 25 type ModuleIter uintptr 26 27 func (iter *ModuleIter) Nth(i int) (md *ModuleData, ok bool) { 28 if i == 0 { 29 *iter = ModuleIter(uintptr(unsafe.Pointer(&firstmoduledata))) 30 } 31 32 for md := (*ModuleData)(unsafe.Pointer(uintptr(*iter))); md != nil; md = md.Next { 33 if md.Bad { 34 continue 35 } 36 37 *iter = ModuleIter(uintptr(unsafe.Pointer(md.Next))) 38 return md, true 39 } 40 41 *iter = 0 42 return nil, false 43 } 44 45 func findModuleForType(ptrInModule uintptr) (md *ModuleData) { 46 for i, iter := 0, ModuleIter(0); ; i++ { 47 md, ok := iter.Nth(i) 48 if !ok { 49 break 50 } 51 52 if ptrInModule >= md.Types && ptrInModule < md.ETypes { 53 return md 54 } 55 } 56 57 return nil 58 } 59 60 func resolveNameOff(ptrInModule unsafe.Pointer, off NameOff) Name { 61 if off == 0 { 62 return Name{} 63 } 64 65 md := findModuleForType(uintptr(ptrInModule)) 66 if md == nil { 67 // TODO: support runtime name 68 // // No module found. see if it is a run time name. 69 // reflectOffsLock() 70 // res, found := reflectOffs.m[int32(off)] 71 // reflectOffsUnlock() 72 // if !found { 73 // println("runtime: nameOff", hex(off), "base", hex(base), "not in ranges:") 74 // for next := &firstmoduledata; next != nil; next = next.next { 75 // println("\ttypes", hex(next.types), "etypes", hex(next.etypes)) 76 // } 77 // throw("runtime: name offset base pointer out of range") 78 // } 79 // 80 // return Name{(*byte)(res)} 81 return Name{} 82 } 83 84 res := md.Types + uintptr(off) 85 if res > md.ETypes { 86 println("runtime: nameOff", hex(off), "out of range", hex(md.Types), "-", hex(md.ETypes)) 87 assert.Throw("runtime:", "name", "offset", "out", "of", "range") 88 } 89 90 return Name{Bytes: (*byte)(unsafe.Pointer(res))} 91 } 92 93 // resolveTypeOff resolves an *rtype offset from a base type. 94 // The (*rtype).typeOff method is a convenience wrapper for this function. 95 // Implemented in the runtime package. 96 func resolveTypeOff(ptrInModule unsafe.Pointer, off TypeOff) *Type { 97 if off == 0 || off == -1 { 98 // -1 is the sentinel value for unreachable code. 99 // See cmd/link/internal/ld/data.go:relocsym. 100 return nil 101 } 102 103 md := findModuleForType(uintptr(ptrInModule)) 104 if md == nil { 105 // TODO: support runtime name 106 // reflectOffsLock() 107 // res := reflectOffs.m[int32(off)] 108 // reflectOffsUnlock() 109 // if res == nil { 110 // println("runtime: typeOff", hex(off), "base", hex(base), "not in ranges:") 111 // for next := &firstmoduledata; next != nil; next = next.next { 112 // println("\ttypes", hex(next.types), "etypes", hex(next.etypes)) 113 // } 114 // throw("runtime: type offset base pointer out of range") 115 // } 116 // return (*_type)(res) 117 return nil 118 } 119 120 // TODO: initialize md.typemap (see $GOROOT/src/runtime/type.go#func.typelinksinit) 121 // if t := md.typemap[off]; t != nil { 122 // return t 123 // } 124 125 res := md.Types + uintptr(off) 126 if res > md.ETypes { 127 println("runtime: typeOff", hex(off), "out of range", hex(md.Types), "-", hex(md.ETypes)) 128 assert.Throw("runtime:", "type", "offset", "out", "of", "range") 129 } 130 return (*Type)(unsafe.Pointer(res)) 131 } 132 133 type hex = uint64 134 135 //go:linkname unreachableMethod runtime.unreachableMethod 136 func unreachableMethod() 137 138 // resolveTextOff resolves a function pointer offset from a base type. 139 // The (*rtype).textOff method is a convenience wrapper for this function. 140 // Implemented in the runtime package. 141 func resolveTextOff(rtype unsafe.Pointer, off TextOff) unsafe.Pointer { 142 if off == -1 { 143 // -1 is the sentinel value for unreachable code. 144 // See cmd/link/internal/ld/data.go:relocsym. 145 return unsafe.Pointer(FuncPCABIInternal(unreachableMethod)) 146 } 147 148 md := findModuleForType(uintptr(rtype)) 149 if md == nil { 150 // TODO: support runtime type. 151 // reflectOffsLock() 152 // res := reflectOffs.m[int32(off)] 153 // reflectOffsUnlock() 154 // if res == nil { 155 // println("runtime: textOff", hex(off), "base", hex(base), "not in ranges:") 156 // for next := &firstmoduledata; next != nil; next = next.next { 157 // println("\ttypes", hex(next.types), "etypes", hex(next.etypes)) 158 // } 159 // throw("runtime: text offset base pointer out of range") 160 // } 161 // return res 162 163 assert.Throw("runtime:", "text", "offset", "base", "pointer", "out", "of", "range") 164 } 165 166 res := md.textAddr(uint32(off)) 167 return unsafe.Pointer(res) 168 } 169 170 type GetItabResult uint8 171 172 const ( 173 GetItabResult_OK GetItabResult = iota 174 GetItabResult_UncommonType 175 GetItabResult_ 176 ) 177 178 func GetItab(inter *InterfaceType, typ *Type) (*Itab, GetItabResult) { 179 if len(inter.Methods) == 0 { 180 assert.Throw("internal", "error:", "misuse", "of", "itab") 181 } 182 183 // easy case 184 if typ.TFlag&TFlagUncommon == 0 { 185 return nil, GetItabResult_UncommonType 186 } 187 188 var m *Itab 189 // // First, look in the existing table to see if we can find the itab we need. 190 // // This is by far the most common case, so do it without locks. 191 // // Use atomic to ensure we see any previous writes done by the thread 192 // // that updates the itabTable field (with atomic.Storep in itabAdd). 193 // t := (*itabTableType)(atomic.Loadp(unsafe.Pointer(&itabTable))) 194 // if m = t.find(inter, typ); m != nil { 195 // goto finish 196 // } 197 // 198 // // Not found. Grab the lock and try again. 199 // lock(&itabLock) 200 // if m = itabTable.find(inter, typ); m != nil { 201 // unlock(&itabLock) 202 // goto finish 203 // } 204 // 205 // // Entry doesn't exist yet. Make a new entry & add it. 206 // m = (*itab)(persistentalloc(unsafe.Sizeof(itab{})+uintptr(len(inter.Methods)-1)*goarch.PtrSize, 0, &memstats.other_sys)) 207 // m.inter = inter 208 // m._type = typ 209 // // The hash is used in type switches. However, compiler statically generates itab's 210 // // for all interface/type pairs used in switches (which are added to itabTable 211 // // in itabsinit). The dynamically-generated itab's never participate in type switches, 212 // // and thus the hash is irrelevant. 213 // // Note: m.hash is _not_ the hash used for the runtime itabTable hash table. 214 // m.hash = 0 215 // m.init() 216 // itabAdd(m) 217 // unlock(&itabLock) 218 // finish: 219 if m.Fun[0] != 0 { 220 return m, GetItabResult_OK 221 } 222 223 // this can only happen if the conversion 224 // was already done once using the , ok form 225 // and we have a cached negative result. 226 // The cached result doesn't record which 227 // interface function was missing, so initialize 228 // the itab again to get the missing function name. 229 return m, GetItabResult_ 230 } 231 232 // ModuleData records information about the layout of the executable 233 // image. It is written by the linker. Any changes here must be 234 // matched changes to the code in cmd/link/internal/ld/symtab.go:symtab. 235 // moduledata is stored in statically allocated non-pointer memory; 236 // none of the pointers here are visible to the garbage collector. 237 // 238 // See $GOROOT/src/runtime/symtab.go#type:moduledata 239 type ModuleData struct { 240 _ mark.NotInHeap // Only in static data 241 242 PCHeader *PCHeader 243 FuncNameTab []byte 244 CuTab []uint32 245 FileTab []byte 246 PCTab []byte 247 PCLnTable []byte 248 FTab []FuncTab 249 FindFuncTab uintptr 250 MinPC, MaxPC uintptr 251 252 Text, Etext uintptr 253 NOPTRdata, ENOPTRdata uintptr 254 Data, Edata uintptr 255 Bss, Ebss uintptr 256 NOPTRbss, ENOPTRbss uintptr 257 CovCtrs, ECovCtrs uintptr // since go1.20 258 End, GCdata, GCbss uintptr 259 Types, ETypes uintptr 260 ROData uintptr 261 GoFunc uintptr // go.func.* 262 263 TextSectMap []TextSect 264 265 // .Types + .TypeLinks[i] = uintptr(unsafe.Pointer(*Type)) 266 // 267 // The linker will leave a table of all the typelinks for 268 // types in the binary, so the runtime can find them. 269 // 270 // When buildmode=shared, all types are in typelinks so the 271 // runtime can deduplicate type pointers. 272 // 273 // Ref: ${GOROOT}/src/cmd/compile/internal/reflectdata/reflect.go#func:writeType 274 TypeLinks []int32 // offsets from types 275 ITabLinks []*Itab 276 277 PTab []PTabEntry 278 279 PluginPath string 280 PkgHashes []ModuleHash 281 282 // This slice records the initializing tasks that need to be 283 // done to start up the program. It is built by the linker. 284 InitTasks []*InitTask // since go1.21 285 286 ModuleName string 287 ModuleHashes []ModuleHash 288 289 HasMain uint8 // 1 if module contains the main function, 0 otherwise 290 291 GCdataMask, GCbssMask BitVector 292 293 TypeMap map[TypeOff]*Type // offset to *_rtype in previous module 294 295 Bad bool // module failed to load and should be ignored 296 297 Next *ModuleData 298 } 299 300 // funcName returns the string at nameOff in the function name table. 301 func (md *ModuleData) funcName(off NameOff) string { 302 if off == 0 { 303 return "" 304 } 305 306 return stdstring.FromByteArray(&md.FuncNameTab[off]) 307 } 308 309 // textAddr returns md.text + off, with special handling for multiple text sections. 310 // off is a (virtual) offset computed at internal linking time, 311 // before the external linker adjusts the sections' base addresses. 312 // 313 // The text, or instruction stream is generated as one large buffer. 314 // The off (offset) for a function is its offset within this buffer. 315 // If the total text size gets too large, there can be issues on platforms like ppc64 316 // if the target of calls are too far for the call instruction. 317 // To resolve the large text issue, the text is split into multiple text sections 318 // to allow the linker to generate long calls when necessary. 319 // When this happens, the vaddr for each text section is set to its offset within the text. 320 // Each function's offset is compared against the section vaddrs and ends to determine the containing section. 321 // Then the section relative offset is added to the section's 322 // relocated baseaddr to compute the function address. 323 // 324 // It is nosplit because it is part of the findfunc implementation. 325 // 326 //go:nosplit 327 func (md *ModuleData) textAddr(off32 uint32) uintptr { 328 off := uintptr(off32) 329 res := md.Text + off 330 if len(md.TextSectMap) > 1 { 331 for i, sect := range md.TextSectMap { 332 // For the last section, include the end address (etext), as it is included in the functab. 333 if off >= sect.VAddr && off < sect.End || (i == len(md.TextSectMap)-1 && off == sect.End) { 334 res = sect.BaseAddr + off - sect.VAddr 335 break 336 } 337 } 338 if res > md.Etext && arch.IsWasm != 0 { // on wasm, functions do not live in the same address space as the linear memory 339 println("runtime: textAddr", hex(res), "out of range", hex(md.Text), "-", hex(md.Etext)) 340 assert.Throw("runtime:", "text", "offset", "out", "of", "range") 341 } 342 } 343 344 return res 345 } 346 347 // textOff is the opposite of textAddr. It converts a PC to a (virtual) offset 348 // to md.text, and returns if the PC is in any Go text section. 349 // 350 // It is nosplit because it is part of the findfunc implementation. 351 // 352 //go:nosplit 353 func (md *ModuleData) textOff(pc uintptr) (uint32, bool) { 354 res := uint32(pc - md.Text) 355 if len(md.TextSectMap) > 1 { 356 for i, sect := range md.TextSectMap { 357 if sect.BaseAddr > pc { 358 // pc is not in any section. 359 return 0, false 360 } 361 end := sect.BaseAddr + (sect.End - sect.VAddr) 362 // For the last section, include the end address (etext), as it is included in the functab. 363 if i == len(md.TextSectMap) { 364 end++ 365 } 366 if pc < end { 367 res = uint32(pc - sect.BaseAddr + sect.VAddr) 368 break 369 } 370 } 371 } 372 return res, true 373 } 374 375 // PCHeader holds data used by the pclntab lookups. 376 type PCHeader struct { 377 // 0xfffffff1 (go1.20), 0xfffffff0 (go1.18) 0xfffffffa (go1.16) 0xfffffffb (go1.2) 378 Magic uint32 379 pad1, pad2 uint8 // 0,0 380 MinLC uint8 // min instruction size 381 PtrSize uint8 // size of a ptr in bytes 382 NFunc int // number of functions in the module 383 NFiles uint // number of entries in the file tab 384 TextStart uintptr // base for function entry PC offsets in this module, equal to moduledata.text 385 FuncNameOffset uintptr // offset to the funcnametab variable from pcHeader 386 CuOffset uintptr // offset to the cutab variable from pcHeader 387 FileTabOffset uintptr // offset to the filetab variable from pcHeader 388 PCTabOffset uintptr // offset to the pctab variable from pcHeader 389 PCLnOffset uintptr // offset to the pclntab variable from pcHeader 390 } 391 392 type FuncTab struct { 393 Entryoff uint32 // relative to runtime.text 394 Funcoff uint32 395 } 396 397 // Mapping information for secondary text sections 398 type TextSect struct { 399 VAddr uintptr // prelinked section vaddr 400 End uintptr // vaddr + section length 401 BaseAddr uintptr // relocated section address 402 } 403 404 // A PTabEntry is generated by the compiler for each exported function 405 // and global variable in the main package of a plugin. It is used to 406 // initialize the plugin module's symbol map. 407 type PTabEntry struct { 408 Name NameOff 409 Type TypeOff 410 } 411 412 const minfunc = 16 // minimum function size 413 const pcbucketsize = 256 * minfunc // size of bucket in the pc->func lookup table 414 415 // A ModuleHash is used to compare the ABI of a new module or a 416 // package in a new module with the loaded program. 417 // 418 // For each shared library a module links against, the linker creates an entry in the 419 // moduledata.modulehashes slice containing the name of the module, the abi hash seen 420 // at link time and a pointer to the runtime abi hash. These are checked in 421 // moduledataverify1 below. 422 // 423 // For each loaded plugin, the pkghashes slice has a ModuleHash of the 424 // newly loaded package that can be used to check the plugin's version of 425 // a package against any previously loaded version of the package. 426 // This is done in plugin.lastmoduleinit. 427 type ModuleHash struct { 428 ModuleName string 429 LinktimeHash string 430 RuntimeHash *string 431 } 432 433 // Information from the compiler about the layout of stack frames. 434 // Note: this type must agree with reflect.bitVector. 435 type BitVector struct { 436 N int32 // # of bits 437 ByteData *uint8 438 } 439 440 // layout of Itab known to compilers 441 // allocated in non-garbage-collected memory 442 // Needs to be in sync with 443 // ../cmd/compile/internal/reflectdata/reflect.go:/^func.WriteTabs. 444 type Itab struct { 445 Inter *InterfaceType 446 Type *Type 447 Hash uint32 // copy of _type.hash. Used for type switches. 448 _ [4]byte 449 450 // actual length of Fun = len(.Inter.Methods) 451 // fun[0]==0 means _type does not implement inter. 452 Fun [1]uintptr 453 } 454 455 func (m *Itab) FuncPCs() []unsafe.Pointer { 456 return unsafe.Slice((*unsafe.Pointer)(unsafe.Pointer(&m.Fun[0])), len(m.Inter.Methods)) 457 } 458 459 // init fills in the m.fun array with all the code pointers for 460 // the m.inter/m._type pair. If the type does not implement the interface, 461 // it sets m.fun[0] to 0 and returns the name of an interface function that is missing. 462 // It is ok to call this multiple times on the same m, even concurrently. 463 func (m *Itab) Init() string { 464 inter := m.Inter 465 typ := m.Type 466 x := typ.Uncommon() 467 468 // both inter and typ have method sorted by name, 469 // and interface names are unique, 470 // so can iterate over both in lock step; 471 // the loop is O(ni+nt) not O(ni*nt). 472 ni := len(inter.Methods) 473 nt := int(x.Mcount) 474 xmhdr := unsafe.Slice((*Method)(unsafe.Add(unsafe.Pointer(x), uintptr(x.Moff))), nt) 475 j := 0 476 methods := unsafe.Slice((*unsafe.Pointer)(unsafe.Pointer(&m.Fun[0])), ni) 477 var fun0 unsafe.Pointer 478 imethods: 479 for k := 0; k < ni; k++ { 480 i := &inter.Methods[k] 481 itype := inter.Type.TypeOff(i.Typ) 482 name := inter.Type.NameOff(i.Name) 483 iname := name.Name() 484 ipkg := pkgPath(name) 485 if len(ipkg) == 0 { 486 ipkg = inter.PkgPath.Name() 487 } 488 for ; j < nt; j++ { 489 t := &xmhdr[j] 490 tname := typ.NameOff(t.Name) 491 if typ.TypeOff(t.Mtyp) == itype && tname.Name() == iname { 492 pkgPath := pkgPath(tname) 493 if len(pkgPath) == 0 { 494 pkgPath = typ.NameOff(x.PkgPath).Name() 495 } 496 if tname.IsExported() || pkgPath == ipkg { 497 if m != nil { 498 ifn := typ.TextOff(t.Ifn) 499 if k == 0 { 500 fun0 = ifn // we'll set m.fun[0] at the end 501 } else { 502 methods[k] = ifn 503 } 504 } 505 continue imethods 506 } 507 } 508 } 509 // didn't find method 510 m.Fun[0] = 0 511 return iname 512 } 513 m.Fun[0] = uintptr(fun0) 514 return "" 515 } 516 517 func pkgPath(n Name) string { 518 if n.Bytes == nil || *n.Data(0)&(1<<2) == 0 { 519 return "" 520 } 521 i, l := n.ReadVarint(1) 522 off := 1 + i + l 523 if *n.Data(0)&(1<<1) != 0 { 524 i2, l2 := n.ReadVarint(off) 525 off += i2 + l2 526 } 527 var nameOff NameOff 528 copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.Data(off)))[:]) 529 pkgPathName := resolveNameOff(unsafe.Pointer(n.Bytes), nameOff) 530 return pkgPathName.Name() 531 } 532 533 // An InitTask represents the set of initializations that need to be done for a package. 534 // Keep in sync with ../../test/noinit.go:InitTask 535 // 536 // see $GOROOT/src/runtime/proc.go#type:initTask 537 type InitTask struct { 538 State uint32 // 0 = uninitialized, 1 = in progress, 2 = done 539 NFns uint32 540 // followed by nfns pcs, uintptr sized, one per init function to run 541 _fns [0]uintptr // len(_fns) = NFns 542 } 543 544 // Nth returns the i-th init() function in the InitTask. 545 // 546 // i MUST be in range [0, tsk.NFns). 547 func (tsk *InitTask) Nth(i uint32) func() { 548 p := unsafe.Add( 549 unsafe.Pointer(tsk), unsafe.Offsetof(tsk._fns)+uintptr(i)*arch.PtrSize, 550 ) 551 return *(*func())(unsafe.Pointer(&p)) 552 }