22 个版本

0.16.11 2024年5月18日
0.16.10 2024年2月29日
0.16.9 2023年12月20日
0.16.5 2023年11月20日
0.14.5 2023年9月20日

#51 in FFI

每月 44 次下载
3 crates 中使用

Apache-2.0…

79KB

Eyra

完全用 Rust 编写的 Rust 程序

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

Eyra 是一个支持使用 Rust 完全实现 Rust 程序的包。

它使用 Origin 进行程序和线程的启动和关闭,以及使用 c-gull 来实现与 ABI 兼容的 libc 函数。目前它支持 Nightly Rust 在 Linux 的 x86-64、x86、aarch64 和 riscv64 上运行。

快速入门

在 Eyra 下运行 Rust 程序需要两个步骤。首先,添加 Cargo.toml 依赖项,我们可以使用以下方式添加:

cargo add eyra --rename=std

然后,添加一个 build.rs 文件,将 -nostartfiles 添加到链接标志中,以禁用主机启动代码,这样 Eyra 就可以提供自己的。build.rs

fn main() {
    println!("cargo:rustc-link-arg=-nostartfiles");
}

有了这些,cargo buildcargo runcargo test(使用 Nightly)等将正常工作,任何 *-unknown-linux-gnu* 目标都将正常工作。

在底层,它使用 Origin 来启动和关闭程序,使用 c-ward 来处理从 std 的 libc 调用,使用 rustix 来进行打印,因此它完全使用 Rust 实现。

示例

以下步骤的示例,请查看这个“Hello World”示例

其他示例包括

为什么?

为什么使用Eyra?

  • 它解决了Rust的set_var不安全问题。环境变量实现内部内存泄漏(它是可选的,但默认启用),因此setenv等是线程安全的。

  • 整个程序LTO,包括libc。这有时会产生更小的静态二进制文件,有时会产生更快的代码(尽管另一方面,有时并不,尽管另一方面,还有一些低垂的果实,所以考虑尝试并提交问题)。

    要实现更多的代码大小缩减,请参阅hello-world-small示例中的技术

  • 支持使用Eyra和-Zbuild-std编译程序,以完全从源代码构建程序

  • 完全静态链接支持平台NSS/DNS配置。“这是否可能?” “是的,可能的。”

  • 或者,提出你自己的原因!发挥创意,做自己想做的事情,并告诉我们。

为什么不使用Eyra?

  • 它不如主要的libc实现成熟。

  • 它不如主要的libc实现完整。它可以运行大多数Rust代码和一些流行的C库,但仍然缺乏许多典型C代码所使用的功能。

  • 它目前依赖于Rust Nightly,并且只能在Linux上运行,目前仅在x86-64、x86、aarch64和riscv64上运行。

  • 它目前无法在Miri下运行,因为Miri目前无法识别从汇编代码中发出的系统调用。话虽如此,Eyra确实努力遵守严格的来源和避免整个过程中出现未定义的行为,因此如果Miri获得了对这些系统调用的支持,Eyra应该处于有利地位。

  • 不支持动态链接。

似乎“内存安全”可能是使用Eyra的理由,Eyra确实有很多用安全Rust编写的代码,因此它确实从Rust的内存安全性中受益。然而,Eyra也有很多unsafe代码(实现libc不可避免)。在代码得到更彻底的验证之前,将其视为比成熟的C代码更安全是不现实的。

完全静态链接

Eyra的可执行文件不依赖于任何动态库,然而默认情况下,它们仍然依赖于动态链接器(例如,“/lib64/ld-linux-x86-64.so.2”)。

对于完全静态链接,目前有两种选择

  • 使用以下命令构建:RUSTFLAGS=-C target-feature=+crt-static -C relocation-model=static。这禁用了位置无关可执行文件(PIE)模式,这是直接的,但会失去地址空间布局随机化(ASLR)的安全性优势。

  • 使用RUSTFLAGS=-C target-feature=+crt-static构建,并启用experimental-relocate特性。这允许PIE模式和ASLR工作,但它是通过启用重定位的实验性实现来实现的。到目前为止,这段代码似乎在实践中的应用是成功的,但它涉及到Rust代码在运行时自我修补,这超出了任何Rust语义。

