1package runtime
2
3import (
4	"reflect"
5	"unsafe"
6)
7
8type SliceHeader struct {
9	Data unsafe.Pointer
10	Len  int
11	Cap  int
12}
13
14const (
15	maxAcceptableTypeAddrRange = 1024 * 1024 * 2 // 2 Mib
16)
17
18type TypeAddr struct {
19	BaseTypeAddr uintptr
20	MaxTypeAddr  uintptr
21	AddrRange    uintptr
22	AddrShift    uintptr
23}
24
25var (
26	typeAddr        *TypeAddr
27	alreadyAnalyzed bool
28)
29
30//go:linkname typelinks reflect.typelinks
31func typelinks() ([]unsafe.Pointer, [][]int32)
32
33//go:linkname rtypeOff reflect.rtypeOff
34func rtypeOff(unsafe.Pointer, int32) unsafe.Pointer
35
36func AnalyzeTypeAddr() *TypeAddr {
37	defer func() {
38		alreadyAnalyzed = true
39	}()
40	if alreadyAnalyzed {
41		return typeAddr
42	}
43	sections, offsets := typelinks()
44	if len(sections) != 1 {
45		return nil
46	}
47	if len(offsets) != 1 {
48		return nil
49	}
50	section := sections[0]
51	offset := offsets[0]
52	var (
53		min         uintptr = uintptr(^uint(0))
54		max         uintptr = 0
55		isAligned64         = true
56		isAligned32         = true
57	)
58	for i := 0; i < len(offset); i++ {
59		typ := (*Type)(rtypeOff(section, offset[i]))
60		addr := uintptr(unsafe.Pointer(typ))
61		if min > addr {
62			min = addr
63		}
64		if max < addr {
65			max = addr
66		}
67		if typ.Kind() == reflect.Ptr {
68			addr = uintptr(unsafe.Pointer(typ.Elem()))
69			if min > addr {
70				min = addr
71			}
72			if max < addr {
73				max = addr
74			}
75		}
76		isAligned64 = isAligned64 && (addr-min)&63 == 0
77		isAligned32 = isAligned32 && (addr-min)&31 == 0
78	}
79	addrRange := max - min
80	if addrRange == 0 {
81		return nil
82	}
83	var addrShift uintptr
84	if isAligned64 {
85		addrShift = 6
86	} else if isAligned32 {
87		addrShift = 5
88	}
89	cacheSize := addrRange >> addrShift
90	if cacheSize > maxAcceptableTypeAddrRange {
91		return nil
92	}
93	typeAddr = &TypeAddr{
94		BaseTypeAddr: min,
95		MaxTypeAddr:  max,
96		AddrRange:    addrRange,
97		AddrShift:    addrShift,
98	}
99	return typeAddr
100}
101