github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/engine/wazevo/backend/isa/amd64/stack_test.go (about)

     1  package amd64
     2  
     3  import (
     4  	"encoding/binary"
     5  	"testing"
     6  	"unsafe"
     7  
     8  	"github.com/tetratelabs/wazero/internal/testing/require"
     9  )
    10  
    11  func TestUnwindStack(t *testing.T) {
    12  	for _, tc := range []struct {
    13  		name  string
    14  		setup func() (stack []byte, exp []uintptr)
    15  	}{
    16  		{name: "no frame", setup: func() (_ []byte, exp []uintptr) { return []byte{0}, exp }},
    17  		{name: "three", setup: func() ([]byte, []uintptr) {
    18  			exp := []uintptr{0xffffffff_00000000, 0xffffffff_00000001, 0xffffffff_00000002}
    19  			stack := make([]byte, 240)
    20  			bp := uintptr(unsafe.Pointer(&stack[0]))
    21  			oldRBP1 := bp + 32
    22  			binary.LittleEndian.PutUint64(stack[0:], uint64(oldRBP1))             // old bp
    23  			binary.LittleEndian.PutUint64(stack[8:], uint64(0xffffffff_00000000)) // return address
    24  			oldRBP2 := oldRBP1 + 16
    25  			binary.LittleEndian.PutUint64(stack[oldRBP1-bp:], uint64(oldRBP2))               // old bp
    26  			binary.LittleEndian.PutUint64(stack[oldRBP1-bp+8:], uint64(0xffffffff_00000001)) // return address
    27  			binary.LittleEndian.PutUint64(stack[oldRBP2-bp:], uint64(0))                     // old bp
    28  			binary.LittleEndian.PutUint64(stack[oldRBP2-bp+8:], uint64(0xffffffff_00000002)) // return address
    29  			return stack, exp
    30  		}},
    31  	} {
    32  		tc := tc
    33  		t.Run(tc.name, func(t *testing.T) {
    34  			stack, exp := tc.setup()
    35  			bp := uintptr(unsafe.Pointer(&stack[0]))
    36  			returnAddresses := UnwindStack(0, bp, uintptr(unsafe.Pointer(&stack[len(stack)-1])), nil)
    37  			require.Equal(t, exp, returnAddresses)
    38  		})
    39  	}
    40  }
    41  
    42  func addressOf(v *byte) uint64 {
    43  	return uint64(uintptr(unsafe.Pointer(v)))
    44  }
    45  
    46  func TestAdjustClonedStack(t *testing.T) {
    47  	// In order to allocate slices on Go heap, we need to allocSlice function.
    48  	allocSlice := func(size int) []byte {
    49  		return make([]byte, size)
    50  	}
    51  
    52  	oldStack := allocSlice(512)
    53  	oldRsp := uintptr(unsafe.Pointer(&oldStack[0]))
    54  	oldTop := uintptr(unsafe.Pointer(&oldStack[len(oldStack)-1]))
    55  	rbpIndex := uintptr(32)
    56  	binary.LittleEndian.PutUint64(oldStack[rbpIndex:], addressOf(&oldStack[16+rbpIndex]))
    57  	binary.LittleEndian.PutUint64(oldStack[rbpIndex+16:], addressOf(&oldStack[32+rbpIndex]))
    58  	binary.LittleEndian.PutUint64(oldStack[rbpIndex+32:], addressOf(&oldStack[160+rbpIndex]))
    59  
    60  	newStack := allocSlice(1024)
    61  	rsp := uintptr(unsafe.Pointer(&newStack[0]))
    62  	rbp := rsp + rbpIndex
    63  	// Coy old stack to new stack which contains the old pointers to the old stack elements.
    64  	copy(newStack, oldStack)
    65  
    66  	AdjustClonedStack(oldRsp, oldTop, rsp, rbp, uintptr(addressOf(&newStack[len(newStack)-1])))
    67  	require.Equal(t, addressOf(&newStack[rbpIndex+16]), binary.LittleEndian.Uint64(newStack[rbpIndex:]))
    68  	require.Equal(t, addressOf(&newStack[rbpIndex+32]), binary.LittleEndian.Uint64(newStack[rbpIndex+16:]))
    69  	require.Equal(t, addressOf(&newStack[rbpIndex+160]), binary.LittleEndian.Uint64(newStack[rbpIndex+32:]))
    70  }