libcosmo | ||
src | ||
.gitignore | ||
Cargo.toml | ||
gcc-linker-wrapper.bash | ||
LICENSE | ||
README.md | ||
x86_64-unknown-linux-cosmo.json |
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.
Build steps
- Download the Cosmopolitan Libc amalgamation into the
libcosmo
folder:
cd libcosmo
wget https://justine.lol/cosmopolitan/cosmopolitan.zip
unzip cosmopolitan.zip
cd ../
- Download the necessary host toolchain and source code for Rust:
# I was on Debian, 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
- 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=core,libc --target=./x86_64-unknown-linux-cosmo.json
- run
objcopy
on the debug binary to obtain the APE:
objcopy -SO binary ./target/x86_64-unknown-linux-cosmo/debug/hello_world.com.dbg ./hello_world.com
What about the std
crate?
The std
crate compiles successfully, but fails at the linker stage. Here's how
that can be tested:
-
Change the source code in
src/main.rs
to use the commented outmain
function and#![restricted_std]
. -
In the source code for Rust's
std
crate, change acfg_if
in$HOME/.rustup/toolchains/<your-host-nightly-toolchain>/lib/rustlib/src/rust/library/backtrace/src/backtrace/mod.rs
to use thenoop
trace instead of depending onlibunwind
.
--- mod.rs 2022-06-21 12:52:21.724053459 +0530
+++ mod.rs 2022-06-21 13:05:50.948777093 +0530
@@ -132,7 +132,7 @@
pub(crate) mod miri;
use self::miri::trace as trace_imp;
pub(crate) use self::miri::Frame as FrameImp;
- } else if #[cfg(
+ } /* else if #[cfg(
any(
all(
unix,
@@ -154,7 +154,7 @@
pub(crate) use self::dbghelp::Frame as FrameImp;
#[cfg(target_env = "msvc")] // only used in dbghelp symbolize
pub(crate) use self::dbghelp::StackFrame;
- } else {
+ } */ else {
mod noop;
use self::noop::trace as trace_imp;
pub(crate) use self::noop::Frame as FrameImp;
I haven't figured out what config I should give to cargo
so I don't need to do
this. I find it surprising that std
cannot be built without relying on
libunwind
.
- The build command now changes to
cargo +nightly build -Zbuild-std=core,alloc,panic_abort,libc,std -Zbuild-std-features= --target=./x86_64-unknown-linux-cosmo.json
- At the linker stage you might find some of the following symbols are missing:
-
If
Unwind_Backtrace
or similar is missing, you need to check if step 2 is done properly. -
open64
,stat64
,fstat64
,__xpg_strerror_r
: these can be added to Cosmopolitan Libc via aliases of the existing functions. -
pthread_key_create
,pthread_setspecific
,pthread_key_delete
,pthread_getspecific
-- these functions should not be needed in a single-threaded program, but somehow they are still linked. Support for threads is currently being added to Cosmopolitan Libc, but possibly the related code in thestd
crate can be changed to avoid this issue.