5.1 KiB
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. To build it
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
libcosmo
folder:
cd libcosmo
wget https://justine.lol/cosmopolitan/cosmopolitan.zip
unzip cosmopolitan.zip
cd ../
For reference, I used the nightly version of cosmopolitan.a
from June 26 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 build
to get the debug executable. This uses a bash script that removes unnecessary linker arguments. A recent version ofgcc
andld.bfd
is 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
objcopy
on 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.