Beneath std
This section documents features that are normally provided by the std
crate and
that #![no_std]
developers have to deal with (i.e. provide) to build
#![no_std]
binary crates.
Using libc
In order to build a #[no_std]
executable we will need libc
as a dependency.
We can specify this using our Cargo.toml
file:
[dependencies]
libc = { version = "0.2.146", default-features = false }
Note that the default features have been disabled. This is a critical step -
the default features of libc
include the std
crate and so must be
disabled.
Alternatively, we can use the unstable rustc_private
private feature together
with an extern crate libc;
declaration as shown in the examples below. Note that
windows-msvc targets do not require a libc, and correspondingly there is no libc
crate in their sysroot. We do not need the extern crate libc;
below, and having it
on a windows-msvc target would be a compile error.
Writing an executable without std
We will probably need a nightly version of the compiler to produce
a #![no_std]
executable because on many platforms, we have to provide the
eh_personality
lang item, which is unstable.
Controlling the entry point is possible in two ways: the #[start]
attribute,
or overriding the default shim for the C main
function with your own.
Additionally, it's required to define a panic handler function.
The function marked #[start]
is passed the command line parameters
in the same format as C (aside from the exact integer types being used):
#![feature(start, lang_items, core_intrinsics, rustc_private)]
#![allow(internal_features)]
#![no_std]
// Necessary for `panic = "unwind"` builds on cfg(unix) platforms.
#![feature(panic_unwind)]
extern crate unwind;
// Pull in the system libc library for what crt0.o likely requires.
#[cfg(not(windows))]
extern crate libc;
use core::panic::PanicInfo;
// Entry point for this program.
#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
0
}
// These functions are used by the compiler, but not for an empty program like this.
// They are normally provided by `std`.
#[lang = "eh_personality"]
fn rust_eh_personality() {}
#[panic_handler]
fn panic_handler(_info: &PanicInfo) -> ! { core::intrinsics::abort() }
To override the compiler-inserted main
shim, we have to disable it
with #![no_main]
and then create the appropriate symbol with the
correct ABI and the correct name, which requires overriding the
compiler's name mangling too:
#![feature(lang_items, core_intrinsics, rustc_private)]
#![allow(internal_features)]
#![no_std]
#![no_main]
// Necessary for `panic = "unwind"` builds on cfg(unix) platforms.
#![feature(panic_unwind)]
extern crate unwind;
// Pull in the system libc library for what crt0.o likely requires.
#[cfg(not(windows))]
extern crate libc;
use core::ffi::{c_char, c_int};
use core::panic::PanicInfo;
// Entry point for this program.
#[no_mangle] // ensure that this symbol is included in the output as `main`
extern "C" fn main(_argc: c_int, _argv: *const *const c_char) -> c_int {
0
}
// These functions are used by the compiler, but not for an empty program like this.
// They are normally provided by `std`.
#[lang = "eh_personality"]
fn rust_eh_personality() {}
#[panic_handler]
fn panic_handler(_info: &PanicInfo) -> ! { core::intrinsics::abort() }
If you are working with a target that doesn't have binary releases of the
standard library available via rustup (this probably means you are building the
core
crate yourself) and need compiler-rt intrinsics (i.e. you are probably
getting linker errors when building an executable:
undefined reference to `__aeabi_memcpy'
), you need to manually link to the
compiler_builtins
crate to get those intrinsics and solve the linker errors.