#!/usr/bin/env python3 """Generate stdlib/linux.sl from syscall_64.tbl metadata.""" from __future__ import annotations from dataclasses import dataclass from pathlib import Path import re ROOT = Path(__file__).resolve().parent.parent SRC = ROOT / "syscall_64.tbl" DST = ROOT / "stdlib" / "linux.sl" def _sanitize_alias(alias: str) -> str: name = alias.strip() if not name: return "" if name.startswith("__x64_sys_"): name = name[len("__x64_sys_") :] elif name.startswith("sys_"): name = name[len("sys_") :] name = re.sub(r"[^A-Za-z0-9_]", "_", name) name = re.sub(r"_+", "_", name).strip("_") if not name: return "" if name[0].isdigit(): name = "n_" + name return name @dataclass(frozen=True) class SyscallEntry: argc: int num: int aliases: tuple[str, ...] def _parse_table(path: Path) -> list[SyscallEntry]: entries: list[SyscallEntry] = [] for raw in path.read_text(encoding="utf-8").splitlines(): line = raw.strip() if not line or line.startswith("#"): continue parts = line.split(maxsplit=2) if len(parts) < 3: continue try: argc = int(parts[0]) num = int(parts[1]) except ValueError: continue aliases = tuple(a for a in parts[2].split("/") if a) if not aliases: continue entries.append(SyscallEntry(argc=argc, num=num, aliases=aliases)) return entries def _emit_header(lines: list[str]) -> None: lines.extend( [ "# Autogenerated from syscall_64.tbl", "# Generated by tools/gen_linux_sl.py", "# Linux syscall constants + convenience wrappers for L2", "", "# File descriptor constants", "macro FD_STDIN 0 0 ;", "macro FD_STDOUT 0 1 ;", "macro FD_STDERR 0 2 ;", "", "# Common open(2) flags", "macro O_RDONLY 0 0 ;", "macro O_WRONLY 0 1 ;", "macro O_RDWR 0 2 ;", "macro O_CREAT 0 64 ;", "macro O_EXCL 0 128 ;", "macro O_NOCTTY 0 256 ;", "macro O_TRUNC 0 512 ;", "macro O_APPEND 0 1024 ;", "macro O_NONBLOCK 0 2048 ;", "macro O_CLOEXEC 0 524288 ;", "", "# lseek(2)", "macro SEEK_SET 0 0 ;", "macro SEEK_CUR 0 1 ;", "macro SEEK_END 0 2 ;", "", "# mmap(2)", "macro PROT_NONE 0 0 ;", "macro PROT_READ 0 1 ;", "macro PROT_WRITE 0 2 ;", "macro PROT_EXEC 0 4 ;", "macro MAP_PRIVATE 0 2 ;", "macro MAP_ANONYMOUS 0 32 ;", "macro MAP_SHARED 0 1 ;", "", "# Socket constants", "macro AF_UNIX 0 1 ;", "macro AF_INET 0 2 ;", "macro AF_INET6 0 10 ;", "macro SOCK_STREAM 0 1 ;", "macro SOCK_DGRAM 0 2 ;", "macro SOCK_NONBLOCK 0 2048 ;", "macro SOCK_CLOEXEC 0 524288 ;", "", "macro INADDR_ANY 0 0 ;", "", "# Generic syscall helpers with explicit argument count", "# Stack form:", "# syscall -> ... syscall", "# syscallN -> ... syscallN", "", "# swap impl is provided so this can be used without stdlib", "# ___linux_swap [*, x1 | x2] -> [*, x2 | x1]", ":asm ___linux_swap {", " mov rax, [r12]", " mov rbx, [r12 + 8]", " mov [r12], rbx", " mov [r12 + 8], rax", "}", ";", "", "macro syscall0 0", " 0", " ___linux_swap", " syscall", ";", "", "macro syscall1 0", " 1", " ___linux_swap", " syscall", ";", "", "macro syscall2 0", " 2", " ___linux_swap", " syscall", ";", "", "macro syscall3 0", " 3", " ___linux_swap", " syscall", ";", "", "macro syscall4 0", " 4", " ___linux_swap", " syscall", ";", "", "macro syscall5 0", " 5", " ___linux_swap", " syscall", ";", "", "macro syscall6 0", " 6", " ___linux_swap", " syscall", ";", "", ] ) def _emit_entry(lines: list[str], alias: str, argc: int, num: int) -> None: safe_argc = max(0, min(argc, 6)) lines.extend( [ f"macro syscall.{alias} 0", f" {num}", f" syscall{safe_argc}", ";", "", f"macro syscall.{alias}.num 0", f" {num}", ";", "", f"macro syscall.{alias}.argc 0", f" {safe_argc}", ";", "", ] ) def generate() -> str: entries = _parse_table(SRC) lines: list[str] = [] _emit_header(lines) emitted: set[str] = set() for entry in sorted(entries, key=lambda e: (e.num, e.aliases[0])): for alias in entry.aliases: name = _sanitize_alias(alias) if not name: continue key = f"syscall.{name}" if key in emitted: continue _emit_entry(lines, name, entry.argc, entry.num) emitted.add(key) return "\n".join(lines).rstrip() + "\n" def main() -> None: output = generate() DST.parent.mkdir(parents=True, exist_ok=True) DST.write_text(output, encoding="utf-8") print(f"wrote {DST}") if __name__ == "__main__": main()