I am writing an OS in Rust and need to directly call into a virtual address that I'm calculating (of type u32
). I expected this to be relatively simple:
let code = virtual_address as (extern "C" fn ());
(code)();
However, this complains that the cast is non-primitive. It suggests I use the From
trait, but I don't see how this could help (although I am relatively new to Rust and so could be missing something).
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
I have everything in libcore
at my disposal, but haven't ported std
and so can't rely on anything that isn't no_std
Rust has a number of different smart pointer types in its standard library, but there are two types that are extra-special. Much of Rust's safety comes from compile-time checks, but raw pointers don't have such guarantees, and are unsafe to use.
You need to use the & operator to get the address of any variable, so you need to write &my_struct as *const _ (where _ can be a literal _ , or the type of the value behind the pointer).
Keyword fnA function or function pointer. Functions are the primary way code is executed within Rust. Function blocks, usually just called functions, can be defined in a variety of different places and be assigned many different attributes and modifiers.
A pointer is a general concept for a variable that contains an address in memory. This address refers to, or “points at,” some other data. The most common kind of pointer in Rust is a reference, which you learned about in Chapter 4. References are indicated by the & symbol and borrow the value they point to.
Casts of the type _ as f-ptr
are not allowed (see the Rustonomicon chapter on casts). So, as far as I can tell, the only way to cast to function pointer types is to use the all mighty weapon mem::transmute()
.
But before we can use transmute()
, we have to bring our input into the right memory layout. We do this by casting to *const ()
(a void pointer). Afterwards we can use transmute()
to get what we want:
let ptr = virtual_address as *const ();
let code: extern "C" fn() = unsafe { std::mem::transmute(ptr) };
(code)();
If you find yourself doing this frequently, various kinds of macros can remove the boilerplate. One possibility:
macro_rules! example {
($address:expr, $t:ty) => {
std::mem::transmute::<*const (), $t>($address as _)
};
}
let f = unsafe { example!(virtual_address, extern "C" fn()) };
f();
However, a few notes on this:
extern "C"
functions have the type unsafe extern "C" fn()
. This means that those functions are unsafe to call. You should probably add the unsafe
to your function.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With