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

     1  package amd64
     2  
     3  import (
     4  	"encoding/binary"
     5  	"reflect"
     6  	"unsafe"
     7  
     8  	"github.com/tetratelabs/wazero/internal/wasmdebug"
     9  )
    10  
    11  func stackView(rbp, top uintptr) []byte {
    12  	var stackBuf []byte
    13  	{
    14  		// TODO: use unsafe.Slice after floor version is set to Go 1.20.
    15  		hdr := (*reflect.SliceHeader)(unsafe.Pointer(&stackBuf))
    16  		hdr.Data = rbp
    17  		setSliceLimits(hdr, top-rbp)
    18  	}
    19  	return stackBuf
    20  }
    21  
    22  // UnwindStack implements wazevo.unwindStack.
    23  func UnwindStack(_, rbp, top uintptr, returnAddresses []uintptr) []uintptr {
    24  	stackBuf := stackView(rbp, top)
    25  
    26  	for i := uint64(0); i < uint64(len(stackBuf)); {
    27  		//       (high address)
    28  		//    +-----------------+
    29  		//    |     .......     |
    30  		//    |      ret Y      |
    31  		//    |     .......     |
    32  		//    |      ret 0      |
    33  		//    |      arg X      |
    34  		//    |     .......     |
    35  		//    |      arg 1      |
    36  		//    |      arg 0      |
    37  		//    |  ReturnAddress  |
    38  		//    |   Caller_RBP    |
    39  		//    +-----------------+ <---- Caller_RBP
    40  		//    |   ...........   |
    41  		//    |   clobbered  M  |
    42  		//    |   ............  |
    43  		//    |   clobbered  0  |
    44  		//    |   spill slot N  |
    45  		//    |   ............  |
    46  		//    |   spill slot 0  |
    47  		//    |  ReturnAddress  |
    48  		//    |   Caller_RBP    |
    49  		//    +-----------------+ <---- RBP
    50  		//       (low address)
    51  
    52  		callerRBP := binary.LittleEndian.Uint64(stackBuf[i:])
    53  		retAddr := binary.LittleEndian.Uint64(stackBuf[i+8:])
    54  		returnAddresses = append(returnAddresses, uintptr(retAddr))
    55  		i = callerRBP - uint64(rbp)
    56  		if len(returnAddresses) == wasmdebug.MaxFrames {
    57  			break
    58  		}
    59  	}
    60  	return returnAddresses
    61  }
    62  
    63  // GoCallStackView implements wazevo.goCallStackView.
    64  func GoCallStackView(stackPointerBeforeGoCall *uint64) []uint64 {
    65  	//                  (high address)
    66  	//              +-----------------+ <----+
    67  	//              |   xxxxxxxxxxx   |      | ;; optional unused space to make it 16-byte aligned.
    68  	//           ^  |  arg[N]/ret[M]  |      |
    69  	// sliceSize |  |  ............   |      | SizeInBytes/8
    70  	//           |  |  arg[1]/ret[1]  |      |
    71  	//           v  |  arg[0]/ret[0]  | <----+
    72  	//              |   SizeInBytes   |
    73  	//              +-----------------+ <---- stackPointerBeforeGoCall
    74  	//                 (low address)
    75  	data := unsafe.Pointer(uintptr(unsafe.Pointer(stackPointerBeforeGoCall)) + 8)
    76  	size := *stackPointerBeforeGoCall / 8
    77  	return unsafe.Slice((*uint64)(data), int(size))
    78  }
    79  
    80  func AdjustClonedStack(oldRsp, oldTop, rsp, rbp, top uintptr) {
    81  	diff := uint64(rsp - oldRsp)
    82  
    83  	newBuf := stackView(rbp, top)
    84  	for i := uint64(0); i < uint64(len(newBuf)); {
    85  		//       (high address)
    86  		//    +-----------------+
    87  		//    |     .......     |
    88  		//    |      ret Y      |
    89  		//    |     .......     |
    90  		//    |      ret 0      |
    91  		//    |      arg X      |
    92  		//    |     .......     |
    93  		//    |      arg 1      |
    94  		//    |      arg 0      |
    95  		//    |  ReturnAddress  |
    96  		//    |   Caller_RBP    |
    97  		//    +-----------------+ <---- Caller_RBP
    98  		//    |   ...........   |
    99  		//    |   clobbered  M  |
   100  		//    |   ............  |
   101  		//    |   clobbered  0  |
   102  		//    |   spill slot N  |
   103  		//    |   ............  |
   104  		//    |   spill slot 0  |
   105  		//    |  ReturnAddress  |
   106  		//    |   Caller_RBP    |
   107  		//    +-----------------+ <---- RBP
   108  		//       (low address)
   109  
   110  		callerRBP := binary.LittleEndian.Uint64(newBuf[i:])
   111  		if callerRBP == 0 {
   112  			// End of stack.
   113  			break
   114  		}
   115  		if i64 := int64(callerRBP); i64 < int64(oldRsp) || i64 >= int64(oldTop) {
   116  			panic("BUG: callerRBP is out of range")
   117  		}
   118  		if int(callerRBP) < 0 {
   119  			panic("BUG: callerRBP is negative")
   120  		}
   121  		adjustedCallerRBP := callerRBP + diff
   122  		if int(adjustedCallerRBP) < 0 {
   123  			panic("BUG: adjustedCallerRBP is negative")
   124  		}
   125  		binary.LittleEndian.PutUint64(newBuf[i:], adjustedCallerRBP)
   126  		i = adjustedCallerRBP - uint64(rbp)
   127  	}
   128  }