116 lines
3.7 KiB
C
116 lines
3.7 KiB
C
#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 */
|