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 }