#linux #programs #libc #abi #platform #system #part

no-std mustang

完全用 Rust 编写的 Rust 程序

38 个版本 (14 个破坏性版本)

0.14.6 2024 年 5 月 18 日
0.14.5 2024 年 2 月 29 日
0.14.4 2023 年 10 月 7 日
0.9.0 2023 年 7 月 1 日
0.3.0 2021 年 11 月 18 日

#35 in FFI

Apache-2.0…

78KB

Mustang

完全用 Rust 编写的 Rust 程序

Github Actions CI Status zulip chat crates.io page docs.rs docs

Mustang 是一个用于构建完全用 Rust 编写的程序的系统,这意味着它们不依赖于 libc 或 crt1.o 的任何部分,也不链接任何 C 代码。

为什么?为了好玩!并且为了锻炼一些具有其他用途的组件(例如 rustixoriginc-scapec-gull),而这些组件恰好也是 Mustang 所需要的一部分。在未来,也许还可以用于尝试新的平台 ABIs。

Mustang 目前在 Linux 上的 Rust Nightly、x86-64、x86、aarch64 和 riscv64 上运行。它旨在支持 Rust 支持的所有 Linux 版本 (尽管目前仅测试了相对较新的版本)。它已经足够完整,可以运行

从可预见的未来来看,Mustang 并不关乎使任何东西更安全。主要的 libc 实现已经经过极其严格的测试和成熟。就 Mustang 而言,它是实验性的,并且有很多 unsafe

用法

要使用它,首先安装 rust-src,这是 -Z build-std 所必需的

$ rustup component add rust-src --toolchain nightly

然后,将环境变量 RUST_TARGET_PATH 设置为 mustang/target-specs 目录的路径,这样您就可以使用 --target=…mustang 目标命名。例如,在 mustang 仓库内

$ export RUST_TARGET_PATH="$PWD/mustang/target-specs"

然后,在自己的 crate 中添加对 mustang 的依赖

[dependencies]
mustang = "<current version>"

并将 mustang::can_run_this!(); 添加到顶级模块中(例如 main.rs)。在非 mustang 目标构建中,这不会做任何事情,但在 mustang 目标构建中,它会安排将 mustang 的库链接进来。

mustang::can_run_this!();

然后,使用 Rust 夜间版本编译,使用 -Z build-std--target=<mustang-target>。例如

$ cargo +nightly run --quiet -Z build-std --target=x86_64-mustang-linux-gnu --example hello
Hello, world!
$

这是一个完全由 Rust 构建的 Rust 程序,说“你好,世界!”!

更多细节,mustang 有一个 env_logger 功能,您可以选择启用它,并设置 RUST_LOG 以查看 mustang 的各种功能

$ RUST_LOG=trace cargo +nightly run --quiet -Z build-std --target=x86_64-mustang-linux-gnu --example hello --features log,env_logger
[2021-06-28T06:28:31Z TRACE origin::program] Program started
[2021-06-28T06:28:31Z TRACE origin::threads] Main Thread[916066] initialized
[2021-06-28T06:28:31Z TRACE origin::program] Calling `.init_array`-registered function `0x5555558fb480(1, 0x7fffffffdb98, 0x7fffffffdba8)`
[2021-06-28T06:28:31Z TRACE origin::program] Calling `main(1, 0x7fffffffdb98, 0x7fffffffdba8)`
Hello, world!
[2021-06-28T06:28:31Z TRACE origin::program] `main` returned `0`
[2021-06-28T06:28:31Z TRACE origin::program] Program exiting
$

检查 libc 函数的使用的一个简单方法是使用 nm -u,因为上述命令配置为动态链接 libc。如果 mustang 已涵盖所有内容,则不应有输出

$ nm -u target/x86_64-mustang-linux-gnu/debug/examples/hello
$

C 运行时互操作

要使用 *-mustang-* 目标编译 C 代码,您可能需要 告诉 cc crate 使用哪个 C 编译器;例如,对于 i686-mustang-linux-gnu,将环境变量 CC_i686-mustang-linux-gnu 设置为 i686-linux-gnu-gcc

