【问题标题】:How can I call a raw address from Rust?如何从 Rust 调用原始地址?
【发布时间】:2018-02-18 10:37:21
【问题描述】:

我正在用 Rust 编写一个操作系统,需要直接调用我正在计算的虚拟地址(u32 类型)。我希望这相对简单:

let code = virtual_address as (extern "C" fn ());
(code)();

但是,这抱怨演员表是非原始的。它建议我使用 From 特征,但我看不出这有什么帮助(尽管我对 Rust 比较陌生,所以可能会遗漏一些东西)。

error[E0605]: non-primitive cast: `u32` as `extern "C" fn()`
 --> src/main.rs:3:16
  |
3 |     let code = virtual_address as (extern "C" fn ());
  |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait

我可以使用 libcore 中的所有内容,但尚未移植 std,因此不能依赖任何不是 no_std 的东西

【问题讨论】:

    标签: rust function-pointers osdev


    【解决方案1】:

    _ as f-ptr 类型的转换是不允许的(请参阅the Rustonomicon chapter on casts)。所以,据我所知,转换为函数指针类型的唯一方法是使用万能武器mem::transmute()

    但在我们可以使用transmute() 之前,我们必须将输入带入正确的内存布局。我们通过转换为*const ()(一个空指针)来做到这一点。之后我们可以使用transmute() 得到我们想要的:

    let ptr = virtual_address as *const ();
    let code: extern "C" fn() = unsafe { std::mem::transmute(ptr) };
    (code)();
    

    如果您发现自己经常这样做,各种宏可以删除样板文件。一种可能性:

    macro_rules! example {
        ($address:expr, $t:ty) => {
            std::mem::transmute::<*const (), $t>($address as _)
        };
    }
    
    let f = unsafe { example!(virtual_address, extern "C" fn()) };
    f(); 
    

    但是,对此有几点说明:

    • 如果你,未来的读者,想用它来做简单的 FFI 事情:请花点时间再考虑一下。很少需要自己计算函数指针。
    • 通常extern "C" 函数具有unsafe extern "C" fn() 类型。这意味着调用这些函数是不安全的。您可能应该将unsafe 添加到您的函数中。

    【讨论】:

    • 请注意,我绝对不是不安全事物方面的专家!如果有人更了解,请告诉我;然后我可以编辑或删除我的答案!
    • Rust 在这里没有做任何花哨的事情。 fn() 是一个普通的函数指针。只要virtual_address处有函数,调用约定和签名匹配,就可以进行强制转换并调用函数。
    • 太棒了,我需要做更多的设置来看看这是否真的有效,但它确实可以编译!非常感谢@LukasKalbertodt。
    • @red75prime 代码本身并不花哨,但在我的情况下确实依赖于传递一个有效的虚拟地址(需要考虑很多事情:物理 -> 虚拟计算,实际映射代码等.) 所以我认为它确实适合作为 Rust 的偏执狂中的 unsafe fn(在这个词的良好意义上)
    猜你喜欢
    • 2019-06-19
    • 1970-01-01
    • 2010-10-20
    • 2019-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多