feat: auto-generate and verify TCC C API syscall numbers
This commit is contained in:
@@ -12,4 +12,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MONTAUK_BUILD_NUMBER 134
|
||||
#define MONTAUK_BUILD_NUMBER 136
|
||||
|
||||
+13
-2
@@ -89,7 +89,7 @@ CONFIGDIR := data/config
|
||||
CONFIGSRC := $(wildcard $(CONFIGDIR)/*.toml)
|
||||
CONFIGDST := $(patsubst $(CONFIGDIR)/%,$(BINDIR)/config/%,$(CONFIGSRC))
|
||||
|
||||
.PHONY: all clean 2048 doom fetch wiki wikipedia weather imageviewer fontpreview spreadsheet wordprocessor pdfviewer disks devexplorer installer audio music video bluetooth network terminal klog procmgr calculator login desktop shell rpgdemo paint tcc lua screenshot texteditor mandelbrot printers timezone printd printctl dialogs icons fonts configs bearssl libc tls libjpeg libjpegwrite install-apps libloader libs libhello test_dialogs test_dl crashpad
|
||||
.PHONY: all clean 2048 doom fetch wiki wikipedia weather imageviewer fontpreview spreadsheet wordprocessor pdfviewer disks devexplorer installer audio music video bluetooth network terminal klog procmgr calculator login desktop shell rpgdemo paint tcc lua screenshot texteditor mandelbrot printers timezone printd printctl dialogs icons fonts configs bearssl libc tls libjpeg libjpegwrite install-apps libloader libs libhello test_dialogs test_dl crashpad check-syscalls gen-syscalls
|
||||
|
||||
all: bearssl libc libjpeg libjpegwrite tls libloader libs libhello $(TARGETS) fetch wiki wikipedia weather imageviewer fontpreview spreadsheet wordprocessor pdfviewer disks devexplorer installer audio music video bluetooth network terminal klog procmgr calculator 2048 doom rpgdemo paint tcc lua screenshot texteditor mandelbrot printers timezone printd printctl dialogs login desktop shell icons fonts install-apps test_dialogs test_dl crashpad $(MANDST) $(WWWDST) $(CA_CERTS) $(CONFIGDST) $(FWDST)
|
||||
|
||||
@@ -278,8 +278,19 @@ doom: libc
|
||||
screenshot: libc libjpegwrite libloader dialogs
|
||||
$(MAKE) -C src/screenshot
|
||||
|
||||
# Verify the TCC C API header (libc/montauk.h) exposes every kernel syscall with
|
||||
# matching numbers. Fails the build on drift; 'make gen-syscalls' refreshes them.
|
||||
check-syscalls:
|
||||
python3 ../scripts/montauk-syscalls.py check
|
||||
|
||||
# Regenerate the MTK_SYS_* number block in libc/montauk.h from Api/Syscall.hpp.
|
||||
# Wrapper functions stay hand-written; this only updates the numbers.
|
||||
gen-syscalls:
|
||||
python3 ../scripts/montauk-syscalls.py gen
|
||||
|
||||
# Build TCC (Tiny C Compiler) via its own Makefile (depends on libc).
|
||||
tcc: libc
|
||||
# check-syscalls guards against shipping a stale C API header into the ramdisk.
|
||||
tcc: libc check-syscalls
|
||||
$(MAKE) -C src/tcc
|
||||
mkdir -p $(BINDIR)/lib/tcc/include
|
||||
cp -r src/tcc/tcc_include/* $(BINDIR)/lib/tcc/include/
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* ====================================================================
|
||||
Syscall numbers
|
||||
==================================================================== */
|
||||
|
||||
/* @SYSCALLS-BEGIN
|
||||
Generated from Api/Syscall.hpp by scripts/montauk-syscalls.py.
|
||||
Do not edit by hand; run 'make gen-syscalls' to refresh.
|
||||
Wrapper functions below are hand-written. */
|
||||
#define MTK_SYS_EXIT 0
|
||||
#define MTK_SYS_YIELD 1
|
||||
#define MTK_SYS_SLEEP_MS 2
|
||||
@@ -44,6 +44,8 @@ extern "C" {
|
||||
#define MTK_SYS_GETCHAR 18
|
||||
#define MTK_SYS_PING 19
|
||||
#define MTK_SYS_SPAWN 20
|
||||
#define MTK_SYS_FBINFO 21
|
||||
#define MTK_SYS_FBMAP 22
|
||||
#define MTK_SYS_WAITPID 23
|
||||
#define MTK_SYS_TERMSIZE 24
|
||||
#define MTK_SYS_GETARGS 25
|
||||
@@ -73,35 +75,52 @@ extern "C" {
|
||||
#define MTK_SYS_SPAWN_REDIR 49
|
||||
#define MTK_SYS_CHILDIO_READ 50
|
||||
#define MTK_SYS_CHILDIO_WRITE 51
|
||||
#define MTK_SYS_CHILDIO_WRITEKEY 52
|
||||
#define MTK_SYS_CHILDIO_SETTERMSZ 53
|
||||
#define MTK_SYS_WINCREATE 54
|
||||
#define MTK_SYS_WINDESTROY 55
|
||||
#define MTK_SYS_WINPRESENT 56
|
||||
#define MTK_SYS_WINPOLL 57
|
||||
#define MTK_SYS_WINENUM 58
|
||||
#define MTK_SYS_WINMAP 59
|
||||
#define MTK_SYS_WINUNMAP 97
|
||||
#define MTK_SYS_WINSENDEVENT 60
|
||||
#define MTK_SYS_PROCLIST 61
|
||||
#define MTK_SYS_KILL 62
|
||||
#define MTK_SYS_DEVLIST 63
|
||||
#define MTK_SYS_WINRESIZE 64
|
||||
#define MTK_SYS_WINSETSCALE 65
|
||||
#define MTK_SYS_WINGETSCALE 66
|
||||
#define MTK_SYS_MEMSTATS 67
|
||||
#define MTK_SYS_WINSETCURSOR 68
|
||||
#define MTK_SYS_WINSETFLAGS 126
|
||||
#define MTK_SYS_DISKINFO 69
|
||||
#define MTK_SYS_PARTLIST 70
|
||||
#define MTK_SYS_DISKREAD 71
|
||||
#define MTK_SYS_DISKWRITE 72
|
||||
#define MTK_SYS_GPTINIT 73
|
||||
#define MTK_SYS_GPTADD 74
|
||||
#define MTK_SYS_FSMOUNT 75
|
||||
#define MTK_SYS_FSFORMAT 76
|
||||
#define MTK_SYS_FDELETE 77
|
||||
#define MTK_SYS_FMKDIR 78
|
||||
#define MTK_SYS_FRENAME 94
|
||||
#define MTK_SYS_DRIVELIST 79
|
||||
#define MTK_SYS_DRIVELABEL 124
|
||||
#define MTK_SYS_AUDIOOPEN 80
|
||||
#define MTK_SYS_AUDIOCLOSE 81
|
||||
#define MTK_SYS_AUDIOWRITE 82
|
||||
#define MTK_SYS_AUDIOCTL 83
|
||||
#define MTK_SYS_BTSCAN 84
|
||||
#define MTK_SYS_BTCONNECT 85
|
||||
#define MTK_SYS_BTDISCONNECT 86
|
||||
#define MTK_SYS_BTLIST 87
|
||||
#define MTK_SYS_BTINFO 88
|
||||
#define MTK_SYS_SUSPEND 89
|
||||
#define MTK_SYS_SETTZ 90
|
||||
#define MTK_SYS_GETTZ 91
|
||||
#define MTK_SYS_SETUSER 92
|
||||
#define MTK_SYS_GETUSER 93
|
||||
#define MTK_SYS_FRENAME 94
|
||||
#define MTK_SYS_GETCWD 95
|
||||
#define MTK_SYS_CHDIR 96
|
||||
#define MTK_SYS_WINUNMAP 97
|
||||
#define MTK_SYS_DUPHANDLE 98
|
||||
#define MTK_SYS_WAIT_HANDLE 99
|
||||
#define MTK_SYS_STREAM_CREATE 100
|
||||
@@ -118,6 +137,33 @@ extern "C" {
|
||||
#define MTK_SYS_SURFACE_CREATE 111
|
||||
#define MTK_SYS_SURFACE_MAP 112
|
||||
#define MTK_SYS_SURFACE_RESIZE 113
|
||||
#define MTK_SYS_LOAD_LIB 114
|
||||
#define MTK_SYS_UNLOAD_LIB 115
|
||||
#define MTK_SYS_DLSYM 116
|
||||
#define MTK_SYS_GETLIBBASE 117
|
||||
#define MTK_SYS_CRASH_REPORT 118
|
||||
#define MTK_SYS_CLIPBOARD_SET_TEXT 119
|
||||
#define MTK_SYS_CLIPBOARD_GET_INFO 120
|
||||
#define MTK_SYS_CLIPBOARD_GET_TEXT 121
|
||||
#define MTK_SYS_CLIPBOARD_CLEAR 122
|
||||
#define MTK_SYS_INPUT_WAIT 123
|
||||
#define MTK_SYS_DRIVELABEL 124
|
||||
#define MTK_SYS_NETSTATUS 125
|
||||
#define MTK_SYS_WINSETFLAGS 126
|
||||
#define MTK_SYS_DRIVEKIND 127
|
||||
#define MTK_SYS_AUDIOLIST 128
|
||||
#define MTK_SYS_AUDIOWAIT 129
|
||||
#define MTK_SYS_THREAD_SPAWN 130
|
||||
#define MTK_SYS_THREAD_EXIT 131
|
||||
#define MTK_SYS_THREAD_JOIN 132
|
||||
#define MTK_SYS_THREAD_SELF 133
|
||||
#define MTK_SYS_FS_SYNC 134
|
||||
#define MTK_SYS_POWER_REQUEST 135
|
||||
#define MTK_SYS_READDIR_AT 136
|
||||
#define MTK_SYS_BTSETADDR 137
|
||||
#define MTK_SYS_BTBONDS 138
|
||||
#define MTK_SYS_BTFORGET 139
|
||||
/* @SYSCALLS-END */
|
||||
|
||||
#define MTK_SOCK_TCP 1
|
||||
#define MTK_SOCK_UDP 2
|
||||
|
||||
Executable
+156
@@ -0,0 +1,156 @@
|
||||
#!/usr/bin/env python3
|
||||
# montauk-syscalls.py - Keep the TCC C API header's syscall numbers in sync
|
||||
# with the canonical syscall ABI.
|
||||
#
|
||||
# Source of truth : programs/include/Api/Syscall.hpp (montauk::abi::SYS_*)
|
||||
# Generated block : programs/include/libc/montauk.h (#define MTK_SYS_*)
|
||||
#
|
||||
# The libc/montauk.h header is a hand-maintained, TCC-compatible C mirror of the
|
||||
# syscall surface. Its typed wrapper functions stay hand-written (their argument
|
||||
# types are not machine-derivable from the enum), but the raw "#define MTK_SYS_*"
|
||||
# number table is mechanical and lives between the @SYSCALLS-BEGIN/END markers,
|
||||
# which this script regenerates.
|
||||
#
|
||||
# Usage:
|
||||
# montauk-syscalls.py check Verify every SYS_* has a matching MTK_SYS_* with
|
||||
# the same number. Exits non-zero on any drift.
|
||||
# Run from the build so stale headers fail loudly.
|
||||
# montauk-syscalls.py gen Rewrite the MTK_SYS_* block in montauk.h from the
|
||||
# canonical enum (numbers only; wrappers untouched).
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
REPO_ROOT = os.path.dirname(SCRIPT_DIR)
|
||||
ABI_HDR = os.path.join(REPO_ROOT, "programs/include/Api/Syscall.hpp")
|
||||
C_HDR = os.path.join(REPO_ROOT, "programs/include/libc/montauk.h")
|
||||
|
||||
BEGIN_MARKER = "/* @SYSCALLS-BEGIN"
|
||||
END_MARKER = "/* @SYSCALLS-END */"
|
||||
|
||||
# Canonical SYS_* names intentionally NOT exposed in the C API. Empty by default;
|
||||
# add a name here (without the SYS_ prefix) to suppress a kernel-internal syscall.
|
||||
EXCLUDE = set()
|
||||
|
||||
# static constexpr uint64_t SYS_FOO = 42;
|
||||
ABI_RE = re.compile(r"constexpr\s+\w+\s+SYS_([A-Z0-9_]+)\s*=\s*(\d+)\s*;")
|
||||
# #define MTK_SYS_FOO 42
|
||||
DEF_RE = re.compile(r"#define\s+MTK_SYS_([A-Z0-9_]+)\s+(\d+)\b")
|
||||
|
||||
|
||||
def read(path):
|
||||
with open(path, "r") as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def parse_abi():
|
||||
"""Return {NAME: number} for every canonical syscall, minus EXCLUDE."""
|
||||
out = {}
|
||||
for name, num in ABI_RE.findall(read(ABI_HDR)):
|
||||
if name in EXCLUDE:
|
||||
continue
|
||||
out[name] = int(num)
|
||||
if not out:
|
||||
sys.exit("montauk-syscalls: no SYS_* entries found in %s" % ABI_HDR)
|
||||
return out
|
||||
|
||||
|
||||
def parse_defines(text):
|
||||
"""Return {NAME: number} for every MTK_SYS_* define in the C header."""
|
||||
return {name: int(num) for name, num in DEF_RE.findall(text)}
|
||||
|
||||
|
||||
def diff(abi, defs):
|
||||
"""Return (missing, mismatched, extra) describing drift of defs vs abi."""
|
||||
missing = sorted(n for n in abi if n not in defs)
|
||||
extra = sorted(n for n in defs if n not in abi)
|
||||
mismatched = sorted(
|
||||
(n, abi[n], defs[n]) for n in abi if n in defs and abi[n] != defs[n]
|
||||
)
|
||||
return missing, mismatched, extra
|
||||
|
||||
|
||||
def cmd_check():
|
||||
abi = parse_abi()
|
||||
defs = parse_defines(read(C_HDR))
|
||||
missing, mismatched, extra = diff(abi, defs)
|
||||
if not (missing or mismatched or extra):
|
||||
print("montauk-syscalls: OK (%d syscalls in sync)" % len(abi))
|
||||
return 0
|
||||
|
||||
print("montauk-syscalls: DRIFT between Api/Syscall.hpp and libc/montauk.h",
|
||||
file=sys.stderr)
|
||||
if missing:
|
||||
print(" %d missing from montauk.h:" % len(missing), file=sys.stderr)
|
||||
for n in missing:
|
||||
print(" SYS_%-22s = %d" % (n, abi[n]), file=sys.stderr)
|
||||
if mismatched:
|
||||
print(" %d number mismatch(es):" % len(mismatched), file=sys.stderr)
|
||||
for n, a, d in mismatched:
|
||||
print(" SYS_%-22s abi=%d montauk.h=%d" % (n, a, d),
|
||||
file=sys.stderr)
|
||||
if extra:
|
||||
print(" %d stale (not in ABI):" % len(extra), file=sys.stderr)
|
||||
for n in extra:
|
||||
print(" MTK_SYS_%s" % n, file=sys.stderr)
|
||||
print("\n Run 'make gen-syscalls' to regenerate the MTK_SYS_* number block,",
|
||||
file=sys.stderr)
|
||||
print(" then add C wrapper functions for any new syscalls by hand.",
|
||||
file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
def render_block(abi):
|
||||
width = max(len("MTK_SYS_" + n) for n in abi) + 1
|
||||
lines = [BEGIN_MARKER,
|
||||
" Generated from Api/Syscall.hpp by scripts/montauk-syscalls.py.",
|
||||
" Do not edit by hand; run 'make gen-syscalls' to refresh.",
|
||||
" Wrapper functions below are hand-written. */"]
|
||||
for name in sorted(abi, key=lambda n: abi[n]):
|
||||
macro = "MTK_SYS_" + name
|
||||
lines.append("#define %-*s%d" % (width, macro, abi[name]))
|
||||
lines.append(END_MARKER)
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def cmd_gen():
|
||||
abi = parse_abi()
|
||||
text = read(C_HDR)
|
||||
block = render_block(abi)
|
||||
|
||||
if BEGIN_MARKER in text and END_MARKER in text:
|
||||
start = text.index(BEGIN_MARKER)
|
||||
end = text.index(END_MARKER) + len(END_MARKER)
|
||||
new = text[:start] + block + text[end:]
|
||||
else:
|
||||
sys.exit("montauk-syscalls: markers not found in %s; add a "
|
||||
"%s ... %s block first" % (C_HDR, BEGIN_MARKER, END_MARKER))
|
||||
|
||||
if new == text:
|
||||
print("montauk-syscalls: montauk.h already up to date (%d syscalls)"
|
||||
% len(abi))
|
||||
return 0
|
||||
with open(C_HDR, "w") as f:
|
||||
f.write(new)
|
||||
print("montauk-syscalls: regenerated MTK_SYS_* block (%d syscalls)" % len(abi))
|
||||
missing_wrappers = [
|
||||
n for n in sorted(abi)
|
||||
if not re.search(r"\bMTK_SYS_%s\b" % n, new[new.index(END_MARKER):])
|
||||
]
|
||||
if missing_wrappers:
|
||||
print(" note: %d syscall(s) have a number but no wrapper yet: %s"
|
||||
% (len(missing_wrappers),
|
||||
", ".join("SYS_" + n for n in missing_wrappers)))
|
||||
return 0
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 2 or sys.argv[1] not in ("check", "gen"):
|
||||
sys.exit("usage: montauk-syscalls.py {check|gen}")
|
||||
return cmd_check() if sys.argv[1] == "check" else cmd_gen()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user