rust-ape-example
rust-ape-example copied to clipboard
A simple example with Rust and Cosmopolitan Libc
Actually Portable Executables with Cosmopolitan Libc and Rust
This repository contains a simple Hello world! example in the Rust
programming language, that builds with Cosmopolitan Libc. Now it also
includes all the example snippets I could scrape from Rust By Example,
and it builds around 175 example programs, including those that use Rust's
std::thread and std::sync::Arc.
ripgrepbuilds with Cosmopolitan Libc -- check it out here.
To build this repo you need a recent version of gcc (9 or 10 ought to be
good), a recent version of binutils (ld.bfd and objcopy), and bash
because I wrote a simple filter script.
I created a custom compilation 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 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
- Download the Cosmopolitan Libc amalgamation into the
libcosmofolder:
cd libcosmo
wget https://justine.lol/cosmopolitan/cosmopolitan.zip
unzip cosmopolitan.zip
cd ../
For reference, I used the nightly version of cosmopolitan.a from September 12
2022, which can be built from source if needed from this commit.
- Download the necessary host toolchain and source code for Rust:
# 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 June 22 2022 (5750a6aa2 2022-06-20)
- the Rust binaries on June 25 2022 (fdca237d5 2022-06-24)
- the Rust binaries on June 26 2022 (20a6f3a8a 2022-06-25)
- the Rust binaries on June 30 2022 (ddcbba036 2022-06-29)
- the Rust binaries on July 27 2022 (4d6d601c8 2022-07-26)
- the Rust binaries on September 6 2022 (78a891d36 2022-09-06)
- run
cargo buildto get the debug executable. This uses a bash script that removes unnecessary linker arguments. A recent version ofgccandld.bfdis required.
cargo +nightly build -Zbuild-std=libc,panic_abort,std -Zbuild-std-features="" --target=./x86_64-unknown-linux-cosmo.json
For reference, I used the below versions of gcc and ld.bfd
gcc (Debian 10.2.1-6) 10.2.1 20210110
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
GNU ld (GNU Binutils for Debian) 2.35.2
Copyright (C) 2020 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.
- run
objcopyon the debug binary to obtain the APE:
# look at the built debug binaries
ls ./target/x86_64-unknown-linux-cosmo/debug/*.com.dbg
# objcopy is the same version as ld.bfd above
objcopy -SO binary ./target/x86_64-unknown-linux-cosmo/debug/hello_world.com.dbg ./hello_world.com
# run the APE
./hello_world.com
# see syscalls made by the APE
./hello_world.com --strace
Now we have Actually Portable Executables built with Rust! I also built a few
more executables using the code from Rust By Example, 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, which depends on
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)
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.