Linux Syscall Support

Linux’s stable, language-agnostic system call interface is contrasted with platforms like OpenBSD and macOS, where direct syscalls are considered unstable and libc is the supported boundary. Commenters explore why projects such as Chrome, Go, and Zig bypass traditional C libraries to talk to the kernel directly—citing bootstrapping new languages, avoiding dynamic linking, tighter control over performance, and reduced dependencies—while others highlight trade-offs around ABI compatibility, features like NSS, ASLR, and the complexity of error handling via `errno`. The thread also compares minimalist alternatives like Linux’s `nolibc` to full-featured libcs such as glibc and musl, noting that very thin syscall layers are powerful but incomplete and demand extra care with calling conventions and evolving kernel interfaces.

Use Cases for Direct Syscalls / nolibc-like Layers

  • Used to build freestanding or near-bare-metal systems: actor frameworks, toy OSes, Lisp interpreters, and new languages without pulling in full glibc.
  • Helpful for bootstrapping new languages and runtimes where a C ABI and full libc are undesired.
  • Also used for special threading models (e.g., raw clone() threads for real-time audio or high-performance I/O) where libc/pthreads are unsafe or too heavy.

Linux vs Other OSes: Syscall ABI Stability

  • Linux guarantees a stable syscall ABI; many see this as a deliberate design choice enabling language-agnostic system interfaces.
  • *BSDs and macOS often treat direct syscalls as unstable/private and expect everyone to go through libc; OpenBSD in particular is explicit about syscall ABI instability.
  • On OpenBSD, static binaries may work only when built against the matching release; future compatibility is not guaranteed.

Pros and Cons of Bypassing libc

  • Claimed advantages:
    • No dependency on libc, C ABI, or dynamic linking.
    • Smaller, simpler binaries; good for static, freestanding, or kernel-adjacent use.
    • Potentially reduced attack surface vs. large libc implementations.
  • Counterpoints:
    • Function-call overhead is negligible vs syscall cost; libc brings useful optimizations and tracks kernel best practices.
    • You must maintain syscall mappings yourself and handle syscall evolution.
    • Losing ASLR for code when avoiding relocatable binaries is a real trade-off, even if ASLR is viewed as a “weak” defense by some.
    • Some OS features (vDSO, NSS, name service) are harder or fragile without libc.

nolibc, Chromium’s Library, and Completeness

  • Linux’s nolibc is minimal by design (e.g., no pread/pwrite, basic printf, no threads).
  • This leaves room for richer syscall layers like Chromium’s, tuned for large projects.
  • nolibc is MIT/LGPL-2.1 licensed and lives under tools/, not as a core kernel header.
  • Some see an “official” minimal layer as obsoleting third-party ones; others argue scale- and project-specific needs still justify custom implementations.

Language Ecosystem Examples

  • Go and Zig use direct syscalls on Linux (with caveats on BSD/macOS).
  • A Lisp-like language is described that boots as a freestanding interpreter using syscalls and self-contained ELF tricks.
  • Golang’s original syscall package is “frozen”; a newer x/sys/unix package tracks new syscalls.

Error Handling and errno

  • Some criticize persisting with errno-style global/thread-local error reporting in new syscall wrappers, preferring return-based (Result-style) errors.
  • Others argue compatibility and “drop-in” behavior with libc justify keeping errno, despite its threading/signal complexities.

Technical Pitfalls and Gotchas

  • syscall() in C is vararg; naïve use can mis-pass 6th+ arguments on some ABIs if types aren’t widened correctly.
  • Direct use of fork/clone-like syscalls can conflict with threads created via glibc/pthreads.
  • Some libc and wrappers transparently map deprecated syscalls like open to openat, which can surprise tools or seccomp profiles.