github.com/stealthrocket/wzprof@v0.2.1-0.20230830205924-5fa86be5e5b3/memory.go (about)

     1  //go:build amd64 || arm64
     2  
     3  package wzprof
     4  
     5  import (
     6  	"fmt"
     7  	"reflect"
     8  	"unsafe"
     9  )
    10  
    11  // ptr64 represents a 64-bits address in the guest memory. It replaces unintptr
    12  // in the original unwinder code. Here, the unwinder executes in the host, so
    13  // this type helps to avoid dereferencing the host memory.
    14  type ptr64 uint64
    15  
    16  func (p ptr64) addr() uint32 {
    17  	return uint32(p)
    18  }
    19  
    20  // ptr32 represents a 32-bits address in the guest memory. It replaces pointers
    21  // in clang-wasi generated code.
    22  type ptr32 uint32
    23  
    24  func (p ptr32) addr() uint32 {
    25  	return uint32(p)
    26  }
    27  
    28  type ptr interface {
    29  	addr() uint32
    30  }
    31  
    32  // vmem is the minimum interface required for virtual memory accesses in this
    33  // package. Is is used to read guest memory and rebuild the constructs needed
    34  // for symbolization. It manipulates ptr to avoid confusion between host and
    35  // guest memory.
    36  //
    37  // The functions that operate on it assume both the guest and host are 64bits
    38  // little-endian machines. That way they can cast bytes to specific types
    39  // without having the deserialize them and perform the endianess or size
    40  // conversion.
    41  //
    42  // uintptr/unsafe.Pointer are used to manipulate memory seen by the host, and
    43  // ptr is used to represent memory inside the guest.
    44  type vmem interface {
    45  	// Read returns a view of the size bytes at the given virtual
    46  	// address, or false if the requested bytes are out of range.
    47  	// Users of this output need not modify the bytes, and make a copy
    48  	// of them if they wish to persist the data.
    49  	Read(address, size uint32) ([]byte, bool)
    50  }
    51  
    52  // deref the bytes at address p in virtual memory, casting them back as T. It is
    53  // not recursive: if T is a struct and contains pointers or slices, deref does
    54  // not bring their contents from memory. Pointers can be deref'd themselves, and
    55  // derefSlice can help to bring the contents of slices to the host memory.
    56  func deref[T any](r vmem, p ptr) T {
    57  	var t T
    58  	s := uint32(unsafe.Sizeof(t))
    59  	b, ok := r.Read(p.addr(), s)
    60  	if !ok {
    61  		panic(fmt.Errorf("invalid virtual memory read at %#x size %d", p, s))
    62  	}
    63  	return *(*T)(unsafe.Pointer((unsafe.SliceData(b))))
    64  }
    65  
    66  // derefArrayInto copies into the given host slice contiguous elements
    67  // of type T starting at the virtual address p to fill it.
    68  func derefArray[T any](r vmem, p ptr, n uint32) []T {
    69  	var t T
    70  	s := uint32(unsafe.Sizeof(t)) * n
    71  	view, ok := r.Read(p.addr(), s)
    72  	if !ok {
    73  		panic(fmt.Errorf("invalid virtual memory array read at %#x size %d", p, s))
    74  	}
    75  
    76  	outb := make([]byte, s)
    77  	copy(outb, view)
    78  	x := (*T)(unsafe.Pointer(unsafe.SliceData(outb)))
    79  	return unsafe.Slice(x, n)
    80  }
    81  
    82  // derefGoSlice takes a slice whose data pointer targets the guest memory, and
    83  // returns a copy the slice's contents in host memory. It is not recursive. Cap
    84  // is set to Len, no matter its initial value. Assumes the underlying pointer is
    85  // 64-bits.
    86  func derefGoSlice[T any](r vmem, s []T) []T {
    87  	count := len(s)
    88  	sh := (*reflect.SliceHeader)(unsafe.Pointer(&s))
    89  	dp := ptr64(sh.Data)
    90  	res := make([]T, count)
    91  	for i := 0; i < count; i++ {
    92  		res[i] = derefArrayIndex[T](r, dp, int32(i))
    93  	}
    94  	return res
    95  }
    96  
    97  // Reads the i-th element of an array that starts at address p.
    98  func derefArrayIndex[T any](r vmem, p ptr, i int32) T {
    99  	var t T
   100  	a := p.addr()
   101  	s := uint32(unsafe.Sizeof(t))
   102  	return deref[T](r, ptr32(a+uint32(i)*s))
   103  }