Skip to content

Latest commit

 

History

18 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

rcrt — A minimal C runtime written in Rust

Implement a tiny C runtime (crt) — the low-level code that sits between the OS kernel and a C program's main() — built entirely in Rust with no dependency on the system libc.

Motivation

Learning how programs are linked and what happens before main() is called. This project replaces glibc/musl's startup code, heap allocator, and basic I/O with Rust equivalents, linked into a standalone static library.

Architecture

┌──────────────────────────┐
│     C user program       │  csrc/test.c — calls printf, fopen, malloc, etc.
├──────────────────────────┤
│    C variadic shim       │  csrc/shims.c — variadic printf/fprintf/sprintf/snprintf
├──────────────────────────┤
│  ┌──────────────────────┐│
│  │  entry  (_start)     ││  ELF entry point → calls main(argc, argv, envp)
│  ├──────────────────────┤│
│  │  exit (atexit/abort) ││  exit handlers, _Exit, SIGABRT
│  ├──────────────────────┤│
│  │  heap (malloc/free)  ││  First-fit free-list allocator, backed by mmap
│  ├──────────────────────┤│
│  │  fs (FILE stdio)     ││  Buffered fopen/fread/fwrite/fflush/fseek, etc.
│  ├──────────────────────┤│
│  │  io (printf family)  ││  Formatted output: vprintf/vfprintf/vsprintf/vsnprintf
│  ├──────────────────────┤│
│  │  string (mem/str)    ││  strlen/strcpy/memcpy/memset/strcmp, etc.
│  ├──────────────────────┤│
│  │  sys (Linux layer)   ││  Raw syscall wrappers: read/write/mmap/exit, etc.
│  └──────────────────────┘│
├──────────────────────────┤
│     Linux kernel         │
└──────────────────────────┘

Supported platforms

GNU/Linux in x86_64/arm64.

make build-linux-x86
make build-linux-arm

Build & test

Required

  • Rust toolchain (nightly not required)
  • C compiler (for variadic shim and test programs)
  • make

Build and link test demo

make test
./target/test_prog

The test binary is linked with -nostartfiles -nodefaultlibs — no glibc involved.

Manual linking

# Build Rust static lib
cargo build --release

# Compile C code
cc -c csrc/shims.c -o shims.o
cc -c my_prog.c -o my_prog.o

# Link — must provide our own _start, no libc
cc -nostartfiles -nodefaultlibs \
    my_prog.o shims.o target/release/libcrt_rs.a \
    -o my_prog

Why variadic shims in C?

Rust cannot define C variadic functions (...). The C file csrc/shims.c provides thin wrappers that unpack va_list and call the Rust-implemented vprintf/vfprintf/vsprintf/vsnprintf functions.

Similarly, C expects stdin/stdout/stderr as macros. The header csrc/shims.h defines them as calls to crt_stdin()/crt_stdout()/crt_stderr() which return *mut FILE pointers to the Rust-managed static FILE objects.

Format specifiers

The printf engine supports: %d %i %u %x %X %o %s %c %p %% with flags - (left-align) and 0 (zero-pad) and minimum field width.

Internal design

Heap allocator

  • First-fit free-list search
  • Block coalescing on free()
  • Backed by anonymous mmap
  • Alignment: 16 bytes (64-bit) / 8 bytes (32-bit)
  • Minimum block data size: 16 bytes

FILE buffering

  • Per-stream 1024-byte buffer
  • Dirty-flag tracking for deferred writes
  • Read buffering: batch reads into buffer, serve from buffer
  • Write buffering: accumulate in buffer, flush when full or on fflush()/fseek()

Unsafe code usage

Unsafe is confined to:

  • Raw syscall invocations (inline assembly)
  • extern "C" function interfaces receiving raw pointers from C
  • static mut access for the heap free-list, FILE pool, and atexit registry
  • Raw pointer dereference in string operations called from C

Internal helpers use safe Rust where possible — e.g., slice indexing instead of pointer arithmetic in int_to_str, &mut [u8] instead of *mut u8.

Limitations

  • No thread safety (no locks, no errno, no thread-local storage)
  • No signal handling
  • No environment variable access (getenv)
  • No longjmp/setjmp
  • No locale support
  • No floating-point formatting in printf
  • No tmpfile/tmpnam
  • Linux-only syscall module
  • Single-threaded only

License

MIT

About

A tiny CRT for c built in rust.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages