Basalt
Basalt is the SaltyOS C and C++ standard library stack. It contains two cooperating runtimes shipped together:
- basaltc
-
A POSIX-compatible C standard library written in Rust, built as
libc.so. 44 modules covering stdio, unistd, pthread, sockets, time, math, regex, locale, dynamic linking, and a FreeBSD compatibility layer. Every system operation is delegated to trona (trona_posix), which talks to userland servers via kernel IPC. - libc++
-
The LLVM C runtime, built in-tree from the `toolchain/llvm-project` submodule against basaltc. Produces `libc.so` (libcxx + libcxxabi + libunwind, all linked into one shared library) plus a SaltyOS
__config_sitethat pins the configuration: C-locale only, exceptions on, RTTI off in libcxx, hardening off,getentropy()random backend.
Both libraries are loaded by the runtime dynamic linker ld-trona.so at process startup.
Static C runtime objects (crt_start.o, setjmp.o) are linked directly into application binaries instead of libc.so.
At a Glance
Languages |
Rust 2024 (basaltc), C/C23 (libc) |
|---|---|
basaltc modules |
44 top-level Rust modules (~26,000 lines) |
Public C headers |
118 in |
Architectures |
x86_64, aarch64 |
Output artifacts |
|
Static dependencies |
|
Threading |
POSIX threads via |
Locale |
C locale only (Fuchsia model) |
Exceptions |
Enabled in libc++ via libunwind DWARF EH |
RTTI |
Disabled in libcxx, enabled in libcxxabi (where the implementation lives) |
Components
The component split exists for two reasons.
First, crt_start.o defines _start and resolves main, so it must be statically linked into each binary — leaving it inside libc.so would create an unresolved main reference in the shared library.
Second, libc depends on basaltc but basaltc must remain free of C runtime requirements, so the two libraries cannot share a translation unit.
New to SaltyOS Userland?
Start with the SaltyOS overview to see how userland servers, the C library, and the kernel relate. Then come back here for the basalt-specific guides:
-
Porting a C Program — walk a FreeBSD utility through the SaltyOS port system.
-
Adding a libc Function — extend basaltc with a new POSIX or BSD function.
-
First Contribution — build basalt locally and submit your first patch.
Technical Reference
-
For the source layout, module list, and dependency direction, read Architecture.
-
For the build pipeline that produces
libc.soandlibc++.so, read Build System. -
For how a process actually starts running, read CRT Startup.
-
For the heap allocator and
errnostorage, read Heap and malloc and trona Boundary. -
For the dispatch mechanism that picks SSE2 or NEON math/string backends at compile time, read Multi-Architecture Dispatch.
-
For the libc build composition and `__config_site` flags, read xref:cxx/cxx-runtime.adoc[libc Runtime] and libc++ Configuration.
Design Philosophy
Three principles shape basalt:
- Thin C ABI over a Rust core
-
Every public C function is a
#[unsafe(no_mangle)] pub extern "C"Rust function. Pointer translation, errno mapping, and option parsing live in Rust; the C side has zero hand-written bridge code. This gives strong type checking inside the library while presenting a familiar C interface to ports. - Delegate, do not reimplement
-
basaltc does not own filesystem state, process tables, network stacks, or signal queues. Those live in userland servers and are reached through
trona_posix. basaltc is a thin shim that converts C calling conventions to IPC. The exceptions are pure-data modules (regex, iconv, sha512, ctype, getopt) and arch-optimized primitives (string, mem, math) where there is no system state to delegate to. - Take libc++ from upstream, configure it explicitly
-
The C++ runtime is the unmodified LLVM in-tree libcxx/libcxxabi/libunwind. SaltyOS contributes a single compile-time header (
__config_site) and a Meson build that compiles each.cppdirectly viacustom_target, so there is no upstream CMake to track. Configuration choices that diverge from defaults (locale backend, hardening, random source) are pinned in that one file.