No_std library usable by rust code that uses stdlib

I have a rust library I would like to not import the standard library, for use in embedded cases, kernels and the like. Consequently I have something like this:

#![no_std]
#![feature(lang_items)]

pub mod somefunctionality;

// ....

#[cfg(not(tests))]
#[lang = "eh_personality"]
#[no_mangle]
pub extern fn rust_eh_personality() {
}

// This function may be needed based on the compilation target.
#[cfg(not(tests))]
#[lang = "eh_unwind_resume"]
#[no_mangle]

pub extern fn rust_eh_unwind_resume() {
}

#[cfg(not(tests))]
#[lang = "panic_fmt"]
#[no_mangle]
pub extern fn rust_begin_panic(_msg: core::fmt::Arguments,
                               _file: &'static str,
                               _line: u32) -> ! {
    loop {}
}

This is taken from rust no_std nightly documentation (although actually the binary page, see questions later).

When I come to compile via cargo test as I have a test in tests/testmylib.rs for example, I get:

error[E0152]: duplicate lang item found: `panic_fmt`.
note: first defined in crate `std`.

Which is unsurprising really. So my questions are:

  1. I don't think as the author of the library I should be providing those pieces of rust functionality. Yet, if I remove them, even though this is a library, rust whinges about their non-existence. How do I fix this?
  2. Assuming the answer to part 1 is in fact "oh this is intentional you have to have these even in a lib" how do I fix it?

I have also read Test with no_std, which I think is helpful but won't fix my issue as the test is an external crate here, which I expect produces the same issue consumers of my library would face.

I might have just answered my own question. In my Cargo.toml I set:

crate-type = ["lib", "staticlib", "dylib"]

Changing to

crate-type = ["lib"]

Fixes the issue. I can understand dylib causing an issue, but staticlib does also, which I find very strange.

1 Like

Those lang items shouldn't be defined by libraries, but rather the "final end products", since they can only be defined once in the entire dependency tree.

6 Likes

Yep, this makes sense. I can see why Rust would block staticlib and dylib from not having these components, since it is an unknown (from the point of view of the linker) where these come from.

It would be nice if there were a way to say "these things are externs, fail to link if you can't find them".

1 Like

The easiest way to fix this, if you want expose a C ABI, would be to split your library into two parts:

  • somelibrary: A Rust library. It's no_std, but doesn't provide any lang items.
  • somelibrary-c: A C library, that happens to be written in Rust, that provides the rust_eh_personality and stuff. It depends on somelibrary, and is also no_std.
1 Like

That's a great suggestion. Thanks!