feat(syscall): add x86_64 raw syscall layer, errno, internal ABI headers

This commit is contained in:
2026-09-03 17:10:27 -04:00
parent 6854c7771d
commit c5a77c61fa
9 changed files with 1139 additions and 0 deletions
+115
View File
@@ -0,0 +1,115 @@
#ifndef VLIBC_INTERNAL_ARCH_X86_64_SYSCALL_ARCH_H
#define VLIBC_INTERNAL_ARCH_X86_64_SYSCALL_ARCH_H
/*
* vlibc — x86_64 raw syscall entry (arch layer).
*
* The System V AMD64 kernel ABI: the syscall number goes in rax, arguments in
* rdi, rsi, rdx, r10, r8, r9 (this order; the fourth argument is r10, NOT
* rcx), and the kernel clobbers rcx and r11. The kernel returns the raw
* result in rax: a non-negative value on success, or -errno on error. Callers
* funnel rax through syscall_ret() (see src/internal/syscall.h), which
* translates the negative-errno convention into C return/-1 + errno.
*
* These wrappers take `long` arguments and return `long` so that pointer-sized
* syscall arguments (addresses, off_t) pass through without truncation; the
* kernel reads exactly the low 64 bits of each register, so the upper bits of
* `long` arguments are irrelevant.
*
* Each wrapper is `always_inline` so that even a -O0 build collapses to a
* bare `syscall` instruction with no call frame. The "memory" clobber tells
* the compiler the syscall may read or write anything the caller can see,
* which is always the safe assumption.
*/
/*
* The __syscall<n> names sit in the implementation-reserved namespace (they
* are this libc's private kernel-ABI seam, never public API), and every
* argument is a `long` because that is the shape of the x86_64 syscall
* register file — both properties are intentional, so the corresponding
* bugprone checks are waived per function.
*/
// NOLINTBEGIN(bugprone-easily-swappable-parameters,bugprone-reserved-identifier)
static inline long __attribute__((always_inline))
__syscall0(long n)
{
unsigned long ret;
__asm__ volatile("syscall" : "=a"(ret) : "a"(n) : "rcx", "r11", "memory");
return (long)ret;
}
static inline long __attribute__((always_inline))
__syscall1(long n, long a)
{
unsigned long ret;
__asm__ volatile("syscall" : "=a"(ret) : "a"(n), "D"(a) : "rcx", "r11", "memory");
return (long)ret;
}
static inline long __attribute__((always_inline))
__syscall2(long n, long a, long b)
{
unsigned long ret;
__asm__ volatile("syscall" : "=a"(ret) : "a"(n), "D"(a), "S"(b) : "rcx", "r11", "memory");
return (long)ret;
}
static inline long __attribute__((always_inline))
__syscall3(long n, long a, long b, long c)
{
unsigned long ret;
__asm__ volatile("syscall"
: "=a"(ret)
: "a"(n), "D"(a), "S"(b), "d"(c)
: "rcx", "r11", "memory");
return (long)ret;
}
static inline long __attribute__((always_inline))
__syscall4(long n, long a, long b, long c, long d)
{
unsigned long ret;
__asm__ volatile("syscall"
: "=a"(ret)
: "a"(n), "D"(a), "S"(b), "d"(c), "r"(d)
: "rcx", "r11", "memory");
return (long)ret;
}
static inline long __attribute__((always_inline))
__syscall5(long n, long a, long b, long c, long d, long e)
{
unsigned long ret;
__asm__ volatile("syscall"
: "=a"(ret)
: "a"(n), "D"(a), "S"(b), "d"(c), "r"(d), "r"(e)
: "rcx", "r11", "memory");
return (long)ret;
}
static inline long __attribute__((always_inline))
__syscall6(long n, long a, long b, long c, long d, long e, long f)
{
unsigned long ret;
register long r10 __asm__("r10") = d;
register long r8 __asm__("r8") = e;
register long r9 __asm__("r9") = f;
__asm__ volatile("syscall"
: "=a"(ret)
: "a"(n), "D"(a), "S"(b), "d"(c), "r"(r10), "r"(r8), "r"(r9)
: "rcx", "r11", "memory");
return (long)ret;
}
// NOLINTEND(bugprone-easily-swappable-parameters,bugprone-reserved-identifier)
#endif /* VLIBC_INTERNAL_ARCH_X86_64_SYSCALL_ARCH_H */