-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
docs: hello-world-small
example size update
#50
Conversation
ReferenceUnified example for the
#![no_std]
#![no_main]
// This approach to include eyra specific lines seems acceptable since Eyra only works with nightly:
// https://github.com/sunfishcode/c-ward/issues/144
#![cfg_attr(feature = "eyra", feature(cfg_match, lang_items), allow(internal_features))]
cfg_match! {
cfg(feature = "eyra") => {
extern crate eyra;
#[global_allocator]
static GLOBAL_ALLOCATOR: rustix_dlmalloc::GlobalDlmalloc = rustix_dlmalloc::GlobalDlmalloc;
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
}
}
// NOTE: This differs from the official example,
// Provides visible feedback to stdout at the expense of extra size:
#[no_mangle]
pub extern "C" fn main() -> isize {
const HELLO: &'static str = "Hello, world!\n";
unsafe { write(1, HELLO.as_ptr() as *const i8, HELLO.len()) };
0
}
extern "C" {
fn write(fd: i32, buf: *const i8, count: usize) -> isize;
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} }
fn main() {
let target_is_musl = std::env::var("CARGO_CFG_TARGET_ENV")
.is_ok_and(|v| v == "musl");
let target_is_glibc = std::env::var("CARGO_CFG_TARGET_ENV")
.is_ok_and(|v| v == "gnu");
// Pass `-nostartfiles` to the linker, when Eyra is enabled.
if cfg!(feature = "eyra") {
println!("cargo:rustc-link-arg=-nostartfiles");
} else {
// NOTE: Not required when building with `cargo zigbuild`:
if target_is_musl {
println!("cargo:rustc-link-arg=-lc");
}
// NOTE: Absolute paths specific to Fedora 41 used here.
// Not providing the static libraries will dynamically link libc and segfault at runtime.
if target_is_glibc {
println!("cargo:rustc-link-arg=/usr/lib64/libc.a");
println!("cargo:rustc-link-arg=/usr/lib/gcc/x86_64-redhat-linux/14/libgcc_eh.a");
}
}
}
[package]
name = "example"
version = "0.1.0"
edition = "2021"
[dependencies]
eyra = { version = "0.17.0", default-features = false, optional = true }
rustix-dlmalloc = { version = "0.1.0", features = ["global"], optional = true }
[features]
eyra = ["dep:eyra", "dep:rustix-dlmalloc"]
[profile.release]
lto = true
codegen-units = 1
panic = "abort"
opt-level = "z"
strip = true
[toolchain]
profile = "minimal"
channel = "nightly-2024-10-04"
components = ["rust-src"]
targets = ["x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl"] glibc (692,976 B)# NOTE: The `-Z build-std*` args aren't doing much for the above `nostd` focused example:
# LLD as the linker benefits musl notably in size (~14KB => ~4KB), gnu targets minimally.
RUSTFLAGS="-Z location-detail=none -C link-arg=-fuse-ld=lld -C relocation-model=static -C target-feature=+crt-static" \
cargo +nightly build --target x86_64-unknown-linux-gnu --release \
-Z build-std=std,panic_abort \
-Z build-std-features=panic_immediate_abort eyra (5,712 B)# NOTE: The `-Z build-std*` args aren't doing much for the above `nostd` focused example:
# LLD as the linker benefits musl notably in size (~14KB => ~4KB), gnu targets minimally.
RUSTFLAGS="-Z location-detail=none -C link-arg=-fuse-ld=lld -C relocation-model=static -C target-feature=+crt-static" \
cargo +nightly build --target x86_64-unknown-linux-gnu --release \
-Z build-std=std,panic_abort \
-Z build-std-features=panic_immediate_abort \
--features eyra musl (3,952 B)# LLD as the linker benefits musl notably in size (~14KB => ~4KB), gnu targets minimally.
RUSTFLAGS="-Z location-detail=none -C link-arg=-fuse-ld=lld -C relocation-model=static -C target-feature=+crt-static" \
cargo +nightly build --target x86_64-unknown-linux-musl --release \
-Z build-std=std,panic_abort \
-Z build-std-features=panic_immediate_abort |
Thanks! Also, if you're using |
No worries! This was just from having some time spare to go over my prior notes on the topic and do a revision / summary over my original issue ( #27 ). I was a bit surprised with some of the musl insights, especially when changing to lld for the linker having a notable improvement.
I hit a bit of a snag not long after the docs PR here was merged nightly releases were failing due to a change that affected Unfortunately due to the semver resolution (despite not changing my Eyra version), the Just adding that context for anyone that lands here and attempts to reproduce the examples without a |
As for size yes I got 352 bytes with your origin example IIRC, while a similar "Hello World" with I had come across this blog article prior to trying your origin example (they managed 640 bytes), where at the end their "Hello World" string version was 888 bytes. The article (at least at the time I read it) was lacking information to reproduce their final result but after I saw the syscall usage to avoid Origin Examples (352 & 504 bytes)After that I got around to looking at the origin examples and realized you did roughly the same (but with proper error handling), and I got that down to 504 bytes:
NOTE: That was with With my
Off-topic: Eyra Hello World examples - 344 bytes + 456 bytesFor anyone interested in reproducing this, I'll share it, but at this point it doesn't help evaluate Origin or Eyra as it's too simple now there is no overhead that they can reduce: #![no_std]
#![no_main]
#[no_mangle]
pub extern "C" fn _start() -> ! {
exit(); // +8 bytes to size vs using `loop() {}`
}
fn exit() -> ! { unsafe { rustix::runtime::exit_thread(42) } }
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} } [package]
name = "example"
version = "0.0.0"
edition = "2021"
[dependencies]
rustix = { version = "0.38.37", default-features = false, features = ["runtime"] }
[profile.release]
lto = true
panic = "abort"
opt-level = "z"
strip = true # Current stable Rust (1.81.0):
$ RUSTFLAGS='-C link-arg=-Wl,--build-id=none,--nmagic,-z,nognustack -C link-arg=-fuse-ld=lld -C relocation-model=static -C target-feature=+crt-static -C link-arg=-nostartfiles' \
cargo build --release --target x86_64-unknown-linux-gnu
# Remove some extra weight:
$ objcopy -R .comment target/x86_64-unknown-linux-gnu/release/example
# Only 344 bytes:
$ du --bytes target/x86_64-unknown-linux-gnu/release/example
344 target/x86_64-unknown-linux-gnu/release/example
$ ldd target/x86_64-unknown-linux-gnu/release/example
not a dynamic executable
# It works:
$ target/x86_64-unknown-linux-gnu/release/example
$ echo $?
42 For a little more functionality, add the #[inline(always)]
fn hello_world() {
rustix::io::write(
unsafe { rustix::stdio::stdout() },
"Hello, world!\n".as_bytes()
).unwrap();
} This will have some extra content we can trim away via other flags (if min size was the goal, these aren't always advised of course): # Additional linker arg `--no-eh-frame-hdr`:
$ RUSTFLAGS='-C link-arg=-Wl,--build-id=none,--nmagic,-z,nognustack,--no-eh-frame-hdr -C link-arg=-fuse-ld=lld -C relocation-model=static -C target-feature=+crt-static -C link-arg=-nostartfiles' \
cargo build --release --target x86_64-unknown-linux-gnu
# Also remove `.eh_frame`:
# NOTE: `--build-id=none` above is more optimal vs `-R .note.gnu.build-id` post-build:
$ objcopy -R .comment -R .eh_frame target/x86_64-unknown-linux-gnu/release/example
# Only 584 bytes:
$ du --bytes target/x86_64-unknown-linux-gnu/release/example
584 target/x86_64-unknown-linux-gnu/release/example
$ ldd target/x86_64-unknown-linux-gnu/release/example
not a dynamic executable
$ target/x86_64-unknown-linux-gnu/release/example
Hello, world! Alternatively, the |
Summary
~13KB
difference to a6KB
difference.Reproduction
musl (25KB)
musl + zig (26.4KB)
glibc (834KB)
eyra (24.7KB)
nostd
referenceFor an additional reference the
nostd
example that was added at a later date:Presently builds with eyra to
5,592
bytes 😎 (with the sameCargo.toml
release profile used below)-Z build-std
args increases to10,144
bytes, or7,608
bytes by adding-C linker-plugin-lto -C linker=clang -C link-arg=-flto=full -C link-arg=-fuse-ld=mold
(note: eyra fails to build if-C link-arg=-fuse-ld=lld
is used with-C linker-plugin-lto
).For the musl target (without eyra):
13,464
bytes, but requires-C link-arg=-lc
to build successfully. Adding-C link-arg=-fuse-ld=lld
reduces this down to3,776
bytes.3,200
bytes, or2,928
bytes after stripping some ELF strings (-C link-arg=-lc
not required, Zig also defaults the linker to LLD already):Static glibc gnu target (without eyra):
also builds smaller at(Nevermind that segfaults and unexpectedly dynamic links glibc. Without4,760
bytes. This also requires-C link-arg=-lc
to be successful.+crt-static
it'll build dynamically linked successfully at4,440
bytes)-C link-arg=/usr/lib64/libc.a -C link-arg=/usr/lib/gcc/x86_64-redhat-linux/14/libgcc_eh.a
(at least on Fedora 41 with theglibc-static
package) to successfully build and weighs in at692,976
bytes with the-Z
build-std flags (otherwise 64 bytes larger at693,040
bytes).-C linker-plugin-lto -C linker=clang -C link-arg=-flto=full
for cross-language LTO, but no improvement.