# relibc is a C standard library written in Rust, built for Redox

> relibc replaces newlib as the C library for the Redox operating system, implementing the C, POSIX, Linux and BSD interfaces in Rust and exposing them to C through generated headers. The design decisions worth knowing are the header generation pipeline, the unusually strict lint policy on unsafe code, and the fact that the Linux support comes through a separate crate rather than being a first class target.

**redox-os/relibc** — Mirror of https://gitlab.redox-os.org/redox-os/relibc

- Repository: https://github.com/redox-os/relibc
- Stars: 1,019 · Forks: 74
- Language: Rust
- License: MIT
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/redox-os-relibc

## Why a new libc exists at all

relibc is a portable C standard library written in Rust, and it is under heavy development. It contains C, Linux, BSD functions and extensions, a POSIX compatibility layer, and interfaces for system components.

The motivation is stated as twofold. The first is to reduce issues that the Redox developers were having with newlib, which is the traditional C library for embedded targets and which Redox had outgrown. The second is to create a more stable and safe alternative to C standard libraries written in C.

That second half is the interesting part and the reason the project is written in Rust rather than in C. A C library that is written in Rust can have its own memory safety, so the bugs that a C library would contain, a buffer overflow in a string function, a use after free in a memory allocator, become much harder to write. For an operating system that wants to be a research vehicle for a new kernel and userland, having a libc whose implementation is not itself a source of memory corruption bugs is a real design goal rather than a style preference.

The project is mainly designed to be used under Redox, as an alternative to newlib, and it also supports Linux through the sc crate. Currently Redox and Linux are supported, and the supported architectures are i586, x86_64, aarch64 and riscv64gc.

One practical note about where the code lives. This GitHub repository is a mirror of the one on the project's own GitLab, and the download instructions point at the GitLab URL with a recursive clone. That is the canonical location, and it is worth knowing before you file an issue or a pull request against the mirror.

## How a Rust library becomes a C library

The repository layout is the best explanation of the mechanism, because a C library written in Rust is not a single thing.

The include/ directory holds the header files, and the documentation notes that these are mostly macros and variadic functions that cbindgen cannot generate. That single sentence describes the whole problem: cbindgen reads Rust and writes C declarations, and it cannot express a variadic function or a macro, so those parts of the interface are hand written and live in include/.

The src/ directory holds the source, and src/header/ holds the implementation of the headers, with each header directory containing a cbindgen.toml file that generates a C to Rust interface and the header files themselves. The Makefile shows the loop: it finds the header directories, skips the ones starting with an underscore, and for each one that has a cbindgen.toml it concatenates that file with the global cbindgen.globdefs.toml and runs cbindgen over the module to write the header out. The underscore convention is how the private, non exported headers are marked.

Then there are the parts of a libc that are not library functions at all. src/crt0, src/crti and src/crtn are the runtime startup code, the C runtime init and fini pieces, and each is a workspace member of its own. src/ld_so/ is the dynamic loader, and src/platform/ holds the platform specific and common code, with src/platform/redox/ and src/platform/linux/ as the two backends. src/pthread/ is the pthread implementation and src/sync/ holds the synchronisation primitives.

The Cargo manifest makes the shape concrete. The package builds as a staticlib, and the workspace members are the three crt directories plus redox-rt, ld_so and generic-rt, while the tests and dlmalloc-rs are excluded from the workspace. The version is 0.2.5, the edition is 2024, and the author is Jeremy Soller.

The vendored dependencies at the repository root, dlmalloc-rs for the allocator, openlibm for the math library, redox-ioctl for the ioctl interface, and generic-rt, are the pieces that would otherwise come from a system, and they are part of the repository because a libc cannot depend on a system libc.

## The lint policy is the safety argument

The Cargo manifest carries a long list of workspace lints, and reading it tells you what the maintainers consider unacceptable in a C library written in Rust. The denied list is where the interesting decisions are.

