This is a super experimental attempt at re-implementing musl in Rust.
If you'd like to run the tests (right now rusl only support x86_64 Linux), clone the repo and run:
scripts/clean_all.sh && scripts/build_and_test.sh
If you want to contribute -- thanks, I'm flattered! Please be aware that this just a hobby project, so reviews may not be very rapid. Also, while CI is configured, please please please make sure your code builds and tests pass using a recent nightly on an x86_64 Linux box/VM/VPS. It will save a lot of time before the submission gets to the review phase.
CI is now set up for Pull Requests, so that needs to pass before a PR is reviewed. There aren't enough contributions right now to write a style guide, but before submitting anything please read some of the existing code to get a feeling for the current style before submitting anything, and also run rustfmt on your submission using this repository's configuration. Since this is just my hobby project at the moment, please be patient if I nitpick your PR -- there are no practical goals here so I'd like to keep things nice (even if it's at the expense of time spent or contributions lost).
Please see issue #6 if you'd like help figuring out a starting point for contributions.
Tips & Caveats
As you port musl functions to Rust, you'll eventually expose one or more
rusl. For example,
src/malloc/malloc.rs. Other (non-public,
malloc.c might get ported but won't necessarily be exposed as
public. With new
librusl.a will contain symbols which
conflict with object files in musl's
libc.a, and the build will fail. The
solution is to put the name of the object file with the conflicting symbols in a
ported_objects living in the root of the repo. It describes all
the object files which should be deleted from musl's
libc.a so we don't get
symbol conflicts during linking. One small caveat is that a few functions
memcmp, and friends) are provided by
rusl, but we
still need to remove the symbols from musl's
libc.a before mixing in
Effectively, this means you should not expose new
public functions in
until an entire C source file has been ported. Don't worry; let build failures
be your guide.
You can use
nm bld/usr/lib/libc.a | less to verify the object filename
providing a given symbol. Sometimes they don't match (i.e.,
Since musl doesn't include documentation itself, this project won't make any effort to add new documentation beyond any comments necessary to clarify implementation-specific behavior. For documentation of C language functions and the POSIX C standard library, see:
A little background
musl is, according to its homepage, "a new standard library to power a new generation of Linux-based devices. musl is lightweight, fast, simple, free, and strives to be correct in the sense of standards-conformance and safety."
Rust is, according to its homepage, "a systems programming language that runs blazingly fast, prevents segfaults, and guarantees thread safety."
Seems like a nice match! In fact, there's been some great work done to make Rust executables statically compile with musl as the libc implementation on Linux, allowing for fully static Rust Linux binaries (a la Go). musl has also been cited as an excellent codebase for someone to read when learning C. But wait, if musl's C code is already so great, why rewrite it in Rust?
- Because I don't know C very well, and I wanted to tinker with a codebase mostly free from preprocessor magic and with sane design and formatting decisions. As an added bonus, musl's source is very well chunked into smaller files with few interdependencies, which means that an incremental porting approach can work well.
- musl has a pretty big test suite. As a systems programming (whatever that means) novice, I don't want to trust in my non-existent psychic powers to detect regressions that result from my actions.
- I thought it'd be fun to have C programs call
malloc()and execute Rust (and right now, the test suite currently does and passes!). It could also (if I get a heck of a lot further than I have) be a cool test case for demonstrating Rust's potential to improve our current computing infrastructure and ecosystem.
Goals and non-goals
It's obviously stupid for one junior programmer to try to build their own C standard library, right? Yeah, it is. But there are still a few reasons I'm liking this:
- Learning: I don't know C and its domains very well. In my day job I write back-end web code, data analysis scripts, and bioinformatics tools. Mostly in Python. This seems like a great way to learn about C, how software interacts with the kernel, and what goes into making a standard library.
- Showing that Rust can do this kind of work (allocators, pthread implementations, etc.), and showing myself that I'm capable of learning how to :).
- Experimenting with ways to incrementally consume a C library or program from the inside out using Rust's solid C interop support.
- I'm starting to get a more realistic idea of Rust's current limitations when operating with C (I'm looking at you, unstable inline assembly and unimplemented untagged union support).
There are also a whole bunch of things I have no intention of doing:
- Using rust-bindgen or some other automated tool to generate type declarations or translate code for me. As a learning project, it's much better if I hand-translate everything (which so far is just a tiny portion of the overall codebase).
- Ever having this code used in production. Ever. No really, ever. The test suite that I'm using has gaps (if you're a POSIX aficionado and a musl fan, I'm sure they'd love help improving the coverage), and while I feel pretty confident about higher-level work I do, I'm dumb as a rock when it comes to pointer math.
- Supporting any platform other than Linux on x86_64. Or really supporting any platform other than my laptop or desktop.
- Trying to improve upon what musl actually does. If a bug gets fixed accidentally, then cool. But right now I'm just trying to replicate its behavior nearly exactly in Rust.
Like musl, rusl is made available under the MIT license.