Mustang is a system for building programs built entirely in Rust, meaning they do not depend on any part of libc or crt1.o, and do not link in any C code.
Why? For fun! And to exercise some components built for other purposes (such as
rustix) but which happen to also be part of what's needed to do what
Mustang is doing. And in the future, possibly also for experimenting with new
kinds of platform ABIs and new forms of process argument passing.
Mustang isn't about making anything safer, for the foreseeable future. The
major libc implementations are extraordinarily well tested and mature. Mustang
for its part is experimental and has lots of
This also isn't about building a complete libc. It currently includes some
things with libc-compatible interfaces, just enough to allow it to slide in
std, however even this may not always be necessary. We'll see.
Mustang currently runs on Rust Nightly on Linux on x86-64, x86, aarch64, and riscv64. It aims to support all Linux versions supported by Rust, though at this time it's only tested on relatively recent versions.
To use it, first install rust-src, which is needed by
$ rustup component add rust-src --toolchain nightly
Then, set the
RUST_TARGET_PATH environment variable to a path to the
mustang/target-specs directory, so that you can name
mustang targets with
--target=.... For example, within a mustang repo:
$ export RUST_TARGET_PATH="$PWD/mustang/target-specs"
Then, in your own crate, add a dependency on
[dependencies] mustang = "<current version>"
And add a
mustang::can_run_this!(); to your top-level module (eg. main.rs).
This does nothing in non-
mustang-target builds, but in
builds arranges for
mustang's libraries to be linked in.
Then, compile with Rust nightly, using
-Z build-std and
--target=<mustang-target>. For example:
$ cargo +nightly run --quiet -Z build-std --target=x86_64-mustang-linux-gnu --example hello Hello, world! $
That's a Rust program built entirely from Rust saying "Hello, world!"!
For more detail, mustang has an
env_logger feature, which you can enable, and set
RUST_LOG to see various pieces of mustang in action:
$ RUST_LOG=trace cargo +nightly run --quiet -Z build-std --target=x86_64-mustang-linux-gnu --example hello --features env_logger [2021-06-28T06:28:31Z TRACE origin::program] Program started [2021-06-28T06:28:31Z TRACE origin::threads] Main Thread[Pid(3916066)] initialized [2021-06-28T06:28:31Z TRACE origin::program] Calling `.init_array`-registered function `0x5555558fb480(1, 0x7fffffffdb98, 0x7fffffffdba8)` [2021-06-28T06:28:31Z TRACE origin::program] Calling `main(1, 0x7fffffffdb98, 0x7fffffffdba8)` Hello, world! [2021-06-28T06:28:31Z TRACE origin::program] `main` returned `0` [2021-06-28T06:28:31Z TRACE origin::program] Program exiting $
A simple way to check for uses of libc functions is to use
nm -u, since
the above commands are configured to link libc dynamically. If
everything covered, there should be no output:
$ nm -u target/x86_64-mustang-linux-gnu/debug/examples/hello $
To compile C code with a
*-mustang-* target, you may need to
cc crate which C compiler to use; for example, for
set the environment variable
Known limitations in
Mustang is partly inspired by similar functionality in
steed, but a few
things are different. cargo's build-std is now available, which makes it
much easier to work with custom targets. And Mustang is starting with the
approach of starting by replacing libc interfaces and using
rather than reimplementing
std. This is likely to evolve, but whatever we
do, a high-level goal of Mustang is to avoid ever having to reimplement
mustang go from here? Will it support feature X, platform Y, or
use case Z? If
origin can do program startup in Rust, and
rustix can do
system calls in Rust, what does it all mean?
mustang eventually support new ABIs that aren't limited to passing
envp) convention, allowing new kinds of program
mustangto a new architecture?
rustixto the architecture, adding assembly sequences for making syscalls.
originto the architecture, adding assembly sequences for program and thread primitives.
mustang/target-specs, by first following these instructions to generate a specification of a built-in target, and then:
"vendor": "mustang"See other targets in the
mustang/target-specsdirectory for examples.
examplesdirectory, using the new target. Try
nm -uon the binaries to check for undefined symbols which need to be implemented.
mustangto a new OS?
One probably needs to do similar things as for a new architecture, and also
write a new
origin::rust implementation to handle the OS's convention for
arguments, environment variables, and initialization functions.
c-scape has some similarities to relibc, but has a different focus. Relibc is
aiming to be a complete libc replacement, while c-scape is just aiming to cover
the things used by Rust's
std and popular crates. Some parts of Relibc are
implemented in C, while c-scape is implemented entirely in Rust.
The most significant thing that makes c-scape unique though is its design as a set of wrappers around Rust crates with Rust interfaces. C ABI compatibility is useful for getting existing code working, but once things are working, we can simplify and optimize by changing code to call into the Rust interfaces directly. This can eliminate many uses of raw pointers and C-style NUL-terminated strings, so it can be much safer.