panic = "abort"

当在 Cargo.toml 中使用 panic = "abort" 时,将 -Z build-std 更改为 -Z build-std=panic_abort,std。有关背景信息,请参阅 此处

已知限制

mustang 的已知限制包括

  • 尚未实现动态链接。
  • 许多 Rust 程序通常不需要的 libc C 函数尚未实现。

替代方案

Eyra 使用与 Mustang 相同的底层库,但不需要自定义目标,也不需要 -Z build-std

直接使用 Origin 是另一种选择;有一些 示例 展示了如何这样做。直接使用 Origin 可以产生非常小的二进制文件大小;tiny 示例可以生成 408 字节的可执行文件。使用 Origin 的缺点是它不提供 std 实现。

Origin Studio 是一个示例,展示了在 Origin 上可能出现的类似 std 的环境。它包含像 println! 这样的功能,但也包括许多其他功能,如 File

背景

Mustang 部分灵感来自 steed 的类似功能,但有一些不同之处。现在 cargo 的 build-std 功能已经可用,这使得与自定义目标一起工作变得容易得多。Mustang 采取的方法是首先替换 libc 接口,并直接使用 std,而不是重新实现 std。这可能会发展,但无论我们做什么,Mustang 的一个高层次目标是永远不需要重新实现 std

从现在开始,mustang 将走向何方?它是否会支持功能 X、平台 Y 或用例 Z?如果 origin 可以用 Rust 进行程序启动,而 rustix 可以用 Rust 进行系统调用,这意味着什么?

并且,mustang 最终是否能够支持不限于 C 风格的 argc/argv(/envp) 传递约定的新 ABIs,从而允许新的程序参数传递方式?

让我们来看看!在 聊天问题 中打个招呼。

如何将 mustang 移植到新的架构上?

  • rustix 移植到该架构,添加生成系统调用的汇编序列。
  • origin 移植到该架构,添加程序和线程原语的汇编序列。
  • mustang/target-specs 中创建一个目标文件,首先按照 这些说明 生成内置目标的规范,然后
    • is-builtin 修改为 false
    • dynamic-linking 修改为 false
    • -nostartfiles-Wl,--undefined=_Unwind_Backtrace 添加到预链接参数
    • 添加 "vendor": "mustang" 看看 mustang/target-specs 目录中的其他目标作为示例。
  • 使用新的目标编译 examples 目录中的某些程序。尝试在二进制文件上运行 nm -u 检查需要实现的不确定符号。
  • 将架构添加到 tests/tests.rs。
  • 通过复制其他架构所做的工作,将 CI 测试添加到 .github/workflows/main.yml。

如何将 mustang 移植到新的操作系统上?

可能需要做与新的架构类似的事情,并且还需要编写一个新的 origin::rust 实现来处理操作系统的参数、环境变量和初始化函数的约定。

在非 Mustang 程序中使用 Mustang

在非*-mustang-*目标中,导入此crate并使用mustang::can_run_this!()宏没有任何效果,不依赖于nightly Rust,并且没有额外的依赖。

有关更多详细信息,请参阅mustang-example示例。

类似的crate

c-scape 与 relibc 有一些相似之处,但侧重点不同。Relibc 致力于成为一个完整的 libc 替代品,而 c-scape 仅仅是旨在涵盖 Rust's std 和流行的 crate 所使用的功能。Relibc 的某些部分是用 C 实现的,而 c-scape 完全是用 Rust 实现的。

c-scape 也与 steed 类似。有关详细信息,请参阅背景

尽管如此,使 c-scape 独特的最显著之处在于其设计为 Rust crate 的包装器集,具有 Rust 接口。C ABI 兼容性对于使现有代码工作很有用,但一旦一切正常,我们可以通过将代码更改为直接调用 Rust 接口来简化并优化。这可以消除许多原始指针和 C 风格的 NUL 终止字符串的使用,因此可以更安全。

其他类似的项目包括 tiny-stdveneer。与 steed 类似,它们包含它们自己的 std 实现。

依赖关系

~0–9.5MB
~97K SLoC