Embassy - minimal build - strings leaking into executable

Hi,
I've been away from embedded rust a while, and suddenly someone wrote the Embassy async runtime for embedded targets! Wow ... I mean WOW!
For fun and glory I want to understand this better, and make a minimal build, removing debug and trace-lines to see how compact a build I can produce.
I now have a build that can toggle a pin based on a timer, that take 4016 bytes, - quite pretty, but now I notice 'something' is leaking these text strings into the binary:

async fn` resumed after completion
assertion failed: n < 4
assertion failed: n < 32

It's not much ... but is there any way to avoid that?

I'm running with:
[profile.release]
codegen-units = 1
opt-level = 'z'
debug-assertions = false
overflow-checks = false # (bad idea?)
lto = 'yes'

I can read, that (real) assertions is always enabled in rust, regardless of debug/release build.
I accept that, but the message could ideally be empty, and the code would just panic.

What is leaking the string e.g. "resumed after completion" into the build?
It's apparently not the embassy source code itself.

thx
Troels

This sounds like something that would come from the Future implementation that the compiler automatically generates for an async function. Those sorts of futures aren't designed to be polled again after they run to completion, so it triggers a panic.

I'm not sure how you would get rid of those strings, though.

1 Like

Note that according to the documentation of Future, a different behavior would be conforming (and thus could be implemented by the compiler):

Once a future has completed (returned Ready from poll), calling its poll method again may panic, block forever, or cause other kinds of problems; the Future trait places no requirements on the effects of such a call. However, as the poll method is not marked unsafe, Rust’s usual rules apply: calls must never cause undefined behavior (memory corruption, incorrect use of unsafe functions, or the like), regardless of the future’s state.

I could imagine a (future) compiler option which (for example) just causes the program to halt (or panic without a message, which might be nicer) if such a completed async fn future is polled again.

1 Like

At least the first string is in the the code GitHub - rust-lang/rust: Empowering everyone to build reliable and efficient software. not looked for second.
(Without me spending time digging deeper) you would have to recompile with a patch to remove the string.

1 Like

As others have pointed out, the compiler adds this panic message.

AFAIK, the simplest way to get rid of the message is to rebuild the standard library with a nightly build of rustc using the panic_immediate_abort flag.

(I've only tested this on my 64-bit Linux machine)

  1. Install nightly if needed: rustup default nightly
  2. Add panic = "abort" to [profile.release]
  3. Rebuild with the following Cargo command: cargo +nightly build --release -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort --target x86_64-unknown-linux-gnu

You can also add this optimization to your Cargo configuration. See:

3 Likes

That did it!

I used the cargo-way and inserted:

[unstable]
# Requires the rust-src component. `rustup +nightly component add rust-src`
build-std = ["std", "panic_abort"]
build-std-features = ["panic_immediate_abort"]

All the mentioned strings went away in the release build, and image is now 3812 bytes flash, of which 432 bytes is interrupt table. Quite impressive.

As written earlier, your suggestion solved my problem, thanks!

Out of curiosity:
The rust LTO optimizer is so effective, that I now wonder why the strings are not eliminated just by inserting an empty panic_handler():

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    loop {
        cortex_m::asm::bkpt();
    }
}

I would suspect that nothing refers to the panic strings now ... but that is probably a false assumption?