rust-ape-example/README.md

4.5 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.

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.

a previous attempt at a Rust APE was done without using the std crate. You can view that in the without-std branch of this repo.

Building a Rust APE with the std crate

  1. 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 22 2022, which can be built from source if needed from this commit.

  1. 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, I used the nightly version of Rust from June 22 2022.

  1. run cargo build to get the debug executable. This uses a bash script that removes unnecessary linker arguments. A recent version of gcc and ld.bfd is required.
cargo +nightly build -Zbuild-std=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.
  1. run objcopy on the debug binary to obtain the APE:
# 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 an Actually Portable Executable built with Rust! There might some edge cases that I haven't noticed, so clone/fork the repo and try it out! The without-std branch of this repo might be useful.

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.