feat(verify): ABI checks on arm64, riscv64 and loong64

Assisted-by: GLM 5.3 Flash
This commit is contained in:
2026-08-30 11:27:54 +02:00
parent 6d7f10f13e
commit 8f84dac10b
19 changed files with 710 additions and 138 deletions
+61
View File
@@ -0,0 +1,61 @@
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: BSD-3-Clause
#include "textflag.h"
// ABI-checking trampoline for LoongArch 64. Sets a sentinel value in the
// register the Go ABI fixes across calls before entering the JIT function
// and checks whether it 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 ABI on loong64 guarantees:
// - R22 holds the goroutine pointer (g) and must survive across any
// call. Go keeps no hardware frame pointer on loong64. The assembler
// spells this register "g"; R22 is not accepted.
// Sentinel value chosen to be unlikely in normal execution.
#define SENTINEL_G 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 a sentinel in g (R22), switches to the prepared stack and jumps to
// fn. The prepared stack's first word must be the address of
// leaveJITCheckedRaw (read from leaveCheckedPtr). Only R4 and R5 are used
// as scratch: caller-saved, and R22 is not among them.
TEXT ·enterJITChecked(SB), NOSPLIT, $0-16
MOVV fn+0(FP), R4 // target function address (A0)
MOVV R1, savedRA(SB) // save return address (RA)
MOVV R3, savedSP(SB) // save Go stack pointer (SP)
MOVV $SENTINEL_G, g // sentinel in g
MOVV stack+8(FP), R5 // load prepared stack pointer (A1)
MOVV 0(R5), R1 // load leaveJITCheckedRaw into RA
ADDV $8, R5, R5 // advance past the return slot
MOVV R5, R3 // switch to prepared stack (SP)
JIRL R0, R4, 0 // jump to JIT function
// 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 g exactly as the function left it. It
// checks the sentinel, records violations in abiResult, then restores the
// Go stack and returns.
TEXT ·leaveJITCheckedRaw(SB), NOSPLIT, $0-0
// Check g against the sentinel.
MOVV $SENTINEL_G, R5
BEQ g, R5, g_ok
MOVV ·abiResult(SB), R4
MOVV $2, R6
OR R6, R4, R4
MOVV R4, ·abiResult(SB)
g_ok:
MOVV savedSP(SB), R5 // restore Go stack pointer
MOVV R5, R3
MOVV savedRA(SB), R1 // restore return address
JIRL R0, R1, 0 // return to Go caller