// Copyright (c) 2026 Petr Balvín (https://petrbalvin.org) // SPDX-License-Identifier: BSD-3-Clause #include "textflag.h" // ABI-checking trampoline. Sets sentinel values in the callee-saved // registers (BP, R14) before entering the JIT function and checks whether // they survived on return. // // The return trampoline (leaveJITCheckedRaw) is a raw TEXT symbol with no // Go function declaration, so the toolchain does NOT interpose an // ABIInternal wrapper; the JIT function RETs directly into the check code, // which sees the registers exactly as the function left them. // // Go ABI0 on amd64 guarantees: // - BP is callee-saved (NOSPLIT frame=0 functions must not touch it). // - R14 holds the goroutine pointer and must survive across any call. // Sentinel values chosen to be unlikely in normal execution. #define SENTINEL_BP 0xDEADBEEFCAFEF00D #define SENTINEL_R14 0x0BADF00DDEADBEEF // GLOBL holding the raw address of the leave trampoline, read by Go. GLOBL ·leaveCheckedPtr(SB), NOPTR, $8 DATA ·leaveCheckedPtr(SB)/8, $·leaveJITCheckedRaw(SB) // func enterJITChecked(fn uintptr, stack uintptr) // Sets sentinels in BP and R14, switches to the prepared stack and jumps // to fn. The prepared stack's return address must be leaveJITCheckedRaw // (read from leaveCheckedPtr). TEXT ·enterJITChecked(SB), NOSPLIT, $0-16 MOVQ fn+0(FP), AX // target (before SP switch) MOVQ SP, ·savedSP(SB) // preserve Go stack MOVQ BP, ·savedBP(SB) // preserve frame pointer (vet requires save before clobber) MOVQ R14, ·savedR14(SB) // preserve the goroutine pointer MOVQ $SENTINEL_BP, BP // sentinel in BP MOVQ $SENTINEL_R14, R14 // sentinel in R14 MOVQ stack+8(FP), SP // switch to prepared stack JMP AX // leaveJITCheckedRaw is the raw return trampoline. It has NO Go function // declaration, so no ABIInternal wrapper is generated; the JIT function's // RET lands here directly, seeing BP and R14 exactly as the function left // them. It checks the sentinels, records violations in abiResult, then // restores the Go stack and returns. TEXT ·leaveJITCheckedRaw(SB), NOSPLIT, $0-0 // Check BP against the sentinel. MOVQ $SENTINEL_BP, CX CMPQ BP, CX JEQ bp_ok ORQ $1, ·abiResult(SB) bp_ok: // Check R14 against the sentinel. MOVQ $SENTINEL_R14, CX CMPQ R14, CX JEQ r14_ok ORQ $2, ·abiResult(SB) r14_ok: MOVQ ·savedR14(SB), R14 // restore the goroutine pointer: the runtime // needs it the moment Go code resumes, whether // or not the kernel violated it (the violation // is already recorded in abiResult) MOVQ ·savedBP(SB), BP // restore the frame pointer MOVQ ·savedSP(SB), SP RET