Rust's static library for an embedded 'hello world' is quite large; how can I optimize it?

lib.rs

#![no_std]
#![no_main]

#[panic_handler]
pub unsafe fn panic_impl(_info: &::core::panic::PanicInfo) -> ! {
    loop {}
}

#[no_mangle]
pub extern "C" fn add(left: u64, right: u64) -> u64 {
    left + right
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }
}

cargo.toml

[package]
name = "testbl"
version = "0.1.0"
edition = "2021"

[lib]
name = "testbl"
path = "src/lib.rs"
crate-type = ["staticlib"]


[dependencies]


[profile.dev]
panic = "abort"


[profile.release]
opt-level = 's'  # 优化大小而非性能
lto = true       # 启用链接时间优化
codegen-units = 1 # 减少代码生成单元,可以获得更小的二进制文件
strip = "debuginfo"     # 删除调试符号
panic = 'abort'  # 减少错误处理代码
debug = false

./cargo/config.toml

[build]
target = "thumbv6m-none-eabi"
rustflags = [
    "-Zlocation-detail=none",
    "-Zfmt-debug=none",
]

[unstable]
build-std-feature = [
    "panic_immediate_abort"
]
build-std = ["core"]

rust-toolchain.toml

[toolchain]
channel = "nightly"

What is “quite large” in this case (I.e., how many kilobytes)?

Have you tried using
opt-level = 'z'
Instead of
opt-level = 's'
?

Thank you for your response.
I've tried setting opt-level = 'z', but the effect is not significant.
I tried using rust-strip, and the size reduced to 242KB from 699KB before stripping. Although it became smaller, it's still quite large compared to a library compiled with C under the same conditions.

In the Rust case it's statically linking the standard library, while in the C case it's most likely dynamically linking it.

To dynamically link in the Rust case:

$ RUSTFLAGS="-C prefer-dynamic" cargo build --release

One day, if operating systems carry the rust standard libraries, this might be useful -- but for now, it's mostly for esoteric use cases.

Edit: We have an embedded arm platform that needs to carry a few rust binaries, and we use dynamic linking. While the standard library .so file is quite massive we still save space by using dynamic linking because each binary is so huge when it statically links the standard library.

1 Like

This library is intended to be integrated into a bare-metal program, so dynamic linking cannot be used.

Oh, right -- sorry; I missed the no_std.

What is that? I tried to find it but only found a tool for automatically removing unused imports.

Maybe this will be of help

3 Likes

Why are you having this in a lib.rs instead of a main.rs?
Where is the entry point to your program?
And how are you compiling?
Are you maybe including the test harness and that makes the file big?

And maybe change the category of your question to embedded

250kB is definitely too much. Just for your reference, from my experience, a bare-metal “hello world” for Cortex-M can be compiled to less than 5 kB.

1 Like

When you link the final program you may want to use --gc-sections. This tells the linker to discard any unused functions when -ffunction-sections is used (which rustc always does) strip = "debuginfo" doesn't have any effect when building a static library. All it does is passing --strip-debug when invoking the linker, but building a static library doesn't invoke the linker. You have to manually pass it when linking your program (or strip the program after linking it)

3 Likes

I just wrote a library function in Rust and then called this library in C

I just wrote a library function in Rust and then called this library in C.