// Copyright (c) 2026 Petr BalvĂ­n (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause //go:build arm64 package verify import ( "encoding/binary" "fmt" "syscall" "unsafe" ) // enterJIT switches to the prepared stack and branches to fn. // //go:nosplit func enterJIT(fn uintptr, stack uintptr) // leaveJIT restores the Go stack after a JIT function returns. // //go:nosplit func leaveJIT() // leaveJITAddr is the raw ABI0 address of leaveJIT, handed over by the // GLOBL/DATA in trampoline_arm64.s (reflect would return the interposed // ABIInternal wrapper instead). var leaveRawAddr uintptr var leaveJITAddr = leaveRawAddr const stackPad = 64 // Call invokes the assembled function at fnAddr with the given ABI0 argument // block. On arm64, the ABI0 convention places arguments on the stack starting // at [SP+8] (same as amd64 ABI0 in this project). func Call(fnAddr uintptr, args []byte) ([]byte, error) { stackSize := stackPad + 8 + len(args) + 64 stackMem, err := syscall.Mmap(-1, 0, stackSize, syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_PRIVATE|syscall.MAP_ANON) if err != nil { return nil, fmt.Errorf("verify: stack mmap: %w", err) } defer syscall.Munmap(stackMem) retOff := stackPad binary.LittleEndian.PutUint64(stackMem[retOff:retOff+8], uint64(leaveJITAddr)) copy(stackMem[retOff+8:], args) stackBase := uintptr(unsafe.Pointer(&stackMem[retOff])) enterJIT(fnAddr, stackBase) out := make([]byte, len(args)) copy(out, stackMem[retOff+8:retOff+8+len(args)]) return out, nil }