#setjmp #access #llvm #ensure #interface #won-t #sigsetjmp

cee-scape

cee-scape 包提供了对 setjmpsigsetjmp 功能的 Rust 访问

8 个版本

0.2.0 2023年9月28日
0.1.6 2023年8月10日

#242 in 编程语言

Download history 48/week @ 2024-03-13 20/week @ 2024-03-20 70/week @ 2024-03-27 480/week @ 2024-04-03 502/week @ 2024-04-10 524/week @ 2024-04-17 443/week @ 2024-04-24 341/week @ 2024-05-01 442/week @ 2024-05-08 67/week @ 2024-05-15 75/week @ 2024-05-22 419/week @ 2024-05-29 460/week @ 2024-06-05 195/week @ 2024-06-12 232/week @ 2024-06-19 270/week @ 2024-06-26

每月 1,285 次下载

MIT/Apache

41KB
469

cee-scape

cee-scape 包通过一个确保 LLVM 不会错误编译的接口,为 Rust 提供对 setjmpsigsetjmp 功能的访问

示例

你可能想象你有一些类似这样的 C 代码(极度简化)

#include <setjmp.h>
#include <stdint.h>

uint32_t subtract_but_longjmp_if_underflow(jmp_buf env, uint32_t a, uint32_t b) {
    if (b > a) {
        longjmp(env, b - a);
    }
    return a - b;
}

如果你想在 Rust 中调用这些 C 代码,你需要建立一个跳转环境(jmp_buf env 参数);但是 Rust 并不提供 setjmp

以下是使用此包来解决你的问题的方法

use cee_scape::call_with_setjmp;

// This invocation passes parameters that follow normal control flow.
assert_eq!(call_with_setjmp(|env| {
    (unsafe {
        subtract_but_longjmp_if_underflow(env, 10, 3)
    }) as c_int
}), 7);

// This invocation passes parameters that cause a non-local jump.
assert_eq!(call_with_setjmp(|env| {
    unsafe {
        subtract_but_longjmp_if_underflow(env, 3, 10);
        panic!("should never get here.");
    }
}), 7);

为什么不在函数中直接添加 setjmp 呢?

在 Rust 项目中,关于这个问题的讨论已经相当广泛。

特别是请参阅 rust-lang/libc PR 1216rust-lang/rfcs Issue 2625

答案是多方面的。

首先,要添加对调用 setjmp 的支持,就需要在 Rust 编译器中添加额外的支持。具体来说,需要支持将函数标记为可能多次返回,例如通过在 rust-lang/rfcs Issue 2625 中讨论的 returns_twice 属性。

其次,对 setjmp 函数的调用将以奇特的方式与 Rust 借用检查器交互;除非借用检查器被扩展以理解 returns_twice 属性的意义,否则会引发立即的不稳定行为,例如将同一个栈槽中的所有权对象多次移动。这可能会导致相同的值被丢弃多次(这将是不可靠的)。

第三,根据 C 标准,setjmp 函数本身只在非常具体的上下文中具有定义良好的行为(再次在 rust-lang/rfcs Issue 2625 中讨论);甚至在 Rust 中,let x = setjmp(...) 都将是未定义的行为。

cee-scape 提供的方法,如 call_with_setjmp,通过限制 setjmp 的使用范围到特定的编码模式,从而绕过了上述问题

call_with_setjmp(|env| { ... })

在调用 call_with_setjmp 的动态作用域内,可以正常返回,或者通过 longjmp 跳转到给定的跳转环境 env(这将导致从 call_with_setjmp 调用返回)。无论哪种方式,外部调用最多只返回一次。给定的跳转环境仅在动态作用域内可用(Rust 的生命周期规则有助于强制执行此约束)。

为什么叫 cee-scape

这是一个双关语:C 的跳转环境也被称为 "escape continuations"。这个包实现了 C 的逃离。

依赖项

~225KB