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
78KB
Mustang 是一个用于构建完全用 Rust 编写的程序的系统,这意味着它们不依赖于 libc 或 crt1.o 的任何部分,也不链接任何 C 代码。
为什么?为了好玩!并且为了锻炼一些具有其他用途的组件(例如 rustix
、origin
、c-scape
和 c-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 终止字符串的使用,因此可以更安全。
依赖关系
~0–9.5MB
~97K SLoC