unsafe_op_in_unsafe_fn is denied, which means every unsafe operation inside an unsafe function still has to be wrapped in an explicit unsafe block. That is the lint that stops unsafe code from quietly spreading, and denying it in a libc is a strong statement about how the code is meant to be reviewed.

The pointer lints are denied as a group: borrow_as_ptr, ptr_as_ptr, ptr_cast_constness, ptr_offset_by_literal, ref_as_ptr, mut_from_ref, from_raw_with_void_ptr, zero_ptr and dangling_pointers_from_temporaries. Together they close the routes by which a Rust pointer is laundered into a C pointer, and the last of them, dangling_pointers_from_temporaries, catches a class of bug that is otherwise very easy to write in FFI code.

The correctness lints are denied too: deprecated, improper_ctypes_definitions, irrefutable_let_patterns, mismatched_lifetime_syntaxes, unexpected_cfgs, unpredictable_function_pointer_comparisons, implicit_clone, manual_let_else, precedence and unreachable_code is allowed rather than denied. Several of the allowed ones carry a comment marking them for review, and one, zero_ptr, has a note that it must be allowed on public constants because of a cbindgen issue, which is a nice detail: a lint relaxed for the sake of a code generator is documented rather than hidden.

The naming lints are relaxed on purpose. non_camel_case_types, non_snake_case and non_upper_case_globals are allowed, with the comment that it is needed for most POSIX type names. That is a deliberate trade: the C API has to look like C, so the Rust inside it is allowed to look like C too.

Read together, the lint list is the strongest technical statement in the repository, and it is a statement about the specific hazards of writing a libc in Rust rather than a generic code style.

## redox-rt, and the POSIX layer that is not part of the library

One component is separated out because it is useful on its own, and the documentation explains why.

redox-rt is a runtime library that provides much of the code that enables POSIX on Redox, like fork, exec and signal handling. relibc uses it as a backend in src/platform/redox, and it is intended to eventually be usable independently, without relibc.

That separation is the interesting design point. A C library provides functions, but a POSIX environment needs more than functions: it needs process creation, program execution, signal delivery and the semantics that tie them together, and those live in the kernel and in the startup runtime rather than in the library. By putting them in a separate crate, the project makes two things possible that would otherwise be tangled together. The runtime can be reused by a program that is not using relibc, and relibc can be tested against a host system where the Linux platform layer provides the same operations.

The funding section says as much. The project is funded through NGI Zero Core, a fund established by NLnet with financial support from the European Commission's Next Generation Internet programme, and the funded work is specifically Unix-style signals and process management, which is exactly the runtime layer described in the previous paragraph. So the separation of redox-rt is not an aesthetic choice but the funded unit of work.

For a user of the library, the practical consequence is that the POSIX surface is partly provided by the runtime rather than by relibc itself, and that the two are versioned and shipped together.

## Building it, and the Redox build system integration

Building relibc takes two dependencies that are not obvious. The first is cbindgen, the header generator:

```sh
cargo install cbindgen
```

The second is the expect tool, and the documentation gives the install command for four distributions because the build uses it. On Debian, Ubuntu and PopOS it is apt install expect, on Fedora it is dnf install expect, on Arch Linux it is pacman -S expect, and on Void Linux it is xbps-install -S expect.

With those in place, the library objects are built with:

```sh
make all
```

and old objects and tests are cleared with make clean.

There is a second, separate path for people working inside the Redox build system, where relibc is built as part of a distribution. Inside a Redox build system you run make prefix, and to force a rebuild for testing a cookbook recipe you touch the relibc directory and run make prefix with the recipe name. The reason for the touch is that it changes the last modified time of the folder, which is what triggers recompilation. The documentation also warns, in as many words, not to edit relibc inside the prefix folder, and to do the work in the relibc folder directly inside the build system instead.

