如何从Rust中调用原始地址?

14

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

let code = virtual_address as (extern "C" fn ());
(code)();
然而,这抱怨说这个强制类型转换是非原始类型。它建议我使用From trait,但我不知道它如何帮助(虽然我对Rust相对较新,所以可能会漏掉一些东西)。
然而,这引发了一个问题,即这种转换是非原始类型的。建议使用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 的东西。

1个回答

22

类型为_ as f-ptr的强制转换是不允许的(请参见Rustonomicon章节关于类型转换)。因此,据我所知,将其转换为函数指针类型的唯一方法是使用万能武器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关键字。

请注意,我绝对不是一个关于不安全事物的专家!如果有人知道得更好,请告诉我;那么我可以编辑或删除我的回答! - Lukas Kalbertodt
Rust 在这里没有做任何花哨的事情。 fn() 是一个普通的函数指针。只要在 virtual_address 上有一个函数,调用惯例和签名匹配,就可以进行转换并调用函数。 - red75prime
太棒了,我需要进行更多的设置来确定它是否真正有效,但是它确实编译成功了!非常感谢 @LukasKalbertodt。 - Isaac Woods
@red75prime 代码本身并不花哨,但是在我的情况下需要传递一个有效的虚拟地址(有许多事情要考虑:物理->虚拟计算,实际映射代码等),因此我认为它适合作为 Rust 的 unsafe fn(在好的意义上)的一部分。 - Isaac Woods
我们能告诉Rust这样一个内联转换函数使用哪种调用约定吗? - Benni

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接