rust-ape-example/README.md
2023-11-01 22:29:57 -05:00

149 lines
5.5 KiB
Markdown

# Cosmopolitan Libc and Rust
> Note: the executables built using Cosmo and Rust do not work across the
> operating systems supported by Cosmopolitan Libc, because Rust decides system
> constants like `EINVAL` [at
> compile-time](https://github.com/search?q=repo%3Arust-lang%2Flibc%20EINVAL&type=code)
> (see https://github.com/ahgamut/rust-ape-example/issues/3), based on the
> operating system provided in the compilation target. Thus, I expect the fat
> binaries built here will only work on `x86-64-linux` and `aarch64-linux`. Very
> well, perhaps in the future we can find a way to have system constants as
> `extern` values in Rust, like how [I did it for
> C](https://github.com/ahgamut/gcc/tree/portcosmo-11.2).
This repository contains a simple `Hello world!` example in the [Rust][rust]
programming language, that builds with [Cosmopolitan Libc][cosmo]. Now it also
includes all the example snippets I could scrape from [Rust By Example][rbe],
and it builds around 175 example programs, including those that use Rust's
`std::thread` and `std::sync::Arc`.
> `ripgrep` builds with Cosmopolitan Libc -- check it out
> [here](https://github.com/ahgamut/ripgrep/tree/cosmopolitan).
To build this repo you need a recent version of the Cosmopolitan Libc monorepo,
and `bash` because I wrote a simple filter script.
I created a [custom compilation target][custom-target] for Rust, called
`x86_64-unknown-linux-cosmo`, to provide a build process that uses the
Cosmopolitan Libc amalgamation and `cargo`. I followed the documentation in the
[Rust Embedonomicon][custom-embed] to create the target.
An alternative method to build APEs with Rust would be to avoid `cargo`, just
use `rustc` or equivalent compiler to generate `.o` files, and then write a
shell script that does the linking with the expected flags. I have not tried
this method.
## Building a Rust APE with the `std` crate
1. Download the Cosmopolitan Libc repo and build the toolchain:
```bash
git clone https://github.com/jart/cosmopolitan
cd cosmopolitan
make -j MODE= toolchain
make -j MODE=aarch64 toolchain
export COSMO=$(realpath ./)
cd ..
mkdir cosmos
export COSMOS=$(realpath ./cosmos)
$COSMO/bin/cosmocc --update
```
Then clone this repo
```bash
git clone https://github.com/ahgamut/rust-ape-example
cd rust-ape-example
```
2. Download the necessary host toolchain and source code for Rust:
```bash
# I was on Debian 11, so I did this
rustup toolchain install nightly-x86_64-unknown-linux-gnu
rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
# on Alpine Linux, you may need to do
rustup toolchain install nightly-x86_64-unknown-linux-musl
rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-musl
```
For reference, this worked when I tried it for `nightly-x86_64-linux-gnu` and:
* the Rust binaries on October 10 2023
3. run `cargo build` to get the debug executables. This uses a bash script that
removes unnecessary linker arguments.
```bash
export ARCH=x86_64
cargo +nightly build --target=./x86_64-unknown-linux-cosmo.json
export ARCH=aarch64
cargo +nightly build --target=./aarch64-unknown-linux-cosmo.json
```
4. run `apelink` on the debug binaries to obtain the fat APE:
```bash
# look at the built debug binaries
ls ./target/x86_64-unknown-linux-cosmo/debug/*.com.dbg
ls ./target/aarch64-unknown-linux-cosmo/debug/*.com.dbg
# apelink
MODE=
MODE_AARCH64=aarch64
APELINK=$COSMO/o/tool/build/apelink.com
apelinkpls () {
OUTPUT="$1"
OUTPUT_X86_64="$2"
OUTPUT_AARCH64="$3"
"$APELINK" -l "$COSMO/o/$MODE/ape/ape.elf" \
-l "$COSMO/o/$MODE_AARCH64/ape/ape.elf" \
-M "$COSMO/ape/ape-m1.c" \
-o "$OUTPUT" \
"$OUTPUT_X86_64" \
"$OUTPUT_AARCH64"
}
apelinkpls ./hello.com\
./target/x86_64-unknown-linux-cosmo/debug/hello.com.dbg\
./target/aarch64-unknown-linux-cosmo/debug/hello.com.dbg
# run the APE
./hello.com
```
Now we have Actually Portable Executables built with Rust! I also built a few
more executables using the code from [Rust By Example][rbe], and an APE that
doesn't use the `std` crate. There might some edge cases that I haven't noticed,
so clone/fork the repo and try it out!
## TODOs
- [ ] figure out build config to avoid using `libunwind`
The `std` crate relies on
[`backtrace`](https://github.com/rust-lang/backtrace-rs), which depends on
[`libunwind`](https://github.com/libunwind/libunwind) in the default builds for
unix. To work around this, `cosmopolitan.a` currently has stubs for the
functions that `backtrace` relies on. However, it might be easier to provide a
build flag in `Cargo.toml` to use the `noop` module of `backtrace`.
A small change needs to be submitted to the source code of `backtrace` (in the
`cfg_if!`
[here](https://github.com/rust-lang/backtrace-rs/blob/4e5a3f72929f152752d5659e95bb15c8f6b41eff/src/backtrace/mod.rs#L128))
to allow choosing `noop` when building as part of the `std` crate. This
conditional compilation flag should be accessible when building the `std` crate
either via `Cargo.toml` or something like `-Z use-std-backtrace-noop` in the
build command.
[without-std-branch]: https://github.com/ahgamut/rust-ape-example/tree/without-std
[rust]: https://rust-lang.org
[rbe]: https://doc.rust-lang.org/rust-by-example/
[cosmo]: https://github.com/jart/cosmopolitan
[cosmo-nightly]: https://github.com/jart/cosmopolitan/commit/b69f3d2488dbaf9dcc541e699f5b7c09fbf046e0
[amalg-download]: https://justine.lol/cosmopolitan/download.html
[custom-target]: https://doc.rust-lang.org/rustc/targets/custom.html
[custom-embed]: https://docs.rust-embedded.org/embedonomicon/custom-target.html