可选日志记录

Eyra有一个log特性,可以启用Rust log对程序和线程的启动和关闭进行跟踪,以及一个env_logger特性,用于安装env_logger作为日志记录器,这可以在Cargo.toml中启用

[dependencies]
std = { package = "eyra", version = "<current-version>", features = ["log", "env_logger"] }

有了这个,并将环境变量RUST_LOG设置为"trace",hello world程序将输出如下

[TRACE origin::program] Program started
[TRACE origin::thread] Main Thread[51383] initialized
[TRACE origin::program] Calling `.init_array`-registered function `0x55e86306bb80(1, 0x7ffd0f76aad8, 0x7ffd0f76aae8)`
[TRACE origin::program] Calling `origin_main(1, 0x7ffd0f76aad8, 0x7ffd0f76aae8)`
Hello, world!
[TRACE origin::program] `origin_main` returned `0`
[TRACE origin::thread] Thread[51383] calling `at_thread_exit`-registered function
[TRACE origin::thread] Thread[51383] calling `at_thread_exit`-registered function
[TRACE origin::program] Program exiting with status `0`

-Zbuild-std的兼容性

Eyra与-Zbuild-std兼容,但是上面使用的--rename=std技巧不起作用,因此需要使用这个cargo add调用

cargo add eyra

并将此行添加到程序的main.rs文件中

extern crate eyra;

以确保链接Eyra库。

减少代码大小

Eyra可以使用min-sized-rust中的技术生成非常小的静态链接二进制文件。查看hello-world-small示例

与Mustang的关系

Eyra类似于Mustang并使用相同的底层代码,但与使用自定义目标和-Z build-std不同,Eyra只需要用户将-nostartfiles添加到他们的链接行,例如通过示例中的build.rs。

与Mustang一样,Eyra目前运行在Linux上的Nightly Rust,支持x86-64、x86、aarch64和riscv64。它旨在支持所有由Rust支持的Linux版本(支持的平台),尽管目前只在相对较新的版本上进行了测试。它已经足够完善,可以运行

编译C程序

Eyra还可以编译成libc.a,可用于编译C程序;请参阅eyra-c存储库。

设计哲学

Eyra及其使用的库有一些设计目标。

从头到尾的正常Rust

有时在libc实现代码中,有一种诱惑要说“如果某些东西在技术上是不确定的,因为这是低级代码,我们知道我们在做什么”。

Origin、c-scape、c-gull、rustix和其他努力抵制这种诱惑,并遵循Rust规则,包括严格的来源、I/O安全以及所有其他规则,一直到底层系统调用。

这只是正常的Rust代码,只要我们在用户空间中能够走多远,当我们最终不得不切换到内联汇编时,我们尽可能少地做。

目前只知道一个地方没有实现这个目标。在“静态 PIE”可执行文件(例如,使用 RUSTFLAGS="-C target-feature=+crt-static" 构建)中,动态连接器没有被使用,所以可执行文件必须自己处理所有的重定位。然而,这意味着将数据存储在通常不会被考虑为可变的内存位置。Origin 实现这一功能的代码默认是禁用的,可以通过“experimental-relocate” cargo 功能来启用。

作为 Rust 之上的 C 兼容性层,而不是相反

Eyra 是基于一系列带有惯用 Rust API 的 Rust crate 构建,以及两个相对薄的层,c-scape 和 c-gull,这两个层实现了与 libc 兼容的 C ABI。

以这种方式编写代码可能需要更多的工作,但它有一个优点,即可以清楚地分离出与 C 指针和字符串等事物相关的 unsafe,这些事物是 libc API 中的基本 unsafe 所必需的,例如系统调用、线程原语和其他功能。这意味着不希望通过 C 兼容性层的 Rust 程序可以直接使用底层 crate。

依赖关系

~12–21MB
~386K SLoC