深入 std

本节介绍通常由 std crate 提供的功能,以及 #![no_std] 开发人员为了构建 #![no_std] 二进制 crate 必须处理(即提供)的功能。

使用 libc

为了构建 #[no_std] 可执行文件,我们需要将 libc 作为依赖项。我们可以使用 Cargo.toml 文件指定它

[dependencies]
libc = { version = "0.2.146", default-features = false }

请注意,默认功能已被禁用。这是一个关键步骤 - libc 的默认功能包括 std crate,因此必须禁用。

或者,我们可以使用不稳定的 rustc_private 私有功能以及 extern crate libc; 声明,如下面的示例所示。

编写不带 std 的可执行文件

我们可能需要一个 nightly 版本的编译器来生成 #![no_std] 可执行文件,因为在许多平台上,我们必须提供 eh_personality lang item,这是不稳定的。

可以通过两种方式控制入口点:#[start] 属性,或使用您自己的代码覆盖 C main 函数的默认 shim。此外,还需要定义一个 panic 处理函数

标记为 #[start] 的函数以与 C 相同的格式传递命令行参数(除了使用的确切整数类型)

#![feature(start, lang_items, core_intrinsics, rustc_private)]
#![allow(internal_features)]
#![no_std]

// Necessary for `panic = "unwind"` builds on some platforms.
#![feature(panic_unwind)]
extern crate unwind;

// Pull in the system libc library for what crt0.o likely requires.
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() }

要覆盖编译器插入的 main shim,我们必须使用 #![no_main] 禁用它,然后使用正确的 ABI 和正确的名称创建相应的符号,这也需要覆盖编译器的名称修饰

#![feature(lang_items, core_intrinsics, rustc_private)]
#![allow(internal_features)]
#![no_std]
#![no_main]

// Necessary for `panic = "unwind"` builds on some platforms.
#![feature(panic_unwind)]
extern crate unwind;

// Pull in the system libc library for what crt0.o likely requires.
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() }

如果您正在使用的目标平台没有通过 rustup 提供标准库的二进制版本(这可能意味着您正在自己构建 core crate)并且需要 compiler-rt 内部函数(即您在构建可执行文件时可能会遇到链接器错误:undefined reference to `__aeabi_memcpy'),则需要手动链接到 compiler_builtins crate 以获取这些内部函数并解决链接器错误。