1// Copyright 2009 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// Runtime type representation. 6 7package runtime 8 9import ( 10 "internal/abi" 11 "unsafe" 12) 13 14type nameOff = abi.NameOff 15type typeOff = abi.TypeOff 16type textOff = abi.TextOff 17 18type _type = abi.Type 19 20// rtype is a wrapper that allows us to define additional methods. 21type rtype struct { 22 *abi.Type // embedding is okay here (unlike reflect) because none of this is public 23} 24 25func (t rtype) string() string { 26 s := t.nameOff(t.Str).Name() 27 if t.TFlag&abi.TFlagExtraStar != 0 { 28 return s[1:] 29 } 30 return s 31} 32 33func (t rtype) uncommon() *uncommontype { 34 return t.Uncommon() 35} 36 37func (t rtype) name() string { 38 if t.TFlag&abi.TFlagNamed == 0 { 39 return "" 40 } 41 s := t.string() 42 i := len(s) - 1 43 sqBrackets := 0 44 for i >= 0 && (s[i] != '.' || sqBrackets != 0) { 45 switch s[i] { 46 case ']': 47 sqBrackets++ 48 case '[': 49 sqBrackets-- 50 } 51 i-- 52 } 53 return s[i+1:] 54} 55 56// pkgpath returns the path of the package where t was defined, if 57// available. This is not the same as the reflect package's PkgPath 58// method, in that it returns the package path for struct and interface 59// types, not just named types. 60func (t rtype) pkgpath() string { 61 if u := t.uncommon(); u != nil { 62 return t.nameOff(u.PkgPath).Name() 63 } 64 switch t.Kind_ & abi.KindMask { 65 case abi.Struct: 66 st := (*structtype)(unsafe.Pointer(t.Type)) 67 return st.PkgPath.Name() 68 case abi.Interface: 69 it := (*interfacetype)(unsafe.Pointer(t.Type)) 70 return it.PkgPath.Name() 71 } 72 return "" 73} 74 75// reflectOffs holds type offsets defined at run time by the reflect package. 76// 77// When a type is defined at run time, its *rtype data lives on the heap. 78// There are a wide range of possible addresses the heap may use, that 79// may not be representable as a 32-bit offset. Moreover the GC may 80// one day start moving heap memory, in which case there is no stable 81// offset that can be defined. 82// 83// To provide stable offsets, we add pin *rtype objects in a global map 84// and treat the offset as an identifier. We use negative offsets that 85// do not overlap with any compile-time module offsets. 86// 87// Entries are created by reflect.addReflectOff. 88var reflectOffs struct { 89 lock mutex 90 next int32 91 m map[int32]unsafe.Pointer 92 minv map[unsafe.Pointer]int32 93} 94 95func reflectOffsLock() { 96 lock(&reflectOffs.lock) 97 if raceenabled { 98 raceacquire(unsafe.Pointer(&reflectOffs.lock)) 99 } 100} 101 102func reflectOffsUnlock() { 103 if raceenabled { 104 racerelease(unsafe.Pointer(&reflectOffs.lock)) 105 } 106 unlock(&reflectOffs.lock) 107} 108 109// resolveNameOff should be an internal detail, 110// but widely used packages access it using linkname. 111// Notable members of the hall of shame include: 112// - github.com/cloudwego/frugal 113// 114// Do not remove or change the type signature. 115// See go.dev/issue/67401. 116// 117//go:linkname resolveNameOff 118func resolveNameOff(ptrInModule unsafe.Pointer, off nameOff) name { 119 if off == 0 { 120 return name{} 121 } 122 base := uintptr(ptrInModule) 123 for md := &firstmoduledata; md != nil; md = md.next { 124 if base >= md.types && base < md.etypes { 125 res := md.types + uintptr(off) 126 if res > md.etypes { 127 println("runtime: nameOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes)) 128 throw("runtime: name offset out of range") 129 } 130 return name{Bytes: (*byte)(unsafe.Pointer(res))} 131 } 132 } 133 134 // No module found. see if it is a run time name. 135 reflectOffsLock() 136 res, found := reflectOffs.m[int32(off)] 137 reflectOffsUnlock() 138 if !found { 139 println("runtime: nameOff", hex(off), "base", hex(base), "not in ranges:") 140 for next := &firstmoduledata; next != nil; next = next.next { 141 println("\ttypes", hex(next.types), "etypes", hex(next.etypes)) 142 } 143 throw("runtime: name offset base pointer out of range") 144 } 145 return name{Bytes: (*byte)(res)} 146} 147 148func (t rtype) nameOff(off nameOff) name { 149 return resolveNameOff(unsafe.Pointer(t.Type), off) 150} 151 152// resolveTypeOff should be an internal detail, 153// but widely used packages access it using linkname. 154// Notable members of the hall of shame include: 155// - github.com/cloudwego/frugal 156// 157// Do not remove or change the type signature. 158// See go.dev/issue/67401. 159// 160//go:linkname resolveTypeOff 161func resolveTypeOff(ptrInModule unsafe.Pointer, off typeOff) *_type { 162 if off == 0 || off == -1 { 163 // -1 is the sentinel value for unreachable code. 164 // See cmd/link/internal/ld/data.go:relocsym. 165 return nil 166 } 167 base := uintptr(ptrInModule) 168 var md *moduledata 169 for next := &firstmoduledata; next != nil; next = next.next { 170 if base >= next.types && base < next.etypes { 171 md = next 172 break 173 } 174 } 175 if md == nil { 176 reflectOffsLock() 177 res := reflectOffs.m[int32(off)] 178 reflectOffsUnlock() 179 if res == nil { 180 println("runtime: typeOff", hex(off), "base", hex(base), "not in ranges:") 181 for next := &firstmoduledata; next != nil; next = next.next { 182 println("\ttypes", hex(next.types), "etypes", hex(next.etypes)) 183 } 184 throw("runtime: type offset base pointer out of range") 185 } 186 return (*_type)(res) 187 } 188 if t := md.typemap[off]; t != nil { 189 return t 190 } 191 res := md.types + uintptr(off) 192 if res > md.etypes { 193 println("runtime: typeOff", hex(off), "out of range", hex(md.types), "-", hex(md.etypes)) 194 throw("runtime: type offset out of range") 195 } 196 return (*_type)(unsafe.Pointer(res)) 197} 198 199func (t rtype) typeOff(off typeOff) *_type { 200 return resolveTypeOff(unsafe.Pointer(t.Type), off) 201} 202 203func (t rtype) textOff(off textOff) unsafe.Pointer { 204 if off == -1 { 205 // -1 is the sentinel value for unreachable code. 206 // See cmd/link/internal/ld/data.go:relocsym. 207 return unsafe.Pointer(abi.FuncPCABIInternal(unreachableMethod)) 208 } 209 base := uintptr(unsafe.Pointer(t.Type)) 210 var md *moduledata 211 for next := &firstmoduledata; next != nil; next = next.next { 212 if base >= next.types && base < next.etypes { 213 md = next 214 break 215 } 216 } 217 if md == nil { 218 reflectOffsLock() 219 res := reflectOffs.m[int32(off)] 220 reflectOffsUnlock() 221 if res == nil { 222 println("runtime: textOff", hex(off), "base", hex(base), "not in ranges:") 223 for next := &firstmoduledata; next != nil; next = next.next { 224 println("\ttypes", hex(next.types), "etypes", hex(next.etypes)) 225 } 226 throw("runtime: text offset base pointer out of range") 227 } 228 return res 229 } 230 res := md.textAddr(uint32(off)) 231 return unsafe.Pointer(res) 232} 233 234type uncommontype = abi.UncommonType 235 236type interfacetype = abi.InterfaceType 237 238type maptype = abi.MapType 239 240type arraytype = abi.ArrayType 241 242type chantype = abi.ChanType 243 244type slicetype = abi.SliceType 245 246type functype = abi.FuncType 247 248type ptrtype = abi.PtrType 249 250type name = abi.Name 251 252type structtype = abi.StructType 253 254func pkgPath(n name) string { 255 if n.Bytes == nil || *n.Data(0)&(1<<2) == 0 { 256 return "" 257 } 258 i, l := n.ReadVarint(1) 259 off := 1 + i + l 260 if *n.Data(0)&(1<<1) != 0 { 261 i2, l2 := n.ReadVarint(off) 262 off += i2 + l2 263 } 264 var nameOff nameOff 265 copy((*[4]byte)(unsafe.Pointer(&nameOff))[:], (*[4]byte)(unsafe.Pointer(n.Data(off)))[:]) 266 pkgPathName := resolveNameOff(unsafe.Pointer(n.Bytes), nameOff) 267 return pkgPathName.Name() 268} 269 270// typelinksinit scans the types from extra modules and builds the 271// moduledata typemap used to de-duplicate type pointers. 272func typelinksinit() { 273 if firstmoduledata.next == nil { 274 return 275 } 276 typehash := make(map[uint32][]*_type, len(firstmoduledata.typelinks)) 277 278 modules := activeModules() 279 prev := modules[0] 280 for _, md := range modules[1:] { 281 // Collect types from the previous module into typehash. 282 collect: 283 for _, tl := range prev.typelinks { 284 var t *_type 285 if prev.typemap == nil { 286 t = (*_type)(unsafe.Pointer(prev.types + uintptr(tl))) 287 } else { 288 t = prev.typemap[typeOff(tl)] 289 } 290 // Add to typehash if not seen before. 291 tlist := typehash[t.Hash] 292 for _, tcur := range tlist { 293 if tcur == t { 294 continue collect 295 } 296 } 297 typehash[t.Hash] = append(tlist, t) 298 } 299 300 if md.typemap == nil { 301 // If any of this module's typelinks match a type from a 302 // prior module, prefer that prior type by adding the offset 303 // to this module's typemap. 304 tm := make(map[typeOff]*_type, len(md.typelinks)) 305 pinnedTypemaps = append(pinnedTypemaps, tm) 306 md.typemap = tm 307 for _, tl := range md.typelinks { 308 t := (*_type)(unsafe.Pointer(md.types + uintptr(tl))) 309 for _, candidate := range typehash[t.Hash] { 310 seen := map[_typePair]struct{}{} 311 if typesEqual(t, candidate, seen) { 312 t = candidate 313 break 314 } 315 } 316 md.typemap[typeOff(tl)] = t 317 } 318 } 319 320 prev = md 321 } 322} 323 324type _typePair struct { 325 t1 *_type 326 t2 *_type 327} 328 329func toRType(t *abi.Type) rtype { 330 return rtype{t} 331} 332 333// typesEqual reports whether two types are equal. 334// 335// Everywhere in the runtime and reflect packages, it is assumed that 336// there is exactly one *_type per Go type, so that pointer equality 337// can be used to test if types are equal. There is one place that 338// breaks this assumption: buildmode=shared. In this case a type can 339// appear as two different pieces of memory. This is hidden from the 340// runtime and reflect package by the per-module typemap built in 341// typelinksinit. It uses typesEqual to map types from later modules 342// back into earlier ones. 343// 344// Only typelinksinit needs this function. 345func typesEqual(t, v *_type, seen map[_typePair]struct{}) bool { 346 tp := _typePair{t, v} 347 if _, ok := seen[tp]; ok { 348 return true 349 } 350 351 // mark these types as seen, and thus equivalent which prevents an infinite loop if 352 // the two types are identical, but recursively defined and loaded from 353 // different modules 354 seen[tp] = struct{}{} 355 356 if t == v { 357 return true 358 } 359 kind := t.Kind_ & abi.KindMask 360 if kind != v.Kind_&abi.KindMask { 361 return false 362 } 363 rt, rv := toRType(t), toRType(v) 364 if rt.string() != rv.string() { 365 return false 366 } 367 ut := t.Uncommon() 368 uv := v.Uncommon() 369 if ut != nil || uv != nil { 370 if ut == nil || uv == nil { 371 return false 372 } 373 pkgpatht := rt.nameOff(ut.PkgPath).Name() 374 pkgpathv := rv.nameOff(uv.PkgPath).Name() 375 if pkgpatht != pkgpathv { 376 return false 377 } 378 } 379 if abi.Bool <= kind && kind <= abi.Complex128 { 380 return true 381 } 382 switch kind { 383 case abi.String, abi.UnsafePointer: 384 return true 385 case abi.Array: 386 at := (*arraytype)(unsafe.Pointer(t)) 387 av := (*arraytype)(unsafe.Pointer(v)) 388 return typesEqual(at.Elem, av.Elem, seen) && at.Len == av.Len 389 case abi.Chan: 390 ct := (*chantype)(unsafe.Pointer(t)) 391 cv := (*chantype)(unsafe.Pointer(v)) 392 return ct.Dir == cv.Dir && typesEqual(ct.Elem, cv.Elem, seen) 393 case abi.Func: 394 ft := (*functype)(unsafe.Pointer(t)) 395 fv := (*functype)(unsafe.Pointer(v)) 396 if ft.OutCount != fv.OutCount || ft.InCount != fv.InCount { 397 return false 398 } 399 tin, vin := ft.InSlice(), fv.InSlice() 400 for i := 0; i < len(tin); i++ { 401 if !typesEqual(tin[i], vin[i], seen) { 402 return false 403 } 404 } 405 tout, vout := ft.OutSlice(), fv.OutSlice() 406 for i := 0; i < len(tout); i++ { 407 if !typesEqual(tout[i], vout[i], seen) { 408 return false 409 } 410 } 411 return true 412 case abi.Interface: 413 it := (*interfacetype)(unsafe.Pointer(t)) 414 iv := (*interfacetype)(unsafe.Pointer(v)) 415 if it.PkgPath.Name() != iv.PkgPath.Name() { 416 return false 417 } 418 if len(it.Methods) != len(iv.Methods) { 419 return false 420 } 421 for i := range it.Methods { 422 tm := &it.Methods[i] 423 vm := &iv.Methods[i] 424 // Note the mhdr array can be relocated from 425 // another module. See #17724. 426 tname := resolveNameOff(unsafe.Pointer(tm), tm.Name) 427 vname := resolveNameOff(unsafe.Pointer(vm), vm.Name) 428 if tname.Name() != vname.Name() { 429 return false 430 } 431 if pkgPath(tname) != pkgPath(vname) { 432 return false 433 } 434 tityp := resolveTypeOff(unsafe.Pointer(tm), tm.Typ) 435 vityp := resolveTypeOff(unsafe.Pointer(vm), vm.Typ) 436 if !typesEqual(tityp, vityp, seen) { 437 return false 438 } 439 } 440 return true 441 case abi.Map: 442 mt := (*maptype)(unsafe.Pointer(t)) 443 mv := (*maptype)(unsafe.Pointer(v)) 444 return typesEqual(mt.Key, mv.Key, seen) && typesEqual(mt.Elem, mv.Elem, seen) 445 case abi.Pointer: 446 pt := (*ptrtype)(unsafe.Pointer(t)) 447 pv := (*ptrtype)(unsafe.Pointer(v)) 448 return typesEqual(pt.Elem, pv.Elem, seen) 449 case abi.Slice: 450 st := (*slicetype)(unsafe.Pointer(t)) 451 sv := (*slicetype)(unsafe.Pointer(v)) 452 return typesEqual(st.Elem, sv.Elem, seen) 453 case abi.Struct: 454 st := (*structtype)(unsafe.Pointer(t)) 455 sv := (*structtype)(unsafe.Pointer(v)) 456 if len(st.Fields) != len(sv.Fields) { 457 return false 458 } 459 if st.PkgPath.Name() != sv.PkgPath.Name() { 460 return false 461 } 462 for i := range st.Fields { 463 tf := &st.Fields[i] 464 vf := &sv.Fields[i] 465 if tf.Name.Name() != vf.Name.Name() { 466 return false 467 } 468 if !typesEqual(tf.Typ, vf.Typ, seen) { 469 return false 470 } 471 if tf.Name.Tag() != vf.Name.Tag() { 472 return false 473 } 474 if tf.Offset != vf.Offset { 475 return false 476 } 477 if tf.Name.IsEmbedded() != vf.Name.IsEmbedded() { 478 return false 479 } 480 } 481 return true 482 default: 483 println("runtime: impossible type kind", kind) 484 throw("runtime: impossible type kind") 485 return false 486 } 487} 488