Hotdry.
compiler-design

Eurydice:通过 MIR 去糖化将 Rust 转译为 C

利用 MIR 去糖化将 Rust 所有权和借用映射到安全的 C 指针与联合体,实现零开销 FFI 并配备运行时检查。

在 Rust 验证生态向 Rust 迁移的过程中,Eurydice 项目提供了一种巧妙的解决方案:通过 Rust 中间表示(MIR)的去糖化(desugaring),将 Rust 代码转译为功能等价且安全的 C 代码。这种方法特别适用于遗留系统,无法直接依赖 Rust 工具链的环境,同时保留了 Rust 的内存安全特性。

Eurydice 的核心观点在于,利用 MIR 这一经过借用检查器的标准化中间表示,直接映射 Rust 的所有权模型和借用规则到 C 的指针和联合体结构中,避免了传统手动 FFI 的开销和错误隐患。具体而言,Rust 的所有权(ownership)被转化为带标签的联合体(tagged unions),借用(borrows)则映射为胖指针(fat pointers),这些结构在 C 中通过运行时检查确保安全,同时编译器优化可实现零开销抽象。

证据支持这一观点来自 Eurydice 的架构设计。“Eurydice consumes Rust programs via the Charon infrastructure, then extracts Rust to KaRaMeL's internal AST via a type-driven translation。” 随后,超过 30 个纳米级 pass(nano-passes)处理 MIR 去糖化,将复杂的高级特性降级为 C 等价物。例如,在 Kyber 后量子加密算法的实现中,Rust 版本经过验证后,通过 Eurydice 生成 C 代码,直接集成到 Mozilla 的 NSS 库中,证明了其在生产环境中的零开销 FFI 能力。

在 MIR 去糖化过程中,关键是所有权和借用的精确映射。Rust 的可变借用(&mut T)转换为 C 中的独占指针 struct {void* data; usize len; bool valid;},其中 valid 标志由运行时检查维护。不可变借用(&T)使用只读胖指针,避免数据竞争。枚举类型如 Option 映射为联合体:

typedef struct {
    uint8_t tag;
    union {
        T payload;
    };
} Option_T;

运行时检查通过内联函数实现,例如 bounds 检查:

static inline bool bounds_check(void* ptr, usize idx, usize len) {
    return idx < len;
}

这些检查在 -O2 优化下可被常量传播消除,实现零开销。对于生命周期,MIR 中的 borrowck 信息被保留为静态分析生成的前置条件断言。

要落地 Eurydice,需要以下参数和清单:

  1. 环境配置

    • 使用 Nix flakes:nix develop 确保 OCaml、Cargo、KaRaMeL 等版本一致。
    • 克隆仓库:git clone https://github.com/AeneasVerif/eurydice
    • 安装依赖:make setup-karamel; make setup-charon; make setup-libcrux
  2. 代码准备

    • 编写 modest subset Rust:避免 unsafe、复杂 trait、async,仅用 structs、enums、vectors(作为 slices)。
    • 配置 cg.yaml:声明 monomorphization,如 monomorphize: [core::option::Option<(PolynomialRingElement, AVX2Simd)>]
    • 测试 MIR 提取:rustc --emit=mir input.rs 验证 borrowck 通过。
  3. 编译参数

    • 构建:make test 生成 out/ 中的 C 文件。
    • 优化阈值:数组初始化 >16 元素用 memset;小数组直接赋值。
    • 运行时检查开关:定义 EURYDICE_CHECKS=1 启用完整检查,回滚到无检查版。
  4. 监控要点

    • 代码大小:比较生成 C 与手工 FFI,目标 <1.5x。
    • 性能基准:perf record Kyber 基准,检查 <5% 开销。
    • 安全审计:Valgrind/ASan 验证无 UAF/overflow。
    • 回滚策略:若 monomorphization 失败,缩小泛型参数;若 C 编译 warn,调整 pass 顺序。

在实际部署中,对于 FFI 边界,Eurydice 生成的 C 函数签名如 void kyber_enc(uint8_t* pk, uint8_t* ct),零开销调用 Rust 逻辑。相比 cbindgen 或 cc,Eurydice 保留验证属性(如 PrfSpec),确保 C 侧等价。

潜在风险包括子集限制:不支持 GATs、复杂借用嵌套;解决方案是渐进迁移,先用 Eurydice 桥接,再全 Rust。另一个是 OCaml 依赖,回滚用 Docker 容器化。

总之,Eurydice 通过 MIR 去糖化桥接 Rust 安全与 C 生态,提供参数化清单让工程落地。未来,随着 Charon 增强,支持更广 Rust 子集。

资料来源

(正文字数:1028)

查看归档