Skip to content

KarpelesLab/purestd

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

purestd

A std written in pure Rust that never calls libc. Every operation — files, time, env, threads, the allocator — is a direct kernel syscall (svc/syscall). Built on core + alloc, purestd provides exactly what a real std provides (the panic handler, the global allocator, I/O, the std-shaped API) and keeps Rust's guarantees intact all the way down to the syscall instruction.

purestd is only std. The process entry point (_start) and the mem*/unwind symbols are not its concern — in a normal build they come from crt0 and libc, and in a libc-free build from the fullrust toolchain. So purestd builds and runs fine as an ordinary no_std library with libc present (which is how it's tested here); it simply never calls into libc.

Zero third-party dependencies. purestd is built on core + alloc and nothing else — it implements its own hash map, hasher, allocator, and OS layer. cargo tree shows only purestd.

#![no_std]
#![no_main]

use purestd::prelude::*;

fn main() {
    println!("hello from purestd");
}

purestd::entry!(main);

entry! emits the standard C main symbol, so the program is started the ordinary way: by crt0 in a normal build, or by the fullrust toolchain in a libc-free build. Either caller passes argc/argv/envp, which purestd records for env before your main runs.

libc or not

  • As a normal library (how this repo is tested): an ordinary cargo build. libc supplies the C runtime (_start, memcpy, …); purestd supplies the panic handler, the global allocator, and rust_eh_personality, and does all of its own work via raw syscalls. The binary links libc but purestd never calls it.
  • Libc-free / fully static: build through fullrust, which supplies the entry point and the mem*/unwind symbols itself, producing a static binary with no libc at all. That path lives in the fullrust repo, not here.

Targets

Target Tested in CI
aarch64-apple-darwin build + run
x86_64-unknown-linux-gnu build + run
aarch64-unknown-linux-gnu build + run

The only architecture/OS-specific code lives in src/arch/: one file per target with the raw syscallN wrappers and the syscall number table. Everything above it is OS-neutral.

Layout

arch/      raw syscall wrappers + number table (per target)
syscall    arch-neutral, Result-returning wrappers (Errno)
allocator  mmap-backed segregated free-list (#[global_allocator])
panic      the #[panic_handler] + rust_eh_personality
rt         exit/abort + Termination + the entry! glue
io fs env process time sync path ffi error net thread   ← the std surface
hash collections   ← own SipHash-1-3 + HashMap/HashSet

core and alloc are re-exported under std-shaped paths (mem, cmp, fmt, vec, collections, …), so when purestd is aliased as std for a freestanding target, ordinary use std::io::Write; / std::fs::read(..) / HashMap code resolves here unchanged.

The rt feature (default on)

Gates the std-provided policy symbols — the #[panic_handler], the #[global_allocator] static, and rust_eh_personality. Disable it (default-features = false) when a host runtime supplies those instead. The mechanisms (syscalls, the allocator type, the std surface) are always available.

Building

cargo run --example stdshow -- some args   # any example, normal build
cargo build --examples

Status

Working: process entry/exit, panic, mmap allocator, args/env, io (Read/ Write/Error/std streams), fs (File/OpenOptions/read/write), process, time, sync (Mutex/RwLock/Once/OnceLock), path, ffi, hash (own SipHash-1-3 + RandomState seeded from getrandom/getentropy; verified against the canonical reference vectors — cargo run --example sipcheck), collections (own open-addressing HashMap/HashSet + the alloc containers).

Threads are real OS threads: thread::spawn/JoinHandle::join/Builder, sleep, yield_now, futex-based join. On macOS they use Mach thread_create_running (no libpthread — see docs/macos-threads.md); on Linux they use clone + futex. Exercised on every target in CI.

net: TcpStream, TcpListener, UdpSocket, and ToSocketAddrs over raw socket syscalls, with name resolution from /etc/hosts plus plain DNS (/etc/resolv.conf, A/AAAA over UDP) — no NSS.

The parity-critical std surface is implemented and exercised in CI on macOS arm64 and Linux x86_64/aarch64: io/fs/env/process (incl. process::Command), time, sync (all locks futex-backed, Condvar, mpsc, …), thread (incl. scope and park/unpark), thread_local!, net, path, collections, and os::fd/os::unix. What remains is optional hardening and platform breadth (OS-level thread names, TLS destructors, DNS hardening, more targets) — none of it blocks aliasing purestd as std.

License

MIT OR Apache-2.0.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages