github.com/trim21/go-phpserialize@v0.0.22-0.20240301204449-2fca0319b3f0/internal/runtime/type.go (about)

     1  package runtime
     2  
     3  import (
     4  	"reflect"
     5  	"unsafe"
     6  )
     7  
     8  type SliceHeader struct {
     9  	Data unsafe.Pointer
    10  	Len  int
    11  	Cap  int
    12  }
    13  
    14  const (
    15  	maxAcceptableTypeAddrRange = 1024 * 1024 * 2 // 2 Mib
    16  )
    17  
    18  type TypeAddr struct {
    19  	BaseTypeAddr uintptr
    20  	MaxTypeAddr  uintptr
    21  	AddrRange    uintptr
    22  	AddrShift    uintptr
    23  }
    24  
    25  var (
    26  	typeAddr        *TypeAddr
    27  	alreadyAnalyzed bool
    28  )
    29  
    30  //go:linkname typelinks reflect.typelinks
    31  func typelinks() ([]unsafe.Pointer, [][]int32)
    32  
    33  //go:linkname rtypeOff reflect.rtypeOff
    34  func rtypeOff(unsafe.Pointer, int32) unsafe.Pointer
    35  
    36  func 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  }