Stabilization of items needed for no_std applications?

Right now, writing a standalone application using no_std requires several unstable features that stable Rust does not allow. See https://doc.rust-lang.org/book/no-stdlib.html for sample code of a no_std application (and https://github.com/rust-lang/rust/issues/41925 for a bug suggesting a further simplification to that sample code).

At a minimum, writing a no_std application requires start and several lang items; commonly, users of no_std may also want no_main, and an intrinsic like intrinsics::abort().

I would guess that everything on that list other than the lang items is ready for stabilization already; I can’t think of any obvious issues, though I’d welcome any suggestions on that point.

As for the lang items required: what might block stabilization of those? What might it take to bring them to a stable state? Does anyone have planned changes they want to make to them?

2 Likes

I imagine some of this stuff will be de-facto stabilized and we can sorta just give in and say “yeah, it’s stable now”. The more years that go by the harder it will be to tweak these things.

The exception handling stuff is pretty frightening to stabilize since that is directly tied to whatever LLVM is doing this year.

I imagine @japaric has some opinions about this subject.

Mm I’d like to write a proposal for “needs provides” which among other things would replace a lot of start/main/entry point stuff and panicking language items, among other things. Therefore I’m not too excited to them stabilized now.

2 Likes

First of all, the only unstable feature really required to build a no_std application is the panic_fmt lang item. See reworked no-stdlib example from the book:

// profile.dev.panic = "abort"

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

// default-features = false, version = "0.2.22"
extern crate libc;

#[no_mangle]
pub extern "C" fn main(argc: isize, argv: *const *const u8) -> isize {
    static HELLO: &[u8] = b"Hello, world!\0";

    unsafe {
        libc::puts(HELLO.as_ptr() as *const i8);
    }

    0
}

#[lang = "panic_fmt"]
fn panic_fmt(_msg: core::fmt::Arguments, _file: &'static str, _line: u32) -> ! {
    unsafe {
        libc::exit(1);
    }
}

Second, the no-stdlib example in the book is not a good representation of no_std applications, IME. It says that no_std is meant to be used on systems where “threads, networking, heap allocation, and others” may not be available, but then tells you to use the libc crate / C library, which is a library mainly used to interface the OS to get access to threads, networking, heap allocation and others. A usual embedded / bare metal Rust application doesn’t link to the C library; of course, you are free to do so if you want to provide some sort of POSIX layer or etc.

Finally, on some embedded / bare metal applications the panic_fmt requirement is totally artificial. It’s usually the case that you’ll build your program so that it never panics (as panics on a system where there’s no panic recovery can be as bad as memory corruption) so your final binary, when compiled in release mode, is free from calls to rust_begin_unwind (what the panic_fmt lang item maps to). I personally would like to see official support for this scenario: maybe some -C panic=undefined mode where the compiler does not demand the panic_fmt lang item and instead just leaves the rust_begin_unwind symbol undefined; the linker would then either complain at link time if your program does contain panics or your link program correctly if it doesn’t have any. This undefined mode seems even easier to stabilize than the panic_fmt lang item as we don’t have to decide on any function signature.

7 Likes

I admit that I have little to no experience with embedded programming, but I struggle to imagine a non-trivial program where this works at all, let alone is a sustainable strategy. Any array subscript operation, any unwrap, and assertions in a number of core APIs will necessarily introduce the possibility of panics. It's true that these can often be optimized out, but if you bank on that, whether your program can be built depends on the optimizer's whims, which can notoriously vary depending on the phase of the moon and the exact way you write your program. Furthermore, from how often we get bug reports about bounds checks not being elided in obvious cases, I doubt that even programs that obviously don't panic (as opposed to not panicking because of subtle global invariants) can be linked successfully in this mode.

Only if we decide we don't care that someone's program will stop linking every time we change anything at all about code generation (MIR building, MIR optimizations, the entirety of trans, and the LLVM version). That seems like a radical departure from what we usually mean by "stabilizing" something.

1 Like

I would like to see some sort of path to stabilization here; it’s always felt like a wart that no_std becoming stable means “only sorta kinda”.

Two things:

  1. What does it mean to have a stable feature which can’t be compiled using a stable compiler? (A library using #[no_std] can be compiled on stable, but not a standalone executable.)

  2. Can’t #[panic_fmt] just be ignored if it doesn’t exist? What’s the worst that could happen? I suppose this would be implemented as an infinite loop, since it has to be ! anyways. Granted, this results in a really bad experience if you do panic, but it would allow people to develop on nightly using unstable features for debugging while shipping something from stable which ‘probably’ doesn’t panic.

What would be really nice here are some default strategies for #[panic_fmt] which could be easily stabilized.

You could imagine something like:

#[lang_default(panic_fmt, loop)]

…which goes into an infinite loop. Or if you got the abort intrinsic working:

#[lang_default(panic_fmt, abort)]

One of the no_std environments I work in has a minimalist pseudo-libc that supports a few things like printf(), abort(), etc (although little else). Perhaps you could even have:

#[lang_default(panic_fmt, print)]

1 Like

I like the lang default idea, I’ve been considering writing an RFC for something like this before.

One nit: It should probably not share names with lang items (those names will always be an implementation detail), but instead it should have some new name like #[use_lang_default(panic_strategy, loop)] or something. This way it can also cover multiple lang items if necessary.

This doesn't seem to be true?

error: language item required, but not found: `eh_personality`

That's on:

rustc 1.20.0-nightly (582af6e1a 2017-07-19)

Are you building with panic=abort?

Yes

It works for me on rustc 1.20.0-nightly (15aa15b03 2017-07-21). If you post your code I can take a look in case it's something simple. I used [profile.dev] panic = "abort".

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.