github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/engine/wazevo/backend/isa/arm64/unwind_stack.go (about)

     1  package arm64
     2  
     3  import (
     4  	"encoding/binary"
     5  	"reflect"
     6  	"unsafe"
     7  )
     8  
     9  // UnwindStack is a function to unwind the stack, and appends return addresses to `returnAddresses` slice.
    10  // The implementation must be aligned with the ABI/Calling convention as in machine_pro_epi_logue.go/abi.go.
    11  func UnwindStack(sp, top uintptr, returnAddresses []uintptr) []uintptr {
    12  	l := int(top - sp)
    13  
    14  	var stackBuf []byte
    15  	{
    16  		// TODO: use unsafe.Slice after floor version is set to Go 1.20.
    17  		hdr := (*reflect.SliceHeader)(unsafe.Pointer(&stackBuf))
    18  		hdr.Data = sp
    19  		hdr.Len = l
    20  		hdr.Cap = l
    21  	}
    22  
    23  	for i := uint64(0); i < uint64(l); {
    24  		//       (high address)
    25  		//    +-----------------+
    26  		//    |     .......     |
    27  		//    |      ret Y      |  <----+
    28  		//    |     .......     |       |
    29  		//    |      ret 0      |       |
    30  		//    |      arg X      |       |  size_of_arg_ret
    31  		//    |     .......     |       |
    32  		//    |      arg 1      |       |
    33  		//    |      arg 0      |  <----+
    34  		//    | size_of_arg_ret |
    35  		//    |  ReturnAddress  |
    36  		//    +-----------------+ <----+
    37  		//    |   ...........   |      |
    38  		//    |   spill slot M  |      |
    39  		//    |   ............  |      |
    40  		//    |   spill slot 2  |      |
    41  		//    |   spill slot 1  |      | frame size
    42  		//    |   spill slot 1  |      |
    43  		//    |   clobbered N   |      |
    44  		//    |   ............  |      |
    45  		//    |   clobbered 0   | <----+
    46  		//    |     xxxxxx      |  ;; unused space to make it 16-byte aligned.
    47  		//    |   frame_size    |
    48  		//    +-----------------+ <---- SP
    49  		//       (low address)
    50  
    51  		frameSize := binary.LittleEndian.Uint64(stackBuf[i:])
    52  		i += frameSize +
    53  			16 // frame size + aligned space.
    54  		retAddr := binary.LittleEndian.Uint64(stackBuf[i:])
    55  		i += 8 // ret addr.
    56  		sizeOfArgRet := binary.LittleEndian.Uint64(stackBuf[i:])
    57  		i += 8 + sizeOfArgRet
    58  		returnAddresses = append(returnAddresses, uintptr(retAddr))
    59  	}
    60  	return returnAddresses
    61  }
    62  
    63  // GoCallStackView is a function to get a view of the stack before a Go call, which
    64  // is the view of the stack allocated in CompileGoFunctionTrampoline.
    65  func GoCallStackView(stackPointerBeforeGoCall *uint64) []uint64 {
    66  	//                  (high address)
    67  	//              +-----------------+ <----+
    68  	//              |   xxxxxxxxxxx   |      | ;; optional unused space to make it 16-byte aligned.
    69  	//           ^  |  arg[N]/ret[M]  |      |
    70  	// sliceSize |  |  ............   |      | sliceSize
    71  	//           |  |  arg[1]/ret[1]  |      |
    72  	//           v  |  arg[0]/ret[0]  | <----+
    73  	//              |    sliceSize    |
    74  	//              |   frame_size    |
    75  	//              +-----------------+ <---- stackPointerBeforeGoCall
    76  	//                 (low address)
    77  	size := *(*uint64)(unsafe.Pointer(uintptr(unsafe.Pointer(stackPointerBeforeGoCall)) + 8))
    78  	var view []uint64
    79  	{
    80  		sh := (*reflect.SliceHeader)(unsafe.Pointer(&view))
    81  		sh.Data = uintptr(unsafe.Pointer(stackPointerBeforeGoCall)) + 16 // skips the (frame_size, sliceSize).
    82  		sh.Len = int(size)
    83  		sh.Cap = int(size)
    84  	}
    85  	return view
    86  }