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_site that 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 lib/basalt/c/include/

Architectures

x86_64, aarch64

Output artifacts

libc.so, libc++.so, crt_start.o, setjmp.o

Static dependencies

libtrona.so, libtrona_posix.so, compiler-rt builtins

Threading

POSIX threads via trona_posix::pthread (futex-based)

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

Diagram

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:

Technical Reference

  • For the source layout, module list, and dependency direction, read Architecture.

  • For the build pipeline that produces libc.so and libc++.so, read Build System.

  • For how a process actually starts running, read CRT Startup.

  • For the heap allocator and errno storage, 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 .cpp directly via custom_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.