The Makefile shows how the pieces fit together. It includes config.mk for the target configuration, passes a nightly cargo flag to build the standard library from source, which is what a libc needs since it cannot use the one it is replacing, and defaults the link flags to the C compiler runtime. There is an option to use a Rust implementation of the math library instead of the vendored openlibm, which switches on a feature and copies different headers. The header generation loop is driven from the Makefile rather than from a script, and the source list is assembled by finding files across src/, redox-rt/src/, ld_so/src/, redox-ioctl/src/ and include/.

## The C test suite runs on Redox, on Linux and on four architectures

Testing is where a libc proves itself, and relibc's test suite is written in C, with the rule stated plainly in the repository layout: each merge request needs to give success in all of them.

The suite runs every time a new commit is pushed, and the configuration for that is in the .gitlab-ci.yml. For local work, the recommended way to run it is a check script, and the documentation lists the options that matter. Running it with the test flag runs all the tests in x86_64 Redox using Redoxer, and adding the host flag runs all the tests on the host, which is Linux. The architecture flag runs the tests for a specified architecture, and the architectures on offer are x86_64, aarch64, i586 and riscv64gc. A single test can be selected by name, such as the stdio printf test, and that can be combined with the host or architecture flags, and the difference in linking is spelled out: the test runs statically linked in Linux and dynamically linked in Redox.

There are three practical notes. The library and its tests rebuild when files change, but switching between architectures or between host and target requires a clean first. Redoxer is needed to run the Redox tests without the host flag, and it is installed with cargo install redoxer. And the tests can hang, with the runner able to anticipate that, on the assumption that the kernel has not hung too, which is a candid statement of the state of the art in testing an operating system's userland.

The issue section of the readme shows the kind of support that actually matters to a new contributor. Building for a platform you also run can fail with a message about the prefixed compiler tools not being found, because the Makefile expects GNU tools prefixed with the platform specifier, as a cross compiler would provide them, and some distributions do not install the prefixed executables. The fix is given as a diff to config.mk, replacing the prefixed compiler, linker, archiver and nm with the plain ones for your own platform.

## Conclusion

relibc suits a developer working on the Redox operating system, or on a project that wants a C library with memory safety guarantees in its implementation rather than only at its edges, and it suits anyone studying how a libc is put together from a modern systems language. It is the wrong choice if you want a drop-in libc for a Linux distribution, because Linux support goes through a separate crate and the primary target is Redox, and a poor match if you need a complete, stable POSIX surface today, because the project describes itself as under heavy development. Before contributing, read the contributing document, install the header generator and the expect tool the build expects, and remember that the GitHub repository is a mirror and that the canonical one is on the project's own GitLab.

## FAQ

### What is relibc and why does Redox need its own C library?

relibc is a portable C standard library written in Rust, containing C, Linux and BSD functions and extensions, a POSIX compatibility layer and interfaces for system components. The stated motivations are to reduce the issues Redox had with newlib and to provide a more stable and safe alternative to C libraries written in C.

### Which platforms and architectures does relibc support?

Redox OS and Linux are supported, and the supported architectures are i586, x86_64, aarch64 and riscv64gc. Linux support comes through the sc crate, and the project is mainly designed for use under Redox.

### How are the C headers for relibc generated?

With cbindgen. Each header directory under src/header has a cbindgen.toml file that generates the C to Rust interface and the header file, combined with the global cbindgen.globdefs.toml. Headers that cbindgen cannot express, such as macros and variadic functions, are kept by hand in the include directory.

### How do you run the relibc test suite?

The recommended way is the check script: with the test flag for all the tests in x86_64 Redox using Redoxer, with the host flag to run them on Linux, with the architecture flag to pick x86_64, aarch64, i586 or riscv64gc, or with a test name to run a single test such as stdio/printf. Switching architecture or host requires a make clean first, and the tests can hang.

## Sources

- [Issues](https://github.com/redox-os/relibc/issues)
- [License: MIT](https://github.com/redox-os/relibc/blob/master/LICENSE)
- [README](https://github.com/redox-os/relibc/blob/master/README.md)
- [redox-os/relibc on GitHub](https://github.com/redox-os/relibc)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/redox-os-relibc
